зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
7961a431ef
Коммит
47c6beb1ee
|
@ -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;
|
||||
|
|
Загрузка…
Ссылка в новой задаче