Skip to content

Commit c92962d

Browse files
authored
vo: add vrr functionality for non-display sync modes
The intention is for vrr mode to be a change to base behaviour, and thus be compatible with every selection of video-sync modes. However, currently it is only compatible for non-display sync ones. For display sync modes in the future, the expectation is to continue using this vrr structure, but just have vo immediatelly output frames one after the other like display sync does, and then use api functions like presentAt to present the frame based on the data from voframe. The expectation would be for display-sync vrr to exclude some of the estimations that display-sync uses, since they are not required and for cleaner design. One other expectation is for the driver to immediatelly drop frames when the swapchain is fully filled out, so that vo can then decide whether to move to a newer frame or recalculate the dropped frame position before trying to send it again, instead of the driver dropping older valid frames, or delaying the newer frame to later. Additional changes: #. merged the repeat functionality from display-sync modes into a new variable that can be used by vrr. it is not expected but it is possible for display-sync behaviour to have changed because of that. #. moved in->wakeup_pts set up inside render_frame instead of vo_queue_frame. This is because it doesn't appear to have a pre-render_frame purpose inside vo_queue_frame, and to allow decision based set up in render_frame. #. we now directly update frame-> information when pts and duration changes (i.e like when "now" has moved past our pts), to give the driver accurate data. #. after doing the initial duration < 0 check, we then clamp duration to 0, as non-valid negative duration input messes up the logic. #. display-sync now also uses a simple pts + duration for its in->wakeup_pts instead of an "= 0" special case. Since it already modifies pts to 0 and duration to -1 -> going to 0, end time is expected to also result in 0. Trying to re-use existing logic than making a lot of special cases. #. The "strict drop threshold" has been changed to include the case where end_time == now (as per duration <= 0), instead of just end_time < now. The vrr logic is cleaner when we drop frames at duration = 0, and i believe makes sense. #. Added a in->current_frame check for VO_CAP_NORETAIN, since stuff may randomly set current_frame to null (like vo_reconfig?), and just generally safer. #. Clarified the VO_CAP_FRAMEOWNER condition to be compatible with vrr, and also put up that the addition of the driver_dropped_frame condition (the logic of which is exactly the same as before this change) doesn't make much sense and should be removed, but i'll leave it to somebody else.
1 parent 48e6c35 commit c92962d

10 files changed

Lines changed: 358 additions & 38 deletions

File tree

DOCS/man/input.rst

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2311,11 +2311,12 @@ Property list
23112311

23122312
``mistimed-frame-count``
23132313
Number of video frames that were not timed correctly in display-sync mode
2314-
for the sake of keeping A/V sync. This does not include external
2314+
for the sake of keeping A/V sync (i.e using the ``display-desync`` mode should
2315+
not change this value from 0), or in ``vrr-adjust`` mode for the sake of keeping
2316+
within the defined refresh rate range. This does not include external
23152317
circumstances, such as video rendering being too slow or the graphics
23162318
driver somehow skipping a vsync. It does not include rounding errors either
2317-
(which can happen especially with bad source timestamps). For example,
2318-
using the ``display-desync`` mode should never change this value from 0.
2319+
(which can happen especially with bad source timestamps).
23192320

23202321
``vsync-ratio``
23212322
For how many vsyncs a frame is displayed on average. This is available if

DOCS/man/options.rst

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1320,6 +1320,10 @@ Video
13201320
Set this option only if you have reason to believe the automatically
13211321
determined value is wrong.
13221322

1323+
``--minimum-display-fps=<fps>``
1324+
Set the minimum display FPS used with the ``--vrr-adjust=true`` mode. By
1325+
default, it uses the maximum display fps.
1326+
13231327
``--hwdec=<api1,api2,...|no|auto|auto-copy>``
13241328
Specify the hardware video decoding API that should be used if possible.
13251329
Whether hardware decoding is actually done depends on the video codec. If
@@ -8224,6 +8228,56 @@ Video Sync
82248228
frame dropping due to the audio "overshooting" and skipping multiple video
82258229
frames before the sync logic can react.
82268230

8231+
``--vrr-adjust=<yes|no>``
8232+
This option tries to increase the chance of the display being ready for
8233+
frames the moment they are sent (default: no), for cases where the
8234+
display is unreliable, primarily useful for VRR displays. This uses
8235+
``--minimum-display-fps`` and display-fps (maximum fps).
8236+
8237+
This is done by observing each individual frame and dynamically:
8238+
8239+
1. Not doing anything if they are perfectly between the minimum refresh time
8240+
and the middle refresh time.
8241+
2. Repeating frames at a middle refresh time if possible, else repeating
8242+
tending towards the middle refresh time (the middle refresh time gives
8243+
us the best error leeway in case there are random application or OS delays
8244+
that would put us in a position that exceeds either the minimum or
8245+
the maximum refresh time).
8246+
3. If we can't reliably repeat it, then we send it even if it's between the
8247+
middle refresh time and the maximum refresh time. Note that because of the
8248+
above mentioned potential delays, this may cause us to exceed our target
8249+
even if we initially appear in valid range.
8250+
4. If the next frame exceeds our minimum refresh time, whether because our
8251+
repetition failed or that's how the frames exist, we will move it to the
8252+
closest valid position that's within the refresh range. If two frames are
8253+
put on the same position, we send the newest one only.
8254+
8255+
Note: This is not compatible with ``--video-sync=display-...`` modes, yet,
8256+
which would be the only ones capable of getting rid of the last displaying
8257+
weirdnesses.
8258+
8259+
``--vrr-adjust-max-refresh-variance=<0.0-1.0>``
8260+
How much percentage wise the refresh time can vary from one frame to the next,
8261+
where 1.0 means the refresh time can immediatelly move between the minimum and
8262+
maximum refresh time, while 0 means the refresh time is static. (default: 1.0)
8263+
8264+
The default does not take into account refresh rate flicker (which may happen
8265+
in specific cases, eg. when time between frames is constantly higher than
8266+
minimum refresh time + middle refresh time), and optimizes just for reaching
8267+
target position. If that's a problem, then this option can limit sudden changes in refresh
8268+
time. The lower the value set, the more refresh time stability, the higher chance
8269+
of missing our target.
8270+
8271+
``--vrr-adjust-target-refresh-rate=<fps>``
8272+
Set the refresh rate we should tend towards, instead of the default "middle refresh
8273+
time". Setting it at a lower refresh rate will reduce the amount of repetitions,
8274+
which can help if computational burden is a problem. However, increasing it or
8275+
reducing it will come at a cost of potential chance increase of missing our target.
8276+
8277+
When needed, we will still use the entire range of ``--minimum-display-fps`` and
8278+
``--display-fps-override``, so this option only increases the chance towards
8279+
the refresh rate we tend towards.
8280+
82278281
Miscellaneous
82288282
-------------
82298283

