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