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