• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 {
operator ()gfx::__anonf904cb370111::ScopedPtrXFree39   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:
OMLSyncControlVSyncProvider(gfx::AcceleratedWidget window)61   explicit OMLSyncControlVSyncProvider(gfx::AcceleratedWidget window)
62       : SyncControlVSyncProvider(),
63         window_(window) {
64   }
65 
~OMLSyncControlVSyncProvider()66   virtual ~OMLSyncControlVSyncProvider() { }
67 
68  protected:
GetSyncValues(int64 * system_time,int64 * media_stream_counter,int64 * swap_buffer_counter)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 
GetMscRate(int32 * numerator,int32 * denominator)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:
Create()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 
SGIVideoSyncThread()112   SGIVideoSyncThread() : base::Thread("SGI_video_sync") {
113     DCHECK(CalledOnValidThread());
114   }
115 
~SGIVideoSyncThread()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:
SGIVideoSyncProviderThreadShim(XID window)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 
~SGIVideoSyncProviderThreadShim()140   virtual ~SGIVideoSyncProviderThreadShim() {
141     if (context_) {
142       glXDestroyContext(display_, context_);
143       context_ = NULL;
144     }
145   }
146 
cancel_vsync_flag()147   base::CancellationFlag* cancel_vsync_flag() {
148     return &cancel_vsync_flag_;
149   }
150 
vsync_lock()151   base::Lock* vsync_lock() {
152     return &vsync_lock_;
153   }
154 
Initialize()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 
GetVSyncParameters(const VSyncProvider::UpdateVSyncCallback & callback)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:
SGIVideoSyncVSyncProvider(gfx::AcceleratedWidget window)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 
~SGIVideoSyncVSyncProvider()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 
GetVSyncParameters(const VSyncProvider::UpdateVSyncCallback & callback)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:
PendingCallbackRunner(const base::TimeTicks timebase,const base::TimeDelta interval)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 
GLSurfaceGLX()306 GLSurfaceGLX::GLSurfaceGLX() {}
307 
InitializeOneOff()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
GetGLXExtensions()358 const char* GLSurfaceGLX::GetGLXExtensions() {
359   return g_glx_extensions;
360 }
361 
362 // static
HasGLXExtension(const char * name)363 bool GLSurfaceGLX::HasGLXExtension(const char* name) {
364   return ExtensionsContain(GetGLXExtensions(), name);
365 }
366 
367 // static
IsCreateContextSupported()368 bool GLSurfaceGLX::IsCreateContextSupported() {
369   return g_glx_context_create;
370 }
371 
372 // static
IsCreateContextRobustnessSupported()373 bool GLSurfaceGLX::IsCreateContextRobustnessSupported() {
374   return g_glx_create_context_robustness_supported;
375 }
376 
377 // static
IsTextureFromPixmapSupported()378 bool GLSurfaceGLX::IsTextureFromPixmapSupported() {
379   return g_glx_texture_from_pixmap_supported;
380 }
381 
382 // static
IsOMLSyncControlSupported()383 bool GLSurfaceGLX::IsOMLSyncControlSupported() {
384   return g_glx_oml_sync_control_supported;
385 }
386 
GetDisplay()387 void* GLSurfaceGLX::GetDisplay() {
388   return g_display;
389 }
390 
~GLSurfaceGLX()391 GLSurfaceGLX::~GLSurfaceGLX() {}
392 
NativeViewGLSurfaceGLX(gfx::AcceleratedWidget window)393 NativeViewGLSurfaceGLX::NativeViewGLSurfaceGLX(gfx::AcceleratedWidget window)
394   : parent_window_(window),
395     window_(0),
396     config_(NULL) {
397 }
398 
GetDrawableHandle() const399 gfx::AcceleratedWidget NativeViewGLSurfaceGLX::GetDrawableHandle() const {
400   return window_;
401 }
402 
Initialize()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 
Destroy()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 
CanDispatchEvent(const ui::PlatformEvent & event)459 bool NativeViewGLSurfaceGLX::CanDispatchEvent(const ui::PlatformEvent& event) {
460   return event->type == Expose && event->xexpose.window == window_;
461 }
462 
DispatchEvent(const ui::PlatformEvent & event)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 
Resize(const gfx::Size & size)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 
IsOffscreen()480 bool NativeViewGLSurfaceGLX::IsOffscreen() {
481   return false;
482 }
483 
SwapBuffers()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 
GetSize()493 gfx::Size NativeViewGLSurfaceGLX::GetSize() {
494   return size_;
495 }
496 
GetHandle()497 void* NativeViewGLSurfaceGLX::GetHandle() {
498   return reinterpret_cast<void*>(GetDrawableHandle());
499 }
500 
SupportsPostSubBuffer()501 bool NativeViewGLSurfaceGLX::SupportsPostSubBuffer() {
502   return gfx::g_driver_glx.ext.b_GLX_MESA_copy_sub_buffer;
503 }
504 
GetConfig()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 
PostSubBuffer(int x,int y,int width,int height)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 
GetVSyncProvider()573 VSyncProvider* NativeViewGLSurfaceGLX::GetVSyncProvider() {
574   return vsync_provider_.get();
575 }
576 
~NativeViewGLSurfaceGLX()577 NativeViewGLSurfaceGLX::~NativeViewGLSurfaceGLX() {
578   Destroy();
579 }
580 
PbufferGLSurfaceGLX(const gfx::Size & size)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 
Initialize()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 
Destroy()640 void PbufferGLSurfaceGLX::Destroy() {
641   if (pbuffer_) {
642     glXDestroyPbuffer(g_display, pbuffer_);
643     pbuffer_ = 0;
644   }
645 
646   config_ = NULL;
647 }
648 
IsOffscreen()649 bool PbufferGLSurfaceGLX::IsOffscreen() {
650   return true;
651 }
652 
SwapBuffers()653 bool PbufferGLSurfaceGLX::SwapBuffers() {
654   NOTREACHED() << "Attempted to call SwapBuffers on a pbuffer.";
655   return false;
656 }
657 
GetSize()658 gfx::Size PbufferGLSurfaceGLX::GetSize() {
659   return size_;
660 }
661 
GetHandle()662 void* PbufferGLSurfaceGLX::GetHandle() {
663   return reinterpret_cast<void*>(pbuffer_);
664 }
665 
GetConfig()666 void* PbufferGLSurfaceGLX::GetConfig() {
667   return config_;
668 }
669 
~PbufferGLSurfaceGLX()670 PbufferGLSurfaceGLX::~PbufferGLSurfaceGLX() {
671   Destroy();
672 }
673 
674 }  // namespace gfx
675