Bug 1759234 - Merge VsyncSource::Display into VsyncSource. r=smaug

Every `VsyncSource` currently only has a single `Display` associated with it.
This means that we're not making use of the `Display` abstraction at all.
This patch gets rid of `Display` by merging it into `VsyncSource`.

Originally, the intention of the `Display` abstraction was to use it for
per-monitor vsync. There would be one software `VsyncSource` and one hardware
`VsyncSource`, and the hardware `VsyncSource` would have one `Display` per
screen. But in reality, things have played out differently: The only platform
with per-monitor vsync is currently Linux Wayland, which has per-**widget**
vsync. And it has chosen to have one `VsyncSource` per widget, with a single
`Display` each.

For the macOS implementation of per-monitor vsync, I think it also makes
sense to have one `VsyncSource` per screen.

We already need to handle switching between VsyncSources, for switching
between software and hardware vsync, if the pref `layout.frame_rate` is
changed. So we might as well reuse that same switching capability for
switching between screens, when a window moves between screens or when a
tab moves between windows on different screens.

Differential Revision: https://phabricator.services.mozilla.com/D140891
This commit is contained in:
Markus Stange 2022-03-15 18:13:56 +00:00
Родитель 7961a431ef
Коммит 47c6beb1ee
20 изменённых файлов: 917 добавлений и 1134 удалений

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

@ -60,7 +60,7 @@ void VsyncParent::DispatchVsyncEvent(const VsyncEvent& aVsync) {
// NotifyVsync(). We use mObservingVsync and mDestroyed flags to skip this
// notification.
if (mObservingVsync && !mDestroyed) {
TimeDuration vsyncRate = mVsyncSource->GetGlobalDisplay().GetVsyncRate();
TimeDuration vsyncRate = mVsyncSource->GetVsyncRate();
Unused << SendNotify(aVsync, vsyncRate.ToMilliseconds());
}
}

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

