• 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/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 {
operator ()gfx::__anon0d98bc2a0111::ScopedPtrXFree40   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:
OMLSyncControlVSyncProvider(gfx::AcceleratedWidget window)75   explicit OMLSyncControlVSyncProvider(gfx::AcceleratedWidget window)
76       : SyncControlVSyncProvider(),
77         window_(window) {
78   }
79 
~OMLSyncControlVSyncProvider()80   virtual ~OMLSyncControlVSyncProvider() { }
81 
82  protected:
GetSyncValues(int64 * system_time,int64 * media_stream_counter,int64 * swap_buffer_counter)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 
GetMscRate(int32 * numerator,int32 * denominator)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:
Create()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 
SGIVideoSyncThread()126   SGIVideoSyncThread() : base::Thread("SGI_video_sync") {
127     DCHECK(CalledOnValidThread());
128   }
129 
~SGIVideoSyncThread()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:
SGIVideoSyncProviderThreadShim(XID window)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 
~SGIVideoSyncProviderThreadShim()154   virtual ~SGIVideoSyncProviderThreadShim() {
155     if (context_) {
156       glXDestroyContext(display_, context_);
157       context_ = NULL;
158     }
159   }
160 
cancel_vsync_flag()161   base::CancellationFlag* cancel_vsync_flag() {
162     return &cancel_vsync_flag_;
163   }
164 
vsync_lock()165   base::Lock* vsync_lock() {
166     return &vsync_lock_;
167   }
168 
Initialize()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 
GetVSyncParameters(const VSyncProvider::UpdateVSyncCallback & callback)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:
SGIVideoSyncVSyncProvider(gfx::AcceleratedWidget window)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 
~SGIVideoSyncVSyncProvider()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 
GetVSyncParameters(const VSyncProvider::UpdateVSyncCallback & callback)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:
PendingCallbackRunner(const base::TimeTicks timebase,const base::TimeDelta interval)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 
GLSurfaceGLX()330 GLSurfaceGLX::GLSurfaceGLX() {}
331 
InitializeOneOff()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
GetGLXExtensions()382 const char* GLSurfaceGLX::GetGLXExtensions() {
383   return g_glx_extensions;
384 }
385 
386 // static
HasGLXExtension(const char * name)387 bool GLSurfaceGLX::HasGLXExtension(const char* name) {
388   return ExtensionsContain(GetGLXExtensions(), name);
389 }
390 
391 // static
IsCreateContextSupported()392 bool GLSurfaceGLX::IsCreateContextSupported() {
393   return g_glx_context_create;
394 }
395 
396 // static
IsCreateContextRobustnessSupported()397 bool GLSurfaceGLX::IsCreateContextRobustnessSupported() {
398   return g_glx_create_context_robustness_supported;
399 }
400 
401 // static
IsTextureFromPixmapSupported()402 bool GLSurfaceGLX::IsTextureFromPixmapSupported() {
403   return g_glx_texture_from_pixmap_supported;
404 }
405 
406 // static
IsOMLSyncControlSupported()407 bool GLSurfaceGLX::IsOMLSyncControlSupported() {
408   return g_glx_oml_sync_control_supported;
409 }
410 
GetDisplay()411 void* GLSurfaceGLX::GetDisplay() {
412   return g_display;
413 }
414 
~GLSurfaceGLX()415 GLSurfaceGLX::~GLSurfaceGLX() {}
416 
NativeViewGLSurfaceGLX(gfx::AcceleratedWidget window)417 NativeViewGLSurfaceGLX::NativeViewGLSurfaceGLX(gfx::AcceleratedWidget window)
418   : parent_window_(window),
419     window_(0),
420     config_(NULL) {
421 }
422 
GetDrawableHandle() const423 gfx::AcceleratedWidget NativeViewGLSurfaceGLX::GetDrawableHandle() const {
424   return window_;
425 }
426 
Initialize()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 
Destroy()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 
CanDispatchEvent(const ui::PlatformEvent & event)483 bool NativeViewGLSurfaceGLX::CanDispatchEvent(const ui::PlatformEvent& event) {
484   return event->type == Expose && event->xexpose.window == window_;
485 }
486 
DispatchEvent(const ui::PlatformEvent & event)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 
Resize(const gfx::Size & size)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 
IsOffscreen()504 bool NativeViewGLSurfaceGLX::IsOffscreen() {
505   return false;
506 }
507 
SwapBuffers()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 
GetSize()517 gfx::Size NativeViewGLSurfaceGLX::GetSize() {
518   return size_;
519 }
520 
GetHandle()521 void* NativeViewGLSurfaceGLX::GetHandle() {
522   return reinterpret_cast<void*>(GetDrawableHandle());
523 }
524 
SupportsPostSubBuffer()525 bool NativeViewGLSurfaceGLX::SupportsPostSubBuffer() {
526   return gfx::g_driver_glx.ext.b_GLX_MESA_copy_sub_buffer;
527 }
528 
GetConfig()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 
PostSubBuffer(int x,int y,int width,int height)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 
GetVSyncProvider()597 VSyncProvider* NativeViewGLSurfaceGLX::GetVSyncProvider() {
598   return vsync_provider_.get();
599 }
600 
~NativeViewGLSurfaceGLX()601 NativeViewGLSurfaceGLX::~NativeViewGLSurfaceGLX() {
602   Destroy();
603 }
604 
PbufferGLSurfaceGLX(const gfx::Size & size)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 
Initialize()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 
Destroy()664 void PbufferGLSurfaceGLX::Destroy() {
665   if (pbuffer_) {
666     glXDestroyPbuffer(g_display, pbuffer_);
667     pbuffer_ = 0;
668   }
669 
670   config_ = NULL;
671 }
672 
IsOffscreen()673 bool PbufferGLSurfaceGLX::IsOffscreen() {
674   return true;
675 }
676 
SwapBuffers()677 bool PbufferGLSurfaceGLX::SwapBuffers() {
678   NOTREACHED() << "Attempted to call SwapBuffers on a pbuffer.";
679   return false;
680 }
681 
GetSize()682 gfx::Size PbufferGLSurfaceGLX::GetSize() {
683   return size_;
684 }
685 
GetHandle()686 void* PbufferGLSurfaceGLX::GetHandle() {
687   return reinterpret_cast<void*>(pbuffer_);
688 }
689 
GetConfig()690 void* PbufferGLSurfaceGLX::GetConfig() {
691   return config_;
692 }
693 
~PbufferGLSurfaceGLX()694 PbufferGLSurfaceGLX::~PbufferGLSurfaceGLX() {
695   Destroy();
696 }
697 
698 }  // namespace gfx
699