Obey renderer-supplied quota in the browser.
[chromium-blink-merge.git] / ui / gl / gl_surface_glx.cc
blob17a481a4de3809988ce9025e80631242e7395fd4
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.
5 extern "C" {
6 #include <X11/Xlib.h>
9 #include "ui/gl/gl_surface_glx.h"
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/sync_control_vsync_provider.h"
29 namespace gfx {
31 namespace {
33 // scoped_ptr functor for XFree(). Use as follows:
34 // scoped_ptr<XVisualInfo, ScopedPtrXFree> foo(...);
35 // where "XVisualInfo" is any X type that is freed with XFree.
36 struct ScopedPtrXFree {
37 void operator()(void* x) const {
38 ::XFree(x);
42 Display* g_display = NULL;
43 const char* g_glx_extensions = NULL;
44 bool g_glx_context_create = false;
45 bool g_glx_create_context_robustness_supported = false;
46 bool g_glx_texture_from_pixmap_supported = false;
47 bool g_glx_oml_sync_control_supported = false;
49 // Track support of glXGetMscRateOML separately from GLX_OML_sync_control as a
50 // whole since on some platforms (e.g. crosbug.com/34585), glXGetMscRateOML
51 // always fails even though GLX_OML_sync_control is reported as being supported.
52 bool g_glx_get_msc_rate_oml_supported = false;
54 bool g_glx_sgi_video_sync_supported = false;
56 class OMLSyncControlVSyncProvider
57 : public gfx::SyncControlVSyncProvider {
58 public:
59 explicit OMLSyncControlVSyncProvider(gfx::AcceleratedWidget window)
60 : SyncControlVSyncProvider(),
61 window_(window) {
64 virtual ~OMLSyncControlVSyncProvider() { }
66 protected:
67 virtual bool GetSyncValues(int64* system_time,
68 int64* media_stream_counter,
69 int64* swap_buffer_counter) OVERRIDE {
70 return glXGetSyncValuesOML(g_display, window_, system_time,
71 media_stream_counter, swap_buffer_counter);
74 virtual bool GetMscRate(int32* numerator, int32* denominator) OVERRIDE {
75 if (!g_glx_get_msc_rate_oml_supported)
76 return false;
78 if (!glXGetMscRateOML(g_display, window_, numerator, denominator)) {
79 // Once glXGetMscRateOML has been found to fail, don't try again,
80 // since each failing call may spew an error message.
81 g_glx_get_msc_rate_oml_supported = false;
82 return false;
85 return true;
88 private:
89 XID window_;
91 DISALLOW_COPY_AND_ASSIGN(OMLSyncControlVSyncProvider);
94 class SGIVideoSyncThread
95 : public base::Thread,
96 public base::NonThreadSafe,
97 public base::RefCounted<SGIVideoSyncThread> {
98 public:
99 static scoped_refptr<SGIVideoSyncThread> Create() {
100 if (!g_video_sync_thread) {
101 g_video_sync_thread = new SGIVideoSyncThread();
102 g_video_sync_thread->Start();
104 return g_video_sync_thread;
107 private:
108 friend class base::RefCounted<SGIVideoSyncThread>;
110 SGIVideoSyncThread() : base::Thread("SGI_video_sync") {
111 DCHECK(CalledOnValidThread());
114 virtual ~SGIVideoSyncThread() {
115 DCHECK(CalledOnValidThread());
116 g_video_sync_thread = NULL;
117 Stop();
120 static SGIVideoSyncThread* g_video_sync_thread;
122 DISALLOW_COPY_AND_ASSIGN(SGIVideoSyncThread);
125 class SGIVideoSyncProviderThreadShim {
126 public:
127 explicit SGIVideoSyncProviderThreadShim(XID window)
128 : window_(window),
129 context_(NULL),
130 message_loop_(base::MessageLoopProxy::current()),
131 cancel_vsync_flag_(),
132 vsync_lock_() {
133 // This ensures that creation of |window_| has occured when this shim
134 // is executing in the same process as the call to create |window_|.
135 XSync(g_display, False);
138 virtual ~SGIVideoSyncProviderThreadShim() {
139 if (context_) {
140 glXDestroyContext(display_, context_);
141 context_ = NULL;
145 base::CancellationFlag* cancel_vsync_flag() {
146 return &cancel_vsync_flag_;
149 base::Lock* vsync_lock() {
150 return &vsync_lock_;
153 void Initialize() {
154 DCHECK(display_);
156 XWindowAttributes attributes;
157 if (!XGetWindowAttributes(display_, window_, &attributes)) {
158 LOG(ERROR) << "XGetWindowAttributes failed for window " <<
159 window_ << ".";
160 return;
163 XVisualInfo visual_info_template;
164 visual_info_template.visualid = XVisualIDFromVisual(attributes.visual);
166 int visual_info_count = 0;
167 scoped_ptr<XVisualInfo, ScopedPtrXFree> visual_info_list(
168 XGetVisualInfo(display_, VisualIDMask,
169 &visual_info_template, &visual_info_count));
171 DCHECK(visual_info_list.get());
172 if (visual_info_count == 0) {
173 LOG(ERROR) << "No visual info for visual ID.";
174 return;
177 context_ = glXCreateContext(display_, visual_info_list.get(), NULL, True);
179 DCHECK(NULL != context_);
182 void GetVSyncParameters(const VSyncProvider::UpdateVSyncCallback& callback) {
183 base::TimeTicks now;
185 // Don't allow |window_| destruction while we're probing vsync.
186 base::AutoLock locked(vsync_lock_);
188 if (!context_ || cancel_vsync_flag_.IsSet())
189 return;
191 glXMakeCurrent(display_, window_, context_);
193 unsigned int retrace_count = 0;
194 if (glXWaitVideoSyncSGI(1, 0, &retrace_count) != 0)
195 return;
197 TRACE_EVENT_INSTANT0("gpu", "vblank", TRACE_EVENT_SCOPE_THREAD);
198 now = base::TimeTicks::HighResNow();
200 glXMakeCurrent(display_, 0, 0);
203 const base::TimeDelta kDefaultInterval =
204 base::TimeDelta::FromSeconds(1) / 60;
206 message_loop_->PostTask(
207 FROM_HERE, base::Bind(callback, now, kDefaultInterval));
210 private:
211 // For initialization of display_ in GLSurface::InitializeOneOff before
212 // the sandbox goes up.
213 friend class gfx::GLSurfaceGLX;
215 static Display* display_;
217 XID window_;
218 GLXContext context_;
220 scoped_refptr<base::MessageLoopProxy> message_loop_;
222 base::CancellationFlag cancel_vsync_flag_;
223 base::Lock vsync_lock_;
225 DISALLOW_COPY_AND_ASSIGN(SGIVideoSyncProviderThreadShim);
228 class SGIVideoSyncVSyncProvider
229 : public gfx::VSyncProvider,
230 public base::SupportsWeakPtr<SGIVideoSyncVSyncProvider> {
231 public:
232 explicit SGIVideoSyncVSyncProvider(gfx::AcceleratedWidget window)
233 : vsync_thread_(SGIVideoSyncThread::Create()),
234 shim_(new SGIVideoSyncProviderThreadShim(window)),
235 cancel_vsync_flag_(shim_->cancel_vsync_flag()),
236 vsync_lock_(shim_->vsync_lock()) {
237 vsync_thread_->message_loop()->PostTask(
238 FROM_HERE,
239 base::Bind(&SGIVideoSyncProviderThreadShim::Initialize,
240 base::Unretained(shim_.get())));
243 virtual ~SGIVideoSyncVSyncProvider() {
245 base::AutoLock locked(*vsync_lock_);
246 cancel_vsync_flag_->Set();
249 // Hand-off |shim_| to be deleted on the |vsync_thread_|.
250 vsync_thread_->message_loop()->DeleteSoon(
251 FROM_HERE,
252 shim_.release());
255 virtual void GetVSyncParameters(
256 const VSyncProvider::UpdateVSyncCallback& callback) OVERRIDE {
257 // Only one outstanding request per surface.
258 if (!pending_callback_) {
259 pending_callback_.reset(
260 new VSyncProvider::UpdateVSyncCallback(callback));
261 vsync_thread_->message_loop()->PostTask(
262 FROM_HERE,
263 base::Bind(&SGIVideoSyncProviderThreadShim::GetVSyncParameters,
264 base::Unretained(shim_.get()),
265 base::Bind(
266 &SGIVideoSyncVSyncProvider::PendingCallbackRunner,
267 AsWeakPtr())));
271 private:
272 void PendingCallbackRunner(const base::TimeTicks timebase,
273 const base::TimeDelta interval) {
274 DCHECK(pending_callback_);
275 pending_callback_->Run(timebase, interval);
276 pending_callback_.reset();
279 scoped_refptr<SGIVideoSyncThread> vsync_thread_;
281 // Thread shim through which the sync provider is accessed on |vsync_thread_|.
282 scoped_ptr<SGIVideoSyncProviderThreadShim> shim_;
284 scoped_ptr<VSyncProvider::UpdateVSyncCallback> pending_callback_;
286 // Raw pointers to sync primitives owned by the shim_.
287 // These will only be referenced before we post a task to destroy
288 // the shim_, so they are safe to access.
289 base::CancellationFlag* cancel_vsync_flag_;
290 base::Lock* vsync_lock_;
292 DISALLOW_COPY_AND_ASSIGN(SGIVideoSyncVSyncProvider);
295 SGIVideoSyncThread* SGIVideoSyncThread::g_video_sync_thread = NULL;
297 // In order to take advantage of GLX_SGI_video_sync, we need a display
298 // for use on a separate thread. We must allocate this before the sandbox
299 // goes up (rather than on-demand when we start the thread).
300 Display* SGIVideoSyncProviderThreadShim::display_ = NULL;
302 #if defined(TOOLKIT_GTK)
303 // A mechanism for forwarding XExpose events from one window to another.
304 // Because in the workaround for http://crbug.com/145600 the child window
305 // is placed on top of the parent window, only the child window will receive
306 // all expose events. These need to be forwared to the parent window to inform
307 // it that it should paint.
308 class XExposeEventForwarder : public base::MessagePumpObserver {
309 public:
310 XExposeEventForwarder() {}
311 virtual ~XExposeEventForwarder() {
312 DCHECK(child_to_parent_map_.empty());
314 void AddParentChildPair(gfx::AcceleratedWidget parent_window,
315 gfx::AcceleratedWidget child_window) {
316 if (child_to_parent_map_.empty())
317 base::MessagePumpX11::Current()->AddObserver(this);
319 DCHECK(child_to_parent_map_.find(child_window) ==
320 child_to_parent_map_.end());
321 child_to_parent_map_.insert(std::make_pair(
322 child_window, parent_window));
324 void RemoveParentChildPair(gfx::AcceleratedWidget parent_window,
325 gfx::AcceleratedWidget child_window) {
326 DCHECK(child_to_parent_map_.find(child_window) !=
327 child_to_parent_map_.end());
328 child_to_parent_map_.erase(child_window);
330 if (child_to_parent_map_.empty())
331 base::MessagePumpX11::Current()->RemoveObserver(this);
334 private:
335 virtual base::EventStatus WillProcessEvent (
336 const base::NativeEvent& xevent) OVERRIDE {
337 if (xevent->type != Expose)
338 return base::EVENT_CONTINUE;
340 WindowMap::const_iterator found = child_to_parent_map_.find(
341 xevent->xexpose.window);
342 if (found == child_to_parent_map_.end())
343 return base::EVENT_CONTINUE;
345 gfx::AcceleratedWidget target_window = found->second;
346 XEvent forwarded_event = *xevent;
347 forwarded_event.xexpose.window = target_window;
348 XSendEvent(g_display, target_window, False, ExposureMask,
349 &forwarded_event);
350 return base::EVENT_CONTINUE;
352 virtual void DidProcessEvent(const base::NativeEvent& xevent) OVERRIDE {
355 typedef std::map<gfx::AcceleratedWidget, gfx::AcceleratedWidget> WindowMap;
356 WindowMap child_to_parent_map_;
358 DISALLOW_COPY_AND_ASSIGN(XExposeEventForwarder);
361 static base::LazyInstance<XExposeEventForwarder> g_xexpose_event_forwarder =
362 LAZY_INSTANCE_INITIALIZER;
364 // Do not use this workaround when running in test harnesses that do not have
365 // a message loop or do not have a TYPE_GPU message loop.
366 bool g_create_child_windows = false;
367 #endif
369 } // namespace
371 GLSurfaceGLX::GLSurfaceGLX() {}
373 bool GLSurfaceGLX::InitializeOneOff() {
374 static bool initialized = false;
375 if (initialized)
376 return true;
378 // http://crbug.com/245466
379 setenv("force_s3tc_enable", "true", 1);
381 // SGIVideoSyncProviderShim (if instantiated) will issue X commands on
382 // it's own thread.
383 XInitThreads();
385 #if defined(TOOLKIT_GTK)
386 // Be sure to use the X display handle and not the GTK display handle if this
387 // is the GPU process.
388 g_create_child_windows =
389 base::MessageLoop::current() &&
390 base::MessageLoop::current()->type() == base::MessageLoop::TYPE_GPU;
392 if (g_create_child_windows)
393 g_display = base::MessagePumpX11::GetDefaultXDisplay();
394 else
395 g_display = base::MessagePumpForUI::GetDefaultXDisplay();
396 #else
397 g_display = base::MessagePumpForUI::GetDefaultXDisplay();
398 #endif
400 if (!g_display) {
401 LOG(ERROR) << "XOpenDisplay failed.";
402 return false;
405 int major, minor;
406 if (!glXQueryVersion(g_display, &major, &minor)) {
407 LOG(ERROR) << "glxQueryVersion failed";
408 return false;
411 if (major == 1 && minor < 3) {
412 LOG(ERROR) << "GLX 1.3 or later is required.";
413 return false;
416 g_glx_extensions = glXQueryExtensionsString(g_display, 0);
417 g_glx_context_create =
418 HasGLXExtension("GLX_ARB_create_context");
419 g_glx_create_context_robustness_supported =
420 HasGLXExtension("GLX_ARB_create_context_robustness");
421 g_glx_texture_from_pixmap_supported =
422 HasGLXExtension("GLX_EXT_texture_from_pixmap");
423 g_glx_oml_sync_control_supported =
424 HasGLXExtension("GLX_OML_sync_control");
425 g_glx_get_msc_rate_oml_supported = g_glx_oml_sync_control_supported;
426 g_glx_sgi_video_sync_supported =
427 HasGLXExtension("GLX_SGI_video_sync");
429 if (!g_glx_get_msc_rate_oml_supported && g_glx_sgi_video_sync_supported)
430 SGIVideoSyncProviderThreadShim::display_ = XOpenDisplay(NULL);
432 initialized = true;
433 return true;
436 // static
437 const char* GLSurfaceGLX::GetGLXExtensions() {
438 return g_glx_extensions;
441 // static
442 bool GLSurfaceGLX::HasGLXExtension(const char* name) {
443 return ExtensionsContain(GetGLXExtensions(), name);
446 // static
447 bool GLSurfaceGLX::IsCreateContextSupported() {
448 return g_glx_context_create;
451 // static
452 bool GLSurfaceGLX::IsCreateContextRobustnessSupported() {
453 return g_glx_create_context_robustness_supported;
456 // static
457 bool GLSurfaceGLX::IsTextureFromPixmapSupported() {
458 return g_glx_texture_from_pixmap_supported;
461 // static
462 bool GLSurfaceGLX::IsOMLSyncControlSupported() {
463 return g_glx_oml_sync_control_supported;
466 void* GLSurfaceGLX::GetDisplay() {
467 return g_display;
470 GLSurfaceGLX::~GLSurfaceGLX() {}
472 #if defined(TOOLKIT_GTK)
473 bool NativeViewGLSurfaceGLX::SetBackbufferAllocation(bool allocated) {
474 backbuffer_allocated_ = allocated;
475 AdjustBufferAllocation();
476 return true;
479 void NativeViewGLSurfaceGLX::SetFrontbufferAllocation(bool allocated) {
480 frontbuffer_allocated_ = allocated;
481 AdjustBufferAllocation();
484 void NativeViewGLSurfaceGLX::AdjustBufferAllocation() {
485 if (!g_create_child_windows)
486 return;
488 if (frontbuffer_allocated_ || backbuffer_allocated_)
489 CreateChildWindow();
490 else
491 DestroyChildWindow();
494 void NativeViewGLSurfaceGLX::CreateChildWindow() {
495 DCHECK(g_create_child_windows);
497 if (child_window_)
498 return;
500 XSetWindowAttributes set_window_attributes;
501 set_window_attributes.event_mask = ExposureMask;
502 child_window_ = XCreateWindow(
503 g_display, parent_window_, 0, 0, size_.width(), size_.height(), 0,
504 CopyFromParent, InputOutput, CopyFromParent, CWEventMask,
505 &set_window_attributes);
506 g_xexpose_event_forwarder.Pointer()->AddParentChildPair(
507 parent_window_, child_window_);
509 XMapWindow(g_display, child_window_);
510 XFlush(g_display);
513 void NativeViewGLSurfaceGLX::DestroyChildWindow() {
514 if (!child_window_)
515 return;
517 g_xexpose_event_forwarder.Pointer()->RemoveParentChildPair(
518 parent_window_, child_window_);
519 XDestroyWindow(g_display, child_window_);
520 XFlush(g_display);
521 child_window_ = 0;
523 #endif
525 NativeViewGLSurfaceGLX::NativeViewGLSurfaceGLX(gfx::AcceleratedWidget window)
526 : parent_window_(window),
527 #if defined(TOOLKIT_GTK)
528 child_window_(0),
529 dummy_window_(0),
530 backbuffer_allocated_(true),
531 frontbuffer_allocated_(true),
532 #endif
533 config_(NULL) {
536 gfx::AcceleratedWidget NativeViewGLSurfaceGLX::GetDrawableHandle() const {
537 #if defined(TOOLKIT_GTK)
538 if (g_create_child_windows) {
539 if (child_window_)
540 return child_window_;
541 return dummy_window_;
543 #endif
544 return parent_window_;
547 bool NativeViewGLSurfaceGLX::Initialize() {
548 XWindowAttributes attributes;
549 if (!XGetWindowAttributes(g_display, parent_window_, &attributes)) {
550 LOG(ERROR) << "XGetWindowAttributes failed for window " << parent_window_
551 << ".";
552 return false;
554 size_ = gfx::Size(attributes.width, attributes.height);
556 gfx::AcceleratedWidget window_for_vsync = parent_window_;
558 #if defined(TOOLKIT_GTK)
559 if (g_create_child_windows) {
560 dummy_window_ = XCreateWindow(
561 g_display,
562 RootWindow(g_display, XScreenNumberOfScreen(attributes.screen)),
563 0, 0, 1, 1, 0, CopyFromParent, InputOutput, attributes.visual, 0, NULL);
564 window_for_vsync = dummy_window_;
565 CreateChildWindow();
567 #endif
569 if (g_glx_oml_sync_control_supported)
570 vsync_provider_.reset(new OMLSyncControlVSyncProvider(window_for_vsync));
571 else if (g_glx_sgi_video_sync_supported)
572 vsync_provider_.reset(new SGIVideoSyncVSyncProvider(window_for_vsync));
574 return true;
577 void NativeViewGLSurfaceGLX::Destroy() {
578 #if defined(TOOLKIT_GTK)
579 DestroyChildWindow();
580 if (dummy_window_)
581 XDestroyWindow(g_display, dummy_window_);
582 dummy_window_ = 0;
583 #endif
586 bool NativeViewGLSurfaceGLX::Resize(const gfx::Size& size) {
587 #if defined(TOOLKIT_GTK)
588 if (child_window_) {
589 XResizeWindow(g_display, child_window_, size.width(), size.height());
590 XFlush(g_display);
592 #endif
593 size_ = size;
594 return true;
597 bool NativeViewGLSurfaceGLX::IsOffscreen() {
598 return false;
601 bool NativeViewGLSurfaceGLX::SwapBuffers() {
602 TRACE_EVENT2("gpu", "NativeViewGLSurfaceGLX:RealSwapBuffers",
603 "width", GetSize().width(),
604 "height", GetSize().height());
606 glXSwapBuffers(g_display, GetDrawableHandle());
607 return true;
610 gfx::Size NativeViewGLSurfaceGLX::GetSize() {
611 return size_;
614 void* NativeViewGLSurfaceGLX::GetHandle() {
615 return reinterpret_cast<void*>(GetDrawableHandle());
618 bool NativeViewGLSurfaceGLX::SupportsPostSubBuffer() {
619 return gfx::g_driver_glx.ext.b_GLX_MESA_copy_sub_buffer;
622 void* NativeViewGLSurfaceGLX::GetConfig() {
623 if (!config_) {
624 // This code path is expensive, but we only take it when
625 // attempting to use GLX_ARB_create_context_robustness, in which
626 // case we need a GLXFBConfig for the window in order to create a
627 // context for it.
629 // TODO(kbr): this is not a reliable code path. On platforms which
630 // support it, we should use glXChooseFBConfig in the browser
631 // process to choose the FBConfig and from there the X Visual to
632 // use when creating the window in the first place. Then we can
633 // pass that FBConfig down rather than attempting to reconstitute
634 // it.
636 XWindowAttributes attributes;
637 if (!XGetWindowAttributes(
638 g_display,
639 parent_window_,
640 &attributes)) {
641 LOG(ERROR) << "XGetWindowAttributes failed for window " <<
642 parent_window_ << ".";
643 return NULL;
646 int visual_id = XVisualIDFromVisual(attributes.visual);
648 int num_elements = 0;
649 scoped_ptr<GLXFBConfig, ScopedPtrXFree> configs(
650 glXGetFBConfigs(g_display,
651 DefaultScreen(g_display),
652 &num_elements));
653 if (!configs.get()) {
654 LOG(ERROR) << "glXGetFBConfigs failed.";
655 return NULL;
657 if (!num_elements) {
658 LOG(ERROR) << "glXGetFBConfigs returned 0 elements.";
659 return NULL;
661 bool found = false;
662 int i;
663 for (i = 0; i < num_elements; ++i) {
664 int value;
665 if (glXGetFBConfigAttrib(
666 g_display, configs.get()[i], GLX_VISUAL_ID, &value)) {
667 LOG(ERROR) << "glXGetFBConfigAttrib failed.";
668 return NULL;
670 if (value == visual_id) {
671 found = true;
672 break;
675 if (found) {
676 config_ = configs.get()[i];
680 return config_;
683 bool NativeViewGLSurfaceGLX::PostSubBuffer(
684 int x, int y, int width, int height) {
685 DCHECK(gfx::g_driver_glx.ext.b_GLX_MESA_copy_sub_buffer);
686 glXCopySubBufferMESA(g_display, GetDrawableHandle(), x, y, width, height);
687 return true;
690 VSyncProvider* NativeViewGLSurfaceGLX::GetVSyncProvider() {
691 return vsync_provider_.get();
694 NativeViewGLSurfaceGLX::NativeViewGLSurfaceGLX()
695 : parent_window_(0),
696 #if defined(TOOLKIT_GTK)
697 child_window_(0),
698 dummy_window_(0),
699 backbuffer_allocated_(true),
700 frontbuffer_allocated_(true),
701 #endif
702 config_(NULL) {
705 NativeViewGLSurfaceGLX::~NativeViewGLSurfaceGLX() {
706 Destroy();
709 PbufferGLSurfaceGLX::PbufferGLSurfaceGLX(const gfx::Size& size)
710 : size_(size),
711 config_(NULL),
712 pbuffer_(0) {
715 bool PbufferGLSurfaceGLX::Initialize() {
716 DCHECK(!pbuffer_);
718 static const int config_attributes[] = {
719 GLX_BUFFER_SIZE, 32,
720 GLX_ALPHA_SIZE, 8,
721 GLX_BLUE_SIZE, 8,
722 GLX_GREEN_SIZE, 8,
723 GLX_RED_SIZE, 8,
724 GLX_RENDER_TYPE, GLX_RGBA_BIT,
725 GLX_DRAWABLE_TYPE, GLX_PBUFFER_BIT,
726 GLX_DOUBLEBUFFER, False,
730 int num_elements = 0;
731 scoped_ptr<GLXFBConfig, ScopedPtrXFree> configs(
732 glXChooseFBConfig(g_display,
733 DefaultScreen(g_display),
734 config_attributes,
735 &num_elements));
736 if (!configs.get()) {
737 LOG(ERROR) << "glXChooseFBConfig failed.";
738 return false;
740 if (!num_elements) {
741 LOG(ERROR) << "glXChooseFBConfig returned 0 elements.";
742 return false;
745 config_ = configs.get()[0];
747 const int pbuffer_attributes[] = {
748 GLX_PBUFFER_WIDTH, size_.width(),
749 GLX_PBUFFER_HEIGHT, size_.height(),
752 pbuffer_ = glXCreatePbuffer(g_display,
753 static_cast<GLXFBConfig>(config_),
754 pbuffer_attributes);
755 if (!pbuffer_) {
756 Destroy();
757 LOG(ERROR) << "glXCreatePbuffer failed.";
758 return false;
761 return true;
764 void PbufferGLSurfaceGLX::Destroy() {
765 if (pbuffer_) {
766 glXDestroyPbuffer(g_display, pbuffer_);
767 pbuffer_ = 0;
770 config_ = NULL;
773 bool PbufferGLSurfaceGLX::IsOffscreen() {
774 return true;
777 bool PbufferGLSurfaceGLX::SwapBuffers() {
778 NOTREACHED() << "Attempted to call SwapBuffers on a pbuffer.";
779 return false;
782 gfx::Size PbufferGLSurfaceGLX::GetSize() {
783 return size_;
786 void* PbufferGLSurfaceGLX::GetHandle() {
787 return reinterpret_cast<void*>(pbuffer_);
790 void* PbufferGLSurfaceGLX::GetConfig() {
791 return config_;
794 PbufferGLSurfaceGLX::~PbufferGLSurfaceGLX() {
795 Destroy();
798 } // namespace gfx