summaryrefslogtreecommitdiffstats
path: root/widget/gtk/WaylandVsyncSource.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--widget/gtk/WaylandVsyncSource.cpp214
1 files changed, 214 insertions, 0 deletions
diff --git a/widget/gtk/WaylandVsyncSource.cpp b/widget/gtk/WaylandVsyncSource.cpp
new file mode 100644
index 0000000000..02f9d9c38b
--- /dev/null
+++ b/widget/gtk/WaylandVsyncSource.cpp
@@ -0,0 +1,214 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifdef MOZ_WAYLAND
+
+# include "WaylandVsyncSource.h"
+# include "nsThreadUtils.h"
+# include "nsISupportsImpl.h"
+# include "MainThreadUtils.h"
+
+# include <gdk/gdkwayland.h>
+
+using namespace mozilla::widget;
+
+namespace mozilla {
+
+static void WaylandVsyncSourceCallbackHandler(void* data,
+ struct wl_callback* callback,
+ uint32_t time) {
+ WaylandVsyncSource::WaylandDisplay* context =
+ (WaylandVsyncSource::WaylandDisplay*)data;
+ wl_callback_destroy(callback);
+ context->FrameCallback(time);
+}
+
+static const struct wl_callback_listener WaylandVsyncSourceCallbackListener = {
+ WaylandVsyncSourceCallbackHandler};
+
+WaylandVsyncSource::WaylandDisplay::WaylandDisplay(MozContainer* container)
+ : mEnabledLock("WaylandVsyncEnabledLock"),
+ mIsShutdown(false),
+ mVsyncEnabled(false),
+ mMonitorEnabled(false),
+ mCallback(nullptr),
+ mContainer(container),
+ mLastVsyncTimeStamp(TimeStamp::Now()) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ // We store the display here so all the frame callbacks won't have to look it
+ // up all the time.
+ mDisplay = WaylandDisplayGetWLDisplay();
+
+ mVsyncRate = TimeDuration::FromMilliseconds(1000.0 / 60.0);
+}
+
+void WaylandVsyncSource::WaylandDisplay::ClearFrameCallback() {
+ if (mCallback) {
+ wl_callback_destroy(mCallback);
+ mCallback = nullptr;
+ }
+}
+
+void WaylandVsyncSource::WaylandDisplay::Refresh() {
+ TimeStamp outputTimestamp;
+ {
+ MutexAutoLock lock(mEnabledLock);
+ if (!mMonitorEnabled || !mVsyncEnabled || mCallback) {
+ // We don't need to do anything because:
+ // * We are unwanted by our widget or monitor, or
+ // * The last frame callback hasn't yet run to see that it had been shut
+ // down, so we can reuse it after having set mVsyncEnabled to true.
+ return;
+ }
+
+ struct wl_surface* surface = moz_container_wayland_surface_lock(mContainer);
+ if (!surface) {
+ // The surface hasn't been created yet. Try again when the surface is
+ // ready.
+ RefPtr<WaylandVsyncSource::WaylandDisplay> self(this);
+ moz_container_wayland_add_initial_draw_callback(
+ mContainer, [self]() -> void { self->Refresh(); });
+ return;
+ }
+ moz_container_wayland_surface_unlock(mContainer, &surface);
+
+ // Vsync is enabled, but we don't have a callback configured. Set one up so
+ // we can get to work.
+ SetupFrameCallback();
+ mLastVsyncTimeStamp = TimeStamp::Now();
+ outputTimestamp = mLastVsyncTimeStamp + GetVsyncRate();
+ }
+ NotifyVsync(mLastVsyncTimeStamp, outputTimestamp);
+}
+
+void WaylandVsyncSource::WaylandDisplay::EnableMonitor() {
+ {
+ MutexAutoLock lock(mEnabledLock);
+ if (mMonitorEnabled) {
+ return;
+ }
+ mMonitorEnabled = true;
+ }
+ Refresh();
+}
+
+void WaylandVsyncSource::WaylandDisplay::DisableMonitor() {
+ MutexAutoLock lock(mEnabledLock);
+ if (!mMonitorEnabled) {
+ return;
+ }
+ mMonitorEnabled = false;
+ ClearFrameCallback();
+}
+
+void WaylandVsyncSource::WaylandDisplay::SetupFrameCallback() {
+ MOZ_ASSERT(mCallback == nullptr);
+ struct wl_surface* surface = moz_container_wayland_surface_lock(mContainer);
+ if (!surface) {
+ // We don't have a surface, either due to being called before it was made
+ // available in the mozcontainer, or after it was destroyed. We're all done
+ // regardless.
+ ClearFrameCallback();
+ return;
+ }
+
+ mCallback = wl_surface_frame(surface);
+ wl_callback_add_listener(mCallback, &WaylandVsyncSourceCallbackListener,
+ this);
+ wl_surface_commit(surface);
+ wl_display_flush(mDisplay);
+ moz_container_wayland_surface_unlock(mContainer, &surface);
+}
+
+void WaylandVsyncSource::WaylandDisplay::FrameCallback(uint32_t timestampTime) {
+ TimeStamp outputTimestamp;
+ {
+ MutexAutoLock lock(mEnabledLock);
+ mCallback = nullptr;
+
+ if (!mVsyncEnabled || !mMonitorEnabled) {
+ // We are unwanted by either our creator or our consumer, so we just stop
+ // here without setting up a new frame callback.
+ return;
+ }
+
+ // Configure our next frame callback.
+ SetupFrameCallback();
+
+ int64_t tick =
+ BaseTimeDurationPlatformUtils::TicksFromMilliseconds(timestampTime);
+ TimeStamp callbackTimeStamp = TimeStamp::FromSystemTime(tick);
+ double duration = (TimeStamp::Now() - callbackTimeStamp).ToMilliseconds();
+
+ TimeStamp vsyncTimestamp;
+ if (duration < 50 && duration > -50) {
+ vsyncTimestamp = callbackTimeStamp;
+ } else {
+ vsyncTimestamp = TimeStamp::Now();
+ }
+
+ CalculateVsyncRate(vsyncTimestamp);
+ mLastVsyncTimeStamp = vsyncTimestamp;
+ outputTimestamp = vsyncTimestamp + GetVsyncRate();
+ }
+ NotifyVsync(mLastVsyncTimeStamp, outputTimestamp);
+}
+
+TimeDuration WaylandVsyncSource::WaylandDisplay::GetVsyncRate() {
+ return mVsyncRate;
+}
+
+void WaylandVsyncSource::WaylandDisplay::CalculateVsyncRate(
+ TimeStamp vsyncTimestamp) {
+ double duration = (vsyncTimestamp - mLastVsyncTimeStamp).ToMilliseconds();
+ double curVsyncRate = mVsyncRate.ToMilliseconds();
+ double correction;
+
+ if (duration > curVsyncRate) {
+ correction = fmin(curVsyncRate, (duration - curVsyncRate) / 10);
+ mVsyncRate += TimeDuration::FromMilliseconds(correction);
+ } else {
+ correction = fmin(curVsyncRate / 2, (curVsyncRate - duration) / 10);
+ mVsyncRate -= TimeDuration::FromMilliseconds(correction);
+ }
+}
+
+void WaylandVsyncSource::WaylandDisplay::EnableVsync() {
+ MOZ_ASSERT(NS_IsMainThread());
+ {
+ MutexAutoLock lock(mEnabledLock);
+ if (mVsyncEnabled || mIsShutdown) {
+ return;
+ }
+ mVsyncEnabled = true;
+ }
+ Refresh();
+}
+
+void WaylandVsyncSource::WaylandDisplay::DisableVsync() {
+ MutexAutoLock lock(mEnabledLock);
+ mVsyncEnabled = false;
+ ClearFrameCallback();
+}
+
+bool WaylandVsyncSource::WaylandDisplay::IsVsyncEnabled() {
+ MutexAutoLock lock(mEnabledLock);
+ return mVsyncEnabled;
+}
+
+void WaylandVsyncSource::WaylandDisplay::Shutdown() {
+ MOZ_ASSERT(NS_IsMainThread());
+ MutexAutoLock lock(mEnabledLock);
+ mIsShutdown = true;
+ mVsyncEnabled = false;
+ ClearFrameCallback();
+ wl_display_roundtrip(mDisplay);
+}
+
+} // namespace mozilla
+
+#endif // MOZ_WAYLAND