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/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 glXSwapBuffers(g_display, GetDrawableHandle());
607 gfx::Size NativeViewGLSurfaceGLX::GetSize() {
611 void* NativeViewGLSurfaceGLX::GetHandle() {
612 return reinterpret_cast<void*>(GetDrawableHandle());
615 std::string NativeViewGLSurfaceGLX::GetExtensions() {
616 std::string extensions = GLSurface::GetExtensions();
617 if (gfx::g_driver_glx.ext.b_GLX_MESA_copy_sub_buffer) {
618 extensions += extensions.empty() ? "" : " ";
619 extensions += "GL_CHROMIUM_post_sub_buffer";
624 void* NativeViewGLSurfaceGLX::GetConfig() {
626 // This code path is expensive, but we only take it when
627 // attempting to use GLX_ARB_create_context_robustness, in which
628 // case we need a GLXFBConfig for the window in order to create a
631 // TODO(kbr): this is not a reliable code path. On platforms which
632 // support it, we should use glXChooseFBConfig in the browser
633 // process to choose the FBConfig and from there the X Visual to
634 // use when creating the window in the first place. Then we can
635 // pass that FBConfig down rather than attempting to reconstitute
638 XWindowAttributes attributes;
639 if (!XGetWindowAttributes(
643 LOG(ERROR) << "XGetWindowAttributes failed for window " <<
644 parent_window_ << ".";
648 int visual_id = XVisualIDFromVisual(attributes.visual);
650 int num_elements = 0;
651 scoped_ptr_malloc<GLXFBConfig, ScopedPtrXFree> configs(
652 glXGetFBConfigs(g_display,
653 DefaultScreen(g_display),
655 if (!configs.get()) {
656 LOG(ERROR) << "glXGetFBConfigs failed.";
660 LOG(ERROR) << "glXGetFBConfigs returned 0 elements.";
665 for (i = 0; i < num_elements; ++i) {
667 if (glXGetFBConfigAttrib(
668 g_display, configs.get()[i], GLX_VISUAL_ID, &value)) {
669 LOG(ERROR) << "glXGetFBConfigAttrib failed.";
672 if (value == visual_id) {
678 config_ = configs.get()[i];
685 bool NativeViewGLSurfaceGLX::PostSubBuffer(
686 int x, int y, int width, int height) {
687 DCHECK(gfx::g_driver_glx.ext.b_GLX_MESA_copy_sub_buffer);
688 glXCopySubBufferMESA(g_display, GetDrawableHandle(), x, y, width, height);
692 VSyncProvider* NativeViewGLSurfaceGLX::GetVSyncProvider() {
693 return vsync_provider_.get();
696 NativeViewGLSurfaceGLX::NativeViewGLSurfaceGLX()
698 #if defined(TOOLKIT_GTK)
701 backbuffer_allocated_(true),
702 frontbuffer_allocated_(true),
707 NativeViewGLSurfaceGLX::~NativeViewGLSurfaceGLX() {
711 PbufferGLSurfaceGLX::PbufferGLSurfaceGLX(const gfx::Size& size)
717 bool PbufferGLSurfaceGLX::Initialize() {
720 static const int config_attributes[] = {
726 GLX_RENDER_TYPE, GLX_RGBA_BIT,
727 GLX_DRAWABLE_TYPE, GLX_PBUFFER_BIT,
728 GLX_DOUBLEBUFFER, False,
732 int num_elements = 0;
733 scoped_ptr_malloc<GLXFBConfig, ScopedPtrXFree> configs(
734 glXChooseFBConfig(g_display,
735 DefaultScreen(g_display),
738 if (!configs.get()) {
739 LOG(ERROR) << "glXChooseFBConfig failed.";
743 LOG(ERROR) << "glXChooseFBConfig returned 0 elements.";
747 config_ = configs.get()[0];
749 const int pbuffer_attributes[] = {
750 GLX_PBUFFER_WIDTH, size_.width(),
751 GLX_PBUFFER_HEIGHT, size_.height(),
754 pbuffer_ = glXCreatePbuffer(g_display,
755 static_cast<GLXFBConfig>(config_),
759 LOG(ERROR) << "glXCreatePbuffer failed.";
766 void PbufferGLSurfaceGLX::Destroy() {
768 glXDestroyPbuffer(g_display, pbuffer_);
775 bool PbufferGLSurfaceGLX::IsOffscreen() {
779 bool PbufferGLSurfaceGLX::SwapBuffers() {
780 NOTREACHED() << "Attempted to call SwapBuffers on a pbuffer.";
784 gfx::Size PbufferGLSurfaceGLX::GetSize() {
788 void* PbufferGLSurfaceGLX::GetHandle() {
789 return reinterpret_cast<void*>(pbuffer_);
792 void* PbufferGLSurfaceGLX::GetConfig() {
796 PbufferGLSurfaceGLX::~PbufferGLSurfaceGLX() {