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/single_thread_task_runner.h"
     19 #include "base/synchronization/cancellation_flag.h"
     20 #include "base/synchronization/lock.h"
     21 #include "base/thread_task_runner_handle.h"
     22 #include "base/threading/non_thread_safe.h"
     23 #include "base/threading/thread.h"
     24 #include "base/time/time.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"
     31 
     32 namespace gfx {
     33 
     34 namespace {
     35 
     36 // scoped_ptr functor for XFree(). Use as follows:
     37 //   scoped_ptr<XVisualInfo, ScopedPtrXFree> foo(...);
     38 // where "XVisualInfo" is any X type that is freed with XFree.
     39 struct ScopedPtrXFree {
     40   void operator()(void* x) const {
     41     ::XFree(x);
     42   }
     43 };
     44 
     45 Display* g_display = NULL;
     46 const char* g_glx_extensions = NULL;
     47 bool g_glx_context_create = false;
     48 bool g_glx_create_context_robustness_supported = false;
     49 bool g_glx_texture_from_pixmap_supported = false;
     50 bool g_glx_oml_sync_control_supported = false;
     51 
     52 // Track support of glXGetMscRateOML separately from GLX_OML_sync_control as a
     53 // whole since on some platforms (e.g. crosbug.com/34585), glXGetMscRateOML
     54 // always fails even though GLX_OML_sync_control is reported as being supported.
     55 bool g_glx_get_msc_rate_oml_supported = false;
     56 
     57 bool g_glx_sgi_video_sync_supported = false;
     58 
     59 static const base::TimeDelta kGetVSyncParametersMinPeriod =
     60 #if defined(OS_LINUX)
     61     // See crbug.com/373489
     62     // On Linux, querying the vsync parameters might burn CPU for up to an
     63     // entire vsync, so we only query periodically to reduce CPU usage.
     64     // 5 seconds is chosen somewhat abitrarily as a balance between:
     65     //  a) Drift in the phase of our signal.
     66     //  b) Potential janks from periodically pegging the CPU.
     67     base::TimeDelta::FromSeconds(5);
     68 #else
     69     base::TimeDelta::FromSeconds(0);
     70 #endif
     71 
     72 class OMLSyncControlVSyncProvider
     73     : public gfx::SyncControlVSyncProvider {
     74  public:
     75   explicit OMLSyncControlVSyncProvider(gfx::AcceleratedWidget window)
     76       : SyncControlVSyncProvider(),
     77         window_(window) {
     78   }
     79 
     80   virtual ~OMLSyncControlVSyncProvider() { }
     81 
     82  protected:
     83   virtual bool GetSyncValues(int64* system_time,
     84                              int64* media_stream_counter,
     85                              int64* swap_buffer_counter) OVERRIDE {
     86     return glXGetSyncValuesOML(g_display, window_, system_time,
     87                                media_stream_counter, swap_buffer_counter);
     88   }
     89 
     90   virtual bool GetMscRate(int32* numerator, int32* denominator) OVERRIDE {
     91     if (!g_glx_get_msc_rate_oml_supported)
     92       return false;
     93 
     94     if (!glXGetMscRateOML(g_display, window_, numerator, denominator)) {
     95       // Once glXGetMscRateOML has been found to fail, don't try again,
     96       // since each failing call may spew an error message.
     97       g_glx_get_msc_rate_oml_supported = false;
     98       return false;
     99     }
    100 
    101     return true;
    102   }
    103 
    104  private:
    105   XID window_;
    106 
    107   DISALLOW_COPY_AND_ASSIGN(OMLSyncControlVSyncProvider);
    108 };
    109 
    110 class SGIVideoSyncThread
    111      : public base::Thread,
    112        public base::NonThreadSafe,
    113        public base::RefCounted<SGIVideoSyncThread> {
    114  public:
    115   static scoped_refptr<SGIVideoSyncThread> Create() {
    116     if (!g_video_sync_thread) {
    117       g_video_sync_thread = new SGIVideoSyncThread();
    118       g_video_sync_thread->Start();
    119     }
    120     return g_video_sync_thread;
    121   }
    122 
    123  private:
    124   friend class base::RefCounted<SGIVideoSyncThread>;
    125 
    126   SGIVideoSyncThread() : base::Thread("SGI_video_sync") {
    127     DCHECK(CalledOnValidThread());
    128   }
    129 
    130   virtual ~SGIVideoSyncThread() {
    131     DCHECK(CalledOnValidThread());
    132     g_video_sync_thread = NULL;
    133     Stop();
    134   }
    135 
    136   static SGIVideoSyncThread* g_video_sync_thread;
    137 
    138   DISALLOW_COPY_AND_ASSIGN(SGIVideoSyncThread);
    139 };
    140 
    141 class SGIVideoSyncProviderThreadShim {
    142  public:
    143   explicit SGIVideoSyncProviderThreadShim(XID window)
    144       : window_(window),
    145         context_(NULL),
    146         task_runner_(base::ThreadTaskRunnerHandle::Get()),
    147         cancel_vsync_flag_(),
    148         vsync_lock_() {
    149     // This ensures that creation of |window_| has occured when this shim
    150     // is executing in the same process as the call to create |window_|.
    151     XSync(g_display, False);
    152   }
    153 
    154   virtual ~SGIVideoSyncProviderThreadShim() {
    155     if (context_) {
    156       glXDestroyContext(display_, context_);
    157       context_ = NULL;
    158     }
    159   }
    160 
    161   base::CancellationFlag* cancel_vsync_flag() {
    162     return &cancel_vsync_flag_;
    163   }
    164 
    165   base::Lock* vsync_lock() {
    166     return &vsync_lock_;
    167   }
    168 
    169   void Initialize() {
    170     DCHECK(display_);
    171 
    172     XWindowAttributes attributes;
    173     if (!XGetWindowAttributes(display_, window_, &attributes)) {
    174       LOG(ERROR) << "XGetWindowAttributes failed for window " <<
    175         window_ << ".";
    176       return;
    177     }
    178 
    179     XVisualInfo visual_info_template;
    180     visual_info_template.visualid = XVisualIDFromVisual(attributes.visual);
    181 
    182     int visual_info_count = 0;
    183     scoped_ptr<XVisualInfo, ScopedPtrXFree> visual_info_list(
    184         XGetVisualInfo(display_, VisualIDMask,
    185                        &visual_info_template, &visual_info_count));
    186 
    187     DCHECK(visual_info_list.get());
    188     if (visual_info_count == 0) {
    189       LOG(ERROR) << "No visual info for visual ID.";
    190       return;
    191     }
    192 
    193     context_ = glXCreateContext(display_, visual_info_list.get(), NULL, True);
    194 
    195     DCHECK(NULL != context_);
    196   }
    197 
    198   void GetVSyncParameters(const VSyncProvider::UpdateVSyncCallback& callback) {
    199     base::TimeTicks now;
    200     {
    201       // Don't allow |window_| destruction while we're probing vsync.
    202       base::AutoLock locked(vsync_lock_);
    203 
    204       if (!context_ || cancel_vsync_flag_.IsSet())
    205         return;
    206 
    207       glXMakeCurrent(display_, window_, context_);
    208 
    209       unsigned int retrace_count = 0;
    210       if (glXWaitVideoSyncSGI(1, 0, &retrace_count) != 0)
    211         return;
    212 
    213       TRACE_EVENT_INSTANT0("gpu", "vblank", TRACE_EVENT_SCOPE_THREAD);
    214       now = base::TimeTicks::HighResNow();
    215 
    216       glXMakeCurrent(display_, 0, 0);
    217     }
    218 
    219     const base::TimeDelta kDefaultInterval =
    220         base::TimeDelta::FromSeconds(1) / 60;
    221 
    222     task_runner_->PostTask(
    223         FROM_HERE, base::Bind(callback, now, kDefaultInterval));
    224   }
    225 
    226  private:
    227   // For initialization of display_ in GLSurface::InitializeOneOff before
    228   // the sandbox goes up.
    229   friend class gfx::GLSurfaceGLX;
    230 
    231   static Display* display_;
    232 
    233   XID window_;
    234   GLXContext context_;
    235 
    236   scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
    237 
    238   base::CancellationFlag cancel_vsync_flag_;
    239   base::Lock vsync_lock_;
    240 
    241   DISALLOW_COPY_AND_ASSIGN(SGIVideoSyncProviderThreadShim);
    242 };
    243 
    244 class SGIVideoSyncVSyncProvider
    245     : public gfx::VSyncProvider,
    246       public base::SupportsWeakPtr<SGIVideoSyncVSyncProvider> {
    247  public:
    248   explicit SGIVideoSyncVSyncProvider(gfx::AcceleratedWidget window)
    249       : vsync_thread_(SGIVideoSyncThread::Create()),
    250         shim_(new SGIVideoSyncProviderThreadShim(window)),
    251         cancel_vsync_flag_(shim_->cancel_vsync_flag()),
    252         vsync_lock_(shim_->vsync_lock()) {
    253     vsync_thread_->message_loop()->PostTask(
    254         FROM_HERE,
    255         base::Bind(&SGIVideoSyncProviderThreadShim::Initialize,
    256                    base::Unretained(shim_.get())));
    257   }
    258 
    259   virtual ~SGIVideoSyncVSyncProvider() {
    260     {
    261       base::AutoLock locked(*vsync_lock_);
    262       cancel_vsync_flag_->Set();
    263     }
    264 
    265     // Hand-off |shim_| to be deleted on the |vsync_thread_|.
    266     vsync_thread_->message_loop()->DeleteSoon(
    267         FROM_HERE,
    268         shim_.release());
    269   }
    270 
    271   virtual void GetVSyncParameters(
    272       const VSyncProvider::UpdateVSyncCallback& callback) OVERRIDE {
    273     if (kGetVSyncParametersMinPeriod > base::TimeDelta()) {
    274       base::TimeTicks now = base::TimeTicks::Now();
    275       base::TimeDelta delta = now - last_get_vsync_parameters_time_;
    276       if (delta < kGetVSyncParametersMinPeriod)
    277         return;
    278       last_get_vsync_parameters_time_ = now;
    279     }
    280 
    281     // Only one outstanding request per surface.
    282     if (!pending_callback_) {
    283       pending_callback_.reset(
    284           new VSyncProvider::UpdateVSyncCallback(callback));
    285       vsync_thread_->message_loop()->PostTask(
    286           FROM_HERE,
    287           base::Bind(&SGIVideoSyncProviderThreadShim::GetVSyncParameters,
    288                      base::Unretained(shim_.get()),
    289                      base::Bind(
    290                          &SGIVideoSyncVSyncProvider::PendingCallbackRunner,
    291                          AsWeakPtr())));
    292     }
    293   }
    294 
    295  private:
    296   void PendingCallbackRunner(const base::TimeTicks timebase,
    297                              const base::TimeDelta interval) {
    298     DCHECK(pending_callback_);
    299     pending_callback_->Run(timebase, interval);
    300     pending_callback_.reset();
    301   }
    302 
    303   scoped_refptr<SGIVideoSyncThread> vsync_thread_;
    304 
    305   // Thread shim through which the sync provider is accessed on |vsync_thread_|.
    306   scoped_ptr<SGIVideoSyncProviderThreadShim> shim_;
    307 
    308   scoped_ptr<VSyncProvider::UpdateVSyncCallback> pending_callback_;
    309 
    310   // Raw pointers to sync primitives owned by the shim_.
    311   // These will only be referenced before we post a task to destroy
    312   // the shim_, so they are safe to access.
    313   base::CancellationFlag* cancel_vsync_flag_;
    314   base::Lock* vsync_lock_;
    315 
    316   base::TimeTicks last_get_vsync_parameters_time_;
    317 
    318   DISALLOW_COPY_AND_ASSIGN(SGIVideoSyncVSyncProvider);
    319 };
    320 
    321 SGIVideoSyncThread* SGIVideoSyncThread::g_video_sync_thread = NULL;
    322 
    323 // In order to take advantage of GLX_SGI_video_sync, we need a display
    324 // for use on a separate thread. We must allocate this before the sandbox
    325 // goes up (rather than on-demand when we start the thread).
    326 Display* SGIVideoSyncProviderThreadShim::display_ = NULL;
    327 
    328 }  // namespace
    329 
    330 GLSurfaceGLX::GLSurfaceGLX() {}
    331 
    332 bool GLSurfaceGLX::InitializeOneOff() {
    333   static bool initialized = false;
    334   if (initialized)
    335     return true;
    336 
    337   // http://crbug.com/245466
    338   setenv("force_s3tc_enable", "true", 1);
    339 
    340   // SGIVideoSyncProviderShim (if instantiated) will issue X commands on
    341   // it's own thread.
    342   gfx::InitializeThreadedX11();
    343   g_display = gfx::GetXDisplay();
    344 
    345   if (!g_display) {
    346     LOG(ERROR) << "XOpenDisplay failed.";
    347     return false;
    348   }
    349 
    350   int major, minor;
    351   if (!glXQueryVersion(g_display, &major, &minor)) {
    352     LOG(ERROR) << "glxQueryVersion failed";
    353     return false;
    354   }
    355 
    356   if (major == 1 && minor < 3) {
    357     LOG(ERROR) << "GLX 1.3 or later is required.";
    358     return false;
    359   }
    360 
    361   g_glx_extensions = glXQueryExtensionsString(g_display, 0);
    362   g_glx_context_create =
    363       HasGLXExtension("GLX_ARB_create_context");
    364   g_glx_create_context_robustness_supported =
    365       HasGLXExtension("GLX_ARB_create_context_robustness");
    366   g_glx_texture_from_pixmap_supported =
    367       HasGLXExtension("GLX_EXT_texture_from_pixmap");
    368   g_glx_oml_sync_control_supported =
    369       HasGLXExtension("GLX_OML_sync_control");
    370   g_glx_get_msc_rate_oml_supported = g_glx_oml_sync_control_supported;
    371   g_glx_sgi_video_sync_supported =
    372       HasGLXExtension("GLX_SGI_video_sync");
    373 
    374   if (!g_glx_get_msc_rate_oml_supported && g_glx_sgi_video_sync_supported)
    375     SGIVideoSyncProviderThreadShim::display_ = gfx::OpenNewXDisplay();
    376 
    377   initialized = true;
    378   return true;
    379 }
    380 
    381 // static
    382 const char* GLSurfaceGLX::GetGLXExtensions() {
    383   return g_glx_extensions;
    384 }
    385 
    386 // static
    387 bool GLSurfaceGLX::HasGLXExtension(const char* name) {
    388   return ExtensionsContain(GetGLXExtensions(), name);
    389 }
    390 
    391 // static
    392 bool GLSurfaceGLX::IsCreateContextSupported() {
    393   return g_glx_context_create;
    394 }
    395 
    396 // static
    397 bool GLSurfaceGLX::IsCreateContextRobustnessSupported() {
    398   return g_glx_create_context_robustness_supported;
    399 }
    400 
    401 // static
    402 bool GLSurfaceGLX::IsTextureFromPixmapSupported() {
    403   return g_glx_texture_from_pixmap_supported;
    404 }
    405 
    406 // static
    407 bool GLSurfaceGLX::IsOMLSyncControlSupported() {
    408   return g_glx_oml_sync_control_supported;
    409 }
    410 
    411 void* GLSurfaceGLX::GetDisplay() {
    412   return g_display;
    413 }
    414 
    415 GLSurfaceGLX::~GLSurfaceGLX() {}
    416 
    417 NativeViewGLSurfaceGLX::NativeViewGLSurfaceGLX(gfx::AcceleratedWidget window)
    418   : parent_window_(window),
    419     window_(0),
    420     config_(NULL) {
    421 }
    422 
    423 gfx::AcceleratedWidget NativeViewGLSurfaceGLX::GetDrawableHandle() const {
    424   return window_;
    425 }
    426 
    427 bool NativeViewGLSurfaceGLX::Initialize() {
    428   XWindowAttributes attributes;
    429   if (!XGetWindowAttributes(g_display, parent_window_, &attributes)) {
    430     LOG(ERROR) << "XGetWindowAttributes failed for window " << parent_window_
    431         << ".";
    432     return false;
    433   }
    434   size_ = gfx::Size(attributes.width, attributes.height);
    435   // Create a child window, with a CopyFromParent visual (to avoid inducing
    436   // extra blits in the driver), that we can resize exactly in Resize(),
    437   // correctly ordered with GL, so that we don't have invalid transient states.
    438   // See https://crbug.com/326995.
    439   window_ = XCreateWindow(g_display,
    440                           parent_window_,
    441                           0,
    442                           0,
    443                           size_.width(),
    444                           size_.height(),
    445                           0,
    446                           CopyFromParent,
    447                           InputOutput,
    448                           CopyFromParent,
    449                           0,
    450                           NULL);
    451   XMapWindow(g_display, window_);
    452 
    453   ui::PlatformEventSource* event_source =
    454       ui::PlatformEventSource::GetInstance();
    455   // Can be NULL in tests, when we don't care about Exposes.
    456   if (event_source) {
    457     XSelectInput(g_display, window_, ExposureMask);
    458     ui::PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this);
    459   }
    460   XFlush(g_display);
    461 
    462   gfx::AcceleratedWidget window_for_vsync = window_;
    463 
    464   if (g_glx_oml_sync_control_supported)
    465     vsync_provider_.reset(new OMLSyncControlVSyncProvider(window_for_vsync));
    466   else if (g_glx_sgi_video_sync_supported)
    467     vsync_provider_.reset(new SGIVideoSyncVSyncProvider(window_for_vsync));
    468 
    469   return true;
    470 }
    471 
    472 void NativeViewGLSurfaceGLX::Destroy() {
    473   if (window_) {
    474     ui::PlatformEventSource* event_source =
    475         ui::PlatformEventSource::GetInstance();
    476     if (event_source)
    477       event_source->RemovePlatformEventDispatcher(this);
    478     XDestroyWindow(g_display, window_);
    479     XFlush(g_display);
    480   }
    481 }
    482 
    483 bool NativeViewGLSurfaceGLX::CanDispatchEvent(const ui::PlatformEvent& event) {
    484   return event->type == Expose && event->xexpose.window == window_;
    485 }
    486 
    487 uint32_t NativeViewGLSurfaceGLX::DispatchEvent(const ui::PlatformEvent& event) {
    488   XEvent forwarded_event = *event;
    489   forwarded_event.xexpose.window = parent_window_;
    490   XSendEvent(g_display, parent_window_, False, ExposureMask,
    491              &forwarded_event);
    492   XFlush(g_display);
    493   return ui::POST_DISPATCH_STOP_PROPAGATION;
    494 }
    495 
    496 bool NativeViewGLSurfaceGLX::Resize(const gfx::Size& size) {
    497   size_ = size;
    498   glXWaitGL();
    499   XResizeWindow(g_display, window_, size.width(), size.height());
    500   glXWaitX();
    501   return true;
    502 }
    503 
    504 bool NativeViewGLSurfaceGLX::IsOffscreen() {
    505   return false;
    506 }
    507 
    508 bool NativeViewGLSurfaceGLX::SwapBuffers() {
    509   TRACE_EVENT2("gpu", "NativeViewGLSurfaceGLX:RealSwapBuffers",
    510       "width", GetSize().width(),
    511       "height", GetSize().height());
    512 
    513   glXSwapBuffers(g_display, GetDrawableHandle());
    514   return true;
    515 }
    516 
    517 gfx::Size NativeViewGLSurfaceGLX::GetSize() {
    518   return size_;
    519 }
    520 
    521 void* NativeViewGLSurfaceGLX::GetHandle() {
    522   return reinterpret_cast<void*>(GetDrawableHandle());
    523 }
    524 
    525 bool NativeViewGLSurfaceGLX::SupportsPostSubBuffer() {
    526   return gfx::g_driver_glx.ext.b_GLX_MESA_copy_sub_buffer;
    527 }
    528 
    529 void* NativeViewGLSurfaceGLX::GetConfig() {
    530   if (!config_) {
    531     // This code path is expensive, but we only take it when
    532     // attempting to use GLX_ARB_create_context_robustness, in which
    533     // case we need a GLXFBConfig for the window in order to create a
    534     // context for it.
    535     //
    536     // TODO(kbr): this is not a reliable code path. On platforms which
    537     // support it, we should use glXChooseFBConfig in the browser
    538     // process to choose the FBConfig and from there the X Visual to
    539     // use when creating the window in the first place. Then we can
    540     // pass that FBConfig down rather than attempting to reconstitute
    541     // it.
    542 
    543     XWindowAttributes attributes;
    544     if (!XGetWindowAttributes(
    545         g_display,
    546         window_,
    547         &attributes)) {
    548       LOG(ERROR) << "XGetWindowAttributes failed for window " <<
    549           window_ << ".";
    550       return NULL;
    551     }
    552 
    553     int visual_id = XVisualIDFromVisual(attributes.visual);
    554 
    555     int num_elements = 0;
    556     scoped_ptr<GLXFBConfig, ScopedPtrXFree> configs(
    557         glXGetFBConfigs(g_display,
    558                         DefaultScreen(g_display),
    559                         &num_elements));
    560     if (!configs.get()) {
    561       LOG(ERROR) << "glXGetFBConfigs failed.";
    562       return NULL;
    563     }
    564     if (!num_elements) {
    565       LOG(ERROR) << "glXGetFBConfigs returned 0 elements.";
    566       return NULL;
    567     }
    568     bool found = false;
    569     int i;
    570     for (i = 0; i < num_elements; ++i) {
    571       int value;
    572       if (glXGetFBConfigAttrib(
    573               g_display, configs.get()[i], GLX_VISUAL_ID, &value)) {
    574         LOG(ERROR) << "glXGetFBConfigAttrib failed.";
    575         return NULL;
    576       }
    577       if (value == visual_id) {
    578         found = true;
    579         break;
    580       }
    581     }
    582     if (found) {
    583       config_ = configs.get()[i];
    584     }
    585   }
    586 
    587   return config_;
    588 }
    589 
    590 bool NativeViewGLSurfaceGLX::PostSubBuffer(
    591     int x, int y, int width, int height) {
    592   DCHECK(gfx::g_driver_glx.ext.b_GLX_MESA_copy_sub_buffer);
    593   glXCopySubBufferMESA(g_display, GetDrawableHandle(), x, y, width, height);
    594   return true;
    595 }
    596 
    597 VSyncProvider* NativeViewGLSurfaceGLX::GetVSyncProvider() {
    598   return vsync_provider_.get();
    599 }
    600 
    601 NativeViewGLSurfaceGLX::~NativeViewGLSurfaceGLX() {
    602   Destroy();
    603 }
    604 
    605 PbufferGLSurfaceGLX::PbufferGLSurfaceGLX(const gfx::Size& size)
    606   : size_(size),
    607     config_(NULL),
    608     pbuffer_(0) {
    609   // Some implementations of Pbuffer do not support having a 0 size. For such
    610   // cases use a (1, 1) surface.
    611   if (size_.GetArea() == 0)
    612     size_.SetSize(1, 1);
    613 }
    614 
    615 bool PbufferGLSurfaceGLX::Initialize() {
    616   DCHECK(!pbuffer_);
    617 
    618   static const int config_attributes[] = {
    619     GLX_BUFFER_SIZE, 32,
    620     GLX_ALPHA_SIZE, 8,
    621     GLX_BLUE_SIZE, 8,
    622     GLX_GREEN_SIZE, 8,
    623     GLX_RED_SIZE, 8,
    624     GLX_RENDER_TYPE, GLX_RGBA_BIT,
    625     GLX_DRAWABLE_TYPE, GLX_PBUFFER_BIT,
    626     GLX_DOUBLEBUFFER, False,
    627     0
    628   };
    629 
    630   int num_elements = 0;
    631   scoped_ptr<GLXFBConfig, ScopedPtrXFree> configs(
    632       glXChooseFBConfig(g_display,
    633                         DefaultScreen(g_display),
    634                         config_attributes,
    635                         &num_elements));
    636   if (!configs.get()) {
    637     LOG(ERROR) << "glXChooseFBConfig failed.";
    638     return false;
    639   }
    640   if (!num_elements) {
    641     LOG(ERROR) << "glXChooseFBConfig returned 0 elements.";
    642     return false;
    643   }
    644 
    645   config_ = configs.get()[0];
    646 
    647   const int pbuffer_attributes[] = {
    648     GLX_PBUFFER_WIDTH, size_.width(),
    649     GLX_PBUFFER_HEIGHT, size_.height(),
    650     0
    651   };
    652   pbuffer_ = glXCreatePbuffer(g_display,
    653                               static_cast<GLXFBConfig>(config_),
    654                               pbuffer_attributes);
    655   if (!pbuffer_) {
    656     Destroy();
    657     LOG(ERROR) << "glXCreatePbuffer failed.";
    658     return false;
    659   }
    660 
    661   return true;
    662 }
    663 
    664 void PbufferGLSurfaceGLX::Destroy() {
    665   if (pbuffer_) {
    666     glXDestroyPbuffer(g_display, pbuffer_);
    667     pbuffer_ = 0;
    668   }
    669 
    670   config_ = NULL;
    671 }
    672 
    673 bool PbufferGLSurfaceGLX::IsOffscreen() {
    674   return true;
    675 }
    676 
    677 bool PbufferGLSurfaceGLX::SwapBuffers() {
    678   NOTREACHED() << "Attempted to call SwapBuffers on a pbuffer.";
    679   return false;
    680 }
    681 
    682 gfx::Size PbufferGLSurfaceGLX::GetSize() {
    683   return size_;
    684 }
    685 
    686 void* PbufferGLSurfaceGLX::GetHandle() {
    687   return reinterpret_cast<void*>(pbuffer_);
    688 }
    689 
    690 void* PbufferGLSurfaceGLX::GetConfig() {
    691   return config_;
    692 }
    693 
    694 PbufferGLSurfaceGLX::~PbufferGLSurfaceGLX() {
    695   Destroy();
    696 }
    697 
    698 }  // namespace gfx
    699