summaryrefslogtreecommitdiffstats
path: root/video/out
diff options
context:
space:
mode:
Diffstat (limited to 'video/out')
-rw-r--r--video/out/opengl/context_glx.c101
-rw-r--r--video/out/opengl/oml_sync.c112
-rw-r--r--video/out/opengl/oml_sync.h28
3 files changed, 145 insertions, 96 deletions
diff --git a/video/out/opengl/context_glx.c b/video/out/opengl/context_glx.c
index 1f3225a9ac..7dc8062c1c 100644
--- a/video/out/opengl/context_glx.c
+++ b/video/out/opengl/context_glx.c
@@ -40,6 +40,7 @@
#include "osdep/timer.h"
#include "video/out/x11_common.h"
#include "context.h"
+#include "oml_sync.h"
#include "utils.h"
struct priv {
@@ -49,14 +50,7 @@ struct priv {
GLXFBConfig fbc;
Bool (*XGetSyncValues)(Display*, GLXDrawable, int64_t*, int64_t*, int64_t*);
- int64_t last_ust;
- int64_t last_msc;
- int64_t last_sbc;
- int64_t last_sbc_mp_time;
- int64_t user_sbc;
- int64_t vsync_duration;
- int64_t last_skipped_vsyncs;
- int64_t last_queue_display_time;
+ struct oml_sync sync;
};
static void glx_uninit(struct ra_ctx *ctx)
@@ -232,91 +226,12 @@ static void update_vsync_oml(struct ra_ctx *ctx)
assert(p->XGetSyncValues);
- p->last_skipped_vsyncs = 0;
- p->user_sbc += 1;
-
- // This extension returns two unrelated values:
- // (ust, msc): clock time and incrementing counter of last vsync (this is
- // reported continuously, even if we don't swap)
- // sbc: swap counter of frame that was last displayed (every swap
- // increments the user_sbc, and the reported sbc is the sbc
- // of the frame that was just displayed)
- // Invariants:
- // - ust and msc change in lockstep (no value can change with the other)
- // - msc is incremented; if you query it in a loop, and your thread isn't
- // frozen or starved by the scheduler, it will usually not change or
- // be incremented by 1 (while the ust will be incremented by vsync
- // duration)
- // - sbc is never higher than the user_sbc
- // - (ust, msc) are equal to or higher by vsync increments than the display
- // time of the frame referenced by the sbc
- // Note that (ust, msc) and sbc are not locked to each other. The following
- // can easily happen if vsync skips occur:
- // - you draw a frame, in the meantime hardware swaps sbc_1
- // - another display vsync happens during drawing
- // - you call swap()
- // - query (ust, msc) and sbc
- // - sbc contains sbc_1, but (ust, msc) contains the vsync after it
- // As a consequence, it's hard to detect the latency or vsync skips.
int64_t ust, msc, sbc;
if (!p->XGetSyncValues(ctx->vo->x11->display, ctx->vo->x11->window,
&ust, &msc, &sbc))
- {
- // Assume the extension is effectively unsupported.
- p->vsync_duration = -1;
- p->last_skipped_vsyncs = -1;
- p->last_queue_display_time = -1;
- return;
- }
-
- int64_t ust_passed = p->last_ust ? ust - p->last_ust : 0;
- p->last_ust = ust;
-
- int64_t msc_passed = p->last_msc ? msc - p->last_msc : 0;
- p->last_msc = msc;
-
- int64_t sbc_passed = sbc - p->last_sbc;
- p->last_sbc = sbc;
-
- // Display frame duration. This makes assumptions about UST (see below).
- if (msc_passed && ust_passed)
- p->vsync_duration = ust_passed / msc_passed;
-
- // Only if a new frame was displayed (sbc increased) we have sort-of a
- // chance that the current (ust, msc) is for the sbc. But this is racy,
- // because skipped frames drops could have increased the msc right after the
- // display event and before we queried the values. This code hopes for the
- // best and ignores this.
- if (sbc_passed) {
- // The extension spec doesn't define what the UST is (not even its unit).
- // Simply assume UST is a simple CLOCK_MONOTONIC usec value. The swap
- // buffer call happened "some" but very small time ago, so we can get
- // away with querying the current time. There is also the implicit
- // assumption that mpv's timer and the UST use the same clock (which it
- // does on POSIX).
- struct timespec ts;
- if (clock_gettime(CLOCK_MONOTONIC, &ts))
- return;
- uint64_t now_monotonic = ts.tv_sec * 1000000LL + ts.tv_nsec / 1000;
- uint64_t ust_mp_time = mp_time_us() - (now_monotonic - ust);
-
- // Assume this is exactly when the actual display event for this sbc
- // happened. This is subject to the race mentioned above.
- p->last_sbc_mp_time = ust_mp_time;
- }
+ ust = msc = sbc = -1;
- // At least one frame needs to be actually displayed before
- // p->last_sbc_mp_time is set.
- if (!sbc)
- return;
-
- // Extrapolate from the last sbc time (when a frame was actually displayed),
- // and by adding the number of frames that were queued since to it.
- // For every unit the sbc is smaller than user_sbc, the actual display
- // is one frame ahead (assumes glx_swap_buffers() is called for every
- // vsync).
- p->last_queue_display_time =
- p->last_sbc_mp_time + (p->user_sbc - sbc) * p->vsync_duration;
+ oml_sync_swap(&p->sync, ust, msc, sbc);
}
static void glx_swap_buffers(struct ra_ctx *ctx)
@@ -332,9 +247,7 @@ static void glx_swap_buffers(struct ra_ctx *ctx)
static void glx_get_vsync(struct ra_ctx *ctx, struct vo_vsync_info *info)
{
struct priv *p = ctx->priv;
- info->vsync_duration = p->vsync_duration;
- info->skipped_vsyncs = p->last_skipped_vsyncs;
- info->last_queue_display_time = p->last_queue_display_time;
+ oml_sync_get_info(&p->sync, info);
}
static bool glx_init(struct ra_ctx *ctx)
@@ -426,10 +339,6 @@ static bool glx_init(struct ra_ctx *ctx)
if (!ra_gl_ctx_init(ctx, gl, params))
goto uninit;
- p->vsync_duration = -1;
- p->last_skipped_vsyncs = -1;
- p->last_queue_display_time = -1;
-
return true;
uninit:
diff --git a/video/out/opengl/oml_sync.c b/video/out/opengl/oml_sync.c
new file mode 100644
index 0000000000..aaa97a7311
--- /dev/null
+++ b/video/out/opengl/oml_sync.c
@@ -0,0 +1,112 @@
+#include <time.h>
+
+#include "osdep/timer.h"
+#include "oml_sync.h"
+#include "video/out/vo.h"
+
+// General nonsense about the associated extension.
+//
+// This extension returns two unrelated values:
+// (ust, msc): clock time and incrementing counter of last vsync (this is
+// reported continuously, even if we don't swap)
+// sbc: swap counter of frame that was last displayed (every swap
+// increments the user_sbc, and the reported sbc is the sbc
+// of the frame that was just displayed)
+// Invariants:
+// - ust and msc change in lockstep (no value can change with the other)
+// - msc is incremented; if you query it in a loop, and your thread isn't
+// frozen or starved by the scheduler, it will usually not change or
+// be incremented by 1 (while the ust will be incremented by vsync
+// duration)
+// - sbc is never higher than the user_sbc
+// - (ust, msc) are equal to or higher by vsync increments than the display
+// time of the frame referenced by the sbc
+// Note that (ust, msc) and sbc are not locked to each other. The following
+// can easily happen if vsync skips occur:
+// - you draw a frame, in the meantime hardware swaps sbc_1
+// - another display vsync happens during drawing
+// - you call swap()
+// - query (ust, msc) and sbc
+// - sbc contains sbc_1, but (ust, msc) contains the vsync after it
+// As a consequence, it's hard to detect the latency or vsync skips.
+
+static void oml_sync_reset(struct oml_sync *oml)
+{
+ oml->vsync_duration = -1;
+ oml->last_skipped_vsyncs = -1;
+ oml->last_queue_display_time = -1;
+}
+
+void oml_sync_swap(struct oml_sync *oml, int64_t ust, int64_t msc, int64_t sbc)
+{
+ if (!oml->state_ok)
+ oml_sync_reset(oml);
+
+ oml->last_skipped_vsyncs = 0;
+ oml->user_sbc += 1;
+
+ if (sbc < 0)
+ return;
+
+ oml->state_ok = true;
+
+ int64_t ust_passed = oml->last_ust ? ust - oml->last_ust : 0;
+ oml->last_ust = ust;
+
+ int64_t msc_passed = oml->last_msc ? msc - oml->last_msc : 0;
+ oml->last_msc = msc;
+
+ int64_t sbc_passed = sbc - oml->last_sbc;
+ oml->last_sbc = sbc;
+
+ // Display frame duration. This makes assumptions about UST (see below).
+ if (msc_passed && ust_passed)
+ oml->vsync_duration = ust_passed / msc_passed;
+
+ // Only if a new frame was displayed (sbc increased) we have sort-of a
+ // chance that the current (ust, msc) is for the sbc. But this is racy,
+ // because skipped frames drops could have increased the msc right after the
+ // display event and before we queried the values. This code hopes for the
+ // best and ignores this.
+ if (sbc_passed) {
+ // The GLX extension spec doesn't define what the UST is (not even its
+ // unit). Simply assume UST is a simple CLOCK_MONOTONIC usec value. This
+ // is what Mesa does, and what the Google EGL extension seems to imply
+ // (they mention CLOCK_MONOTONIC, but not the unit).
+ // The swap buffer call happened "some" but very small time ago, so we
+ // can get away with querying the current time. There is also the
+ // implicit assumption that mpv's timer and the UST use the same clock
+ // (which it does on POSIX).
+ struct timespec ts;
+ if (clock_gettime(CLOCK_MONOTONIC, &ts))
+ return;
+ uint64_t now_monotonic = ts.tv_sec * 1000000LL + ts.tv_nsec / 1000;
+ uint64_t ust_mp_time = mp_time_us() - (now_monotonic - ust);
+
+ // Assume this is exactly when the actual display event for this sbc
+ // happened. This is subject to the race mentioned above.
+ oml->last_sbc_mp_time = ust_mp_time;
+ }
+
+ // At least one frame needs to be actually displayed before
+ // oml->last_sbc_mp_time is set.
+ if (!sbc)
+ return;
+
+ // Extrapolate from the last sbc time (when a frame was actually displayed),
+ // and by adding the number of frames that were queued since to it.
+ // For every unit the sbc is smaller than user_sbc, the actual display
+ // is one frame ahead (assumes oml_sync_swap() is called for every
+ // vsync).
+ oml->last_queue_display_time =
+ oml->last_sbc_mp_time + (oml->user_sbc - sbc) * oml->vsync_duration;
+}
+
+void oml_sync_get_info(struct oml_sync *oml, struct vo_vsync_info *info)
+{
+ if (!oml->state_ok)
+ oml_sync_reset(oml);
+ info->vsync_duration = oml->vsync_duration;
+ info->skipped_vsyncs = oml->last_skipped_vsyncs;
+ info->last_queue_display_time = oml->last_queue_display_time;
+}
diff --git a/video/out/opengl/oml_sync.h b/video/out/opengl/oml_sync.h
new file mode 100644
index 0000000000..e27ccb943f
--- /dev/null
+++ b/video/out/opengl/oml_sync.h
@@ -0,0 +1,28 @@
+#pragma once
+
+#include <stdbool.h>
+#include <stdint.h>
+
+// Must be initialized to {0} by user.
+struct oml_sync {
+ bool state_ok;
+ int64_t last_ust;
+ int64_t last_msc;
+ int64_t last_sbc;
+ int64_t last_sbc_mp_time;
+ int64_t user_sbc;
+ int64_t vsync_duration;
+ int64_t last_skipped_vsyncs;
+ int64_t last_queue_display_time;
+};
+
+struct vo_vsync_info;
+
+// This must be called on every SwapBuffer call. Pass the ust/msc/sbc values
+// returned by a successful GetSyncValues call. Pass -1 for all these 3 values
+// if GetSyncValues returned failure (but note that you need to set them to -1
+// manually).
+void oml_sync_swap(struct oml_sync *oml, int64_t ust, int64_t msc, int64_t sbc);
+
+// Can be called any time; returns state determined by last oml_sync_swap() call.
+void oml_sync_get_info(struct oml_sync *oml, struct vo_vsync_info *info);