gl_surface_glx.cc revision c2e0dbddbe15c98d52c4786dac06cb8952a8ae6d
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.
4
5extern "C" {
6#include <X11/Xlib.h>
7}
8
9#include "ui/gl/gl_surface_glx.h"
10
11#include "base/basictypes.h"
12#include "base/debug/trace_event.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.h"
17#include "base/process_util.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.h"
23#include "third_party/mesa/MesaLib/include/GL/osmesa.h"
24#include "ui/base/x/x11_util.h"
25#include "ui/gl/gl_bindings.h"
26#include "ui/gl/gl_implementation.h"
27#include "ui/gl/vsync_provider.h"
28
29namespace gfx {
30
31namespace {
32
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.
36class ScopedPtrXFree {
37 public:
38  void operator()(void* x) const {
39    ::XFree(x);
40  }
41};
42
43Display* g_display;
44const char* g_glx_extensions = NULL;
45bool g_glx_context_create = false;
46bool g_glx_create_context_robustness_supported = false;
47bool g_glx_texture_from_pixmap_supported = false;
48bool g_glx_oml_sync_control_supported = false;
49
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.
53bool g_glx_get_msc_rate_oml_supported = false;
54
55bool g_glx_sgi_video_sync_supported = false;
56
57class OMLSyncControlVSyncProvider
58    : public gfx::SyncControlVSyncProvider {
59 public:
60  explicit OMLSyncControlVSyncProvider(gfx::AcceleratedWidget window)
61      : SyncControlVSyncProvider(),
62        window_(window) {
63  }
64
65  virtual ~OMLSyncControlVSyncProvider() { }
66
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);
73  }
74
75  virtual bool GetMscRate(int32* numerator, int32* denominator) OVERRIDE {
76    if (!g_glx_get_msc_rate_oml_supported)
77      return false;
78
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;
84    }
85
86    return true;
87  }
88
89 private:
90  XID window_;
91
92  DISALLOW_COPY_AND_ASSIGN(OMLSyncControlVSyncProvider);
93};
94
95class 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();
104    }
105    return g_video_sync_thread;
106  }
107
108 private:
109  friend class base::RefCounted<SGIVideoSyncThread>;
110
111  SGIVideoSyncThread() : base::Thread("SGI_video_sync") {
112    DCHECK(CalledOnValidThread());
113  }
114
115  virtual ~SGIVideoSyncThread() {
116    DCHECK(CalledOnValidThread());
117    g_video_sync_thread = NULL;
118    Stop();
119  }
120
121  static SGIVideoSyncThread* g_video_sync_thread;
122
123  DISALLOW_COPY_AND_ASSIGN(SGIVideoSyncThread);
124};
125
126class SGIVideoSyncProviderThreadShim
127    : public base::SupportsWeakPtr<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);
138  }
139
140  base::CancellationFlag* cancel_vsync_flag() {
141    return &cancel_vsync_flag_;
142  }
143
144  base::Lock* vsync_lock() {
145    return &vsync_lock_;
146  }
147
148  void Initialize() {
149    DCHECK(display_);
150
151    XWindowAttributes attributes;
152    if (!XGetWindowAttributes(display_, window_, &attributes)) {
153      LOG(ERROR) << "XGetWindowAttributes failed for window " <<
154        window_ << ".";
155      return;
156    }
157
158    XVisualInfo visual_info_template;
159    visual_info_template.visualid = XVisualIDFromVisual(attributes.visual);
160
161    int visual_info_count = 0;
162    scoped_ptr_malloc<XVisualInfo, ScopedPtrXFree> visual_info_list(
163        XGetVisualInfo(display_, VisualIDMask,
164                       &visual_info_template, &visual_info_count));
165
166    DCHECK(visual_info_list.get());
167    if (visual_info_count == 0) {
168      LOG(ERROR) << "No visual info for visual ID.";
169      return;
170    }
171
172    context_ = glXCreateContext(display_, visual_info_list.get(), NULL, True);
173
174    DCHECK(NULL != context_);
175  }
176
177  void Destroy() {
178    if (context_) {
179      glXDestroyContext(display_, context_);
180      context_ = NULL;
181    }
182    delete this;
183  }
184
185  void GetVSyncParameters(const VSyncProvider::UpdateVSyncCallback& callback) {
186    base::TimeTicks now;
187    {
188      // Don't allow |window_| destruction while we're probing vsync.
189      base::AutoLock locked(vsync_lock_);
190
191      if (!context_ || cancel_vsync_flag_.IsSet())
192        return;
193
194      glXMakeCurrent(display_, window_, context_);
195
196      unsigned int retrace_count = 0;
197      if (glXWaitVideoSyncSGI(1, 0, &retrace_count) != 0)
198        return;
199
200      TRACE_EVENT_INSTANT0("gpu", "vblank", TRACE_EVENT_SCOPE_THREAD);
201      now = base::TimeTicks::HighResNow();
202
203      glXMakeCurrent(display_, 0, 0);
204    }
205
206    const base::TimeDelta kDefaultInterval =
207        base::TimeDelta::FromSeconds(1) / 60;
208
209    message_loop_->PostTask(
210        FROM_HERE, base::Bind(callback, now, kDefaultInterval));
211  }
212
213 private:
214  // For initialization of display_ in GLSurface::InitializeOneOff before
215  // the sandbox goes up.
216  friend class gfx::GLSurfaceGLX;
217
218  virtual ~SGIVideoSyncProviderThreadShim() {
219  }
220
221  static Display* display_;
222
223  XID window_;
224  GLXContext context_;
225
226  scoped_refptr<base::MessageLoopProxy> message_loop_;
227
228  base::CancellationFlag cancel_vsync_flag_;
229  base::Lock vsync_lock_;
230
231  DISALLOW_COPY_AND_ASSIGN(SGIVideoSyncProviderThreadShim);
232};
233
234class 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))->AsWeakPtr()),
241        cancel_vsync_flag_(shim_->cancel_vsync_flag()),
242        vsync_lock_(shim_->vsync_lock()) {
243    // The WeakPtr is bound to the SGIVideoSyncThread. We only use it for
244    // PostTask.
245    shim_->DetachFromThread();
246    vsync_thread_->message_loop()->PostTask(
247        FROM_HERE,
248        base::Bind(&SGIVideoSyncProviderThreadShim::Initialize, shim_));
249  }
250
251  virtual ~SGIVideoSyncVSyncProvider() {
252    {
253      base::AutoLock locked(*vsync_lock_);
254      cancel_vsync_flag_->Set();
255    }
256    vsync_thread_->message_loop()->PostTask(
257        FROM_HERE,
258        base::Bind(&SGIVideoSyncProviderThreadShim::Destroy, shim_));
259  }
260
261  virtual void GetVSyncParameters(
262      const VSyncProvider::UpdateVSyncCallback& callback) OVERRIDE {
263    // Only one outstanding request per surface.
264    if (!pending_callback_) {
265      pending_callback_.reset(
266          new VSyncProvider::UpdateVSyncCallback(callback));
267      vsync_thread_->message_loop()->PostTask(
268          FROM_HERE,
269          base::Bind(&SGIVideoSyncProviderThreadShim::GetVSyncParameters,
270                     shim_, base::Bind(
271                         &SGIVideoSyncVSyncProvider::PendingCallbackRunner,
272                         AsWeakPtr())));
273    }
274  }
275
276 private:
277  void PendingCallbackRunner(const base::TimeTicks timebase,
278                             const base::TimeDelta interval) {
279    DCHECK(pending_callback_);
280    pending_callback_->Run(timebase, interval);
281    pending_callback_.reset();
282  }
283
284  scoped_refptr<SGIVideoSyncThread> vsync_thread_;
285  base::WeakPtr<SGIVideoSyncProviderThreadShim> shim_;
286
287  scoped_ptr<VSyncProvider::UpdateVSyncCallback> pending_callback_;
288
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_;
294
295  DISALLOW_COPY_AND_ASSIGN(SGIVideoSyncVSyncProvider);
296};
297
298SGIVideoSyncThread* SGIVideoSyncThread::g_video_sync_thread = NULL;
299
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).
303Display* SGIVideoSyncProviderThreadShim::display_ = NULL;
304
305}  // namespace
306
307GLSurfaceGLX::GLSurfaceGLX() {}
308
309bool GLSurfaceGLX::InitializeOneOff() {
310  static bool initialized = false;
311  if (initialized)
312    return true;
313
314  // SGIVideoSyncProviderShim (if instantiated) will issue X commands on
315  // it's own thread.
316  XInitThreads();
317
318  g_display = base::MessagePumpForUI::GetDefaultXDisplay();
319  if (!g_display) {
320    LOG(ERROR) << "XOpenDisplay failed.";
321    return false;
322  }
323
324  int major, minor;
325  if (!glXQueryVersion(g_display, &major, &minor)) {
326    LOG(ERROR) << "glxQueryVersion failed";
327    return false;
328  }
329
330  if (major == 1 && minor < 3) {
331    LOG(ERROR) << "GLX 1.3 or later is required.";
332    return false;
333  }
334
335  g_glx_extensions = glXQueryExtensionsString(g_display, 0);
336  g_glx_context_create =
337      HasGLXExtension("GLX_ARB_create_context");
338  g_glx_create_context_robustness_supported =
339      HasGLXExtension("GLX_ARB_create_context_robustness");
340  g_glx_texture_from_pixmap_supported =
341      HasGLXExtension("GLX_EXT_texture_from_pixmap");
342  g_glx_oml_sync_control_supported =
343      HasGLXExtension("GLX_OML_sync_control");
344  g_glx_get_msc_rate_oml_supported = g_glx_oml_sync_control_supported;
345  g_glx_sgi_video_sync_supported =
346      HasGLXExtension("GLX_SGI_video_sync");
347
348  if (!g_glx_get_msc_rate_oml_supported && g_glx_sgi_video_sync_supported)
349    SGIVideoSyncProviderThreadShim::display_ = XOpenDisplay(NULL);
350
351  initialized = true;
352  return true;
353}
354
355// static
356const char* GLSurfaceGLX::GetGLXExtensions() {
357  return g_glx_extensions;
358}
359
360// static
361bool GLSurfaceGLX::HasGLXExtension(const char* name) {
362  return ExtensionsContain(GetGLXExtensions(), name);
363}
364
365// static
366bool GLSurfaceGLX::IsCreateContextSupported() {
367  return g_glx_context_create;
368}
369
370// static
371bool GLSurfaceGLX::IsCreateContextRobustnessSupported() {
372  return g_glx_create_context_robustness_supported;
373}
374
375// static
376bool GLSurfaceGLX::IsTextureFromPixmapSupported() {
377  return g_glx_texture_from_pixmap_supported;
378}
379
380// static
381bool GLSurfaceGLX::IsOMLSyncControlSupported() {
382  return g_glx_oml_sync_control_supported;
383}
384
385void* GLSurfaceGLX::GetDisplay() {
386  return g_display;
387}
388
389GLSurfaceGLX::~GLSurfaceGLX() {}
390
391NativeViewGLSurfaceGLX::NativeViewGLSurfaceGLX(gfx::AcceleratedWidget window)
392  : window_(window),
393    config_(NULL) {
394}
395
396bool NativeViewGLSurfaceGLX::Initialize() {
397  XWindowAttributes attributes;
398  if (!XGetWindowAttributes(g_display, window_, &attributes)) {
399    LOG(ERROR) << "XGetWindowAttributes failed for window " << window_ << ".";
400    return false;
401  }
402  size_ = gfx::Size(attributes.width, attributes.height);
403
404  if (g_glx_oml_sync_control_supported)
405    vsync_provider_.reset(new OMLSyncControlVSyncProvider(window_));
406  else if (g_glx_sgi_video_sync_supported)
407    vsync_provider_.reset(new SGIVideoSyncVSyncProvider(window_));
408
409  return true;
410}
411
412void NativeViewGLSurfaceGLX::Destroy() {
413}
414
415bool NativeViewGLSurfaceGLX::Resize(const gfx::Size& size) {
416  // On Intel drivers, the frame buffer won't be resize until the next swap. If
417  // we only do PostSubBuffer, then we're stuck in the old size. Force a swap
418  // now.
419  if (gfx::g_driver_glx.ext.b_GLX_MESA_copy_sub_buffer && size_ != size)
420    SwapBuffers();
421  size_ = size;
422  return true;
423}
424
425bool NativeViewGLSurfaceGLX::IsOffscreen() {
426  return false;
427}
428
429bool NativeViewGLSurfaceGLX::SwapBuffers() {
430  glXSwapBuffers(g_display, window_);
431  // For latency_tests.cc:
432  UNSHIPPED_TRACE_EVENT_INSTANT0("test_gpu", "CompositorSwapBuffersComplete",
433                                 TRACE_EVENT_SCOPE_THREAD);
434  return true;
435}
436
437gfx::Size NativeViewGLSurfaceGLX::GetSize() {
438  return size_;
439}
440
441void* NativeViewGLSurfaceGLX::GetHandle() {
442  return reinterpret_cast<void*>(window_);
443}
444
445std::string NativeViewGLSurfaceGLX::GetExtensions() {
446  std::string extensions = GLSurface::GetExtensions();
447  if (gfx::g_driver_glx.ext.b_GLX_MESA_copy_sub_buffer) {
448    extensions += extensions.empty() ? "" : " ";
449    extensions += "GL_CHROMIUM_post_sub_buffer";
450  }
451  return extensions;
452}
453
454void* NativeViewGLSurfaceGLX::GetConfig() {
455  if (!config_) {
456    // This code path is expensive, but we only take it when
457    // attempting to use GLX_ARB_create_context_robustness, in which
458    // case we need a GLXFBConfig for the window in order to create a
459    // context for it.
460    //
461    // TODO(kbr): this is not a reliable code path. On platforms which
462    // support it, we should use glXChooseFBConfig in the browser
463    // process to choose the FBConfig and from there the X Visual to
464    // use when creating the window in the first place. Then we can
465    // pass that FBConfig down rather than attempting to reconstitute
466    // it.
467
468    XWindowAttributes attributes;
469    if (!XGetWindowAttributes(
470        g_display,
471        window_,
472        &attributes)) {
473      LOG(ERROR) << "XGetWindowAttributes failed for window " <<
474          window_ << ".";
475      return NULL;
476    }
477
478    int visual_id = XVisualIDFromVisual(attributes.visual);
479
480    int num_elements = 0;
481    scoped_ptr_malloc<GLXFBConfig, ScopedPtrXFree> configs(
482        glXGetFBConfigs(g_display,
483                        DefaultScreen(g_display),
484                        &num_elements));
485    if (!configs.get()) {
486      LOG(ERROR) << "glXGetFBConfigs failed.";
487      return NULL;
488    }
489    if (!num_elements) {
490      LOG(ERROR) << "glXGetFBConfigs returned 0 elements.";
491      return NULL;
492    }
493    bool found = false;
494    int i;
495    for (i = 0; i < num_elements; ++i) {
496      int value;
497      if (glXGetFBConfigAttrib(
498              g_display, configs.get()[i], GLX_VISUAL_ID, &value)) {
499        LOG(ERROR) << "glXGetFBConfigAttrib failed.";
500        return NULL;
501      }
502      if (value == visual_id) {
503        found = true;
504        break;
505      }
506    }
507    if (found) {
508      config_ = configs.get()[i];
509    }
510  }
511
512  return config_;
513}
514
515bool NativeViewGLSurfaceGLX::PostSubBuffer(
516    int x, int y, int width, int height) {
517  DCHECK(gfx::g_driver_glx.ext.b_GLX_MESA_copy_sub_buffer);
518  glXCopySubBufferMESA(g_display, window_, x, y, width, height);
519  return true;
520}
521
522VSyncProvider* NativeViewGLSurfaceGLX::GetVSyncProvider() {
523  return vsync_provider_.get();
524}
525
526NativeViewGLSurfaceGLX::NativeViewGLSurfaceGLX()
527  : window_(0),
528    config_(NULL) {
529}
530
531NativeViewGLSurfaceGLX::~NativeViewGLSurfaceGLX() {
532  Destroy();
533}
534
535PbufferGLSurfaceGLX::PbufferGLSurfaceGLX(const gfx::Size& size)
536  : size_(size),
537    config_(NULL),
538    pbuffer_(0) {
539}
540
541bool PbufferGLSurfaceGLX::Initialize() {
542  DCHECK(!pbuffer_);
543
544  static const int config_attributes[] = {
545    GLX_BUFFER_SIZE, 32,
546    GLX_ALPHA_SIZE, 8,
547    GLX_BLUE_SIZE, 8,
548    GLX_GREEN_SIZE, 8,
549    GLX_RED_SIZE, 8,
550    GLX_RENDER_TYPE, GLX_RGBA_BIT,
551    GLX_DRAWABLE_TYPE, GLX_PBUFFER_BIT,
552    GLX_DOUBLEBUFFER, False,
553    0
554  };
555
556  int num_elements = 0;
557  scoped_ptr_malloc<GLXFBConfig, ScopedPtrXFree> configs(
558      glXChooseFBConfig(g_display,
559                        DefaultScreen(g_display),
560                        config_attributes,
561                        &num_elements));
562  if (!configs.get()) {
563    LOG(ERROR) << "glXChooseFBConfig failed.";
564    return false;
565  }
566  if (!num_elements) {
567    LOG(ERROR) << "glXChooseFBConfig returned 0 elements.";
568    return false;
569  }
570
571  config_ = configs.get()[0];
572
573  const int pbuffer_attributes[] = {
574    GLX_PBUFFER_WIDTH, size_.width(),
575    GLX_PBUFFER_HEIGHT, size_.height(),
576    0
577  };
578  pbuffer_ = glXCreatePbuffer(g_display,
579                              static_cast<GLXFBConfig>(config_),
580                              pbuffer_attributes);
581  if (!pbuffer_) {
582    Destroy();
583    LOG(ERROR) << "glXCreatePbuffer failed.";
584    return false;
585  }
586
587  return true;
588}
589
590void PbufferGLSurfaceGLX::Destroy() {
591  if (pbuffer_) {
592    glXDestroyPbuffer(g_display, pbuffer_);
593    pbuffer_ = 0;
594  }
595
596  config_ = NULL;
597}
598
599bool PbufferGLSurfaceGLX::IsOffscreen() {
600  return true;
601}
602
603bool PbufferGLSurfaceGLX::SwapBuffers() {
604  NOTREACHED() << "Attempted to call SwapBuffers on a pbuffer.";
605  return false;
606}
607
608gfx::Size PbufferGLSurfaceGLX::GetSize() {
609  return size_;
610}
611
612void* PbufferGLSurfaceGLX::GetHandle() {
613  return reinterpret_cast<void*>(pbuffer_);
614}
615
616void* PbufferGLSurfaceGLX::GetConfig() {
617  return config_;
618}
619
620PbufferGLSurfaceGLX::~PbufferGLSurfaceGLX() {
621  Destroy();
622}
623
624}  // namespace gfx
625