360 lines
11 KiB
Diff
360 lines
11 KiB
Diff
|
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 */
|
||
|
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/widget/gtk/WindowSurfaceWayland.cpp b/widget/gtk/WindowSurfaceWayland.cpp
|
||
|
--- a/widget/gtk/WindowSurfaceWayland.cpp
|
||
|
+++ b/widget/gtk/WindowSurfaceWayland.cpp
|
||
|
@@ -546,16 +546,18 @@ WindowBackBuffer::Lock()
|
||
|
mWaylandDisplay->GetSurfaceFormat());
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
frame_callback_handler(void *data, struct wl_callback *callback, uint32_t time)
|
||
|
{
|
||
|
auto surface = reinterpret_cast<WindowSurfaceWayland*>(data);
|
||
|
surface->FrameCallbackHandler();
|
||
|
+
|
||
|
+ gfxPlatformGtk::GetPlatform()->SetWaylandLastVsync(time);
|
||
|
}
|
||
|
|
||
|
static const struct wl_callback_listener frame_listener = {
|
||
|
frame_callback_handler
|
||
|
};
|
||
|
|
||
|
WindowSurfaceWayland::WindowSurfaceWayland(nsWindow *aWindow)
|
||
|
: mWindow(aWindow)
|
||
|
|