Compare commits

...
3 changed files with 60 additions and 0 deletions

No files matched your search

+8
View File
@@ -45,5 +45,13 @@ library list, before anything is published.
`GAMESCOPE_FOCUSABLE_WINDOWS`, so the client kept its loading screen over the running game
(Skyrim SE, A Plague Tale: Innocence, on the games' own Xwayland). MapNotify now refreshes the flag.
- `0115-vblank-timer-once-per-refresh.patch` - this app: host presentation feedback can re-arm
the nested vblank timer after it already woke for a refresh. As the draw offset decays, the
same refresh's new wakeup can be ahead of the current time, so it fires twice and advances the
FPS limiter's counter twice. A 30 FPS FIFO game then reaches the host 1 or 3 refreshes apart
instead of 2, particularly with MangoHud redrawing between game frames. Remember the last
timerfd refresh dispatched and schedule beyond it, allowing small host-clock corrections.
This does not change the frame-sync/nudge path or add compositor buffering.
Sixteen more of Armada's patches are DRM/lease/HDR-on-KMS work for a native display, which this
app's Wayland-hosted gamescope never reaches, or need a newer gamescope than the runtime has.
@@ -0,0 +1,36 @@
--- a/src/vblankmanager.cpp
+++ b/src/vblankmanager.cpp
@@ -89,7 +89,12 @@
uint64_t ulTargetPoint = GetLastVBlank() + ulIntervalNSecs - ulOffset;
- while ( ulTargetPoint < ulNow )
+ // Host feedback can re-arm us after we already woke for this refresh.
+ // A decaying draw offset then puts the same refresh's wakeup ahead of
+ // now again. Do not count it twice; allow small host-clock corrections.
+ const uint64_t ulLastTimerVBlank = m_ulLastTimerVBlank.load();
+ while ( ulTargetPoint < ulNow ||
+ ulTargetPoint + ulOffset < ulLastTimerVBlank + ulIntervalNSecs / 2 )
ulTargetPoint += ulIntervalNSecs;
return ulTargetPoint;
@@ -299,6 +304,8 @@
return;
+ m_ulLastTimerVBlank = m_TimerFDSchedule.ulTargetVBlank;
+
m_PendingVBlank = VBlankTime
{
.schedule = m_TimerFDSchedule,
--- a/src/vblankmanager.hpp
+++ b/src/vblankmanager.hpp
@@ -69,6 +69,8 @@
uint64_t m_ulTargetVBlank = 0;
std::atomic<uint64_t> m_ulLastVBlank = { 0 };
+ // Last timerfd refresh dispatched; the frame-sync/nudge path is unchanged.
+ std::atomic<uint64_t> m_ulLastTimerVBlank = { 0 };
std::atomic<bool> m_bArmed = { false };
std::atomic<bool> m_bRunning = { true };
@@ -0,0 +1,16 @@
--- a/src/Backends/WaylandBackend.cpp
+++ b/src/Backends/WaylandBackend.cpp
@@ -1183,7 +1183,12 @@
wl_display_flush( m_pBackend->GetDisplay() );
GetVBlankTimer().UpdateWasCompositing( bNeedsFullComposite );
- GetVBlankTimer().UpdateLastDrawTime( get_time_in_nanos() - g_SteamCompMgrVBlankTime.ulWakeupTime );
+ // Direct planes still need time for the host compositor to dispatch and draw.
+ // Measuring only our submission lets the offset decay towards the host's
+ // latch deadline, so small dispatch delays make a capped frame miss a refresh.
+ GetVBlankTimer().UpdateLastDrawTime( std::max<uint64_t>(
+ get_time_in_nanos() - g_SteamCompMgrVBlankTime.ulWakeupTime,
+ CVBlankTimer::kDefaultVBlankDrawTimeMinCompositing ) );
m_pBackend->PollState();