options/options.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,8 @@ static const m_option_t mp_vo_opt_list[] = {
189189
{"show-in-taskbar", OPT_BOOL(show_in_taskbar)},
190190
{"display-fps-override", OPT_DOUBLE(display_fps_override),
191191
M_RANGE(0, DBL_MAX)},
192+
{"minimum-display-fps", OPT_DOUBLE(minimum_display_fps),
193+
M_RANGE(0, DBL_MAX)},
192194
{"video-timing-offset", OPT_DOUBLE(timing_offset), M_RANGE(0.0, 1.0)},
193195
{"video-sync", OPT_CHOICE(video_sync,
194196
{"audio", VS_DEFAULT},
@@ -200,6 +202,9 @@ static const m_option_t mp_vo_opt_list[] = {
200202
{"display-vdrop", VS_DISP_VDROP},
201203
{"display-desync", VS_DISP_NONE},
202204
{"desync", VS_NONE})},
205+
{"vrr-adjust", OPT_BOOL(vrr_adjust)},
206+
{"vrr-adjust-max-refresh-variance", OPT_DOUBLE(vrr_max_refresh_variance), M_RANGE(0.0, 1.0)},
207+
{"vrr-adjust-target-refresh-rate", OPT_DOUBLE(vrr_target_refresh_rate)},
203208
#if HAVE_X11
204209
{"x11-netwm", OPT_CHOICE(x11_netwm, {"auto", 0}, {"no", -1}, {"yes", 1})},
205210
{"x11-bypass-compositor", OPT_CHOICE(x11_bypass_compositor,
@@ -292,7 +297,10 @@ const struct m_sub_options vo_sub_opts = {
292297
.wl_present = true,
293298
.mmcss_profile = "Playback",
294299
.ontop_level = -1,
300+
.minimum_display_fps = -1,
295301
.timing_offset = 0.050,
302+
.vrr_max_refresh_variance = 1,
303+
.vrr_target_refresh_rate = -1,
296304
.swapchain_depth = 2,
297305
.focus_on = 1,
298306
},

options/options.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,12 @@ typedef struct mp_vo_opts {
8282
int window_corners;
8383

8484
double display_fps_override;
85+
double minimum_display_fps;
8586
double timing_offset;
8687
int video_sync;
88+
bool vrr_adjust;
89+
double vrr_max_refresh_variance;
90+
double vrr_target_refresh_rate;
8791

8892
struct m_geometry android_surface_size;
8993

player/command.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -765,7 +765,7 @@ static int mp_property_mistimed_frame_count(void *ctx, struct m_property *prop,
765765
int action, void *arg)
766766
{
767767
MPContext *mpctx = ctx;
768-
if (!mpctx->vo_chain || !mpctx->display_sync_active)
768+
if (!mpctx->vo_chain || !(mpctx->display_sync_active || mpctx->video_out->opts->vrr_adjust))
769769
return M_PROPERTY_UNAVAILABLE;
770770

771771
return m_property_int_ro(action, arg, mpctx->mistimed_frames_total);

player/video.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -808,6 +808,11 @@ static void handle_display_sync_frame(struct MPContext *mpctx,
808808

809809
mpctx->display_sync_active = false;
810810

811+
if (vo->opts->vrr_adjust && vo_get_pts_offset(vo) != 0) {
812+
mpctx->mistimed_frames_total += 1;
813+
MP_STATS(mpctx, "mistimed");
814+
}
815+
811816
if (!VS_IS_DISP(mode) || !vo_is_visible(vo))
812817
return;
813818

video/out/gpu/video.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3596,7 +3596,7 @@ void gl_video_render_frame(struct gl_video *p, struct vo_frame *frame,
35963596

35973597
// For the non-interpolation case, we draw to a single "cache"
35983598
// texture to speed up subsequent re-draws (if any exist)
3599-
bool repeats = frame->num_vsyncs > 1 && frame->display_synced;
3599+
bool repeats = frame->request_repeat;
36003600
bool r = false;
36013601
if ((repeats || frame->still) && !p->dumb_mode &&
36023602
(p->ra->caps & RA_CAP_BLIT) && fbo->tex->params.blit_dst)

0 commit comments

Comments
 (0)