# HG changeset patch # User Martin Stransky # 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 +#endif + #endif /* MOZ_X11 */ #include #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 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 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 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 mGLContext; _XDisplay* mXDisplay; Monitor mSetupLock; base::Thread mVsyncThread; RefPtr mVsyncTask; Monitor mVsyncEnabledLock; bool mVsyncEnabled; +#ifdef MOZ_WAYLAND + bool mIsWaylandDisplay; +#endif }; private: // We need a refcounted VsyncSource::Display to use chromium IPC runnables. RefPtr mGlobalDisplay; }; already_AddRefed gfxPlatformGtk::CreateHardwareVsyncSource() { +#ifdef MOZ_WAYLAND + if (GDK_IS_WAYLAND_DISPLAY(gdk_display_get_default())) { + RefPtr vsyncSource = new GtkVsyncSource(); + VsyncSource::Display& display = vsyncSource->GetGlobalDisplay(); + static_cast(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 = new GLXVsyncSource(); + if (gl::sGLXLibrary.SupportsVideoSync()) { + RefPtr vsyncSource = new GtkVsyncSource(); VsyncSource::Display& display = vsyncSource->GetGlobalDisplay(); - if (!static_cast(display).Setup()) { + if (!static_cast(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 */