summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2017-03-23 16:19:21 +0100
committerwm4 <wm4@nowhere>2017-03-23 16:19:21 +0100
commitf36187a432cb38692ea952fbd8034441f58943ed (patch)
tree155804ba36701f841c583215544af1b78e712b99
parenta398b4c09c16783a4f2aa50382c0bf2fea6fc042 (diff)
downloadmpv-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.c53
-rw-r--r--video/out/vo.c72
-rw-r--r--video/out/vo.h69
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;