summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2016-11-01 13:06:48 +0100
committerwm4 <wm4@nowhere>2016-11-01 16:25:40 +0100
commit17733bd5b84a7bb8caf497aa9ea35efd657e967a (patch)
tree0bf5d2ee9c50327649e595f06f374dc65396dc95
parenteedda59a6941f1b9d1968ccf75d5578718b0b08c (diff)
downloadmpv-17733bd5b84a7bb8caf497aa9ea35efd657e967a.tar.bz2
mpv-17733bd5b84a7bb8caf497aa9ea35efd657e967a.tar.xz
vo_opengl: make frame reupload logic more robust
It's not that easy to decide whether a frame needs to be reuploaded/rerendered. Using unique frame IDs for input makes it slightly easier and more robust. This also removes the use of video PTS in the interpolation path. This should also avoid reuploading the video frame if it's just redrawn in paused mode, or when using OSD/subtitles in cover art mode.
-rw-r--r--video/out/opengl/video.c60
-rw-r--r--video/out/vo.h1
2 files changed, 39 insertions, 22 deletions
diff --git a/video/out/opengl/video.c b/video/out/opengl/video.c
index dadfe705ff..17468ab9a5 100644
--- a/video/out/opengl/video.c
+++ b/video/out/opengl/video.c
@@ -104,6 +104,7 @@ struct texplane {
struct video_image {
struct texplane planes[4];
struct mp_image *mpi; // original input image
+ uint64_t id; // unique ID identifying mpi contents
bool hwdec_mapped;
};
@@ -153,6 +154,7 @@ struct tex_hook {
struct fbosurface {
struct fbotex fbotex;
+ uint64_t id;
double pts;
};
@@ -517,7 +519,8 @@ static void uninit_scaler(struct gl_video *p, struct scaler *scaler);
static void check_gl_features(struct gl_video *p);
static bool init_format(struct gl_video *p, int fmt, bool test_only);
static void init_image_desc(struct gl_video *p, int fmt);
-static bool gl_video_upload_image(struct gl_video *p, struct mp_image *mpi);
+static bool gl_video_upload_image(struct gl_video *p, struct mp_image *mpi,
+ uint64_t id);
static const char *handle_scaler_opt(const char *name, bool tscale);
static void reinit_from_options(struct gl_video *p);
static void get_scale_factors(struct gl_video *p, bool transpose_rot, double xy[2]);
@@ -565,8 +568,10 @@ void gl_video_set_debug(struct gl_video *p, bool enable)
static void gl_video_reset_surfaces(struct gl_video *p)
{
- for (int i = 0; i < FBOSURFACES_MAX; i++)
+ for (int i = 0; i < FBOSURFACES_MAX; i++) {
+ p->surfaces[i].id = 0;
p->surfaces[i].pts = MP_NOPTS_VALUE;
+ }
p->surface_idx = 0;
p->surface_now = 0;
p->frames_drawn = 0;
@@ -960,6 +965,7 @@ static void unref_current_image(struct gl_video *p)
{
unmap_current_image(p);
mp_image_unrefp(&p->image.mpi);
+ p->image.id = 0;
}
static void uninit_video(struct gl_video *p)
@@ -2552,22 +2558,23 @@ static void gl_video_interpolate_frame(struct gl_video *p, struct vo_frame *t,
// First of all, figure out if we have a frame available at all, and draw
// it manually + reset the queue if not
- if (p->surfaces[p->surface_now].pts == MP_NOPTS_VALUE) {
- if (!gl_video_upload_image(p, t->current))
+ if (p->surfaces[p->surface_now].id == 0) {
+ if (!gl_video_upload_image(p, t->current, t->frame_id))
return;
pass_render_frame(p);
finish_pass_fbo(p, &p->surfaces[p->surface_now].fbotex,
vp_w, vp_h, FBOTEX_FUZZY);
+ p->surfaces[p->surface_now].id = p->image.id;
p->surfaces[p->surface_now].pts = p->image.mpi->pts;
p->surface_idx = p->surface_now;
}
// Find the right frame for this instant
- if (t->current && t->current->pts != MP_NOPTS_VALUE) {
+ if (t->current) {
int next = fbosurface_wrap(p->surface_now + 1);
- while (p->surfaces[next].pts != MP_NOPTS_VALUE &&
- p->surfaces[next].pts > p->surfaces[p->surface_now].pts &&
- p->surfaces[p->surface_now].pts < t->current->pts)
+ while (p->surfaces[next].id &&
+ p->surfaces[next].id > p->surfaces[p->surface_now].id &&
+ p->surfaces[p->surface_now].id < t->frame_id)
{
p->surface_now = next;
next = fbosurface_wrap(next + 1);
@@ -2609,16 +2616,17 @@ static void gl_video_interpolate_frame(struct gl_video *p, struct vo_frame *t,
break;
struct mp_image *f = t->frames[i];
- if (!mp_image_params_equal(&f->params, &p->real_image_params) ||
- f->pts == MP_NOPTS_VALUE)
+ uint64_t f_id = t->frame_id + i;
+ if (!mp_image_params_equal(&f->params, &p->real_image_params))
continue;
- if (f->pts > p->surfaces[p->surface_idx].pts) {
- if (!gl_video_upload_image(p, f))
+ if (f_id > p->surfaces[p->surface_idx].id) {
+ if (!gl_video_upload_image(p, f, f_id))
return;
pass_render_frame(p);
finish_pass_fbo(p, &p->surfaces[surface_dst].fbotex,
vp_w, vp_h, FBOTEX_FUZZY);
+ p->surfaces[surface_dst].id = f_id;
p->surfaces[surface_dst].pts = f->pts;
p->surface_idx = surface_dst;
surface_dst = fbosurface_wrap(surface_dst + 1);
@@ -2633,11 +2641,9 @@ static void gl_video_interpolate_frame(struct gl_video *p, struct vo_frame *t,
bool valid = true;
for (int i = surface_bse, ii; valid && i != surface_end; i = ii) {
ii = fbosurface_wrap(i + 1);
- if (p->surfaces[i].pts == MP_NOPTS_VALUE ||
- p->surfaces[ii].pts == MP_NOPTS_VALUE)
- {
+ if (p->surfaces[i].id == 0 || p->surfaces[ii].id == 0) {
valid = false;
- } else if (p->surfaces[ii].pts < p->surfaces[i].pts) {
+ } else if (p->surfaces[ii].id < p->surfaces[i].id) {
valid = false;
MP_DBG(p, "interpolation queue underrun\n");
}
@@ -2658,8 +2664,8 @@ static void gl_video_interpolate_frame(struct gl_video *p, struct vo_frame *t,
// (which requires some extra checking to make sure it's valid)
if (mix < 0.0) {
int prev = fbosurface_wrap(surface_bse - 1);
- if (p->surfaces[prev].pts != MP_NOPTS_VALUE &&
- p->surfaces[prev].pts < p->surfaces[surface_bse].pts)
+ if (p->surfaces[prev].id != 0 &&
+ p->surfaces[prev].id < p->surfaces[surface_bse].id)
{
mix += 1.0;
surface_bse = prev;
@@ -2738,7 +2744,6 @@ void gl_video_render_frame(struct gl_video *p, struct vo_frame *frame, int fbo)
gl->BindFramebuffer(GL_FRAMEBUFFER, fbo);
bool has_frame = !!frame->current;
- bool is_new = has_frame && !frame->redraw && !frame->repeat;
if (!has_frame || p->dst_rect.x0 > 0 || p->dst_rect.y0 > 0 ||
p->dst_rect.x1 < p->vp_w || p->dst_rect.y1 < abs(p->vp_h))
@@ -2760,7 +2765,7 @@ void gl_video_render_frame(struct gl_video *p, struct vo_frame *frame, int fbo)
gl->Disable(GL_SCISSOR_TEST);
}
- if (is_new || !frame->current)
+ if (frame->frame_id != p->image.id || !frame->current)
p->hwdec->driver->overlay_frame(p->hwdec, frame->current);
if (frame->current)
@@ -2784,10 +2789,16 @@ void gl_video_render_frame(struct gl_video *p, struct vo_frame *frame, int fbo)
if (interpolate) {
gl_video_interpolate_frame(p, frame, fbo);
} else {
+ bool is_new = frame->frame_id != p->image.id;
+
+ // Redrawing a frame might update subtitles.
+ if (!frame->repeat && p->opts.blend_subs)
+ is_new = false;
+
if (is_new || !p->output_fbo_valid) {
p->output_fbo_valid = false;
- if (!gl_video_upload_image(p, frame->current))
+ if (!gl_video_upload_image(p, frame->current, frame->frame_id))
goto done;
pass_render_frame(p);
@@ -2949,11 +2960,15 @@ static void reinterleave_vdpau(struct gl_video *p, struct gl_hwdec_frame *frame)
}
// Returns false on failure.
-static bool gl_video_upload_image(struct gl_video *p, struct mp_image *mpi)
+static bool gl_video_upload_image(struct gl_video *p, struct mp_image *mpi,
+ uint64_t id)
{
GL *gl = p->gl;
struct video_image *vimg = &p->image;
+ if (vimg->id == id)
+ return true;
+
unref_current_image(p);
mpi = mp_image_new_ref(mpi);
@@ -2961,6 +2976,7 @@ static bool gl_video_upload_image(struct gl_video *p, struct mp_image *mpi)
goto error;
vimg->mpi = mpi;
+ vimg->id = id;
p->osd_pts = mpi->pts;
p->frames_uploaded++;
diff --git a/video/out/vo.h b/video/out/vo.h
index d2393f829b..99e6ccabae 100644
--- a/video/out/vo.h
+++ b/video/out/vo.h
@@ -223,6 +223,7 @@ struct vo_frame {
// a frame is guaranteed not to change (instant redraws will use the same
// ID). frames[n] has the ID frame_id+n, with the guarantee that frame
// drops or reconfigs will keep the guarantee.
+ // The ID is never 0 (unless num_frames==0). IDs are strictly monotonous.
uint64_t frame_id;
};