- add sources.
[platform/framework/web/crosswalk.git] / src / ui / gl / gl_surface_glx.cc
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 extern "C" {
6 #include <X11/Xlib.h>
7 }
8
9 #include "ui/gl/gl_surface_glx.h"
10
11 #include "base/basictypes.h"
12 #include "base/debug/trace_event.h"
13 #include "base/lazy_instance.h"
14 #include "base/logging.h"
15 #include "base/memory/scoped_ptr.h"
16 #include "base/memory/weak_ptr.h"
17 #include "base/message_loop/message_loop.h"
18 #include "base/synchronization/cancellation_flag.h"
19 #include "base/synchronization/lock.h"
20 #include "base/threading/non_thread_safe.h"
21 #include "base/threading/thread.h"
22 #include "base/time/time.h"
23 #include "third_party/mesa/src/include/GL/osmesa.h"
24 #include "ui/gfx/x/x11_types.h"
25 #include "ui/gl/gl_bindings.h"
26 #include "ui/gl/gl_implementation.h"
27 #include "ui/gl/vsync_provider.h"
28
29 namespace gfx {
30
31 namespace {
32
33 // scoped_ptr functor for XFree(). Use as follows:
34 //   scoped_ptr_malloc<XVisualInfo, ScopedPtrXFree> foo(...);
35 // where "XVisualInfo" is any X type that is freed with XFree.
36 class ScopedPtrXFree {
37  public:
38   void operator()(void* x) const {
39     ::XFree(x);
40   }
41 };
42
43 Display* g_display = NULL;
44 const char* g_glx_extensions = NULL;
45 bool g_glx_context_create = false;
46 bool g_glx_create_context_robustness_supported = false;
47 bool g_glx_texture_from_pixmap_supported = false;
48 bool g_glx_oml_sync_control_supported = false;
49
50 // Track support of glXGetMscRateOML separately from GLX_OML_sync_control as a
51 // whole since on some platforms (e.g. crosbug.com/34585), glXGetMscRateOML
52 // always fails even though GLX_OML_sync_control is reported as being supported.
53 bool g_glx_get_msc_rate_oml_supported = false;
54
55 bool g_glx_sgi_video_sync_supported = false;
56
57 class OMLSyncControlVSyncProvider
58     : public gfx::SyncControlVSyncProvider {
59  public:
60   explicit OMLSyncControlVSyncProvider(gfx::AcceleratedWidget window)
61       : SyncControlVSyncProvider(),
62         window_(window) {
63   }
64
65   virtual ~OMLSyncControlVSyncProvider() { }
66
67  protected:
68   virtual bool GetSyncValues(int64* system_time,
69                              int64* media_stream_counter,
70                              int64* swap_buffer_counter) OVERRIDE {
71     return glXGetSyncValuesOML(g_display, window_, system_time,
72                                media_stream_counter, swap_buffer_counter);
73   }
74
75   virtual bool GetMscRate(int32* numerator, int32* denominator) OVERRIDE {
76     if (!g_glx_get_msc_rate_oml_supported)
77       return false;
78
79     if (!glXGetMscRateOML(g_display, window_, numerator, denominator)) {
80       // Once glXGetMscRateOML has been found to fail, don't try again,
81       // since each failing call may spew an error message.
82       g_glx_get_msc_rate_oml_supported = false;
83       return false;
84     }
85
86     return true;
87   }
88
89  private:
90   XID window_;
91
92   DISALLOW_COPY_AND_ASSIGN(OMLSyncControlVSyncProvider);
93 };
94
95 class SGIVideoSyncThread
96      : public base::Thread,
97        public base::NonThreadSafe,
98        public base::RefCounted<SGIVideoSyncThread> {
99  public:
100   static scoped_refptr<SGIVideoSyncThread> Create() {
101     if (!g_video_sync_thread) {
102       g_video_sync_thread = new SGIVideoSyncThread();
103       g_video_sync_thread->Start();
104     }
105     return g_video_sync_thread;
106   }
107
108  private:
109   friend class base::RefCounted<SGIVideoSyncThread>;
110
111   SGIVideoSyncThread() : base::Thread("SGI_video_sync") {
112     DCHECK(CalledOnValidThread());
113   }
114
115   virtual ~SGIVideoSyncThread() {
116     DCHECK(CalledOnValidThread());
117     g_video_sync_thread = NULL;
118     Stop();
119   }
120
121   static SGIVideoSyncThread* g_video_sync_thread;
122
123   DISALLOW_COPY_AND_ASSIGN(SGIVideoSyncThread);
124 };
125
126 class SGIVideoSyncProviderThreadShim {
127  public:
128   explicit SGIVideoSyncProviderThreadShim(XID window)
129       : window_(window),
130         context_(NULL),
131         message_loop_(base::MessageLoopProxy::current()),
132         cancel_vsync_flag_(),
133         vsync_lock_() {
134     // This ensures that creation of |window_| has occured when this shim
135     // is executing in the same process as the call to create |window_|.
136     XSync(g_display, False);
137   }
138
139   virtual ~SGIVideoSyncProviderThreadShim() {
140     if (context_) {
141       glXDestroyContext(display_, context_);
142       context_ = NULL;
143     }
144   }
145
146   base::CancellationFlag* cancel_vsync_flag() {
147     return &cancel_vsync_flag_;
148   }
149
150   base::Lock* vsync_lock() {
151     return &vsync_lock_;
152   }
153
154   void Initialize() {
155     DCHECK(display_);
156
157     XWindowAttributes attributes;
158     if (!XGetWindowAttributes(display_, window_, &attributes)) {
159       LOG(ERROR) << "XGetWindowAttributes failed for window " <<
160         window_ << ".";
161       return;
162     }
163
164     XVisualInfo visual_info_template;
165     visual_info_template.visualid = XVisualIDFromVisual(attributes.visual);
166
167     int visual_info_count = 0;
168     scoped_ptr_malloc<XVisualInfo, ScopedPtrXFree> visual_info_list(
169         XGetVisualInfo(display_, VisualIDMask,
170                        &visual_info_template, &visual_info_count));
171
172     DCHECK(visual_info_list.get());
173     if (visual_info_count == 0) {
174       LOG(ERROR) << "No visual info for visual ID.";
175       return;
176     }
177
178     context_ = glXCreateContext(display_, visual_info_list.get(), NULL, True);
179
180     DCHECK(NULL != context_);
181   }
182
183   void GetVSyncParameters(const VSyncProvider::UpdateVSyncCallback& callback) {
184     base::TimeTicks now;
185     {
186       // Don't allow |window_| destruction while we're probing vsync.
187       base::AutoLock locked(vsync_lock_);
188
189       if (!context_ || cancel_vsync_flag_.IsSet())
190         return;
191
192       glXMakeCurrent(display_, window_, context_);
193
194       unsigned int retrace_count = 0;
195       if (glXWaitVideoSyncSGI(1, 0, &retrace_count) != 0)
196         return;
197
198       TRACE_EVENT_INSTANT0("gpu", "vblank", TRACE_EVENT_SCOPE_THREAD);
199       now = base::TimeTicks::HighResNow();
200
201       glXMakeCurrent(display_, 0, 0);
202     }
203
204     const base::TimeDelta kDefaultInterval =
205         base::TimeDelta::FromSeconds(1) / 60;
206
207     message_loop_->PostTask(
208         FROM_HERE, base::Bind(callback, now, kDefaultInterval));
209   }
210
211  private:
212   // For initialization of display_ in GLSurface::InitializeOneOff before
213   // the sandbox goes up.
214   friend class gfx::GLSurfaceGLX;
215
216   static Display* display_;
217
218   XID window_;
219   GLXContext context_;
220
221   scoped_refptr<base::MessageLoopProxy> message_loop_;
222
223   base::CancellationFlag cancel_vsync_flag_;
224   base::Lock vsync_lock_;
225
226   DISALLOW_COPY_AND_ASSIGN(SGIVideoSyncProviderThreadShim);
227 };
228
229 class SGIVideoSyncVSyncProvider
230     : public gfx::VSyncProvider,
231       public base::SupportsWeakPtr<SGIVideoSyncVSyncProvider> {
232  public:
233   explicit SGIVideoSyncVSyncProvider(gfx::AcceleratedWidget window)
234       : vsync_thread_(SGIVideoSyncThread::Create()),
235         shim_(new SGIVideoSyncProviderThreadShim(window)),
236         cancel_vsync_flag_(shim_->cancel_vsync_flag()),
237         vsync_lock_(shim_->vsync_lock()) {
238     vsync_thread_->message_loop()->PostTask(
239         FROM_HERE,
240         base::Bind(&SGIVideoSyncProviderThreadShim::Initialize,
241                    base::Unretained(shim_.get())));
242   }
243
244   virtual ~SGIVideoSyncVSyncProvider() {
245     {
246       base::AutoLock locked(*vsync_lock_);
247       cancel_vsync_flag_->Set();
248     }
249
250     // Hand-off |shim_| to be deleted on the |vsync_thread_|.
251     vsync_thread_->message_loop()->DeleteSoon(
252         FROM_HERE,
253         shim_.release());
254   }
255
256   virtual void GetVSyncParameters(
257       const VSyncProvider::UpdateVSyncCallback& callback) OVERRIDE {
258     // Only one outstanding request per surface.
259     if (!pending_callback_) {
260       pending_callback_.reset(
261           new VSyncProvider::UpdateVSyncCallback(callback));
262       vsync_thread_->message_loop()->PostTask(
263           FROM_HERE,
264           base::Bind(&SGIVideoSyncProviderThreadShim::GetVSyncParameters,
265                      base::Unretained(shim_.get()),
266                      base::Bind(
267                          &SGIVideoSyncVSyncProvider::PendingCallbackRunner,
268                          AsWeakPtr())));
269     }
270   }
271
272  private:
273   void PendingCallbackRunner(const base::TimeTicks timebase,
274                              const base::TimeDelta interval) {
275     DCHECK(pending_callback_);
276     pending_callback_->Run(timebase, interval);
277     pending_callback_.reset();
278   }
279
280   scoped_refptr<SGIVideoSyncThread> vsync_thread_;
281
282   // Thread shim through which the sync provider is accessed on |vsync_thread_|.
283   scoped_ptr<SGIVideoSyncProviderThreadShim> shim_;
284
285   scoped_ptr<VSyncProvider::UpdateVSyncCallback> pending_callback_;
286
287   // Raw pointers to sync primitives owned by the shim_.
288   // These will only be referenced before we post a task to destroy
289   // the shim_, so they are safe to access.
290   base::CancellationFlag* cancel_vsync_flag_;
291   base::Lock* vsync_lock_;
292
293   DISALLOW_COPY_AND_ASSIGN(SGIVideoSyncVSyncProvider);
294 };
295
296 SGIVideoSyncThread* SGIVideoSyncThread::g_video_sync_thread = NULL;
297
298 // In order to take advantage of GLX_SGI_video_sync, we need a display
299 // for use on a separate thread. We must allocate this before the sandbox
300 // goes up (rather than on-demand when we start the thread).
301 Display* SGIVideoSyncProviderThreadShim::display_ = NULL;
302
303 #if defined(TOOLKIT_GTK)
304 // A mechanism for forwarding XExpose events from one window to another.
305 // Because in the workaround for http://crbug.com/145600 the child window
306 // is placed on top of the parent window, only the child window will receive
307 // all expose events. These need to be forwared to the parent window to inform
308 // it that it should paint.
309 class XExposeEventForwarder : public base::MessagePumpObserver {
310  public:
311   XExposeEventForwarder() {}
312   virtual ~XExposeEventForwarder() {
313     DCHECK(child_to_parent_map_.empty());
314   }
315   void AddParentChildPair(gfx::AcceleratedWidget parent_window,
316                           gfx::AcceleratedWidget child_window) {
317     if (child_to_parent_map_.empty())
318       base::MessagePumpX11::Current()->AddObserver(this);
319
320     DCHECK(child_to_parent_map_.find(child_window) ==
321            child_to_parent_map_.end());
322     child_to_parent_map_.insert(std::make_pair(
323         child_window, parent_window));
324   }
325   void RemoveParentChildPair(gfx::AcceleratedWidget parent_window,
326                              gfx::AcceleratedWidget child_window) {
327     DCHECK(child_to_parent_map_.find(child_window) !=
328            child_to_parent_map_.end());
329     child_to_parent_map_.erase(child_window);
330
331     if (child_to_parent_map_.empty())
332       base::MessagePumpX11::Current()->RemoveObserver(this);
333   }
334
335  private:
336   virtual base::EventStatus WillProcessEvent (
337       const base::NativeEvent& xevent) OVERRIDE {
338     if (xevent->type != Expose)
339       return base::EVENT_CONTINUE;
340
341     WindowMap::const_iterator found = child_to_parent_map_.find(
342         xevent->xexpose.window);
343     if (found == child_to_parent_map_.end())
344       return base::EVENT_CONTINUE;
345
346     gfx::AcceleratedWidget target_window = found->second;
347     XEvent forwarded_event = *xevent;
348     forwarded_event.xexpose.window = target_window;
349     XSendEvent(g_display, target_window, False, ExposureMask,
350                &forwarded_event);
351     return base::EVENT_CONTINUE;
352   }
353   virtual void DidProcessEvent(const base::NativeEvent& xevent) OVERRIDE {
354   }
355
356   typedef std::map<gfx::AcceleratedWidget, gfx::AcceleratedWidget> WindowMap;
357   WindowMap child_to_parent_map_;
358
359   DISALLOW_COPY_AND_ASSIGN(XExposeEventForwarder);
360 };
361
362 static base::LazyInstance<XExposeEventForwarder> g_xexpose_event_forwarder =
363     LAZY_INSTANCE_INITIALIZER;
364
365 // Do not use this workaround when running in test harnesses that do not have
366 // a message loop or do not have a TYPE_GPU message loop.
367 bool g_create_child_windows = false;
368 #endif
369
370 }  // namespace
371
372 GLSurfaceGLX::GLSurfaceGLX() {}
373
374 bool GLSurfaceGLX::InitializeOneOff() {
375   static bool initialized = false;
376   if (initialized)
377     return true;
378
379   // http://crbug.com/245466
380   setenv("force_s3tc_enable", "true", 1);
381
382   // SGIVideoSyncProviderShim (if instantiated) will issue X commands on
383   // it's own thread.
384   XInitThreads();
385
386 #if defined(TOOLKIT_GTK)
387   // Be sure to use the X display handle and not the GTK display handle if this
388   // is the GPU process.
389   g_create_child_windows =
390       base::MessageLoop::current() &&
391       base::MessageLoop::current()->type() == base::MessageLoop::TYPE_GPU;
392
393   if (g_create_child_windows)
394     g_display = base::MessagePumpX11::GetDefaultXDisplay();
395   else
396     g_display = base::MessagePumpForUI::GetDefaultXDisplay();
397 #else
398   g_display = base::MessagePumpForUI::GetDefaultXDisplay();
399 #endif
400
401   if (!g_display) {
402     LOG(ERROR) << "XOpenDisplay failed.";
403     return false;
404   }
405
406   int major, minor;
407   if (!glXQueryVersion(g_display, &major, &minor)) {
408     LOG(ERROR) << "glxQueryVersion failed";
409     return false;
410   }
411
412   if (major == 1 && minor < 3) {
413     LOG(ERROR) << "GLX 1.3 or later is required.";
414     return false;
415   }
416
417   g_glx_extensions = glXQueryExtensionsString(g_display, 0);
418   g_glx_context_create =
419       HasGLXExtension("GLX_ARB_create_context");
420   g_glx_create_context_robustness_supported =
421       HasGLXExtension("GLX_ARB_create_context_robustness");
422   g_glx_texture_from_pixmap_supported =
423       HasGLXExtension("GLX_EXT_texture_from_pixmap");
424   g_glx_oml_sync_control_supported =
425       HasGLXExtension("GLX_OML_sync_control");
426   g_glx_get_msc_rate_oml_supported = g_glx_oml_sync_control_supported;
427   g_glx_sgi_video_sync_supported =
428       HasGLXExtension("GLX_SGI_video_sync");
429
430   if (!g_glx_get_msc_rate_oml_supported && g_glx_sgi_video_sync_supported)
431     SGIVideoSyncProviderThreadShim::display_ = XOpenDisplay(NULL);
432
433   initialized = true;
434   return true;
435 }
436
437 // static
438 const char* GLSurfaceGLX::GetGLXExtensions() {
439   return g_glx_extensions;
440 }
441
442 // static
443 bool GLSurfaceGLX::HasGLXExtension(const char* name) {
444   return ExtensionsContain(GetGLXExtensions(), name);
445 }
446
447 // static
448 bool GLSurfaceGLX::IsCreateContextSupported() {
449   return g_glx_context_create;
450 }
451
452 // static
453 bool GLSurfaceGLX::IsCreateContextRobustnessSupported() {
454   return g_glx_create_context_robustness_supported;
455 }
456
457 // static
458 bool GLSurfaceGLX::IsTextureFromPixmapSupported() {
459   return g_glx_texture_from_pixmap_supported;
460 }
461
462 // static
463 bool GLSurfaceGLX::IsOMLSyncControlSupported() {
464   return g_glx_oml_sync_control_supported;
465 }
466
467 void* GLSurfaceGLX::GetDisplay() {
468   return g_display;
469 }
470
471 GLSurfaceGLX::~GLSurfaceGLX() {}
472
473 #if defined(TOOLKIT_GTK)
474 bool NativeViewGLSurfaceGLX::SetBackbufferAllocation(bool allocated) {
475   backbuffer_allocated_ = allocated;
476   AdjustBufferAllocation();
477   return true;
478 }
479
480 void NativeViewGLSurfaceGLX::SetFrontbufferAllocation(bool allocated) {
481   frontbuffer_allocated_ = allocated;
482   AdjustBufferAllocation();
483 }
484
485 void NativeViewGLSurfaceGLX::AdjustBufferAllocation() {
486   if (!g_create_child_windows)
487     return;
488
489   if (frontbuffer_allocated_ || backbuffer_allocated_)
490     CreateChildWindow();
491   else
492     DestroyChildWindow();
493 }
494
495 void NativeViewGLSurfaceGLX::CreateChildWindow() {
496   DCHECK(g_create_child_windows);
497
498   if (child_window_)
499     return;
500
501   XSetWindowAttributes set_window_attributes;
502   set_window_attributes.event_mask = ExposureMask;
503   child_window_ = XCreateWindow(
504       g_display, parent_window_, 0, 0, size_.width(), size_.height(), 0,
505       CopyFromParent, InputOutput, CopyFromParent, CWEventMask,
506       &set_window_attributes);
507   g_xexpose_event_forwarder.Pointer()->AddParentChildPair(
508       parent_window_, child_window_);
509
510   XMapWindow(g_display, child_window_);
511   XFlush(g_display);
512 }
513
514 void NativeViewGLSurfaceGLX::DestroyChildWindow() {
515   if (!child_window_)
516     return;
517
518   g_xexpose_event_forwarder.Pointer()->RemoveParentChildPair(
519       parent_window_, child_window_);
520   XDestroyWindow(g_display, child_window_);
521   XFlush(g_display);
522   child_window_ = 0;
523 }
524 #endif
525
526 NativeViewGLSurfaceGLX::NativeViewGLSurfaceGLX(gfx::AcceleratedWidget window)
527   : parent_window_(window),
528 #if defined(TOOLKIT_GTK)
529     child_window_(0),
530     dummy_window_(0),
531     backbuffer_allocated_(true),
532     frontbuffer_allocated_(true),
533 #endif
534     config_(NULL) {
535 }
536
537 gfx::AcceleratedWidget NativeViewGLSurfaceGLX::GetDrawableHandle() const {
538 #if defined(TOOLKIT_GTK)
539   if (g_create_child_windows) {
540     if (child_window_)
541       return child_window_;
542     return dummy_window_;
543   }
544 #endif
545   return parent_window_;
546 }
547
548 bool NativeViewGLSurfaceGLX::Initialize() {
549   XWindowAttributes attributes;
550   if (!XGetWindowAttributes(g_display, parent_window_, &attributes)) {
551     LOG(ERROR) << "XGetWindowAttributes failed for window " << parent_window_
552         << ".";
553     return false;
554   }
555   size_ = gfx::Size(attributes.width, attributes.height);
556
557   gfx::AcceleratedWidget window_for_vsync = parent_window_;
558
559 #if defined(TOOLKIT_GTK)
560   if (g_create_child_windows) {
561     dummy_window_ = XCreateWindow(
562         g_display,
563         RootWindow(g_display, XScreenNumberOfScreen(attributes.screen)),
564         0, 0, 1, 1, 0, CopyFromParent, InputOutput, attributes.visual, 0, NULL);
565     window_for_vsync = dummy_window_;
566     CreateChildWindow();
567   }
568 #endif
569
570   if (g_glx_oml_sync_control_supported)
571     vsync_provider_.reset(new OMLSyncControlVSyncProvider(window_for_vsync));
572   else if (g_glx_sgi_video_sync_supported)
573     vsync_provider_.reset(new SGIVideoSyncVSyncProvider(window_for_vsync));
574
575   return true;
576 }
577
578 void NativeViewGLSurfaceGLX::Destroy() {
579 #if defined(TOOLKIT_GTK)
580   DestroyChildWindow();
581   if (dummy_window_)
582     XDestroyWindow(g_display, dummy_window_);
583   dummy_window_ = 0;
584 #endif
585 }
586
587 bool NativeViewGLSurfaceGLX::Resize(const gfx::Size& size) {
588 #if defined(TOOLKIT_GTK)
589   if (child_window_) {
590     XResizeWindow(g_display, child_window_, size.width(), size.height());
591     XFlush(g_display);
592   }
593 #endif
594   size_ = size;
595   return true;
596 }
597
598 bool NativeViewGLSurfaceGLX::IsOffscreen() {
599   return false;
600 }
601
602 bool NativeViewGLSurfaceGLX::SwapBuffers() {
603   glXSwapBuffers(g_display, GetDrawableHandle());
604   return true;
605 }
606
607 gfx::Size NativeViewGLSurfaceGLX::GetSize() {
608   return size_;
609 }
610
611 void* NativeViewGLSurfaceGLX::GetHandle() {
612   return reinterpret_cast<void*>(GetDrawableHandle());
613 }
614
615 std::string NativeViewGLSurfaceGLX::GetExtensions() {
616   std::string extensions = GLSurface::GetExtensions();
617   if (gfx::g_driver_glx.ext.b_GLX_MESA_copy_sub_buffer) {
618     extensions += extensions.empty() ? "" : " ";
619     extensions += "GL_CHROMIUM_post_sub_buffer";
620   }
621   return extensions;
622 }
623
624 void* NativeViewGLSurfaceGLX::GetConfig() {
625   if (!config_) {
626     // This code path is expensive, but we only take it when
627     // attempting to use GLX_ARB_create_context_robustness, in which
628     // case we need a GLXFBConfig for the window in order to create a
629     // context for it.
630     //
631     // TODO(kbr): this is not a reliable code path. On platforms which
632     // support it, we should use glXChooseFBConfig in the browser
633     // process to choose the FBConfig and from there the X Visual to
634     // use when creating the window in the first place. Then we can
635     // pass that FBConfig down rather than attempting to reconstitute
636     // it.
637
638     XWindowAttributes attributes;
639     if (!XGetWindowAttributes(
640         g_display,
641         parent_window_,
642         &attributes)) {
643       LOG(ERROR) << "XGetWindowAttributes failed for window " <<
644           parent_window_ << ".";
645       return NULL;
646     }
647
648     int visual_id = XVisualIDFromVisual(attributes.visual);
649
650     int num_elements = 0;
651     scoped_ptr_malloc<GLXFBConfig, ScopedPtrXFree> configs(
652         glXGetFBConfigs(g_display,
653                         DefaultScreen(g_display),
654                         &num_elements));
655     if (!configs.get()) {
656       LOG(ERROR) << "glXGetFBConfigs failed.";
657       return NULL;
658     }
659     if (!num_elements) {
660       LOG(ERROR) << "glXGetFBConfigs returned 0 elements.";
661       return NULL;
662     }
663     bool found = false;
664     int i;
665     for (i = 0; i < num_elements; ++i) {
666       int value;
667       if (glXGetFBConfigAttrib(
668               g_display, configs.get()[i], GLX_VISUAL_ID, &value)) {
669         LOG(ERROR) << "glXGetFBConfigAttrib failed.";
670         return NULL;
671       }
672       if (value == visual_id) {
673         found = true;
674         break;
675       }
676     }
677     if (found) {
678       config_ = configs.get()[i];
679     }
680   }
681
682   return config_;
683 }
684
685 bool NativeViewGLSurfaceGLX::PostSubBuffer(
686     int x, int y, int width, int height) {
687   DCHECK(gfx::g_driver_glx.ext.b_GLX_MESA_copy_sub_buffer);
688   glXCopySubBufferMESA(g_display, GetDrawableHandle(), x, y, width, height);
689   return true;
690 }
691
692 VSyncProvider* NativeViewGLSurfaceGLX::GetVSyncProvider() {
693   return vsync_provider_.get();
694 }
695
696 NativeViewGLSurfaceGLX::NativeViewGLSurfaceGLX()
697   : parent_window_(0),
698 #if defined(TOOLKIT_GTK)
699     child_window_(0),
700     dummy_window_(0),
701     backbuffer_allocated_(true),
702     frontbuffer_allocated_(true),
703 #endif
704     config_(NULL) {
705 }
706
707 NativeViewGLSurfaceGLX::~NativeViewGLSurfaceGLX() {
708   Destroy();
709 }
710
711 PbufferGLSurfaceGLX::PbufferGLSurfaceGLX(const gfx::Size& size)
712   : size_(size),
713     config_(NULL),
714     pbuffer_(0) {
715 }
716
717 bool PbufferGLSurfaceGLX::Initialize() {
718   DCHECK(!pbuffer_);
719
720   static const int config_attributes[] = {
721     GLX_BUFFER_SIZE, 32,
722     GLX_ALPHA_SIZE, 8,
723     GLX_BLUE_SIZE, 8,
724     GLX_GREEN_SIZE, 8,
725     GLX_RED_SIZE, 8,
726     GLX_RENDER_TYPE, GLX_RGBA_BIT,
727     GLX_DRAWABLE_TYPE, GLX_PBUFFER_BIT,
728     GLX_DOUBLEBUFFER, False,
729     0
730   };
731
732   int num_elements = 0;
733   scoped_ptr_malloc<GLXFBConfig, ScopedPtrXFree> configs(
734       glXChooseFBConfig(g_display,
735                         DefaultScreen(g_display),
736                         config_attributes,
737                         &num_elements));
738   if (!configs.get()) {
739     LOG(ERROR) << "glXChooseFBConfig failed.";
740     return false;
741   }
742   if (!num_elements) {
743     LOG(ERROR) << "glXChooseFBConfig returned 0 elements.";
744     return false;
745   }
746
747   config_ = configs.get()[0];
748
749   const int pbuffer_attributes[] = {
750     GLX_PBUFFER_WIDTH, size_.width(),
751     GLX_PBUFFER_HEIGHT, size_.height(),
752     0
753   };
754   pbuffer_ = glXCreatePbuffer(g_display,
755                               static_cast<GLXFBConfig>(config_),
756                               pbuffer_attributes);
757   if (!pbuffer_) {
758     Destroy();
759     LOG(ERROR) << "glXCreatePbuffer failed.";
760     return false;
761   }
762
763   return true;
764 }
765
766 void PbufferGLSurfaceGLX::Destroy() {
767   if (pbuffer_) {
768     glXDestroyPbuffer(g_display, pbuffer_);
769     pbuffer_ = 0;
770   }
771
772   config_ = NULL;
773 }
774
775 bool PbufferGLSurfaceGLX::IsOffscreen() {
776   return true;
777 }
778
779 bool PbufferGLSurfaceGLX::SwapBuffers() {
780   NOTREACHED() << "Attempted to call SwapBuffers on a pbuffer.";
781   return false;
782 }
783
784 gfx::Size PbufferGLSurfaceGLX::GetSize() {
785   return size_;
786 }
787
788 void* PbufferGLSurfaceGLX::GetHandle() {
789   return reinterpret_cast<void*>(pbuffer_);
790 }
791
792 void* PbufferGLSurfaceGLX::GetConfig() {
793   return config_;
794 }
795
796 PbufferGLSurfaceGLX::~PbufferGLSurfaceGLX() {
797   Destroy();
798 }
799
800 }  // namespace gfx