blob: 2740138ccc2cdeaf659a1c3d78c0d4009770051c [file] [log] [blame]
James Robinson646469d2014-10-03 15:33:28 -07001// 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
5extern "C" {
6#include <X11/Xlib.h>
7}
8
9#include "ui/gl/gl_surface_glx.h"
10
11#include "base/basictypes.h"
James Robinson646469d2014-10-03 15:33:28 -070012#include "base/lazy_instance.h"
13#include "base/logging.h"
14#include "base/memory/scoped_ptr.h"
15#include "base/memory/weak_ptr.h"
16#include "base/message_loop/message_loop.h"
17#include "base/single_thread_task_runner.h"
18#include "base/synchronization/cancellation_flag.h"
19#include "base/synchronization/lock.h"
20#include "base/thread_task_runner_handle.h"
21#include "base/threading/non_thread_safe.h"
22#include "base/threading/thread.h"
23#include "base/time/time.h"
Benjamin Lermancdfc88d2015-02-03 14:35:12 +010024#include "base/trace_event/trace_event.h"
James Robinson646469d2014-10-03 15:33:28 -070025#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
32namespace gfx {
33
34namespace {
35
James Robinson646469d2014-10-03 15:33:28 -070036Display* g_display = NULL;
37const char* g_glx_extensions = NULL;
38bool g_glx_context_create = false;
39bool g_glx_create_context_robustness_supported = false;
40bool g_glx_texture_from_pixmap_supported = false;
41bool g_glx_oml_sync_control_supported = false;
42
43// Track support of glXGetMscRateOML separately from GLX_OML_sync_control as a
44// whole since on some platforms (e.g. crosbug.com/34585), glXGetMscRateOML
45// always fails even though GLX_OML_sync_control is reported as being supported.
46bool g_glx_get_msc_rate_oml_supported = false;
47
48bool g_glx_sgi_video_sync_supported = false;
49
50static const base::TimeDelta kGetVSyncParametersMinPeriod =
51#if defined(OS_LINUX)
52 // See crbug.com/373489
53 // On Linux, querying the vsync parameters might burn CPU for up to an
54 // entire vsync, so we only query periodically to reduce CPU usage.
55 // 5 seconds is chosen somewhat abitrarily as a balance between:
56 // a) Drift in the phase of our signal.
57 // b) Potential janks from periodically pegging the CPU.
58 base::TimeDelta::FromSeconds(5);
59#else
60 base::TimeDelta::FromSeconds(0);
61#endif
62
63class OMLSyncControlVSyncProvider
64 : public gfx::SyncControlVSyncProvider {
65 public:
66 explicit OMLSyncControlVSyncProvider(gfx::AcceleratedWidget window)
67 : SyncControlVSyncProvider(),
68 window_(window) {
69 }
70
James Robinson7f480212014-10-31 10:28:08 -070071 ~OMLSyncControlVSyncProvider() override {}
James Robinson646469d2014-10-03 15:33:28 -070072
73 protected:
James Robinson7f480212014-10-31 10:28:08 -070074 bool GetSyncValues(int64* system_time,
75 int64* media_stream_counter,
76 int64* swap_buffer_counter) override {
James Robinson646469d2014-10-03 15:33:28 -070077 return glXGetSyncValuesOML(g_display, window_, system_time,
78 media_stream_counter, swap_buffer_counter);
79 }
80
James Robinson7f480212014-10-31 10:28:08 -070081 bool GetMscRate(int32* numerator, int32* denominator) override {
James Robinson646469d2014-10-03 15:33:28 -070082 if (!g_glx_get_msc_rate_oml_supported)
83 return false;
84
85 if (!glXGetMscRateOML(g_display, window_, numerator, denominator)) {
86 // Once glXGetMscRateOML has been found to fail, don't try again,
87 // since each failing call may spew an error message.
88 g_glx_get_msc_rate_oml_supported = false;
89 return false;
90 }
91
92 return true;
93 }
94
95 private:
96 XID window_;
97
98 DISALLOW_COPY_AND_ASSIGN(OMLSyncControlVSyncProvider);
99};
100
101class SGIVideoSyncThread
102 : public base::Thread,
103 public base::NonThreadSafe,
104 public base::RefCounted<SGIVideoSyncThread> {
105 public:
106 static scoped_refptr<SGIVideoSyncThread> Create() {
107 if (!g_video_sync_thread) {
108 g_video_sync_thread = new SGIVideoSyncThread();
109 g_video_sync_thread->Start();
110 }
111 return g_video_sync_thread;
112 }
113
114 private:
115 friend class base::RefCounted<SGIVideoSyncThread>;
116
117 SGIVideoSyncThread() : base::Thread("SGI_video_sync") {
118 DCHECK(CalledOnValidThread());
119 }
120
James Robinson7f480212014-10-31 10:28:08 -0700121 ~SGIVideoSyncThread() override {
James Robinson646469d2014-10-03 15:33:28 -0700122 DCHECK(CalledOnValidThread());
123 g_video_sync_thread = NULL;
124 Stop();
125 }
126
127 static SGIVideoSyncThread* g_video_sync_thread;
128
129 DISALLOW_COPY_AND_ASSIGN(SGIVideoSyncThread);
130};
131
132class SGIVideoSyncProviderThreadShim {
133 public:
134 explicit SGIVideoSyncProviderThreadShim(XID window)
135 : window_(window),
136 context_(NULL),
137 task_runner_(base::ThreadTaskRunnerHandle::Get()),
138 cancel_vsync_flag_(),
139 vsync_lock_() {
140 // This ensures that creation of |window_| has occured when this shim
141 // is executing in the same process as the call to create |window_|.
142 XSync(g_display, False);
143 }
144
145 virtual ~SGIVideoSyncProviderThreadShim() {
146 if (context_) {
147 glXDestroyContext(display_, context_);
148 context_ = NULL;
149 }
150 }
151
152 base::CancellationFlag* cancel_vsync_flag() {
153 return &cancel_vsync_flag_;
154 }
155
156 base::Lock* vsync_lock() {
157 return &vsync_lock_;
158 }
159
160 void Initialize() {
161 DCHECK(display_);
162
163 XWindowAttributes attributes;
164 if (!XGetWindowAttributes(display_, window_, &attributes)) {
165 LOG(ERROR) << "XGetWindowAttributes failed for window " <<
166 window_ << ".";
167 return;
168 }
169
170 XVisualInfo visual_info_template;
171 visual_info_template.visualid = XVisualIDFromVisual(attributes.visual);
172
173 int visual_info_count = 0;
Nick Bray0bcbd3b2015-03-12 16:29:36 -0700174 gfx::XScopedPtr<XVisualInfo> visual_info_list(XGetVisualInfo(
175 display_, VisualIDMask, &visual_info_template, &visual_info_count));
James Robinson646469d2014-10-03 15:33:28 -0700176
177 DCHECK(visual_info_list.get());
178 if (visual_info_count == 0) {
179 LOG(ERROR) << "No visual info for visual ID.";
180 return;
181 }
182
183 context_ = glXCreateContext(display_, visual_info_list.get(), NULL, True);
184
185 DCHECK(NULL != context_);
186 }
187
188 void GetVSyncParameters(const VSyncProvider::UpdateVSyncCallback& callback) {
189 base::TimeTicks now;
190 {
191 // Don't allow |window_| destruction while we're probing vsync.
192 base::AutoLock locked(vsync_lock_);
193
194 if (!context_ || cancel_vsync_flag_.IsSet())
195 return;
196
197 glXMakeCurrent(display_, window_, context_);
198
199 unsigned int retrace_count = 0;
200 if (glXWaitVideoSyncSGI(1, 0, &retrace_count) != 0)
201 return;
202
203 TRACE_EVENT_INSTANT0("gpu", "vblank", TRACE_EVENT_SCOPE_THREAD);
Elliot Glayshereae49292015-01-28 10:47:32 -0800204 now = base::TimeTicks::Now();
James Robinson646469d2014-10-03 15:33:28 -0700205
206 glXMakeCurrent(display_, 0, 0);
207 }
208
209 const base::TimeDelta kDefaultInterval =
210 base::TimeDelta::FromSeconds(1) / 60;
211
212 task_runner_->PostTask(
213 FROM_HERE, base::Bind(callback, now, kDefaultInterval));
214 }
215
216 private:
217 // For initialization of display_ in GLSurface::InitializeOneOff before
218 // the sandbox goes up.
219 friend class gfx::GLSurfaceGLX;
220
221 static Display* display_;
222
223 XID window_;
224 GLXContext context_;
225
226 scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
227
228 base::CancellationFlag cancel_vsync_flag_;
229 base::Lock vsync_lock_;
230
231 DISALLOW_COPY_AND_ASSIGN(SGIVideoSyncProviderThreadShim);
232};
233
234class SGIVideoSyncVSyncProvider
235 : public gfx::VSyncProvider,
236 public base::SupportsWeakPtr<SGIVideoSyncVSyncProvider> {
237 public:
238 explicit SGIVideoSyncVSyncProvider(gfx::AcceleratedWidget window)
239 : vsync_thread_(SGIVideoSyncThread::Create()),
240 shim_(new SGIVideoSyncProviderThreadShim(window)),
241 cancel_vsync_flag_(shim_->cancel_vsync_flag()),
242 vsync_lock_(shim_->vsync_lock()) {
243 vsync_thread_->message_loop()->PostTask(
244 FROM_HERE,
245 base::Bind(&SGIVideoSyncProviderThreadShim::Initialize,
246 base::Unretained(shim_.get())));
247 }
248
James Robinson7f480212014-10-31 10:28:08 -0700249 ~SGIVideoSyncVSyncProvider() override {
James Robinson646469d2014-10-03 15:33:28 -0700250 {
251 base::AutoLock locked(*vsync_lock_);
252 cancel_vsync_flag_->Set();
253 }
254
255 // Hand-off |shim_| to be deleted on the |vsync_thread_|.
256 vsync_thread_->message_loop()->DeleteSoon(
257 FROM_HERE,
258 shim_.release());
259 }
260
James Robinson7f480212014-10-31 10:28:08 -0700261 void GetVSyncParameters(
James Robinsone2ac7e82014-10-15 13:21:59 -0700262 const VSyncProvider::UpdateVSyncCallback& callback) override {
James Robinson646469d2014-10-03 15:33:28 -0700263 if (kGetVSyncParametersMinPeriod > base::TimeDelta()) {
264 base::TimeTicks now = base::TimeTicks::Now();
265 base::TimeDelta delta = now - last_get_vsync_parameters_time_;
266 if (delta < kGetVSyncParametersMinPeriod)
267 return;
268 last_get_vsync_parameters_time_ = now;
269 }
270
271 // Only one outstanding request per surface.
272 if (!pending_callback_) {
273 pending_callback_.reset(
274 new VSyncProvider::UpdateVSyncCallback(callback));
275 vsync_thread_->message_loop()->PostTask(
276 FROM_HERE,
277 base::Bind(&SGIVideoSyncProviderThreadShim::GetVSyncParameters,
278 base::Unretained(shim_.get()),
279 base::Bind(
280 &SGIVideoSyncVSyncProvider::PendingCallbackRunner,
281 AsWeakPtr())));
282 }
283 }
284
285 private:
286 void PendingCallbackRunner(const base::TimeTicks timebase,
287 const base::TimeDelta interval) {
288 DCHECK(pending_callback_);
289 pending_callback_->Run(timebase, interval);
290 pending_callback_.reset();
291 }
292
293 scoped_refptr<SGIVideoSyncThread> vsync_thread_;
294
295 // Thread shim through which the sync provider is accessed on |vsync_thread_|.
296 scoped_ptr<SGIVideoSyncProviderThreadShim> shim_;
297
298 scoped_ptr<VSyncProvider::UpdateVSyncCallback> pending_callback_;
299
300 // Raw pointers to sync primitives owned by the shim_.
301 // These will only be referenced before we post a task to destroy
302 // the shim_, so they are safe to access.
303 base::CancellationFlag* cancel_vsync_flag_;
304 base::Lock* vsync_lock_;
305
306 base::TimeTicks last_get_vsync_parameters_time_;
307
308 DISALLOW_COPY_AND_ASSIGN(SGIVideoSyncVSyncProvider);
309};
310
311SGIVideoSyncThread* SGIVideoSyncThread::g_video_sync_thread = NULL;
312
313// In order to take advantage of GLX_SGI_video_sync, we need a display
314// for use on a separate thread. We must allocate this before the sandbox
315// goes up (rather than on-demand when we start the thread).
316Display* SGIVideoSyncProviderThreadShim::display_ = NULL;
317
318} // namespace
319
Ian Fischerf66f4372015-06-09 16:50:28 -0700320GLSurfaceGLX::GLSurfaceGLX(
321 const gfx::SurfaceConfiguration& requested_configuration)
322 : GLSurface(requested_configuration) {
323}
James Robinson646469d2014-10-03 15:33:28 -0700324
325bool GLSurfaceGLX::InitializeOneOff() {
326 static bool initialized = false;
327 if (initialized)
328 return true;
329
330 // http://crbug.com/245466
331 setenv("force_s3tc_enable", "true", 1);
332
333 // SGIVideoSyncProviderShim (if instantiated) will issue X commands on
334 // it's own thread.
335 gfx::InitializeThreadedX11();
336 g_display = gfx::GetXDisplay();
337
338 if (!g_display) {
339 LOG(ERROR) << "XOpenDisplay failed.";
340 return false;
341 }
342
343 int major, minor;
344 if (!glXQueryVersion(g_display, &major, &minor)) {
345 LOG(ERROR) << "glxQueryVersion failed";
346 return false;
347 }
348
349 if (major == 1 && minor < 3) {
350 LOG(ERROR) << "GLX 1.3 or later is required.";
351 return false;
352 }
353
354 g_glx_extensions = glXQueryExtensionsString(g_display, 0);
355 g_glx_context_create =
356 HasGLXExtension("GLX_ARB_create_context");
357 g_glx_create_context_robustness_supported =
358 HasGLXExtension("GLX_ARB_create_context_robustness");
359 g_glx_texture_from_pixmap_supported =
360 HasGLXExtension("GLX_EXT_texture_from_pixmap");
361 g_glx_oml_sync_control_supported =
362 HasGLXExtension("GLX_OML_sync_control");
363 g_glx_get_msc_rate_oml_supported = g_glx_oml_sync_control_supported;
364 g_glx_sgi_video_sync_supported =
365 HasGLXExtension("GLX_SGI_video_sync");
366
367 if (!g_glx_get_msc_rate_oml_supported && g_glx_sgi_video_sync_supported)
368 SGIVideoSyncProviderThreadShim::display_ = gfx::OpenNewXDisplay();
369
370 initialized = true;
371 return true;
372}
373
374// static
375const char* GLSurfaceGLX::GetGLXExtensions() {
376 return g_glx_extensions;
377}
378
379// static
380bool GLSurfaceGLX::HasGLXExtension(const char* name) {
381 return ExtensionsContain(GetGLXExtensions(), name);
382}
383
384// static
385bool GLSurfaceGLX::IsCreateContextSupported() {
386 return g_glx_context_create;
387}
388
389// static
390bool GLSurfaceGLX::IsCreateContextRobustnessSupported() {
391 return g_glx_create_context_robustness_supported;
392}
393
394// static
395bool GLSurfaceGLX::IsTextureFromPixmapSupported() {
396 return g_glx_texture_from_pixmap_supported;
397}
398
399// static
400bool GLSurfaceGLX::IsOMLSyncControlSupported() {
401 return g_glx_oml_sync_control_supported;
402}
403
404void* GLSurfaceGLX::GetDisplay() {
405 return g_display;
406}
407
408GLSurfaceGLX::~GLSurfaceGLX() {}
409
Ian Fischerf66f4372015-06-09 16:50:28 -0700410NativeViewGLSurfaceGLX::NativeViewGLSurfaceGLX(
411 gfx::AcceleratedWidget window,
412 const gfx::SurfaceConfiguration& requested_configuration)
413 : GLSurfaceGLX(requested_configuration),
414 parent_window_(window),
James Robinson646469d2014-10-03 15:33:28 -0700415 window_(0),
416 config_(NULL) {
417}
418
419gfx::AcceleratedWidget NativeViewGLSurfaceGLX::GetDrawableHandle() const {
420 return window_;
421}
422
423bool NativeViewGLSurfaceGLX::Initialize() {
424 XWindowAttributes attributes;
425 if (!XGetWindowAttributes(g_display, parent_window_, &attributes)) {
426 LOG(ERROR) << "XGetWindowAttributes failed for window " << parent_window_
427 << ".";
428 return false;
429 }
430 size_ = gfx::Size(attributes.width, attributes.height);
431 // Create a child window, with a CopyFromParent visual (to avoid inducing
432 // extra blits in the driver), that we can resize exactly in Resize(),
433 // correctly ordered with GL, so that we don't have invalid transient states.
434 // See https://crbug.com/326995.
435 window_ = XCreateWindow(g_display,
436 parent_window_,
437 0,
438 0,
439 size_.width(),
440 size_.height(),
441 0,
442 CopyFromParent,
443 InputOutput,
444 CopyFromParent,
445 0,
446 NULL);
447 XMapWindow(g_display, window_);
448
449 ui::PlatformEventSource* event_source =
450 ui::PlatformEventSource::GetInstance();
451 // Can be NULL in tests, when we don't care about Exposes.
452 if (event_source) {
453 XSelectInput(g_display, window_, ExposureMask);
454 ui::PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this);
455 }
456 XFlush(g_display);
457
458 gfx::AcceleratedWidget window_for_vsync = window_;
459
460 if (g_glx_oml_sync_control_supported)
461 vsync_provider_.reset(new OMLSyncControlVSyncProvider(window_for_vsync));
462 else if (g_glx_sgi_video_sync_supported)
463 vsync_provider_.reset(new SGIVideoSyncVSyncProvider(window_for_vsync));
464
465 return true;
466}
467
468void NativeViewGLSurfaceGLX::Destroy() {
469 if (window_) {
470 ui::PlatformEventSource* event_source =
471 ui::PlatformEventSource::GetInstance();
472 if (event_source)
473 event_source->RemovePlatformEventDispatcher(this);
474 XDestroyWindow(g_display, window_);
475 XFlush(g_display);
476 }
477}
478
479bool NativeViewGLSurfaceGLX::CanDispatchEvent(const ui::PlatformEvent& event) {
480 return event->type == Expose && event->xexpose.window == window_;
481}
482
483uint32_t NativeViewGLSurfaceGLX::DispatchEvent(const ui::PlatformEvent& event) {
484 XEvent forwarded_event = *event;
485 forwarded_event.xexpose.window = parent_window_;
486 XSendEvent(g_display, parent_window_, False, ExposureMask,
487 &forwarded_event);
488 XFlush(g_display);
489 return ui::POST_DISPATCH_STOP_PROPAGATION;
490}
491
492bool NativeViewGLSurfaceGLX::Resize(const gfx::Size& size) {
493 size_ = size;
494 glXWaitGL();
495 XResizeWindow(g_display, window_, size.width(), size.height());
496 glXWaitX();
497 return true;
498}
499
500bool NativeViewGLSurfaceGLX::IsOffscreen() {
501 return false;
502}
503
Craig Stoutd6387092015-08-18 16:46:50 -0700504gfx::SwapResult NativeViewGLSurfaceGLX::SwapBuffers() {
James Robinson646469d2014-10-03 15:33:28 -0700505 TRACE_EVENT2("gpu", "NativeViewGLSurfaceGLX:RealSwapBuffers",
506 "width", GetSize().width(),
507 "height", GetSize().height());
508
509 glXSwapBuffers(g_display, GetDrawableHandle());
Craig Stoutd6387092015-08-18 16:46:50 -0700510 return gfx::SwapResult::SWAP_ACK;
James Robinson646469d2014-10-03 15:33:28 -0700511}
512
513gfx::Size NativeViewGLSurfaceGLX::GetSize() {
514 return size_;
515}
516
517void* NativeViewGLSurfaceGLX::GetHandle() {
518 return reinterpret_cast<void*>(GetDrawableHandle());
519}
520
521bool NativeViewGLSurfaceGLX::SupportsPostSubBuffer() {
522 return gfx::g_driver_glx.ext.b_GLX_MESA_copy_sub_buffer;
523}
524
525void* NativeViewGLSurfaceGLX::GetConfig() {
526 if (!config_) {
527 // This code path is expensive, but we only take it when
528 // attempting to use GLX_ARB_create_context_robustness, in which
529 // case we need a GLXFBConfig for the window in order to create a
530 // context for it.
531 //
532 // TODO(kbr): this is not a reliable code path. On platforms which
533 // support it, we should use glXChooseFBConfig in the browser
534 // process to choose the FBConfig and from there the X Visual to
535 // use when creating the window in the first place. Then we can
536 // pass that FBConfig down rather than attempting to reconstitute
537 // it.
Ian Fischerf66f4372015-06-09 16:50:28 -0700538 //
539 // TODO(iansf): Perhaps instead of kbr's suggestion above, we can
540 // now use GLSurface::GetSurfaceConfiguration to use the returned
541 // gfx::SurfaceConfiguration with glXChooseFBConfig in a manner
542 // similar to that used in NativeViewGLSurfaceEGL::GetConfig.
James Robinson646469d2014-10-03 15:33:28 -0700543
544 XWindowAttributes attributes;
545 if (!XGetWindowAttributes(
546 g_display,
547 window_,
548 &attributes)) {
549 LOG(ERROR) << "XGetWindowAttributes failed for window " <<
550 window_ << ".";
551 return NULL;
552 }
553
554 int visual_id = XVisualIDFromVisual(attributes.visual);
555
556 int num_elements = 0;
Nick Bray0bcbd3b2015-03-12 16:29:36 -0700557 gfx::XScopedPtr<GLXFBConfig> configs(
558 glXGetFBConfigs(g_display, DefaultScreen(g_display), &num_elements));
James Robinson646469d2014-10-03 15:33:28 -0700559 if (!configs.get()) {
560 LOG(ERROR) << "glXGetFBConfigs failed.";
561 return NULL;
562 }
563 if (!num_elements) {
564 LOG(ERROR) << "glXGetFBConfigs returned 0 elements.";
565 return NULL;
566 }
567 bool found = false;
568 int i;
569 for (i = 0; i < num_elements; ++i) {
570 int value;
571 if (glXGetFBConfigAttrib(
572 g_display, configs.get()[i], GLX_VISUAL_ID, &value)) {
573 LOG(ERROR) << "glXGetFBConfigAttrib failed.";
574 return NULL;
575 }
576 if (value == visual_id) {
577 found = true;
578 break;
579 }
580 }
581 if (found) {
582 config_ = configs.get()[i];
583 }
584 }
585
586 return config_;
587}
588
Craig Stoutd6387092015-08-18 16:46:50 -0700589gfx::SwapResult NativeViewGLSurfaceGLX::PostSubBuffer(
James Robinson646469d2014-10-03 15:33:28 -0700590 int x, int y, int width, int height) {
591 DCHECK(gfx::g_driver_glx.ext.b_GLX_MESA_copy_sub_buffer);
592 glXCopySubBufferMESA(g_display, GetDrawableHandle(), x, y, width, height);
Craig Stoutd6387092015-08-18 16:46:50 -0700593 return gfx::SwapResult::SWAP_ACK;
James Robinson646469d2014-10-03 15:33:28 -0700594}
595
596VSyncProvider* NativeViewGLSurfaceGLX::GetVSyncProvider() {
597 return vsync_provider_.get();
598}
599
600NativeViewGLSurfaceGLX::~NativeViewGLSurfaceGLX() {
601 Destroy();
602}
603
Ian Fischerf66f4372015-06-09 16:50:28 -0700604PbufferGLSurfaceGLX::PbufferGLSurfaceGLX(
605 const gfx::Size& size,
606 const gfx::SurfaceConfiguration& requested_configuration)
607 : GLSurfaceGLX(requested_configuration),
608 size_(size),
James Robinson646469d2014-10-03 15:33:28 -0700609 config_(NULL),
610 pbuffer_(0) {
611 // Some implementations of Pbuffer do not support having a 0 size. For such
612 // cases use a (1, 1) surface.
613 if (size_.GetArea() == 0)
614 size_.SetSize(1, 1);
615}
616
617bool PbufferGLSurfaceGLX::Initialize() {
618 DCHECK(!pbuffer_);
619
620 static const int config_attributes[] = {
621 GLX_BUFFER_SIZE, 32,
622 GLX_ALPHA_SIZE, 8,
623 GLX_BLUE_SIZE, 8,
624 GLX_GREEN_SIZE, 8,
625 GLX_RED_SIZE, 8,
626 GLX_RENDER_TYPE, GLX_RGBA_BIT,
627 GLX_DRAWABLE_TYPE, GLX_PBUFFER_BIT,
628 GLX_DOUBLEBUFFER, False,
629 0
630 };
631
632 int num_elements = 0;
Nick Bray0bcbd3b2015-03-12 16:29:36 -0700633 gfx::XScopedPtr<GLXFBConfig> configs(glXChooseFBConfig(
634 g_display, DefaultScreen(g_display), config_attributes, &num_elements));
James Robinson646469d2014-10-03 15:33:28 -0700635 if (!configs.get()) {
636 LOG(ERROR) << "glXChooseFBConfig failed.";
637 return false;
638 }
639 if (!num_elements) {
640 LOG(ERROR) << "glXChooseFBConfig returned 0 elements.";
641 return false;
642 }
643
644 config_ = configs.get()[0];
645
646 const int pbuffer_attributes[] = {
647 GLX_PBUFFER_WIDTH, size_.width(),
648 GLX_PBUFFER_HEIGHT, size_.height(),
649 0
650 };
651 pbuffer_ = glXCreatePbuffer(g_display,
652 static_cast<GLXFBConfig>(config_),
653 pbuffer_attributes);
654 if (!pbuffer_) {
655 Destroy();
656 LOG(ERROR) << "glXCreatePbuffer failed.";
657 return false;
658 }
659
660 return true;
661}
662
663void PbufferGLSurfaceGLX::Destroy() {
664 if (pbuffer_) {
665 glXDestroyPbuffer(g_display, pbuffer_);
666 pbuffer_ = 0;
667 }
668
669 config_ = NULL;
670}
671
672bool PbufferGLSurfaceGLX::IsOffscreen() {
673 return true;
674}
675
Craig Stoutd6387092015-08-18 16:46:50 -0700676gfx::SwapResult PbufferGLSurfaceGLX::SwapBuffers() {
James Robinson646469d2014-10-03 15:33:28 -0700677 NOTREACHED() << "Attempted to call SwapBuffers on a pbuffer.";
Craig Stoutd6387092015-08-18 16:46:50 -0700678 return gfx::SwapResult::SWAP_FAILED;
James Robinson646469d2014-10-03 15:33:28 -0700679}
680
681gfx::Size PbufferGLSurfaceGLX::GetSize() {
682 return size_;
683}
684
685void* PbufferGLSurfaceGLX::GetHandle() {
686 return reinterpret_cast<void*>(pbuffer_);
687}
688
689void* PbufferGLSurfaceGLX::GetConfig() {
690 return config_;
691}
692
693PbufferGLSurfaceGLX::~PbufferGLSurfaceGLX() {
694 Destroy();
695}
696
697} // namespace gfx