mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-08 08:10:30 -10:00
runloop: name the frame pacing sources
The loop is held to a rate by four mechanisms, each of which decides on its own whether to block, and none of which knows about the others: video_vsync present blocks in the video driver audio_sync audio_driver_write() blocks on the buffer video_scanline_sync after_frame waits on the vblank-locked target frame limit a timer sleep at the end of runloop_iterate() They are not exclusive and they are not prioritised. Any combination can be live on the same frame, which is why VSync together with audio_sync stutters - two hardware clocks holding one loop - and why the RUNLOOP_STATE_MENU throttle has a comment special-casing audio backpressure against the timer. Each overlap that has been found so far was handled where it was found. This does not change which of them fires. It records the composition for the current frame in runloop_state_t::pace as a bitmask, computed once from the same conditions each mechanism already tests, and gates the timer sleep on its bit. The timer's condition is copied verbatim, so the gate is boolean-equivalent to the inline test it replaces; the other three still block inside their own subsystems and are only recorded here. A bitmask rather than an enum deliberately. A single value would assert that the loop chooses one source, and it does not. Making that choice - hardware clocks over the timer, one hardware clock at a time - is a behaviour change with a configuration-dependent blast radius, and it belongs in its own commit argued on its own. This gives it one place to be made.
This commit is contained in:
1 parent
6869b9df65
commit
542c5abdbc
2 files changed
+51
-1
No files matched your search
@@ -8263,9 +8263,21 @@ end:
|
||||
runloop_set_frame_limit(&video_st->av_info, 1.0f);
|
||||
}
|
||||
|
||||
/* if there's a fast forward limit, inject sleeps to keep from going too fast. */
|
||||
/* Record which sources hold the pace this frame. The other three
|
||||
* block inside their own subsystems; only the timer is decided
|
||||
* here. See enum runloop_pace_source. */
|
||||
runloop_st->pace = RUNLOOP_PACE_NONE;
|
||||
if (settings->bools.video_vsync)
|
||||
runloop_st->pace |= RUNLOOP_PACE_VSYNC;
|
||||
if ( settings->bools.audio_sync
|
||||
&& (AUDIO_FLAGS_GET(audio_st) & AUDIO_FLAG_ACTIVE))
|
||||
runloop_st->pace |= RUNLOOP_PACE_AUDIO;
|
||||
if ( settings->bools.video_scanline_sync
|
||||
&& video_st->scanline[SCANLINE_NEXT])
|
||||
runloop_st->pace |= RUNLOOP_PACE_SCANLINE;
|
||||
{
|
||||
retro_time_t frame_limit_min = runloop_st->frame_limit_minimum_time;
|
||||
/* Identical to the condition the sleep below used inline. */
|
||||
if ( (frame_limit_min)
|
||||
&& ( (vrr_runloop_enable)
|
||||
|| (runloop_st->flags & RUNLOOP_FLAG_FASTMOTION)
|
||||
@@ -8275,6 +8287,13 @@ end:
|
||||
|| !(runloop_st->flags & RUNLOOP_FLAG_FOCUSED)))
|
||||
#endif
|
||||
|| (runloop_st->flags & RUNLOOP_FLAG_PAUSED)))
|
||||
runloop_st->pace |= RUNLOOP_PACE_TIMER;
|
||||
}
|
||||
|
||||
/* if there's a fast forward limit, inject sleeps to keep from going too fast. */
|
||||
{
|
||||
retro_time_t frame_limit_min = runloop_st->frame_limit_minimum_time;
|
||||
if (runloop_st->pace & RUNLOOP_PACE_TIMER)
|
||||
{
|
||||
const retro_time_t end_frame_time = cpu_features_get_time_usec();
|
||||
const retro_time_t to_sleep_us = (
|
||||
|
||||
@@ -181,6 +181,7 @@ struct runloop
|
||||
retro_time_t core_run_time;
|
||||
retro_time_t frame_limit_minimum_time;
|
||||
retro_time_t frame_limit_last_time;
|
||||
unsigned pace; /* enum runloop_pace_source bits */
|
||||
retro_usec_t frame_time_last; /* int64_t alignment */
|
||||
|
||||
/* Per-frame scalar state. Kept adjacent to the timing block above so the
|
||||
@@ -342,6 +343,36 @@ struct runloop
|
||||
bool content_closing;
|
||||
};
|
||||
|
||||
/* Frame pacing sources.
|
||||
*
|
||||
* The loop is held to a rate by whichever of these block on a given
|
||||
* frame. They are not exclusive and they are not prioritised: VSync
|
||||
* blocks in the video driver's present, audio backpressure blocks in
|
||||
* audio_driver_write(), Scanline Sync waits in
|
||||
* video_driver_scanline_after_frame(), and the frame-limit timer sleeps
|
||||
* at the end of runloop_iterate(). Any combination can be live on the
|
||||
* same frame, which is why VSync together with audio_sync stutters -
|
||||
* two hardware clocks holding the same loop - and why the comment at
|
||||
* the RUNLOOP_STATE_MENU throttle special-cases audio backpressure
|
||||
* against the timer.
|
||||
*
|
||||
* runloop_state_t::pace records the composition for the current
|
||||
* frame, computed once in runloop_pace_compute() from the same
|
||||
* conditions each mechanism already tests. It does not yet choose
|
||||
* between them: this is the composition made explicit, so that a
|
||||
* priority can be decided in one place later rather than by adding a
|
||||
* fourth special case in a third file. A bitmask rather than an enum
|
||||
* for exactly that reason - a single value would assert a choice the
|
||||
* loop does not currently make. */
|
||||
enum runloop_pace_source
|
||||
{
|
||||
RUNLOOP_PACE_NONE = 0,
|
||||
RUNLOOP_PACE_VSYNC = (1 << 0), /* display: present blocks */
|
||||
RUNLOOP_PACE_AUDIO = (1 << 1), /* audio crystal: write blocks */
|
||||
RUNLOOP_PACE_SCANLINE = (1 << 2), /* display: vblank-locked wait */
|
||||
RUNLOOP_PACE_TIMER = (1 << 3) /* CPU counter: frame-limit sleep */
|
||||
};
|
||||
|
||||
typedef struct runloop runloop_state_t;
|
||||
|
||||
RETRO_BEGIN_DECLS
|
||||
|
||||
Reference in new issue
Block a user