Skip to content

Commit

Permalink
Bug 1542808 - Implement widget-local VsyncSource for Wayland windows.…
Browse files Browse the repository at this point in the history
… r=stransky,lsalzman

Lets Wayland sessions run vsync off wayland surface frame callbacks by creating
an interface for widgets to return a local VsyncSource, if applicable.

This interface is currently used for the compositor, and for refresh drivers
in the parent process. It is not yet used for vsync in content processes.

Differential Revision: https://phabricator.services.mozilla.com/D28430
  • Loading branch information
kennylevinsen committed Nov 27, 2019
1 parent 109a742 commit 848158c
Show file tree
Hide file tree
Showing 19 changed files with 555 additions and 141 deletions.
3 changes: 2 additions & 1 deletion gfx/thebes/VsyncSource.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ VsyncSource::Display::Display()
: mDispatcherLock("display dispatcher lock"),
mRefreshTimerNeedsVsync(false) {
MOZ_ASSERT(NS_IsMainThread());
mRefreshTimerVsyncDispatcher = new RefreshTimerVsyncDispatcher();
mRefreshTimerVsyncDispatcher = new RefreshTimerVsyncDispatcher(this);
}

VsyncSource::Display::~Display() {
Expand Down Expand Up @@ -117,6 +117,7 @@ void VsyncSource::Display::MoveListenersToNewSource(
std::move(mCompositorVsyncDispatchers));

aNewDisplay.mRefreshTimerVsyncDispatcher = mRefreshTimerVsyncDispatcher;
mRefreshTimerVsyncDispatcher->MoveToDisplay(&aNewDisplay);
mRefreshTimerVsyncDispatcher = nullptr;
}

