Fix search results being clipped in app list.
[chromium-blink-merge.git] / ui / gl / gl_surface_glx.cc
blob803906093508bd509714136523192d8c6cc20143
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/lazy_instance.h"
13 #include "base/logging.h"
14 #include "base/memory/scoped_ptr.h"
15 #include "base/memory/weak_ptr.h"
16 #include "base/message_loop/message_loop.h"
17 #include "base/single_thread_task_runner.h"
18 #include "base/synchronization/cancellation_flag.h"
19 #include "base/synchronization/lock.h"
20 #include "base/thread_task_runner_handle.h"
21 #include "base/threading/non_thread_safe.h"
22 #include "base/threading/thread.h"
23 #include "base/time/time.h"
24 #include "base/trace_event/trace_event.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"
32 namespace gfx {
34 namespace {
36 Display* g_display = NULL;
37 const char* g_glx_extensions = NULL;
38 bool g_glx_context_create = false;
39 bool g_glx_create_context_robustness_supported = false;
40 bool g_glx_texture_from_pixmap_supported = false;
41 bool g_glx_oml_sync_control_supported = false;
43 // Track support of glXGetMscRateOML separately from GLX_OML_sync_control as a
44 // whole since on some platforms (e.g. crosbug.com/34585), glXGetMscRateOML
45 // always fails even though GLX_OML_sync_control is reported as being supported.
46 bool g_glx_get_msc_rate_oml_supported = false;
48 bool g_glx_sgi_video_sync_supported = false;
50 static const base::TimeDelta kGetVSyncParametersMinPeriod =
51 #if defined(OS_LINUX)
52 // See crbug.com/373489
53 // On Linux, querying the vsync parameters might burn CPU for up to an
54 // entire vsync, so we only query periodically to reduce CPU usage.
55 // 5 seconds is chosen somewhat abitrarily as a balance between:
56 // a) Drift in the phase of our signal.
57 // b) Potential janks from periodically pegging the CPU.
58 base::TimeDelta::FromSeconds(5);
59 #else
60 base::TimeDelta::FromSeconds(0);
61 #endif
63 class OMLSyncControlVSyncProvider
64 : public gfx::SyncControlVSyncProvider {
65 public:
66 explicit OMLSyncControlVSyncProvider(gfx::AcceleratedWidget window)
67 : SyncControlVSyncProvider(),
68 window_(window) {
71 ~OMLSyncControlVSyncProvider() override {}
73 protected:
74 bool GetSyncValues(int64* system_time,
75 int64* media_stream_counter,
76 int64* swap_buffer_counter) override {
77 return glXGetSyncValuesOML(g_display, window_, system_time,
78 media_stream_counter, swap_buffer_counter);
81 bool GetMscRate(int32* numerator, int32* denominator) override {
82 if (!g_glx_get_msc_rate_oml_supported)
83 return false;
85 if (!glXGetMscRateOML(g_display, window_, numerator, denominator)) {
86 // Once glXGetMscRateOML has been found to fail, don't try again,
87 // since each failing call may spew an error message.
88 g_glx_get_msc_rate_oml_supported = false;
89 return false;
92 return true;
95 private:
96 XID window_;
98 DISALLOW_COPY_AND_ASSIGN(OMLSyncControlVSyncProvider);
101 class SGIVideoSyncThread
102 : public base::Thread,
103 public base::NonThreadSafe,
104 public base::RefCounted<SGIVideoSyncThread> {
105 public:
106 static scoped_refptr<SGIVideoSyncThread> Create() {
107 if (!g_video_sync_thread) {
108 g_video_sync_thread = new SGIVideoSyncThread();
109 g_video_sync_thread->Start();
111 return g_video_sync_thread;
114 private:
115 friend class base::RefCounted<SGIVideoSyncThread>;
117 SGIVideoSyncThread() : base::Thread("SGI_video_sync") {
118 DCHECK(CalledOnValidThread());
121 ~SGIVideoSyncThread() override {
122 DCHECK(CalledOnValidThread());
123 g_video_sync_thread = NULL;
124 Stop();
127 static SGIVideoSyncThread* g_video_sync_thread;
129 DISALLOW_COPY_AND_ASSIGN(SGIVideoSyncThread);
132 class SGIVideoSyncProviderThreadShim {
133 public:
134 explicit SGIVideoSyncProviderThreadShim(XID window)
135 : window_(window),
136 context_(NULL),
137 task_runner_(base::ThreadTaskRunnerHandle::Get()),
138 cancel_vsync_flag_(),
139 vsync_lock_() {
140 // This ensures that creation of |window_| has occured when this shim
141 // is executing in the same process as the call to create |window_|.
142 XSync(g_display, False);
145 virtual ~SGIVideoSyncProviderThreadShim() {
146 if (context_) {
147 glXDestroyContext(display_, context_);
148 context_ = NULL;
152 base::CancellationFlag* cancel_vsync_flag() {
153 return &cancel_vsync_flag_;
156 base::Lock* vsync_lock() {
157 return &vsync_lock_;
160 void Initialize() {
161 DCHECK(display_);
163 XWindowAttributes attributes;
164 if (!XGetWindowAttributes(display_, window_, &attributes)) {
165 LOG(ERROR) << "XGetWindowAttributes failed for window " <<
166 window_ << ".";
167 return;
170 XVisualInfo visual_info_template;
171 visual_info_template.visualid = XVisualIDFromVisual(attributes.visual);
173 int visual_info_count = 0;
174 gfx::XScopedPtr<XVisualInfo> visual_info_list(XGetVisualInfo(
175 display_, VisualIDMask, &visual_info_template, &visual_info_count));
177 DCHECK(visual_info_list.get());
178 if (visual_info_count == 0) {
179 LOG(ERROR) << "No visual info for visual ID.";
180 return;
183 context_ = glXCreateContext(display_, visual_info_list.get(), NULL, True);
185 DCHECK(NULL != context_);
188 void GetVSyncParameters(const VSyncProvider::UpdateVSyncCallback& callback) {
189 base::TimeTicks now;
191 // Don't allow |window_| destruction while we're probing vsync.
192 base::AutoLock locked(vsync_lock_);
194 if (!context_ || cancel_vsync_flag_.IsSet())
195 return;
197 glXMakeCurrent(display_, window_, context_);
199 unsigned int retrace_count = 0;
200 if (glXWaitVideoSyncSGI(1, 0, &retrace_count) != 0)
201 return;
203 TRACE_EVENT_INSTANT0("gpu", "vblank", TRACE_EVENT_SCOPE_THREAD);
204 now = base::TimeTicks::Now();
206 glXMakeCurrent(display_, 0, 0);
209 const base::TimeDelta kDefaultInterval =
210 base::TimeDelta::FromSeconds(1) / 60;
212 task_runner_->PostTask(
213 FROM_HERE, base::Bind(callback, now, kDefaultInterval));
216 private:
217 // For initialization of display_ in GLSurface::InitializeOneOff before
218 // the sandbox goes up.
219 friend class gfx::GLSurfaceGLX;
221 static Display* display_;
223 XID window_;
224 GLXContext context_;
226 scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
228 base::CancellationFlag cancel_vsync_flag_;
229 base::Lock vsync_lock_;
231 DISALLOW_COPY_AND_ASSIGN(SGIVideoSyncProviderThreadShim);
234 class SGIVideoSyncVSyncProvider
235 : public gfx::VSyncProvider,
236 public base::SupportsWeakPtr<SGIVideoSyncVSyncProvider> {
237 public:
238 explicit SGIVideoSyncVSyncProvider(gfx::AcceleratedWidget window)
239 : vsync_thread_(SGIVideoSyncThread::Create()),
240 shim_(new SGIVideoSyncProviderThreadShim(window)),
241 cancel_vsync_flag_(shim_->cancel_vsync_flag()),
242 vsync_lock_(shim_->vsync_lock()) {
243 vsync_thread_->message_loop()->PostTask(
244 FROM_HERE,
245 base::Bind(&SGIVideoSyncProviderThreadShim::Initialize,
246 base::Unretained(shim_.get())));
249 ~SGIVideoSyncVSyncProvider() override {
251 base::AutoLock locked(*vsync_lock_);
252 cancel_vsync_flag_->Set();
255 // Hand-off |shim_| to be deleted on the |vsync_thread_|.
256 vsync_thread_->message_loop()->DeleteSoon(
257 FROM_HERE,
258 shim_.release());
261 void GetVSyncParameters(
262 const VSyncProvider::UpdateVSyncCallback& callback) override {
263 if (kGetVSyncParametersMinPeriod > base::TimeDelta()) {
264 base::TimeTicks now = base::TimeTicks::Now();
265 base::TimeDelta delta = now - last_get_vsync_parameters_time_;
266 if (delta < kGetVSyncParametersMinPeriod)
267 return;
268 last_get_vsync_parameters_time_ = now;
271 // Only one outstanding request per surface.
272 if (!pending_callback_) {
273 pending_callback_.reset(
274 new VSyncProvider::UpdateVSyncCallback(callback));
275 vsync_thread_->message_loop()->PostTask(
276 FROM_HERE,
277 base::Bind(&SGIVideoSyncProviderThreadShim::GetVSyncParameters,
278 base::Unretained(shim_.get()),
279 base::Bind(
280 &SGIVideoSyncVSyncProvider::PendingCallbackRunner,
281 AsWeakPtr())));
285 private:
286 void PendingCallbackRunner(const base::TimeTicks timebase,
287 const base::TimeDelta interval) {
288 DCHECK(pending_callback_);
289 pending_callback_->Run(timebase, interval);
290 pending_callback_.reset();
293 scoped_refptr<SGIVideoSyncThread> vsync_thread_;
295 // Thread shim through which the sync provider is accessed on |vsync_thread_|.
296 scoped_ptr<SGIVideoSyncProviderThreadShim> shim_;
298 scoped_ptr<VSyncProvider::UpdateVSyncCallback> pending_callback_;
300 // Raw pointers to sync primitives owned by the shim_.
301 // These will only be referenced before we post a task to destroy
302 // the shim_, so they are safe to access.
303 base::CancellationFlag* cancel_vsync_flag_;
304 base::Lock* vsync_lock_;
306 base::TimeTicks last_get_vsync_parameters_time_;
308 DISALLOW_COPY_AND_ASSIGN(SGIVideoSyncVSyncProvider);
311 SGIVideoSyncThread* SGIVideoSyncThread::g_video_sync_thread = NULL;
313 // In order to take advantage of GLX_SGI_video_sync, we need a display
314 // for use on a separate thread. We must allocate this before the sandbox
315 // goes up (rather than on-demand when we start the thread).
316 Display* SGIVideoSyncProviderThreadShim::display_ = NULL;
318 } // namespace
320 GLSurfaceGLX::GLSurfaceGLX() {}
322 bool GLSurfaceGLX::InitializeOneOff() {
323 static bool initialized = false;
324 if (initialized)
325 return true;
327 // http://crbug.com/245466
328 setenv("force_s3tc_enable", "true", 1);
330 // SGIVideoSyncProviderShim (if instantiated) will issue X commands on
331 // it's own thread.
332 gfx::InitializeThreadedX11();
333 g_display = gfx::GetXDisplay();
335 if (!g_display) {
336 LOG(ERROR) << "XOpenDisplay failed.";
337 return false;
340 int major, minor;
341 if (!glXQueryVersion(g_display, &major, &minor)) {
342 LOG(ERROR) << "glxQueryVersion failed";
343 return false;
346 if (major == 1 && minor < 3) {
347 LOG(ERROR) << "GLX 1.3 or later is required.";
348 return false;
351 g_glx_extensions = glXQueryExtensionsString(g_display, 0);
352 g_glx_context_create =
353 HasGLXExtension("GLX_ARB_create_context");
354 g_glx_create_context_robustness_supported =
355 HasGLXExtension("GLX_ARB_create_context_robustness");
356 g_glx_texture_from_pixmap_supported =
357 HasGLXExtension("GLX_EXT_texture_from_pixmap");
358 g_glx_oml_sync_control_supported =
359 HasGLXExtension("GLX_OML_sync_control");
360 g_glx_get_msc_rate_oml_supported = g_glx_oml_sync_control_supported;
361 g_glx_sgi_video_sync_supported =
362 HasGLXExtension("GLX_SGI_video_sync");
364 if (!g_glx_get_msc_rate_oml_supported && g_glx_sgi_video_sync_supported)
365 SGIVideoSyncProviderThreadShim::display_ = gfx::OpenNewXDisplay();
367 initialized = true;
368 return true;
371 // static
372 const char* GLSurfaceGLX::GetGLXExtensions() {
373 return g_glx_extensions;
376 // static
377 bool GLSurfaceGLX::HasGLXExtension(const char* name) {
378 return ExtensionsContain(GetGLXExtensions(), name);
381 // static
382 bool GLSurfaceGLX::IsCreateContextSupported() {
383 return g_glx_context_create;
386 // static
387 bool GLSurfaceGLX::IsCreateContextRobustnessSupported() {
388 return g_glx_create_context_robustness_supported;
391 // static
392 bool GLSurfaceGLX::IsTextureFromPixmapSupported() {
393 return g_glx_texture_from_pixmap_supported;
396 // static
397 bool GLSurfaceGLX::IsOMLSyncControlSupported() {
398 return g_glx_oml_sync_control_supported;
401 void* GLSurfaceGLX::GetDisplay() {
402 return g_display;
405 GLSurfaceGLX::~GLSurfaceGLX() {}
407 NativeViewGLSurfaceGLX::NativeViewGLSurfaceGLX(gfx::AcceleratedWidget window)
408 : parent_window_(window),
409 window_(0),
410 config_(NULL) {
413 gfx::AcceleratedWidget NativeViewGLSurfaceGLX::GetDrawableHandle() const {
414 return window_;
417 bool NativeViewGLSurfaceGLX::Initialize() {
418 XWindowAttributes attributes;
419 if (!XGetWindowAttributes(g_display, parent_window_, &attributes)) {
420 LOG(ERROR) << "XGetWindowAttributes failed for window " << parent_window_
421 << ".";
422 return false;
424 size_ = gfx::Size(attributes.width, attributes.height);
425 // Create a child window, with a CopyFromParent visual (to avoid inducing
426 // extra blits in the driver), that we can resize exactly in Resize(),
427 // correctly ordered with GL, so that we don't have invalid transient states.
428 // See https://crbug.com/326995.
429 window_ = XCreateWindow(g_display,
430 parent_window_,
433 size_.width(),
434 size_.height(),
436 CopyFromParent,
437 InputOutput,
438 CopyFromParent,
440 NULL);
441 XMapWindow(g_display, window_);
443 ui::PlatformEventSource* event_source =
444 ui::PlatformEventSource::GetInstance();
445 // Can be NULL in tests, when we don't care about Exposes.
446 if (event_source) {
447 XSelectInput(g_display, window_, ExposureMask);
448 ui::PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this);
450 XFlush(g_display);
452 gfx::AcceleratedWidget window_for_vsync = window_;
454 if (g_glx_oml_sync_control_supported)
455 vsync_provider_.reset(new OMLSyncControlVSyncProvider(window_for_vsync));
456 else if (g_glx_sgi_video_sync_supported)
457 vsync_provider_.reset(new SGIVideoSyncVSyncProvider(window_for_vsync));
459 return true;
462 void NativeViewGLSurfaceGLX::Destroy() {
463 if (window_) {
464 ui::PlatformEventSource* event_source =
465 ui::PlatformEventSource::GetInstance();
466 if (event_source)
467 event_source->RemovePlatformEventDispatcher(this);
468 XDestroyWindow(g_display, window_);
469 window_ = 0;
470 XFlush(g_display);
474 bool NativeViewGLSurfaceGLX::CanDispatchEvent(const ui::PlatformEvent& event) {
475 return event->type == Expose && event->xexpose.window == window_;
478 uint32_t NativeViewGLSurfaceGLX::DispatchEvent(const ui::PlatformEvent& event) {
479 XEvent forwarded_event = *event;
480 forwarded_event.xexpose.window = parent_window_;
481 XSendEvent(g_display, parent_window_, False, ExposureMask,
482 &forwarded_event);
483 XFlush(g_display);
484 return ui::POST_DISPATCH_STOP_PROPAGATION;
487 bool NativeViewGLSurfaceGLX::Resize(const gfx::Size& size) {
488 size_ = size;
489 glXWaitGL();
490 XResizeWindow(g_display, window_, size.width(), size.height());
491 glXWaitX();
492 return true;
495 bool NativeViewGLSurfaceGLX::IsOffscreen() {
496 return false;
499 bool NativeViewGLSurfaceGLX::SwapBuffers() {
500 TRACE_EVENT2("gpu", "NativeViewGLSurfaceGLX:RealSwapBuffers",
501 "width", GetSize().width(),
502 "height", GetSize().height());
504 glXSwapBuffers(g_display, GetDrawableHandle());
505 return true;
508 gfx::Size NativeViewGLSurfaceGLX::GetSize() {
509 return size_;
512 void* NativeViewGLSurfaceGLX::GetHandle() {
513 return reinterpret_cast<void*>(GetDrawableHandle());
516 bool NativeViewGLSurfaceGLX::SupportsPostSubBuffer() {
517 return gfx::g_driver_glx.ext.b_GLX_MESA_copy_sub_buffer;
520 void* NativeViewGLSurfaceGLX::GetConfig() {
521 if (!config_) {
522 // This code path is expensive, but we only take it when
523 // attempting to use GLX_ARB_create_context_robustness, in which
524 // case we need a GLXFBConfig for the window in order to create a
525 // context for it.
527 // TODO(kbr): this is not a reliable code path. On platforms which
528 // support it, we should use glXChooseFBConfig in the browser
529 // process to choose the FBConfig and from there the X Visual to
530 // use when creating the window in the first place. Then we can
531 // pass that FBConfig down rather than attempting to reconstitute
532 // it.
534 XWindowAttributes attributes;
535 if (!XGetWindowAttributes(
536 g_display,
537 window_,
538 &attributes)) {
539 LOG(ERROR) << "XGetWindowAttributes failed for window " <<
540 window_ << ".";
541 return NULL;
544 int visual_id = XVisualIDFromVisual(attributes.visual);
546 int num_elements = 0;
547 gfx::XScopedPtr<GLXFBConfig> configs(
548 glXGetFBConfigs(g_display, DefaultScreen(g_display), &num_elements));
549 if (!configs.get()) {
550 LOG(ERROR) << "glXGetFBConfigs failed.";
551 return NULL;
553 if (!num_elements) {
554 LOG(ERROR) << "glXGetFBConfigs returned 0 elements.";
555 return NULL;
557 bool found = false;
558 int i;
559 for (i = 0; i < num_elements; ++i) {
560 int value;
561 if (glXGetFBConfigAttrib(
562 g_display, configs.get()[i], GLX_VISUAL_ID, &value)) {
563 LOG(ERROR) << "glXGetFBConfigAttrib failed.";
564 return NULL;
566 if (value == visual_id) {
567 found = true;
568 break;
571 if (found) {
572 config_ = configs.get()[i];
576 return config_;
579 bool NativeViewGLSurfaceGLX::PostSubBuffer(
580 int x, int y, int width, int height) {
581 DCHECK(gfx::g_driver_glx.ext.b_GLX_MESA_copy_sub_buffer);
582 glXCopySubBufferMESA(g_display, GetDrawableHandle(), x, y, width, height);
583 return true;
586 VSyncProvider* NativeViewGLSurfaceGLX::GetVSyncProvider() {
587 return vsync_provider_.get();
590 NativeViewGLSurfaceGLX::~NativeViewGLSurfaceGLX() {
591 Destroy();
594 PbufferGLSurfaceGLX::PbufferGLSurfaceGLX(const gfx::Size& size)
595 : size_(size),
596 config_(NULL),
597 pbuffer_(0) {
598 // Some implementations of Pbuffer do not support having a 0 size. For such
599 // cases use a (1, 1) surface.
600 if (size_.GetArea() == 0)
601 size_.SetSize(1, 1);
604 bool PbufferGLSurfaceGLX::Initialize() {
605 DCHECK(!pbuffer_);
607 static const int config_attributes[] = {
608 GLX_BUFFER_SIZE, 32,
609 GLX_ALPHA_SIZE, 8,
610 GLX_BLUE_SIZE, 8,
611 GLX_GREEN_SIZE, 8,
612 GLX_RED_SIZE, 8,
613 GLX_RENDER_TYPE, GLX_RGBA_BIT,
614 GLX_DRAWABLE_TYPE, GLX_PBUFFER_BIT,
615 GLX_DOUBLEBUFFER, False,
619 int num_elements = 0;
620 gfx::XScopedPtr<GLXFBConfig> configs(glXChooseFBConfig(
621 g_display, DefaultScreen(g_display), config_attributes, &num_elements));
622 if (!configs.get()) {
623 LOG(ERROR) << "glXChooseFBConfig failed.";
624 return false;
626 if (!num_elements) {
627 LOG(ERROR) << "glXChooseFBConfig returned 0 elements.";
628 return false;
631 config_ = configs.get()[0];
633 const int pbuffer_attributes[] = {
634 GLX_PBUFFER_WIDTH, size_.width(),
635 GLX_PBUFFER_HEIGHT, size_.height(),
638 pbuffer_ = glXCreatePbuffer(g_display,
639 static_cast<GLXFBConfig>(config_),
640 pbuffer_attributes);
641 if (!pbuffer_) {
642 Destroy();
643 LOG(ERROR) << "glXCreatePbuffer failed.";
644 return false;
647 return true;
650 void PbufferGLSurfaceGLX::Destroy() {
651 if (pbuffer_) {
652 glXDestroyPbuffer(g_display, pbuffer_);
653 pbuffer_ = 0;
656 config_ = NULL;
659 bool PbufferGLSurfaceGLX::IsOffscreen() {
660 return true;
663 bool PbufferGLSurfaceGLX::SwapBuffers() {
664 NOTREACHED() << "Attempted to call SwapBuffers on a pbuffer.";
665 return false;
668 gfx::Size PbufferGLSurfaceGLX::GetSize() {
669 return size_;
672 void* PbufferGLSurfaceGLX::GetHandle() {
673 return reinterpret_cast<void*>(pbuffer_);
676 void* PbufferGLSurfaceGLX::GetConfig() {
677 return config_;
680 PbufferGLSurfaceGLX::~PbufferGLSurfaceGLX() {
681 Destroy();
684 } // namespace gfx