1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
9 #include "ui/gl/gl_surface_glx.h"
11 #include "base/basictypes.h"
12 #include "base/lazy_instance.h"
13 #include "base/logging.h"
14 #include "base/memory/scoped_ptr.h"
15 #include "base/memory/weak_ptr.h"
16 #include "base/message_loop/message_loop.h"
17 #include "base/single_thread_task_runner.h"
18 #include "base/synchronization/cancellation_flag.h"
19 #include "base/synchronization/lock.h"
20 #include "base/thread_task_runner_handle.h"
21 #include "base/threading/non_thread_safe.h"
22 #include "base/threading/thread.h"
23 #include "base/time/time.h"
24 #include "base/trace_event/trace_event.h"
25 #include "ui/events/platform/platform_event_source.h"
26 #include "ui/gfx/x/x11_connection.h"
27 #include "ui/gfx/x/x11_types.h"
28 #include "ui/gl/gl_bindings.h"
29 #include "ui/gl/gl_implementation.h"
30 #include "ui/gl/sync_control_vsync_provider.h"
36 Display
* g_display
= nullptr;
37 const char* g_glx_extensions
= nullptr;
38 bool g_glx_context_create
= false;
39 bool g_glx_create_context_robustness_supported
= false;
40 bool g_glx_texture_from_pixmap_supported
= false;
41 bool g_glx_oml_sync_control_supported
= false;
43 // Track support of glXGetMscRateOML separately from GLX_OML_sync_control as a
44 // whole since on some platforms (e.g. crosbug.com/34585), glXGetMscRateOML
45 // always fails even though GLX_OML_sync_control is reported as being supported.
46 bool g_glx_get_msc_rate_oml_supported
= false;
48 bool g_glx_sgi_video_sync_supported
= false;
50 static const base::TimeDelta kGetVSyncParametersMinPeriod
=
52 // See crbug.com/373489
53 // On Linux, querying the vsync parameters might burn CPU for up to an
54 // entire vsync, so we only query periodically to reduce CPU usage.
55 // 5 seconds is chosen somewhat abitrarily as a balance between:
56 // a) Drift in the phase of our signal.
57 // b) Potential janks from periodically pegging the CPU.
58 base::TimeDelta::FromSeconds(5);
60 base::TimeDelta::FromSeconds(0);
63 class OMLSyncControlVSyncProvider
64 : public gfx::SyncControlVSyncProvider
{
66 explicit OMLSyncControlVSyncProvider(gfx::AcceleratedWidget window
)
67 : SyncControlVSyncProvider(),
71 ~OMLSyncControlVSyncProvider() override
{}
74 bool GetSyncValues(int64
* system_time
,
75 int64
* media_stream_counter
,
76 int64
* swap_buffer_counter
) override
{
77 return glXGetSyncValuesOML(g_display
, window_
, system_time
,
78 media_stream_counter
, swap_buffer_counter
);
81 bool GetMscRate(int32
* numerator
, int32
* denominator
) override
{
82 if (!g_glx_get_msc_rate_oml_supported
)
85 if (!glXGetMscRateOML(g_display
, window_
, numerator
, denominator
)) {
86 // Once glXGetMscRateOML has been found to fail, don't try again,
87 // since each failing call may spew an error message.
88 g_glx_get_msc_rate_oml_supported
= false;
98 DISALLOW_COPY_AND_ASSIGN(OMLSyncControlVSyncProvider
);
101 class SGIVideoSyncThread
102 : public base::Thread
,
103 public base::NonThreadSafe
,
104 public base::RefCounted
<SGIVideoSyncThread
> {
106 static scoped_refptr
<SGIVideoSyncThread
> Create() {
107 if (!g_video_sync_thread
) {
108 g_video_sync_thread
= new SGIVideoSyncThread();
109 g_video_sync_thread
->Start();
111 return g_video_sync_thread
;
115 friend class base::RefCounted
<SGIVideoSyncThread
>;
117 SGIVideoSyncThread() : base::Thread("SGI_video_sync") {
118 DCHECK(CalledOnValidThread());
121 ~SGIVideoSyncThread() override
{
122 DCHECK(CalledOnValidThread());
123 g_video_sync_thread
= nullptr;
127 static SGIVideoSyncThread
* g_video_sync_thread
;
129 DISALLOW_COPY_AND_ASSIGN(SGIVideoSyncThread
);
132 class SGIVideoSyncProviderThreadShim
{
134 explicit SGIVideoSyncProviderThreadShim(XID window
)
137 task_runner_(base::ThreadTaskRunnerHandle::Get()),
138 cancel_vsync_flag_(),
140 // This ensures that creation of |window_| has occured when this shim
141 // is executing in the same process as the call to create |window_|.
142 XSync(g_display
, False
);
145 virtual ~SGIVideoSyncProviderThreadShim() {
147 glXDestroyContext(display_
, context_
);
152 base::CancellationFlag
* cancel_vsync_flag() {
153 return &cancel_vsync_flag_
;
156 base::Lock
* vsync_lock() {
163 XWindowAttributes attributes
;
164 if (!XGetWindowAttributes(display_
, window_
, &attributes
)) {
165 LOG(ERROR
) << "XGetWindowAttributes failed for window " <<
170 XVisualInfo visual_info_template
;
171 visual_info_template
.visualid
= XVisualIDFromVisual(attributes
.visual
);
173 int visual_info_count
= 0;
174 gfx::XScopedPtr
<XVisualInfo
> visual_info_list(XGetVisualInfo(
175 display_
, VisualIDMask
, &visual_info_template
, &visual_info_count
));
177 DCHECK(visual_info_list
.get());
178 if (visual_info_count
== 0) {
179 LOG(ERROR
) << "No visual info for visual ID.";
184 glXCreateContext(display_
, visual_info_list
.get(), nullptr, True
);
186 DCHECK(nullptr != context_
);
189 void GetVSyncParameters(const VSyncProvider::UpdateVSyncCallback
& callback
) {
192 // Don't allow |window_| destruction while we're probing vsync.
193 base::AutoLock
locked(vsync_lock_
);
195 if (!context_
|| cancel_vsync_flag_
.IsSet())
198 glXMakeCurrent(display_
, window_
, context_
);
200 unsigned int retrace_count
= 0;
201 if (glXWaitVideoSyncSGI(1, 0, &retrace_count
) != 0)
204 TRACE_EVENT_INSTANT0("gpu", "vblank", TRACE_EVENT_SCOPE_THREAD
);
205 now
= base::TimeTicks::Now();
207 glXMakeCurrent(display_
, 0, 0);
210 const base::TimeDelta kDefaultInterval
=
211 base::TimeDelta::FromSeconds(1) / 60;
213 task_runner_
->PostTask(
214 FROM_HERE
, base::Bind(callback
, now
, kDefaultInterval
));
218 // For initialization of display_ in GLSurface::InitializeOneOff before
219 // the sandbox goes up.
220 friend class gfx::GLSurfaceGLX
;
222 static Display
* display_
;
227 scoped_refptr
<base::SingleThreadTaskRunner
> task_runner_
;
229 base::CancellationFlag cancel_vsync_flag_
;
230 base::Lock vsync_lock_
;
232 DISALLOW_COPY_AND_ASSIGN(SGIVideoSyncProviderThreadShim
);
235 class SGIVideoSyncVSyncProvider
236 : public gfx::VSyncProvider
,
237 public base::SupportsWeakPtr
<SGIVideoSyncVSyncProvider
> {
239 explicit SGIVideoSyncVSyncProvider(gfx::AcceleratedWidget window
)
240 : vsync_thread_(SGIVideoSyncThread::Create()),
241 shim_(new SGIVideoSyncProviderThreadShim(window
)),
242 cancel_vsync_flag_(shim_
->cancel_vsync_flag()),
243 vsync_lock_(shim_
->vsync_lock()) {
244 vsync_thread_
->message_loop()->PostTask(
246 base::Bind(&SGIVideoSyncProviderThreadShim::Initialize
,
247 base::Unretained(shim_
.get())));
250 ~SGIVideoSyncVSyncProvider() override
{
252 base::AutoLock
locked(*vsync_lock_
);
253 cancel_vsync_flag_
->Set();
256 // Hand-off |shim_| to be deleted on the |vsync_thread_|.
257 vsync_thread_
->message_loop()->DeleteSoon(
262 void GetVSyncParameters(
263 const VSyncProvider::UpdateVSyncCallback
& callback
) override
{
264 if (kGetVSyncParametersMinPeriod
> base::TimeDelta()) {
265 base::TimeTicks now
= base::TimeTicks::Now();
266 base::TimeDelta delta
= now
- last_get_vsync_parameters_time_
;
267 if (delta
< kGetVSyncParametersMinPeriod
)
269 last_get_vsync_parameters_time_
= now
;
272 // Only one outstanding request per surface.
273 if (!pending_callback_
) {
274 pending_callback_
.reset(
275 new VSyncProvider::UpdateVSyncCallback(callback
));
276 vsync_thread_
->message_loop()->PostTask(
278 base::Bind(&SGIVideoSyncProviderThreadShim::GetVSyncParameters
,
279 base::Unretained(shim_
.get()),
281 &SGIVideoSyncVSyncProvider::PendingCallbackRunner
,
287 void PendingCallbackRunner(const base::TimeTicks timebase
,
288 const base::TimeDelta interval
) {
289 DCHECK(pending_callback_
);
290 pending_callback_
->Run(timebase
, interval
);
291 pending_callback_
.reset();
294 scoped_refptr
<SGIVideoSyncThread
> vsync_thread_
;
296 // Thread shim through which the sync provider is accessed on |vsync_thread_|.
297 scoped_ptr
<SGIVideoSyncProviderThreadShim
> shim_
;
299 scoped_ptr
<VSyncProvider::UpdateVSyncCallback
> pending_callback_
;
301 // Raw pointers to sync primitives owned by the shim_.
302 // These will only be referenced before we post a task to destroy
303 // the shim_, so they are safe to access.
304 base::CancellationFlag
* cancel_vsync_flag_
;
305 base::Lock
* vsync_lock_
;
307 base::TimeTicks last_get_vsync_parameters_time_
;
309 DISALLOW_COPY_AND_ASSIGN(SGIVideoSyncVSyncProvider
);
312 SGIVideoSyncThread
* SGIVideoSyncThread::g_video_sync_thread
= nullptr;
314 // In order to take advantage of GLX_SGI_video_sync, we need a display
315 // for use on a separate thread. We must allocate this before the sandbox
316 // goes up (rather than on-demand when we start the thread).
317 Display
* SGIVideoSyncProviderThreadShim::display_
= nullptr;
321 GLSurfaceGLX::GLSurfaceGLX() {}
323 bool GLSurfaceGLX::InitializeOneOff() {
324 static bool initialized
= false;
328 // http://crbug.com/245466
329 setenv("force_s3tc_enable", "true", 1);
331 // SGIVideoSyncProviderShim (if instantiated) will issue X commands on
333 gfx::InitializeThreadedX11();
334 g_display
= gfx::GetXDisplay();
337 LOG(ERROR
) << "XOpenDisplay failed.";
342 if (!glXQueryVersion(g_display
, &major
, &minor
)) {
343 LOG(ERROR
) << "glxQueryVersion failed";
347 if (major
== 1 && minor
< 3) {
348 LOG(ERROR
) << "GLX 1.3 or later is required.";
352 g_glx_extensions
= glXQueryExtensionsString(g_display
, 0);
353 g_glx_context_create
=
354 HasGLXExtension("GLX_ARB_create_context");
355 g_glx_create_context_robustness_supported
=
356 HasGLXExtension("GLX_ARB_create_context_robustness");
357 g_glx_texture_from_pixmap_supported
=
358 HasGLXExtension("GLX_EXT_texture_from_pixmap");
359 g_glx_oml_sync_control_supported
=
360 HasGLXExtension("GLX_OML_sync_control");
361 g_glx_get_msc_rate_oml_supported
= g_glx_oml_sync_control_supported
;
362 g_glx_sgi_video_sync_supported
=
363 HasGLXExtension("GLX_SGI_video_sync");
365 if (!g_glx_get_msc_rate_oml_supported
&& g_glx_sgi_video_sync_supported
)
366 SGIVideoSyncProviderThreadShim::display_
= gfx::OpenNewXDisplay();
373 const char* GLSurfaceGLX::GetGLXExtensions() {
374 return g_glx_extensions
;
378 bool GLSurfaceGLX::HasGLXExtension(const char* name
) {
379 return ExtensionsContain(GetGLXExtensions(), name
);
383 bool GLSurfaceGLX::IsCreateContextSupported() {
384 return g_glx_context_create
;
388 bool GLSurfaceGLX::IsCreateContextRobustnessSupported() {
389 return g_glx_create_context_robustness_supported
;
393 bool GLSurfaceGLX::IsTextureFromPixmapSupported() {
394 return g_glx_texture_from_pixmap_supported
;
398 bool GLSurfaceGLX::IsOMLSyncControlSupported() {
399 return g_glx_oml_sync_control_supported
;
402 void* GLSurfaceGLX::GetDisplay() {
406 GLSurfaceGLX::~GLSurfaceGLX() {}
408 void* GLSurfaceGLX::GetConfig(gfx::AcceleratedWidget window
) {
411 // This code path is expensive, but we only take it when
412 // attempting to use GLX_ARB_create_context_robustness, in which
413 // case we need a GLXFBConfig for the window in order to create a
416 // TODO(kbr): this is not a reliable code path. On platforms which
417 // support it, we should use glXChooseFBConfig in the browser
418 // process to choose the FBConfig and from there the X Visual to
419 // use when creating the window in the first place. Then we can
420 // pass that FBConfig down rather than attempting to reconstitute
423 XWindowAttributes attributes
;
424 if (!XGetWindowAttributes(g_display
, window
, &attributes
)) {
425 LOG(ERROR
) << "XGetWindowAttributes failed for window " << window
<< ".";
429 int visual_id
= XVisualIDFromVisual(attributes
.visual
);
431 int num_elements
= 0;
432 gfx::XScopedPtr
<GLXFBConfig
> configs(
433 glXGetFBConfigs(g_display
, DefaultScreen(g_display
), &num_elements
));
434 if (!configs
.get()) {
435 LOG(ERROR
) << "glXGetFBConfigs failed.";
439 LOG(ERROR
) << "glXGetFBConfigs returned 0 elements.";
444 for (i
= 0; i
< num_elements
; ++i
) {
446 if (glXGetFBConfigAttrib(g_display
, configs
.get()[i
], GLX_VISUAL_ID
,
448 LOG(ERROR
) << "glXGetFBConfigAttrib failed.";
451 if (value
== visual_id
) {
457 return configs
.get()[i
];
462 NativeViewGLSurfaceGLX::NativeViewGLSurfaceGLX(gfx::AcceleratedWidget window
)
463 : parent_window_(window
), window_(0), config_(nullptr) {
466 gfx::AcceleratedWidget
NativeViewGLSurfaceGLX::GetDrawableHandle() const {
470 bool NativeViewGLSurfaceGLX::Initialize() {
471 XWindowAttributes attributes
;
472 if (!XGetWindowAttributes(g_display
, parent_window_
, &attributes
)) {
473 LOG(ERROR
) << "XGetWindowAttributes failed for window " << parent_window_
477 size_
= gfx::Size(attributes
.width
, attributes
.height
);
478 // Create a child window, with a CopyFromParent visual (to avoid inducing
479 // extra blits in the driver), that we can resize exactly in Resize(),
480 // correctly ordered with GL, so that we don't have invalid transient states.
481 // See https://crbug.com/326995.
482 window_
= XCreateWindow(g_display
, parent_window_
, 0, 0, size_
.width(),
483 size_
.height(), 0, CopyFromParent
, InputOutput
,
484 CopyFromParent
, 0, nullptr);
485 XMapWindow(g_display
, window_
);
487 ui::PlatformEventSource
* event_source
=
488 ui::PlatformEventSource::GetInstance();
489 // Can be nullptr in tests, when we don't care about Exposes.
491 XSelectInput(g_display
, window_
, ExposureMask
);
492 ui::PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this);
496 gfx::AcceleratedWidget window_for_vsync
= window_
;
498 if (g_glx_oml_sync_control_supported
)
499 vsync_provider_
.reset(new OMLSyncControlVSyncProvider(window_for_vsync
));
500 else if (g_glx_sgi_video_sync_supported
)
501 vsync_provider_
.reset(new SGIVideoSyncVSyncProvider(window_for_vsync
));
506 void NativeViewGLSurfaceGLX::Destroy() {
508 ui::PlatformEventSource
* event_source
=
509 ui::PlatformEventSource::GetInstance();
511 event_source
->RemovePlatformEventDispatcher(this);
512 XDestroyWindow(g_display
, window_
);
518 bool NativeViewGLSurfaceGLX::CanDispatchEvent(const ui::PlatformEvent
& event
) {
519 return event
->type
== Expose
&& event
->xexpose
.window
== window_
;
522 uint32_t NativeViewGLSurfaceGLX::DispatchEvent(const ui::PlatformEvent
& event
) {
523 XEvent forwarded_event
= *event
;
524 forwarded_event
.xexpose
.window
= parent_window_
;
525 XSendEvent(g_display
, parent_window_
, False
, ExposureMask
,
528 return ui::POST_DISPATCH_STOP_PROPAGATION
;
531 bool NativeViewGLSurfaceGLX::Resize(const gfx::Size
& size
) {
534 XResizeWindow(g_display
, window_
, size
.width(), size
.height());
539 bool NativeViewGLSurfaceGLX::IsOffscreen() {
543 gfx::SwapResult
NativeViewGLSurfaceGLX::SwapBuffers() {
544 TRACE_EVENT2("gpu", "NativeViewGLSurfaceGLX:RealSwapBuffers",
545 "width", GetSize().width(),
546 "height", GetSize().height());
548 glXSwapBuffers(g_display
, GetDrawableHandle());
549 return gfx::SwapResult::SWAP_ACK
;
552 gfx::Size
NativeViewGLSurfaceGLX::GetSize() {
556 void* NativeViewGLSurfaceGLX::GetHandle() {
557 return reinterpret_cast<void*>(GetDrawableHandle());
560 bool NativeViewGLSurfaceGLX::SupportsPostSubBuffer() {
561 return gfx::g_driver_glx
.ext
.b_GLX_MESA_copy_sub_buffer
;
564 void* NativeViewGLSurfaceGLX::GetConfig() {
566 config_
= GLSurfaceGLX::GetConfig(window_
);
570 gfx::SwapResult
NativeViewGLSurfaceGLX::PostSubBuffer(int x
,
574 DCHECK(gfx::g_driver_glx
.ext
.b_GLX_MESA_copy_sub_buffer
);
575 glXCopySubBufferMESA(g_display
, GetDrawableHandle(), x
, y
, width
, height
);
576 return gfx::SwapResult::SWAP_ACK
;
579 VSyncProvider
* NativeViewGLSurfaceGLX::GetVSyncProvider() {
580 return vsync_provider_
.get();
583 NativeViewGLSurfaceGLX::~NativeViewGLSurfaceGLX() {
587 UnmappedNativeViewGLSurfaceGLX::UnmappedNativeViewGLSurfaceGLX(
588 const gfx::Size
& size
)
589 : size_(size
), config_(nullptr), window_(0) {
590 // Ensure that we don't create a window with zero size.
591 if (size_
.GetArea() == 0)
595 bool UnmappedNativeViewGLSurfaceGLX::Initialize() {
598 gfx::AcceleratedWidget parent_window
=
599 RootWindow(g_display
, DefaultScreen(g_display
));
601 // We create a window with CopyFromParent visual so that we have the same
602 // visual as NativeViewGLSurfaceGLX (i.e. same GLXFBConfig), to ensure
603 // contexts are compatible and can be made current with either.
604 window_
= XCreateWindow(g_display
, parent_window
, 0, 0, size_
.width(),
605 size_
.height(), 0, CopyFromParent
, InputOutput
,
606 CopyFromParent
, 0, nullptr);
610 void UnmappedNativeViewGLSurfaceGLX::Destroy() {
613 XDestroyWindow(g_display
, window_
);
618 bool UnmappedNativeViewGLSurfaceGLX::IsOffscreen() {
622 gfx::SwapResult
UnmappedNativeViewGLSurfaceGLX::SwapBuffers() {
623 NOTREACHED() << "Attempted to call SwapBuffers on an unmapped window.";
624 return gfx::SwapResult::SWAP_FAILED
;
627 gfx::Size
UnmappedNativeViewGLSurfaceGLX::GetSize() {
631 void* UnmappedNativeViewGLSurfaceGLX::GetHandle() {
632 return reinterpret_cast<void*>(window_
);
635 void* UnmappedNativeViewGLSurfaceGLX::GetConfig() {
637 config_
= GLSurfaceGLX::GetConfig(window_
);
641 UnmappedNativeViewGLSurfaceGLX::~UnmappedNativeViewGLSurfaceGLX() {