Expand Down
79 changes: 6 additions & 73 deletions gfx/thebes/gfxPlatformGtk.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,13 +101,6 @@ gfxPlatformGtk::gfxPlatformGtk() {
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

gPlatformFTLibrary = Factory::NewFTLibrary();
MOZ_ASSERT(gPlatformFTLibrary);
Factory::SetFTLibrary(gPlatformFTLibrary);
Expand Down Expand Up @@ -501,13 +494,7 @@ class GtkVsyncSource final : public VsyncSource {
mVsyncThread("GLXVsyncThread"),
mVsyncTask(nullptr),
mVsyncEnabledLock("GLXVsyncEnabledLock"),
mVsyncEnabled(false)
# ifdef MOZ_WAYLAND
,
mIsWaylandDisplay(false)
# endif
{
}
mVsyncEnabled(false) {}

// Sets up the display's GL context on a worker thread.
// Required as GLContexts may only be used by the creating thread.
Expand All @@ -526,15 +513,6 @@ class GtkVsyncSource final : public VsyncSource {
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);
Expand Down Expand Up @@ -585,9 +563,7 @@ class GtkVsyncSource final : public VsyncSource {

virtual void EnableVsync() override {
MOZ_ASSERT(NS_IsMainThread());
# if !defined(MOZ_WAYLAND)
MOZ_ASSERT(mGLContext, "GLContext not setup!");
# endif

MonitorAutoLock lock(mVsyncEnabledLock);
if (mVsyncEnabled) {
Expand All @@ -598,12 +574,8 @@ class GtkVsyncSource final : public VsyncSource {
// 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("GtkVsyncSource::GLXDisplay::RunVsync", this,
# if defined(MOZ_WAYLAND)
mIsWaylandDisplay ? &GLXDisplay::RunVsyncWayland :
# endif
&GLXDisplay::RunVsync);
mVsyncTask = NewRunnableMethod("GtkVsyncSource::GLXDisplay::RunVsync",
this, &GLXDisplay::RunVsync);
RefPtr<Runnable> addrefedTask = mVsyncTask;
mVsyncThread.message_loop()->PostTask(addrefedTask.forget());
}
Expand Down Expand Up @@ -683,41 +655,6 @@ class GtkVsyncSource final : public VsyncSource {
}
}

# 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());

Expand All @@ -733,9 +670,6 @@ class GtkVsyncSource final : public VsyncSource {
RefPtr<Runnable> mVsyncTask;
Monitor mVsyncEnabledLock;
bool mVsyncEnabled;
# ifdef MOZ_WAYLAND
bool mIsWaylandDisplay;
# endif
};

private:
Expand All @@ -746,10 +680,9 @@ class GtkVsyncSource final : public VsyncSource {
already_AddRefed<gfx::VsyncSource> gfxPlatformGtk::CreateHardwareVsyncSource() {
# ifdef MOZ_WAYLAND
if (IsWaylandDisplay()) {
RefPtr<VsyncSource> vsyncSource = new GtkVsyncSource();
VsyncSource::Display& display = vsyncSource->GetGlobalDisplay();
static_cast<GtkVsyncSource::GLXDisplay&>(display).SetupWayland();
return vsyncSource.forget();
// For wayland, we simply return the standard software vsync for now.
// This powers refresh drivers and the likes.
return gfxPlatform::CreateHardwareVsyncSource();
}
# endif

Expand Down
12 changes: 0 additions & 12 deletions gfx/thebes/gfxPlatformGtk.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,14 +93,6 @@ class gfxPlatformGtk : public gfxPlatform {
#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; }
bool UseWaylandDMABufSurfaces();
#endif

Expand All @@ -122,10 +114,6 @@ class gfxPlatformGtk : public gfxPlatform {
#ifdef MOZ_X11
Display* mCompositorDisplay;
#endif
#ifdef MOZ_WAYLAND
int64_t mWaylandLastVsyncTimestamp;
int64_t mWaylandFrameDelay;
#endif
};

#endif /* GFX_PLATFORM_GTK_H */
49 changes: 42 additions & 7 deletions layout/base/nsRefreshDriver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -452,7 +452,7 @@ class VsyncRefreshDriverTimer : public RefreshDriverTimer {
gfxPlatform::GetPlatform()->GetHardwareVsync();
MOZ_ALWAYS_TRUE(mVsyncDispatcher =
vsyncSource->GetRefreshTimerVsyncDispatcher());
mVsyncDispatcher->SetParentRefreshTimer(mVsyncObserver);
mVsyncDispatcher->AddChildRefreshTimer(mVsyncObserver);
mVsyncRate = vsyncSource->GetGlobalDisplay().GetVsyncRate();
}

Expand All @@ -466,6 +466,18 @@ class VsyncRefreshDriverTimer : public RefreshDriverTimer {
mVsyncRate = mVsyncChild->GetVsyncRate();
}

explicit VsyncRefreshDriverTimer(const RefPtr<gfx::VsyncSource>& aVsyncSource)
: mVsyncChild(nullptr) {
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(NS_IsMainThread());
mVsyncSource = aVsyncSource;
mVsyncObserver = new RefreshDriverVsyncObserver(this);
MOZ_ALWAYS_TRUE(mVsyncDispatcher =
aVsyncSource->GetRefreshTimerVsyncDispatcher());
mVsyncDispatcher->AddChildRefreshTimer(mVsyncObserver);
mVsyncRate = aVsyncSource->GetGlobalDisplay().GetVsyncRate();
}

TimeDuration GetTimerRate() override {
if (mVsyncRate != TimeDuration::Forever()) {
return mVsyncRate;
Expand Down Expand Up @@ -750,7 +762,7 @@ class VsyncRefreshDriverTimer : public RefreshDriverTimer {

~VsyncRefreshDriverTimer() override {
if (XRE_IsParentProcess()) {
mVsyncDispatcher->SetParentRefreshTimer(nullptr);
mVsyncDispatcher->RemoveChildRefreshTimer(mVsyncObserver);
mVsyncDispatcher = nullptr;
} else {
// Since the PVsyncChild actors live through the life of the process, just
Expand All @@ -775,7 +787,7 @@ class VsyncRefreshDriverTimer : public RefreshDriverTimer {
mLastFireTime = TimeStamp::Now();

if (XRE_IsParentProcess()) {
mVsyncDispatcher->SetParentRefreshTimer(mVsyncObserver);
mVsyncDispatcher->AddChildRefreshTimer(mVsyncObserver);
} else {
Unused << mVsyncChild->SendObserve();
mVsyncObserver->OnTimerStart();
Expand All @@ -789,7 +801,7 @@ class VsyncRefreshDriverTimer : public RefreshDriverTimer {
MOZ_ASSERT(NS_IsMainThread());

if (XRE_IsParentProcess()) {
mVsyncDispatcher->SetParentRefreshTimer(nullptr);
mVsyncDispatcher->RemoveChildRefreshTimer(mVsyncObserver);
} else {
Unused << mVsyncChild->SendUnobserve();
}
Expand All @@ -807,6 +819,9 @@ class VsyncRefreshDriverTimer : public RefreshDriverTimer {
Tick(aId, aTimeStamp);
}

// Used to hold external vsync sources alive. Must be destroyed *after*
// mVsyncDispatcher.
RefPtr<gfx::VsyncSource> mVsyncSource;
RefPtr<RefreshDriverVsyncObserver> mVsyncObserver;
// Used for parent process.
RefPtr<RefreshTimerVsyncDispatcher> mVsyncDispatcher;
Expand Down Expand Up @@ -1004,7 +1019,7 @@ static void CreateContentVsyncRefreshTimer(void*) {
nsRefreshDriver::PVsyncActorCreated(child);
}

static void CreateVsyncRefreshTimer() {
void nsRefreshDriver::CreateVsyncRefreshTimer() {
MOZ_ASSERT(NS_IsMainThread());

PodArrayZero(sJankLevels);
Expand All @@ -1013,6 +1028,16 @@ static void CreateVsyncRefreshTimer() {
return;
}

// If available, we fetch the widget-specific vsync source.
nsIWidget* widget = GetPresContext()->GetRootWidget();
if (widget) {
RefPtr<gfx::VsyncSource> localVsyncSource = widget->GetVsyncSource();
if (localVsyncSource) {
mOwnTimer = new VsyncRefreshDriverTimer(localVsyncSource);
return;
}
}

if (XRE_IsParentProcess()) {
// Make sure all vsync systems are ready.
gfxPlatform::GetPlatform();
Expand Down Expand Up @@ -1089,7 +1114,7 @@ nsRefreshDriver::GetMinRecomputeVisibilityInterval() {
return TimeDuration::FromMilliseconds(interval);
}

RefreshDriverTimer* nsRefreshDriver::ChooseTimer() const {
RefreshDriverTimer* nsRefreshDriver::ChooseTimer() {
if (mThrottled) {
if (!sThrottledRateTimer)
sThrottledRateTimer = new InactiveRefreshDriverTimer(
Expand All @@ -1098,21 +1123,31 @@ RefreshDriverTimer* nsRefreshDriver::ChooseTimer() const {
return sThrottledRateTimer;
}

if (!sRegularRateTimer) {
if (!sRegularRateTimer && !mOwnTimer) {
double rate = GetRegularTimerInterval();

// Try to use vsync-base refresh timer first for sRegularRateTimer.
CreateVsyncRefreshTimer();

if (mOwnTimer) {
return mOwnTimer.get();
}

if (!sRegularRateTimer) {
sRegularRateTimer = new StartupRefreshDriverTimer(rate);
}
}

if (mOwnTimer) {
return mOwnTimer.get();
}

return sRegularRateTimer;
}

nsRefreshDriver::nsRefreshDriver(nsPresContext* aPresContext)
: mActiveTimer(nullptr),
mOwnTimer(nullptr),
mPresContext(aPresContext),
mRootRefresh(nullptr),
mNextTransactionId{0},
Expand Down
5 changes: 4 additions & 1 deletion layout/base/nsRefreshDriver.h
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,8 @@ class nsRefreshDriver final : public mozilla::layers::TransactionIdAllocator,
*/
static void PVsyncActorCreated(mozilla::layout::VsyncChild* aVsyncChild);

void CreateVsyncRefreshTimer();

#ifdef DEBUG
/**
* Check whether the given observer is an observer for the given flush type
Expand Down Expand Up @@ -479,8 +481,9 @@ class nsRefreshDriver final : public mozilla::layers::TransactionIdAllocator,

void FinishedWaitingForTransaction();

mozilla::RefreshDriverTimer* ChooseTimer() const;
mozilla::RefreshDriverTimer* ChooseTimer();
mozilla::RefreshDriverTimer* mActiveTimer;
RefPtr<mozilla::RefreshDriverTimer> mOwnTimer;

// nsPresContext passed in constructor and unset in Disconnect.
mozilla::WeakPtr<nsPresContext> mPresContext;
Expand Down
1 change: 1 addition & 0 deletions modules/libpref/init/all.js
Original file line number Diff line number Diff line change
Expand Up @@ -3987,6 +3987,7 @@ pref("network.tcp.tcp_fastopen_http_stalls_timeout", 20);
#endif
#ifdef MOZ_WAYLAND
pref("widget.wayland_dmabuf_backend.enabled", false);
pref("widget.wayland_vsync.enabled", false);
#endif

// Timeout for outbound network geolocation provider XHR
Expand Down
Loading

0 comments on commit 848158c

Please sign in to comment.