summaryrefslogtreecommitdiff
path: root/mozilla-1467128.patch
blob: d50b2e2cb25d6d352890e258e200d2490fe6dfb8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352

# HG changeset patch
# User Martin Stransky <stransky@redhat.com>
# Date 1530270941 -7200
# Node ID 338c0093263be6440a33b79a92801bd2b4658f79
# Parent  84a6d5a0b551f5da116aab702dd38bb725bc8a08
Bug 1467128 - [Wayland] Get VSync from Gtk/Wayland, r?lsalzman

VSync on Wayland is a bit tricky as we can get only "last VSync" event signal with
CLOCK_MONOTONIC timestamp or none (if application is hidden/minimized).

That means we should draw a next frame at "last Vsync + frame delay" time and also
approximate next VSync event when we don't get any.

MozReview-Commit-ID: FI3Z4nkmDNK

diff --git a/gfx/thebes/gfxPlatformGtk.cpp b/gfx/thebes/gfxPlatformGtk.cpp
--- a/gfx/thebes/gfxPlatformGtk.cpp
+++ b/gfx/thebes/gfxPlatformGtk.cpp
@@ -46,16 +46,20 @@
 #include "GLContextGLX.h"
 #include "GLXLibrary.h"
 
 /* Undefine the Status from Xlib since it will conflict with system headers on OSX */
 #if defined(__APPLE__) && defined(Status)
 #undef Status
 #endif
 
+#ifdef MOZ_WAYLAND
+#include <gdk/gdkwayland.h>
+#endif
+
 #endif /* MOZ_X11 */
 
 #include <fontconfig/fontconfig.h>
 
 #include "nsMathUtils.h"
 
 #define GDK_PIXMAP_SIZE_MAX 32767
 
@@ -89,16 +93,22 @@ gfxPlatformGtk::gfxPlatformGtk()
 #ifdef MOZ_X11
     if (gfxPlatform::IsHeadless() && GDK_IS_X11_DISPLAY(gdk_display_get_default())) {
       mCompositorDisplay = XOpenDisplay(nullptr);
       MOZ_ASSERT(mCompositorDisplay, "Failed to create compositor display!");
     } else {
       mCompositorDisplay = nullptr;
     }
 #endif // MOZ_X11
+#ifdef MOZ_WAYLAND
+    // Wayland compositors use g_get_monotonic_time() to get timestamps.
+    mWaylandLastVsyncTimestamp = (g_get_monotonic_time() / 1000);
+    // Set default display fps to 60
+    mWaylandFrameDelay = 1000/60;
+#endif
 }
 
 gfxPlatformGtk::~gfxPlatformGtk()
 {
 #ifdef MOZ_X11
     if (mCompositorDisplay) {
       XCloseDisplay(mCompositorDisplay);
     }
@@ -505,26 +515,26 @@ gfxPlatformGtk::CheckVariationFontSuppor
   // until at least 2.7.1.
   FT_Int major, minor, patch;
   FT_Library_Version(GetFTLibrary(), &major, &minor, &patch);
   return major * 1000000 + minor * 1000 + patch >= 2007001;
 }
 
 #ifdef MOZ_X11
 
