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 #include "ui/gl/gl_surface_glx.h"
6
7 #include <utility>
8
9 #include "base/bind.h"
10 #include "base/command_line.h"
11 #include "base/lazy_instance.h"
12 #include "base/logging.h"
13 #include "base/macros.h"
14 #include "base/memory/weak_ptr.h"
15 #include "base/no_destructor.h"
16 #include "base/single_thread_task_runner.h"
17 #include "base/synchronization/atomic_flag.h"
18 #include "base/synchronization/lock.h"
19 #include "base/threading/thread.h"
20 #include "base/threading/thread_checker.h"
21 #include "base/threading/thread_task_runner_handle.h"
22 #include "base/time/time.h"
23 #include "base/trace_event/trace_event.h"
24 #include "build/build_config.h"
25 #include "ui/base/x/x11_display_util.h"
26 #include "ui/base/x/x11_util.h"
27 #include "ui/events/platform/platform_event_source.h"
28 #include "ui/gfx/native_widget_types.h"
29 #include "ui/gfx/x/xproto_util.h"
30 #include "ui/gl/gl_bindings.h"
31 #include "ui/gl/gl_context.h"
32 #include "ui/gl/gl_implementation.h"
33 #include "ui/gl/gl_surface_presentation_helper.h"
34 #include "ui/gl/gl_visual_picker_glx.h"
35 #include "ui/gl/glx_util.h"
36 #include "ui/gl/sync_control_vsync_provider.h"
37
38 namespace gl {
39
40 namespace {
41
42 bool g_glx_context_create = false;
43 bool g_glx_create_context_robustness_supported = false;
44 bool g_glx_robustness_video_memory_purge_supported = false;
45 bool g_glx_create_context_profile_supported = false;
46 bool g_glx_create_context_profile_es2_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 bool g_glx_ext_swap_control_supported = false;
55 bool g_glx_mesa_swap_control_supported = false;
56 bool g_glx_sgi_video_sync_supported = false;
57
58 // A 24-bit RGB visual and colormap to use when creating offscreen surfaces.
59 x11::VisualId g_visual{};
60 int g_depth = static_cast<int>(x11::WindowClass::CopyFromParent);
61 x11::ColorMap g_colormap{};
62
CreateDummyWindow(x11::Connection * conn)63 bool CreateDummyWindow(x11::Connection* conn) {
64 DCHECK(conn);
65 auto parent_window = conn->default_root();
66 auto window = conn->GenerateId<x11::Window>();
67 auto create_window = conn->CreateWindow(x11::CreateWindowRequest{
68 .wid = window,
69 .parent = parent_window,
70 .width = 1,
71 .height = 1,
72 .c_class = x11::WindowClass::InputOutput,
73 });
74 if (create_window.Sync().error) {
75 LOG(ERROR) << "Failed to create window";
76 return false;
77 }
78 GLXFBConfig config = GetFbConfigForWindow(conn, window);
79 if (!config) {
80 LOG(ERROR) << "Failed to get GLXConfig";
81 conn->DestroyWindow({window});
82 return false;
83 }
84
85 GLXWindow glx_window = glXCreateWindow(
86 conn->GetXlibDisplay(), config, static_cast<uint32_t>(window), nullptr);
87 if (!glx_window) {
88 LOG(ERROR) << "glXCreateWindow failed";
89 conn->DestroyWindow({window});
90 return false;
91 }
92 glXDestroyWindow(conn->GetXlibDisplay(x11::XlibDisplayType::kFlushing),
93 glx_window);
94 conn->DestroyWindow({window});
95 return true;
96 }
97
98 class OMLSyncControlVSyncProvider : public SyncControlVSyncProvider {
99 public:
OMLSyncControlVSyncProvider(GLXWindow glx_window)100 explicit OMLSyncControlVSyncProvider(GLXWindow glx_window)
101 : SyncControlVSyncProvider(), glx_window_(glx_window) {}
102
103 ~OMLSyncControlVSyncProvider() override = default;
104
105 protected:
GetSyncValues(int64_t * system_time,int64_t * media_stream_counter,int64_t * swap_buffer_counter)106 bool GetSyncValues(int64_t* system_time,
107 int64_t* media_stream_counter,
108 int64_t* swap_buffer_counter) override {
109 return glXGetSyncValuesOML(x11::Connection::Get()->GetXlibDisplay(),
110 glx_window_, system_time, media_stream_counter,
111 swap_buffer_counter);
112 }
113
GetMscRate(int32_t * numerator,int32_t * denominator)114 bool GetMscRate(int32_t* numerator, int32_t* denominator) override {
115 if (!g_glx_get_msc_rate_oml_supported)
116 return false;
117
118 if (!glXGetMscRateOML(x11::Connection::Get()->GetXlibDisplay(), glx_window_,
119 numerator, denominator)) {
120 // Once glXGetMscRateOML has been found to fail, don't try again,
121 // since each failing call may spew an error message.
122 g_glx_get_msc_rate_oml_supported = false;
123 return false;
124 }
125
126 return true;
127 }
128
IsHWClock() const129 bool IsHWClock() const override { return true; }
130
131 private:
132 GLXWindow glx_window_;
133
134 DISALLOW_COPY_AND_ASSIGN(OMLSyncControlVSyncProvider);
135 };
136
137 class SGIVideoSyncThread : public base::Thread,
138 public base::RefCounted<SGIVideoSyncThread> {
139 public:
140 // Create a connection to the X server for use on g_video_sync_thread before
141 // the sandbox starts.
InitializeBeforeSandboxStarts()142 static bool InitializeBeforeSandboxStarts() {
143 auto* connection = GetConnectionImpl();
144 if (!connection || !connection->Ready())
145 return false;
146
147 if (!CreateDummyWindow(connection)) {
148 LOG(ERROR) << "CreateDummyWindow(display) failed";
149 return false;
150 }
151 connection->DetachFromSequence();
152 return true;
153 }
154
Create()155 static scoped_refptr<SGIVideoSyncThread> Create() {
156 if (!g_video_sync_thread) {
157 g_video_sync_thread = new SGIVideoSyncThread();
158 g_video_sync_thread->Start();
159 }
160 return g_video_sync_thread;
161 }
162
GetConnection()163 x11::Connection* GetConnection() {
164 DCHECK(task_runner()->BelongsToCurrentThread());
165 return GetConnectionImpl();
166 }
167
MaybeCreateGLXContext(GLXFBConfig config)168 void MaybeCreateGLXContext(GLXFBConfig config) {
169 DCHECK(task_runner()->BelongsToCurrentThread());
170 if (!context_) {
171 context_ = glXCreateNewContext(
172 GetConnection()->GetXlibDisplay(x11::XlibDisplayType::kSyncing),
173 config, GLX_RGBA_TYPE, nullptr, true);
174 }
175 LOG_IF(ERROR, !context_) << "video_sync: glXCreateNewContext failed";
176 }
177
178 // Destroy |context_| on the thread where it is used.
CleanUp()179 void CleanUp() override {
180 DCHECK(task_runner()->BelongsToCurrentThread());
181 if (context_)
182 glXDestroyContext(
183 GetConnection()->GetXlibDisplay(x11::XlibDisplayType::kFlushing),
184 context_);
185 // Release the connection from this thread's sequence so that a new
186 // SGIVideoSyncThread can reuse the connection. The connection must be
187 // reused since it can only be created before sandbox initialization.
188 GetConnection()->DetachFromSequence();
189 }
190
GetGLXContext()191 GLXContext GetGLXContext() {
192 DCHECK(task_runner()->BelongsToCurrentThread());
193 return context_;
194 }
195
196 private:
197 friend class base::RefCounted<SGIVideoSyncThread>;
198
SGIVideoSyncThread()199 SGIVideoSyncThread() : base::Thread("SGI_video_sync") {
200 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
201 }
202
~SGIVideoSyncThread()203 ~SGIVideoSyncThread() override {
204 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
205 g_video_sync_thread = nullptr;
206 Stop();
207 }
208
GetConnectionImpl()209 static x11::Connection* GetConnectionImpl() {
210 if (!g_connection)
211 g_connection = x11::Connection::Get()->Clone().release();
212 return g_connection;
213 }
214
215 static SGIVideoSyncThread* g_video_sync_thread;
216 static x11::Connection* g_connection;
217 GLXContext context_ = nullptr;
218
219 THREAD_CHECKER(thread_checker_);
220
221 DISALLOW_COPY_AND_ASSIGN(SGIVideoSyncThread);
222 };
223
224 class SGIVideoSyncProviderThreadShim {
225 public:
SGIVideoSyncProviderThreadShim(gfx::AcceleratedWidget parent_window,SGIVideoSyncThread * vsync_thread)226 SGIVideoSyncProviderThreadShim(gfx::AcceleratedWidget parent_window,
227 SGIVideoSyncThread* vsync_thread)
228 : parent_window_(parent_window),
229 vsync_thread_(vsync_thread),
230 glx_window_(0),
231 task_runner_(base::ThreadTaskRunnerHandle::Get()),
232 cancel_vsync_flag_(),
233 vsync_lock_() {
234 // This ensures that creation of |parent_window_| has occured when this shim
235 // is executing in the same thread as the call to create |parent_window_|.
236 x11::Connection::Get()->Sync();
237 }
238
~SGIVideoSyncProviderThreadShim()239 ~SGIVideoSyncProviderThreadShim() {
240 auto* connection = vsync_thread_->GetConnection();
241 if (glx_window_) {
242 glXDestroyWindow(
243 connection->GetXlibDisplay(x11::XlibDisplayType::kFlushing),
244 glx_window_);
245 }
246
247 if (window_ != x11::Window::None)
248 connection->DestroyWindow({window_});
249 }
250
cancel_vsync_flag()251 base::AtomicFlag* cancel_vsync_flag() { return &cancel_vsync_flag_; }
252
vsync_lock()253 base::Lock* vsync_lock() { return &vsync_lock_; }
254
Initialize()255 void Initialize() {
256 auto* connection = vsync_thread_->GetConnection();
257 DCHECK(connection);
258
259 auto window = connection->GenerateId<x11::Window>();
260 auto req = connection->CreateWindow(x11::CreateWindowRequest{
261 .wid = window,
262 .parent = static_cast<x11::Window>(parent_window_),
263 .width = 1,
264 .height = 1,
265 .c_class = x11::WindowClass::InputOutput,
266 });
267 if (req.Sync().error) {
268 LOG(ERROR) << "video_sync: XCreateWindow failed";
269 return;
270 }
271 window_ = window;
272
273 GLXFBConfig config = GetFbConfigForWindow(connection, window_);
274 if (!config) {
275 LOG(ERROR) << "video_sync: Failed to get GLXConfig";
276 return;
277 }
278
279 glx_window_ = glXCreateWindow(
280 connection->GetXlibDisplay(x11::XlibDisplayType::kSyncing), config,
281 static_cast<uint32_t>(window_), nullptr);
282 if (!glx_window_) {
283 LOG(ERROR) << "video_sync: glXCreateWindow failed";
284 return;
285 }
286
287 vsync_thread_->MaybeCreateGLXContext(config);
288 }
289
GetVSyncParameters(gfx::VSyncProvider::UpdateVSyncCallback callback)290 void GetVSyncParameters(gfx::VSyncProvider::UpdateVSyncCallback callback) {
291 // Don't allow |window_| destruction while we're probing vsync.
292 base::AutoLock locked(vsync_lock_);
293
294 if (!vsync_thread_->GetGLXContext() || cancel_vsync_flag_.IsSet())
295 return;
296
297 base::TimeDelta interval = ui::GetPrimaryDisplayRefreshIntervalFromXrandr();
298
299 auto* connection = vsync_thread_->GetConnection();
300 glXMakeContextCurrent(
301 connection->GetXlibDisplay(x11::XlibDisplayType::kFlushing),
302 glx_window_, glx_window_, vsync_thread_->GetGLXContext());
303
304 unsigned int retrace_count = 0;
305 if (glXWaitVideoSyncSGI(1, 0, &retrace_count) != 0)
306 return;
307
308 base::TimeTicks now = base::TimeTicks::Now();
309 TRACE_EVENT_INSTANT0("gpu", "vblank", TRACE_EVENT_SCOPE_THREAD);
310
311 glXMakeContextCurrent(
312 connection->GetXlibDisplay(x11::XlibDisplayType::kFlushing), 0, 0,
313 nullptr);
314
315 task_runner_->PostTask(FROM_HERE,
316 base::BindOnce(std::move(callback), now, interval));
317 }
318
319 private:
320 gfx::AcceleratedWidget parent_window_;
321 SGIVideoSyncThread* vsync_thread_;
322 x11::Window window_ = x11::Window::None;
323 GLXWindow glx_window_;
324
325 scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
326
327 base::AtomicFlag cancel_vsync_flag_;
328 base::Lock vsync_lock_;
329
330 DISALLOW_COPY_AND_ASSIGN(SGIVideoSyncProviderThreadShim);
331 };
332
333 class SGIVideoSyncVSyncProvider
334 : public gfx::VSyncProvider,
335 public base::SupportsWeakPtr<SGIVideoSyncVSyncProvider> {
336 public:
SGIVideoSyncVSyncProvider(gfx::AcceleratedWidget parent_window)337 explicit SGIVideoSyncVSyncProvider(gfx::AcceleratedWidget parent_window)
338 : vsync_thread_(SGIVideoSyncThread::Create()),
339 shim_(new SGIVideoSyncProviderThreadShim(parent_window,
340 vsync_thread_.get())),
341 cancel_vsync_flag_(shim_->cancel_vsync_flag()),
342 vsync_lock_(shim_->vsync_lock()) {
343 vsync_thread_->task_runner()->PostTask(
344 FROM_HERE, base::BindOnce(&SGIVideoSyncProviderThreadShim::Initialize,
345 base::Unretained(shim_.get())));
346 }
347
~SGIVideoSyncVSyncProvider()348 ~SGIVideoSyncVSyncProvider() override {
349 {
350 base::AutoLock locked(*vsync_lock_);
351 cancel_vsync_flag_->Set();
352 }
353
354 // Hand-off |shim_| to be deleted on the |vsync_thread_|.
355 vsync_thread_->task_runner()->DeleteSoon(FROM_HERE, shim_.release());
356 }
357
GetVSyncParameters(gfx::VSyncProvider::UpdateVSyncCallback callback)358 void GetVSyncParameters(
359 gfx::VSyncProvider::UpdateVSyncCallback callback) override {
360 // Only one outstanding request per surface.
361 if (!pending_callback_) {
362 DCHECK(callback);
363 pending_callback_ = std::move(callback);
364 vsync_thread_->task_runner()->PostTask(
365 FROM_HERE,
366 base::BindOnce(&SGIVideoSyncProviderThreadShim::GetVSyncParameters,
367 base::Unretained(shim_.get()),
368 base::BindRepeating(
369 &SGIVideoSyncVSyncProvider::PendingCallbackRunner,
370 AsWeakPtr())));
371 }
372 }
373
GetVSyncParametersIfAvailable(base::TimeTicks * timebase,base::TimeDelta * interval)374 bool GetVSyncParametersIfAvailable(base::TimeTicks* timebase,
375 base::TimeDelta* interval) override {
376 return false;
377 }
378
SupportGetVSyncParametersIfAvailable() const379 bool SupportGetVSyncParametersIfAvailable() const override { return false; }
IsHWClock() const380 bool IsHWClock() const override { return false; }
381
382 private:
PendingCallbackRunner(const base::TimeTicks timebase,const base::TimeDelta interval)383 void PendingCallbackRunner(const base::TimeTicks timebase,
384 const base::TimeDelta interval) {
385 DCHECK(pending_callback_);
386 std::move(pending_callback_).Run(timebase, interval);
387 }
388
389 scoped_refptr<SGIVideoSyncThread> vsync_thread_;
390
391 // Thread shim through which the sync provider is accessed on |vsync_thread_|.
392 std::unique_ptr<SGIVideoSyncProviderThreadShim> shim_;
393
394 gfx::VSyncProvider::UpdateVSyncCallback pending_callback_;
395
396 // Raw pointers to sync primitives owned by the shim_.
397 // These will only be referenced before we post a task to destroy
398 // the shim_, so they are safe to access.
399 base::AtomicFlag* cancel_vsync_flag_;
400 base::Lock* vsync_lock_;
401
402 DISALLOW_COPY_AND_ASSIGN(SGIVideoSyncVSyncProvider);
403 };
404
405 SGIVideoSyncThread* SGIVideoSyncThread::g_video_sync_thread = nullptr;
406 x11::Connection* SGIVideoSyncThread::g_connection = nullptr;
407
408 } // namespace
409
410 bool GLSurfaceGLX::initialized_ = false;
411
412 GLSurfaceGLX::GLSurfaceGLX() = default;
413
InitializeOneOff()414 bool GLSurfaceGLX::InitializeOneOff() {
415 if (initialized_)
416 return true;
417
418 // http://crbug.com/245466
419 setenv("force_s3tc_enable", "true", 1);
420
421 if (!x11::Connection::Get()->Ready()) {
422 LOG(ERROR) << "Could not open X11 connection.";
423 return false;
424 }
425
426 int major = 0, minor = 0;
427 if (!glXQueryVersion(x11::Connection::Get()->GetXlibDisplay(), &major,
428 &minor)) {
429 LOG(ERROR) << "glxQueryVersion failed";
430 return false;
431 }
432
433 if (major == 1 && minor < 3) {
434 LOG(ERROR) << "GLX 1.3 or later is required.";
435 return false;
436 }
437
438 auto* visual_picker = gl::GLVisualPickerGLX::GetInstance();
439 auto visual_id = visual_picker->rgba_visual();
440 if (visual_id == x11::VisualId{})
441 visual_id = visual_picker->system_visual();
442 g_visual = visual_id;
443 auto* connection = x11::Connection::Get();
444 g_depth = connection->GetVisualInfoFromId(visual_id)->format->depth;
445 g_colormap = connection->GenerateId<x11::ColorMap>();
446 connection->CreateColormap({x11::ColormapAlloc::None, g_colormap,
447 connection->default_root(), g_visual});
448 // We create a dummy unmapped window for both the main Display and the video
449 // sync Display so that the Nvidia driver can initialize itself before the
450 // sandbox is set up.
451 // Unfortunately some fds e.g. /dev/nvidia0 are cached per thread and because
452 // we can't start threads before the sandbox is set up, these are accessed
453 // through the broker process. See GpuProcessPolicy::InitGpuBrokerProcess.
454 if (!CreateDummyWindow(x11::Connection::Get())) {
455 LOG(ERROR) << "CreateDummyWindow() failed";
456 return false;
457 }
458
459 initialized_ = true;
460 return true;
461 }
462
463 // static
InitializeExtensionSettingsOneOff()464 bool GLSurfaceGLX::InitializeExtensionSettingsOneOff() {
465 if (!initialized_)
466 return false;
467
468 g_driver_glx.InitializeExtensionBindings();
469
470 g_glx_context_create = HasGLXExtension("GLX_ARB_create_context");
471 g_glx_create_context_robustness_supported =
472 HasGLXExtension("GLX_ARB_create_context_robustness");
473 g_glx_robustness_video_memory_purge_supported =
474 HasGLXExtension("GLX_NV_robustness_video_memory_purge");
475 g_glx_create_context_profile_supported =
476 HasGLXExtension("GLX_ARB_create_context_profile");
477 g_glx_create_context_profile_es2_supported =
478 HasGLXExtension("GLX_ARB_create_context_es2_profile");
479 g_glx_texture_from_pixmap_supported =
480 HasGLXExtension("GLX_EXT_texture_from_pixmap");
481 g_glx_oml_sync_control_supported = HasGLXExtension("GLX_OML_sync_control");
482 g_glx_get_msc_rate_oml_supported = g_glx_oml_sync_control_supported;
483 g_glx_ext_swap_control_supported = HasGLXExtension("GLX_EXT_swap_control");
484 g_glx_mesa_swap_control_supported = HasGLXExtension("GLX_MESA_swap_control");
485 g_glx_sgi_video_sync_supported = HasGLXExtension("GLX_SGI_video_sync");
486
487 if (!g_glx_get_msc_rate_oml_supported && g_glx_sgi_video_sync_supported) {
488 if (!SGIVideoSyncThread::InitializeBeforeSandboxStarts())
489 return false;
490 }
491 return true;
492 }
493
494 // static
ShutdownOneOff()495 void GLSurfaceGLX::ShutdownOneOff() {
496 initialized_ = false;
497 g_glx_context_create = false;
498 g_glx_create_context_robustness_supported = false;
499 g_glx_robustness_video_memory_purge_supported = false;
500 g_glx_create_context_profile_supported = false;
501 g_glx_create_context_profile_es2_supported = false;
502 g_glx_texture_from_pixmap_supported = false;
503 g_glx_oml_sync_control_supported = false;
504
505 g_glx_get_msc_rate_oml_supported = false;
506 g_glx_ext_swap_control_supported = false;
507 g_glx_mesa_swap_control_supported = false;
508 g_glx_sgi_video_sync_supported = false;
509
510 g_visual = {};
511 g_depth = static_cast<int>(x11::WindowClass::CopyFromParent);
512 g_colormap = {};
513 }
514
515 // static
QueryGLXExtensions()516 std::string GLSurfaceGLX::QueryGLXExtensions() {
517 auto* connection = x11::Connection::Get();
518 const int screen = connection ? connection->DefaultScreenId() : 0;
519 const char* extensions =
520 glXQueryExtensionsString(connection->GetXlibDisplay(), screen);
521 if (extensions)
522 return std::string(extensions);
523 return "";
524 }
525
526 // static
GetGLXExtensions()527 const char* GLSurfaceGLX::GetGLXExtensions() {
528 static base::NoDestructor<std::string> glx_extensions("");
529 if (glx_extensions->empty()) {
530 *glx_extensions = QueryGLXExtensions();
531 }
532 return glx_extensions->c_str();
533 }
534
535 // static
HasGLXExtension(const char * name)536 bool GLSurfaceGLX::HasGLXExtension(const char* name) {
537 return ExtensionsContain(GetGLXExtensions(), name);
538 }
539
540 // static
IsCreateContextSupported()541 bool GLSurfaceGLX::IsCreateContextSupported() {
542 return g_glx_context_create;
543 }
544
545 // static
IsCreateContextRobustnessSupported()546 bool GLSurfaceGLX::IsCreateContextRobustnessSupported() {
547 return g_glx_create_context_robustness_supported;
548 }
549
550 // static
IsRobustnessVideoMemoryPurgeSupported()551 bool GLSurfaceGLX::IsRobustnessVideoMemoryPurgeSupported() {
552 return g_glx_robustness_video_memory_purge_supported;
553 }
554
555 // static
IsCreateContextProfileSupported()556 bool GLSurfaceGLX::IsCreateContextProfileSupported() {
557 return g_glx_create_context_profile_supported;
558 }
559
560 // static
IsCreateContextES2ProfileSupported()561 bool GLSurfaceGLX::IsCreateContextES2ProfileSupported() {
562 return g_glx_create_context_profile_es2_supported;
563 }
564
565 // static
IsTextureFromPixmapSupported()566 bool GLSurfaceGLX::IsTextureFromPixmapSupported() {
567 return g_glx_texture_from_pixmap_supported;
568 }
569
570 // static
IsEXTSwapControlSupported()571 bool GLSurfaceGLX::IsEXTSwapControlSupported() {
572 return g_glx_ext_swap_control_supported;
573 }
574
575 // static
IsMESASwapControlSupported()576 bool GLSurfaceGLX::IsMESASwapControlSupported() {
577 return g_glx_mesa_swap_control_supported;
578 }
579
580 // static
IsOMLSyncControlSupported()581 bool GLSurfaceGLX::IsOMLSyncControlSupported() {
582 return g_glx_oml_sync_control_supported;
583 }
584
GetDisplay()585 void* GLSurfaceGLX::GetDisplay() {
586 return x11::Connection::Get()->GetXlibDisplay();
587 }
588
589 GLSurfaceGLX::~GLSurfaceGLX() = default;
590
NativeViewGLSurfaceGLX(gfx::AcceleratedWidget window)591 NativeViewGLSurfaceGLX::NativeViewGLSurfaceGLX(gfx::AcceleratedWidget window)
592 : parent_window_(window),
593 window_(x11::Window::None),
594 glx_window_(),
595 config_(nullptr),
596 has_swapped_buffers_(false) {}
597
Initialize(GLSurfaceFormat format)598 bool NativeViewGLSurfaceGLX::Initialize(GLSurfaceFormat format) {
599 auto* conn = x11::Connection::Get();
600
601 auto parent = static_cast<x11::Window>(parent_window_);
602 auto attributes_req = conn->GetWindowAttributes({parent});
603 auto geometry_req = conn->GetGeometry({parent});
604 conn->Flush();
605 auto attributes = attributes_req.Sync();
606 auto geometry = geometry_req.Sync();
607
608 if (!attributes || !geometry) {
609 LOG(ERROR) << "GetGeometry/GetWindowAttribues failed for window "
610 << static_cast<uint32_t>(parent_window_) << ".";
611 return false;
612 }
613 size_ = gfx::Size(geometry->width, geometry->height);
614
615 window_ = conn->GenerateId<x11::Window>();
616 x11::CreateWindowRequest req{
617 .depth = g_depth,
618 .wid = window_,
619 .parent = static_cast<x11::Window>(parent_window_),
620 .width = size_.width(),
621 .height = size_.height(),
622 .c_class = x11::WindowClass::InputOutput,
623 .visual = g_visual,
624 .background_pixmap = x11::Pixmap::None,
625 .border_pixel = 0,
626 .bit_gravity = x11::Gravity::NorthWest,
627 .colormap = g_colormap,
628 };
629 if (ui::IsCompositingManagerPresent() && attributes->visual == g_visual) {
630 // When parent and child are using the same visual, the back buffer will be
631 // shared between parent and child. If WM compositing is enabled, we set
632 // child's background pixel to ARGB(0,0,0,0), so ARGB(0,0,0,0) will be
633 // filled to the shared buffer, when the child window is mapped. It can
634 // avoid an annoying flash when the child window is mapped below.
635 // If WM compositing is disabled, we don't set the background pixel, so
636 // nothing will be draw when the child window is mapped.
637 req.background_pixel = 0; // ARGB(0,0,0,0) for compositing WM
638 }
639 conn->CreateWindow(req);
640 conn->MapWindow({window_});
641
642 RegisterEvents();
643 conn->Sync();
644
645 GetConfig();
646 if (!config_) {
647 LOG(ERROR) << "Failed to get GLXConfig";
648 return false;
649 }
650 glx_window_ = static_cast<x11::Glx::Window>(
651 glXCreateWindow(conn->GetXlibDisplay(x11::XlibDisplayType::kSyncing),
652 config_, static_cast<uint32_t>(window_), nullptr));
653 if (!GetDrawableHandle()) {
654 LOG(ERROR) << "glXCreateWindow failed";
655 return false;
656 }
657
658 if (g_glx_oml_sync_control_supported) {
659 vsync_provider_ = std::make_unique<OMLSyncControlVSyncProvider>(
660 static_cast<GLXWindow>(glx_window_));
661 presentation_helper_ =
662 std::make_unique<GLSurfacePresentationHelper>(vsync_provider_.get());
663 } else if (g_glx_sgi_video_sync_supported) {
664 vsync_provider_ =
665 std::make_unique<SGIVideoSyncVSyncProvider>(parent_window_);
666 presentation_helper_ =
667 std::make_unique<GLSurfacePresentationHelper>(vsync_provider_.get());
668 } else {
669 // Assume a refresh rate of 59.9 Hz, which will cause us to skip
670 // 1 frame every 10 seconds on a 60Hz monitor, but will prevent us
671 // from blocking the GPU service due to back pressure. This would still
672 // encounter backpressure on a <60Hz monitor, but hopefully that is
673 // not common.
674 const base::TimeTicks kDefaultTimebase;
675 const base::TimeDelta kDefaultInterval =
676 base::TimeDelta::FromSeconds(1) / 59.9;
677 vsync_provider_ = std::make_unique<gfx::FixedVSyncProvider>(
678 kDefaultTimebase, kDefaultInterval);
679 presentation_helper_ = std::make_unique<GLSurfacePresentationHelper>(
680 kDefaultTimebase, kDefaultInterval);
681 }
682
683 return true;
684 }
685
Destroy()686 void NativeViewGLSurfaceGLX::Destroy() {
687 presentation_helper_ = nullptr;
688 vsync_provider_ = nullptr;
689 if (GetDrawableHandle()) {
690 glXDestroyWindow(
691 x11::Connection::Get()->GetXlibDisplay(x11::XlibDisplayType::kFlushing),
692 GetDrawableHandle());
693 glx_window_ = {};
694 }
695 if (window_ != x11::Window::None) {
696 UnregisterEvents();
697 x11::Connection::Get()->DestroyWindow({window_});
698 window_ = x11::Window::None;
699 x11::Connection::Get()->Flush();
700 }
701 }
702
Resize(const gfx::Size & size,float scale_factor,const gfx::ColorSpace & color_space,bool has_alpha)703 bool NativeViewGLSurfaceGLX::Resize(const gfx::Size& size,
704 float scale_factor,
705 const gfx::ColorSpace& color_space,
706 bool has_alpha) {
707 size_ = size;
708 glXWaitGL();
709 x11::Connection::Get()->ConfigureWindow(
710 {.window = window_, .width = size.width(), .height = size.height()});
711 glXWaitX();
712 return true;
713 }
714
IsOffscreen()715 bool NativeViewGLSurfaceGLX::IsOffscreen() {
716 return false;
717 }
718
SwapBuffers(PresentationCallback callback)719 gfx::SwapResult NativeViewGLSurfaceGLX::SwapBuffers(
720 PresentationCallback callback) {
721 TRACE_EVENT2("gpu", "NativeViewGLSurfaceGLX:RealSwapBuffers", "width",
722 GetSize().width(), "height", GetSize().height());
723 GLSurfacePresentationHelper::ScopedSwapBuffers scoped_swap_buffers(
724 presentation_helper_.get(), std::move(callback));
725
726 auto* connection = x11::Connection::Get();
727 glXSwapBuffers(connection->GetXlibDisplay(x11::XlibDisplayType::kFlushing),
728 GetDrawableHandle());
729
730 // We need to restore the background pixel that we set to WhitePixel on
731 // views::DesktopWindowTreeHostX11::InitX11Window back to None for the
732 // XWindow associated to this surface after the first SwapBuffers has
733 // happened, to avoid showing a weird white background while resizing.
734 if (!has_swapped_buffers_) {
735 connection->ChangeWindowAttributes({
736 .window = static_cast<x11::Window>(parent_window_),
737 .background_pixmap = x11::Pixmap::None,
738 });
739 has_swapped_buffers_ = true;
740 }
741
742 return scoped_swap_buffers.result();
743 }
744
GetSize()745 gfx::Size NativeViewGLSurfaceGLX::GetSize() {
746 return size_;
747 }
748
GetHandle()749 void* NativeViewGLSurfaceGLX::GetHandle() {
750 return reinterpret_cast<void*>(GetDrawableHandle());
751 }
752
SupportsPostSubBuffer()753 bool NativeViewGLSurfaceGLX::SupportsPostSubBuffer() {
754 return g_driver_glx.ext.b_GLX_MESA_copy_sub_buffer;
755 }
756
GetConfig()757 void* NativeViewGLSurfaceGLX::GetConfig() {
758 if (!config_)
759 config_ = GetFbConfigForWindow(x11::Connection::Get(), window_);
760 return config_;
761 }
762
GetFormat()763 GLSurfaceFormat NativeViewGLSurfaceGLX::GetFormat() {
764 return GLSurfaceFormat();
765 }
766
PostSubBuffer(int x,int y,int width,int height,PresentationCallback callback)767 gfx::SwapResult NativeViewGLSurfaceGLX::PostSubBuffer(
768 int x,
769 int y,
770 int width,
771 int height,
772 PresentationCallback callback) {
773 DCHECK(g_driver_glx.ext.b_GLX_MESA_copy_sub_buffer);
774
775 GLSurfacePresentationHelper::ScopedSwapBuffers scoped_swap_buffers(
776 presentation_helper_.get(), std::move(callback));
777 glXCopySubBufferMESA(
778 x11::Connection::Get()->GetXlibDisplay(x11::XlibDisplayType::kFlushing),
779 GetDrawableHandle(), x, y, width, height);
780 return scoped_swap_buffers.result();
781 }
782
OnMakeCurrent(GLContext * context)783 bool NativeViewGLSurfaceGLX::OnMakeCurrent(GLContext* context) {
784 presentation_helper_->OnMakeCurrent(context, this);
785 return GLSurfaceGLX::OnMakeCurrent(context);
786 }
787
GetVSyncProvider()788 gfx::VSyncProvider* NativeViewGLSurfaceGLX::GetVSyncProvider() {
789 return vsync_provider_.get();
790 }
791
SetVSyncEnabled(bool enabled)792 void NativeViewGLSurfaceGLX::SetVSyncEnabled(bool enabled) {
793 DCHECK(GLContext::GetCurrent() && GLContext::GetCurrent()->IsCurrent(this));
794 int interval = enabled ? 1 : 0;
795 if (GLSurfaceGLX::IsEXTSwapControlSupported()) {
796 glXSwapIntervalEXT(
797 x11::Connection::Get()->GetXlibDisplay(x11::XlibDisplayType::kFlushing),
798 GetDrawableHandle(), interval);
799 } else if (GLSurfaceGLX::IsMESASwapControlSupported()) {
800 glXSwapIntervalMESA(interval);
801 } else if (interval == 0) {
802 LOG(WARNING)
803 << "Could not disable vsync: driver does not support swap control";
804 }
805 }
806
~NativeViewGLSurfaceGLX()807 NativeViewGLSurfaceGLX::~NativeViewGLSurfaceGLX() {
808 Destroy();
809 }
810
ForwardExposeEvent(x11::Event * event)811 void NativeViewGLSurfaceGLX::ForwardExposeEvent(x11::Event* event) {
812 auto forwarded_event = *event->As<x11::ExposeEvent>();
813 auto window = static_cast<x11::Window>(parent_window_);
814 forwarded_event.window = window;
815 x11::SendEvent(forwarded_event, window, x11::EventMask::Exposure);
816 x11::Connection::Get()->Flush();
817 }
818
CanHandleEvent(x11::Event * x11_event)819 bool NativeViewGLSurfaceGLX::CanHandleEvent(x11::Event* x11_event) {
820 auto* expose = x11_event->As<x11::ExposeEvent>();
821 return expose && expose->window == static_cast<x11::Window>(window_);
822 }
823
GetDrawableHandle() const824 uint32_t NativeViewGLSurfaceGLX::GetDrawableHandle() const {
825 return static_cast<uint32_t>(glx_window_);
826 }
827
UnmappedNativeViewGLSurfaceGLX(const gfx::Size & size)828 UnmappedNativeViewGLSurfaceGLX::UnmappedNativeViewGLSurfaceGLX(
829 const gfx::Size& size)
830 : size_(size), config_(nullptr), window_(x11::Window::None), glx_window_() {
831 // Ensure that we don't create a window with zero size.
832 if (size_.GetArea() == 0)
833 size_.SetSize(1, 1);
834 }
835
Initialize(GLSurfaceFormat format)836 bool UnmappedNativeViewGLSurfaceGLX::Initialize(GLSurfaceFormat format) {
837 DCHECK_EQ(window_, x11::Window::None);
838
839 auto parent_window = ui::GetX11RootWindow();
840
841 auto* conn = x11::Connection::Get();
842 window_ = conn->GenerateId<x11::Window>();
843 conn->CreateWindow(x11::CreateWindowRequest{
844 .depth = g_depth,
845 .wid = window_,
846 .parent = parent_window,
847 .width = size_.width(),
848 .height = size_.height(),
849 .c_class = x11::WindowClass::InputOutput,
850 .visual = g_visual,
851 .border_pixel = 0,
852 .colormap = g_colormap,
853 })
854 .Sync();
855 GetConfig();
856 if (!config_) {
857 LOG(ERROR) << "Failed to get GLXConfig";
858 return false;
859 }
860 glx_window_ = static_cast<x11::Glx::Window>(
861 glXCreateWindow(conn->GetXlibDisplay(x11::XlibDisplayType::kSyncing),
862 config_, static_cast<uint32_t>(window_), nullptr));
863 if (glx_window_ == x11::Glx::Window{}) {
864 LOG(ERROR) << "glXCreateWindow failed";
865 return false;
866 }
867 return true;
868 }
869
Destroy()870 void UnmappedNativeViewGLSurfaceGLX::Destroy() {
871 config_ = nullptr;
872 if (glx_window_ != x11::Glx::Window{}) {
873 glXDestroyWindow(
874 x11::Connection::Get()->GetXlibDisplay(x11::XlibDisplayType::kFlushing),
875 static_cast<uint32_t>(glx_window_));
876 glx_window_ = {};
877 }
878 if (window_ != x11::Window::None) {
879 x11::Connection::Get()->DestroyWindow({window_});
880 window_ = x11::Window::None;
881 }
882 }
883
IsOffscreen()884 bool UnmappedNativeViewGLSurfaceGLX::IsOffscreen() {
885 return true;
886 }
887
SwapBuffers(PresentationCallback callback)888 gfx::SwapResult UnmappedNativeViewGLSurfaceGLX::SwapBuffers(
889 PresentationCallback callback) {
890 NOTREACHED() << "Attempted to call SwapBuffers on an unmapped window.";
891 return gfx::SwapResult::SWAP_FAILED;
892 }
893
GetSize()894 gfx::Size UnmappedNativeViewGLSurfaceGLX::GetSize() {
895 return size_;
896 }
897
GetHandle()898 void* UnmappedNativeViewGLSurfaceGLX::GetHandle() {
899 return reinterpret_cast<void*>(glx_window_);
900 }
901
GetConfig()902 void* UnmappedNativeViewGLSurfaceGLX::GetConfig() {
903 if (!config_)
904 config_ = GetFbConfigForWindow(x11::Connection::Get(), window_);
905 return config_;
906 }
907
GetFormat()908 GLSurfaceFormat UnmappedNativeViewGLSurfaceGLX::GetFormat() {
909 return GLSurfaceFormat();
910 }
911
~UnmappedNativeViewGLSurfaceGLX()912 UnmappedNativeViewGLSurfaceGLX::~UnmappedNativeViewGLSurfaceGLX() {
913 Destroy();
914 }
915
916 } // namespace gl
917