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/single_thread_task_runner.h"
19 #include "base/synchronization/cancellation_flag.h"
20 #include "base/synchronization/lock.h"
21 #include "base/thread_task_runner_handle.h"
22 #include "base/threading/non_thread_safe.h"
23 #include "base/threading/thread.h"
24 #include "base/time/time.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 // scoped_ptr functor for XFree(). Use as follows:
37 // scoped_ptr<XVisualInfo, ScopedPtrXFree> foo(...);
38 // where "XVisualInfo" is any X type that is freed with XFree.
39 struct ScopedPtrXFree
{
40 void operator()(void* x
) const {
45 Display
* g_display
= NULL
;
46 const char* g_glx_extensions
= NULL
;
47 bool g_glx_context_create
= false;
48 bool g_glx_create_context_robustness_supported
= false;
49 bool g_glx_texture_from_pixmap_supported
= false;
50 bool g_glx_oml_sync_control_supported
= false;
52 // Track support of glXGetMscRateOML separately from GLX_OML_sync_control as a
53 // whole since on some platforms (e.g. crosbug.com/34585), glXGetMscRateOML
54 // always fails even though GLX_OML_sync_control is reported as being supported.
55 bool g_glx_get_msc_rate_oml_supported
= false;
57 bool g_glx_sgi_video_sync_supported
= false;
59 class OMLSyncControlVSyncProvider
60 : public gfx::SyncControlVSyncProvider
{
62 explicit OMLSyncControlVSyncProvider(gfx::AcceleratedWidget window
)
63 : SyncControlVSyncProvider(),
67 virtual ~OMLSyncControlVSyncProvider() { }
70 virtual bool GetSyncValues(int64
* system_time
,
71 int64
* media_stream_counter
,
72 int64
* swap_buffer_counter
) OVERRIDE
{
73 return glXGetSyncValuesOML(g_display
, window_
, system_time
,
74 media_stream_counter
, swap_buffer_counter
);
77 virtual bool GetMscRate(int32
* numerator
, int32
* denominator
) OVERRIDE
{
78 if (!g_glx_get_msc_rate_oml_supported
)
81 if (!glXGetMscRateOML(g_display
, window_
, numerator
, denominator
)) {
82 // Once glXGetMscRateOML has been found to fail, don't try again,
83 // since each failing call may spew an error message.
84 g_glx_get_msc_rate_oml_supported
= false;
94 DISALLOW_COPY_AND_ASSIGN(OMLSyncControlVSyncProvider
);
97 class SGIVideoSyncThread
98 : public base::Thread
,
99 public base::NonThreadSafe
,
100 public base::RefCounted
<SGIVideoSyncThread
> {
102 static scoped_refptr
<SGIVideoSyncThread
> Create() {
103 if (!g_video_sync_thread
) {
104 g_video_sync_thread
= new SGIVideoSyncThread();
105 g_video_sync_thread
->Start();
107 return g_video_sync_thread
;
111 friend class base::RefCounted
<SGIVideoSyncThread
>;
113 SGIVideoSyncThread() : base::Thread("SGI_video_sync") {
114 DCHECK(CalledOnValidThread());
117 virtual ~SGIVideoSyncThread() {
118 DCHECK(CalledOnValidThread());
119 g_video_sync_thread
= NULL
;
123 static SGIVideoSyncThread
* g_video_sync_thread
;
125 DISALLOW_COPY_AND_ASSIGN(SGIVideoSyncThread
);
128 class SGIVideoSyncProviderThreadShim
{
130 explicit SGIVideoSyncProviderThreadShim(XID window
)
133 task_runner_(base::ThreadTaskRunnerHandle::Get()),
134 cancel_vsync_flag_(),
136 // This ensures that creation of |window_| has occured when this shim
137 // is executing in the same process as the call to create |window_|.
138 XSync(g_display
, False
);
141 virtual ~SGIVideoSyncProviderThreadShim() {
143 glXDestroyContext(display_
, context_
);
148 base::CancellationFlag
* cancel_vsync_flag() {
149 return &cancel_vsync_flag_
;
152 base::Lock
* vsync_lock() {
159 XWindowAttributes attributes
;
160 if (!XGetWindowAttributes(display_
, window_
, &attributes
)) {
161 LOG(ERROR
) << "XGetWindowAttributes failed for window " <<
166 XVisualInfo visual_info_template
;
167 visual_info_template
.visualid
= XVisualIDFromVisual(attributes
.visual
);
169 int visual_info_count
= 0;
170 scoped_ptr
<XVisualInfo
, ScopedPtrXFree
> visual_info_list(
171 XGetVisualInfo(display_
, VisualIDMask
,
172 &visual_info_template
, &visual_info_count
));
174 DCHECK(visual_info_list
.get());
175 if (visual_info_count
== 0) {
176 LOG(ERROR
) << "No visual info for visual ID.";
180 context_
= glXCreateContext(display_
, visual_info_list
.get(), NULL
, True
);
182 DCHECK(NULL
!= context_
);
185 void GetVSyncParameters(const VSyncProvider::UpdateVSyncCallback
& callback
) {
188 // Don't allow |window_| destruction while we're probing vsync.
189 base::AutoLock
locked(vsync_lock_
);
191 if (!context_
|| cancel_vsync_flag_
.IsSet())
194 glXMakeCurrent(display_
, window_
, context_
);
196 unsigned int retrace_count
= 0;
197 if (glXWaitVideoSyncSGI(1, 0, &retrace_count
) != 0)
200 TRACE_EVENT_INSTANT0("gpu", "vblank", TRACE_EVENT_SCOPE_THREAD
);
201 now
= base::TimeTicks::HighResNow();
203 glXMakeCurrent(display_
, 0, 0);
206 const base::TimeDelta kDefaultInterval
=
207 base::TimeDelta::FromSeconds(1) / 60;
209 task_runner_
->PostTask(
210 FROM_HERE
, base::Bind(callback
, now
, kDefaultInterval
));
214 // For initialization of display_ in GLSurface::InitializeOneOff before
215 // the sandbox goes up.
216 friend class gfx::GLSurfaceGLX
;
218 static Display
* display_
;
223 scoped_refptr
<base::SingleThreadTaskRunner
> task_runner_
;
225 base::CancellationFlag cancel_vsync_flag_
;
226 base::Lock vsync_lock_
;
228 DISALLOW_COPY_AND_ASSIGN(SGIVideoSyncProviderThreadShim
);
231 class SGIVideoSyncVSyncProvider
232 : public gfx::VSyncProvider
,
233 public base::SupportsWeakPtr
<SGIVideoSyncVSyncProvider
> {
235 explicit SGIVideoSyncVSyncProvider(gfx::AcceleratedWidget window
)
236 : vsync_thread_(SGIVideoSyncThread::Create()),
237 shim_(new SGIVideoSyncProviderThreadShim(window
)),
238 cancel_vsync_flag_(shim_
->cancel_vsync_flag()),
239 vsync_lock_(shim_
->vsync_lock()) {
240 vsync_thread_
->message_loop()->PostTask(
242 base::Bind(&SGIVideoSyncProviderThreadShim::Initialize
,
243 base::Unretained(shim_
.get())));
246 virtual ~SGIVideoSyncVSyncProvider() {
248 base::AutoLock
locked(*vsync_lock_
);
249 cancel_vsync_flag_
->Set();
252 // Hand-off |shim_| to be deleted on the |vsync_thread_|.
253 vsync_thread_
->message_loop()->DeleteSoon(
258 virtual void GetVSyncParameters(
259 const VSyncProvider::UpdateVSyncCallback
& callback
) OVERRIDE
{
260 // Only one outstanding request per surface.
261 if (!pending_callback_
) {
262 pending_callback_
.reset(
263 new VSyncProvider::UpdateVSyncCallback(callback
));
264 vsync_thread_
->message_loop()->PostTask(
266 base::Bind(&SGIVideoSyncProviderThreadShim::GetVSyncParameters
,
267 base::Unretained(shim_
.get()),
269 &SGIVideoSyncVSyncProvider::PendingCallbackRunner
,
275 void PendingCallbackRunner(const base::TimeTicks timebase
,
276 const base::TimeDelta interval
) {
277 DCHECK(pending_callback_
);
278 pending_callback_
->Run(timebase
, interval
);
279 pending_callback_
.reset();
282 scoped_refptr
<SGIVideoSyncThread
> vsync_thread_
;
284 // Thread shim through which the sync provider is accessed on |vsync_thread_|.
285 scoped_ptr
<SGIVideoSyncProviderThreadShim
> shim_
;
287 scoped_ptr
<VSyncProvider::UpdateVSyncCallback
> pending_callback_
;
289 // Raw pointers to sync primitives owned by the shim_.
290 // These will only be referenced before we post a task to destroy
291 // the shim_, so they are safe to access.
292 base::CancellationFlag
* cancel_vsync_flag_
;
293 base::Lock
* vsync_lock_
;
295 DISALLOW_COPY_AND_ASSIGN(SGIVideoSyncVSyncProvider
);
298 SGIVideoSyncThread
* SGIVideoSyncThread::g_video_sync_thread
= NULL
;
300 // In order to take advantage of GLX_SGI_video_sync, we need a display
301 // for use on a separate thread. We must allocate this before the sandbox
302 // goes up (rather than on-demand when we start the thread).
303 Display
* SGIVideoSyncProviderThreadShim::display_
= NULL
;
307 GLSurfaceGLX::GLSurfaceGLX() {}
309 bool GLSurfaceGLX::InitializeOneOff() {
310 static bool initialized
= false;
314 // http://crbug.com/245466
315 setenv("force_s3tc_enable", "true", 1);
317 // SGIVideoSyncProviderShim (if instantiated) will issue X commands on
319 gfx::InitializeThreadedX11();
320 g_display
= gfx::GetXDisplay();
323 LOG(ERROR
) << "XOpenDisplay failed.";
328 if (!glXQueryVersion(g_display
, &major
, &minor
)) {
329 LOG(ERROR
) << "glxQueryVersion failed";
333 if (major
== 1 && minor
< 3) {
334 LOG(ERROR
) << "GLX 1.3 or later is required.";
338 g_glx_extensions
= glXQueryExtensionsString(g_display
, 0);
339 g_glx_context_create
=
340 HasGLXExtension("GLX_ARB_create_context");
341 g_glx_create_context_robustness_supported
=
342 HasGLXExtension("GLX_ARB_create_context_robustness");
343 g_glx_texture_from_pixmap_supported
=
344 HasGLXExtension("GLX_EXT_texture_from_pixmap");
345 g_glx_oml_sync_control_supported
=
346 HasGLXExtension("GLX_OML_sync_control");
347 g_glx_get_msc_rate_oml_supported
= g_glx_oml_sync_control_supported
;
348 g_glx_sgi_video_sync_supported
=
349 HasGLXExtension("GLX_SGI_video_sync");
351 if (!g_glx_get_msc_rate_oml_supported
&& g_glx_sgi_video_sync_supported
)
352 SGIVideoSyncProviderThreadShim::display_
= gfx::OpenNewXDisplay();
359 const char* GLSurfaceGLX::GetGLXExtensions() {
360 return g_glx_extensions
;
364 bool GLSurfaceGLX::HasGLXExtension(const char* name
) {
365 return ExtensionsContain(GetGLXExtensions(), name
);
369 bool GLSurfaceGLX::IsCreateContextSupported() {
370 return g_glx_context_create
;
374 bool GLSurfaceGLX::IsCreateContextRobustnessSupported() {
375 return g_glx_create_context_robustness_supported
;
379 bool GLSurfaceGLX::IsTextureFromPixmapSupported() {
380 return g_glx_texture_from_pixmap_supported
;
384 bool GLSurfaceGLX::IsOMLSyncControlSupported() {
385 return g_glx_oml_sync_control_supported
;
388 void* GLSurfaceGLX::GetDisplay() {
392 GLSurfaceGLX::~GLSurfaceGLX() {}
394 NativeViewGLSurfaceGLX::NativeViewGLSurfaceGLX(gfx::AcceleratedWidget window
)
395 : parent_window_(window
),
400 gfx::AcceleratedWidget
NativeViewGLSurfaceGLX::GetDrawableHandle() const {
404 bool NativeViewGLSurfaceGLX::Initialize() {
405 XWindowAttributes attributes
;
406 if (!XGetWindowAttributes(g_display
, parent_window_
, &attributes
)) {
407 LOG(ERROR
) << "XGetWindowAttributes failed for window " << parent_window_
411 size_
= gfx::Size(attributes
.width
, attributes
.height
);
412 // Create a child window, with a CopyFromParent visual (to avoid inducing
413 // extra blits in the driver), that we can resize exactly in Resize(),
414 // correctly ordered with GL, so that we don't have invalid transient states.
415 // See https://crbug.com/326995.
416 window_
= XCreateWindow(g_display
,
428 XMapWindow(g_display
, window_
);
430 ui::PlatformEventSource
* event_source
=
431 ui::PlatformEventSource::GetInstance();
432 // Can be NULL in tests, when we don't care about Exposes.
434 XSelectInput(g_display
, window_
, ExposureMask
);
435 ui::PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this);
439 gfx::AcceleratedWidget window_for_vsync
= window_
;
441 if (g_glx_oml_sync_control_supported
)
442 vsync_provider_
.reset(new OMLSyncControlVSyncProvider(window_for_vsync
));
443 else if (g_glx_sgi_video_sync_supported
)
444 vsync_provider_
.reset(new SGIVideoSyncVSyncProvider(window_for_vsync
));
449 void NativeViewGLSurfaceGLX::Destroy() {
451 ui::PlatformEventSource
* event_source
=
452 ui::PlatformEventSource::GetInstance();
454 event_source
->RemovePlatformEventDispatcher(this);
455 XDestroyWindow(g_display
, window_
);
460 bool NativeViewGLSurfaceGLX::CanDispatchEvent(const ui::PlatformEvent
& event
) {
461 return event
->type
== Expose
&& event
->xexpose
.window
== window_
;
464 uint32_t NativeViewGLSurfaceGLX::DispatchEvent(const ui::PlatformEvent
& event
) {
465 XEvent forwarded_event
= *event
;
466 forwarded_event
.xexpose
.window
= parent_window_
;
467 XSendEvent(g_display
, parent_window_
, False
, ExposureMask
,
470 return ui::POST_DISPATCH_STOP_PROPAGATION
;
473 bool NativeViewGLSurfaceGLX::Resize(const gfx::Size
& size
) {
476 XResizeWindow(g_display
, window_
, size
.width(), size
.height());
481 bool NativeViewGLSurfaceGLX::IsOffscreen() {
485 bool NativeViewGLSurfaceGLX::SwapBuffers() {
486 TRACE_EVENT2("gpu", "NativeViewGLSurfaceGLX:RealSwapBuffers",
487 "width", GetSize().width(),
488 "height", GetSize().height());
490 glXSwapBuffers(g_display
, GetDrawableHandle());
494 gfx::Size
NativeViewGLSurfaceGLX::GetSize() {
498 void* NativeViewGLSurfaceGLX::GetHandle() {
499 return reinterpret_cast<void*>(GetDrawableHandle());
502 bool NativeViewGLSurfaceGLX::SupportsPostSubBuffer() {
503 return gfx::g_driver_glx
.ext
.b_GLX_MESA_copy_sub_buffer
;
506 void* NativeViewGLSurfaceGLX::GetConfig() {
508 // This code path is expensive, but we only take it when
509 // attempting to use GLX_ARB_create_context_robustness, in which
510 // case we need a GLXFBConfig for the window in order to create a
513 // TODO(kbr): this is not a reliable code path. On platforms which
514 // support it, we should use glXChooseFBConfig in the browser
515 // process to choose the FBConfig and from there the X Visual to
516 // use when creating the window in the first place. Then we can
517 // pass that FBConfig down rather than attempting to reconstitute
520 XWindowAttributes attributes
;
521 if (!XGetWindowAttributes(
525 LOG(ERROR
) << "XGetWindowAttributes failed for window " <<
530 int visual_id
= XVisualIDFromVisual(attributes
.visual
);
532 int num_elements
= 0;
533 scoped_ptr
<GLXFBConfig
, ScopedPtrXFree
> configs(
534 glXGetFBConfigs(g_display
,
535 DefaultScreen(g_display
),
537 if (!configs
.get()) {
538 LOG(ERROR
) << "glXGetFBConfigs failed.";
542 LOG(ERROR
) << "glXGetFBConfigs returned 0 elements.";
547 for (i
= 0; i
< num_elements
; ++i
) {
549 if (glXGetFBConfigAttrib(
550 g_display
, configs
.get()[i
], GLX_VISUAL_ID
, &value
)) {
551 LOG(ERROR
) << "glXGetFBConfigAttrib failed.";
554 if (value
== visual_id
) {
560 config_
= configs
.get()[i
];
567 bool NativeViewGLSurfaceGLX::PostSubBuffer(
568 int x
, int y
, int width
, int height
) {
569 DCHECK(gfx::g_driver_glx
.ext
.b_GLX_MESA_copy_sub_buffer
);
570 glXCopySubBufferMESA(g_display
, GetDrawableHandle(), x
, y
, width
, height
);
574 VSyncProvider
* NativeViewGLSurfaceGLX::GetVSyncProvider() {
575 return vsync_provider_
.get();
578 NativeViewGLSurfaceGLX::~NativeViewGLSurfaceGLX() {
582 PbufferGLSurfaceGLX::PbufferGLSurfaceGLX(const gfx::Size
& size
)
586 // Some implementations of Pbuffer do not support having a 0 size. For such
587 // cases use a (1, 1) surface.
588 if (size_
.GetArea() == 0)
592 bool PbufferGLSurfaceGLX::Initialize() {
595 static const int config_attributes
[] = {
601 GLX_RENDER_TYPE
, GLX_RGBA_BIT
,
602 GLX_DRAWABLE_TYPE
, GLX_PBUFFER_BIT
,
603 GLX_DOUBLEBUFFER
, False
,
607 int num_elements
= 0;
608 scoped_ptr
<GLXFBConfig
, ScopedPtrXFree
> configs(
609 glXChooseFBConfig(g_display
,
610 DefaultScreen(g_display
),
613 if (!configs
.get()) {
614 LOG(ERROR
) << "glXChooseFBConfig failed.";
618 LOG(ERROR
) << "glXChooseFBConfig returned 0 elements.";
622 config_
= configs
.get()[0];
624 const int pbuffer_attributes
[] = {
625 GLX_PBUFFER_WIDTH
, size_
.width(),
626 GLX_PBUFFER_HEIGHT
, size_
.height(),
629 pbuffer_
= glXCreatePbuffer(g_display
,
630 static_cast<GLXFBConfig
>(config_
),
634 LOG(ERROR
) << "glXCreatePbuffer failed.";
641 void PbufferGLSurfaceGLX::Destroy() {
643 glXDestroyPbuffer(g_display
, pbuffer_
);
650 bool PbufferGLSurfaceGLX::IsOffscreen() {
654 bool PbufferGLSurfaceGLX::SwapBuffers() {
655 NOTREACHED() << "Attempted to call SwapBuffers on a pbuffer.";
659 gfx::Size
PbufferGLSurfaceGLX::GetSize() {
663 void* PbufferGLSurfaceGLX::GetHandle() {
664 return reinterpret_cast<void*>(pbuffer_
);
667 void* PbufferGLSurfaceGLX::GetConfig() {
671 PbufferGLSurfaceGLX::~PbufferGLSurfaceGLX() {