Home | History | Annotate | Download | only in gl
      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 
      5 extern "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/message_loop.h"
     17 #include "base/synchronization/cancellation_flag.h"
     18 #include "base/synchronization/lock.h"
     19 #include "base/threading/non_thread_safe.h"
     20 #include "base/threading/thread.h"
     21 #include "base/time/time.h"
     22 #include "third_party/mesa/src/include/GL/osmesa.h"
     23 #include "ui/base/x/x11_util.h"
     24 #include "ui/gl/gl_bindings.h"
     25 #include "ui/gl/gl_implementation.h"
     26 #include "ui/gl/vsync_provider.h"
     27 
     28 namespace gfx {
     29 
     30 namespace {
     31 
     32 // scoped_ptr functor for XFree(). Use as follows:
     33 //   scoped_ptr_malloc<XVisualInfo, ScopedPtrXFree> foo(...);
     34 // where "XVisualInfo" is any X type that is freed with XFree.
     35 class ScopedPtrXFree {
     36  public:
     37   void operator()(void* x) const {
     38     ::XFree(x);
     39   }
     40 };
     41 
     42 Display* g_display;
     43 const char* g_glx_extensions = NULL;
     44 bool g_glx_context_create = false;
     45 bool g_glx_create_context_robustness_supported = false;
     46 bool g_glx_texture_from_pixmap_supported = false;
     47 bool g_glx_oml_sync_control_supported = false;
     48 
     49 // Track support of glXGetMscRateOML separately from GLX_OML_sync_control as a
     50 // whole since on some platforms (e.g. crosbug.com/34585), glXGetMscRateOML
     51 // always fails even though GLX_OML_sync_control is reported as being supported.
     52 bool g_glx_get_msc_rate_oml_supported = false;
     53 
     54 bool g_glx_sgi_video_sync_supported = false;
     55 
     56 class OMLSyncControlVSyncProvider
     57     : public gfx::SyncControlVSyncProvider {
     58  public:
     59   explicit OMLSyncControlVSyncProvider(gfx::AcceleratedWidget window)
     60       : SyncControlVSyncProvider(),
     61         window_(window) {
     62   }
     63 
     64   virtual ~OMLSyncControlVSyncProvider() { }
     65 
     66  protected:
     67   virtual bool GetSyncValues(int64* system_time,
     68                              int64* media_stream_counter,
     69                              int64* swap_buffer_counter) OVERRIDE {
     70     return glXGetSyncValuesOML(g_display, window_, system_time,
     71                                media_stream_counter, swap_buffer_counter);
     72   }
     73 
     74   virtual bool GetMscRate(int32* numerator, int32* denominator) OVERRIDE {
     75     if (!g_glx_get_msc_rate_oml_supported)
     76       return false;
     77 
     78     if (!glXGetMscRateOML(g_display, window_, numerator, denominator)) {
     79       // Once glXGetMscRateOML has been found to fail, don't try again,
     80       // since each failing call may spew an error message.
     81       g_glx_get_msc_rate_oml_supported = false;
     82       return false;
     83     }
     84 
     85     return true;
     86   }
     87 
     88  private:
     89   XID window_;
     90 
     91   DISALLOW_COPY_AND_ASSIGN(OMLSyncControlVSyncProvider);
     92 };
     93 
     94 class SGIVideoSyncThread
     95      : public base::Thread,
     96        public base::NonThreadSafe,
     97        public base::RefCounted<SGIVideoSyncThread> {
     98  public:
     99   static scoped_refptr<SGIVideoSyncThread> Create() {
    100     if (!g_video_sync_thread) {
    101       g_video_sync_thread = new SGIVideoSyncThread();
    102       g_video_sync_thread->Start();
    103     }
    104     return g_video_sync_thread;
    105   }
    106 
    107  private:
    108   friend class base::RefCounted<SGIVideoSyncThread>;
    109 
    110   SGIVideoSyncThread() : base::Thread("SGI_video_sync") {
    111     DCHECK(CalledOnValidThread());
    112   }
    113 
    114   virtual ~SGIVideoSyncThread() {
    115     DCHECK(CalledOnValidThread());
    116     g_video_sync_thread = NULL;
    117     Stop();
    118   }
    119 
    120   static SGIVideoSyncThread* g_video_sync_thread;
    121 
    122   DISALLOW_COPY_AND_ASSIGN(SGIVideoSyncThread);
    123 };
    124 
    125 class SGIVideoSyncProviderThreadShim {
    126  public:
    127   explicit SGIVideoSyncProviderThreadShim(XID window)
    128       : window_(window),
    129         context_(NULL),
    130         message_loop_(base::MessageLoopProxy::current()),
    131         cancel_vsync_flag_(),
    132         vsync_lock_() {
    133     // This ensures that creation of |window_| has occured when this shim
    134     // is executing in the same process as the call to create |window_|.
    135     XSync(g_display, False);
    136   }
    137 
    138   virtual ~SGIVideoSyncProviderThreadShim() {
    139     if (context_) {
    140       glXDestroyContext(display_, context_);
    141       context_ = NULL;
    142     }
    143   }
    144 
    145   base::CancellationFlag* cancel_vsync_flag() {
    146     return &cancel_vsync_flag_;
    147   }
    148 
    149   base::Lock* vsync_lock() {
    150     return &vsync_lock_;
    151   }
    152 
    153   void Initialize() {
    154     DCHECK(display_);
    155 
    156     XWindowAttributes attributes;
    157     if (!XGetWindowAttributes(display_, window_, &attributes)) {
    158       LOG(ERROR) << "XGetWindowAttributes failed for window " <<
    159         window_ << ".";
    160       return;
    161     }
    162 
    163     XVisualInfo visual_info_template;
    164     visual_info_template.visualid = XVisualIDFromVisual(attributes.visual);
    165 
    166     int visual_info_count = 0;
    167     scoped_ptr_malloc<XVisualInfo, ScopedPtrXFree> visual_info_list(
    168         XGetVisualInfo(display_, VisualIDMask,
    169                        &visual_info_template, &visual_info_count));
    170 
    171     DCHECK(visual_info_list.get());
    172     if (visual_info_count == 0) {
    173       LOG(ERROR) << "No visual info for visual ID.";
    174       return;
    175     }
    176 
    177     context_ = glXCreateContext(display_, visual_info_list.get(), NULL, True);
    178 
    179     DCHECK(NULL != context_);
    180   }
    181 
    182   void GetVSyncParameters(const VSyncProvider::UpdateVSyncCallback& callback) {
    183     base::TimeTicks now;
    184     {
    185       // Don't allow |window_| destruction while we're probing vsync.
    186       base::AutoLock locked(vsync_lock_);
    187 
    188       if (!context_ || cancel_vsync_flag_.IsSet())
    189         return;
    190 
    191       glXMakeCurrent(display_, window_, context_);
    192 
    193       unsigned int retrace_count = 0;
    194       if (glXWaitVideoSyncSGI(1, 0, &retrace_count) != 0)
    195         return;
    196 
    197       TRACE_EVENT_INSTANT0("gpu", "vblank", TRACE_EVENT_SCOPE_THREAD);
    198       now = base::TimeTicks::HighResNow();
    199 
    200       glXMakeCurrent(display_, 0, 0);
    201     }
    202 
    203     const base::TimeDelta kDefaultInterval =
    204         base::TimeDelta::FromSeconds(1) / 60;
    205 
    206     message_loop_->PostTask(
    207         FROM_HERE, base::Bind(callback, now, kDefaultInterval));
    208   }
    209 
    210  private:
    211   // For initialization of display_ in GLSurface::InitializeOneOff before
    212   // the sandbox goes up.
    213   friend class gfx::GLSurfaceGLX;
    214 
    215   static Display* display_;
    216 
    217   XID window_;
    218   GLXContext context_;
    219 
    220   scoped_refptr<base::MessageLoopProxy> message_loop_;
    221 
    222   base::CancellationFlag cancel_vsync_flag_;
    223   base::Lock vsync_lock_;
    224 
    225   DISALLOW_COPY_AND_ASSIGN(SGIVideoSyncProviderThreadShim);
    226 };
    227 
    228 class SGIVideoSyncVSyncProvider
    229     : public gfx::VSyncProvider,
    230       public base::SupportsWeakPtr<SGIVideoSyncVSyncProvider> {
    231  public:
    232   explicit SGIVideoSyncVSyncProvider(gfx::AcceleratedWidget window)
    233       : vsync_thread_(SGIVideoSyncThread::Create()),
    234         shim_(new SGIVideoSyncProviderThreadShim(window)),
    235         cancel_vsync_flag_(shim_->cancel_vsync_flag()),
    236         vsync_lock_(shim_->vsync_lock()) {
    237     vsync_thread_->message_loop()->PostTask(
    238         FROM_HERE,
    239         base::Bind(&SGIVideoSyncProviderThreadShim::Initialize,
    240                    base::Unretained(shim_.get())));
    241   }
    242 
    243   virtual ~SGIVideoSyncVSyncProvider() {
    244     {
    245       base::AutoLock locked(*vsync_lock_);
    246       cancel_vsync_flag_->Set();
    247     }
    248 
    249     // Hand-off |shim_| to be deleted on the |vsync_thread_|.
    250     vsync_thread_->message_loop()->DeleteSoon(
    251         FROM_HERE,
    252         shim_.release());
    253   }
    254 
    255   virtual void GetVSyncParameters(
    256       const VSyncProvider::UpdateVSyncCallback& callback) OVERRIDE {
    257     // Only one outstanding request per surface.
    258     if (!pending_callback_) {
    259       pending_callback_.reset(
    260           new VSyncProvider::UpdateVSyncCallback(callback));
    261       vsync_thread_->message_loop()->PostTask(
    262           FROM_HERE,
    263           base::Bind(&SGIVideoSyncProviderThreadShim::GetVSyncParameters,
    264                      base::Unretained(shim_.get()),
    265                      base::Bind(
    266                          &SGIVideoSyncVSyncProvider::PendingCallbackRunner,
    267                          AsWeakPtr())));
    268     }
    269   }
    270 
    271  private:
    272   void PendingCallbackRunner(const base::TimeTicks timebase,
    273                              const base::TimeDelta interval) {
    274     DCHECK(pending_callback_);
    275     pending_callback_->Run(timebase, interval);
    276     pending_callback_.reset();
    277   }
    278 
    279   scoped_refptr<SGIVideoSyncThread> vsync_thread_;
    280 
    281   // Thread shim through which the sync provider is accessed on |vsync_thread_|.
    282   scoped_ptr<SGIVideoSyncProviderThreadShim> shim_;
    283 
    284   scoped_ptr<VSyncProvider::UpdateVSyncCallback> pending_callback_;
    285 
    286   // Raw pointers to sync primitives owned by the shim_.
    287   // These will only be referenced before we post a task to destroy
    288   // the shim_, so they are safe to access.
    289   base::CancellationFlag* cancel_vsync_flag_;
    290   base::Lock* vsync_lock_;
    291 
    292   DISALLOW_COPY_AND_ASSIGN(SGIVideoSyncVSyncProvider);
    293 };
    294 
    295 SGIVideoSyncThread* SGIVideoSyncThread::g_video_sync_thread = NULL;
    296 
    297 // In order to take advantage of GLX_SGI_video_sync, we need a display
    298 // for use on a separate thread. We must allocate this before the sandbox
    299 // goes up (rather than on-demand when we start the thread).
    300 Display* SGIVideoSyncProviderThreadShim::display_ = NULL;
    301 
    302 }  // namespace
    303 
    304 GLSurfaceGLX::GLSurfaceGLX() {}
    305 
    306 bool GLSurfaceGLX::InitializeOneOff() {
    307   static bool initialized = false;
    308   if (initialized)
    309     return true;
    310 
    311   // http://crbug.com/245466
    312   setenv("force_s3tc_enable", "true", 1);
    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
    356 const char* GLSurfaceGLX::GetGLXExtensions() {
    357   return g_glx_extensions;
    358 }
    359 
    360 // static
    361 bool GLSurfaceGLX::HasGLXExtension(const char* name) {
    362   return ExtensionsContain(GetGLXExtensions(), name);
    363 }
    364 
    365 // static
    366 bool GLSurfaceGLX::IsCreateContextSupported() {
    367   return g_glx_context_create;
    368 }
    369 
    370 // static
    371 bool GLSurfaceGLX::IsCreateContextRobustnessSupported() {
    372   return g_glx_create_context_robustness_supported;
    373 }
    374 
    375 // static
    376 bool GLSurfaceGLX::IsTextureFromPixmapSupported() {
    377   return g_glx_texture_from_pixmap_supported;
    378 }
    379 
    380 // static
    381 bool GLSurfaceGLX::IsOMLSyncControlSupported() {
    382   return g_glx_oml_sync_control_supported;
    383 }
    384 
    385 void* GLSurfaceGLX::GetDisplay() {
    386   return g_display;
    387 }
    388 
    389 GLSurfaceGLX::~GLSurfaceGLX() {}
    390 
    391 NativeViewGLSurfaceGLX::NativeViewGLSurfaceGLX(gfx::AcceleratedWidget window)
    392   : window_(window),
    393     config_(NULL) {
    394 }
    395 
    396 bool 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 
    412 void NativeViewGLSurfaceGLX::Destroy() {
    413 }
    414 
    415 bool NativeViewGLSurfaceGLX::Resize(const gfx::Size& size) {
    416   size_ = size;
    417   return true;
    418 }
    419 
    420 bool NativeViewGLSurfaceGLX::IsOffscreen() {
    421   return false;
    422 }
    423 
    424 bool NativeViewGLSurfaceGLX::SwapBuffers() {
    425   glXSwapBuffers(g_display, window_);
    426   return true;
    427 }
    428 
    429 gfx::Size NativeViewGLSurfaceGLX::GetSize() {
    430   return size_;
    431 }
    432 
    433 void* NativeViewGLSurfaceGLX::GetHandle() {
    434   return reinterpret_cast<void*>(window_);
    435 }
    436 
    437 std::string NativeViewGLSurfaceGLX::GetExtensions() {
    438   std::string extensions = GLSurface::GetExtensions();
    439   if (gfx::g_driver_glx.ext.b_GLX_MESA_copy_sub_buffer) {
    440     extensions += extensions.empty() ? "" : " ";
    441     extensions += "GL_CHROMIUM_post_sub_buffer";
    442   }
    443   return extensions;
    444 }
    445 
    446 void* NativeViewGLSurfaceGLX::GetConfig() {
    447   if (!config_) {
    448     // This code path is expensive, but we only take it when
    449     // attempting to use GLX_ARB_create_context_robustness, in which
    450     // case we need a GLXFBConfig for the window in order to create a
    451     // context for it.
    452     //
    453     // TODO(kbr): this is not a reliable code path. On platforms which
    454     // support it, we should use glXChooseFBConfig in the browser
    455     // process to choose the FBConfig and from there the X Visual to
    456     // use when creating the window in the first place. Then we can
    457     // pass that FBConfig down rather than attempting to reconstitute
    458     // it.
    459 
    460     XWindowAttributes attributes;
    461     if (!XGetWindowAttributes(
    462         g_display,
    463         window_,
    464         &attributes)) {
    465       LOG(ERROR) << "XGetWindowAttributes failed for window " <<
    466           window_ << ".";
    467       return NULL;
    468     }
    469 
    470     int visual_id = XVisualIDFromVisual(attributes.visual);
    471 
    472     int num_elements = 0;
    473     scoped_ptr_malloc<GLXFBConfig, ScopedPtrXFree> configs(
    474         glXGetFBConfigs(g_display,
    475                         DefaultScreen(g_display),
    476                         &num_elements));
    477     if (!configs.get()) {
    478       LOG(ERROR) << "glXGetFBConfigs failed.";
    479       return NULL;
    480     }
    481     if (!num_elements) {
    482       LOG(ERROR) << "glXGetFBConfigs returned 0 elements.";
    483       return NULL;
    484     }
    485     bool found = false;
    486     int i;
    487     for (i = 0; i < num_elements; ++i) {
    488       int value;
    489       if (glXGetFBConfigAttrib(
    490               g_display, configs.get()[i], GLX_VISUAL_ID, &value)) {
    491         LOG(ERROR) << "glXGetFBConfigAttrib failed.";
    492         return NULL;
    493       }
    494       if (value == visual_id) {
    495         found = true;
    496         break;
    497       }
    498     }
    499     if (found) {
    500       config_ = configs.get()[i];
    501     }
    502   }
    503 
    504   return config_;
    505 }
    506 
    507 bool NativeViewGLSurfaceGLX::PostSubBuffer(
    508     int x, int y, int width, int height) {
    509   DCHECK(gfx::g_driver_glx.ext.b_GLX_MESA_copy_sub_buffer);
    510   glXCopySubBufferMESA(g_display, window_, x, y, width, height);
    511   return true;
    512 }
    513 
    514 VSyncProvider* NativeViewGLSurfaceGLX::GetVSyncProvider() {
    515   return vsync_provider_.get();
    516 }
    517 
    518 NativeViewGLSurfaceGLX::NativeViewGLSurfaceGLX()
    519   : window_(0),
    520     config_(NULL) {
    521 }
    522 
    523 NativeViewGLSurfaceGLX::~NativeViewGLSurfaceGLX() {
    524   Destroy();
    525 }
    526 
    527 PbufferGLSurfaceGLX::PbufferGLSurfaceGLX(const gfx::Size& size)
    528   : size_(size),
    529     config_(NULL),
    530     pbuffer_(0) {
    531 }
    532 
    533 bool PbufferGLSurfaceGLX::Initialize() {
    534   DCHECK(!pbuffer_);
    535 
    536   static const int config_attributes[] = {
    537     GLX_BUFFER_SIZE, 32,
    538     GLX_ALPHA_SIZE, 8,
    539     GLX_BLUE_SIZE, 8,
    540     GLX_GREEN_SIZE, 8,
    541     GLX_RED_SIZE, 8,
    542     GLX_RENDER_TYPE, GLX_RGBA_BIT,
    543     GLX_DRAWABLE_TYPE, GLX_PBUFFER_BIT,
    544     GLX_DOUBLEBUFFER, False,
    545     0
    546   };
    547 
    548   int num_elements = 0;
    549   scoped_ptr_malloc<GLXFBConfig, ScopedPtrXFree> configs(
    550       glXChooseFBConfig(g_display,
    551                         DefaultScreen(g_display),
    552                         config_attributes,
    553                         &num_elements));
    554   if (!configs.get()) {
    555     LOG(ERROR) << "glXChooseFBConfig failed.";
    556     return false;
    557   }
    558   if (!num_elements) {
    559     LOG(ERROR) << "glXChooseFBConfig returned 0 elements.";
    560     return false;
    561   }
    562 
    563   config_ = configs.get()[0];
    564 
    565   const int pbuffer_attributes[] = {
    566     GLX_PBUFFER_WIDTH, size_.width(),
    567     GLX_PBUFFER_HEIGHT, size_.height(),
    568     0
    569   };
    570   pbuffer_ = glXCreatePbuffer(g_display,
    571                               static_cast<GLXFBConfig>(config_),
    572                               pbuffer_attributes);
    573   if (!pbuffer_) {
    574     Destroy();
    575     LOG(ERROR) << "glXCreatePbuffer failed.";
    576     return false;
    577   }
    578 
    579   return true;
    580 }
    581 
    582 void PbufferGLSurfaceGLX::Destroy() {
    583   if (pbuffer_) {
    584     glXDestroyPbuffer(g_display, pbuffer_);
    585     pbuffer_ = 0;
    586   }
    587 
    588   config_ = NULL;
    589 }
    590 
    591 bool PbufferGLSurfaceGLX::IsOffscreen() {
    592   return true;
    593 }
    594 
    595 bool PbufferGLSurfaceGLX::SwapBuffers() {
    596   NOTREACHED() << "Attempted to call SwapBuffers on a pbuffer.";
    597   return false;
    598 }
    599 
    600 gfx::Size PbufferGLSurfaceGLX::GetSize() {
    601   return size_;
    602 }
    603 
    604 void* PbufferGLSurfaceGLX::GetHandle() {
    605   return reinterpret_cast<void*>(pbuffer_);
    606 }
    607 
    608 void* PbufferGLSurfaceGLX::GetConfig() {
    609   return config_;
    610 }
    611 
    612 PbufferGLSurfaceGLX::~PbufferGLSurfaceGLX() {
    613   Destroy();
    614 }
    615 
    616 }  // namespace gfx
    617