1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
9 # include "WaylandVsyncSource.h"
10 # include "nsThreadUtils.h"
11 # include "nsISupportsImpl.h"
12 # include "MainThreadUtils.h"
14 # include <gdk/gdkwayland.h>
16 using namespace mozilla::widget
;
20 static void WaylandVsyncSourceCallbackHandler(void* data
,
21 struct wl_callback
* callback
,
23 WaylandVsyncSource::WaylandDisplay
* context
=
24 (WaylandVsyncSource::WaylandDisplay
*)data
;
25 wl_callback_destroy(callback
);
26 context
->FrameCallback(time
);
29 static const struct wl_callback_listener WaylandVsyncSourceCallbackListener
= {
30 WaylandVsyncSourceCallbackHandler
};
32 WaylandVsyncSource::WaylandDisplay::WaylandDisplay(MozContainer
* container
)
33 : mEnabledLock("WaylandVsyncEnabledLock"),
36 mMonitorEnabled(false),
38 mContainer(container
),
39 mLastVsyncTimeStamp(TimeStamp::Now()) {
40 MOZ_ASSERT(NS_IsMainThread());
42 // We store the display here so all the frame callbacks won't have to look it
44 mDisplay
= WaylandDisplayGetWLDisplay();
46 mVsyncRate
= TimeDuration::FromMilliseconds(1000.0 / 60.0);
49 void WaylandVsyncSource::WaylandDisplay::ClearFrameCallback() {
51 wl_callback_destroy(mCallback
);
56 void WaylandVsyncSource::WaylandDisplay::Refresh() {
57 TimeStamp outputTimestamp
;
59 MutexAutoLock
lock(mEnabledLock
);
60 if (!mMonitorEnabled
|| !mVsyncEnabled
|| mCallback
) {
61 // We don't need to do anything because:
62 // * We are unwanted by our widget or monitor, or
63 // * The last frame callback hasn't yet run to see that it had been shut
64 // down, so we can reuse it after having set mVsyncEnabled to true.
68 struct wl_surface
* surface
= moz_container_wayland_surface_lock(mContainer
);
70 // The surface hasn't been created yet. Try again when the surface is
72 RefPtr
<WaylandVsyncSource::WaylandDisplay
> self(this);
73 moz_container_wayland_add_initial_draw_callback(
74 mContainer
, [self
]() -> void { self
->Refresh(); });
77 moz_container_wayland_surface_unlock(mContainer
, &surface
);
79 // Vsync is enabled, but we don't have a callback configured. Set one up so
80 // we can get to work.
82 mLastVsyncTimeStamp
= TimeStamp::Now();
83 outputTimestamp
= mLastVsyncTimeStamp
+ GetVsyncRate();
85 NotifyVsync(mLastVsyncTimeStamp
, outputTimestamp
);
88 void WaylandVsyncSource::WaylandDisplay::EnableMonitor() {
90 MutexAutoLock
lock(mEnabledLock
);
91 if (mMonitorEnabled
) {
94 mMonitorEnabled
= true;
99 void WaylandVsyncSource::WaylandDisplay::DisableMonitor() {
100 MutexAutoLock
lock(mEnabledLock
);
101 if (!mMonitorEnabled
) {
104 mMonitorEnabled
= false;
105 ClearFrameCallback();
108 void WaylandVsyncSource::WaylandDisplay::SetupFrameCallback() {
109 MOZ_ASSERT(mCallback
== nullptr);
110 struct wl_surface
* surface
= moz_container_wayland_surface_lock(mContainer
);
112 // We don't have a surface, either due to being called before it was made
113 // available in the mozcontainer, or after it was destroyed. We're all done
115 ClearFrameCallback();
119 mCallback
= wl_surface_frame(surface
);
120 wl_callback_add_listener(mCallback
, &WaylandVsyncSourceCallbackListener
,
122 wl_surface_commit(surface
);
123 wl_display_flush(mDisplay
);
124 moz_container_wayland_surface_unlock(mContainer
, &surface
);
127 void WaylandVsyncSource::WaylandDisplay::FrameCallback(uint32_t timestampTime
) {
128 TimeStamp outputTimestamp
;
130 MutexAutoLock
lock(mEnabledLock
);
133 if (!mVsyncEnabled
|| !mMonitorEnabled
) {
134 // We are unwanted by either our creator or our consumer, so we just stop
135 // here without setting up a new frame callback.
139 // Configure our next frame callback.
140 SetupFrameCallback();
143 BaseTimeDurationPlatformUtils::TicksFromMilliseconds(timestampTime
);
144 TimeStamp callbackTimeStamp
= TimeStamp::FromSystemTime(tick
);
145 double duration
= (TimeStamp::Now() - callbackTimeStamp
).ToMilliseconds();
147 TimeStamp vsyncTimestamp
;
148 if (duration
< 50 && duration
> -50) {
149 vsyncTimestamp
= callbackTimeStamp
;
151 vsyncTimestamp
= TimeStamp::Now();
154 CalculateVsyncRate(vsyncTimestamp
);
155 mLastVsyncTimeStamp
= vsyncTimestamp
;
156 outputTimestamp
= vsyncTimestamp
+ GetVsyncRate();
158 NotifyVsync(mLastVsyncTimeStamp
, outputTimestamp
);
161 TimeDuration
WaylandVsyncSource::WaylandDisplay::GetVsyncRate() {
165 void WaylandVsyncSource::WaylandDisplay::CalculateVsyncRate(
166 TimeStamp vsyncTimestamp
) {
167 double duration
= (vsyncTimestamp
- mLastVsyncTimeStamp
).ToMilliseconds();
168 double curVsyncRate
= mVsyncRate
.ToMilliseconds();
171 if (duration
> curVsyncRate
) {
172 correction
= fmin(curVsyncRate
, (duration
- curVsyncRate
) / 10);
173 mVsyncRate
+= TimeDuration::FromMilliseconds(correction
);
175 correction
= fmin(curVsyncRate
/ 2, (curVsyncRate
- duration
) / 10);
176 mVsyncRate
-= TimeDuration::FromMilliseconds(correction
);
180 void WaylandVsyncSource::WaylandDisplay::EnableVsync() {
181 MOZ_ASSERT(NS_IsMainThread());
183 MutexAutoLock
lock(mEnabledLock
);
184 if (mVsyncEnabled
|| mIsShutdown
) {
187 mVsyncEnabled
= true;
192 void WaylandVsyncSource::WaylandDisplay::DisableVsync() {
193 MutexAutoLock
lock(mEnabledLock
);
194 mVsyncEnabled
= false;
195 ClearFrameCallback();
198 bool WaylandVsyncSource::WaylandDisplay::IsVsyncEnabled() {
199 MutexAutoLock
lock(mEnabledLock
);
200 return mVsyncEnabled
;
203 void WaylandVsyncSource::WaylandDisplay::Shutdown() {
204 MOZ_ASSERT(NS_IsMainThread());
205 MutexAutoLock
lock(mEnabledLock
);
207 mVsyncEnabled
= false;
208 ClearFrameCallback();
211 } // namespace mozilla
213 #endif // MOZ_WAYLAND