diff options
author | wm4 <wm4@nowhere> | 2017-03-23 16:19:21 +0100 |
---|---|---|
committer | wm4 <wm4@nowhere> | 2017-03-23 16:19:21 +0100 |
commit | f36187a432cb38692ea952fbd8034441f58943ed (patch) | |
tree | 155804ba36701f841c583215544af1b78e712b99 | |
parent | a398b4c09c16783a4f2aa50382c0bf2fea6fc042 (diff) | |
download | mpv-frame-stats-wip.tar.bz2 mpv-frame-stats-wip.tar.xz |
more WIP I guessframe-stats-wip
very low I-know-what-I'm-doing levels and also low it's-working levels
-rw-r--r-- | video/out/opengl/context_vdpau.c | 53 | ||||
-rw-r--r-- | video/out/vo.c | 72 | ||||
-rw-r--r-- | video/out/vo.h | 69 |
3 files changed, 117 insertions, 77 deletions
diff --git a/video/out/opengl/context_vdpau.c b/video/out/opengl/context_vdpau.c index 4d5d2538e2..b5e33e2c92 100644 --- a/video/out/opengl/context_vdpau.c +++ b/video/out/opengl/context_vdpau.c @@ -21,6 +21,7 @@ #define MP_GET_GLX_WORKAROUNDS #include "header_fixes.h" +#include "osdep/timer.h" #include "video/vdpau.h" #include "video/out/x11_common.h" #include "context.h" @@ -32,6 +33,7 @@ #define NUM_SURFACES 4 struct surface { + int64_t frame_id; int w, h; VdpOutputSurface surface; // This nested shitshow of handles to the same object piss me off. @@ -49,6 +51,7 @@ struct priv { int num_surfaces; struct surface surfaces[NUM_SURFACES]; int current_surface; + int64_t frame_id; }; typedef GLXContext (*glXCreateContextAttribsARBProc) @@ -344,6 +347,8 @@ static void glx_start_frame(struct MPGLContext *ctx) gl->VDPAUMapSurfacesNV(1, &surface->registered); surface->mapped = true; } + + p->frame_id++; } static void glx_swap_buffers(struct MPGLContext *ctx) @@ -365,9 +370,56 @@ static void glx_swap_buffers(struct MPGLContext *ctx) 0, 0, 0); CHECK_VDP_WARNING(ctx, "trying to present vdp surface"); + surface->frame_id = p->frame_id; + p->current_surface = (p->current_surface + 1) % p->num_surfaces; } +static void glx_get_frame_statistics(struct MPGLContext *ctx, + struct vo_frame_statistics *st) +{ + struct priv *p = ctx->priv; + struct vdp_functions *vdp = &p->vdp->vdp; + VdpStatus vdp_st; + + // Find the most recently displayed frame. + for (int n = 0; n < p->num_surfaces; n++) { + int index = p->current_surface - 1 - n; + while (index < 0) + index += p->num_surfaces; + index = index % p->num_surfaces; + struct surface *surface = &p->surfaces[index]; + + if (surface->surface == VDP_INVALID_HANDLE) + continue; + + VdpPresentationQueueStatus status = 0; + VdpTime visible_time = 0; + vdp_st = vdp->presentation_queue_query_surface_status(p->vdp_queue, + surface->surface, &status, &visible_time); + CHECK_VDP_WARNING(ctx, "trying to present vdp queue status"); + + // A surface can be VISIBLE or IDLE (= was visible, or never queued). + // Checking for VISIBLE is racy, so we try to allow for IDLE too. + if (!visible_time || (status != VDP_PRESENTATION_QUEUE_STATUS_VISIBLE && + status != VDP_PRESENTATION_QUEUE_STATUS_QUEUED)) + continue; + + VdpTime now; + vdp_st = vdp->presentation_queue_get_time(p->vdp_queue, &now); + CHECK_VDP_WARNING(ctx, "getting current time failed"); + if (!now) + break; + + // Assume vdpau time is running roughly at the same speed as ours. + st->hw_visible_frame_time_us = + mp_time_us() + (now - visible_time + 500) / 1000; + st->hw_visible_frame_id = surface->frame_id; + } + + st->most_recent_frame_id = p->frame_id; +} + static void glx_wakeup(struct MPGLContext *ctx) { vo_x11_wakeup(ctx->vo); @@ -385,6 +437,7 @@ const struct mpgl_driver mpgl_driver_vdpauglx = { .reconfig = glx_reconfig, .start_frame = glx_start_frame, .swap_buffers = glx_swap_buffers, + .get_frame_statistics = glx_get_frame_statistics, .control = glx_control, .wakeup = glx_wakeup, .wait_events = glx_wait_events, diff --git a/video/out/vo.c b/video/out/vo.c index 59a64f667b..bfd3ca5676 100644 --- a/video/out/vo.c +++ b/video/out/vo.c @@ -136,7 +136,9 @@ struct vo_internal { int num_vsync_samples; int64_t num_total_vsync_samples; int64_t prev_vsync; + int64_t real_prev_vsync; int64_t base_vsync; + int64_t latency_us; int drop_point; double estimated_vsync_interval; double estimated_vsync_jitter; @@ -165,7 +167,7 @@ struct vo_internal { static void forget_frames(struct vo *vo); static void *vo_thread(void *ptr); -#define MP_VO_FRAME_STATISTICS_INIT { .present_count = -1, } +#define MP_VO_FRAME_STATISTICS_INIT { .most_recent_frame_id = -1, } static bool get_desc(struct m_obj_desc *dst, int index) { @@ -331,6 +333,7 @@ static void reset_vsync_timings(struct vo *vo) struct vo_internal *in = vo->in; in->drop_point = 0; in->base_vsync = 0; + in->real_prev_vsync = 0; in->expecting_vsync = false; in->num_successive_vsyncs = 0; } @@ -435,6 +438,33 @@ static void update_vsync_timing_after_swap(struct vo *vo, if (in->num_successive_vsyncs <= 2) return; + in->latency_us = 0; + + if (st->most_recent_frame_id > 0) { + if (st->hw_visible_frame_id > 0) { + // this is probably not sane, but seems like an ok assumption in + // display sync mode + int64_t latency_frames = + st->most_recent_frame_id - st->hw_visible_frame_id; + int64_t predicted_time_us = st->hw_visible_frame_time_us + + latency_frames * in->vsync_interval; + + in->latency_us = predicted_time_us - mp_time_us(); + + MP_WARN(vo, "present at: %lld (latency=%lld)\n", + (long long)predicted_time_us, + (long long)in->latency_us); + MP_STATS(vo, "event-timed %lld present-time", (long long)predicted_time_us); + } else { + MP_WARN(vo, "present time unknown\n"); + MP_STATS(vo, "signal present-unknown"); + } + } + + // eh?? + now += in->latency_us; + in->prev_vsync = now; + if (in->num_vsync_samples >= MAX_VSYNC_SAMPLES) in->num_vsync_samples -= 1; MP_TARRAY_INSERT_AT(in, in->vsync_samples, in->num_vsync_samples, 0, @@ -459,44 +489,6 @@ static void update_vsync_timing_after_swap(struct vo *vo, MP_STATS(vo, "value %f jitter", in->estimated_vsync_jitter); MP_STATS(vo, "value %f vsync-diff", in->vsync_samples[0] / 1e6); - - if (st->present_count >= 0) { - int64_t present_time = 0; - if (st->predicted_present_time_us > 0) { - present_time = st->predicted_present_time_us; - } else if (st->predicted_present_count > 0 && st->hw_present_count > 0) { - // obviously both hw_present_* fields should be set if any is set - assert(st->hw_present_vsync_count); - // hw_last_vsync_count < hw_present_vsync_count not allowed - assert(st->hw_last_vsync_count >= st->hw_present_vsync_count); - // both hw_last_vsync_* fields should be set if any is set - assert(st->hw_last_vsync_time_us); - // obviously the predicted count must be higher than whatever what - // was shown already (or == if the frame was shown) - assert(st->predicted_present_count >= st->hw_present_count); - - // this is optional here, but for now simplifies the mess below - assert(st->hw_last_vsync_time_us > 0); - - // if hw_last_vsync_count is ahead of hw_present_vsync_count, - // get the time for hw_present_vsync_count - int64_t hw_present_time_us = st->hw_last_vsync_time_us - - (st->hw_last_vsync_count - st->hw_present_vsync_count) - * st->hw_last_vsync_time_us; - - present_time = hw_present_time_us - - (st->predicted_present_count - st->hw_present_count) - * st->hw_last_vsync_time_us; - } - - if (present_time) { - MP_WARN(vo, "present at: %lld\n", (long long)present_time); - MP_STATS(vo, "event-timed %lld present-time", (long long)present_time); - } else { - MP_WARN(vo, "present time unknown\n"); - MP_STATS(vo, "signal present-unknown"); - } - } } // to be called from VO thread only @@ -1190,7 +1182,7 @@ double vo_get_delay(struct vo *vo) assert (!in->frame_queued); int64_t res = 0; if (in->base_vsync && in->vsync_interval > 1 && in->current_frame) { - res = in->base_vsync; + res = in->base_vsync + in->latency_us; int extra = !!in->rendering; res += (in->current_frame->num_vsyncs + extra) * in->vsync_interval; if (!in->current_frame->display_synced) diff --git a/video/out/vo.h b/video/out/vo.h index 796a7dcc83..303d9f47e9 100644 --- a/video/out/vo.h +++ b/video/out/vo.h @@ -230,56 +230,51 @@ struct vo_frame { // Note: all fields are usually initialized to 0 or -1 by the caller, so that // the callee does not need to touch unsupported fields. // Where possible, all values should be consistent and acquired atomically. -// For async present, present_count must be valid, and one of the following -// groups of fields must have all fields set: -// - present_count, predicted_present_time_us -// - present_count, predicted_present_count, hw_present_*, hw_last_vsync_* struct vo_frame_statistics { - // Increased on every vo_driver.flip_page call. + // An identification for the most recently submitted frame. Typicall + // increased on every vo_driver.flip_page call, but being strictly + // monotonically increasing is good enough. // You can reset this (and all other fields) to 0 to signal discontinuities. - // (OML_sync_control: SBC returned by glXGetSyncValuesOML().) - int64_t present_count; + int64_t most_recent_frame_id; - // present_count + predicted number of frames it will take to display the - // frame. - // (OML_sync_control: glXSwapBuffersMscOML() return value, SBC + latency.) - int64_t predicted_present_count; + // Predicted number of vsyncs it will take to display the most recently + // submitted frame. + //int64_t predicted_frame_latency; - // Absolute time in mp_time_us() time at which the frame will most likely - // be shown. (This is like predicted_present_count, but can include the - // clock time offset to the vsync event.) - int64_t predicted_present_time_us; + // Absolute time in mp_time_us() time at which the most recently submitted + // frame will most likely be displayed. + //int64_t predicted_display_time_us; - // The present_count of the last frame that was actually displayed. This - // must be set to 0 if no frame was displayed yet. - // (OML_sync_control: SBC returned by glXGetSyncValuesOML().) - int64_t hw_present_count; + // The frame ID of the most recent frame that was known to be displayed. + // It is possible that this information is outdated by a few frames, in + // which case the consumer has to interpolate timings. + int64_t hw_visible_frame_id; + + // The system time in mp_time_us() time at which the frame corresponding to + // the hw_visible_frame_id field was displayed. + int64_t hw_visible_frame_time_us; // The hardware vsync counter at the time the frame corresponding to the - // hw_present_count field was presented. (The hardware vsync counter is - // incremented on each display refresh, even if no frame is presented. This - // field contains the counter value when the frame was presented.) - // (OML_sync_control: MSC returned by glXGetSyncValuesOML().) - int64_t hw_present_vsync_count; - - // Last known hardware vsync count. This could be either the last frame that - // was presented by the VO, or the last hardware vsync that happened (even - // if no frame was presented, or the last frame was presented at a hw vsync - // before this). In particular, hw_last_vsync_count > hw_present_vsync_count - // is possible (but < is not allowed). - // (OML_sync_control: always the same value as hw_present_vsync_count, - // because it always refers to the last presented frame.) + // hw_visible_frame_id field was displayed. (The hardware vsync counter is + // incremented on each display refresh, even if no frame is displayed. This + // field contains the counter value when the frame was displayed.) + int64_t hw_visible_frame_vsync_count; + + // Last known hardware vsync count. This can be higher than + // hw_visible_frame_vsync_count in case the underlying API reports vsync + // timestamps separately from frame display events (i.e. the vsync counter + // could be newer, and after the first time the hw_visible_frame_id became + // visible. int64_t hw_last_vsync_count; // Absolute time in mp_time_us() time at which hw_last_vsync_count was // incremented most recently. Graphics APIs will use something different // than mp_time_us(), so you have to rebase the values to mp_time_us(). - // (OML_sync_control: UST returned by glXGetSyncValuesOML().) int64_t hw_last_vsync_time_us; - // The length of a display frame in microseconds. this doesn't not need to - // be set if VOCTRL_GET_DISPLAY_FPS returns a better value. - // (OML_sync_control: roughly what glXGetMscRateOML() returns.) + // The length of a display frame in microseconds. This doesn't not need to + // be set if VOCTRL_GET_DISPLAY_FPS returns a better value. This value is + // supposed to be nominal, i.e. rarely changes. int64_t nominal_vsync_duration_us; }; @@ -288,7 +283,7 @@ struct vo_frame_statistics { struct vo_frame_statistics_simple { // mp_time() time at which to display vsync int64_t predicted_display_time_us; - // timing error for the most recently presented frame + // timing error for the most recently displayed frame // (we only expect display too late) int64_t delayed_time_us; int64_t nominal_vsync_duration_us; |