@@ -46,6 +46,7 @@ Pacer::Pacer()
4646 m_Stopping(false ),
4747 m_StreamFps(0 ),
4848 m_RefreshRate(0.0 ),
49+ m_LastSyncRefreshCount(0 ),
4950 m_LastSyncQpc(0 ),
5051 m_VsyncIntervalQpc(0 ) {
5152}
@@ -193,10 +194,8 @@ void Pacer::updateFrameStats() {
193194void Pacer::waitForFrame (double timeoutMs) {
194195 if (!running ()) return ;
195196
196- // mark frame begin for use by afterPresent
197- m_BeginFrameQpc = QpcNow ();
198-
199197 // Wait for a decoded frame to be available
198+ int64_t t0 = QpcNow ();
200199 int queueHas = 1 ;
201200 FrameQueue::instance ().waitForEnqueue (queueHas, timeoutMs);
202201}
@@ -214,7 +213,9 @@ bool Pacer::renderOnMainThread(std::shared_ptr<VideoRenderer> &sceneRenderer) {
214213
215214 // Render it
216215 FQLog (" > Frame rendered [pts: %.3f]\n " , frame->pts / 90.0 );
217- sceneRenderer->Render (frame);
216+ if (!sceneRenderer->Render (frame)) {
217+ return false ; // something went wrong rendering the frame
218+ }
218219
219220 if (frame->opaque_ref ) {
220221 // Count time spent in FrameQueue
@@ -227,26 +228,23 @@ bool Pacer::renderOnMainThread(std::shared_ptr<VideoRenderer> &sceneRenderer) {
227228}
228229
229230// called by render thread
230- void Pacer::waitBeforePresent () {
231+ void Pacer::waitBeforePresent (int64_t deadline ) {
231232 if (!running ()) return ;
232233
233234 // Wait until vsync
234- int64_t now, interval = 0 ;
235- int64_t target = getNextVBlankQpc (&now, &interval);
236-
237- if (IsXbox () && m_StreamFps == 120 && m_RefreshRate > 119.0 ) {
238- // 120hz on Xbox requires us to present each frame at half-vsync intervals
239- int64_t half = interval / 2 ;
240- if (target - half > now) {
241- // we're currently in the first half of a vblank, sleep till the halfway mark
242- target -= half;
243- }
235+ int64_t now = 0 ;
236+ int64_t target = getNextVBlankQpc (&now);
237+ if (deadline && deadline != target) {
238+ // we missed the deadline
239+ target = deadline;
244240 }
245241
246242 m_LastSyncTarget.store (target, std::memory_order_release);
247243
248- FQLog (" waitBeforePresent(): waiting %.3fms to target %lld\n " , QpcToMs (target - now), target);
249- SleepUntilQpc (target);
244+ if (target > now) {
245+ FQLog (" waitBeforePresent(): waiting %.3fms\n " , QpcToMs (target - now));
246+ SleepUntilQpc (target);
247+ }
250248}
251249
252250// end main thread
@@ -267,23 +265,34 @@ void Pacer::submitFrame(AVFrame *frame) {
267265
268266// Caller often needs now and the vsync interval, since this needs locking
269267// the logic is confined to this function.
270- int64_t Pacer::getNextVBlankQpc (int64_t *now, int64_t *interval ) {
268+ int64_t Pacer::getNextVBlankQpc (int64_t *now) {
271269 std::scoped_lock<std::mutex> lock (m_FrameStatsLock);
270+ int64_t target, interval = 0 ;
271+ *now = QpcNow ();
272272
273273 if (m_LastSyncQpc == 0 || m_VsyncIntervalQpc == 0 ) {
274274 // Fallback until vsyncHardware spins up
275- *now = QpcNow ();
276275 double rr = m_RefreshRate > 0.0 ? m_RefreshRate : 60.0 ;
277- *interval = MsToQpc (1000.0 / rr);
278- return *now + *interval;
279- }
276+ interval = MsToQpc (1000.0 / rr);
277+ target = *now + interval;
278+ } else {
279+ interval = m_VsyncIntervalQpc;
280+ int64_t next = m_LastSyncQpc;
280281
281- *now = QpcNow ();
282- *interval = m_VsyncIntervalQpc;
283- int64_t next = m_LastSyncQpc;
282+ while (next < *now) {
283+ next += interval;
284+ }
285+ target = next + static_cast <int64_t >(m_ewmaVsyncDriftQpc);
286+ }
284287
285- while (next < *now) {
286- next += *interval;
288+ if (IsXbox () && m_StreamFps == 120 && m_RefreshRate > 119.0 ) {
289+ // 120hz on Xbox requires us to present each frame at half-vsync intervals
290+ int64_t half = interval / 2 ;
291+ if (target - half > *now) {
292+ // we're currently in the first half of a vblank, sleep till the halfway mark
293+ target -= half;
294+ }
287295 }
288- return next + static_cast <int64_t >(m_ewmaVsyncDriftQpc);
296+
297+ return target;
289298}
0 commit comments