-class GLXVsyncSource final : public VsyncSource
+class GtkVsyncSource final : public VsyncSource
 {
 public:
-  GLXVsyncSource()
+  GtkVsyncSource()
   {
     MOZ_ASSERT(NS_IsMainThread());
     mGlobalDisplay = new GLXDisplay();
   }
 
-  virtual ~GLXVsyncSource()
+  virtual ~GtkVsyncSource()
   {
     MOZ_ASSERT(NS_IsMainThread());
   }
 
   virtual Display& GetGlobalDisplay() override
   {
     return *mGlobalDisplay;
   }
@@ -536,39 +546,52 @@ public:
   public:
     GLXDisplay() : mGLContext(nullptr)
                  , mXDisplay(nullptr)
                  , mSetupLock("GLXVsyncSetupLock")
                  , mVsyncThread("GLXVsyncThread")
                  , mVsyncTask(nullptr)
                  , mVsyncEnabledLock("GLXVsyncEnabledLock")
                  , mVsyncEnabled(false)
+#ifdef MOZ_WAYLAND
+                 , mIsWaylandDisplay(false)
+#endif
     {
     }
 
     // Sets up the display's GL context on a worker thread.
     // Required as GLContexts may only be used by the creating thread.
     // Returns true if setup was a success.
     bool Setup()
     {
       MonitorAutoLock lock(mSetupLock);
       MOZ_ASSERT(NS_IsMainThread());
       if (!mVsyncThread.Start())
         return false;
 
       RefPtr<Runnable> vsyncSetup =
-        NewRunnableMethod("GLXVsyncSource::GLXDisplay::SetupGLContext",
+        NewRunnableMethod("GtkVsyncSource::GLXDisplay::SetupGLContext",
                           this,
                           &GLXDisplay::SetupGLContext);
       mVsyncThread.message_loop()->PostTask(vsyncSetup.forget());
       // Wait until the setup has completed.
       lock.Wait();
       return mGLContext != nullptr;
     }
 
+#ifdef MOZ_WAYLAND
+    bool SetupWayland()
+    {
+      MonitorAutoLock lock(mSetupLock);
+      MOZ_ASSERT(NS_IsMainThread());
+      mIsWaylandDisplay = true;
+      return mVsyncThread.Start();
+    }
+#endif
+
     // Called on the Vsync thread to setup the GL context.
     void SetupGLContext()
     {
         MonitorAutoLock lock(mSetupLock);
         MOZ_ASSERT(!NS_IsMainThread());
         MOZ_ASSERT(!mGLContext, "GLContext already setup!");
 
         // Create video sync timer on a separate Display to prevent locking the
@@ -613,29 +636,35 @@ public:
         }
 
         lock.NotifyAll();
     }
 
     virtual void EnableVsync() override
     {
       MOZ_ASSERT(NS_IsMainThread());
+#if !defined(MOZ_WAYLAND)
       MOZ_ASSERT(mGLContext, "GLContext not setup!");
+#endif
 
       MonitorAutoLock lock(mVsyncEnabledLock);
       if (mVsyncEnabled) {
         return;
       }
       mVsyncEnabled = true;
 
       // If the task has not nulled itself out, it hasn't yet realized
       // that vsync was disabled earlier, so continue its execution.
       if (!mVsyncTask) {
         mVsyncTask = NewRunnableMethod(
-          "GLXVsyncSource::GLXDisplay::RunVsync", this, &GLXDisplay::RunVsync);
+          "GtkVsyncSource::GLXDisplay::RunVsync", this,
+#if defined(MOZ_WAYLAND)
+          mIsWaylandDisplay ? &GLXDisplay::RunVsyncWayland :
+#endif
+          &GLXDisplay::RunVsync);
         RefPtr<Runnable> addrefedTask = mVsyncTask;
         mVsyncThread.message_loop()->PostTask(addrefedTask.forget());
       }
     }
 
     virtual void DisableVsync() override
     {
       MonitorAutoLock lock(mVsyncEnabledLock);
@@ -650,17 +679,17 @@ public:
 
     virtual void Shutdown() override
     {
       MOZ_ASSERT(NS_IsMainThread());
       DisableVsync();
 
       // Cleanup thread-specific resources before shutting down.
       RefPtr<Runnable> shutdownTask = NewRunnableMethod(
-        "GLXVsyncSource::GLXDisplay::Cleanup", this, &GLXDisplay::Cleanup);
+        "GtkVsyncSource::GLXDisplay::Cleanup", this, &GLXDisplay::Cleanup);
       mVsyncThread.message_loop()->PostTask(shutdownTask.forget());
 
       // Stop, waiting for the cleanup task to finish execution.
       mVsyncThread.Stop();
     }
 
   private:
     virtual ~GLXDisplay()
@@ -709,50 +738,96 @@ public:
           }
         }
 
         lastVsync = TimeStamp::Now();
         NotifyVsync(lastVsync);
       }
     }
 
+#ifdef MOZ_WAYLAND
+    /* VSync on Wayland is tricky as we can get only "last VSync" event signal.
+     * That means we should draw next frame at "last Vsync + frame delay" time.
+     */
+    void RunVsyncWayland()
+    {
+      MOZ_ASSERT(!NS_IsMainThread());
+
+      for (;;) {
+        {
+          MonitorAutoLock lock(mVsyncEnabledLock);
+          if (!mVsyncEnabled) {
+            mVsyncTask = nullptr;
+            return;
+          }
+        }
+
+        gint64 lastVsync = gfxPlatformGtk::GetPlatform()->GetWaylandLastVsync();
+        gint64 currTime = (g_get_monotonic_time() / 1000);
+
+        gint64 remaining = gfxPlatformGtk::GetPlatform()->GetWaylandFrameDelay() -
+          (currTime - lastVsync);
+        if (remaining > 0) {
+          PlatformThread::Sleep(remaining);
+        } else {
+          // Time from last HW Vsync is longer than our frame delay,
+          // use our approximation then.
+          gfxPlatformGtk::GetPlatform()->SetWaylandLastVsync(currTime);
+        }
+
+        NotifyVsync(TimeStamp::Now());
+      }
+    }
+#endif
+
     void Cleanup() {
       MOZ_ASSERT(!NS_IsMainThread());
 
       mGLContext = nullptr;
-      XCloseDisplay(mXDisplay);
+      if (mXDisplay)
+        XCloseDisplay(mXDisplay);
     }
 
     // Owned by the vsync thread.
     RefPtr<gl::GLContextGLX> mGLContext;
     _XDisplay* mXDisplay;
     Monitor mSetupLock;
     base::Thread mVsyncThread;
     RefPtr<Runnable> mVsyncTask;
     Monitor mVsyncEnabledLock;
     bool mVsyncEnabled;
