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_malloc<XVisualInfo, ScopedPtrXFree> foo(...);
35 // where "XVisualInfo" is any X type that is freed with XFree.
36 class ScopedPtrXFree
{
38 void operator()(void* x
) const {
43 Display
* g_display
= NULL
;
44 const char* g_glx_extensions
= NULL
;
45 bool g_glx_context_create
= false;
46 bool g_glx_create_context_robustness_supported
= false;
47 bool g_glx_texture_from_pixmap_supported
= false;
48 bool g_glx_oml_sync_control_supported
= false;
50 // Track support of glXGetMscRateOML separately from GLX_OML_sync_control as a
51 // whole since on some platforms (e.g. crosbug.com/34585), glXGetMscRateOML
52 // always fails even though GLX_OML_sync_control is reported as being supported.
53 bool g_glx_get_msc_rate_oml_supported
= false;
55 bool g_glx_sgi_video_sync_supported
= false;
57 class OMLSyncControlVSyncProvider
58 : public gfx::SyncControlVSyncProvider
{
60 explicit OMLSyncControlVSyncProvider(gfx::AcceleratedWidget window
)
61 : SyncControlVSyncProvider(),
65 virtual ~OMLSyncControlVSyncProvider() { }
68 virtual bool GetSyncValues(int64
* system_time
,
69 int64
* media_stream_counter
,
70 int64
* swap_buffer_counter
) OVERRIDE
{
71 return glXGetSyncValuesOML(g_display
, window_
, system_time
,
72 media_stream_counter
, swap_buffer_counter
);
75 virtual bool GetMscRate(int32
* numerator
, int32
* denominator
) OVERRIDE
{
76 if (!g_glx_get_msc_rate_oml_supported
)
79 if (!glXGetMscRateOML(g_display
, window_
, numerator
, denominator
)) {
80 // Once glXGetMscRateOML has been found to fail, don't try again,
81 // since each failing call may spew an error message.
82 g_glx_get_msc_rate_oml_supported
= false;
92 DISALLOW_COPY_AND_ASSIGN(OMLSyncControlVSyncProvider
);
95 class SGIVideoSyncThread
96 : public base::Thread
,
97 public base::NonThreadSafe
,
98 public base::RefCounted
<SGIVideoSyncThread
> {
100 static scoped_refptr
<SGIVideoSyncThread
> Create() {
101 if (!g_video_sync_thread
) {
102 g_video_sync_thread
= new SGIVideoSyncThread();
103 g_video_sync_thread
->Start();
105 return g_video_sync_thread
;
109 friend class base::RefCounted
<SGIVideoSyncThread
>;
111 SGIVideoSyncThread() : base::Thread("SGI_video_sync") {
112 DCHECK(CalledOnValidThread());
115 virtual ~SGIVideoSyncThread() {
116 DCHECK(CalledOnValidThread());
117 g_video_sync_thread
= NULL
;
121 static SGIVideoSyncThread
* g_video_sync_thread
;
123 DISALLOW_COPY_AND_ASSIGN(SGIVideoSyncThread
);
126 class SGIVideoSyncProviderThreadShim
{
128 explicit SGIVideoSyncProviderThreadShim(XID window
)
131 message_loop_(base::MessageLoopProxy::current()),
132 cancel_vsync_flag_(),
134 // This ensures that creation of |window_| has occured when this shim
135 // is executing in the same process as the call to create |window_|.
136 XSync(g_display
, False
);
139 virtual ~SGIVideoSyncProviderThreadShim() {
141 glXDestroyContext(display_
, context_
);
146 base::CancellationFlag
* cancel_vsync_flag() {
147 return &cancel_vsync_flag_
;
150 base::Lock
* vsync_lock() {
157 XWindowAttributes attributes
;
158 if (!XGetWindowAttributes(display_
, window_
, &attributes
)) {
159 LOG(ERROR
) << "XGetWindowAttributes failed for window " <<
164 XVisualInfo visual_info_template
;
165 visual_info_template
.visualid
= XVisualIDFromVisual(attributes
.visual
);
167 int visual_info_count
= 0;
168 scoped_ptr_malloc
<XVisualInfo
, ScopedPtrXFree
> visual_info_list(
169 XGetVisualInfo(display_
, VisualIDMask
,
170 &visual_info_template
, &visual_info_count
));
172 DCHECK(visual_info_list
.get());
173 if (visual_info_count
== 0) {
174 LOG(ERROR
) << "No visual info for visual ID.";
178 context_
= glXCreateContext(display_
, visual_info_list
.get(), NULL
, True
);
180 DCHECK(NULL
!= context_
);
183 void GetVSyncParameters(const VSyncProvider::UpdateVSyncCallback
& callback
) {
186 // Don't allow |window_| destruction while we're probing vsync.
187 base::AutoLock
locked(vsync_lock_
);
189 if (!context_
|| cancel_vsync_flag_
.IsSet())
192 glXMakeCurrent(display_
, window_
, context_
);
194 unsigned int retrace_count
= 0;
195 if (glXWaitVideoSyncSGI(1, 0, &retrace_count
) != 0)
198 TRACE_EVENT_INSTANT0("gpu", "vblank", TRACE_EVENT_SCOPE_THREAD
);
199 now
= base::TimeTicks::HighResNow();
201 glXMakeCurrent(display_
, 0, 0);
204 const base::TimeDelta kDefaultInterval
=
205 base::TimeDelta::FromSeconds(1) / 60;
207 message_loop_
->PostTask(
208 FROM_HERE
, base::Bind(callback
, now
, kDefaultInterval
));
212 // For initialization of display_ in GLSurface::InitializeOneOff before
213 // the sandbox goes up.
214 friend class gfx::GLSurfaceGLX
;
216 static Display
* display_
;
221 scoped_refptr
<base::MessageLoopProxy
> message_loop_
;
223 base::CancellationFlag cancel_vsync_flag_
;
224 base::Lock vsync_lock_
;
226 DISALLOW_COPY_AND_ASSIGN(SGIVideoSyncProviderThreadShim
);
229 class SGIVideoSyncVSyncProvider
230 : public gfx::VSyncProvider
,
231 public base::SupportsWeakPtr
<SGIVideoSyncVSyncProvider
> {
233 explicit SGIVideoSyncVSyncProvider(gfx::AcceleratedWidget window
)
234 : vsync_thread_(SGIVideoSyncThread::Create()),
235 shim_(new SGIVideoSyncProviderThreadShim(window
)),
236 cancel_vsync_flag_(shim_
->cancel_vsync_flag()),
237 vsync_lock_(shim_
->vsync_lock()) {
238 vsync_thread_
->message_loop()->PostTask(
240 base::Bind(&SGIVideoSyncProviderThreadShim::Initialize
,
241 base::Unretained(shim_
.get())));
244 virtual ~SGIVideoSyncVSyncProvider() {
246 base::AutoLock
locked(*vsync_lock_
);
247 cancel_vsync_flag_
->Set();
250 // Hand-off |shim_| to be deleted on the |vsync_thread_|.
251 vsync_thread_
->message_loop()->DeleteSoon(
256 virtual void GetVSyncParameters(
257 const VSyncProvider::UpdateVSyncCallback
& callback
) OVERRIDE
{
258 // Only one outstanding request per surface.
259 if (!pending_callback_
) {
260 pending_callback_
.reset(
261 new VSyncProvider::UpdateVSyncCallback(callback
));
262 vsync_thread_
->message_loop()->PostTask(
264 base::Bind(&SGIVideoSyncProviderThreadShim::GetVSyncParameters
,
265 base::Unretained(shim_
.get()),
267 &SGIVideoSyncVSyncProvider::PendingCallbackRunner
,
273 void PendingCallbackRunner(const base::TimeTicks timebase
,
274 const base::TimeDelta interval
) {
275 DCHECK(pending_callback_
);
276 pending_callback_
->Run(timebase
, interval
);
277 pending_callback_
.reset();
280 scoped_refptr
<SGIVideoSyncThread
> vsync_thread_
;
282 // Thread shim through which the sync provider is accessed on |vsync_thread_|.
283 scoped_ptr
<SGIVideoSyncProviderThreadShim
> shim_
;
285 scoped_ptr
<VSyncProvider::UpdateVSyncCallback
> pending_callback_
;
287 // Raw pointers to sync primitives owned by the shim_.
288 // These will only be referenced before we post a task to destroy
289 // the shim_, so they are safe to access.
290 base::CancellationFlag
* cancel_vsync_flag_
;
291 base::Lock
* vsync_lock_
;
293 DISALLOW_COPY_AND_ASSIGN(SGIVideoSyncVSyncProvider
);
296 SGIVideoSyncThread
* SGIVideoSyncThread::g_video_sync_thread
= NULL
;
298 // In order to take advantage of GLX_SGI_video_sync, we need a display
299 // for use on a separate thread. We must allocate this before the sandbox
300 // goes up (rather than on-demand when we start the thread).
301 Display
* SGIVideoSyncProviderThreadShim::display_
= NULL
;
303 #if defined(TOOLKIT_GTK)
304 // A mechanism for forwarding XExpose events from one window to another.
305 // Because in the workaround for http://crbug.com/145600 the child window
306 // is placed on top of the parent window, only the child window will receive
307 // all expose events. These need to be forwared to the parent window to inform
308 // it that it should paint.
309 class XExposeEventForwarder
: public base::MessagePumpObserver
{
311 XExposeEventForwarder() {}
312 virtual ~XExposeEventForwarder() {
313 DCHECK(child_to_parent_map_
.empty());
315 void AddParentChildPair(gfx::AcceleratedWidget parent_window
,
316 gfx::AcceleratedWidget child_window
) {
317 if (child_to_parent_map_
.empty())
318 base::MessagePumpX11::Current()->AddObserver(this);
320 DCHECK(child_to_parent_map_
.find(child_window
) ==
321 child_to_parent_map_
.end());
322 child_to_parent_map_
.insert(std::make_pair(
323 child_window
, parent_window
));
325 void RemoveParentChildPair(gfx::AcceleratedWidget parent_window
,
326 gfx::AcceleratedWidget child_window
) {
327 DCHECK(child_to_parent_map_
.find(child_window
) !=
328 child_to_parent_map_
.end());
329 child_to_parent_map_
.erase(child_window
);
331 if (child_to_parent_map_
.empty())
332 base::MessagePumpX11::Current()->RemoveObserver(this);
336 virtual base::EventStatus
WillProcessEvent (
337 const base::NativeEvent
& xevent
) OVERRIDE
{
338 if (xevent
->type
!= Expose
)
339 return base::EVENT_CONTINUE
;
341 WindowMap::const_iterator found
= child_to_parent_map_
.find(
342 xevent
->xexpose
.window
);
343 if (found
== child_to_parent_map_
.end())
344 return base::EVENT_CONTINUE
;
346 gfx::AcceleratedWidget target_window
= found
->second
;
347 XEvent forwarded_event
= *xevent
;
348 forwarded_event
.xexpose
.window
= target_window
;
349 XSendEvent(g_display
, target_window
, False
, ExposureMask
,
351 return base::EVENT_CONTINUE
;
353 virtual void DidProcessEvent(const base::NativeEvent
& xevent
) OVERRIDE
{
356 typedef std::map
<gfx::AcceleratedWidget
, gfx::AcceleratedWidget
> WindowMap
;
357 WindowMap child_to_parent_map_
;
359 DISALLOW_COPY_AND_ASSIGN(XExposeEventForwarder
);
362 static base::LazyInstance
<XExposeEventForwarder
> g_xexpose_event_forwarder
=
363 LAZY_INSTANCE_INITIALIZER
;
365 // Do not use this workaround when running in test harnesses that do not have
366 // a message loop or do not have a TYPE_GPU message loop.
367 bool g_create_child_windows
= false;
372 GLSurfaceGLX::GLSurfaceGLX() {}
374 bool GLSurfaceGLX::InitializeOneOff() {
375 static bool initialized
= false;
379 // http://crbug.com/245466
380 setenv("force_s3tc_enable", "true", 1);
382 // SGIVideoSyncProviderShim (if instantiated) will issue X commands on
386 #if defined(TOOLKIT_GTK)
387 // Be sure to use the X display handle and not the GTK display handle if this
388 // is the GPU process.
389 g_create_child_windows
=
390 base::MessageLoop::current() &&
391 base::MessageLoop::current()->type() == base::MessageLoop::TYPE_GPU
;
393 if (g_create_child_windows
)
394 g_display
= base::MessagePumpX11::GetDefaultXDisplay();
396 g_display
= base::MessagePumpForUI::GetDefaultXDisplay();
398 g_display
= base::MessagePumpForUI::GetDefaultXDisplay();
402 LOG(ERROR
) << "XOpenDisplay failed.";
407 if (!glXQueryVersion(g_display
, &major
, &minor
)) {
408 LOG(ERROR
) << "glxQueryVersion failed";
412 if (major
== 1 && minor
< 3) {
413 LOG(ERROR
) << "GLX 1.3 or later is required.";
417 g_glx_extensions
= glXQueryExtensionsString(g_display
, 0);
418 g_glx_context_create
=
419 HasGLXExtension("GLX_ARB_create_context");
420 g_glx_create_context_robustness_supported
=
421 HasGLXExtension("GLX_ARB_create_context_robustness");
422 g_glx_texture_from_pixmap_supported
=
423 HasGLXExtension("GLX_EXT_texture_from_pixmap");
424 g_glx_oml_sync_control_supported
=
425 HasGLXExtension("GLX_OML_sync_control");
426 g_glx_get_msc_rate_oml_supported
= g_glx_oml_sync_control_supported
;
427 g_glx_sgi_video_sync_supported
=
428 HasGLXExtension("GLX_SGI_video_sync");
430 if (!g_glx_get_msc_rate_oml_supported
&& g_glx_sgi_video_sync_supported
)
431 SGIVideoSyncProviderThreadShim::display_
= XOpenDisplay(NULL
);
438 const char* GLSurfaceGLX::GetGLXExtensions() {
439 return g_glx_extensions
;
443 bool GLSurfaceGLX::HasGLXExtension(const char* name
) {
444 return ExtensionsContain(GetGLXExtensions(), name
);
448 bool GLSurfaceGLX::IsCreateContextSupported() {
449 return g_glx_context_create
;
453 bool GLSurfaceGLX::IsCreateContextRobustnessSupported() {
454 return g_glx_create_context_robustness_supported
;
458 bool GLSurfaceGLX::IsTextureFromPixmapSupported() {
459 return g_glx_texture_from_pixmap_supported
;
463 bool GLSurfaceGLX::IsOMLSyncControlSupported() {
464 return g_glx_oml_sync_control_supported
;
467 void* GLSurfaceGLX::GetDisplay() {
471 GLSurfaceGLX::~GLSurfaceGLX() {}
473 #if defined(TOOLKIT_GTK)
474 bool NativeViewGLSurfaceGLX::SetBackbufferAllocation(bool allocated
) {
475 backbuffer_allocated_
= allocated
;
476 AdjustBufferAllocation();
480 void NativeViewGLSurfaceGLX::SetFrontbufferAllocation(bool allocated
) {
481 frontbuffer_allocated_
= allocated
;
482 AdjustBufferAllocation();
485 void NativeViewGLSurfaceGLX::AdjustBufferAllocation() {
486 if (!g_create_child_windows
)
489 if (frontbuffer_allocated_
|| backbuffer_allocated_
)
492 DestroyChildWindow();
495 void NativeViewGLSurfaceGLX::CreateChildWindow() {
496 DCHECK(g_create_child_windows
);
501 XSetWindowAttributes set_window_attributes
;
502 set_window_attributes
.event_mask
= ExposureMask
;
503 child_window_
= XCreateWindow(
504 g_display
, parent_window_
, 0, 0, size_
.width(), size_
.height(), 0,
505 CopyFromParent
, InputOutput
, CopyFromParent
, CWEventMask
,
506 &set_window_attributes
);
507 g_xexpose_event_forwarder
.Pointer()->AddParentChildPair(
508 parent_window_
, child_window_
);
510 XMapWindow(g_display
, child_window_
);
514 void NativeViewGLSurfaceGLX::DestroyChildWindow() {
518 g_xexpose_event_forwarder
.Pointer()->RemoveParentChildPair(
519 parent_window_
, child_window_
);
520 XDestroyWindow(g_display
, child_window_
);
526 NativeViewGLSurfaceGLX::NativeViewGLSurfaceGLX(gfx::AcceleratedWidget window
)
527 : parent_window_(window
),
528 #if defined(TOOLKIT_GTK)
531 backbuffer_allocated_(true),
532 frontbuffer_allocated_(true),
537 gfx::AcceleratedWidget
NativeViewGLSurfaceGLX::GetDrawableHandle() const {
538 #if defined(TOOLKIT_GTK)
539 if (g_create_child_windows
) {
541 return child_window_
;
542 return dummy_window_
;
545 return parent_window_
;
548 bool NativeViewGLSurfaceGLX::Initialize() {
549 XWindowAttributes attributes
;
550 if (!XGetWindowAttributes(g_display
, parent_window_
, &attributes
)) {
551 LOG(ERROR
) << "XGetWindowAttributes failed for window " << parent_window_
555 size_
= gfx::Size(attributes
.width
, attributes
.height
);
557 gfx::AcceleratedWidget window_for_vsync
= parent_window_
;
559 #if defined(TOOLKIT_GTK)
560 if (g_create_child_windows
) {
561 dummy_window_
= XCreateWindow(
563 RootWindow(g_display
, XScreenNumberOfScreen(attributes
.screen
)),
564 0, 0, 1, 1, 0, CopyFromParent
, InputOutput
, attributes
.visual
, 0, NULL
);
565 window_for_vsync
= dummy_window_
;
570 if (g_glx_oml_sync_control_supported
)
571 vsync_provider_
.reset(new OMLSyncControlVSyncProvider(window_for_vsync
));
572 else if (g_glx_sgi_video_sync_supported
)
573 vsync_provider_
.reset(new SGIVideoSyncVSyncProvider(window_for_vsync
));
578 void NativeViewGLSurfaceGLX::Destroy() {
579 #if defined(TOOLKIT_GTK)
580 DestroyChildWindow();
582 XDestroyWindow(g_display
, dummy_window_
);
587 bool NativeViewGLSurfaceGLX::Resize(const gfx::Size
& size
) {
588 #if defined(TOOLKIT_GTK)
590 XResizeWindow(g_display
, child_window_
, size
.width(), size
.height());
598 bool NativeViewGLSurfaceGLX::IsOffscreen() {
602 bool NativeViewGLSurfaceGLX::SwapBuffers() {
603 TRACE_EVENT2("gpu", "NativeViewGLSurfaceGLX:RealSwapBuffers",
604 "width", GetSize().width(),
605 "height", GetSize().height());
607 glXSwapBuffers(g_display
, GetDrawableHandle());
611 gfx::Size
NativeViewGLSurfaceGLX::GetSize() {
615 void* NativeViewGLSurfaceGLX::GetHandle() {
616 return reinterpret_cast<void*>(GetDrawableHandle());
619 std::string
NativeViewGLSurfaceGLX::GetExtensions() {
620 std::string extensions
= GLSurface::GetExtensions();
621 if (gfx::g_driver_glx
.ext
.b_GLX_MESA_copy_sub_buffer
) {
622 extensions
+= extensions
.empty() ? "" : " ";
623 extensions
+= "GL_CHROMIUM_post_sub_buffer";
628 void* NativeViewGLSurfaceGLX::GetConfig() {
630 // This code path is expensive, but we only take it when
631 // attempting to use GLX_ARB_create_context_robustness, in which
632 // case we need a GLXFBConfig for the window in order to create a
635 // TODO(kbr): this is not a reliable code path. On platforms which
636 // support it, we should use glXChooseFBConfig in the browser
637 // process to choose the FBConfig and from there the X Visual to
638 // use when creating the window in the first place. Then we can
639 // pass that FBConfig down rather than attempting to reconstitute
642 XWindowAttributes attributes
;
643 if (!XGetWindowAttributes(
647 LOG(ERROR
) << "XGetWindowAttributes failed for window " <<
648 parent_window_
<< ".";
652 int visual_id
= XVisualIDFromVisual(attributes
.visual
);
654 int num_elements
= 0;
655 scoped_ptr_malloc
<GLXFBConfig
, ScopedPtrXFree
> configs(
656 glXGetFBConfigs(g_display
,
657 DefaultScreen(g_display
),
659 if (!configs
.get()) {
660 LOG(ERROR
) << "glXGetFBConfigs failed.";
664 LOG(ERROR
) << "glXGetFBConfigs returned 0 elements.";
669 for (i
= 0; i
< num_elements
; ++i
) {
671 if (glXGetFBConfigAttrib(
672 g_display
, configs
.get()[i
], GLX_VISUAL_ID
, &value
)) {
673 LOG(ERROR
) << "glXGetFBConfigAttrib failed.";
676 if (value
== visual_id
) {
682 config_
= configs
.get()[i
];
689 bool NativeViewGLSurfaceGLX::PostSubBuffer(
690 int x
, int y
, int width
, int height
) {
691 DCHECK(gfx::g_driver_glx
.ext
.b_GLX_MESA_copy_sub_buffer
);
692 glXCopySubBufferMESA(g_display
, GetDrawableHandle(), x
, y
, width
, height
);
696 VSyncProvider
* NativeViewGLSurfaceGLX::GetVSyncProvider() {
697 return vsync_provider_
.get();
700 NativeViewGLSurfaceGLX::NativeViewGLSurfaceGLX()
702 #if defined(TOOLKIT_GTK)
705 backbuffer_allocated_(true),
706 frontbuffer_allocated_(true),
711 NativeViewGLSurfaceGLX::~NativeViewGLSurfaceGLX() {
715 PbufferGLSurfaceGLX::PbufferGLSurfaceGLX(const gfx::Size
& size
)
721 bool PbufferGLSurfaceGLX::Initialize() {
724 static const int config_attributes
[] = {
730 GLX_RENDER_TYPE
, GLX_RGBA_BIT
,
731 GLX_DRAWABLE_TYPE
, GLX_PBUFFER_BIT
,
732 GLX_DOUBLEBUFFER
, False
,
736 int num_elements
= 0;
737 scoped_ptr_malloc
<GLXFBConfig
, ScopedPtrXFree
> configs(
738 glXChooseFBConfig(g_display
,
739 DefaultScreen(g_display
),
742 if (!configs
.get()) {
743 LOG(ERROR
) << "glXChooseFBConfig failed.";
747 LOG(ERROR
) << "glXChooseFBConfig returned 0 elements.";
751 config_
= configs
.get()[0];
753 const int pbuffer_attributes
[] = {
754 GLX_PBUFFER_WIDTH
, size_
.width(),
755 GLX_PBUFFER_HEIGHT
, size_
.height(),
758 pbuffer_
= glXCreatePbuffer(g_display
,
759 static_cast<GLXFBConfig
>(config_
),
763 LOG(ERROR
) << "glXCreatePbuffer failed.";
770 void PbufferGLSurfaceGLX::Destroy() {
772 glXDestroyPbuffer(g_display
, pbuffer_
);
779 bool PbufferGLSurfaceGLX::IsOffscreen() {
783 bool PbufferGLSurfaceGLX::SwapBuffers() {
784 NOTREACHED() << "Attempted to call SwapBuffers on a pbuffer.";
788 gfx::Size
PbufferGLSurfaceGLX::GetSize() {
792 void* PbufferGLSurfaceGLX::GetHandle() {
793 return reinterpret_cast<void*>(pbuffer_
);
796 void* PbufferGLSurfaceGLX::GetConfig() {
800 PbufferGLSurfaceGLX::~PbufferGLSurfaceGLX() {