author | Mason Chang <mchang@mozilla.com> |
Tue, 26 Jul 2016 11:12:24 -0700 | |
changeset 306858 | 445065352cf082627b3865c08dfe32ca328cb3ec |
parent 306857 | 7d4e238cd15e480c122500bc9b6bfdf52059a177 |
child 306859 | 54e8922be9ef98d9ee0ade82480d2876ae9bd1df |
push id | 79947 |
push user | mchang@mozilla.com |
push date | Wed, 27 Jul 2016 15:36:37 +0000 |
treeherder | mozilla-inbound@445065352cf0 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | jrmuizel |
bugs | 1278408 |
milestone | 50.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/gfx/thebes/gfxWindowsPlatform.cpp +++ b/gfx/thebes/gfxWindowsPlatform.cpp @@ -1743,17 +1743,16 @@ public: public: D3DVsyncDisplay() : mPrevVsync(TimeStamp::Now()) , mVsyncEnabledLock("D3DVsyncEnabledLock") , mVsyncEnabled(false) { mVsyncThread = new base::Thread("WindowsVsyncThread"); const double rate = 1000 / 60.0; - mSoftwareVsyncRate = TimeDuration::FromMilliseconds(rate); MOZ_RELEASE_ASSERT(mVsyncThread->Start(), "GFX: Could not start Windows vsync thread"); SetVsyncRate(); } void SetVsyncRate() { if (!gfxWindowsPlatform::GetPlatform()->DwmCompositionEnabled()) { mVsyncRate = TimeDuration::FromMilliseconds(1000.0 / 60.0); @@ -1822,91 +1821,90 @@ public: return mVsyncRate; } void ScheduleSoftwareVsync(TimeStamp aVsyncTimestamp) { MOZ_ASSERT(IsInVsyncThread()); NS_WARNING("DwmComposition dynamically disabled, falling back to software timers"); - TimeStamp nextVsync = aVsyncTimestamp + mSoftwareVsyncRate; + TimeStamp nextVsync = aVsyncTimestamp + mVsyncRate; TimeDuration delay = nextVsync - TimeStamp::Now(); if (delay.ToMilliseconds() < 0) { delay = mozilla::TimeDuration::FromMilliseconds(0); } mVsyncThread->message_loop()->PostDelayedTask( NewRunnableMethod(this, &D3DVsyncDisplay::VBlankLoop), delay.ToMilliseconds()); } - TimeStamp GetAdjustedVsyncTimeStamp(LARGE_INTEGER& aFrequency, - QPC_TIME& aQpcVblankTime) + // 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 = WinUtils::dwmGetCompositionTimingInfoPtr(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 - aQpcVblankTime; - int64_t usAdjust = (adjust * microseconds) / aFrequency.QuadPart; + 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. Sometimes, the previous vsync - // is still in the future. In these error cases, - // we try to normalize to Now(). - TimeStamp upcomingVsync = vsync; - if (upcomingVsync < mPrevVsync) { - // Windows can report a vsync that's before - // the previous one. So update it to sometime in the future. - upcomingVsync = TimeStamp::Now() + TimeDuration::FromMilliseconds(1); + // the previously seen vsync. + // In these error cases, normalize to Now(); + if (vsync >= now) { + vsync = vsync - mVsyncRate; + return vsync <= now ? vsync : now; } + } - vsync = mPrevVsync; - mPrevVsync = upcomingVsync; - } // On Windows 7 and 8, DwmFlush wakes up AFTER qpcVBlankTime // from DWMGetCompositionTimingInfo. We can return the adjusted vsync. - - // Once in a while, the reported vsync timestamp can be in the future. - // Normalize the reported timestamp to now. - if (vsync >= TimeStamp::Now()) { - vsync = TimeStamp::Now(); + // If we got here on Windows 10, it means we got a weird timestamp. + if (vsync >= now) { + vsync = now; } return vsync; } void VBlankLoop() { MOZ_ASSERT(IsInVsyncThread()); MOZ_ASSERT(sizeof(int64_t) == sizeof(QPC_TIME)); - DWM_TIMING_INFO vblankTime; - // Make sure to init the cbSize, otherwise GetCompositionTiming will fail - vblankTime.cbSize = sizeof(DWM_TIMING_INFO); - - LARGE_INTEGER frequency; - QueryPerformanceFrequency(&frequency); TimeStamp vsync = TimeStamp::Now(); - // On Windows 10, DwmGetCompositionInfo returns the upcoming vsync. - // See GetAdjustedVsyncTimestamp. - // On start, set mPrevVsync to the "next" vsync - // So we'll use this timestamp on the 2nd loop iteration. - mPrevVsync = vsync + mSoftwareVsyncRate; + mPrevVsync = TimeStamp(); + TimeStamp flushTime = TimeStamp::Now(); + TimeDuration longVBlank = mVsyncRate * 2; for (;;) { { // scope lock MonitorAutoLock lock(mVsyncEnabledLock); if (!mVsyncEnabled) return; } // Large parts of gecko assume that the refresh driver timestamp @@ -1918,47 +1916,65 @@ public: // 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; } - // Use a combination of DwmFlush + DwmGetCompositionTimingInfoPtr - // Using WaitForVBlank, the whole system dies :/ + // Using WaitForVBlank, the whole system dies because WaitForVBlank + // only works if it's run on the same thread as the Present(); HRESULT hr = WinUtils::dwmFlushProcPtr(); if (!SUCCEEDED(hr)) { - // We don't actually know how long we had to wait on DWMFlush - // Instead of trying to calculate how long DwmFlush actually took - // Fallback to software vsync. + // DWMFlush isn't working, fallback to software vsync. ScheduleSoftwareVsync(TimeStamp::Now()); return; } - hr = WinUtils::dwmGetCompositionTimingInfoPtr(0, &vblankTime); - vsync = SUCCEEDED(hr) ? - GetAdjustedVsyncTimeStamp(frequency, vblankTime.qpcVBlank) : - TimeStamp::Now(); + 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(); + } + + mPrevVsync = vsync; + } } // end for } private: virtual ~D3DVsyncDisplay() { MOZ_ASSERT(NS_IsMainThread()); } bool IsInVsyncThread() { return mVsyncThread->thread_id() == PlatformThread::CurrentId(); } - TimeDuration mSoftwareVsyncRate; - TimeStamp mPrevVsync; // Only used on Windows 10 + TimeStamp mPrevVsync; Monitor mVsyncEnabledLock; base::Thread* mVsyncThread; TimeDuration mVsyncRate; bool mVsyncEnabled; }; // end d3dvsyncdisplay D3DVsyncSource() {