@ -130,10 +130,8 @@ CompositorManagerChild::CreateWidgetCompositorBridge(
return nullptr;
}
TimeDuration vsyncRate = gfxPlatform::GetPlatform()
->GetHardwareVsync()
->GetGlobalDisplay()
.GetVsyncRate();
TimeDuration vsyncRate =
gfxPlatform::GetPlatform()->GetHardwareVsync()->GetVsyncRate();
CompositorBridgeOptions options = WidgetCompositorOptions(
aScale, vsyncRate, aOptions, aUseExternalSurfaceSize, aSurfaceSize,

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

@ -104,10 +104,8 @@ CompositorManagerParent::CreateSameProcessWidgetCompositorBridge(
return nullptr;
}
TimeDuration vsyncRate = gfxPlatform::GetPlatform()
->GetHardwareVsync()
->GetGlobalDisplay()
.GetVsyncRate();
TimeDuration vsyncRate =
gfxPlatform::GetPlatform()->GetHardwareVsync()->GetVsyncRate();
RefPtr<CompositorBridgeParent> bridge = new CompositorBridgeParent(
sInstance, aScale, vsyncRate, aOptions, aUseExternalSurfaceSize,

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

@ -100,23 +100,21 @@ static void FlushMainThreadLoop() {
// Tests that we can enable/disable vsync notifications
TEST_F(VsyncTester, EnableVsync) {
VsyncSource::Display& globalDisplay = mVsyncSource->GetGlobalDisplay();
globalDisplay.DisableVsync();
ASSERT_FALSE(globalDisplay.IsVsyncEnabled());
mVsyncSource->DisableVsync();
ASSERT_FALSE(mVsyncSource->IsVsyncEnabled());
globalDisplay.EnableVsync();
ASSERT_TRUE(globalDisplay.IsVsyncEnabled());
mVsyncSource->EnableVsync();
ASSERT_TRUE(mVsyncSource->IsVsyncEnabled());
globalDisplay.DisableVsync();
ASSERT_FALSE(globalDisplay.IsVsyncEnabled());
mVsyncSource->DisableVsync();
ASSERT_FALSE(mVsyncSource->IsVsyncEnabled());
}
// Test that if we have vsync enabled, the display should get vsync
// Test that if we have vsync enabled, the source should get vsync
// notifications
TEST_F(VsyncTester, CompositorGetVsyncNotifications) {
VsyncSource::Display& globalDisplay = mVsyncSource->GetGlobalDisplay();
globalDisplay.DisableVsync();
ASSERT_FALSE(globalDisplay.IsVsyncEnabled());
mVsyncSource->DisableVsync();
ASSERT_FALSE(mVsyncSource->IsVsyncEnabled());
RefPtr<CompositorVsyncDispatcher> vsyncDispatcher =
new CompositorVsyncDispatcher();
@ -124,7 +122,7 @@ TEST_F(VsyncTester, CompositorGetVsyncNotifications) {
vsyncDispatcher->SetCompositorVsyncObserver(testVsyncObserver);
FlushMainThreadLoop();
ASSERT_TRUE(globalDisplay.IsVsyncEnabled());
ASSERT_TRUE(mVsyncSource->IsVsyncEnabled());
testVsyncObserver->WaitForVsyncNotification();
ASSERT_TRUE(testVsyncObserver->DidGetVsyncNotification());
@ -132,23 +130,22 @@ TEST_F(VsyncTester, CompositorGetVsyncNotifications) {
vsyncDispatcher = nullptr;
testVsyncObserver = nullptr;
globalDisplay.DisableVsync();
ASSERT_FALSE(globalDisplay.IsVsyncEnabled());
mVsyncSource->DisableVsync();
ASSERT_FALSE(mVsyncSource->IsVsyncEnabled());
}
// Test that child refresh vsync observers get vsync notifications
TEST_F(VsyncTester, ChildRefreshDriverGetVsyncNotifications) {
VsyncSource::Display& globalDisplay = mVsyncSource->GetGlobalDisplay();
globalDisplay.DisableVsync();
ASSERT_FALSE(globalDisplay.IsVsyncEnabled());
mVsyncSource->DisableVsync();
ASSERT_FALSE(mVsyncSource->IsVsyncEnabled());
RefPtr<RefreshTimerVsyncDispatcher> vsyncDispatcher =
globalDisplay.GetRefreshTimerVsyncDispatcher();
mVsyncSource->GetRefreshTimerVsyncDispatcher();
ASSERT_TRUE(vsyncDispatcher != nullptr);
RefPtr<TestVsyncObserver> testVsyncObserver = new TestVsyncObserver();
vsyncDispatcher->AddVsyncObserver(testVsyncObserver);
ASSERT_TRUE(globalDisplay.IsVsyncEnabled());
ASSERT_TRUE(mVsyncSource->IsVsyncEnabled());
testVsyncObserver->WaitForVsyncNotification();
ASSERT_TRUE(testVsyncObserver->DidGetVsyncNotification());
@ -161,17 +158,16 @@ TEST_F(VsyncTester, ChildRefreshDriverGetVsyncNotifications) {
vsyncDispatcher = nullptr;
testVsyncObserver = nullptr;
globalDisplay.DisableVsync();
ASSERT_FALSE(globalDisplay.IsVsyncEnabled());
mVsyncSource->DisableVsync();
ASSERT_FALSE(mVsyncSource->IsVsyncEnabled());
}
// Test that we can read the vsync rate
TEST_F(VsyncTester, VsyncSourceHasVsyncRate) {
VsyncSource::Display& globalDisplay = mVsyncSource->GetGlobalDisplay();
TimeDuration vsyncRate = globalDisplay.GetVsyncRate();
TimeDuration vsyncRate = mVsyncSource->GetVsyncRate();
ASSERT_NE(vsyncRate, TimeDuration::Forever());
ASSERT_GT(vsyncRate.ToMilliseconds(), 0);
globalDisplay.DisableVsync();
ASSERT_FALSE(globalDisplay.IsVsyncEnabled());
mVsyncSource->DisableVsync();
ASSERT_FALSE(mVsyncSource->IsVsyncEnabled());
}

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

@ -11,16 +11,7 @@
using namespace mozilla;
SoftwareVsyncSource::SoftwareVsyncSource(bool aInit) {
MOZ_ASSERT(NS_IsMainThread());
if (aInit) {
mGlobalDisplay = new SoftwareDisplay();
}
}
SoftwareVsyncSource::~SoftwareVsyncSource() { MOZ_ASSERT(NS_IsMainThread()); }
SoftwareDisplay::SoftwareDisplay() : mVsyncEnabled(false) {
SoftwareVsyncSource::SoftwareVsyncSource() : mVsyncEnabled(false) {
// Mimic 60 fps
MOZ_ASSERT(NS_IsMainThread());
const double rate = 1000.0 / (double)gfxPlatform::GetSoftwareVsyncRate();
@ -30,7 +21,7 @@ SoftwareDisplay::SoftwareDisplay() : mVsyncEnabled(false) {
"GFX: Could not start software vsync thread");
}
SoftwareDisplay::~SoftwareDisplay() {
SoftwareVsyncSource::~SoftwareVsyncSource() {
MOZ_ASSERT(NS_IsMainThread());
if (mVsyncThread) {
mVsyncThread->Stop();
@ -38,7 +29,7 @@ SoftwareDisplay::~SoftwareDisplay() {
}
};
void SoftwareDisplay::EnableVsync() {
void SoftwareVsyncSource::EnableVsync() {
MOZ_ASSERT(mVsyncThread->IsRunning());
if (NS_IsMainThread()) {
if (mVsyncEnabled) {
@ -46,8 +37,9 @@ void SoftwareDisplay::EnableVsync() {
}
mVsyncEnabled = true;
mVsyncThread->message_loop()->PostTask(NewRunnableMethod(
"SoftwareDisplay::EnableVsync", this, &SoftwareDisplay::EnableVsync));
mVsyncThread->message_loop()->PostTask(
NewRunnableMethod("SoftwareVsyncSource::EnableVsync", this,
&SoftwareVsyncSource::EnableVsync));
return;
}
@ -57,7 +49,7 @@ void SoftwareDisplay::EnableVsync() {
NotifyVsync(vsyncTime, outputTime);
}
void SoftwareDisplay::DisableVsync() {
void SoftwareVsyncSource::DisableVsync() {
MOZ_ASSERT(mVsyncThread->IsRunning());
if (NS_IsMainThread()) {
if (!mVsyncEnabled) {
@ -65,8 +57,9 @@ void SoftwareDisplay::DisableVsync() {
}
mVsyncEnabled = false;
mVsyncThread->message_loop()->PostTask(NewRunnableMethod(
"SoftwareDisplay::DisableVsync", this, &SoftwareDisplay::DisableVsync));
mVsyncThread->message_loop()->PostTask(
NewRunnableMethod("SoftwareVsyncSource::DisableVsync", this,
&SoftwareVsyncSource::DisableVsync));
return;
}
@ -77,17 +70,18 @@ void SoftwareDisplay::DisableVsync() {
}
}
bool SoftwareDisplay::IsVsyncEnabled() {
bool SoftwareVsyncSource::IsVsyncEnabled() {
MOZ_ASSERT(NS_IsMainThread());
return mVsyncEnabled;
}
bool SoftwareDisplay::IsInSoftwareVsyncThread() {
bool SoftwareVsyncSource::IsInSoftwareVsyncThread() {
return mVsyncThread->thread_id() == PlatformThread::CurrentId();
}
void SoftwareDisplay::NotifyVsync(const mozilla::TimeStamp& aVsyncTimestamp,
const mozilla::TimeStamp& aOutputTimestamp) {
void SoftwareVsyncSource::NotifyVsync(
const mozilla::TimeStamp& aVsyncTimestamp,
const mozilla::TimeStamp& aOutputTimestamp) {
MOZ_ASSERT(IsInSoftwareVsyncThread());
mozilla::TimeStamp displayVsyncTime = aVsyncTimestamp;
@ -101,16 +95,17 @@ void SoftwareDisplay::NotifyVsync(const mozilla::TimeStamp& aVsyncTimestamp,
displayVsyncTime = now;
}
Display::NotifyVsync(displayVsyncTime, aOutputTimestamp);
VsyncSource::NotifyVsync(displayVsyncTime, aOutputTimestamp);
// Prevent skew by still scheduling based on the original
// vsync timestamp
ScheduleNextVsync(aVsyncTimestamp);
}
mozilla::TimeDuration SoftwareDisplay::GetVsyncRate() { return mVsyncRate; }
mozilla::TimeDuration SoftwareVsyncSource::GetVsyncRate() { return mVsyncRate; }
void SoftwareDisplay::ScheduleNextVsync(mozilla::TimeStamp aVsyncTimestamp) {
void SoftwareVsyncSource::ScheduleNextVsync(
mozilla::TimeStamp aVsyncTimestamp) {
MOZ_ASSERT(IsInSoftwareVsyncThread());
mozilla::TimeStamp nextVsync = aVsyncTimestamp + mVsyncRate;
mozilla::TimeDuration delay = nextVsync - mozilla::TimeStamp::Now();
@ -123,15 +118,15 @@ void SoftwareDisplay::ScheduleNextVsync(mozilla::TimeStamp aVsyncTimestamp) {
mCurrentVsyncTask =
NewCancelableRunnableMethod<mozilla::TimeStamp, mozilla::TimeStamp>(
"SoftwareDisplay::NotifyVsync", this, &SoftwareDisplay::NotifyVsync,
nextVsync, outputTime);
"SoftwareVsyncSource::NotifyVsync", this,
&SoftwareVsyncSource::NotifyVsync, nextVsync, outputTime);
RefPtr<Runnable> addrefedTask = mCurrentVsyncTask;
mVsyncThread->message_loop()->PostDelayedTask(addrefedTask.forget(),
delay.ToMilliseconds());
}
void SoftwareDisplay::Shutdown() {
void SoftwareVsyncSource::Shutdown() {
MOZ_ASSERT(NS_IsMainThread());
DisableVsync();
mVsyncThread->Stop();

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

@ -14,9 +14,14 @@
#include "nsISupportsImpl.h"
#include "VsyncSource.h"
class SoftwareDisplay : public mozilla::gfx::VsyncSource::Display {
// Fallback option to use a software timer to mimic vsync. Useful for gtests
// To mimic a hardware vsync thread, we create a dedicated software timer
// vsync thread.
class SoftwareVsyncSource : public mozilla::gfx::VsyncSource {
public:
SoftwareDisplay();
explicit SoftwareVsyncSource();
virtual ~SoftwareVsyncSource();
void EnableVsync() override;
void DisableVsync() override;
bool IsVsyncEnabled() override;
@ -27,8 +32,6 @@ class SoftwareDisplay : public mozilla::gfx::VsyncSource::Display {
void ScheduleNextVsync(mozilla::TimeStamp aVsyncTimestamp);
void Shutdown() override;
virtual ~SoftwareDisplay();
protected:
mozilla::TimeDuration mVsyncRate;
// Use a chromium thread because nsITimers* fire on the main thread
@ -36,23 +39,6 @@ class SoftwareDisplay : public mozilla::gfx::VsyncSource::Display {
RefPtr<mozilla::CancelableRunnable>
mCurrentVsyncTask; // only access on vsync thread
bool mVsyncEnabled; // Only access on main thread
}; // SoftwareDisplay
// Fallback option to use a software timer to mimic vsync. Useful for gtests
// To mimic a hardware vsync thread, we create a dedicated software timer
// vsync thread.
class SoftwareVsyncSource : public mozilla::gfx::VsyncSource {
public:
explicit SoftwareVsyncSource(bool aInit = true);
virtual ~SoftwareVsyncSource();
Display& GetGlobalDisplay() override {
MOZ_ASSERT(mGlobalDisplay != nullptr);
return *mGlobalDisplay;
}
protected:
RefPtr<SoftwareDisplay> mGlobalDisplay;
};
#endif /* GFX_SOFTWARE_VSYNC_SOURCE_H */

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

@ -17,68 +17,7 @@
namespace mozilla {
namespace gfx {
void VsyncSource::EnableCompositorVsyncDispatcher(
CompositorVsyncDispatcher* aCompositorVsyncDispatcher) {
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(NS_IsMainThread());
// Just use the global display until we have enough information to get the
// corresponding display for compositor.
GetGlobalDisplay().EnableCompositorVsyncDispatcher(
aCompositorVsyncDispatcher);
}
void VsyncSource::DisableCompositorVsyncDispatcher(
CompositorVsyncDispatcher* aCompositorVsyncDispatcher) {
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(NS_IsMainThread());
// See also EnableCompositorVsyncDispatcher().
GetGlobalDisplay().DisableCompositorVsyncDispatcher(
aCompositorVsyncDispatcher);
}
void VsyncSource::RegisterCompositorVsyncDispatcher(
CompositorVsyncDispatcher* aCompositorVsyncDispatcher) {
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(NS_IsMainThread());
GetGlobalDisplay().RegisterCompositorVsyncDispatcher(
aCompositorVsyncDispatcher);
}
void VsyncSource::DeregisterCompositorVsyncDispatcher(
CompositorVsyncDispatcher* aCompositorVsyncDispatcher) {
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(NS_IsMainThread());
GetGlobalDisplay().DeregisterCompositorVsyncDispatcher(
aCompositorVsyncDispatcher);
}
void VsyncSource::AddGenericObserver(VsyncObserver* aObserver) {
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(NS_IsMainThread());
GetGlobalDisplay().AddGenericObserver(aObserver);
}
void VsyncSource::RemoveGenericObserver(VsyncObserver* aObserver) {
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(NS_IsMainThread());
GetGlobalDisplay().RemoveGenericObserver(aObserver);
}
void VsyncSource::MoveListenersToNewSource(
const RefPtr<VsyncSource>& aNewSource) {
GetGlobalDisplay().MoveListenersToNewSource(aNewSource);
}
RefPtr<RefreshTimerVsyncDispatcher>
VsyncSource::GetRefreshTimerVsyncDispatcher() {
MOZ_ASSERT(XRE_IsParentProcess());
// See also AddCompositorVsyncDispatcher().
return GetGlobalDisplay().GetRefreshTimerVsyncDispatcher();
}
VsyncSource::Display::Display()
VsyncSource::VsyncSource()
: mDispatcherLock("display dispatcher lock"),
mRefreshTimerNeedsVsync(false),
mHasGenericObservers(false) {
@ -86,7 +25,7 @@ VsyncSource::Display::Display()
mRefreshTimerVsyncDispatcher = new RefreshTimerVsyncDispatcher(this);
}
VsyncSource::Display::~Display() {
VsyncSource::~VsyncSource() {
MOZ_ASSERT(NS_IsMainThread());
MutexAutoLock lock(mDispatcherLock);
mRefreshTimerVsyncDispatcher = nullptr;
@ -94,8 +33,8 @@ VsyncSource::Display::~Display() {
MOZ_ASSERT(mEnabledCompositorVsyncDispatchers.Length() == 0);
}
void VsyncSource::Display::NotifyVsync(const TimeStamp& aVsyncTimestamp,
const TimeStamp& aOutputTimestamp) {
void VsyncSource::NotifyVsync(const TimeStamp& aVsyncTimestamp,
const TimeStamp& aOutputTimestamp) {
// Called on the vsync thread
MutexAutoLock lock(mDispatcherLock);
@ -126,12 +65,12 @@ void VsyncSource::Display::NotifyVsync(const TimeStamp& aVsyncTimestamp,
if (dispatchToMainThread) {
mLastVsyncIdSentToMainThread = mVsyncId;
NS_DispatchToMainThread(NewRunnableMethod<VsyncEvent>(
"VsyncSource::Display::NotifyGenericObservers", this,
&VsyncSource::Display::NotifyGenericObservers, event));
"VsyncSource::NotifyGenericObservers", this,
&VsyncSource::NotifyGenericObservers, event));
}
}
void VsyncSource::Display::NotifyGenericObservers(VsyncEvent aEvent) {
void VsyncSource::NotifyGenericObservers(VsyncEvent aEvent) {
MOZ_ASSERT(NS_IsMainThread());
for (size_t i = 0; i < mGenericObservers.Length(); i++) {
mGenericObservers[i]->NotifyVsync(aEvent);
@ -143,12 +82,12 @@ void VsyncSource::Display::NotifyGenericObservers(VsyncEvent aEvent) {
}
}
TimeDuration VsyncSource::Display::GetVsyncRate() {
TimeDuration VsyncSource::GetVsyncRate() {
// If hardware queries fail / are unsupported, we have to just guess.
return TimeDuration::FromMilliseconds(1000.0 / 60.0);
}
void VsyncSource::Display::RegisterCompositorVsyncDispatcher(
void VsyncSource::RegisterCompositorVsyncDispatcher(
CompositorVsyncDispatcher* aCompositorVsyncDispatcher) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aCompositorVsyncDispatcher);
@ -159,7 +98,7 @@ void VsyncSource::Display::RegisterCompositorVsyncDispatcher(
}
}
void VsyncSource::Display::DeregisterCompositorVsyncDispatcher(
void VsyncSource::DeregisterCompositorVsyncDispatcher(
CompositorVsyncDispatcher* aCompositorVsyncDispatcher) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aCompositorVsyncDispatcher);
@ -170,7 +109,7 @@ void VsyncSource::Display::DeregisterCompositorVsyncDispatcher(
}
}
void VsyncSource::Display::EnableCompositorVsyncDispatcher(
void VsyncSource::EnableCompositorVsyncDispatcher(
CompositorVsyncDispatcher* aCompositorVsyncDispatcher) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aCompositorVsyncDispatcher);
@ -185,7 +124,7 @@ void VsyncSource::Display::EnableCompositorVsyncDispatcher(
UpdateVsyncStatus();
}
void VsyncSource::Display::DisableCompositorVsyncDispatcher(
void VsyncSource::DisableCompositorVsyncDispatcher(
CompositorVsyncDispatcher* aCompositorVsyncDispatcher) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aCompositorVsyncDispatcher);
@ -200,7 +139,7 @@ void VsyncSource::Display::DisableCompositorVsyncDispatcher(
UpdateVsyncStatus();
}
void VsyncSource::Display::AddGenericObserver(VsyncObserver* aObserver) {
void VsyncSource::AddGenericObserver(VsyncObserver* aObserver) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aObserver);
mGenericObservers.AppendElement(aObserver);
@ -208,7 +147,7 @@ void VsyncSource::Display::AddGenericObserver(VsyncObserver* aObserver) {
UpdateVsyncStatus();
}
void VsyncSource::Display::RemoveGenericObserver(VsyncObserver* aObserver) {
void VsyncSource::RemoveGenericObserver(VsyncObserver* aObserver) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aObserver);
mGenericObservers.RemoveElement(aObserver);
@ -216,36 +155,35 @@ void VsyncSource::Display::RemoveGenericObserver(VsyncObserver* aObserver) {
UpdateVsyncStatus();
}
void VsyncSource::Display::MoveListenersToNewSource(
void VsyncSource::MoveListenersToNewSource(
const RefPtr<VsyncSource>& aNewSource) {
MOZ_ASSERT(NS_IsMainThread());
VsyncSource::Display& aNewDisplay = aNewSource->GetGlobalDisplay();
MutexAutoLock lock(mDispatcherLock);
MutexAutoLock newLock(aNewDisplay.mDispatcherLock);
aNewDisplay.mRegisteredCompositorVsyncDispatchers.AppendElements(
MutexAutoLock newLock(aNewSource->mDispatcherLock);
aNewSource->mRegisteredCompositorVsyncDispatchers.AppendElements(
std::move(mRegisteredCompositorVsyncDispatchers));
aNewDisplay.mEnabledCompositorVsyncDispatchers.AppendElements(
aNewSource->mEnabledCompositorVsyncDispatchers.AppendElements(
std::move(mEnabledCompositorVsyncDispatchers));
aNewDisplay.mGenericObservers.AppendElements(std::move(mGenericObservers));
aNewSource->mGenericObservers.AppendElements(std::move(mGenericObservers));
for (size_t i = 0;
i < aNewDisplay.mRegisteredCompositorVsyncDispatchers.Length(); i++) {
aNewDisplay.mRegisteredCompositorVsyncDispatchers[i]->MoveToSource(
i < aNewSource->mRegisteredCompositorVsyncDispatchers.Length(); i++) {
aNewSource->mRegisteredCompositorVsyncDispatchers[i]->MoveToSource(
aNewSource);
}
aNewDisplay.mRefreshTimerVsyncDispatcher = mRefreshTimerVsyncDispatcher;
mRefreshTimerVsyncDispatcher->MoveToDisplay(&aNewDisplay);
aNewSource->mRefreshTimerVsyncDispatcher = mRefreshTimerVsyncDispatcher;
mRefreshTimerVsyncDispatcher->MoveToSource(aNewSource);
mRefreshTimerVsyncDispatcher = nullptr;
}
void VsyncSource::Display::NotifyRefreshTimerVsyncStatus(bool aEnable) {
void VsyncSource::NotifyRefreshTimerVsyncStatus(bool aEnable) {
MOZ_ASSERT(NS_IsMainThread());
mRefreshTimerNeedsVsync = aEnable;
UpdateVsyncStatus();
}
void VsyncSource::Display::UpdateVsyncStatus() {
void VsyncSource::UpdateVsyncStatus() {
MOZ_ASSERT(NS_IsMainThread());
// WARNING: This function SHOULD NOT BE CALLED WHILE HOLDING LOCKS
// NotifyVsync grabs a lock to dispatch vsync events
@ -272,7 +210,7 @@ void VsyncSource::Display::UpdateVsyncStatus() {
}
RefPtr<RefreshTimerVsyncDispatcher>
VsyncSource::Display::GetRefreshTimerVsyncDispatcher() {
VsyncSource::GetRefreshTimerVsyncDispatcher() {
return mRefreshTimerVsyncDispatcher;
}
@ -285,8 +223,8 @@ Maybe<TimeDuration> VsyncSource::GetFastestVsyncRate() {
mozilla::gfx::VsyncSource* vsyncSource =
gfxPlatform::GetPlatform()->GetHardwareVsync();
if (vsyncSource && vsyncSource->GetGlobalDisplay().IsVsyncEnabled()) {
retVal.emplace(vsyncSource->GetGlobalDisplay().GetVsyncRate());
if (vsyncSource && vsyncSource->IsVsyncEnabled()) {
retVal.emplace(vsyncSource->GetVsyncRate());
}
#ifdef MOZ_WAYLAND
@ -303,7 +241,5 @@ Maybe<TimeDuration> VsyncSource::GetFastestVsyncRate() {
return retVal;
}
void VsyncSource::Shutdown() { GetGlobalDisplay().Shutdown(); }
} // namespace gfx
} // namespace mozilla

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

@ -34,74 +34,26 @@ class VsyncSource {
typedef mozilla::CompositorVsyncDispatcher CompositorVsyncDispatcher;
public:
// Controls vsync unique to each display and unique on each platform
class Display {
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Display)
public:
Display();
VsyncSource();
// Notified when this display's vsync callback occurs, on the vsync thread
// Different platforms give different aVsyncTimestamp values.
// macOS: TimeStamp::Now() or the output time of the previous vsync
// callback, whichever is older.
// Windows: It's messy, see gfxWindowsPlatform.
// Android: TODO
//
// @param aVsyncTimestamp The time of the Vsync that just occured. Needs to
// be at or before the time of the NotifyVsync call.
// @param aOutputTimestamp The estimated timestamp at which drawing will
// appear on the screen, if the drawing happens within a certain
// (unknown) budget. Useful for Audio/Video sync. On platforms where
// this timestamp is provided by the system (macOS), it is a much more
// stable and consistent timing source than the time at which the vsync
// callback is called.
virtual void NotifyVsync(const TimeStamp& aVsyncTimestamp,
const TimeStamp& aOutputTimestamp);
void NotifyGenericObservers(VsyncEvent aEvent);
RefPtr<RefreshTimerVsyncDispatcher> GetRefreshTimerVsyncDispatcher();
void RegisterCompositorVsyncDispatcher(
CompositorVsyncDispatcher* aCompositorVsyncDispatcher);
void DeregisterCompositorVsyncDispatcher(
CompositorVsyncDispatcher* aCompositorVsyncDispatcher);
void EnableCompositorVsyncDispatcher(
CompositorVsyncDispatcher* aCompositorVsyncDispatcher);
void DisableCompositorVsyncDispatcher(
CompositorVsyncDispatcher* aCompositorVsyncDispatcher);
void AddGenericObserver(VsyncObserver* aObserver);
void RemoveGenericObserver(VsyncObserver* aObserver);
void MoveListenersToNewSource(const RefPtr<VsyncSource>& aNewSource);
void NotifyRefreshTimerVsyncStatus(bool aEnable);
virtual TimeDuration GetVsyncRate();
// These should all only be called on the main thread
virtual void EnableVsync() = 0;
virtual void DisableVsync() = 0;
virtual bool IsVsyncEnabled() = 0;
virtual void Shutdown() = 0;
protected:
virtual ~Display();
private:
void UpdateVsyncStatus();
Mutex mDispatcherLock;
bool mRefreshTimerNeedsVsync;
nsTArray<RefPtr<CompositorVsyncDispatcher>>
mEnabledCompositorVsyncDispatchers;
nsTArray<RefPtr<CompositorVsyncDispatcher>>
mRegisteredCompositorVsyncDispatchers;
RefPtr<RefreshTimerVsyncDispatcher> mRefreshTimerVsyncDispatcher;
nsTArray<RefPtr<VsyncObserver>>
mGenericObservers; // can only be touched from the main thread
VsyncId mVsyncId;
VsyncId mLastVsyncIdSentToMainThread; // hold mDispatcherLock to touch
VsyncId mLastMainThreadProcessedVsyncId; // hold mDispatcherLock to touch
bool mHasGenericObservers; // hold mDispatcherLock to touch
};
// Notified when this display's vsync callback occurs, on the vsync thread
// Different platforms give different aVsyncTimestamp values.
// macOS: TimeStamp::Now() or the output time of the previous vsync
// callback, whichever is older.
// Windows: It's messy, see gfxWindowsPlatform.
// Android: TODO
//
// @param aVsyncTimestamp The time of the Vsync that just occured. Needs to
// be at or before the time of the NotifyVsync call.
// @param aOutputTimestamp The estimated timestamp at which drawing will
// appear on the screen, if the drawing happens within a certain
// (unknown) budget. Useful for Audio/Video sync. On platforms where
// this timestamp is provided by the system (macOS), it is a much more
// stable and consistent timing source than the time at which the vsync
// callback is called.
virtual void NotifyVsync(const TimeStamp& aVsyncTimestamp,
const TimeStamp& aOutputTimestamp);
void NotifyGenericObservers(VsyncEvent aEvent);
void EnableCompositorVsyncDispatcher(
CompositorVsyncDispatcher* aCompositorVsyncDispatcher);
@ -112,6 +64,15 @@ class VsyncSource {
void DeregisterCompositorVsyncDispatcher(
CompositorVsyncDispatcher* aCompositorVsyncDispatcher);
void NotifyRefreshTimerVsyncStatus(bool aEnable);
virtual TimeDuration GetVsyncRate();
// These should all only be called on the main thread
virtual void EnableVsync() = 0;
virtual void DisableVsync() = 0;
virtual bool IsVsyncEnabled() = 0;
virtual void Shutdown() = 0;
// Add and remove a generic observer for vsync. Note that keeping an observer
// registered means vsync will keep firing, which may impact power usage. So
// this is intended only for "short term" vsync observers. These methods must
@ -123,14 +84,29 @@ class VsyncSource {
void MoveListenersToNewSource(const RefPtr<VsyncSource>& aNewSource);
RefPtr<RefreshTimerVsyncDispatcher> GetRefreshTimerVsyncDispatcher();
virtual Display& GetGlobalDisplay() = 0; // Works across all displays
void Shutdown();
// Returns the rate of the fastest enabled VsyncSource::Display or Nothing().
// Returns the rate of the fastest enabled VsyncSource or Nothing().
static Maybe<TimeDuration> GetFastestVsyncRate();
protected:
virtual ~VsyncSource() = default;
virtual ~VsyncSource();
private:
void UpdateVsyncStatus();
Mutex mDispatcherLock;
bool mRefreshTimerNeedsVsync;
nsTArray<RefPtr<CompositorVsyncDispatcher>>
mEnabledCompositorVsyncDispatchers;
nsTArray<RefPtr<CompositorVsyncDispatcher>>
mRegisteredCompositorVsyncDispatchers;
RefPtr<RefreshTimerVsyncDispatcher> mRefreshTimerVsyncDispatcher;
nsTArray<RefPtr<VsyncObserver>>
mGenericObservers; // can only be touched from the main thread
VsyncId mVsyncId;
VsyncId mLastVsyncIdSentToMainThread; // hold mDispatcherLock to touch
VsyncId mLastMainThreadProcessedVsyncId; // hold mDispatcherLock to touch
bool mHasGenericObservers; // hold mDispatcherLock to touch
};
} // namespace gfx

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

@ -305,71 +305,57 @@ bool gfxAndroidPlatform::RequiresLinearZoom() {
return gfxPlatform::RequiresLinearZoom();
}
class AndroidVsyncSource final : public VsyncSource {
class AndroidVsyncSource final : public VsyncSource,
public widget::AndroidVsync::Observer {
public:
class Display final : public VsyncSource::Display,
public widget::AndroidVsync::Observer {
public:
Display() : mAndroidVsync(widget::AndroidVsync::GetInstance()) {}
~Display() { DisableVsync(); }
AndroidVsyncSource() : mAndroidVsync(widget::AndroidVsync::GetInstance()) {}
bool IsVsyncEnabled() override {
MOZ_ASSERT(NS_IsMainThread());
return mObservingVsync;
bool IsVsyncEnabled() override {
MOZ_ASSERT(NS_IsMainThread());
return mObservingVsync;
}
void EnableVsync() override {
MOZ_ASSERT(NS_IsMainThread());
if (mObservingVsync) {
return;
}
mAndroidVsync->RegisterObserver(this, widget::AndroidVsync::RENDER);
mObservingVsync = true;
}
void EnableVsync() override {
MOZ_ASSERT(NS_IsMainThread());
void DisableVsync() override {
MOZ_ASSERT(NS_IsMainThread());
if (mObservingVsync) {
return;
}
mAndroidVsync->RegisterObserver(this, widget::AndroidVsync::RENDER);
mObservingVsync = true;
if (!mObservingVsync) {
return;
}
mAndroidVsync->UnregisterObserver(this, widget::AndroidVsync::RENDER);
mObservingVsync = false;
}
void DisableVsync() override {
MOZ_ASSERT(NS_IsMainThread());
TimeDuration GetVsyncRate() override { return mAndroidVsync->GetVsyncRate(); }
if (!mObservingVsync) {
return;
}
mAndroidVsync->UnregisterObserver(this, widget::AndroidVsync::RENDER);
mObservingVsync = false;
}
void Shutdown() override { DisableVsync(); }
TimeDuration GetVsyncRate() override {
return mAndroidVsync->GetVsyncRate();
}
// Override for the widget::AndroidVsync::Observer method
void OnVsync(const TimeStamp& aTimeStamp) override {
// Use the timebase from the frame callback as the vsync time, unless it
// is in the future.
TimeStamp now = TimeStamp::Now();
TimeStamp vsyncTime = aTimeStamp < now ? aTimeStamp : now;
TimeStamp outputTime = vsyncTime + GetVsyncRate();
void Shutdown() override { DisableVsync(); }
// Override for the widget::AndroidVsync::Observer method
void OnVsync(const TimeStamp& aTimeStamp) override {
// Use the timebase from the frame callback as the vsync time, unless it
// is in the future.
TimeStamp now = TimeStamp::Now();
TimeStamp vsyncTime = aTimeStamp < now ? aTimeStamp : now;
TimeStamp outputTime = vsyncTime + GetVsyncRate();
NotifyVsync(vsyncTime, outputTime);
}
private:
RefPtr<widget::AndroidVsync> mAndroidVsync;
bool mObservingVsync = false;
TimeDuration mVsyncDuration;
};
Display& GetGlobalDisplay() final { return GetDisplayInstance(); }
NotifyVsync(vsyncTime, outputTime);
}
private:
virtual ~AndroidVsyncSource() = default;
virtual ~AndroidVsyncSource() { DisableVsync(); }
static Display& GetDisplayInstance() {
static RefPtr<Display> globalDisplay = new Display();
return *globalDisplay;
}
RefPtr<widget::AndroidVsync> mAndroidVsync;
bool mObservingVsync = false;
TimeDuration mVsyncDuration;
};
already_AddRefed<mozilla::gfx::VsyncSource>

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

@ -3125,8 +3125,8 @@ void gfxPlatform::NotifyFrameStats(nsTArray<FrameStats>&& aFrameStats) {
/*static*/
uint32_t gfxPlatform::TargetFrameRate() {
if (gPlatform && gPlatform->mVsyncSource) {
VsyncSource::Display& display = gPlatform->mVsyncSource->GetGlobalDisplay();
return round(1000.0 / display.GetVsyncRate().ToMilliseconds());
return round(1000.0 /
gPlatform->mVsyncSource->GetVsyncRate().ToMilliseconds());
}
return 0;
}

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

@ -664,317 +664,292 @@ bool gfxPlatformGtk::CheckVariationFontSupport() {
class GtkVsyncSource final : public VsyncSource {
public:
GtkVsyncSource() {
GtkVsyncSource()
: mGLContext(nullptr),
mXDisplay(nullptr),
mSetupLock("GLXVsyncSetupLock"),
mVsyncThread("GLXVsyncThread"),
mVsyncTask(nullptr),
mVsyncEnabledLock("GLXVsyncEnabledLock"),
mVsyncEnabled(false) {
MOZ_ASSERT(NS_IsMainThread());
mGlobalDisplay = new GLXDisplay();
}
virtual ~GtkVsyncSource() { MOZ_ASSERT(NS_IsMainThread()); }
virtual Display& GetGlobalDisplay() override { return *mGlobalDisplay; }
// 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;
class GLXDisplay final : public VsyncSource::Display {
public:
GLXDisplay()
: mGLContext(nullptr),
mXDisplay(nullptr),
mSetupLock("GLXVsyncSetupLock"),
mVsyncThread("GLXVsyncThread"),
mVsyncTask(nullptr),
mVsyncEnabledLock("GLXVsyncEnabledLock"),
mVsyncEnabled(false) {}
RefPtr<Runnable> vsyncSetup =
NewRunnableMethod("GtkVsyncSource::SetupGLContext", this,
&GtkVsyncSource::SetupGLContext);
mVsyncThread.message_loop()->PostTask(vsyncSetup.forget());
// Wait until the setup has completed.
lock.Wait();
return mGLContext != nullptr;
}
// 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<Runnable> vsyncSetup =
NewRunnableMethod("GtkVsyncSource::GLXDisplay::SetupGLContext", this,
&GLXDisplay::SetupGLContext);
mVsyncThread.message_loop()->PostTask(vsyncSetup.forget());
// Wait until the setup has completed.
lock.Wait();
return mGLContext != nullptr;
}
// 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
// main thread X display.
mXDisplay = XOpenDisplay(nullptr);
if (!mXDisplay) {
lock.NotifyAll();
return;
}
// Most compositors wait for vsync events on the root window.
Window root = DefaultRootWindow(mXDisplay);
int screen = DefaultScreen(mXDisplay);
ScopedXFree<GLXFBConfig> cfgs;
GLXFBConfig config;
int visid;
bool forWebRender = false;
if (!gl::GLContextGLX::FindFBConfigForWindow(
mXDisplay, screen, root, &cfgs, &config, &visid, forWebRender)) {
lock.NotifyAll();
return;
}
mGLContext = gl::GLContextGLX::CreateGLContext(
{}, gfx::XlibDisplay::Borrow(mXDisplay), root, config, false,
nullptr);
if (!mGLContext) {
lock.NotifyAll();
return;
}
mGLContext->MakeCurrent();
// Test that SGI_video_sync lets us get the counter.
unsigned int syncCounter = 0;
if (gl::sGLXLibrary.fGetVideoSync(&syncCounter) != 0) {
mGLContext = nullptr;
}
// 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
// main thread X display.
mXDisplay = XOpenDisplay(nullptr);
if (!mXDisplay) {
lock.NotifyAll();
return;
}
virtual void EnableVsync() override {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mGLContext, "GLContext not setup!");
// Most compositors wait for vsync events on the root window.
Window root = DefaultRootWindow(mXDisplay);
int screen = DefaultScreen(mXDisplay);
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("GtkVsyncSource::GLXDisplay::RunVsync",
this, &GLXDisplay::RunVsync);
RefPtr<Runnable> addrefedTask = mVsyncTask;
mVsyncThread.message_loop()->PostTask(addrefedTask.forget());
}
ScopedXFree<GLXFBConfig> cfgs;
GLXFBConfig config;
int visid;
bool forWebRender = false;
if (!gl::GLContextGLX::FindFBConfigForWindow(
mXDisplay, screen, root, &cfgs, &config, &visid, forWebRender)) {
lock.NotifyAll();
return;
}
virtual void DisableVsync() override {
MonitorAutoLock lock(mVsyncEnabledLock);
mVsyncEnabled = false;
mGLContext = gl::GLContextGLX::CreateGLContext(
{}, gfx::XlibDisplay::Borrow(mXDisplay), root, config, false, nullptr);
if (!mGLContext) {
lock.NotifyAll();
return;
}
virtual bool IsVsyncEnabled() override {
MonitorAutoLock lock(mVsyncEnabledLock);
return mVsyncEnabled;
}
virtual void Shutdown() override {
MOZ_ASSERT(NS_IsMainThread());
DisableVsync();
// Cleanup thread-specific resources before shutting down.
RefPtr<Runnable> shutdownTask = NewRunnableMethod(
"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() = default;
void RunVsync() {
MOZ_ASSERT(!NS_IsMainThread());
mGLContext->MakeCurrent();
unsigned int syncCounter = 0;
gl::sGLXLibrary.fGetVideoSync(&syncCounter);
for (;;) {
{
MonitorAutoLock lock(mVsyncEnabledLock);
if (!mVsyncEnabled) {
mVsyncTask = nullptr;
return;
}
}
TimeStamp lastVsync = TimeStamp::Now();
bool useSoftware = false;
// Wait until the video sync counter reaches the next value by waiting
// until the parity of the counter value changes.
unsigned int nextSync = syncCounter + 1;
int status;
if ((status = gl::sGLXLibrary.fWaitVideoSync(2, (int)nextSync % 2,
&syncCounter)) != 0) {
gfxWarningOnce() << "glXWaitVideoSync returned " << status;
useSoftware = true;
}
if (syncCounter == (nextSync - 1)) {
gfxWarningOnce()
<< "glXWaitVideoSync failed to increment the sync counter.";
useSoftware = true;
}
if (useSoftware) {
double remaining =
(1000.f / 60.f) - (TimeStamp::Now() - lastVsync).ToMilliseconds();
if (remaining > 0) {
AUTO_PROFILER_THREAD_SLEEP;
PlatformThread::Sleep((int)remaining);
}
}
lastVsync = TimeStamp::Now();
TimeStamp outputTime = lastVsync + GetVsyncRate();
NotifyVsync(lastVsync, outputTime);
}
}
void Cleanup() {
MOZ_ASSERT(!NS_IsMainThread());
mGLContext->MakeCurrent();
// Test that SGI_video_sync lets us get the counter.
unsigned int syncCounter = 0;
if (gl::sGLXLibrary.fGetVideoSync(&syncCounter) != 0) {
mGLContext = nullptr;
if (mXDisplay) XCloseDisplay(mXDisplay);
}
// Owned by the vsync thread.
RefPtr<gl::GLContextGLX> mGLContext;
_XDisplay* mXDisplay;
Monitor mSetupLock;
base::Thread mVsyncThread;
RefPtr<Runnable> mVsyncTask;
Monitor mVsyncEnabledLock;
bool mVsyncEnabled;
};
lock.NotifyAll();
}
virtual void EnableVsync() override {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mGLContext, "GLContext not setup!");
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("GtkVsyncSource::RunVsync", this,
&GtkVsyncSource::RunVsync);
RefPtr<Runnable> addrefedTask = mVsyncTask;
mVsyncThread.message_loop()->PostTask(addrefedTask.forget());
}
}
virtual void DisableVsync() override {
MonitorAutoLock lock(mVsyncEnabledLock);
mVsyncEnabled = false;
}
virtual bool IsVsyncEnabled() override {
MonitorAutoLock lock(mVsyncEnabledLock);
return mVsyncEnabled;
}
virtual void Shutdown() override {
MOZ_ASSERT(NS_IsMainThread());
DisableVsync();
// Cleanup thread-specific resources before shutting down.
RefPtr<Runnable> shutdownTask = NewRunnableMethod(
"GtkVsyncSource::Cleanup", this, &GtkVsyncSource::Cleanup);
mVsyncThread.message_loop()->PostTask(shutdownTask.forget());
// Stop, waiting for the cleanup task to finish execution.
mVsyncThread.Stop();
}
private:
// We need a refcounted VsyncSource::Display to use chromium IPC runnables.
RefPtr<GLXDisplay> mGlobalDisplay;
void RunVsync() {
MOZ_ASSERT(!NS_IsMainThread());
mGLContext->MakeCurrent();
unsigned int syncCounter = 0;
gl::sGLXLibrary.fGetVideoSync(&syncCounter);
for (;;) {
{
MonitorAutoLock lock(mVsyncEnabledLock);
if (!mVsyncEnabled) {
mVsyncTask = nullptr;
return;
}
}
TimeStamp lastVsync = TimeStamp::Now();
bool useSoftware = false;
// Wait until the video sync counter reaches the next value by waiting
// until the parity of the counter value changes.
unsigned int nextSync = syncCounter + 1;
int status;
if ((status = gl::sGLXLibrary.fWaitVideoSync(2, (int)nextSync % 2,
&syncCounter)) != 0) {
gfxWarningOnce() << "glXWaitVideoSync returned " << status;
useSoftware = true;
}
if (syncCounter == (nextSync - 1)) {
gfxWarningOnce()
<< "glXWaitVideoSync failed to increment the sync counter.";
useSoftware = true;
}
if (useSoftware) {
double remaining =
(1000.f / 60.f) - (TimeStamp::Now() - lastVsync).ToMilliseconds();
if (remaining > 0) {
AUTO_PROFILER_THREAD_SLEEP;
PlatformThread::Sleep((int)remaining);
}
}
lastVsync = TimeStamp::Now();
TimeStamp outputTime = lastVsync + GetVsyncRate();
NotifyVsync(lastVsync, outputTime);
}
}
void Cleanup() {
MOZ_ASSERT(!NS_IsMainThread());
mGLContext = nullptr;
if (mXDisplay) XCloseDisplay(mXDisplay);
}
// Owned by the vsync thread.
RefPtr<gl::GLContextGLX> mGLContext;
_XDisplay* mXDisplay;
Monitor mSetupLock;
base::Thread mVsyncThread;
RefPtr<Runnable> mVsyncTask;
Monitor mVsyncEnabledLock;
bool mVsyncEnabled;
};
class XrandrSoftwareVsyncSource final : public SoftwareVsyncSource {
public:
XrandrSoftwareVsyncSource() : SoftwareVsyncSource(false) {
XrandrSoftwareVsyncSource() {
MOZ_ASSERT(NS_IsMainThread());
mGlobalDisplay = new XrandrSoftwareDisplay();
UpdateVsyncRate();
GdkScreen* defaultScreen = gdk_screen_get_default();
g_signal_connect(defaultScreen, "monitors-changed",
G_CALLBACK(monitors_changed), this);
}
private:
class XrandrSoftwareDisplay final : public SoftwareDisplay {
public:
XrandrSoftwareDisplay() {
MOZ_ASSERT(NS_IsMainThread());
// Request the current refresh rate via xrandr. It is hard to find the
// "correct" one, thus choose the highest one, assuming this will usually
// give the best user experience.
void UpdateVsyncRate() {
struct _XDisplay* dpy = gdk_x11_get_default_xdisplay();
UpdateVsyncRate();
// Use the default software refresh rate as lower bound. Allowing lower
// rates makes a bunch of tests start to fail on CI. The main goal of this
// VsyncSource is to support refresh rates greater than the default one.
double highestRefreshRate = gfxPlatform::GetSoftwareVsyncRate();
GdkScreen* defaultScreen = gdk_screen_get_default();
g_signal_connect(defaultScreen, "monitors-changed",
G_CALLBACK(monitors_changed), this);
}
// When running on remote X11 the xrandr version may be stuck on an
// ancient version. There are still setups using remote X11 out there, so
// make sure we don't crash.
int eventBase, errorBase, major, minor;
if (XRRQueryExtension(dpy, &eventBase, &errorBase) &&
XRRQueryVersion(dpy, &major, &minor) &&
(major > 1 || (major == 1 && minor >= 3))) {
Window root = gdk_x11_get_default_root_xwindow();
XRRScreenResources* res = XRRGetScreenResourcesCurrent(dpy, root);
private:
// Request the current refresh rate via xrandr. It is hard to find the
// "correct" one, thus choose the highest one, assuming this will usually
// give the best user experience.
void UpdateVsyncRate() {
struct _XDisplay* dpy = gdk_x11_get_default_xdisplay();
// We can't use refresh rates far below the default one (60Hz) because
// otherwise random CI tests start to fail. However, many users have
// screens just below the default rate, e.g. 59.95Hz. So slightly
// decrease the lower bound.
highestRefreshRate -= 1.0;
// Use the default software refresh rate as lower bound. Allowing lower
// rates makes a bunch of tests start to fail on CI. The main goal of this
// VsyncSource is to support refresh rates greater than the default one.
double highestRefreshRate = gfxPlatform::GetSoftwareVsyncRate();
// When running on remote X11 the xrandr version may be stuck on an
// ancient version. There are still setups using remote X11 out there, so
// make sure we don't crash.
int eventBase, errorBase, major, minor;
if (XRRQueryExtension(dpy, &eventBase, &errorBase) &&
XRRQueryVersion(dpy, &major, &minor) &&
(major > 1 || (major == 1 && minor >= 3))) {
Window root = gdk_x11_get_default_root_xwindow();
XRRScreenResources* res = XRRGetScreenResourcesCurrent(dpy, root);
// We can't use refresh rates far below the default one (60Hz) because
// otherwise random CI tests start to fail. However, many users have
// screens just below the default rate, e.g. 59.95Hz. So slightly
// decrease the lower bound.
highestRefreshRate -= 1.0;
for (int i = 0; i < res->noutput; i++) {
XRROutputInfo* outputInfo =
XRRGetOutputInfo(dpy, res, res->outputs[i]);
if (!outputInfo->crtc) {
XRRFreeOutputInfo(outputInfo);
continue;
}
XRRCrtcInfo* crtcInfo = XRRGetCrtcInfo(dpy, res, outputInfo->crtc);
for (int j = 0; j < res->nmode; j++) {
if (res->modes[j].id == crtcInfo->mode) {
double refreshRate = mode_refresh(&res->modes[j]);
if (refreshRate > highestRefreshRate) {
highestRefreshRate = refreshRate;
}
break;
}
}
XRRFreeCrtcInfo(crtcInfo);
for (int i = 0; i < res->noutput; i++) {
XRROutputInfo* outputInfo = XRRGetOutputInfo(dpy, res, res->outputs[i]);
if (!outputInfo->crtc) {
XRRFreeOutputInfo(outputInfo);
continue;
}
XRRFreeScreenResources(res);
}
const double rate = 1000.0 / highestRefreshRate;
mVsyncRate = mozilla::TimeDuration::FromMilliseconds(rate);
XRRCrtcInfo* crtcInfo = XRRGetCrtcInfo(dpy, res, outputInfo->crtc);
for (int j = 0; j < res->nmode; j++) {
if (res->modes[j].id == crtcInfo->mode) {
double refreshRate = mode_refresh(&res->modes[j]);
if (refreshRate > highestRefreshRate) {
highestRefreshRate = refreshRate;
}
break;
}
}
XRRFreeCrtcInfo(crtcInfo);
XRRFreeOutputInfo(outputInfo);
}
XRRFreeScreenResources(res);
}
static void monitors_changed(GdkScreen* aScreen, gpointer aClosure) {
XrandrSoftwareDisplay* self =
static_cast<XrandrSoftwareDisplay*>(aClosure);
self->UpdateVsyncRate();
const double rate = 1000.0 / highestRefreshRate;
mVsyncRate = mozilla::TimeDuration::FromMilliseconds(rate);
}
static void monitors_changed(GdkScreen* aScreen, gpointer aClosure) {
XrandrSoftwareVsyncSource* self =
static_cast<XrandrSoftwareVsyncSource*>(aClosure);
self->UpdateVsyncRate();
}
// from xrandr.c
static double mode_refresh(const XRRModeInfo* mode_info) {
double rate;
double vTotal = mode_info->vTotal;
if (mode_info->modeFlags & RR_DoubleScan) {
/* doublescan doubles the number of lines */
vTotal *= 2;
}
// from xrandr.c
static double mode_refresh(const XRRModeInfo* mode_info) {
double rate;
double vTotal = mode_info->vTotal;
if (mode_info->modeFlags & RR_DoubleScan) {
/* doublescan doubles the number of lines */
vTotal *= 2;
}
if (mode_info->modeFlags & RR_Interlace) {
/* interlace splits the frame into two fields */
/* the field rate is what is typically reported by monitors */
vTotal /= 2;
}
if (mode_info->hTotal && vTotal) {
rate = ((double)mode_info->dotClock /
((double)mode_info->hTotal * (double)vTotal));
} else {
rate = 0;
}
return rate;
if (mode_info->modeFlags & RR_Interlace) {
/* interlace splits the frame into two fields */
/* the field rate is what is typically reported by monitors */
vTotal /= 2;
}
};
if (mode_info->hTotal && vTotal) {
rate = ((double)mode_info->dotClock /
((double)mode_info->hTotal * (double)vTotal));
} else {
rate = 0;
}
return rate;
}
};
already_AddRefed<gfx::VsyncSource> gfxPlatformGtk::CreateHardwareVsyncSource() {
@ -1003,9 +978,8 @@ already_AddRefed<gfx::VsyncSource> gfxPlatformGtk::CreateHardwareVsyncSource() {
if (gfxConfig::IsEnabled(Feature::HW_COMPOSITING) && !isXwayland &&
(!gfxVars::UseEGL() || isMesa) &&
gl::sGLXLibrary.SupportsVideoSync(DefaultXDisplay())) {
RefPtr<VsyncSource> vsyncSource = new GtkVsyncSource();
VsyncSource::Display& display = vsyncSource->GetGlobalDisplay();
if (!static_cast<GtkVsyncSource::GLXDisplay&>(display).Setup()) {
RefPtr<GtkVsyncSource> vsyncSource = new GtkVsyncSource();
if (!vsyncSource->Setup()) {
NS_WARNING("Failed to setup GLContext, falling back to software vsync.");
return gfxPlatform::CreateHardwareVsyncSource();
}

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

@ -846,208 +846,197 @@ static CVReturn VsyncCallback(CVDisplayLinkRef aDisplayLink,
class OSXVsyncSource final : public VsyncSource {
public:
OSXVsyncSource() : mGlobalDisplay(new OSXDisplay()) {}
Display& GetGlobalDisplay() override { return *mGlobalDisplay; }
class OSXDisplay final : public VsyncSource::Display {
public:
OSXDisplay()
: mDisplayLink(nullptr, "OSXVsyncSource::OSXDisplay::mDisplayLink") {
MOZ_ASSERT(NS_IsMainThread());
mTimer = NS_NewTimer();
CGDisplayRegisterReconfigurationCallback(DisplayReconfigurationCallback,
this);
}
virtual ~OSXDisplay() {
MOZ_ASSERT(NS_IsMainThread());
CGDisplayRemoveReconfigurationCallback(DisplayReconfigurationCallback,
OSXVsyncSource()
: mDisplayLink(nullptr, "OSXVsyncSource::OSXDisplay::mDisplayLink") {
MOZ_ASSERT(NS_IsMainThread());
mTimer = NS_NewTimer();
CGDisplayRegisterReconfigurationCallback(DisplayReconfigurationCallback,
this);
}
virtual ~OSXVsyncSource() {
MOZ_ASSERT(NS_IsMainThread());
CGDisplayRemoveReconfigurationCallback(DisplayReconfigurationCallback,
this);
}
static void RetryEnableVsync(nsITimer* aTimer, void* aOsxVsyncSource) {
MOZ_ASSERT(NS_IsMainThread());
OSXVsyncSource* osxVsyncSource =
static_cast<OSXVsyncSource*>(aOsxVsyncSource);
MOZ_ASSERT(osxVsyncSource);
osxVsyncSource->EnableVsync();
}
void EnableVsync() override {
MOZ_ASSERT(NS_IsMainThread());
if (IsVsyncEnabled()) {
return;
}
static void RetryEnableVsync(nsITimer* aTimer, void* aOsxDisplay) {
MOZ_ASSERT(NS_IsMainThread());
OSXDisplay* osxDisplay = static_cast<OSXDisplay*>(aOsxDisplay);
MOZ_ASSERT(osxDisplay);
osxDisplay->EnableVsync();
auto displayLink = mDisplayLink.Lock();
// Create a display link capable of being used with all active displays
// TODO: See if we need to create an active DisplayLink for each monitor
// in multi-monitor situations. According to the docs, it is compatible
// with all displays running on the computer But if we have different
// monitors at different display rates, we may hit issues.
CVReturn retval = CVDisplayLinkCreateWithActiveCGDisplays(&*displayLink);
// Workaround for bug 1201401: CVDisplayLinkCreateWithCGDisplays()
// (called by CVDisplayLinkCreateWithActiveCGDisplays()) sometimes
// creates a CVDisplayLinkRef with an uninitialized (nulled) internal
// pointer. If we continue to use this CVDisplayLinkRef, we will
// eventually crash in CVCGDisplayLink::getDisplayTimes(), where the
// internal pointer is dereferenced. Fortunately, when this happens
// another internal variable is also left uninitialized (zeroed),
// which is accessible via CVDisplayLinkGetCurrentCGDisplay(). In
// normal conditions the current display is never zero.
if ((retval == kCVReturnSuccess) &&
(CVDisplayLinkGetCurrentCGDisplay(*displayLink) == 0)) {
retval = kCVReturnInvalidDisplay;
}
void EnableVsync() override {
MOZ_ASSERT(NS_IsMainThread());
if (IsVsyncEnabled()) {
return;
}
if (retval != kCVReturnSuccess) {
NS_WARNING(
"Could not create a display link with all active displays. "
"Retrying");
CVDisplayLinkRelease(*displayLink);
*displayLink = nullptr;
auto displayLink = mDisplayLink.Lock();
// Create a display link capable of being used with all active displays
// TODO: See if we need to create an active DisplayLink for each monitor
// in multi-monitor situations. According to the docs, it is compatible
// with all displays running on the computer But if we have different
// monitors at different display rates, we may hit issues.
CVReturn retval = CVDisplayLinkCreateWithActiveCGDisplays(&*displayLink);
// Workaround for bug 1201401: CVDisplayLinkCreateWithCGDisplays()
// (called by CVDisplayLinkCreateWithActiveCGDisplays()) sometimes
// creates a CVDisplayLinkRef with an uninitialized (nulled) internal
// pointer. If we continue to use this CVDisplayLinkRef, we will
// eventually crash in CVCGDisplayLink::getDisplayTimes(), where the
// internal pointer is dereferenced. Fortunately, when this happens
// another internal variable is also left uninitialized (zeroed),
// which is accessible via CVDisplayLinkGetCurrentCGDisplay(). In
// normal conditions the current display is never zero.
if ((retval == kCVReturnSuccess) &&
(CVDisplayLinkGetCurrentCGDisplay(*displayLink) == 0)) {
retval = kCVReturnInvalidDisplay;
}
if (retval != kCVReturnSuccess) {
NS_WARNING(
"Could not create a display link with all active displays. "
"Retrying");
CVDisplayLinkRelease(*displayLink);
*displayLink = nullptr;
// bug 1142708 - When coming back from sleep,
// or when changing displays, active displays may not be ready yet,
// even if listening for the kIOMessageSystemHasPoweredOn event
// from OS X sleep notifications.
// Active displays are those that are drawable.
// bug 1144638 - When changing display configurations and getting
// notifications from CGDisplayReconfigurationCallBack, the
// callback gets called twice for each active display
// so it's difficult to know when all displays are active.
// Instead, try again soon. The delay is arbitrary. 100ms chosen
// because on a late 2013 15" retina, it takes about that
// long to come back up from sleep.
uint32_t delay = 100;
mTimer->InitWithNamedFuncCallback(RetryEnableVsync, this, delay,
nsITimer::TYPE_ONE_SHOT,
"RetryEnableVsync");
return;
}
if (CVDisplayLinkSetOutputCallback(*displayLink, &VsyncCallback, this) !=
kCVReturnSuccess) {
NS_WARNING("Could not set displaylink output callback");
CVDisplayLinkRelease(*displayLink);
*displayLink = nullptr;
return;
}
mPreviousTimestamp = TimeStamp::Now();
if (CVDisplayLinkStart(*displayLink) != kCVReturnSuccess) {
NS_WARNING("Could not activate the display link");
CVDisplayLinkRelease(*displayLink);
*displayLink = nullptr;
}
CVTime vsyncRate =
CVDisplayLinkGetNominalOutputVideoRefreshPeriod(*displayLink);
if (vsyncRate.flags & kCVTimeIsIndefinite) {
NS_WARNING("Could not get vsync rate, setting to 60.");
mVsyncRate = TimeDuration::FromMilliseconds(1000.0 / 60.0);
} else {
int64_t timeValue = vsyncRate.timeValue;
int64_t timeScale = vsyncRate.timeScale;
const int milliseconds = 1000;
float rateInMs = ((double)timeValue / (double)timeScale) * milliseconds;
mVsyncRate = TimeDuration::FromMilliseconds(rateInMs);
}
// bug 1142708 - When coming back from sleep,
// or when changing displays, active displays may not be ready yet,
// even if listening for the kIOMessageSystemHasPoweredOn event
// from OS X sleep notifications.
// Active displays are those that are drawable.
// bug 1144638 - When changing display configurations and getting
// notifications from CGDisplayReconfigurationCallBack, the
// callback gets called twice for each active display
// so it's difficult to know when all displays are active.
// Instead, try again soon. The delay is arbitrary. 100ms chosen
// because on a late 2013 15" retina, it takes about that
// long to come back up from sleep.
uint32_t delay = 100;
mTimer->InitWithNamedFuncCallback(RetryEnableVsync, this, delay,
nsITimer::TYPE_ONE_SHOT,
"RetryEnableVsync");
return;
}
void DisableVsync() override {
MOZ_ASSERT(NS_IsMainThread());
if (!IsVsyncEnabled()) {
return;
}
// Release the display link
auto displayLink = mDisplayLink.Lock();
if (*displayLink) {
CVDisplayLinkRelease(*displayLink);
*displayLink = nullptr;
}
if (CVDisplayLinkSetOutputCallback(*displayLink, &VsyncCallback, this) !=
kCVReturnSuccess) {
NS_WARNING("Could not set displaylink output callback");
CVDisplayLinkRelease(*displayLink);
*displayLink = nullptr;
return;
}
bool IsVsyncEnabled() override {
MOZ_ASSERT(NS_IsMainThread());
auto displayLink = mDisplayLink.Lock();
return *displayLink != nullptr;
mPreviousTimestamp = TimeStamp::Now();
if (CVDisplayLinkStart(*displayLink) != kCVReturnSuccess) {
NS_WARNING("Could not activate the display link");
CVDisplayLinkRelease(*displayLink);
*displayLink = nullptr;
}
TimeDuration GetVsyncRate() override { return mVsyncRate; }
CVTime vsyncRate =
CVDisplayLinkGetNominalOutputVideoRefreshPeriod(*displayLink);
if (vsyncRate.flags & kCVTimeIsIndefinite) {
NS_WARNING("Could not get vsync rate, setting to 60.");
mVsyncRate = TimeDuration::FromMilliseconds(1000.0 / 60.0);
} else {
int64_t timeValue = vsyncRate.timeValue;
int64_t timeScale = vsyncRate.timeScale;
const int milliseconds = 1000;
float rateInMs = ((double)timeValue / (double)timeScale) * milliseconds;
mVsyncRate = TimeDuration::FromMilliseconds(rateInMs);
}
}
void Shutdown() override {
MOZ_ASSERT(NS_IsMainThread());
mTimer->Cancel();
mTimer = nullptr;
DisableVsync();
void DisableVsync() override {
MOZ_ASSERT(NS_IsMainThread());
if (!IsVsyncEnabled()) {
return;
}
// The vsync timestamps given by the CVDisplayLinkCallback are
// in the future for the NEXT frame. Large parts of Gecko, such
// as animations assume a timestamp at either now or in the past.
// Normalize the timestamps given to the VsyncDispatchers to the vsync
// that just occured, not the vsync that is upcoming.
TimeStamp mPreviousTimestamp;
private:
static void DisplayReconfigurationCallback(
CGDirectDisplayID aDisplay, CGDisplayChangeSummaryFlags aFlags,
void* aUserInfo) {
static_cast<OSXDisplay*>(aUserInfo)->OnDisplayReconfiguration(aDisplay,
aFlags);
// Release the display link
auto displayLink = mDisplayLink.Lock();
if (*displayLink) {
CVDisplayLinkRelease(*displayLink);
*displayLink = nullptr;
}
}
void OnDisplayReconfiguration(CGDirectDisplayID aDisplay,
CGDisplayChangeSummaryFlags aFlags) {
// Display reconfiguration notifications are fired in two phases: Before
// the reconfiguration and after the reconfiguration.
// All displays are notified before (with a "BeginConfiguration" flag),
// and the reconfigured displays are notified again after the
// configuration.
if (aFlags & kCGDisplayBeginConfigurationFlag) {
// We're only interested in the "after" notification, for the display
// link's current display.
return;
}
bool IsVsyncEnabled() override {
MOZ_ASSERT(NS_IsMainThread());
auto displayLink = mDisplayLink.Lock();
return *displayLink != nullptr;
}
if (!NS_IsMainThread()) {
return;
}
TimeDuration GetVsyncRate() override { return mVsyncRate; }
bool didReconfigureCurrentDisplayLinkDisplay = false;
{ // scope for lock
auto displayLink = mDisplayLink.Lock();
didReconfigureCurrentDisplayLinkDisplay =
*displayLink &&
CVDisplayLinkGetCurrentCGDisplay(*displayLink) == aDisplay;
}
void Shutdown() override {
MOZ_ASSERT(NS_IsMainThread());
mTimer->Cancel();
mTimer = nullptr;
DisableVsync();
}
if (didReconfigureCurrentDisplayLinkDisplay) {
// The link's current display has been reconfigured.
// Recreate the display link, because otherwise it may be stuck with a
// "removed" display forever and never notify us again.
DisableVsync();
EnableVsync();
}
}
// Accessed from main thread and from display reconfiguration callback
// thread... which also happens to be the main thread.
DataMutex<CVDisplayLinkRef> mDisplayLink;
// Accessed only from the main thread.
RefPtr<nsITimer> mTimer;
TimeDuration mVsyncRate;
}; // OSXDisplay
// The vsync timestamps given by the CVDisplayLinkCallback are
// in the future for the NEXT frame. Large parts of Gecko, such
// as animations assume a timestamp at either now or in the past.
// Normalize the timestamps given to the VsyncDispatchers to the vsync
// that just occured, not the vsync that is upcoming.
TimeStamp mPreviousTimestamp;
private:
virtual ~OSXVsyncSource() = default;
static void DisplayReconfigurationCallback(CGDirectDisplayID aDisplay,
CGDisplayChangeSummaryFlags aFlags,
void* aUserInfo) {
static_cast<OSXVsyncSource*>(aUserInfo)->OnDisplayReconfiguration(aDisplay,
aFlags);
}
RefPtr<OSXDisplay> mGlobalDisplay;
void OnDisplayReconfiguration(CGDirectDisplayID aDisplay,
CGDisplayChangeSummaryFlags aFlags) {
// Display reconfiguration notifications are fired in two phases: Before
// the reconfiguration and after the reconfiguration.
// All displays are notified before (with a "BeginConfiguration" flag),
// and the reconfigured displays are notified again after the
// configuration.
if (aFlags & kCGDisplayBeginConfigurationFlag) {
// We're only interested in the "after" notification, for the display
// link's current display.
return;
}
if (!NS_IsMainThread()) {
return;
}
bool didReconfigureCurrentDisplayLinkDisplay = false;
{ // scope for lock
auto displayLink = mDisplayLink.Lock();
didReconfigureCurrentDisplayLinkDisplay =
*displayLink &&
CVDisplayLinkGetCurrentCGDisplay(*displayLink) == aDisplay;
}
if (didReconfigureCurrentDisplayLinkDisplay) {
// The link's current display has been reconfigured.
// Recreate the display link, because otherwise it may be stuck with a
// "removed" display forever and never notify us again.
DisableVsync();
EnableVsync();
}
}
// Accessed from main thread and from display reconfiguration callback
// thread... which also happens to be the main thread.
DataMutex<CVDisplayLinkRef> mDisplayLink;
// Accessed only from the main thread.
RefPtr<nsITimer> mTimer;
TimeDuration mVsyncRate;
}; // OSXVsyncSource
static CVReturn VsyncCallback(CVDisplayLinkRef aDisplayLink,
@ -1056,13 +1045,12 @@ static CVReturn VsyncCallback(CVDisplayLinkRef aDisplayLink,
CVOptionFlags aFlagsIn, CVOptionFlags* aFlagsOut,
void* aDisplayLinkContext) {
// Executed on OS X hardware vsync thread
OSXVsyncSource::OSXDisplay* display =
(OSXVsyncSource::OSXDisplay*)aDisplayLinkContext;
OSXVsyncSource* vsyncSource = (OSXVsyncSource*)aDisplayLinkContext;
mozilla::TimeStamp outputTime =
mozilla::TimeStamp::FromSystemTime(aOutputTime->hostTime);
mozilla::TimeStamp nextVsync = outputTime;
mozilla::TimeStamp previousVsync = display->mPreviousTimestamp;
mozilla::TimeStamp previousVsync = vsyncSource->mPreviousTimestamp;
mozilla::TimeStamp now = TimeStamp::Now();
// Snow leopard sometimes sends vsync timestamps very far in the past.
@ -1074,28 +1062,27 @@ static CVReturn VsyncCallback(CVDisplayLinkRef aDisplayLink,
// Bug 1158321 - The VsyncCallback can sometimes execute before the reported
// vsync time. In those cases, normalize the timestamp to Now() as sending
// timestamps in the future has undefined behavior. See the comment above
// OSXDisplay::mPreviousTimestamp
// OSXVsyncSource::mPreviousTimestamp
previousVsync = now;
}
display->mPreviousTimestamp = nextVsync;
vsyncSource->mPreviousTimestamp = nextVsync;
display->NotifyVsync(previousVsync, outputTime);
vsyncSource->NotifyVsync(previousVsync, outputTime);
return kCVReturnSuccess;
}
already_AddRefed<mozilla::gfx::VsyncSource>
gfxPlatformMac::CreateHardwareVsyncSource() {
RefPtr<VsyncSource> osxVsyncSource = new OSXVsyncSource();
VsyncSource::Display& primaryDisplay = osxVsyncSource->GetGlobalDisplay();
primaryDisplay.EnableVsync();
if (!primaryDisplay.IsVsyncEnabled()) {
osxVsyncSource->EnableVsync();
if (!osxVsyncSource->IsVsyncEnabled()) {
NS_WARNING(
"OS X Vsync source not enabled. Falling back to software vsync.");
return gfxPlatform::CreateHardwareVsyncSource();
}
primaryDisplay.DisableVsync();
osxVsyncSource->DisableVsync();
return osxVsyncSource.forget();
}

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

@ -1509,295 +1509,284 @@ bool gfxWindowsPlatform::DwmCompositionEnabled() {
class D3DVsyncSource final : public VsyncSource {
public:
class D3DVsyncDisplay final : public VsyncSource::Display {
public:
D3DVsyncDisplay()
: mPrevVsync(TimeStamp::Now()),
mVsyncEnabled(false),
mWaitVBlankMonitor(NULL),
mIsWindows8OrLater(false) {
mVsyncThread = new base::Thread("WindowsVsyncThread");
MOZ_RELEASE_ASSERT(mVsyncThread->Start(),
"GFX: Could not start Windows vsync thread");
SetVsyncRate();
D3DVsyncSource()
: mPrevVsync(TimeStamp::Now()),
mVsyncEnabled(false),
mWaitVBlankMonitor(NULL),
mIsWindows8OrLater(false) {
mVsyncThread = new base::Thread("WindowsVsyncThread");
MOZ_RELEASE_ASSERT(mVsyncThread->Start(),
"GFX: Could not start Windows vsync thread");
SetVsyncRate();
mIsWindows8OrLater = IsWin8OrLater();
mIsWindows8OrLater = IsWin8OrLater();
}
void SetVsyncRate() {
if (!gfxWindowsPlatform::GetPlatform()->DwmCompositionEnabled()) {
mVsyncRate = TimeDuration::FromMilliseconds(1000.0 / 60.0);
return;
}
void SetVsyncRate() {
if (!gfxWindowsPlatform::GetPlatform()->DwmCompositionEnabled()) {
mVsyncRate = TimeDuration::FromMilliseconds(1000.0 / 60.0);
DWM_TIMING_INFO vblankTime;
// Make sure to init the cbSize, otherwise GetCompositionTiming will fail
vblankTime.cbSize = sizeof(DWM_TIMING_INFO);
HRESULT hr = DwmGetCompositionTimingInfo(0, &vblankTime);
if (SUCCEEDED(hr)) {
UNSIGNED_RATIO refreshRate = vblankTime.rateRefresh;
// We get the rate in hertz / time, but we want the rate in ms.
float rate =
((float)refreshRate.uiDenominator / (float)refreshRate.uiNumerator) *
1000;
mVsyncRate = TimeDuration::FromMilliseconds(rate);
} else {
mVsyncRate = TimeDuration::FromMilliseconds(1000.0 / 60.0);
}
}
virtual void Shutdown() override {
MOZ_ASSERT(NS_IsMainThread());
DisableVsync();
mVsyncThread->Stop();
delete mVsyncThread;
}
virtual void EnableVsync() override {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mVsyncThread->IsRunning());
{ // scope lock
if (mVsyncEnabled) {
return;
}
DWM_TIMING_INFO vblankTime;
// Make sure to init the cbSize, otherwise GetCompositionTiming will fail
vblankTime.cbSize = sizeof(DWM_TIMING_INFO);
HRESULT hr = DwmGetCompositionTimingInfo(0, &vblankTime);
if (SUCCEEDED(hr)) {
UNSIGNED_RATIO refreshRate = vblankTime.rateRefresh;
// We get the rate in hertz / time, but we want the rate in ms.
float rate = ((float)refreshRate.uiDenominator /
(float)refreshRate.uiNumerator) *
1000;
mVsyncRate = TimeDuration::FromMilliseconds(rate);
} else {
mVsyncRate = TimeDuration::FromMilliseconds(1000.0 / 60.0);
}
mVsyncEnabled = true;
}
virtual void Shutdown() override {
MOZ_ASSERT(NS_IsMainThread());
DisableVsync();
mVsyncThread->Stop();
delete mVsyncThread;
mVsyncThread->message_loop()->PostTask(NewRunnableMethod(
"D3DVsyncSource::VBlankLoop", this, &D3DVsyncSource::VBlankLoop));
}
virtual void DisableVsync() override {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mVsyncThread->IsRunning());
if (!mVsyncEnabled) {
return;
}
mVsyncEnabled = false;
}
virtual bool IsVsyncEnabled() override {
MOZ_ASSERT(NS_IsMainThread());
return mVsyncEnabled;
}
virtual TimeDuration GetVsyncRate() override { return mVsyncRate; }
void ScheduleSoftwareVsync(TimeStamp aVsyncTimestamp) {
MOZ_ASSERT(IsInVsyncThread());
NS_WARNING(
"DwmComposition dynamically disabled, falling back to software "
"timers");
TimeStamp nextVsync = aVsyncTimestamp + mVsyncRate;
TimeDuration delay = nextVsync - TimeStamp::Now();
if (delay.ToMilliseconds() < 0) {
delay = mozilla::TimeDuration::FromMilliseconds(0);
}
virtual void EnableVsync() override {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mVsyncThread->IsRunning());
{ // scope lock
if (mVsyncEnabled) {
return;
}
mVsyncEnabled = true;
}
mVsyncThread->message_loop()->PostDelayedTask(
NewRunnableMethod("D3DVsyncSource::VBlankLoop", this,
&D3DVsyncSource::VBlankLoop),
delay.ToMilliseconds());
}
mVsyncThread->message_loop()->PostTask(NewRunnableMethod(
"D3DVsyncDisplay::VBlankLoop", this, &D3DVsyncDisplay::VBlankLoop));
}
virtual void DisableVsync() override {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mVsyncThread->IsRunning());
if (!mVsyncEnabled) {
return;
}
mVsyncEnabled = false;
}
virtual bool IsVsyncEnabled() override {
MOZ_ASSERT(NS_IsMainThread());
return mVsyncEnabled;
}
virtual TimeDuration GetVsyncRate() override { return mVsyncRate; }
void ScheduleSoftwareVsync(TimeStamp aVsyncTimestamp) {
MOZ_ASSERT(IsInVsyncThread());
NS_WARNING(
"DwmComposition dynamically disabled, falling back to software "
"timers");
TimeStamp nextVsync = aVsyncTimestamp + mVsyncRate;
TimeDuration delay = nextVsync - TimeStamp::Now();
if (delay.ToMilliseconds() < 0) {
delay = mozilla::TimeDuration::FromMilliseconds(0);
}
mVsyncThread->message_loop()->PostDelayedTask(
NewRunnableMethod("D3DVsyncDisplay::VBlankLoop", this,
&D3DVsyncDisplay::VBlankLoop),
delay.ToMilliseconds());
}
// Returns the timestamp for the just happened vsync
TimeStamp GetVBlankTime() {
TimeStamp vsync = TimeStamp::Now();
TimeStamp now = vsync;
DWM_TIMING_INFO vblankTime;
// Make sure to init the cbSize, otherwise
// GetCompositionTiming will fail
vblankTime.cbSize = sizeof(DWM_TIMING_INFO);
HRESULT hr = DwmGetCompositionTimingInfo(0, &vblankTime);
if (!SUCCEEDED(hr)) {
return vsync;
}
LARGE_INTEGER frequency;
QueryPerformanceFrequency(&frequency);
LARGE_INTEGER qpcNow;
QueryPerformanceCounter(&qpcNow);
const int microseconds = 1000000;
int64_t adjust = qpcNow.QuadPart - vblankTime.qpcVBlank;
int64_t usAdjust = (adjust * microseconds) / frequency.QuadPart;
vsync -= TimeDuration::FromMicroseconds((double)usAdjust);
if (IsWin10OrLater()) {
// On Windows 10 and on, DWMGetCompositionTimingInfo, mostly
// reports the upcoming vsync time, which is in the future.
// It can also sometimes report a vblank time in the past.
// Since large parts of Gecko assume TimeStamps can't be in future,
// use the previous vsync.
// Windows 10 and Intel HD vsync timestamps are messy and
// all over the place once in a while. Most of the time,
// it reports the upcoming vsync. Sometimes, that upcoming
// vsync is in the past. Sometimes that upcoming vsync is before
// the previously seen vsync.
// In these error cases, normalize to Now();
if (vsync >= now) {
vsync = vsync - mVsyncRate;
}
}
// On Windows 7 and 8, DwmFlush wakes up AFTER qpcVBlankTime
// from DWMGetCompositionTimingInfo. We can return the adjusted vsync.
if (vsync >= now) {
vsync = now;
}
// Our vsync time is some time very far in the past, adjust to Now.
// 4 ms is arbitrary, so feel free to pick something else if this isn't
// working. See the comment above within IsWin10OrLater().
if ((now - vsync).ToMilliseconds() > 4.0) {
vsync = now;
}
// Returns the timestamp for the just happened vsync
TimeStamp GetVBlankTime() {
TimeStamp vsync = TimeStamp::Now();
TimeStamp now = vsync;
DWM_TIMING_INFO vblankTime;
// Make sure to init the cbSize, otherwise
// GetCompositionTiming will fail
vblankTime.cbSize = sizeof(DWM_TIMING_INFO);
HRESULT hr = DwmGetCompositionTimingInfo(0, &vblankTime);
if (!SUCCEEDED(hr)) {
return vsync;
}
void VBlankLoop() {
MOZ_ASSERT(IsInVsyncThread());
MOZ_ASSERT(sizeof(int64_t) == sizeof(QPC_TIME));
LARGE_INTEGER frequency;
QueryPerformanceFrequency(&frequency);
TimeStamp vsync = TimeStamp::Now();
mPrevVsync = TimeStamp();
TimeStamp flushTime = TimeStamp::Now();
TimeDuration longVBlank = mVsyncRate * 2;
LARGE_INTEGER qpcNow;
QueryPerformanceCounter(&qpcNow);
for (;;) {
{ // scope lock
if (!mVsyncEnabled) return;
}
const int microseconds = 1000000;
int64_t adjust = qpcNow.QuadPart - vblankTime.qpcVBlank;
int64_t usAdjust = (adjust * microseconds) / frequency.QuadPart;
vsync -= TimeDuration::FromMicroseconds((double)usAdjust);
// Large parts of gecko assume that the refresh driver timestamp
// must be <= Now() and cannot be in the future.
MOZ_ASSERT(vsync <= TimeStamp::Now());
Display::NotifyVsync(vsync, vsync + mVsyncRate);
if (IsWin10OrLater()) {
// On Windows 10 and on, DWMGetCompositionTimingInfo, mostly
// reports the upcoming vsync time, which is in the future.
// It can also sometimes report a vblank time in the past.
// Since large parts of Gecko assume TimeStamps can't be in future,
// use the previous vsync.
// DwmComposition can be dynamically enabled/disabled
// so we have to check every time that it's available.
// When it is unavailable, we fallback to software but will try
// to get back to dwm rendering once it's re-enabled
if (!gfxWindowsPlatform::GetPlatform()->DwmCompositionEnabled()) {
ScheduleSoftwareVsync(vsync);
return;
}
HRESULT hr = E_FAIL;
if (mIsWindows8OrLater &&
!StaticPrefs::gfx_vsync_force_disable_waitforvblank()) {
UpdateVBlankOutput();
if (mWaitVBlankOutput) {
const TimeStamp vblank_begin_wait = TimeStamp::Now();
{
AUTO_PROFILER_THREAD_SLEEP;
hr = mWaitVBlankOutput->WaitForVBlank();
}
if (SUCCEEDED(hr)) {
// vblank might return instantly when running headless,
// monitor powering off, etc. Since we're on a dedicated
// thread, instant-return should not happen in the normal
// case, so catch any odd behavior with a time cutoff:
TimeDuration vblank_wait = TimeStamp::Now() - vblank_begin_wait;
if (vblank_wait.ToMilliseconds() < 1.0) {
hr = E_FAIL; // fall back on old behavior
}
}
}
}
if (!SUCCEEDED(hr)) {
hr = DwmFlush();
}
if (!SUCCEEDED(hr)) {
// DWMFlush isn't working, fallback to software vsync.
ScheduleSoftwareVsync(TimeStamp::Now());
return;
}
TimeStamp now = TimeStamp::Now();
TimeDuration flushDiff = now - flushTime;
flushTime = now;
if ((flushDiff > longVBlank) || mPrevVsync.IsNull()) {
// Our vblank took longer than 2 intervals, readjust our timestamps
vsync = GetVBlankTime();
mPrevVsync = vsync;
} else {
// Instead of giving the actual vsync time, a constant interval
// between vblanks instead of the noise generated via hardware
// is actually what we want. Most apps just care about the diff
// between vblanks to animate, so a clean constant interval is
// smoother.
vsync = mPrevVsync + mVsyncRate;
if (vsync > now) {
// DWMFlush woke up very early, so readjust our times again
vsync = GetVBlankTime();
}
if (vsync <= mPrevVsync) {
vsync = TimeStamp::Now();
}
if ((now - vsync).ToMilliseconds() > 2.0) {
// Account for time drift here where vsync never quite catches up to
// Now and we'd fall ever so slightly further behind Now().
vsync = GetVBlankTime();
}
mPrevVsync = vsync;
}
} // end for
}
virtual ~D3DVsyncDisplay() { MOZ_ASSERT(NS_IsMainThread()); }
private:
bool IsInVsyncThread() {
return mVsyncThread->thread_id() == PlatformThread::CurrentId();
// Windows 10 and Intel HD vsync timestamps are messy and
// all over the place once in a while. Most of the time,
// it reports the upcoming vsync. Sometimes, that upcoming
// vsync is in the past. Sometimes that upcoming vsync is before
// the previously seen vsync.
// In these error cases, normalize to Now();
if (vsync >= now) {
vsync = vsync - mVsyncRate;
}
}
void UpdateVBlankOutput() {
HMONITOR primary_monitor =
MonitorFromWindow(nullptr, MONITOR_DEFAULTTOPRIMARY);
if (primary_monitor == mWaitVBlankMonitor && mWaitVBlankOutput) {
// On Windows 7 and 8, DwmFlush wakes up AFTER qpcVBlankTime
// from DWMGetCompositionTimingInfo. We can return the adjusted vsync.
if (vsync >= now) {
vsync = now;
}
// Our vsync time is some time very far in the past, adjust to Now.
// 4 ms is arbitrary, so feel free to pick something else if this isn't
// working. See the comment above within IsWin10OrLater().
if ((now - vsync).ToMilliseconds() > 4.0) {
vsync = now;
}
return vsync;
}
void VBlankLoop() {
MOZ_ASSERT(IsInVsyncThread());
MOZ_ASSERT(sizeof(int64_t) == sizeof(QPC_TIME));
TimeStamp vsync = TimeStamp::Now();
mPrevVsync = TimeStamp();
TimeStamp flushTime = TimeStamp::Now();
TimeDuration longVBlank = mVsyncRate * 2;
for (;;) {
{ // scope lock
if (!mVsyncEnabled) return;
}
// Large parts of gecko assume that the refresh driver timestamp
// must be <= Now() and cannot be in the future.
MOZ_ASSERT(vsync <= TimeStamp::Now());
NotifyVsync(vsync, vsync + mVsyncRate);
// DwmComposition can be dynamically enabled/disabled
// so we have to check every time that it's available.
// When it is unavailable, we fallback to software but will try
// to get back to dwm rendering once it's re-enabled
if (!gfxWindowsPlatform::GetPlatform()->DwmCompositionEnabled()) {
ScheduleSoftwareVsync(vsync);
return;
}
mWaitVBlankMonitor = primary_monitor;
RefPtr<IDXGIOutput> output = nullptr;
if (DeviceManagerDx* dx = DeviceManagerDx::Get()) {
if (dx->GetOutputFromMonitor(mWaitVBlankMonitor, &output)) {
mWaitVBlankOutput = output;
return;
HRESULT hr = E_FAIL;
if (mIsWindows8OrLater &&
!StaticPrefs::gfx_vsync_force_disable_waitforvblank()) {
UpdateVBlankOutput();
if (mWaitVBlankOutput) {
const TimeStamp vblank_begin_wait = TimeStamp::Now();
{
AUTO_PROFILER_THREAD_SLEEP;
hr = mWaitVBlankOutput->WaitForVBlank();
}
if (SUCCEEDED(hr)) {
// vblank might return instantly when running headless,
// monitor powering off, etc. Since we're on a dedicated
// thread, instant-return should not happen in the normal
// case, so catch any odd behavior with a time cutoff:
TimeDuration vblank_wait = TimeStamp::Now() - vblank_begin_wait;
if (vblank_wait.ToMilliseconds() < 1.0) {
hr = E_FAIL; // fall back on old behavior
}
}
}
}
if (!SUCCEEDED(hr)) {
hr = DwmFlush();
}
if (!SUCCEEDED(hr)) {
// DWMFlush isn't working, fallback to software vsync.
ScheduleSoftwareVsync(TimeStamp::Now());
return;
}
// failed to convert a monitor to an output so keep trying
mWaitVBlankOutput = nullptr;
}
TimeStamp now = TimeStamp::Now();
TimeDuration flushDiff = now - flushTime;
flushTime = now;
if ((flushDiff > longVBlank) || mPrevVsync.IsNull()) {
// Our vblank took longer than 2 intervals, readjust our timestamps
vsync = GetVBlankTime();
mPrevVsync = vsync;
} else {
// Instead of giving the actual vsync time, a constant interval
// between vblanks instead of the noise generated via hardware
// is actually what we want. Most apps just care about the diff
// between vblanks to animate, so a clean constant interval is
// smoother.
vsync = mPrevVsync + mVsyncRate;
if (vsync > now) {
// DWMFlush woke up very early, so readjust our times again
vsync = GetVBlankTime();
}
TimeStamp mPrevVsync;
base::Thread* mVsyncThread;
TimeDuration mVsyncRate;
Atomic<bool> mVsyncEnabled;
if (vsync <= mPrevVsync) {
vsync = TimeStamp::Now();
}
HMONITOR mWaitVBlankMonitor;
RefPtr<IDXGIOutput> mWaitVBlankOutput;
bool mIsWindows8OrLater;
}; // end d3dvsyncdisplay
if ((now - vsync).ToMilliseconds() > 2.0) {
// Account for time drift here where vsync never quite catches up to
// Now and we'd fall ever so slightly further behind Now().
vsync = GetVBlankTime();
}
D3DVsyncSource() { mPrimaryDisplay = new D3DVsyncDisplay(); }
virtual Display& GetGlobalDisplay() override { return *mPrimaryDisplay; }
mPrevVsync = vsync;
}
} // end for
}
virtual ~D3DVsyncSource() { MOZ_ASSERT(NS_IsMainThread()); }
private:
virtual ~D3DVsyncSource() = default;
RefPtr<D3DVsyncDisplay> mPrimaryDisplay;
}; // end D3DVsyncSource
bool IsInVsyncThread() {
return mVsyncThread->thread_id() == PlatformThread::CurrentId();
}
void UpdateVBlankOutput() {
HMONITOR primary_monitor =
MonitorFromWindow(nullptr, MONITOR_DEFAULTTOPRIMARY);
if (primary_monitor == mWaitVBlankMonitor && mWaitVBlankOutput) {
return;
}
mWaitVBlankMonitor = primary_monitor;
RefPtr<IDXGIOutput> output = nullptr;
if (DeviceManagerDx* dx = DeviceManagerDx::Get()) {
if (dx->GetOutputFromMonitor(mWaitVBlankMonitor, &output)) {
mWaitVBlankOutput = output;
return;
}
}
// failed to convert a monitor to an output so keep trying
mWaitVBlankOutput = nullptr;
}
TimeStamp mPrevVsync;
base::Thread* mVsyncThread;
TimeDuration mVsyncRate;
Atomic<bool> mVsyncEnabled;
HMONITOR mWaitVBlankMonitor;
RefPtr<IDXGIOutput> mWaitVBlankOutput;
bool mIsWindows8OrLater;
}; // D3DVsyncSource
already_AddRefed<mozilla::gfx::VsyncSource>
gfxWindowsPlatform::CreateHardwareVsyncSource() {

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

@ -481,7 +481,7 @@ class VsyncRefreshDriverTimer : public RefreshDriverTimer {
TimeDuration GetTimerRate() override {
if (mVsyncSource) {
mVsyncRate = mVsyncSource->GetGlobalDisplay().GetVsyncRate();
mVsyncRate = mVsyncSource->GetVsyncRate();
} else if (mVsyncChild) {
mVsyncRate = mVsyncChild->GetVsyncRate();
}

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

@ -111,8 +111,8 @@ void CompositorVsyncDispatcher::Shutdown() {
}
RefreshTimerVsyncDispatcher::RefreshTimerVsyncDispatcher(
gfx::VsyncSource::Display* aDisplay)
: mDisplay(aDisplay),
gfx::VsyncSource* aVsyncSource)
: mVsyncSource(aVsyncSource),
mVsyncObservers("RefreshTimerVsyncDispatcher::mVsyncObservers") {
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(NS_IsMainThread());
@ -123,10 +123,9 @@ RefreshTimerVsyncDispatcher::~RefreshTimerVsyncDispatcher() {
MOZ_ASSERT(NS_IsMainThread());
}
void RefreshTimerVsyncDispatcher::MoveToDisplay(
gfx::VsyncSource::Display* aDisplay) {
void RefreshTimerVsyncDispatcher::MoveToSource(gfx::VsyncSource* aVsyncSource) {
MOZ_ASSERT(NS_IsMainThread());
mDisplay = aDisplay;
mVsyncSource = aVsyncSource;
}
void RefreshTimerVsyncDispatcher::NotifyVsync(const VsyncEvent& aVsync) {
@ -169,7 +168,7 @@ void RefreshTimerVsyncDispatcher::UpdateVsyncStatus() {
return;
}
mDisplay->NotifyRefreshTimerVsyncStatus(NeedsVsync());
mVsyncSource->NotifyRefreshTimerVsyncStatus(NeedsVsync());
}
bool RefreshTimerVsyncDispatcher::NeedsVsync() {

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

@ -79,12 +79,12 @@ class RefreshTimerVsyncDispatcher final {
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RefreshTimerVsyncDispatcher)
public:
explicit RefreshTimerVsyncDispatcher(gfx::VsyncSource::Display* aDisplay);
explicit RefreshTimerVsyncDispatcher(gfx::VsyncSource* aVsyncSource);
// Please check CompositorVsyncDispatcher::NotifyVsync().
void NotifyVsync(const VsyncEvent& aVsync);
void MoveToDisplay(gfx::VsyncSource::Display* aDisplay);
void MoveToSource(gfx::VsyncSource* aVsyncSource);
// Add a vsync observer to this dispatcher. This is a no-op if the observer is
// already registered. Can be called from any thread.
@ -99,10 +99,10 @@ 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;
// We need to hold a weak ref to the vsync source we belong to in order to
// notify it of our vsync requirement. The vsync source holds a RefPtr to us,
// so we can't hold a RefPtr back without causing a cyclic dependency.
gfx::VsyncSource* mVsyncSource;
DataMutex<nsTArray<RefPtr<VsyncObserver>>> mVsyncObservers;
};

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

@ -32,15 +32,13 @@ namespace mozilla {
static void WaylandVsyncSourceCallbackHandler(void* aData,
struct wl_callback* aCallback,
uint32_t aTime) {
WaylandVsyncSource::WaylandDisplay* context =
(WaylandVsyncSource::WaylandDisplay*)aData;
WaylandVsyncSource* context = (WaylandVsyncSource*)aData;
wl_callback_destroy(aCallback);
context->FrameCallback(aTime);
}
static void WaylandVsyncSourceCallbackHandler(void* aData, uint32_t aTime) {
WaylandVsyncSource::WaylandDisplay* context =
(WaylandVsyncSource::WaylandDisplay*)aData;
WaylandVsyncSource* context = (WaylandVsyncSource*)aData;
context->FrameCallback(aTime);
}
@ -51,20 +49,17 @@ static float GetFPS(TimeDuration aVsyncRate) {
return 1000.0 / aVsyncRate.ToMilliseconds();
}
static UniquePtr<LinkedList<WaylandVsyncSource::WaylandDisplay>>
gWaylandDisplays;
static nsTArray<WaylandVsyncSource*> gWaylandVsyncSources;
Maybe<TimeDuration> WaylandVsyncSource::GetFastestVsyncRate() {
Maybe<TimeDuration> retVal;
if (gWaylandDisplays) {
for (auto* display : *gWaylandDisplays) {
if (display->IsVsyncEnabled()) {
TimeDuration rate = display->GetVsyncRate();
if (!retVal.isSome()) {
retVal.emplace(rate);
} else if (rate < *retVal) {
retVal.ref() = rate;
}
for (auto* source : gWaylandVsyncSources) {
if (source->IsVsyncEnabled()) {
TimeDuration rate = source->GetVsyncRate();
if (!retVal.isSome()) {
retVal.emplace(rate);
} else if (rate < *retVal) {
retVal.ref() = rate;
}
}
}
@ -72,7 +67,7 @@ Maybe<TimeDuration> WaylandVsyncSource::GetFastestVsyncRate() {
return retVal;
}
WaylandVsyncSource::WaylandDisplay::WaylandDisplay()
WaylandVsyncSource::WaylandVsyncSource()
: mMutex("WaylandVsyncSource"),
mIsShutdown(false),
mVsyncEnabled(false),
@ -83,22 +78,14 @@ WaylandVsyncSource::WaylandDisplay::WaylandDisplay()
mLastVsyncTimeStamp(TimeStamp::Now()) {
MOZ_ASSERT(NS_IsMainThread());
if (!gWaylandDisplays) {
gWaylandDisplays =
MakeUnique<LinkedList<WaylandVsyncSource::WaylandDisplay>>();
}
gWaylandDisplays->insertBack(this);
gWaylandVsyncSources.AppendElement(this);
}
WaylandVsyncSource::WaylandDisplay::~WaylandDisplay() {
remove(); // Remove from the linked list.
if (gWaylandDisplays->isEmpty()) {
gWaylandDisplays = nullptr;
}
WaylandVsyncSource::~WaylandVsyncSource() {
gWaylandVsyncSources.RemoveElement(this);
}
void WaylandVsyncSource::WaylandDisplay::MaybeUpdateSource(
MozContainer* aContainer) {
void WaylandVsyncSource::MaybeUpdateSource(MozContainer* aContainer) {
MutexAutoLock lock(mMutex);
LOG("WaylandVsyncSource::MaybeUpdateSource mContainer (nsWindow %p) fps %f",
@ -120,7 +107,7 @@ void WaylandVsyncSource::WaylandDisplay::MaybeUpdateSource(
}
}
void WaylandVsyncSource::WaylandDisplay::MaybeUpdateSource(
void WaylandVsyncSource::MaybeUpdateSource(
const RefPtr<NativeLayerRootWayland>& aNativeLayerRoot) {
MutexAutoLock lock(mMutex);
@ -142,9 +129,8 @@ void WaylandVsyncSource::WaylandDisplay::MaybeUpdateSource(
}
}
void WaylandVsyncSource::WaylandDisplay::Refresh(
const MutexAutoLock& aProofOfLock) {
LOG("WaylandDisplay::Refresh fps %f\n", GetFPS(mVsyncRate));
void WaylandVsyncSource::Refresh(const MutexAutoLock& aProofOfLock) {
LOG("WaylandVsyncSource::Refresh fps %f\n", GetFPS(mVsyncRate));
if (!(mContainer || mNativeLayerRoot) || !mMonitorEnabled || !mVsyncEnabled ||
mCallbackRequested) {
@ -166,7 +152,7 @@ void WaylandVsyncSource::WaylandDisplay::Refresh(
LOG(" we're missing wl_surface, register Refresh() callback");
// The surface hasn't been created yet. Try again when the surface is
// ready.
RefPtr<WaylandVsyncSource::WaylandDisplay> self(this);
RefPtr<WaylandVsyncSource> self(this);
moz_container_wayland_add_initial_draw_callback(
mContainer, [self]() -> void {
MutexAutoLock lock(self->mMutex);
@ -189,7 +175,7 @@ void WaylandVsyncSource::WaylandDisplay::Refresh(
}
}
void WaylandVsyncSource::WaylandDisplay::EnableMonitor() {
void WaylandVsyncSource::EnableMonitor() {
LOG("WaylandVsyncSource::EnableMonitor");
MutexAutoLock lock(mMutex);
if (mMonitorEnabled) {
@ -199,7 +185,7 @@ void WaylandVsyncSource::WaylandDisplay::EnableMonitor() {
Refresh(lock);
}
void WaylandVsyncSource::WaylandDisplay::DisableMonitor() {
void WaylandVsyncSource::DisableMonitor() {
LOG("WaylandVsyncSource::DisableMonitor");
MutexAutoLock lock(mMutex);
if (!mMonitorEnabled) {
@ -209,8 +195,7 @@ void WaylandVsyncSource::WaylandDisplay::DisableMonitor() {
mCallbackRequested = false;
}
void WaylandVsyncSource::WaylandDisplay::SetupFrameCallback(
const MutexAutoLock& aProofOfLock) {
void WaylandVsyncSource::SetupFrameCallback(const MutexAutoLock& aProofOfLock) {
MOZ_ASSERT(!mCallbackRequested);
LOG("WaylandVsyncSource::SetupFrameCallback");
@ -242,7 +227,7 @@ void WaylandVsyncSource::WaylandDisplay::SetupFrameCallback(
mCallbackRequested = true;
}
void WaylandVsyncSource::WaylandDisplay::FrameCallback(uint32_t aTime) {
void WaylandVsyncSource::FrameCallback(uint32_t aTime) {
LOG("WaylandVsyncSource::FrameCallback");
MutexAutoLock lock(mMutex);
@ -280,16 +265,15 @@ void WaylandVsyncSource::WaylandDisplay::FrameCallback(uint32_t aTime) {
}
}
TimeDuration WaylandVsyncSource::WaylandDisplay::GetVsyncRate() {
return mVsyncRate;
}
TimeDuration WaylandVsyncSource::GetVsyncRate() { return mVsyncRate; }
void WaylandVsyncSource::WaylandDisplay::CalculateVsyncRate(
const MutexAutoLock& aProofOfLock, TimeStamp aVsyncTimestamp) {
void WaylandVsyncSource::CalculateVsyncRate(const MutexAutoLock& aProofOfLock,
TimeStamp aVsyncTimestamp) {
double duration = (aVsyncTimestamp - mLastVsyncTimeStamp).ToMilliseconds();
double curVsyncRate = mVsyncRate.ToMilliseconds();
LOG("WaylandDisplay::CalculateVsyncRate start fps %f\n", GetFPS(mVsyncRate));
LOG("WaylandVsyncSource::CalculateVsyncRate start fps %f\n",
GetFPS(mVsyncRate));
double correction;
if (duration > curVsyncRate) {
@ -303,7 +287,7 @@ void WaylandVsyncSource::WaylandDisplay::CalculateVsyncRate(
LOG(" new fps %f correction %f\n", GetFPS(mVsyncRate), correction);
}
void WaylandVsyncSource::WaylandDisplay::EnableVsync() {
void WaylandVsyncSource::EnableVsync() {
MOZ_ASSERT(NS_IsMainThread());
LOG("WaylandVsyncSource::EnableVsync fps %f\n", GetFPS(mVsyncRate));
@ -316,19 +300,19 @@ void WaylandVsyncSource::WaylandDisplay::EnableVsync() {
Refresh(lock);
}
void WaylandVsyncSource::WaylandDisplay::DisableVsync() {
void WaylandVsyncSource::DisableVsync() {
LOG("WaylandVsyncSource::DisableVsync fps %f\n", GetFPS(mVsyncRate));
MutexAutoLock lock(mMutex);
mVsyncEnabled = false;
mCallbackRequested = false;
}
bool WaylandVsyncSource::WaylandDisplay::IsVsyncEnabled() {
bool WaylandVsyncSource::IsVsyncEnabled() {
MutexAutoLock lock(mMutex);
return mVsyncEnabled;
}
void WaylandVsyncSource::WaylandDisplay::Shutdown() {
void WaylandVsyncSource::Shutdown() {
MOZ_ASSERT(NS_IsMainThread());
LOG("WaylandVsyncSource::Shutdown fps %f\n", GetFPS(mVsyncRate));
MutexAutoLock lock(mMutex);

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

@ -8,7 +8,6 @@
#include "base/thread.h"
#include "mozilla/RefPtr.h"
#include "mozilla/LinkedList.h"
#include "mozilla/Maybe.h"
#include "mozilla/Mutex.h"
#include "mozilla/Monitor.h"
@ -44,59 +43,45 @@ using layers::NativeLayerRootWayland;
*/
class WaylandVsyncSource final : public gfx::VsyncSource {
public:
WaylandVsyncSource() { mGlobalDisplay = new WaylandDisplay(); }
virtual ~WaylandVsyncSource() { MOZ_ASSERT(NS_IsMainThread()); }
virtual Display& GetGlobalDisplay() override { return *mGlobalDisplay; }
WaylandVsyncSource();
virtual ~WaylandVsyncSource();
static Maybe<TimeDuration> GetFastestVsyncRate();
class WaylandDisplay final : public mozilla::gfx::VsyncSource::Display,
public LinkedListElement<WaylandDisplay> {
public:
WaylandDisplay();
void MaybeUpdateSource(MozContainer* aContainer);
void MaybeUpdateSource(
const RefPtr<NativeLayerRootWayland>& aNativeLayerRoot);
void MaybeUpdateSource(MozContainer* aContainer);
void MaybeUpdateSource(
const RefPtr<NativeLayerRootWayland>& aNativeLayerRoot);
void EnableMonitor();
void DisableMonitor();
void EnableMonitor();
void DisableMonitor();
void FrameCallback(uint32_t aTime);
void FrameCallback(uint32_t aTime);
TimeDuration GetVsyncRate() override;
TimeDuration GetVsyncRate() override;
virtual void EnableVsync() override;
virtual void EnableVsync() override;
virtual void DisableVsync() override;
virtual void DisableVsync() override;
virtual bool IsVsyncEnabled() override;
virtual bool IsVsyncEnabled() override;
virtual void Shutdown() override;
private:
~WaylandDisplay() override;
void Refresh(const MutexAutoLock& aProofOfLock);
void SetupFrameCallback(const MutexAutoLock& aProofOfLock);
void CalculateVsyncRate(const MutexAutoLock& aProofOfLock,
TimeStamp aVsyncTimestamp);
Mutex mMutex;
bool mIsShutdown;
bool mVsyncEnabled;
bool mMonitorEnabled;
bool mCallbackRequested;
MozContainer* mContainer;
RefPtr<NativeLayerRootWayland> mNativeLayerRoot;
TimeDuration mVsyncRate;
TimeStamp mLastVsyncTimeStamp;
};
virtual void Shutdown() override;
private:
// We need a refcounted VsyncSource::Display to use chromium IPC runnables.
RefPtr<WaylandDisplay> mGlobalDisplay;
void Refresh(const MutexAutoLock& aProofOfLock);
void SetupFrameCallback(const MutexAutoLock& aProofOfLock);
void CalculateVsyncRate(const MutexAutoLock& aProofOfLock,
TimeStamp aVsyncTimestamp);
Mutex mMutex;
bool mIsShutdown;
bool mVsyncEnabled;
bool mMonitorEnabled;
bool mCallbackRequested;
MozContainer* mContainer;
RefPtr<NativeLayerRootWayland> mNativeLayerRoot;
TimeDuration mVsyncRate;
TimeStamp mLastVsyncTimeStamp;
};
} // namespace mozilla

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

@ -6101,22 +6101,19 @@ void nsWindow::WaylandStartVsync() {
LOG_VSYNC("nsWindow::WaylandStartVsync");
WaylandVsyncSource::WaylandDisplay& display =
static_cast<WaylandVsyncSource::WaylandDisplay&>(
mWaylandVsyncSource->GetGlobalDisplay());
if (mCompositorWidgetDelegate) {
if (RefPtr<layers::NativeLayerRoot> nativeLayerRoot =
mCompositorWidgetDelegate->AsGtkCompositorWidget()
->GetNativeLayerRoot()) {
LOG_VSYNC(" use source NativeLayerRootWayland");
display.MaybeUpdateSource(nativeLayerRoot->AsNativeLayerRootWayland());
mWaylandVsyncSource->MaybeUpdateSource(
nativeLayerRoot->AsNativeLayerRootWayland());
} else {
LOG_VSYNC(" use source mContainer");
display.MaybeUpdateSource(mContainer);
mWaylandVsyncSource->MaybeUpdateSource(mContainer);
}
}
display.EnableMonitor();
mWaylandVsyncSource->EnableMonitor();
#endif
}
@ -6130,11 +6127,8 @@ void nsWindow::WaylandStopVsync() {
// 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();
display.MaybeUpdateSource(nullptr);
mWaylandVsyncSource->DisableMonitor();
mWaylandVsyncSource->MaybeUpdateSource(nullptr);
#endif
}

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

@ -878,7 +878,7 @@ class nsWindow final : public nsBaseWidget {
bool ConfigureX11GLVisual();
#endif
#ifdef MOZ_WAYLAND
RefPtr<mozilla::gfx::VsyncSource> mWaylandVsyncSource;
RefPtr<mozilla::WaylandVsyncSource> mWaylandVsyncSource;
LayoutDeviceIntPoint mNativePointerLockCenter;
zwp_locked_pointer_v1* mLockedPointer = nullptr;
zwp_relative_pointer_v1* mRelativePointer = nullptr;