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/debug/trace_event.h"
13 #include "base/lazy_instance.h"
14 #include "base/logging.h"
15 #include "base/memory/scoped_ptr.h"
16 #include "base/memory/weak_ptr.h"
17 #include "base/message_loop/message_loop.h"
18 #include "base/synchronization/cancellation_flag.h"
19 #include "base/synchronization/lock.h"
20 #include "base/threading/non_thread_safe.h"
21 #include "base/threading/thread.h"
22 #include "base/time/time.h"
23 #include "third_party/mesa/src/include/GL/osmesa.h"
24 #include "ui/gfx/x/x11_types.h"
25 #include "ui/gl/gl_bindings.h"
26 #include "ui/gl/gl_implementation.h"
27 #include "ui/gl/sync_control_vsync_provider.h"
33 // scoped_ptr functor for XFree(). Use as follows:
34 // scoped_ptr<XVisualInfo, ScopedPtrXFree> foo(...);
35 // where "XVisualInfo" is any X type that is freed with XFree.
36 struct ScopedPtrXFree
{
37 void operator()(void* x
) const {
42 Display
* g_display
= NULL
;
43 const char* g_glx_extensions
= NULL
;
44 bool g_glx_context_create
= false;
45 bool g_glx_create_context_robustness_supported
= false;
46 bool g_glx_texture_from_pixmap_supported
= false;
47 bool g_glx_oml_sync_control_supported
= false;
49 // Track support of glXGetMscRateOML separately from GLX_OML_sync_control as a
50 // whole since on some platforms (e.g. crosbug.com/34585), glXGetMscRateOML
51 // always fails even though GLX_OML_sync_control is reported as being supported.
52 bool g_glx_get_msc_rate_oml_supported
= false;
54 bool g_glx_sgi_video_sync_supported
= false;
56 class OMLSyncControlVSyncProvider
57 : public gfx::SyncControlVSyncProvider
{
59 explicit OMLSyncControlVSyncProvider(gfx::AcceleratedWidget window
)
60 : SyncControlVSyncProvider(),
64 virtual ~OMLSyncControlVSyncProvider() { }
67 virtual bool GetSyncValues(int64
* system_time
,
68 int64
* media_stream_counter
,
69 int64
* swap_buffer_counter
) OVERRIDE
{
70 return glXGetSyncValuesOML(g_display
, window_
, system_time
,
71 media_stream_counter
, swap_buffer_counter
);
74 virtual bool GetMscRate(int32
* numerator
, int32
* denominator
) OVERRIDE
{
75 if (!g_glx_get_msc_rate_oml_supported
)
78 if (!glXGetMscRateOML(g_display
, window_
, numerator
, denominator
)) {
79 // Once glXGetMscRateOML has been found to fail, don't try again,
80 // since each failing call may spew an error message.
81 g_glx_get_msc_rate_oml_supported
= false;
91 DISALLOW_COPY_AND_ASSIGN(OMLSyncControlVSyncProvider
);
94 class SGIVideoSyncThread
95 : public base::Thread
,
96 public base::NonThreadSafe
,
97 public base::RefCounted
<SGIVideoSyncThread
> {
99 static scoped_refptr
<SGIVideoSyncThread
> Create() {
100 if (!g_video_sync_thread
) {
101 g_video_sync_thread
= new SGIVideoSyncThread();
102 g_video_sync_thread
->Start();
104 return g_video_sync_thread
;
108 friend class base::RefCounted
<SGIVideoSyncThread
>;
110 SGIVideoSyncThread() : base::Thread("SGI_video_sync") {
111 DCHECK(CalledOnValidThread());
114 virtual ~SGIVideoSyncThread() {
115 DCHECK(CalledOnValidThread());
116 g_video_sync_thread
= NULL
;
120 static SGIVideoSyncThread
* g_video_sync_thread
;
122 DISALLOW_COPY_AND_ASSIGN(SGIVideoSyncThread
);
125 class SGIVideoSyncProviderThreadShim
{
127 explicit SGIVideoSyncProviderThreadShim(XID window
)
130 message_loop_(base::MessageLoopProxy::current()),
131 cancel_vsync_flag_(),
133 // This ensures that creation of |window_| has occured when this shim
134 // is executing in the same process as the call to create |window_|.
135 XSync(g_display
, False
);
138 virtual ~SGIVideoSyncProviderThreadShim() {
140 glXDestroyContext(display_
, context_
);
145 base::CancellationFlag
* cancel_vsync_flag() {
146 return &cancel_vsync_flag_
;
149 base::Lock
* vsync_lock() {
156 XWindowAttributes attributes
;
157 if (!XGetWindowAttributes(display_
, window_
, &attributes
)) {
158 LOG(ERROR
) << "XGetWindowAttributes failed for window " <<
163 XVisualInfo visual_info_template
;
164 visual_info_template
.visualid
= XVisualIDFromVisual(attributes
.visual
);
166 int visual_info_count
= 0;
167 scoped_ptr
<XVisualInfo
, ScopedPtrXFree
> visual_info_list(
168 XGetVisualInfo(display_
, VisualIDMask
,
169 &visual_info_template
, &visual_info_count
));
171 DCHECK(visual_info_list
.get());
172 if (visual_info_count
== 0) {
173 LOG(ERROR
) << "No visual info for visual ID.";
177 context_
= glXCreateContext(display_
, visual_info_list
.get(), NULL
, True
);
179 DCHECK(NULL
!= context_
);
182 void GetVSyncParameters(const VSyncProvider::UpdateVSyncCallback
& callback
) {
185 // Don't allow |window_| destruction while we're probing vsync.
186 base::AutoLock
locked(vsync_lock_
);
188 if (!context_
|| cancel_vsync_flag_
.IsSet())
191 glXMakeCurrent(display_
, window_
, context_
);
193 unsigned int retrace_count
= 0;
194 if (glXWaitVideoSyncSGI(1, 0, &retrace_count
) != 0)
197 TRACE_EVENT_INSTANT0("gpu", "vblank", TRACE_EVENT_SCOPE_THREAD
);
198 now
= base::TimeTicks::HighResNow();
200 glXMakeCurrent(display_
, 0, 0);
203 const base::TimeDelta kDefaultInterval
=
204 base::TimeDelta::FromSeconds(1) / 60;
206 message_loop_
->PostTask(
207 FROM_HERE
, base::Bind(callback
, now
, kDefaultInterval
));
211 // For initialization of display_ in GLSurface::InitializeOneOff before
212 // the sandbox goes up.
213 friend class gfx::GLSurfaceGLX
;
215 static Display
* display_
;
220 scoped_refptr
<base::MessageLoopProxy
> message_loop_
;
222 base::CancellationFlag cancel_vsync_flag_
;
223 base::Lock vsync_lock_
;
225 DISALLOW_COPY_AND_ASSIGN(SGIVideoSyncProviderThreadShim
);
228 class SGIVideoSyncVSyncProvider
229 : public gfx::VSyncProvider
,
230 public base::SupportsWeakPtr
<SGIVideoSyncVSyncProvider
> {
232 explicit SGIVideoSyncVSyncProvider(gfx::AcceleratedWidget window
)
233 : vsync_thread_(SGIVideoSyncThread::Create()),
234 shim_(new SGIVideoSyncProviderThreadShim(window
)),
235 cancel_vsync_flag_(shim_
->cancel_vsync_flag()),
236 vsync_lock_(shim_
->vsync_lock()) {
237 vsync_thread_
->message_loop()->PostTask(
239 base::Bind(&SGIVideoSyncProviderThreadShim::Initialize
,
240 base::Unretained(shim_
.get())));
243 virtual ~SGIVideoSyncVSyncProvider() {
245 base::AutoLock
locked(*vsync_lock_
);
246 cancel_vsync_flag_
->Set();
249 // Hand-off |shim_| to be deleted on the |vsync_thread_|.
250 vsync_thread_
->message_loop()->DeleteSoon(
255 virtual void GetVSyncParameters(
256 const VSyncProvider::UpdateVSyncCallback
& callback
) OVERRIDE
{
257 // Only one outstanding request per surface.
258 if (!pending_callback_
) {
259 pending_callback_
.reset(
260 new VSyncProvider::UpdateVSyncCallback(callback
));
261 vsync_thread_
->message_loop()->PostTask(
263 base::Bind(&SGIVideoSyncProviderThreadShim::GetVSyncParameters
,
264 base::Unretained(shim_
.get()),
266 &SGIVideoSyncVSyncProvider::PendingCallbackRunner
,
272 void PendingCallbackRunner(const base::TimeTicks timebase
,
273 const base::TimeDelta interval
) {
274 DCHECK(pending_callback_
);
275 pending_callback_
->Run(timebase
, interval
);
276 pending_callback_
.reset();
279 scoped_refptr
<SGIVideoSyncThread
> vsync_thread_
;
281 // Thread shim through which the sync provider is accessed on |vsync_thread_|.
282 scoped_ptr
<SGIVideoSyncProviderThreadShim
> shim_
;
284 scoped_ptr
<VSyncProvider::UpdateVSyncCallback
> pending_callback_
;
286 // Raw pointers to sync primitives owned by the shim_.
287 // These will only be referenced before we post a task to destroy
288 // the shim_, so they are safe to access.
289 base::CancellationFlag
* cancel_vsync_flag_
;
290 base::Lock
* vsync_lock_
;
292 DISALLOW_COPY_AND_ASSIGN(SGIVideoSyncVSyncProvider
);
295 SGIVideoSyncThread
* SGIVideoSyncThread::g_video_sync_thread
= NULL
;
297 // In order to take advantage of GLX_SGI_video_sync, we need a display
298 // for use on a separate thread. We must allocate this before the sandbox
299 // goes up (rather than on-demand when we start the thread).
300 Display
* SGIVideoSyncProviderThreadShim::display_
= NULL
;
302 #if defined(TOOLKIT_GTK)
303 // A mechanism for forwarding XExpose events from one window to another.
304 // Because in the workaround for http://crbug.com/145600 the child window
305 // is placed on top of the parent window, only the child window will receive
306 // all expose events. These need to be forwared to the parent window to inform
307 // it that it should paint.
308 class XExposeEventForwarder
: public base::MessagePumpObserver
{
310 XExposeEventForwarder() {}
311 virtual ~XExposeEventForwarder() {
312 DCHECK(child_to_parent_map_
.empty());
314 void AddParentChildPair(gfx::AcceleratedWidget parent_window
,
315 gfx::AcceleratedWidget child_window
) {
316 if (child_to_parent_map_
.empty())
317 base::MessagePumpX11::Current()->AddObserver(this);
319 DCHECK(child_to_parent_map_
.find(child_window
) ==
320 child_to_parent_map_
.end());
321 child_to_parent_map_
.insert(std::make_pair(
322 child_window
, parent_window
));
324 void RemoveParentChildPair(gfx::AcceleratedWidget parent_window
,
325 gfx::AcceleratedWidget child_window
) {
326 DCHECK(child_to_parent_map_
.find(child_window
) !=
327 child_to_parent_map_
.end());
328 child_to_parent_map_
.erase(child_window
);
330 if (child_to_parent_map_
.empty())
331 base::MessagePumpX11::Current()->RemoveObserver(this);
335 virtual base::EventStatus
WillProcessEvent (
336 const base::NativeEvent
& xevent
) OVERRIDE
{
337 if (xevent
->type
!= Expose
)
338 return base::EVENT_CONTINUE
;
340 WindowMap::const_iterator found
= child_to_parent_map_
.find(
341 xevent
->xexpose
.window
);
342 if (found
== child_to_parent_map_
.end())
343 return base::EVENT_CONTINUE
;
345 gfx::AcceleratedWidget target_window
= found
->second
;
346 XEvent forwarded_event
= *xevent
;
347 forwarded_event
.xexpose
.window
= target_window
;
348 XSendEvent(g_display
, target_window
, False
, ExposureMask
,
350 return base::EVENT_CONTINUE
;
352 virtual void DidProcessEvent(const base::NativeEvent
& xevent
) OVERRIDE
{
355 typedef std::map
<gfx::AcceleratedWidget
, gfx::AcceleratedWidget
> WindowMap
;
356 WindowMap child_to_parent_map_
;
358 DISALLOW_COPY_AND_ASSIGN(XExposeEventForwarder
);
361 static base::LazyInstance
<XExposeEventForwarder
> g_xexpose_event_forwarder
=
362 LAZY_INSTANCE_INITIALIZER
;
364 // Do not use this workaround when running in test harnesses that do not have
365 // a message loop or do not have a TYPE_GPU message loop.
366 bool g_create_child_windows
= false;
371 GLSurfaceGLX::GLSurfaceGLX() {}
373 bool GLSurfaceGLX::InitializeOneOff() {
374 static bool initialized
= false;
378 // http://crbug.com/245466
379 setenv("force_s3tc_enable", "true", 1);
381 // SGIVideoSyncProviderShim (if instantiated) will issue X commands on
385 #if defined(TOOLKIT_GTK)
386 // Be sure to use the X display handle and not the GTK display handle if this
387 // is the GPU process.
388 g_create_child_windows
=
389 base::MessageLoop::current() &&
390 base::MessageLoop::current()->type() == base::MessageLoop::TYPE_GPU
;
392 if (g_create_child_windows
)
393 g_display
= base::MessagePumpX11::GetDefaultXDisplay();
395 g_display
= base::MessagePumpForUI::GetDefaultXDisplay();
397 g_display
= base::MessagePumpForUI::GetDefaultXDisplay();
401 LOG(ERROR
) << "XOpenDisplay failed.";
406 if (!glXQueryVersion(g_display
, &major
, &minor
)) {
407 LOG(ERROR
) << "glxQueryVersion failed";
411 if (major
== 1 && minor
< 3) {
412 LOG(ERROR
) << "GLX 1.3 or later is required.";
416 g_glx_extensions
= glXQueryExtensionsString(g_display
, 0);
417 g_glx_context_create
=
418 HasGLXExtension("GLX_ARB_create_context");
419 g_glx_create_context_robustness_supported
=
420 HasGLXExtension("GLX_ARB_create_context_robustness");
421 g_glx_texture_from_pixmap_supported
=
422 HasGLXExtension("GLX_EXT_texture_from_pixmap");
423 g_glx_oml_sync_control_supported
=
424 HasGLXExtension("GLX_OML_sync_control");
425 g_glx_get_msc_rate_oml_supported
= g_glx_oml_sync_control_supported
;
426 g_glx_sgi_video_sync_supported
=
427 HasGLXExtension("GLX_SGI_video_sync");
429 if (!g_glx_get_msc_rate_oml_supported
&& g_glx_sgi_video_sync_supported
)
430 SGIVideoSyncProviderThreadShim::display_
= XOpenDisplay(NULL
);
437 const char* GLSurfaceGLX::GetGLXExtensions() {
438 return g_glx_extensions
;
442 bool GLSurfaceGLX::HasGLXExtension(const char* name
) {
443 return ExtensionsContain(GetGLXExtensions(), name
);
447 bool GLSurfaceGLX::IsCreateContextSupported() {
448 return g_glx_context_create
;
452 bool GLSurfaceGLX::IsCreateContextRobustnessSupported() {
453 return g_glx_create_context_robustness_supported
;
457 bool GLSurfaceGLX::IsTextureFromPixmapSupported() {
458 return g_glx_texture_from_pixmap_supported
;
462 bool GLSurfaceGLX::IsOMLSyncControlSupported() {
463 return g_glx_oml_sync_control_supported
;
466 void* GLSurfaceGLX::GetDisplay() {
470 GLSurfaceGLX::~GLSurfaceGLX() {}
472 #if defined(TOOLKIT_GTK)
473 bool NativeViewGLSurfaceGLX::SetBackbufferAllocation(bool allocated
) {
474 backbuffer_allocated_
= allocated
;
475 AdjustBufferAllocation();
479 void NativeViewGLSurfaceGLX::SetFrontbufferAllocation(bool allocated
) {
480 frontbuffer_allocated_
= allocated
;
481 AdjustBufferAllocation();
484 void NativeViewGLSurfaceGLX::AdjustBufferAllocation() {
485 if (!g_create_child_windows
)
488 if (frontbuffer_allocated_
|| backbuffer_allocated_
)
491 DestroyChildWindow();
494 void NativeViewGLSurfaceGLX::CreateChildWindow() {
495 DCHECK(g_create_child_windows
);
500 XSetWindowAttributes set_window_attributes
;
501 set_window_attributes
.event_mask
= ExposureMask
;
502 child_window_
= XCreateWindow(
503 g_display
, parent_window_
, 0, 0, size_
.width(), size_
.height(), 0,
504 CopyFromParent
, InputOutput
, CopyFromParent
, CWEventMask
,
505 &set_window_attributes
);
506 g_xexpose_event_forwarder
.Pointer()->AddParentChildPair(
507 parent_window_
, child_window_
);
509 XMapWindow(g_display
, child_window_
);
513 void NativeViewGLSurfaceGLX::DestroyChildWindow() {
517 g_xexpose_event_forwarder
.Pointer()->RemoveParentChildPair(
518 parent_window_
, child_window_
);
519 XDestroyWindow(g_display
, child_window_
);
525 NativeViewGLSurfaceGLX::NativeViewGLSurfaceGLX(gfx::AcceleratedWidget window
)
526 : parent_window_(window
),
527 #if defined(TOOLKIT_GTK)
530 backbuffer_allocated_(true),
531 frontbuffer_allocated_(true),
536 gfx::AcceleratedWidget
NativeViewGLSurfaceGLX::GetDrawableHandle() const {
537 #if defined(TOOLKIT_GTK)
538 if (g_create_child_windows
) {
540 return child_window_
;
541 return dummy_window_
;
544 return parent_window_
;
547 bool NativeViewGLSurfaceGLX::Initialize() {
548 XWindowAttributes attributes
;
549 if (!XGetWindowAttributes(g_display
, parent_window_
, &attributes
)) {
550 LOG(ERROR
) << "XGetWindowAttributes failed for window " << parent_window_
554 size_
= gfx::Size(attributes
.width
, attributes
.height
);
556 gfx::AcceleratedWidget window_for_vsync
= parent_window_
;
558 #if defined(TOOLKIT_GTK)
559 if (g_create_child_windows
) {
560 dummy_window_
= XCreateWindow(
562 RootWindow(g_display
, XScreenNumberOfScreen(attributes
.screen
)),
563 0, 0, 1, 1, 0, CopyFromParent
, InputOutput
, attributes
.visual
, 0, NULL
);
564 window_for_vsync
= dummy_window_
;
569 if (g_glx_oml_sync_control_supported
)
570 vsync_provider_
.reset(new OMLSyncControlVSyncProvider(window_for_vsync
));
571 else if (g_glx_sgi_video_sync_supported
)
572 vsync_provider_
.reset(new SGIVideoSyncVSyncProvider(window_for_vsync
));
577 void NativeViewGLSurfaceGLX::Destroy() {
578 #if defined(TOOLKIT_GTK)
579 DestroyChildWindow();
581 XDestroyWindow(g_display
, dummy_window_
);
586 bool NativeViewGLSurfaceGLX::Resize(const gfx::Size
& size
) {
587 #if defined(TOOLKIT_GTK)
589 XResizeWindow(g_display
, child_window_
, size
.width(), size
.height());
597 bool NativeViewGLSurfaceGLX::IsOffscreen() {
601 bool NativeViewGLSurfaceGLX::SwapBuffers() {
602 TRACE_EVENT2("gpu", "NativeViewGLSurfaceGLX:RealSwapBuffers",
603 "width", GetSize().width(),
604 "height", GetSize().height());
606 glXSwapBuffers(g_display
, GetDrawableHandle());
610 gfx::Size
NativeViewGLSurfaceGLX::GetSize() {
614 void* NativeViewGLSurfaceGLX::GetHandle() {
615 return reinterpret_cast<void*>(GetDrawableHandle());
618 bool NativeViewGLSurfaceGLX::SupportsPostSubBuffer() {
619 return gfx::g_driver_glx
.ext
.b_GLX_MESA_copy_sub_buffer
;
622 void* NativeViewGLSurfaceGLX::GetConfig() {
624 // This code path is expensive, but we only take it when
625 // attempting to use GLX_ARB_create_context_robustness, in which
626 // case we need a GLXFBConfig for the window in order to create a
629 // TODO(kbr): this is not a reliable code path. On platforms which
630 // support it, we should use glXChooseFBConfig in the browser
631 // process to choose the FBConfig and from there the X Visual to
632 // use when creating the window in the first place. Then we can
633 // pass that FBConfig down rather than attempting to reconstitute
636 XWindowAttributes attributes
;
637 if (!XGetWindowAttributes(
641 LOG(ERROR
) << "XGetWindowAttributes failed for window " <<
642 parent_window_
<< ".";
646 int visual_id
= XVisualIDFromVisual(attributes
.visual
);
648 int num_elements
= 0;
649 scoped_ptr
<GLXFBConfig
, ScopedPtrXFree
> configs(
650 glXGetFBConfigs(g_display
,
651 DefaultScreen(g_display
),
653 if (!configs
.get()) {
654 LOG(ERROR
) << "glXGetFBConfigs failed.";
658 LOG(ERROR
) << "glXGetFBConfigs returned 0 elements.";
663 for (i
= 0; i
< num_elements
; ++i
) {
665 if (glXGetFBConfigAttrib(
666 g_display
, configs
.get()[i
], GLX_VISUAL_ID
, &value
)) {
667 LOG(ERROR
) << "glXGetFBConfigAttrib failed.";
670 if (value
== visual_id
) {
676 config_
= configs
.get()[i
];
683 bool NativeViewGLSurfaceGLX::PostSubBuffer(
684 int x
, int y
, int width
, int height
) {
685 DCHECK(gfx::g_driver_glx
.ext
.b_GLX_MESA_copy_sub_buffer
);
686 glXCopySubBufferMESA(g_display
, GetDrawableHandle(), x
, y
, width
, height
);
690 VSyncProvider
* NativeViewGLSurfaceGLX::GetVSyncProvider() {
691 return vsync_provider_
.get();
694 NativeViewGLSurfaceGLX::NativeViewGLSurfaceGLX()
696 #if defined(TOOLKIT_GTK)
699 backbuffer_allocated_(true),
700 frontbuffer_allocated_(true),
705 NativeViewGLSurfaceGLX::~NativeViewGLSurfaceGLX() {
709 PbufferGLSurfaceGLX::PbufferGLSurfaceGLX(const gfx::Size
& size
)
715 bool PbufferGLSurfaceGLX::Initialize() {
718 static const int config_attributes
[] = {
724 GLX_RENDER_TYPE
, GLX_RGBA_BIT
,
725 GLX_DRAWABLE_TYPE
, GLX_PBUFFER_BIT
,
726 GLX_DOUBLEBUFFER
, False
,
730 int num_elements
= 0;
731 scoped_ptr
<GLXFBConfig
, ScopedPtrXFree
> configs(
732 glXChooseFBConfig(g_display
,
733 DefaultScreen(g_display
),
736 if (!configs
.get()) {
737 LOG(ERROR
) << "glXChooseFBConfig failed.";
741 LOG(ERROR
) << "glXChooseFBConfig returned 0 elements.";
745 config_
= configs
.get()[0];
747 const int pbuffer_attributes
[] = {
748 GLX_PBUFFER_WIDTH
, size_
.width(),
749 GLX_PBUFFER_HEIGHT
, size_
.height(),
752 pbuffer_
= glXCreatePbuffer(g_display
,
753 static_cast<GLXFBConfig
>(config_
),
757 LOG(ERROR
) << "glXCreatePbuffer failed.";
764 void PbufferGLSurfaceGLX::Destroy() {
766 glXDestroyPbuffer(g_display
, pbuffer_
);
773 bool PbufferGLSurfaceGLX::IsOffscreen() {
777 bool PbufferGLSurfaceGLX::SwapBuffers() {
778 NOTREACHED() << "Attempted to call SwapBuffers on a pbuffer.";
782 gfx::Size
PbufferGLSurfaceGLX::GetSize() {
786 void* PbufferGLSurfaceGLX::GetHandle() {
787 return reinterpret_cast<void*>(pbuffer_
);
790 void* PbufferGLSurfaceGLX::GetConfig() {
794 PbufferGLSurfaceGLX::~PbufferGLSurfaceGLX() {