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