ozone: evdev: Don't crash without cursor in scroll & click events
[chromium-blink-merge.git] / ui / gl / gl_surface_glx.cc
blob58f97f5e3a583dc961804a0595f483acb222bffb
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 "third_party/mesa/src/include/GL/osmesa.h"
24 #include "ui/events/platform/platform_event_source.h"
25 #include "ui/gfx/x/x11_connection.h"
26 #include "ui/gfx/x/x11_types.h"
27 #include "ui/gl/gl_bindings.h"
28 #include "ui/gl/gl_implementation.h"
29 #include "ui/gl/sync_control_vsync_provider.h"
31 namespace gfx {
33 namespace {
35 // scoped_ptr functor for XFree(). Use as follows:
36 // scoped_ptr<XVisualInfo, ScopedPtrXFree> foo(...);
37 // where "XVisualInfo" is any X type that is freed with XFree.
38 struct ScopedPtrXFree {
39 void operator()(void* x) const {
40 ::XFree(x);
44 Display* g_display = NULL;
45 const char* g_glx_extensions = NULL;
46 bool g_glx_context_create = false;
47 bool g_glx_create_context_robustness_supported = false;
48 bool g_glx_texture_from_pixmap_supported = false;
49 bool g_glx_oml_sync_control_supported = false;
51 // Track support of glXGetMscRateOML separately from GLX_OML_sync_control as a
52 // whole since on some platforms (e.g. crosbug.com/34585), glXGetMscRateOML
53 // always fails even though GLX_OML_sync_control is reported as being supported.
54 bool g_glx_get_msc_rate_oml_supported = false;
56 bool g_glx_sgi_video_sync_supported = false;
58 class OMLSyncControlVSyncProvider
59 : public gfx::SyncControlVSyncProvider {
60 public:
61 explicit OMLSyncControlVSyncProvider(gfx::AcceleratedWidget window)
62 : SyncControlVSyncProvider(),
63 window_(window) {
66 virtual ~OMLSyncControlVSyncProvider() { }
68 protected:
69 virtual bool GetSyncValues(int64* system_time,
70 int64* media_stream_counter,
71 int64* swap_buffer_counter) OVERRIDE {
72 return glXGetSyncValuesOML(g_display, window_, system_time,
73 media_stream_counter, swap_buffer_counter);
76 virtual bool GetMscRate(int32* numerator, int32* denominator) OVERRIDE {
77 if (!g_glx_get_msc_rate_oml_supported)
78 return false;
80 if (!glXGetMscRateOML(g_display, window_, numerator, denominator)) {
81 // Once glXGetMscRateOML has been found to fail, don't try again,
82 // since each failing call may spew an error message.
83 g_glx_get_msc_rate_oml_supported = false;
84 return false;
87 return true;
90 private:
91 XID window_;
93 DISALLOW_COPY_AND_ASSIGN(OMLSyncControlVSyncProvider);
96 class SGIVideoSyncThread
97 : public base::Thread,
98 public base::NonThreadSafe,
99 public base::RefCounted<SGIVideoSyncThread> {
100 public:
101 static scoped_refptr<SGIVideoSyncThread> Create() {
102 if (!g_video_sync_thread) {
103 g_video_sync_thread = new SGIVideoSyncThread();
104 g_video_sync_thread->Start();
106 return g_video_sync_thread;
109 private:
110 friend class base::RefCounted<SGIVideoSyncThread>;
112 SGIVideoSyncThread() : base::Thread("SGI_video_sync") {
113 DCHECK(CalledOnValidThread());
116 virtual ~SGIVideoSyncThread() {
117 DCHECK(CalledOnValidThread());
118 g_video_sync_thread = NULL;
119 Stop();
122 static SGIVideoSyncThread* g_video_sync_thread;
124 DISALLOW_COPY_AND_ASSIGN(SGIVideoSyncThread);
127 class SGIVideoSyncProviderThreadShim {
128 public:
129 explicit SGIVideoSyncProviderThreadShim(XID window)
130 : window_(window),
131 context_(NULL),
132 message_loop_(base::MessageLoopProxy::current()),
133 cancel_vsync_flag_(),
134 vsync_lock_() {
135 // This ensures that creation of |window_| has occured when this shim
136 // is executing in the same process as the call to create |window_|.
137 XSync(g_display, False);
140 virtual ~SGIVideoSyncProviderThreadShim() {
141 if (context_) {
142 glXDestroyContext(display_, context_);
143 context_ = NULL;
147 base::CancellationFlag* cancel_vsync_flag() {
148 return &cancel_vsync_flag_;
151 base::Lock* vsync_lock() {
152 return &vsync_lock_;
155 void Initialize() {
156 DCHECK(display_);
158 XWindowAttributes attributes;
159 if (!XGetWindowAttributes(display_, window_, &attributes)) {
160 LOG(ERROR) << "XGetWindowAttributes failed for window " <<
161 window_ << ".";
162 return;
165 XVisualInfo visual_info_template;
166 visual_info_template.visualid = XVisualIDFromVisual(attributes.visual);
168 int visual_info_count = 0;
169 scoped_ptr<XVisualInfo, ScopedPtrXFree> visual_info_list(
170 XGetVisualInfo(display_, VisualIDMask,
171 &visual_info_template, &visual_info_count));
173 DCHECK(visual_info_list.get());
174 if (visual_info_count == 0) {
175 LOG(ERROR) << "No visual info for visual ID.";
176 return;
179 context_ = glXCreateContext(display_, visual_info_list.get(), NULL, True);
181 DCHECK(NULL != context_);
184 void GetVSyncParameters(const VSyncProvider::UpdateVSyncCallback& callback) {
185 base::TimeTicks now;
187 // Don't allow |window_| destruction while we're probing vsync.
188 base::AutoLock locked(vsync_lock_);
190 if (!context_ || cancel_vsync_flag_.IsSet())
191 return;
193 glXMakeCurrent(display_, window_, context_);
195 unsigned int retrace_count = 0;
196 if (glXWaitVideoSyncSGI(1, 0, &retrace_count) != 0)
197 return;
199 TRACE_EVENT_INSTANT0("gpu", "vblank", TRACE_EVENT_SCOPE_THREAD);
200 now = base::TimeTicks::HighResNow();
202 glXMakeCurrent(display_, 0, 0);
205 const base::TimeDelta kDefaultInterval =
206 base::TimeDelta::FromSeconds(1) / 60;
208 message_loop_->PostTask(
209 FROM_HERE, base::Bind(callback, now, kDefaultInterval));
212 private:
213 // For initialization of display_ in GLSurface::InitializeOneOff before
214 // the sandbox goes up.
215 friend class gfx::GLSurfaceGLX;
217 static Display* display_;
219 XID window_;
220 GLXContext context_;
222 scoped_refptr<base::MessageLoopProxy> message_loop_;
224 base::CancellationFlag cancel_vsync_flag_;
225 base::Lock vsync_lock_;
227 DISALLOW_COPY_AND_ASSIGN(SGIVideoSyncProviderThreadShim);
230 class SGIVideoSyncVSyncProvider
231 : public gfx::VSyncProvider,
232 public base::SupportsWeakPtr<SGIVideoSyncVSyncProvider> {
233 public:
234 explicit SGIVideoSyncVSyncProvider(gfx::AcceleratedWidget window)
235 : vsync_thread_(SGIVideoSyncThread::Create()),
236 shim_(new SGIVideoSyncProviderThreadShim(window)),
237 cancel_vsync_flag_(shim_->cancel_vsync_flag()),
238 vsync_lock_(shim_->vsync_lock()) {
239 vsync_thread_->message_loop()->PostTask(
240 FROM_HERE,
241 base::Bind(&SGIVideoSyncProviderThreadShim::Initialize,
242 base::Unretained(shim_.get())));
245 virtual ~SGIVideoSyncVSyncProvider() {
247 base::AutoLock locked(*vsync_lock_);
248 cancel_vsync_flag_->Set();
251 // Hand-off |shim_| to be deleted on the |vsync_thread_|.
252 vsync_thread_->message_loop()->DeleteSoon(
253 FROM_HERE,
254 shim_.release());
257 virtual void GetVSyncParameters(
258 const VSyncProvider::UpdateVSyncCallback& callback) OVERRIDE {
259 // Only one outstanding request per surface.
260 if (!pending_callback_) {
261 pending_callback_.reset(
262 new VSyncProvider::UpdateVSyncCallback(callback));
263 vsync_thread_->message_loop()->PostTask(
264 FROM_HERE,
265 base::Bind(&SGIVideoSyncProviderThreadShim::GetVSyncParameters,
266 base::Unretained(shim_.get()),
267 base::Bind(
268 &SGIVideoSyncVSyncProvider::PendingCallbackRunner,
269 AsWeakPtr())));
273 private:
274 void PendingCallbackRunner(const base::TimeTicks timebase,
275 const base::TimeDelta interval) {
276 DCHECK(pending_callback_);
277 pending_callback_->Run(timebase, interval);
278 pending_callback_.reset();
281 scoped_refptr<SGIVideoSyncThread> vsync_thread_;
283 // Thread shim through which the sync provider is accessed on |vsync_thread_|.
284 scoped_ptr<SGIVideoSyncProviderThreadShim> shim_;
286 scoped_ptr<VSyncProvider::UpdateVSyncCallback> pending_callback_;
288 // Raw pointers to sync primitives owned by the shim_.
289 // These will only be referenced before we post a task to destroy
290 // the shim_, so they are safe to access.
291 base::CancellationFlag* cancel_vsync_flag_;
292 base::Lock* vsync_lock_;
294 DISALLOW_COPY_AND_ASSIGN(SGIVideoSyncVSyncProvider);
297 SGIVideoSyncThread* SGIVideoSyncThread::g_video_sync_thread = NULL;
299 // In order to take advantage of GLX_SGI_video_sync, we need a display
300 // for use on a separate thread. We must allocate this before the sandbox
301 // goes up (rather than on-demand when we start the thread).
302 Display* SGIVideoSyncProviderThreadShim::display_ = NULL;
304 } // namespace
306 GLSurfaceGLX::GLSurfaceGLX() {}
308 bool GLSurfaceGLX::InitializeOneOff() {
309 static bool initialized = false;
310 if (initialized)
311 return true;
313 // http://crbug.com/245466
314 setenv("force_s3tc_enable", "true", 1);
316 // SGIVideoSyncProviderShim (if instantiated) will issue X commands on
317 // it's own thread.
318 gfx::InitializeThreadedX11();
319 g_display = gfx::GetXDisplay();
321 if (!g_display) {
322 LOG(ERROR) << "XOpenDisplay failed.";
323 return false;
326 int major, minor;
327 if (!glXQueryVersion(g_display, &major, &minor)) {
328 LOG(ERROR) << "glxQueryVersion failed";
329 return false;
332 if (major == 1 && minor < 3) {
333 LOG(ERROR) << "GLX 1.3 or later is required.";
334 return false;
337 g_glx_extensions = glXQueryExtensionsString(g_display, 0);
338 g_glx_context_create =
339 HasGLXExtension("GLX_ARB_create_context");
340 g_glx_create_context_robustness_supported =
341 HasGLXExtension("GLX_ARB_create_context_robustness");
342 g_glx_texture_from_pixmap_supported =
343 HasGLXExtension("GLX_EXT_texture_from_pixmap");
344 g_glx_oml_sync_control_supported =
345 HasGLXExtension("GLX_OML_sync_control");
346 g_glx_get_msc_rate_oml_supported = g_glx_oml_sync_control_supported;
347 g_glx_sgi_video_sync_supported =
348 HasGLXExtension("GLX_SGI_video_sync");
350 if (!g_glx_get_msc_rate_oml_supported && g_glx_sgi_video_sync_supported)
351 SGIVideoSyncProviderThreadShim::display_ = gfx::OpenNewXDisplay();
353 initialized = true;
354 return true;
357 // static
358 const char* GLSurfaceGLX::GetGLXExtensions() {
359 return g_glx_extensions;
362 // static
363 bool GLSurfaceGLX::HasGLXExtension(const char* name) {
364 return ExtensionsContain(GetGLXExtensions(), name);
367 // static
368 bool GLSurfaceGLX::IsCreateContextSupported() {
369 return g_glx_context_create;
372 // static
373 bool GLSurfaceGLX::IsCreateContextRobustnessSupported() {
374 return g_glx_create_context_robustness_supported;
377 // static
378 bool GLSurfaceGLX::IsTextureFromPixmapSupported() {
379 return g_glx_texture_from_pixmap_supported;
382 // static
383 bool GLSurfaceGLX::IsOMLSyncControlSupported() {
384 return g_glx_oml_sync_control_supported;
387 void* GLSurfaceGLX::GetDisplay() {
388 return g_display;
391 GLSurfaceGLX::~GLSurfaceGLX() {}
393 NativeViewGLSurfaceGLX::NativeViewGLSurfaceGLX(gfx::AcceleratedWidget window)
394 : parent_window_(window),
395 window_(0),
396 config_(NULL) {
399 gfx::AcceleratedWidget NativeViewGLSurfaceGLX::GetDrawableHandle() const {
400 return window_;
403 bool NativeViewGLSurfaceGLX::Initialize() {
404 XWindowAttributes attributes;
405 if (!XGetWindowAttributes(g_display, parent_window_, &attributes)) {
406 LOG(ERROR) << "XGetWindowAttributes failed for window " << parent_window_
407 << ".";
408 return false;
410 size_ = gfx::Size(attributes.width, attributes.height);
411 // Create a child window, with a CopyFromParent visual (to avoid inducing
412 // extra blits in the driver), that we can resize exactly in Resize(),
413 // correctly ordered with GL, so that we don't have invalid transient states.
414 // See https://crbug.com/326995.
415 window_ = XCreateWindow(g_display,
416 parent_window_,
419 size_.width(),
420 size_.height(),
422 CopyFromParent,
423 InputOutput,
424 CopyFromParent,
426 NULL);
427 XMapWindow(g_display, window_);
429 ui::PlatformEventSource* event_source =
430 ui::PlatformEventSource::GetInstance();
431 // Can be NULL in tests, when we don't care about Exposes.
432 if (event_source) {
433 XSelectInput(g_display, window_, ExposureMask);
434 ui::PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this);
436 XFlush(g_display);
438 gfx::AcceleratedWidget window_for_vsync = window_;
440 if (g_glx_oml_sync_control_supported)
441 vsync_provider_.reset(new OMLSyncControlVSyncProvider(window_for_vsync));
442 else if (g_glx_sgi_video_sync_supported)
443 vsync_provider_.reset(new SGIVideoSyncVSyncProvider(window_for_vsync));
445 return true;
448 void NativeViewGLSurfaceGLX::Destroy() {
449 if (window_) {
450 ui::PlatformEventSource* event_source =
451 ui::PlatformEventSource::GetInstance();
452 if (event_source)
453 event_source->RemovePlatformEventDispatcher(this);
454 XDestroyWindow(g_display, window_);
455 XFlush(g_display);
459 bool NativeViewGLSurfaceGLX::CanDispatchEvent(const ui::PlatformEvent& event) {
460 return event->type == Expose && event->xexpose.window == window_;
463 uint32_t NativeViewGLSurfaceGLX::DispatchEvent(const ui::PlatformEvent& event) {
464 XEvent forwarded_event = *event;
465 forwarded_event.xexpose.window = parent_window_;
466 XSendEvent(g_display, parent_window_, False, ExposureMask,
467 &forwarded_event);
468 XFlush(g_display);
469 return ui::POST_DISPATCH_STOP_PROPAGATION;
472 bool NativeViewGLSurfaceGLX::Resize(const gfx::Size& size) {
473 size_ = size;
474 glXWaitGL();
475 XResizeWindow(g_display, window_, size.width(), size.height());
476 glXWaitX();
477 return true;
480 bool NativeViewGLSurfaceGLX::IsOffscreen() {
481 return false;
484 bool NativeViewGLSurfaceGLX::SwapBuffers() {
485 TRACE_EVENT2("gpu", "NativeViewGLSurfaceGLX:RealSwapBuffers",
486 "width", GetSize().width(),
487 "height", GetSize().height());
489 glXSwapBuffers(g_display, GetDrawableHandle());
490 return true;
493 gfx::Size NativeViewGLSurfaceGLX::GetSize() {
494 return size_;
497 void* NativeViewGLSurfaceGLX::GetHandle() {
498 return reinterpret_cast<void*>(GetDrawableHandle());
501 bool NativeViewGLSurfaceGLX::SupportsPostSubBuffer() {
502 return gfx::g_driver_glx.ext.b_GLX_MESA_copy_sub_buffer;
505 void* NativeViewGLSurfaceGLX::GetConfig() {
506 if (!config_) {
507 // This code path is expensive, but we only take it when
508 // attempting to use GLX_ARB_create_context_robustness, in which
509 // case we need a GLXFBConfig for the window in order to create a
510 // context for it.
512 // TODO(kbr): this is not a reliable code path. On platforms which
513 // support it, we should use glXChooseFBConfig in the browser
514 // process to choose the FBConfig and from there the X Visual to
515 // use when creating the window in the first place. Then we can
516 // pass that FBConfig down rather than attempting to reconstitute
517 // it.
519 XWindowAttributes attributes;
520 if (!XGetWindowAttributes(
521 g_display,
522 window_,
523 &attributes)) {
524 LOG(ERROR) << "XGetWindowAttributes failed for window " <<
525 window_ << ".";
526 return NULL;
529 int visual_id = XVisualIDFromVisual(attributes.visual);
531 int num_elements = 0;
532 scoped_ptr<GLXFBConfig, ScopedPtrXFree> configs(
533 glXGetFBConfigs(g_display,
534 DefaultScreen(g_display),
535 &num_elements));
536 if (!configs.get()) {
537 LOG(ERROR) << "glXGetFBConfigs failed.";
538 return NULL;
540 if (!num_elements) {
541 LOG(ERROR) << "glXGetFBConfigs returned 0 elements.";
542 return NULL;
544 bool found = false;
545 int i;
546 for (i = 0; i < num_elements; ++i) {
547 int value;
548 if (glXGetFBConfigAttrib(
549 g_display, configs.get()[i], GLX_VISUAL_ID, &value)) {
550 LOG(ERROR) << "glXGetFBConfigAttrib failed.";
551 return NULL;
553 if (value == visual_id) {
554 found = true;
555 break;
558 if (found) {
559 config_ = configs.get()[i];
563 return config_;
566 bool NativeViewGLSurfaceGLX::PostSubBuffer(
567 int x, int y, int width, int height) {
568 DCHECK(gfx::g_driver_glx.ext.b_GLX_MESA_copy_sub_buffer);
569 glXCopySubBufferMESA(g_display, GetDrawableHandle(), x, y, width, height);
570 return true;
573 VSyncProvider* NativeViewGLSurfaceGLX::GetVSyncProvider() {
574 return vsync_provider_.get();
577 NativeViewGLSurfaceGLX::~NativeViewGLSurfaceGLX() {
578 Destroy();
581 PbufferGLSurfaceGLX::PbufferGLSurfaceGLX(const gfx::Size& size)
582 : size_(size),
583 config_(NULL),
584 pbuffer_(0) {
585 // Some implementations of Pbuffer do not support having a 0 size. For such
586 // cases use a (1, 1) surface.
587 if (size_.GetArea() == 0)
588 size_.SetSize(1, 1);
591 bool PbufferGLSurfaceGLX::Initialize() {
592 DCHECK(!pbuffer_);
594 static const int config_attributes[] = {
595 GLX_BUFFER_SIZE, 32,
596 GLX_ALPHA_SIZE, 8,
597 GLX_BLUE_SIZE, 8,
598 GLX_GREEN_SIZE, 8,
599 GLX_RED_SIZE, 8,
600 GLX_RENDER_TYPE, GLX_RGBA_BIT,
601 GLX_DRAWABLE_TYPE, GLX_PBUFFER_BIT,
602 GLX_DOUBLEBUFFER, False,
606 int num_elements = 0;
607 scoped_ptr<GLXFBConfig, ScopedPtrXFree> configs(
608 glXChooseFBConfig(g_display,
609 DefaultScreen(g_display),
610 config_attributes,
611 &num_elements));
612 if (!configs.get()) {
613 LOG(ERROR) << "glXChooseFBConfig failed.";
614 return false;
616 if (!num_elements) {
617 LOG(ERROR) << "glXChooseFBConfig returned 0 elements.";
618 return false;
621 config_ = configs.get()[0];
623 const int pbuffer_attributes[] = {
624 GLX_PBUFFER_WIDTH, size_.width(),
625 GLX_PBUFFER_HEIGHT, size_.height(),
628 pbuffer_ = glXCreatePbuffer(g_display,
629 static_cast<GLXFBConfig>(config_),
630 pbuffer_attributes);
631 if (!pbuffer_) {
632 Destroy();
633 LOG(ERROR) << "glXCreatePbuffer failed.";
634 return false;
637 return true;
640 void PbufferGLSurfaceGLX::Destroy() {
641 if (pbuffer_) {
642 glXDestroyPbuffer(g_display, pbuffer_);
643 pbuffer_ = 0;
646 config_ = NULL;
649 bool PbufferGLSurfaceGLX::IsOffscreen() {
650 return true;
653 bool PbufferGLSurfaceGLX::SwapBuffers() {
654 NOTREACHED() << "Attempted to call SwapBuffers on a pbuffer.";
655 return false;
658 gfx::Size PbufferGLSurfaceGLX::GetSize() {
659 return size_;
662 void* PbufferGLSurfaceGLX::GetHandle() {
663 return reinterpret_cast<void*>(pbuffer_);
666 void* PbufferGLSurfaceGLX::GetConfig() {
667 return config_;
670 PbufferGLSurfaceGLX::~PbufferGLSurfaceGLX() {
671 Destroy();
674 } // namespace gfx