Updating trunk VERSION from 2139.0 to 2140.0
[chromium-blink-merge.git] / ui / gl / gl_surface_glx.cc
blob05ab13bec182f93bedcd1651bcb2f975811470de
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.
5 extern "C" {
6 #include <X11/Xlib.h>
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 "ui/events/platform/platform_event_source.h"
24 #include "ui/gfx/x/x11_connection.h"
25 #include "ui/gfx/x/x11_types.h"
26 #include "ui/gl/gl_bindings.h"
27 #include "ui/gl/gl_implementation.h"
28 #include "ui/gl/sync_control_vsync_provider.h"
30 namespace gfx {
32 namespace {
34 // scoped_ptr functor for XFree(). Use as follows:
35 // scoped_ptr<XVisualInfo, ScopedPtrXFree> foo(...);
36 // where "XVisualInfo" is any X type that is freed with XFree.
37 struct ScopedPtrXFree {
38 void operator()(void* x) const {
39 ::XFree(x);
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 {
59 public:
60 explicit OMLSyncControlVSyncProvider(gfx::AcceleratedWidget window)
61 : SyncControlVSyncProvider(),
62 window_(window) {
65 virtual ~OMLSyncControlVSyncProvider() { }
67 protected:
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)
77 return false;
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;
83 return false;
86 return true;
89 private:
90 XID window_;
92 DISALLOW_COPY_AND_ASSIGN(OMLSyncControlVSyncProvider);
95 class SGIVideoSyncThread
96 : public base::Thread,
97 public base::NonThreadSafe,
98 public base::RefCounted<SGIVideoSyncThread> {
99 public:
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;
108 private:
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;
118 Stop();
121 static SGIVideoSyncThread* g_video_sync_thread;
123 DISALLOW_COPY_AND_ASSIGN(SGIVideoSyncThread);
126 class SGIVideoSyncProviderThreadShim {
127 public:
128 explicit SGIVideoSyncProviderThreadShim(XID window)
129 : window_(window),
130 context_(NULL),
131 message_loop_(base::MessageLoopProxy::current()),
132 cancel_vsync_flag_(),
133 vsync_lock_() {
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() {
140 if (context_) {
141 glXDestroyContext(display_, context_);
142 context_ = NULL;
146 base::CancellationFlag* cancel_vsync_flag() {
147 return &cancel_vsync_flag_;
150 base::Lock* vsync_lock() {
151 return &vsync_lock_;
154 void Initialize() {
155 DCHECK(display_);
157 XWindowAttributes attributes;
158 if (!XGetWindowAttributes(display_, window_, &attributes)) {
159 LOG(ERROR) << "XGetWindowAttributes failed for window " <<
160 window_ << ".";
161 return;
164 XVisualInfo visual_info_template;
165 visual_info_template.visualid = XVisualIDFromVisual(attributes.visual);
167 int visual_info_count = 0;
168 scoped_ptr<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.";
175 return;
178 context_ = glXCreateContext(display_, visual_info_list.get(), NULL, True);
180 DCHECK(NULL != context_);
183 void GetVSyncParameters(const VSyncProvider::UpdateVSyncCallback& callback) {
184 base::TimeTicks now;
186 // Don't allow |window_| destruction while we're probing vsync.
187 base::AutoLock locked(vsync_lock_);
189 if (!context_ || cancel_vsync_flag_.IsSet())
190 return;
192 glXMakeCurrent(display_, window_, context_);
194 unsigned int retrace_count = 0;
195 if (glXWaitVideoSyncSGI(1, 0, &retrace_count) != 0)
196 return;
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));
211 private:
212 // For initialization of display_ in GLSurface::InitializeOneOff before
213 // the sandbox goes up.
214 friend class gfx::GLSurfaceGLX;
216 static Display* display_;
218 XID window_;
219 GLXContext context_;
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> {
232 public:
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(
239 FROM_HERE,
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(
252 FROM_HERE,
253 shim_.release());
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(
263 FROM_HERE,
264 base::Bind(&SGIVideoSyncProviderThreadShim::GetVSyncParameters,
265 base::Unretained(shim_.get()),
266 base::Bind(
267 &SGIVideoSyncVSyncProvider::PendingCallbackRunner,
268 AsWeakPtr())));
272 private:
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 } // namespace
305 GLSurfaceGLX::GLSurfaceGLX() {}
307 bool GLSurfaceGLX::InitializeOneOff() {
308 static bool initialized = false;
309 if (initialized)
310 return true;
312 // http://crbug.com/245466
313 setenv("force_s3tc_enable", "true", 1);
315 // SGIVideoSyncProviderShim (if instantiated) will issue X commands on
316 // it's own thread.
317 gfx::InitializeThreadedX11();
318 g_display = gfx::GetXDisplay();
320 if (!g_display) {
321 LOG(ERROR) << "XOpenDisplay failed.";
322 return false;
325 int major, minor;
326 if (!glXQueryVersion(g_display, &major, &minor)) {
327 LOG(ERROR) << "glxQueryVersion failed";
328 return false;
331 if (major == 1 && minor < 3) {
332 LOG(ERROR) << "GLX 1.3 or later is required.";
333 return false;
336 g_glx_extensions = glXQueryExtensionsString(g_display, 0);
337 g_glx_context_create =
338 HasGLXExtension("GLX_ARB_create_context");
339 g_glx_create_context_robustness_supported =
340 HasGLXExtension("GLX_ARB_create_context_robustness");
341 g_glx_texture_from_pixmap_supported =
342 HasGLXExtension("GLX_EXT_texture_from_pixmap");
343 g_glx_oml_sync_control_supported =
344 HasGLXExtension("GLX_OML_sync_control");
345 g_glx_get_msc_rate_oml_supported = g_glx_oml_sync_control_supported;
346 g_glx_sgi_video_sync_supported =
347 HasGLXExtension("GLX_SGI_video_sync");
349 if (!g_glx_get_msc_rate_oml_supported && g_glx_sgi_video_sync_supported)
350 SGIVideoSyncProviderThreadShim::display_ = gfx::OpenNewXDisplay();
352 initialized = true;
353 return true;
356 // static
357 const char* GLSurfaceGLX::GetGLXExtensions() {
358 return g_glx_extensions;
361 // static
362 bool GLSurfaceGLX::HasGLXExtension(const char* name) {
363 return ExtensionsContain(GetGLXExtensions(), name);
366 // static
367 bool GLSurfaceGLX::IsCreateContextSupported() {
368 return g_glx_context_create;
371 // static
372 bool GLSurfaceGLX::IsCreateContextRobustnessSupported() {
373 return g_glx_create_context_robustness_supported;
376 // static
377 bool GLSurfaceGLX::IsTextureFromPixmapSupported() {
378 return g_glx_texture_from_pixmap_supported;
381 // static
382 bool GLSurfaceGLX::IsOMLSyncControlSupported() {
383 return g_glx_oml_sync_control_supported;
386 void* GLSurfaceGLX::GetDisplay() {
387 return g_display;
390 GLSurfaceGLX::~GLSurfaceGLX() {}
392 NativeViewGLSurfaceGLX::NativeViewGLSurfaceGLX(gfx::AcceleratedWidget window)
393 : parent_window_(window),
394 window_(0),
395 config_(NULL) {
398 gfx::AcceleratedWidget NativeViewGLSurfaceGLX::GetDrawableHandle() const {
399 return window_;
402 bool NativeViewGLSurfaceGLX::Initialize() {
403 XWindowAttributes attributes;
404 if (!XGetWindowAttributes(g_display, parent_window_, &attributes)) {
405 LOG(ERROR) << "XGetWindowAttributes failed for window " << parent_window_
406 << ".";
407 return false;
409 size_ = gfx::Size(attributes.width, attributes.height);
410 // Create a child window, with a CopyFromParent visual (to avoid inducing
411 // extra blits in the driver), that we can resize exactly in Resize(),
412 // correctly ordered with GL, so that we don't have invalid transient states.
413 // See https://crbug.com/326995.
414 window_ = XCreateWindow(g_display,
415 parent_window_,
418 size_.width(),
419 size_.height(),
421 CopyFromParent,
422 InputOutput,
423 CopyFromParent,
425 NULL);
426 XMapWindow(g_display, window_);
428 ui::PlatformEventSource* event_source =
429 ui::PlatformEventSource::GetInstance();
430 // Can be NULL in tests, when we don't care about Exposes.
431 if (event_source) {
432 XSelectInput(g_display, window_, ExposureMask);
433 ui::PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this);
435 XFlush(g_display);
437 gfx::AcceleratedWidget window_for_vsync = window_;
439 if (g_glx_oml_sync_control_supported)
440 vsync_provider_.reset(new OMLSyncControlVSyncProvider(window_for_vsync));
441 else if (g_glx_sgi_video_sync_supported)
442 vsync_provider_.reset(new SGIVideoSyncVSyncProvider(window_for_vsync));
444 return true;
447 void NativeViewGLSurfaceGLX::Destroy() {
448 if (window_) {
449 ui::PlatformEventSource* event_source =
450 ui::PlatformEventSource::GetInstance();
451 if (event_source)
452 event_source->RemovePlatformEventDispatcher(this);
453 XDestroyWindow(g_display, window_);
454 XFlush(g_display);
458 bool NativeViewGLSurfaceGLX::CanDispatchEvent(const ui::PlatformEvent& event) {
459 return event->type == Expose && event->xexpose.window == window_;
462 uint32_t NativeViewGLSurfaceGLX::DispatchEvent(const ui::PlatformEvent& event) {
463 XEvent forwarded_event = *event;
464 forwarded_event.xexpose.window = parent_window_;
465 XSendEvent(g_display, parent_window_, False, ExposureMask,
466 &forwarded_event);
467 XFlush(g_display);
468 return ui::POST_DISPATCH_STOP_PROPAGATION;
471 bool NativeViewGLSurfaceGLX::Resize(const gfx::Size& size) {
472 size_ = size;
473 glXWaitGL();
474 XResizeWindow(g_display, window_, size.width(), size.height());
475 glXWaitX();
476 return true;
479 bool NativeViewGLSurfaceGLX::IsOffscreen() {
480 return false;
483 bool NativeViewGLSurfaceGLX::SwapBuffers() {
484 TRACE_EVENT2("gpu", "NativeViewGLSurfaceGLX:RealSwapBuffers",
485 "width", GetSize().width(),
486 "height", GetSize().height());
488 glXSwapBuffers(g_display, GetDrawableHandle());
489 return true;
492 gfx::Size NativeViewGLSurfaceGLX::GetSize() {
493 return size_;
496 void* NativeViewGLSurfaceGLX::GetHandle() {
497 return reinterpret_cast<void*>(GetDrawableHandle());
500 bool NativeViewGLSurfaceGLX::SupportsPostSubBuffer() {
501 return gfx::g_driver_glx.ext.b_GLX_MESA_copy_sub_buffer;
504 void* NativeViewGLSurfaceGLX::GetConfig() {
505 if (!config_) {
506 // This code path is expensive, but we only take it when
507 // attempting to use GLX_ARB_create_context_robustness, in which
508 // case we need a GLXFBConfig for the window in order to create a
509 // context for it.
511 // TODO(kbr): this is not a reliable code path. On platforms which
512 // support it, we should use glXChooseFBConfig in the browser
513 // process to choose the FBConfig and from there the X Visual to
514 // use when creating the window in the first place. Then we can
515 // pass that FBConfig down rather than attempting to reconstitute
516 // it.
518 XWindowAttributes attributes;
519 if (!XGetWindowAttributes(
520 g_display,
521 window_,
522 &attributes)) {
523 LOG(ERROR) << "XGetWindowAttributes failed for window " <<
524 window_ << ".";
525 return NULL;
528 int visual_id = XVisualIDFromVisual(attributes.visual);
530 int num_elements = 0;
531 scoped_ptr<GLXFBConfig, ScopedPtrXFree> configs(
532 glXGetFBConfigs(g_display,
533 DefaultScreen(g_display),
534 &num_elements));
535 if (!configs.get()) {
536 LOG(ERROR) << "glXGetFBConfigs failed.";
537 return NULL;
539 if (!num_elements) {
540 LOG(ERROR) << "glXGetFBConfigs returned 0 elements.";
541 return NULL;
543 bool found = false;
544 int i;
545 for (i = 0; i < num_elements; ++i) {
546 int value;
547 if (glXGetFBConfigAttrib(
548 g_display, configs.get()[i], GLX_VISUAL_ID, &value)) {
549 LOG(ERROR) << "glXGetFBConfigAttrib failed.";
550 return NULL;
552 if (value == visual_id) {
553 found = true;
554 break;
557 if (found) {
558 config_ = configs.get()[i];
562 return config_;
565 bool NativeViewGLSurfaceGLX::PostSubBuffer(
566 int x, int y, int width, int height) {
567 DCHECK(gfx::g_driver_glx.ext.b_GLX_MESA_copy_sub_buffer);
568 glXCopySubBufferMESA(g_display, GetDrawableHandle(), x, y, width, height);
569 return true;
572 VSyncProvider* NativeViewGLSurfaceGLX::GetVSyncProvider() {
573 return vsync_provider_.get();
576 NativeViewGLSurfaceGLX::~NativeViewGLSurfaceGLX() {
577 Destroy();
580 PbufferGLSurfaceGLX::PbufferGLSurfaceGLX(const gfx::Size& size)
581 : size_(size),
582 config_(NULL),
583 pbuffer_(0) {
584 // Some implementations of Pbuffer do not support having a 0 size. For such
585 // cases use a (1, 1) surface.
586 if (size_.GetArea() == 0)
587 size_.SetSize(1, 1);
590 bool PbufferGLSurfaceGLX::Initialize() {
591 DCHECK(!pbuffer_);
593 static const int config_attributes[] = {
594 GLX_BUFFER_SIZE, 32,
595 GLX_ALPHA_SIZE, 8,
596 GLX_BLUE_SIZE, 8,
597 GLX_GREEN_SIZE, 8,
598 GLX_RED_SIZE, 8,
599 GLX_RENDER_TYPE, GLX_RGBA_BIT,
600 GLX_DRAWABLE_TYPE, GLX_PBUFFER_BIT,
601 GLX_DOUBLEBUFFER, False,
605 int num_elements = 0;
606 scoped_ptr<GLXFBConfig, ScopedPtrXFree> configs(
607 glXChooseFBConfig(g_display,
608 DefaultScreen(g_display),
609 config_attributes,
610 &num_elements));
611 if (!configs.get()) {
612 LOG(ERROR) << "glXChooseFBConfig failed.";
613 return false;
615 if (!num_elements) {
616 LOG(ERROR) << "glXChooseFBConfig returned 0 elements.";
617 return false;
620 config_ = configs.get()[0];
622 const int pbuffer_attributes[] = {
623 GLX_PBUFFER_WIDTH, size_.width(),
624 GLX_PBUFFER_HEIGHT, size_.height(),
627 pbuffer_ = glXCreatePbuffer(g_display,
628 static_cast<GLXFBConfig>(config_),
629 pbuffer_attributes);
630 if (!pbuffer_) {
631 Destroy();
632 LOG(ERROR) << "glXCreatePbuffer failed.";
633 return false;
636 return true;
639 void PbufferGLSurfaceGLX::Destroy() {
640 if (pbuffer_) {
641 glXDestroyPbuffer(g_display, pbuffer_);
642 pbuffer_ = 0;
645 config_ = NULL;
648 bool PbufferGLSurfaceGLX::IsOffscreen() {
649 return true;
652 bool PbufferGLSurfaceGLX::SwapBuffers() {
653 NOTREACHED() << "Attempted to call SwapBuffers on a pbuffer.";
654 return false;
657 gfx::Size PbufferGLSurfaceGLX::GetSize() {
658 return size_;
661 void* PbufferGLSurfaceGLX::GetHandle() {
662 return reinterpret_cast<void*>(pbuffer_);
665 void* PbufferGLSurfaceGLX::GetConfig() {
666 return config_;
669 PbufferGLSurfaceGLX::~PbufferGLSurfaceGLX() {
670 Destroy();
673 } // namespace gfx