From ba5071a7ef225da20fb2a525a555ef4f6ba86049 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Michaj=C5=82ow?= Date: Tue, 14 Nov 2023 09:51:11 +0100 Subject: [PATCH] vo_gpu_next: add validation for invalid pl_queue usage This is mainly for debugging purposes, as it should't happen in normal use. If it does, it needs fixing. --- video/out/vo_gpu_next.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/video/out/vo_gpu_next.c b/video/out/vo_gpu_next.c index 869a11d54d..f079d7570e 100644 --- a/video/out/vo_gpu_next.c +++ b/video/out/vo_gpu_next.c @@ -874,6 +874,7 @@ static void draw_frame(struct vo *vo, struct vo_frame *frame) bool cache_frame = will_redraw || frame->still; bool can_interpolate = opts->interpolation && frame->display_synced && !frame->still && frame->num_frames > 1; + double pts_offset = can_interpolate ? frame->ideal_frame_vsync : 0; params.info_callback = info_callback; params.info_priv = vo; params.skip_caching_single_frame = !cache_frame; @@ -881,6 +882,27 @@ static void draw_frame(struct vo *vo, struct vo_frame *frame) if (frame->still) params.frame_mixer = NULL; + // pl_queue advances its internal virtual PTS and culls available frames + // based on this value and the VPS/FPS ratio. Requesting a non-monotonic PTS + // is an invalid use of pl_queue. Reset it if this happens in an attempt to + // recover as much as possible. Ideally, this should never occur, and if it + // does, it should be corrected. The ideal_frame_vsync may be negative if + // the last draw did not align perfectly with the vsync. In this case, we + // should have the previous frame available in pl_queue, or a reset is + // already requested. Clamp the check to 0, as we don't have the previous + // frame in vo_frame anyway. + struct pl_source_frame vpts; + if (frame->current && !p->want_reset) { + if (pl_queue_peek(p->queue, 0, &vpts) && + frame->current->pts + MPMAX(0, pts_offset) < vpts.pts) + { + MP_VERBOSE(vo, "Forcing queue refill, PTS(%f + %f | %f) < VPTS(%f)\n", + frame->current->pts, pts_offset, + frame->ideal_frame_vsync_duration, vpts.pts); + p->want_reset = true; + } + } + // Push all incoming frames into the frame queue for (int n = 0; n < frame->num_frames; n++) { int id = frame->frame_id + n; @@ -929,7 +951,6 @@ static void draw_frame(struct vo *vo, struct vo_frame *frame) struct pl_swapchain_frame swframe; struct ra_swapchain *sw = p->ra_ctx->swapchain; - double pts_offset = can_interpolate ? frame->ideal_frame_vsync : 0; bool should_draw = sw->fns->start_frame(sw, NULL); // for wayland logic if (!should_draw || !pl_swapchain_start_frame(p->sw, &swframe)) { if (frame->current) {