+#ifdef MOZ_WAYLAND
+    bool mIsWaylandDisplay;
+#endif
   };
 private:
   // We need a refcounted VsyncSource::Display to use chromium IPC runnables.
   RefPtr<GLXDisplay> mGlobalDisplay;
 };
 
 already_AddRefed<gfx::VsyncSource>
 gfxPlatformGtk::CreateHardwareVsyncSource()
 {
+#ifdef MOZ_WAYLAND
+  if (GDK_IS_WAYLAND_DISPLAY(gdk_display_get_default())) {
+    RefPtr<VsyncSource> vsyncSource = new GtkVsyncSource();
+    VsyncSource::Display& display = vsyncSource->GetGlobalDisplay();
+    static_cast<GtkVsyncSource::GLXDisplay&>(display).SetupWayland();
+    return vsyncSource.forget();
+  }
+#endif
+
   // Only use GLX vsync when the OpenGL compositor is being used.
   // The extra cost of initializing a GLX context while blocking the main
   // thread is not worth it when using basic composition.
-  // Also don't use it on non-X11 displays.
   if (gfxConfig::IsEnabled(Feature::HW_COMPOSITING)) {
-    if (GDK_IS_X11_DISPLAY(gdk_display_get_default()) &&
-        gl::sGLXLibrary.SupportsVideoSync()) {
-      RefPtr<VsyncSource> vsyncSource = new GLXVsyncSource();
+    if (gl::sGLXLibrary.SupportsVideoSync()) {
+      RefPtr<VsyncSource> vsyncSource = new GtkVsyncSource();
       VsyncSource::Display& display = vsyncSource->GetGlobalDisplay();
-      if (!static_cast<GLXVsyncSource::GLXDisplay&>(display).Setup()) {
+      if (!static_cast<GtkVsyncSource::GLXDisplay&>(display).Setup()) {
         NS_WARNING("Failed to setup GLContext, falling back to software vsync.");
         return gfxPlatform::CreateHardwareVsyncSource();
       }
       return vsyncSource.forget();
     }
     NS_WARNING("SGI_video_sync unsupported. Falling back to software vsync.");
   }
   return gfxPlatform::CreateHardwareVsyncSource();
diff --git a/gfx/thebes/gfxPlatformGtk.h b/gfx/thebes/gfxPlatformGtk.h
--- a/gfx/thebes/gfxPlatformGtk.h
+++ b/gfx/thebes/gfxPlatformGtk.h
@@ -102,23 +102,42 @@ public:
 #endif
 
 #ifdef MOZ_X11
     Display* GetCompositorDisplay() {
       return mCompositorDisplay;
     }
 #endif // MOZ_X11
 
+#ifdef MOZ_WAYLAND
+    void SetWaylandLastVsync(uint32_t aVsyncTimestamp) {
+      mWaylandLastVsyncTimestamp = aVsyncTimestamp;
+    }
+    int64_t GetWaylandLastVsync() {
+      return mWaylandLastVsyncTimestamp;
+    }
+    void SetWaylandFrameDelay(int64_t aFrameDelay) {
+      mWaylandFrameDelay = aFrameDelay;
+    }
+    int64_t GetWaylandFrameDelay() {
+      return mWaylandFrameDelay;
+    }
+#endif
+
 protected:
     bool CheckVariationFontSupport() override;
 
     int8_t mMaxGenericSubstitutions;
 
 private:
     virtual void GetPlatformCMSOutputProfile(void *&mem,
                                              size_t &size) override;
 
 #ifdef MOZ_X11
     Display* mCompositorDisplay;
 #endif
+#ifdef MOZ_WAYLAND
+    int64_t  mWaylandLastVsyncTimestamp;
+    int64_t  mWaylandFrameDelay;
+#endif
 };
 
 #endif /* GFX_PLATFORM_GTK_H */
bgstack15