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/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/gfx/x/x11_types.h"
     25 #include "ui/gl/gl_bindings.h"
     26 #include "ui/gl/gl_implementation.h"
     27 #include "ui/gl/sync_control_vsync_provider.h"
     28 
     29 namespace gfx {
     30 
     31 namespace {
     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.
     36 class ScopedPtrXFree {
     37  public:
     38   void operator()(void* x) const {
     39     ::XFree(x);
     40   }
     41 };
     42 
     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;
     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.
     53 bool g_glx_get_msc_rate_oml_supported = false;
     54 
     55 bool g_glx_sgi_video_sync_supported = false;
     56 
     57 class 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 
     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();
    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 
    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);
    137   }
    138 
    139   virtual ~SGIVideoSyncProviderThreadShim() {
    140     if (context_) {
    141       glXDestroyContext(display_, context_);
    142       context_ = NULL;
    143     }
    144   }
    145 
    146   base::CancellationFlag* cancel_vsync_flag() {
    147     return &cancel_vsync_flag_;
    148   }
    149 
    150   base::Lock* vsync_lock() {
    151     return &vsync_lock_;
    152   }
    153 
    154   void Initialize() {
    155     DCHECK(display_);
    156 
    157     XWindowAttributes attributes;
    158     if (!XGetWindowAttributes(display_, window_, &attributes)) {
    159       LOG(ERROR) << "XGetWindowAttributes failed for window " <<
    160         window_ << ".";
    161       return;
    162     }
    163 
    164     XVisualInfo visual_info_template;
    165     visual_info_template.visualid = XVisualIDFromVisual(attributes.visual);
    166 
    167     int visual_info_count = 0;
    168     scoped_ptr_malloc<XVisualInfo, ScopedPtrXFree> visual_info_list(
    169         XGetVisualInfo(display_, VisualIDMask,
    170                        &visual_info_template, &visual_info_count));
    171 
    172     DCHECK(visual_info_list.get());
    173     if (visual_info_count == 0) {
    174       LOG(ERROR) << "No visual info for visual ID.";
    175       return;
    176     }
    177 
    178     context_ = glXCreateContext(display_, visual_info_list.get(), NULL, True);
    179 
    180     DCHECK(NULL != context_);
    181   }
    182 
    183   void GetVSyncParameters(const VSyncProvider::UpdateVSyncCallback& callback) {
    184     base::TimeTicks now;
    185     {
    186       // Don't allow |window_| destruction while we're probing vsync.
    187       base::AutoLock locked(vsync_lock_);
    188 
    189       if (!context_ || cancel_vsync_flag_.IsSet())
    190         return;
    191 
    192       glXMakeCurrent(display_, window_, context_);
    193 
    194       unsigned int retrace_count = 0;
    195       if (glXWaitVideoSyncSGI(1, 0, &retrace_count) != 0)
    196         return;
    197 
    198       TRACE_EVENT_INSTANT0("gpu", "vblank", TRACE_EVENT_SCOPE_THREAD);
    199       now = base::TimeTicks::HighResNow();
    200 
    201       glXMakeCurrent(display_, 0, 0);
    202     }
    203 
    204     const base::TimeDelta kDefaultInterval =
    205         base::TimeDelta::FromSeconds(1) / 60;
    206 
    207     message_loop_->PostTask(
    208         FROM_HERE, base::Bind(callback, now, kDefaultInterval));
    209   }
    210 
    211  private:
    212   // For initialization of display_ in GLSurface::InitializeOneOff before
    213   // the sandbox goes up.
    214   friend class gfx::GLSurfaceGLX;
    215 
    216   static Display* display_;
    217 
    218   XID window_;
    219   GLXContext context_;
    220 
    221   scoped_refptr<base::MessageLoopProxy> message_loop_;
    222 
    223   base::CancellationFlag cancel_vsync_flag_;
    224   base::Lock vsync_lock_;
    225 
    226   DISALLOW_COPY_AND_ASSIGN(SGIVideoSyncProviderThreadShim);
    227 };
    228 
    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())));
    242   }
    243 
    244   virtual ~SGIVideoSyncVSyncProvider() {
    245     {
    246       base::AutoLock locked(*vsync_lock_);
    247       cancel_vsync_flag_->Set();
    248     }
    249 
    250     // Hand-off |shim_| to be deleted on the |vsync_thread_|.
    251     vsync_thread_->message_loop()->DeleteSoon(
    252         FROM_HERE,
    253         shim_.release());
    254   }
    255 
    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())));
    269     }
    270   }
    271 
    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();
    278   }
    279 
    280   scoped_refptr<SGIVideoSyncThread> vsync_thread_;
    281 
    282   // Thread shim through which the sync provider is accessed on |vsync_thread_|.
    283   scoped_ptr<SGIVideoSyncProviderThreadShim> shim_;
    284 
    285   scoped_ptr<VSyncProvider::UpdateVSyncCallback> pending_callback_;
    286 
    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_;
    292 
    293   DISALLOW_COPY_AND_ASSIGN(SGIVideoSyncVSyncProvider);
    294 };
    295 
    296 SGIVideoSyncThread* SGIVideoSyncThread::g_video_sync_thread = NULL;
    297 
    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;
    302 
    303 #if defined(TOOLKIT_GTK)
    304 // A mechanism for forwarding XExpose events from one window to another.
    305 // Because in the workaround for http://crbug.com/145600 the child window
    306 // is placed on top of the parent window, only the child window will receive
    307 // all expose events. These need to be forwared to the parent window to inform
    308 // it that it should paint.
    309 class XExposeEventForwarder : public base::MessagePumpObserver {
    310  public:
    311   XExposeEventForwarder() {}
    312   virtual ~XExposeEventForwarder() {
    313     DCHECK(child_to_parent_map_.empty());
    314   }
    315   void AddParentChildPair(gfx::AcceleratedWidget parent_window,
    316                           gfx::AcceleratedWidget child_window) {
    317     if (child_to_parent_map_.empty())
    318       base::MessagePumpX11::Current()->AddObserver(this);
    319 
    320     DCHECK(child_to_parent_map_.find(child_window) ==
    321            child_to_parent_map_.end());
    322     child_to_parent_map_.insert(std::make_pair(
    323         child_window, parent_window));
    324   }
    325   void RemoveParentChildPair(gfx::AcceleratedWidget parent_window,
    326                              gfx::AcceleratedWidget child_window) {
    327     DCHECK(child_to_parent_map_.find(child_window) !=
    328            child_to_parent_map_.end());
    329     child_to_parent_map_.erase(child_window);
    330 
    331     if (child_to_parent_map_.empty())
    332       base::MessagePumpX11::Current()->RemoveObserver(this);
    333   }
    334 
    335  private:
    336   virtual base::EventStatus WillProcessEvent (
    337       const base::NativeEvent& xevent) OVERRIDE {
    338     if (xevent->type != Expose)
    339       return base::EVENT_CONTINUE;
    340 
    341     WindowMap::const_iterator found = child_to_parent_map_.find(
    342         xevent->xexpose.window);
    343     if (found == child_to_parent_map_.end())
    344       return base::EVENT_CONTINUE;
    345 
    346     gfx::AcceleratedWidget target_window = found->second;
    347     XEvent forwarded_event = *xevent;
    348     forwarded_event.xexpose.window = target_window;
    349     XSendEvent(g_display, target_window, False, ExposureMask,
    350                &forwarded_event);
    351     return base::EVENT_CONTINUE;
    352   }
    353   virtual void DidProcessEvent(const base::NativeEvent& xevent) OVERRIDE {
    354   }
    355 
    356   typedef std::map<gfx::AcceleratedWidget, gfx::AcceleratedWidget> WindowMap;
    357   WindowMap child_to_parent_map_;
    358 
    359   DISALLOW_COPY_AND_ASSIGN(XExposeEventForwarder);
    360 };
    361 
    362 static base::LazyInstance<XExposeEventForwarder> g_xexpose_event_forwarder =
    363     LAZY_INSTANCE_INITIALIZER;
    364 
    365 // Do not use this workaround when running in test harnesses that do not have
    366 // a message loop or do not have a TYPE_GPU message loop.
    367 bool g_create_child_windows = false;
    368 #endif
    369 
    370 }  // namespace
    371 
    372 GLSurfaceGLX::GLSurfaceGLX() {}
    373 
    374 bool GLSurfaceGLX::InitializeOneOff() {
    375   static bool initialized = false;
    376   if (initialized)
    377     return true;
    378 
    379   // http://crbug.com/245466
    380   setenv("force_s3tc_enable", "true", 1);
    381 
    382   // SGIVideoSyncProviderShim (if instantiated) will issue X commands on
    383   // it's own thread.
    384   XInitThreads();
    385 
    386 #if defined(TOOLKIT_GTK)
    387   // Be sure to use the X display handle and not the GTK display handle if this
    388   // is the GPU process.
    389   g_create_child_windows =
    390       base::MessageLoop::current() &&
    391       base::MessageLoop::current()->type() == base::MessageLoop::TYPE_GPU;
    392 
    393   if (g_create_child_windows)
    394     g_display = base::MessagePumpX11::GetDefaultXDisplay();
    395   else
    396     g_display = base::MessagePumpForUI::GetDefaultXDisplay();
    397 #else
    398   g_display = base::MessagePumpForUI::GetDefaultXDisplay();
    399 #endif
    400 
    401   if (!g_display) {
    402     LOG(ERROR) << "XOpenDisplay failed.";
    403     return false;
    404   }
    405 
    406   int major, minor;
    407   if (!glXQueryVersion(g_display, &major, &minor)) {
    408     LOG(ERROR) << "glxQueryVersion failed";
    409     return false;
    410   }
    411 
    412   if (major == 1 && minor < 3) {
    413     LOG(ERROR) << "GLX 1.3 or later is required.";
    414     return false;
    415   }
    416 
    417   g_glx_extensions = glXQueryExtensionsString(g_display, 0);
    418   g_glx_context_create =
    419       HasGLXExtension("GLX_ARB_create_context");
    420   g_glx_create_context_robustness_supported =
    421       HasGLXExtension("GLX_ARB_create_context_robustness");
    422   g_glx_texture_from_pixmap_supported =
    423       HasGLXExtension("GLX_EXT_texture_from_pixmap");
    424   g_glx_oml_sync_control_supported =
    425       HasGLXExtension("GLX_OML_sync_control");
    426   g_glx_get_msc_rate_oml_supported = g_glx_oml_sync_control_supported;
    427   g_glx_sgi_video_sync_supported =
    428       HasGLXExtension("GLX_SGI_video_sync");
    429 
    430   if (!g_glx_get_msc_rate_oml_supported && g_glx_sgi_video_sync_supported)
    431     SGIVideoSyncProviderThreadShim::display_ = XOpenDisplay(NULL);
    432 
    433   initialized = true;
    434   return true;
    435 }
    436 
    437 // static
    438 const char* GLSurfaceGLX::GetGLXExtensions() {
    439   return g_glx_extensions;
    440 }
    441 
    442 // static
    443 bool GLSurfaceGLX::HasGLXExtension(const char* name) {
    444   return ExtensionsContain(GetGLXExtensions(), name);
    445 }
    446 
    447 // static
    448 bool GLSurfaceGLX::IsCreateContextSupported() {
    449   return g_glx_context_create;
    450 }
    451 
    452 // static
    453 bool GLSurfaceGLX::IsCreateContextRobustnessSupported() {
    454   return g_glx_create_context_robustness_supported;
    455 }
    456 
    457 // static
    458 bool GLSurfaceGLX::IsTextureFromPixmapSupported() {
    459   return g_glx_texture_from_pixmap_supported;
    460 }
    461 
    462 // static
    463 bool GLSurfaceGLX::IsOMLSyncControlSupported() {
    464   return g_glx_oml_sync_control_supported;
    465 }
    466 
    467 void* GLSurfaceGLX::GetDisplay() {
    468   return g_display;
    469 }
    470 
    471 GLSurfaceGLX::~GLSurfaceGLX() {}
    472 
    473 #if defined(TOOLKIT_GTK)
    474 bool NativeViewGLSurfaceGLX::SetBackbufferAllocation(bool allocated) {
    475   backbuffer_allocated_ = allocated;
    476   AdjustBufferAllocation();
    477   return true;
    478 }
    479 
    480 void NativeViewGLSurfaceGLX::SetFrontbufferAllocation(bool allocated) {
    481   frontbuffer_allocated_ = allocated;
    482   AdjustBufferAllocation();
    483 }
    484 
    485 void NativeViewGLSurfaceGLX::AdjustBufferAllocation() {
    486   if (!g_create_child_windows)
    487     return;
    488 
    489   if (frontbuffer_allocated_ || backbuffer_allocated_)
    490     CreateChildWindow();
    491   else
    492     DestroyChildWindow();
    493 }
    494 
    495 void NativeViewGLSurfaceGLX::CreateChildWindow() {
    496   DCHECK(g_create_child_windows);
    497 
    498   if (child_window_)
    499     return;
    500 
    501   XSetWindowAttributes set_window_attributes;
    502   set_window_attributes.event_mask = ExposureMask;
    503   child_window_ = XCreateWindow(
    504       g_display, parent_window_, 0, 0, size_.width(), size_.height(), 0,
    505       CopyFromParent, InputOutput, CopyFromParent, CWEventMask,
    506       &set_window_attributes);
    507   g_xexpose_event_forwarder.Pointer()->AddParentChildPair(
    508       parent_window_, child_window_);
    509 
    510   XMapWindow(g_display, child_window_);
    511   XFlush(g_display);
    512 }
    513 
    514 void NativeViewGLSurfaceGLX::DestroyChildWindow() {
    515   if (!child_window_)
    516     return;
    517 
    518   g_xexpose_event_forwarder.Pointer()->RemoveParentChildPair(
    519       parent_window_, child_window_);
    520   XDestroyWindow(g_display, child_window_);
    521   XFlush(g_display);
    522   child_window_ = 0;
    523 }
    524 #endif
    525 
    526 NativeViewGLSurfaceGLX::NativeViewGLSurfaceGLX(gfx::AcceleratedWidget window)
    527   : parent_window_(window),
    528 #if defined(TOOLKIT_GTK)
    529     child_window_(0),
    530     dummy_window_(0),
    531     backbuffer_allocated_(true),
    532     frontbuffer_allocated_(true),
    533 #endif
    534     config_(NULL) {
    535 }
    536 
    537 gfx::AcceleratedWidget NativeViewGLSurfaceGLX::GetDrawableHandle() const {
    538 #if defined(TOOLKIT_GTK)
    539   if (g_create_child_windows) {
    540     if (child_window_)
    541       return child_window_;
    542     return dummy_window_;
    543   }
    544 #endif
    545   return parent_window_;
    546 }
    547 
    548 bool NativeViewGLSurfaceGLX::Initialize() {
    549   XWindowAttributes attributes;
    550   if (!XGetWindowAttributes(g_display, parent_window_, &attributes)) {
    551     LOG(ERROR) << "XGetWindowAttributes failed for window " << parent_window_
    552         << ".";
    553     return false;
    554   }
    555   size_ = gfx::Size(attributes.width, attributes.height);
    556 
    557   gfx::AcceleratedWidget window_for_vsync = parent_window_;
    558 
    559 #if defined(TOOLKIT_GTK)
    560   if (g_create_child_windows) {
    561     dummy_window_ = XCreateWindow(
    562         g_display,
    563         RootWindow(g_display, XScreenNumberOfScreen(attributes.screen)),
    564         0, 0, 1, 1, 0, CopyFromParent, InputOutput, attributes.visual, 0, NULL);
    565     window_for_vsync = dummy_window_;
    566     CreateChildWindow();
    567   }
    568 #endif
    569 
    570   if (g_glx_oml_sync_control_supported)
    571     vsync_provider_.reset(new OMLSyncControlVSyncProvider(window_for_vsync));
    572   else if (g_glx_sgi_video_sync_supported)
    573     vsync_provider_.reset(new SGIVideoSyncVSyncProvider(window_for_vsync));
    574 
    575   return true;
    576 }
    577 
    578 void NativeViewGLSurfaceGLX::Destroy() {
    579 #if defined(TOOLKIT_GTK)
    580   DestroyChildWindow();
    581   if (dummy_window_)
    582     XDestroyWindow(g_display, dummy_window_);
    583   dummy_window_ = 0;
    584 #endif
    585 }
    586 
    587 bool NativeViewGLSurfaceGLX::Resize(const gfx::Size& size) {
    588 #if defined(TOOLKIT_GTK)
    589   if (child_window_) {
    590     XResizeWindow(g_display, child_window_, size.width(), size.height());
    591     XFlush(g_display);
    592   }
    593 #endif
    594   size_ = size;
    595   return true;
    596 }
    597 
    598 bool NativeViewGLSurfaceGLX::IsOffscreen() {
    599   return false;
    600 }
    601 
    602 bool NativeViewGLSurfaceGLX::SwapBuffers() {
    603   glXSwapBuffers(g_display, GetDrawableHandle());
    604   return true;
    605 }
    606 
    607 gfx::Size NativeViewGLSurfaceGLX::GetSize() {
    608   return size_;
    609 }
    610 
    611 void* NativeViewGLSurfaceGLX::GetHandle() {
    612   return reinterpret_cast<void*>(GetDrawableHandle());
    613 }
    614 
    615 std::string NativeViewGLSurfaceGLX::GetExtensions() {
    616   std::string extensions = GLSurface::GetExtensions();
    617   if (gfx::g_driver_glx.ext.b_GLX_MESA_copy_sub_buffer) {
    618     extensions += extensions.empty() ? "" : " ";
    619     extensions += "GL_CHROMIUM_post_sub_buffer";
    620   }
    621   return extensions;
    622 }
    623 
    624 void* NativeViewGLSurfaceGLX::GetConfig() {
    625   if (!config_) {
    626     // This code path is expensive, but we only take it when
    627     // attempting to use GLX_ARB_create_context_robustness, in which
    628     // case we need a GLXFBConfig for the window in order to create a
    629     // context for it.
    630     //
    631     // TODO(kbr): this is not a reliable code path. On platforms which
    632     // support it, we should use glXChooseFBConfig in the browser
    633     // process to choose the FBConfig and from there the X Visual to
    634     // use when creating the window in the first place. Then we can
    635     // pass that FBConfig down rather than attempting to reconstitute
    636     // it.
    637 
    638     XWindowAttributes attributes;
    639     if (!XGetWindowAttributes(
    640         g_display,
    641         parent_window_,
    642         &attributes)) {
    643       LOG(ERROR) << "XGetWindowAttributes failed for window " <<
    644           parent_window_ << ".";
    645       return NULL;
    646     }
    647 
    648     int visual_id = XVisualIDFromVisual(attributes.visual);
    649 
    650     int num_elements = 0;
    651     scoped_ptr_malloc<GLXFBConfig, ScopedPtrXFree> configs(
    652         glXGetFBConfigs(g_display,
    653                         DefaultScreen(g_display),
    654                         &num_elements));
    655     if (!configs.get()) {
    656       LOG(ERROR) << "glXGetFBConfigs failed.";
    657       return NULL;
    658     }
    659     if (!num_elements) {
    660       LOG(ERROR) << "glXGetFBConfigs returned 0 elements.";
    661       return NULL;
    662     }
    663     bool found = false;
    664     int i;
    665     for (i = 0; i < num_elements; ++i) {
    666       int value;
    667       if (glXGetFBConfigAttrib(
    668               g_display, configs.get()[i], GLX_VISUAL_ID, &value)) {
    669         LOG(ERROR) << "glXGetFBConfigAttrib failed.";
    670         return NULL;
    671       }
    672       if (value == visual_id) {
    673         found = true;
    674         break;
    675       }
    676     }
    677     if (found) {
    678       config_ = configs.get()[i];
    679     }
    680   }
    681 
    682   return config_;
    683 }
    684 
    685 bool NativeViewGLSurfaceGLX::PostSubBuffer(
    686     int x, int y, int width, int height) {
    687   DCHECK(gfx::g_driver_glx.ext.b_GLX_MESA_copy_sub_buffer);
    688   glXCopySubBufferMESA(g_display, GetDrawableHandle(), x, y, width, height);
    689   return true;
    690 }
    691 
    692 VSyncProvider* NativeViewGLSurfaceGLX::GetVSyncProvider() {
    693   return vsync_provider_.get();
    694 }
    695 
    696 NativeViewGLSurfaceGLX::NativeViewGLSurfaceGLX()
    697   : parent_window_(0),
    698 #if defined(TOOLKIT_GTK)
    699     child_window_(0),
    700     dummy_window_(0),
    701     backbuffer_allocated_(true),
    702     frontbuffer_allocated_(true),
    703 #endif
    704     config_(NULL) {
    705 }
    706 
    707 NativeViewGLSurfaceGLX::~NativeViewGLSurfaceGLX() {
    708   Destroy();
    709 }
    710 
    711 PbufferGLSurfaceGLX::PbufferGLSurfaceGLX(const gfx::Size& size)
    712   : size_(size),
    713     config_(NULL),
    714     pbuffer_(0) {
    715 }
    716 
    717 bool PbufferGLSurfaceGLX::Initialize() {
    718   DCHECK(!pbuffer_);
    719 
    720   static const int config_attributes[] = {
    721     GLX_BUFFER_SIZE, 32,
    722     GLX_ALPHA_SIZE, 8,
    723     GLX_BLUE_SIZE, 8,
    724     GLX_GREEN_SIZE, 8,
    725     GLX_RED_SIZE, 8,
    726     GLX_RENDER_TYPE, GLX_RGBA_BIT,
    727     GLX_DRAWABLE_TYPE, GLX_PBUFFER_BIT,
    728     GLX_DOUBLEBUFFER, False,
    729     0
    730   };
    731 
    732   int num_elements = 0;
    733   scoped_ptr_malloc<GLXFBConfig, ScopedPtrXFree> configs(
    734       glXChooseFBConfig(g_display,
    735                         DefaultScreen(g_display),
    736                         config_attributes,
    737                         &num_elements));
    738   if (!configs.get()) {
    739     LOG(ERROR) << "glXChooseFBConfig failed.";
    740     return false;
    741   }
    742   if (!num_elements) {
    743     LOG(ERROR) << "glXChooseFBConfig returned 0 elements.";
    744     return false;
    745   }
    746 
    747   config_ = configs.get()[0];
    748 
    749   const int pbuffer_attributes[] = {
    750     GLX_PBUFFER_WIDTH, size_.width(),
    751     GLX_PBUFFER_HEIGHT, size_.height(),
    752     0
    753   };
    754   pbuffer_ = glXCreatePbuffer(g_display,
    755                               static_cast<GLXFBConfig>(config_),
    756                               pbuffer_attributes);
    757   if (!pbuffer_) {
    758     Destroy();
    759     LOG(ERROR) << "glXCreatePbuffer failed.";
    760     return false;
    761   }
    762 
    763   return true;
    764 }
    765 
    766 void PbufferGLSurfaceGLX::Destroy() {
    767   if (pbuffer_) {
    768     glXDestroyPbuffer(g_display, pbuffer_);
    769     pbuffer_ = 0;
    770   }
    771 
    772   config_ = NULL;
    773 }
    774 
    775 bool PbufferGLSurfaceGLX::IsOffscreen() {
    776   return true;
    777 }
    778 
    779 bool PbufferGLSurfaceGLX::SwapBuffers() {
    780   NOTREACHED() << "Attempted to call SwapBuffers on a pbuffer.";
    781   return false;
    782 }
    783 
    784 gfx::Size PbufferGLSurfaceGLX::GetSize() {
    785   return size_;
    786 }
    787 
    788 void* PbufferGLSurfaceGLX::GetHandle() {
    789   return reinterpret_cast<void*>(pbuffer_);
    790 }
    791 
    792 void* PbufferGLSurfaceGLX::GetConfig() {
    793   return config_;
    794 }
    795 
    796 PbufferGLSurfaceGLX::~PbufferGLSurfaceGLX() {
    797   Destroy();
    798 }
    799 
    800 }  // namespace gfx
    801