Bug 1542808 - Implement widget-local VsyncSource for Wayland windows. 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

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Kenny Levinsen 2019-11-27 00:21:33 +00:00
Родитель b724f12298
Коммит 1c5650fe48
19 изменённых файлов: 555 добавлений и 141 удалений

Просмотреть файл

@ -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() {
@ -117,6 +117,7 @@ void VsyncSource::Display::MoveListenersToNewSource(
std::move(mCompositorVsyncDispatchers));
aNewDisplay.mRefreshTimerVsyncDispatcher = mRefreshTimerVsyncDispatcher;
mRefreshTimerVsyncDispatcher->MoveToDisplay(&aNewDisplay);
mRefreshTimerVsyncDispatcher = nullptr;
}

Просмотреть файл

@ -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);
@ -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.
@ -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);
@ -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) {
@ -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());
}
@ -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());
@ -733,9 +670,6 @@ class GtkVsyncSource final : public VsyncSource {
RefPtr<Runnable> mVsyncTask;
Monitor mVsyncEnabledLock;
bool mVsyncEnabled;
# ifdef MOZ_WAYLAND
bool mIsWaylandDisplay;
# endif
};
private:
@ -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

Просмотреть файл

@ -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
@ -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 */

Просмотреть файл

@ -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();
}
@ -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;
@ -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
@ -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();
@ -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();
}
@ -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;
@ -1004,7 +1019,7 @@ static void CreateContentVsyncRefreshTimer(void*) {
nsRefreshDriver::PVsyncActorCreated(child);
}
static void CreateVsyncRefreshTimer() {
void nsRefreshDriver::CreateVsyncRefreshTimer() {
MOZ_ASSERT(NS_IsMainThread());
PodArrayZero(sJankLevels);
@ -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();
@ -1089,7 +1114,7 @@ nsRefreshDriver::GetMinRecomputeVisibilityInterval() {
return TimeDuration::FromMilliseconds(interval);
}
RefreshDriverTimer* nsRefreshDriver::ChooseTimer() const {
RefreshDriverTimer* nsRefreshDriver::ChooseTimer() {
if (mThrottled) {
if (!sThrottledRateTimer)
sThrottledRateTimer = new InactiveRefreshDriverTimer(
@ -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},

Просмотреть файл

@ -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
@ -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;

Просмотреть файл

@ -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

Просмотреть файл

@ -16,7 +16,18 @@ using namespace mozilla::layers;
namespace mozilla {
CompositorVsyncDispatcher::CompositorVsyncDispatcher()
: mCompositorObserverLock("CompositorObserverLock"), mDidShutdown(false) {
: mVsyncSource(gfxPlatform::GetPlatform()->GetHardwareVsync()),
mCompositorObserverLock("CompositorObserverLock"),
mDidShutdown(false) {
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(NS_IsMainThread());
}
CompositorVsyncDispatcher::CompositorVsyncDispatcher(
RefPtr<gfx::VsyncSource> aVsyncSource)
: mVsyncSource(std::move(aVsyncSource)),
mCompositorObserverLock("CompositorObserverLock"),
mDidShutdown(false) {
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(NS_IsMainThread());
}
@ -45,13 +56,9 @@ void CompositorVsyncDispatcher::ObserveVsync(bool aEnable) {
}
if (aEnable) {
gfxPlatform::GetPlatform()
->GetHardwareVsync()
->AddCompositorVsyncDispatcher(this);
mVsyncSource->AddCompositorVsyncDispatcher(this);
} else {
gfxPlatform::GetPlatform()
->GetHardwareVsync()
->RemoveCompositorVsyncDispatcher(this);
mVsyncSource->RemoveCompositorVsyncDispatcher(this);
}
}
@ -89,8 +96,11 @@ void CompositorVsyncDispatcher::Shutdown() {
}
}
RefreshTimerVsyncDispatcher::RefreshTimerVsyncDispatcher()
: mRefreshTimersLock("RefreshTimers lock") {
RefreshTimerVsyncDispatcher::RefreshTimerVsyncDispatcher(
gfx::VsyncSource::Display* aDisplay)
: mDisplay(aDisplay),
mDisplayLock("RefreshTimerVsyncDispatcherDisplayLock"),
mRefreshTimersLock("RefreshTimers lock") {
MOZ_ASSERT(XRE_IsParentProcess() || recordreplay::IsRecordingOrReplaying());
MOZ_ASSERT(NS_IsMainThread());
}
@ -100,6 +110,12 @@ RefreshTimerVsyncDispatcher::~RefreshTimerVsyncDispatcher() {
MOZ_ASSERT(NS_IsMainThread());
}
void RefreshTimerVsyncDispatcher::MoveToDisplay(
gfx::VsyncSource::Display* aDisplay) {
MutexAutoLock lock(mDisplayLock);
mDisplay = aDisplay;
}
void RefreshTimerVsyncDispatcher::NotifyVsync(const VsyncEvent& aVsync) {
MutexAutoLock lock(mRefreshTimersLock);
@ -156,9 +172,8 @@ void RefreshTimerVsyncDispatcher::UpdateVsyncStatus() {
return;
}
gfx::VsyncSource::Display& display =
gfxPlatform::GetPlatform()->GetHardwareVsync()->GetGlobalDisplay();
display.NotifyRefreshTimerVsyncStatus(NeedsVsync());
MutexAutoLock lock(mDisplayLock);
mDisplay->NotifyRefreshTimerVsyncStatus(NeedsVsync());
}
bool RefreshTimerVsyncDispatcher::NeedsVsync() {

Просмотреть файл

@ -48,6 +48,7 @@ class CompositorVsyncDispatcher final {
public:
CompositorVsyncDispatcher();
explicit CompositorVsyncDispatcher(RefPtr<gfx::VsyncSource> aVsyncSource);
// Called on the vsync thread when a hardware vsync occurs
void NotifyVsync(const VsyncEvent& aVsync);
@ -60,6 +61,7 @@ class CompositorVsyncDispatcher final {
virtual ~CompositorVsyncDispatcher();
void ObserveVsync(bool aEnable);
RefPtr<gfx::VsyncSource> mVsyncSource;
Mutex mCompositorObserverLock;
RefPtr<VsyncObserver> mCompositorVsyncObserver;
bool mDidShutdown;
@ -70,11 +72,13 @@ class RefreshTimerVsyncDispatcher final {
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RefreshTimerVsyncDispatcher)
public:
RefreshTimerVsyncDispatcher();
explicit RefreshTimerVsyncDispatcher(gfx::VsyncSource::Display* aDisplay);
// Please check CompositorVsyncDispatcher::NotifyVsync().
void NotifyVsync(const VsyncEvent& aVsync);
void MoveToDisplay(gfx::VsyncSource::Display* aDisplay);
// Set chrome process's RefreshTimer to this dispatcher.
// This function can be called from any thread.
void SetParentRefreshTimer(VsyncObserver* aVsyncObserver);
@ -91,6 +95,11 @@ class RefreshTimerVsyncDispatcher final {
void UpdateVsyncStatus();
bool NeedsVsync();
// We need to hold a weak ref to the display we belong to in order to notify
// it of our vsync requirement. The display holds a RefPtr to us, so we can't
// hold a RefPtr back without causing a cyclic dependency.
gfx::VsyncSource::Display* mDisplay;
Mutex mDisplayLock;
Mutex mRefreshTimersLock;
RefPtr<VsyncObserver> mParentRefreshTimer;
nsTArray<RefPtr<VsyncObserver>> mChildRefreshTimers;

Просмотреть файл

@ -0,0 +1,209 @@
/* -*- 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>
namespace mozilla {
static void WaylandVsyncSourceCallbackHandler(void* data,
struct wl_callback* callback,
uint32_t time) {
WaylandVsyncSource::WaylandFrameCallbackContext* context =
(WaylandVsyncSource::WaylandFrameCallbackContext*)data;
wl_callback_destroy(callback);
if (!context->mEnabled) {
// We have been terminated, so just clean up the context and return.
delete context;
return;
}
context->mDisplay->FrameCallback();
}
static const struct wl_callback_listener WaylandVsyncSourceCallbackListener = {
WaylandVsyncSourceCallbackHandler};
WaylandVsyncSource::WaylandDisplay::WaylandDisplay(MozContainer* container)
: mThread("WaylandVsyncThread"),
mTask(nullptr),
mCallbackContext(nullptr),
mNotifyThreadMonitor("WaylandVsyncNotifyThreadMonitor"),
mEnabledLock("WaylandVsyncEnabledLock"),
mVsyncEnabled(false),
mMonitorEnabled(false),
mShutdown(false),
mContainer(container) {
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 = widget::WaylandDisplayGet()->GetDisplay();
}
void WaylandVsyncSource::WaylandDisplay::Loop() {
MonitorAutoLock lock(mNotifyThreadMonitor);
while (true) {
lock.Wait();
if (mShutdown) {
return;
}
NotifyVsync(TimeStamp::Now());
}
}
void WaylandVsyncSource::WaylandDisplay::ClearFrameCallback() {
if (mCallbackContext) {
mCallbackContext->mEnabled = false;
mCallbackContext = nullptr;
}
}
bool WaylandVsyncSource::WaylandDisplay::Setup() {
MutexAutoLock lock(mEnabledLock);
MOZ_ASSERT(!mTask);
MOZ_ASSERT(!mShutdown);
if (!mThread.Start()) {
return false;
}
mTask = NewRunnableMethod("WaylandVsyncSource::WaylandDisplay::Loop", this,
&WaylandDisplay::Loop);
RefPtr<Runnable> addrefedTask = mTask;
mThread.message_loop()->PostTask(addrefedTask.forget());
return true;
}
void WaylandVsyncSource::WaylandDisplay::EnableMonitor() {
MutexAutoLock lock(mEnabledLock);
if (mMonitorEnabled) {
return;
}
mMonitorEnabled = true;
if (mVsyncEnabled && (!mCallbackContext || !mCallbackContext->mEnabled)) {
// Vsync is enabled, but we don't have a callback configured. Set one up so
// we can get to work.
SetupFrameCallback();
}
}
void WaylandVsyncSource::WaylandDisplay::DisableMonitor() {
MutexAutoLock lock(mEnabledLock);
if (!mMonitorEnabled) {
return;
}
mMonitorEnabled = false;
ClearFrameCallback();
}
void WaylandVsyncSource::WaylandDisplay::Notify() {
// Use our vsync thread to notify any vsync observers.
MonitorAutoLock lock(mNotifyThreadMonitor);
mNotifyThreadMonitor.NotifyAll();
}
void WaylandVsyncSource::WaylandDisplay::SetupFrameCallback() {
struct wl_surface* surface = moz_container_get_wl_surface(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;
}
if (mCallbackContext == nullptr) {
// We don't have a context as we're not currently running, so allocate one.
// The callback handler is responsible for deallocating the context, as
// the callback might be called after we have been destroyed.
mCallbackContext = new WaylandFrameCallbackContext(this);
}
struct wl_callback* callback = wl_surface_frame(surface);
wl_callback_add_listener(callback, &WaylandVsyncSourceCallbackListener,
mCallbackContext);
wl_surface_commit(surface);
wl_display_flush(mDisplay);
}
void WaylandVsyncSource::WaylandDisplay::FrameCallback() {
{
MutexAutoLock lock(mEnabledLock);
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();
}
Notify();
}
void WaylandVsyncSource::WaylandDisplay::EnableVsync() {
MOZ_ASSERT(NS_IsMainThread());
MutexAutoLock lock(mEnabledLock);
if (mVsyncEnabled) {
return;
}
mVsyncEnabled = true;
if (!mMonitorEnabled || (mCallbackContext && mCallbackContext->mEnabled)) {
// We don't need to do anything because:
// * We are unwanted by our widget, 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;
}
// We don't have a callback configured, so set one up.
SetupFrameCallback();
}
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());
DisableVsync();
// Terminate our task by setting the shutdown flag and waking up the thread so
// that it may check the flag.
{
MonitorAutoLock lock(mNotifyThreadMonitor);
mShutdown = true;
mNotifyThreadMonitor.NotifyAll();
}
// Stop the thread. Note that Stop() waits for tasks to terminate, so we must
// manually ensure that we will break out of our thread loop first in order to
// not block forever.
mThread.Stop();
}
} // namespace mozilla
#endif // MOZ_WAYLAND

Просмотреть файл

@ -0,0 +1,110 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* 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/. */
#ifndef _WaylandVsyncSource_h_
#define _WaylandVsyncSource_h_
#include "mozilla/RefPtr.h"
#include "mozilla/Mutex.h"
#include "mozilla/Monitor.h"
#include "mozcontainer.h"
#include "VsyncSource.h"
#include "base/thread.h"
#include "nsWaylandDisplay.h"
namespace mozilla {
/*
* WaylandVsyncSource
*
* This class provides a per-widget VsyncSource under Wayland, emulated using
* frame callbacks on the widget surface with empty surface commits.
*
* Wayland does not expose vsync/vblank, as it considers that an implementation
* detail the clients should not concern themselves with. Instead, frame
* callbacks are provided whenever the compositor believes it is a good time to
* start drawing the next frame for a particular surface, giving us as much
* time as possible to do so.
*
* Note that the compositor sends frame callbacks only when it sees fit, and
* when that may be is entirely up to the compositor. One cannot expect a
* certain rate of callbacks, or any callbacks at all. Examples of common
* variations would be surfaces moved between outputs with different refresh
* rates, and surfaces that are hidden and therefore do not receieve any
* callbacks at all. Other hypothetical scenarios of variation could be
* throttling to conserve power, or because a user has requested it.
*
*/
class WaylandVsyncSource final : public gfx::VsyncSource {
public:
explicit WaylandVsyncSource(MozContainer* container) {
MOZ_ASSERT(NS_IsMainThread());
mGlobalDisplay = new WaylandDisplay(container);
}
virtual ~WaylandVsyncSource() { MOZ_ASSERT(NS_IsMainThread()); }
virtual Display& GetGlobalDisplay() override { return *mGlobalDisplay; }
struct WaylandFrameCallbackContext;
class WaylandDisplay final : public mozilla::gfx::VsyncSource::Display {
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WaylandDisplay);
public:
explicit WaylandDisplay(MozContainer* container);
bool Setup();
void EnableMonitor();
void DisableMonitor();
void FrameCallback();
void Notify();
virtual void EnableVsync() override;
virtual void DisableVsync() override;
virtual bool IsVsyncEnabled() override;
virtual void Shutdown() override;
private:
virtual ~WaylandDisplay() = default;
void Loop();
void SetupFrameCallback();
void ClearFrameCallback();
base::Thread mThread;
RefPtr<Runnable> mTask;
WaylandFrameCallbackContext* mCallbackContext;
Monitor mNotifyThreadMonitor;
Mutex mEnabledLock;
bool mVsyncEnabled;
bool mMonitorEnabled;
bool mShutdown;
struct wl_display* mDisplay;
MozContainer* mContainer;
};
// The WaylandFrameCallbackContext is a context owned by the frame callbacks.
// It is created by the display, but deleted by the frame callbacks on the
// next callback after being disabled.
struct WaylandFrameCallbackContext {
explicit WaylandFrameCallbackContext(
WaylandVsyncSource::WaylandDisplay* aDisplay)
: mEnabled(true), mDisplay(aDisplay) {}
bool mEnabled;
WaylandVsyncSource::WaylandDisplay* mDisplay;
};
private:
// We need a refcounted VsyncSource::Display to use chromium IPC runnables.
RefPtr<WaylandDisplay> mGlobalDisplay;
};
} // namespace mozilla
#endif // _WaylandVsyncSource_h_

Просмотреть файл

@ -503,8 +503,6 @@ 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 = {

Просмотреть файл

@ -54,6 +54,7 @@ UNIFIED_SOURCES += [
SOURCES += [
'nsWindow.cpp', # conflicts with X11 headers
'WaylandVsyncSource.cpp', # conflicts with X11 headers
]
if CONFIG['MOZ_X11']:

Просмотреть файл

@ -217,19 +217,19 @@ void moz_container_init(MozContainer* container) {
// We can draw to x11 window any time.
container->ready_to_draw = gfxPlatformGtk::GetPlatform()->IsX11Display();
container->surface_needs_clear = true;
container->inital_draw_cb = nullptr;
container->subsurface_dx = 0;
container->subsurface_dy = 0;
container->surface_position_needs_update = 0;
container->initial_draw_cbs.clear();
#endif
LOG(("%s [%p]\n", __FUNCTION__, (void*)container));
}
#if defined(MOZ_WAYLAND)
void moz_container_set_initial_draw_callback(
MozContainer* container, std::function<void(void)> inital_draw_cb) {
container->inital_draw_cb = inital_draw_cb;
void moz_container_add_initial_draw_callback(
MozContainer* container, const std::function<void(void)>& initial_draw_cb) {
container->initial_draw_cbs.push_back(initial_draw_cb);
}
static wl_surface* moz_container_get_gtk_container_surface(
@ -253,17 +253,20 @@ static void frame_callback_handler(void* data, struct wl_callback* callback,
LOGWAYLAND(
("%s [%p] frame_callback_handler %p ready_to_draw %d (set to true)"
" inital_draw callback %d\n",
" initial_draw callback %zd\n",
__FUNCTION__, (void*)container, (void*)container->frame_callback_handler,
container->ready_to_draw, container->inital_draw_cb ? 1 : 0));
container->ready_to_draw, container->initial_draw_cbs.size()));
g_clear_pointer(&container->frame_callback_handler, wl_callback_destroy);
container->frame_callback_handler_surface_id = -1;
if (!container->ready_to_draw && container->inital_draw_cb) {
container->inital_draw_cb();
if (!container->ready_to_draw) {
container->ready_to_draw = true;
for (auto const& cb : container->initial_draw_cbs) {
cb();
}
container->initial_draw_cbs.clear();
}
container->ready_to_draw = true;
}
static const struct wl_callback_listener frame_listener = {
@ -561,8 +564,7 @@ static void moz_container_add(GtkContainer* container, GtkWidget* widget) {
}
#ifdef MOZ_WAYLAND
struct wl_surface* moz_container_get_wl_surface(MozContainer* container,
int scale) {
struct wl_surface* moz_container_get_wl_surface(MozContainer* container) {
LOGWAYLAND(("%s [%p] surface %p ready_to_draw %d\n", __FUNCTION__,
(void*)container, (void*)container->surface,
container->ready_to_draw));
@ -611,7 +613,6 @@ struct wl_surface* moz_container_get_wl_surface(MozContainer* container,
container->subsurface_dy);
}
wl_surface_set_buffer_scale(container->surface, scale);
return container->surface;
}
@ -622,11 +623,11 @@ struct wl_egl_window* moz_container_get_wl_egl_window(MozContainer* container,
// Always call moz_container_get_wl_surface() to ensure underlying
// container->surface has correct scale and position.
wl_surface* surface = moz_container_get_wl_surface(container, scale);
wl_surface* surface = moz_container_get_wl_surface(container);
if (!surface) {
return nullptr;
}
wl_surface_set_buffer_scale(surface, scale);
if (!container->eglwindow) {
GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(container));
container->eglwindow =

Просмотреть файл

@ -10,6 +10,7 @@
#include <gtk/gtk.h>
#include <functional>
#include <vector>
/*
* MozContainer
@ -82,7 +83,7 @@ struct _MozContainer {
gboolean surface_position_needs_update;
gboolean surface_needs_clear;
gboolean ready_to_draw;
std::function<void(void)> inital_draw_cb;
std::vector<std::function<void(void)>> initial_draw_cbs;
#endif
gboolean force_default_visual;
};
@ -98,19 +99,20 @@ void moz_container_put(MozContainer* container, GtkWidget* child_widget, gint x,
void moz_container_force_default_visual(MozContainer* container);
#ifdef MOZ_WAYLAND
struct wl_surface* moz_container_get_wl_surface(MozContainer* container,
int scale);
struct wl_surface* moz_container_get_wl_surface(MozContainer* container);
struct wl_egl_window* moz_container_get_wl_egl_window(MozContainer* container,
int scale);
gboolean moz_container_has_wl_egl_window(MozContainer* container);
gboolean moz_container_surface_needs_clear(MozContainer* container);
void moz_container_set_initial_draw_callback(
MozContainer* container, std::function<void(void)> inital_draw_cb);
void moz_container_move_resize(MozContainer* container, int dx, int dy,
int width, int height);
void moz_container_egl_window_set_size(MozContainer* container, int width,
int height);
void moz_container_scale_changed(MozContainer* container,
GtkAllocation* aAllocation);
void moz_container_add_initial_draw_callback(
MozContainer* container, const std::function<void(void)>& initial_draw_cb);
#endif
#endif /* __MOZ_CONTAINER_H__ */

Просмотреть файл

@ -340,6 +340,7 @@ static nsWindow* gFocusWindow = nullptr;
static bool gBlockActivateEvent = false;
static bool gGlobalsInitialized = false;
static bool gRaiseWindows = true;
static bool gUseWaylandVsync = false;
static GList* gVisibleWaylandPopupWindows = nullptr;
#if GTK_CHECK_VERSION(3, 4, 0)
@ -672,6 +673,14 @@ void nsWindow::Destroy() {
}
mLayerManager = nullptr;
#ifdef MOZ_WAYLAND
// Shut down our local vsync source
if (mWaylandVsyncSource) {
mWaylandVsyncSource->Shutdown();
mWaylandVsyncSource = nullptr;
}
#endif
// It is safe to call DestroyeCompositor several times (here and
// in the parent class) since it will take effect only once.
// The reason we call it here is because on gtk platforms we need
@ -716,12 +725,6 @@ void nsWindow::Destroy() {
gFocusWindow = nullptr;
}
#ifdef MOZ_WAYLAND
if (mContainer) {
moz_container_set_initial_draw_callback(mContainer, nullptr);
}
#endif
GtkWidget* owningWidget = GetMozContainerWidget();
if (mShell) {
gtk_widget_destroy(mShell);
@ -2218,6 +2221,25 @@ void nsWindow::MaybeResumeCompositor() {
remoteRenderer->SendForcePresent();
}
}
void nsWindow::CreateCompositorVsyncDispatcher() {
if (!mWaylandVsyncSource) {
nsBaseWidget::CreateCompositorVsyncDispatcher();
return;
}
if (XRE_IsParentProcess()) {
if (!mCompositorVsyncDispatcherLock) {
mCompositorVsyncDispatcherLock =
MakeUnique<Mutex>("mCompositorVsyncDispatcherLock");
}
MutexAutoLock lock(*mCompositorVsyncDispatcherLock);
if (!mCompositorVsyncDispatcher) {
mCompositorVsyncDispatcher =
new CompositorVsyncDispatcher(mWaylandVsyncSource);
}
}
}
#endif
gboolean nsWindow::OnExposeEvent(cairo_t* cr) {
@ -3929,7 +3951,7 @@ nsresult nsWindow::Create(nsIWidget* aParent, nsNativeWidget aNativeParent,
if (!mIsX11Display && ComputeShouldAccelerate()) {
mCompositorInitiallyPaused = true;
RefPtr<nsWindow> self(this);
moz_container_set_initial_draw_callback(mContainer, [self]() -> void {
moz_container_add_initial_draw_callback(mContainer, [self]() -> void {
self->mNeedsCompositorResume = true;
self->MaybeResumeCompositor();
});
@ -4209,6 +4231,7 @@ nsresult nsWindow::Create(nsIWidget* aParent, nsNativeWidget aNativeParent,
# ifdef MOZ_WAYLAND
else if (!mIsX11Display) {
mSurfaceProvider.Initialize(this);
WaylandStartVsync();
}
# endif
#endif
@ -4415,6 +4438,50 @@ void nsWindow::HideWaylandWindow() {
gtk_widget_hide(mShell);
}
void nsWindow::WaylandStartVsync() {
#ifdef MOZ_WAYLAND
if (!gUseWaylandVsync) {
return;
}
if (!mWaylandVsyncSource) {
mWaylandVsyncSource = new mozilla::WaylandVsyncSource(mContainer);
WaylandVsyncSource::WaylandDisplay& display =
static_cast<WaylandVsyncSource::WaylandDisplay&>(
mWaylandVsyncSource->GetGlobalDisplay());
if (!display.Setup()) {
NS_WARNING("Could not start Wayland vsync monitor");
}
}
// The widget is going to be shown, so reconfigure the surface
// of our vsync source.
RefPtr<nsWindow> self(this);
moz_container_add_initial_draw_callback(mContainer, [self]() -> void {
WaylandVsyncSource::WaylandDisplay& display =
static_cast<WaylandVsyncSource::WaylandDisplay&>(
self->mWaylandVsyncSource->GetGlobalDisplay());
display.EnableMonitor();
if (display.IsVsyncEnabled()) {
display.Notify();
}
});
#endif
}
void nsWindow::WaylandStopVsync() {
#ifdef MOZ_WAYLAND
if (mWaylandVsyncSource) {
// The widget is going to be hidden, so clear the surface of our
// vsync source.
WaylandVsyncSource::WaylandDisplay& display =
static_cast<WaylandVsyncSource::WaylandDisplay&>(
mWaylandVsyncSource->GetGlobalDisplay());
display.DisableMonitor();
}
#endif
}
void nsWindow::NativeShow(bool aAction) {
if (aAction) {
// unset our flag now that our window has been shown
@ -4433,6 +4500,9 @@ void nsWindow::NativeShow(bool aAction) {
}
}
gtk_widget_show(mShell);
if (!mIsX11Display) {
WaylandStartVsync();
}
} else if (mContainer) {
gtk_widget_show(GTK_WIDGET(mContainer));
} else if (mGdkWindow) {
@ -4440,6 +4510,7 @@ void nsWindow::NativeShow(bool aAction) {
}
} else {
if (!mIsX11Display) {
WaylandStopVsync();
if (IsWaylandPopup() && IsMainMenuWindow()) {
CleanupWaylandPopups();
}
@ -5318,6 +5389,16 @@ already_AddRefed<nsIScreen> nsWindow::GetWidgetScreen() {
return screen.forget();
}
RefPtr<VsyncSource> nsWindow::GetVsyncSource() {
#ifdef MOZ_WAYLAND
if (mWaylandVsyncSource) {
return mWaylandVsyncSource;
}
#endif
return nullptr;
}
static bool IsFullscreenSupported(GtkWidget* aShell) {
#ifdef MOZ_X11
GdkScreen* screen = gtk_widget_get_screen(aShell);
@ -6310,7 +6391,8 @@ static void drag_data_received_event_cb(GtkWidget* aWidget,
static nsresult initialize_prefs(void) {
gRaiseWindows =
Preferences::GetBool("mozilla.widget.raise-on-setfocus", true);
gUseWaylandVsync =
Preferences::GetBool("widget.wayland_vsync.enabled", false);
return NS_OK;
}
@ -7250,9 +7332,14 @@ void nsWindow::GetCompositorWidgetInitData(
#ifdef MOZ_WAYLAND
wl_surface* nsWindow::GetWaylandSurface() {
if (mContainer)
return moz_container_get_wl_surface(MOZ_CONTAINER(mContainer),
GdkScaleFactor());
if (mContainer) {
struct wl_surface* surface = moz_container_get_wl_surface(
MOZ_CONTAINER(mContainer));
if (surface != NULL) {
wl_surface_set_buffer_scale(surface, GdkScaleFactor());
}
return surface;
}
NS_WARNING(
"nsWindow::GetWaylandSurfaces(): We don't have any mContainer for "

Просмотреть файл

@ -16,6 +16,8 @@
#endif /* MOZ_X11 */
#ifdef MOZ_WAYLAND
# include <gdk/gdkwayland.h>
# include "base/thread.h"
# include "WaylandVsyncSource.h"
#endif
#include "mozcontainer.h"
#include "mozilla/RefPtr.h"
@ -83,6 +85,7 @@ class gfxPattern;
namespace mozilla {
class TimeStamp;
class CurrentX11TimeGetter;
} // namespace mozilla
class nsWindow final : public nsBaseWidget {
@ -230,6 +233,8 @@ class nsWindow final : public nsBaseWidget {
void SetEGLNativeWindowSize(const LayoutDeviceIntSize& aEGLWindowSize);
#endif
RefPtr<mozilla::gfx::VsyncSource> GetVsyncSource() override;
private:
void UpdateAlpha(mozilla::gfx::SourceSurface* aSourceSurface,
nsIntRect aBoundsRect);
@ -254,6 +259,9 @@ class nsWindow final : public nsBaseWidget {
void MaybeResumeCompositor();
#endif
void WaylandStartVsync();
void WaylandStopVsync();
public:
void ThemeChanged(void);
void OnDPIChanged(void);
@ -354,6 +362,7 @@ class nsWindow final : public nsBaseWidget {
wl_display* GetWaylandDisplay();
wl_surface* GetWaylandSurface();
bool WaylandSurfaceNeedsClear();
virtual void CreateCompositorVsyncDispatcher() override;
#endif
virtual void GetCompositorWidgetInitData(
mozilla::widget::CompositorWidgetInitData* aInitData) override;
@ -521,6 +530,9 @@ class nsWindow final : public nsBaseWidget {
int mXDepth;
mozilla::widget::WindowSurfaceProvider mSurfaceProvider;
#endif
#ifdef MOZ_WAYLAND
RefPtr<mozilla::gfx::VsyncSource> mWaylandVsyncSource;
#endif
// Upper bound on pending ConfigureNotify events to be dispatched to the
// window. See bug 1225044.

Просмотреть файл

@ -1180,7 +1180,9 @@ void nsBaseWidget::CreateCompositorVsyncDispatcher() {
MakeUnique<Mutex>("mCompositorVsyncDispatcherLock");
}
MutexAutoLock lock(*mCompositorVsyncDispatcherLock.get());
mCompositorVsyncDispatcher = new CompositorVsyncDispatcher();
if (!mCompositorVsyncDispatcher) {
mCompositorVsyncDispatcher = new CompositorVsyncDispatcher();
}
}
}

Просмотреть файл

@ -214,7 +214,7 @@ class nsBaseWidget : public nsIWidget, public nsSupportsWeakReference {
already_AddRefed<mozilla::CompositorVsyncDispatcher>
GetCompositorVsyncDispatcher();
void CreateCompositorVsyncDispatcher();
virtual void CreateCompositorVsyncDispatcher();
virtual void CreateCompositor();
virtual void CreateCompositor(int aWidth, int aHeight);
virtual void SetCompositorWidgetDelegate(CompositorWidgetDelegate* delegate) {

Просмотреть файл

@ -30,6 +30,7 @@
#include "mozilla/TimeStamp.h"
#include "mozilla/gfx/Point.h"
#include "mozilla/widget/IMEData.h"
#include "VsyncSource.h"
#include "nsDataHashtable.h"
#include "nsIObserver.h"
#include "nsIWidgetListener.h"
@ -2024,6 +2025,12 @@ class nsIWidget : public nsISupports {
*/
virtual CompositorBridgeChild* GetRemoteRenderer() { return nullptr; }
/**
* If this widget has its own vsync source, return it, otherwise return
* nullptr. An example of such local source would be Wayland frame callbacks.
*/
virtual RefPtr<mozilla::gfx::VsyncSource> GetVsyncSource() { return nullptr; }
/**
* Returns true if the widget requires synchronous repaints on resize,
* false otherwise.