summaryrefslogtreecommitdiffstats
path: root/video/out/opengl/video.c
diff options
context:
space:
mode:
Diffstat (limited to 'video/out/opengl/video.c')
-rw-r--r--video/out/opengl/video.c103
1 files changed, 70 insertions, 33 deletions
diff --git a/video/out/opengl/video.c b/video/out/opengl/video.c
index 9461153615..498d89259e 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;
};
@@ -322,6 +324,7 @@ static const struct gl_video_opts gl_video_opts_def = {
.target_brightness = 250,
.hdr_tone_mapping = TONE_MAPPING_HABLE,
.tone_mapping_param = NAN,
+ .early_flush = -1,
};
static int validate_scaler_opt(struct mp_log *log, const m_option_t *opt,
@@ -337,7 +340,10 @@ static int validate_window_opt(struct mp_log *log, const m_option_t *opt,
OPT_FLOAT(n"-param1", scaler[i].kernel.params[0], 0), \
OPT_FLOAT(n"-param2", scaler[i].kernel.params[1], 0), \
OPT_FLOAT(n"-blur", scaler[i].kernel.blur, 0), \
+ OPT_FLOATRANGE(n"-taper", scaler[i].kernel.taper, 0, 0.0, 1.0), \
OPT_FLOAT(n"-wparam", scaler[i].window.params[0], 0), \
+ OPT_FLOAT(n"-wblur", scaler[i].window.blur, 0), \
+ OPT_FLOATRANGE(n"-wtaper", scaler[i].window.taper, 0, 0.0, 1.0), \
OPT_FLAG(n"-clamp", scaler[i].clamp, 0), \
OPT_FLOATRANGE(n"-radius", scaler[i].radius, 0, 0.5, 16.0), \
OPT_FLOATRANGE(n"-antiring", scaler[i].antiring, 0, 0.0, 1.0), \
@@ -412,7 +418,8 @@ const struct m_sub_options gl_video_conf = {
OPT_INTRANGE("opengl-tex-pad-x", tex_pad_x, 0, 0, 4096),
OPT_INTRANGE("opengl-tex-pad-y", tex_pad_y, 0, 0, 4096),
OPT_SUBSTRUCT("", icc_opts, mp_icc_conf, 0),
- OPT_FLAG("opengl-early-flush", early_flush, 0),
+ OPT_CHOICE("opengl-early-flush", early_flush, 0,
+ ({"no", 0}, {"yes", 1}, {"auto", -1})),
{0}
},
@@ -515,7 +522,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]);
@@ -563,8 +571,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;
@@ -951,6 +961,7 @@ static void unmap_current_image(struct gl_video *p)
p->hwdec->driver->unmap(p->hwdec);
memset(vimg->planes, 0, sizeof(vimg->planes));
vimg->hwdec_mapped = false;
+ vimg->id = 0; // needs to be mapped again
}
}
@@ -958,6 +969,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)
@@ -1349,7 +1361,8 @@ static bool scaler_fun_eq(struct scaler_fun a, struct scaler_fun b)
return ((!a.name && !b.name) || strcmp(a.name, b.name) == 0) &&
double_seq(a.params[0], b.params[0]) &&
double_seq(a.params[1], b.params[1]) &&
- a.blur == b.blur;
+ a.blur == b.blur &&
+ a.taper == b.taper;
}
static bool scaler_conf_eq(struct scaler_config a, struct scaler_config b)
@@ -1410,6 +1423,11 @@ static void reinit_scaler(struct gl_video *p, struct scaler *scaler,
if (conf->window.blur > 0.0)
scaler->kernel->w.blur = conf->window.blur;
+ if (conf->kernel.taper > 0.0)
+ scaler->kernel->f.taper = conf->kernel.taper;
+ if (conf->window.taper > 0.0)
+ scaler->kernel->w.taper = conf->window.taper;
+
if (scaler->kernel->f.resizable && conf->radius > 0.0)
scaler->kernel->f.radius = conf->radius;
@@ -1995,8 +2013,6 @@ static void pass_convert_yuv(struct gl_video *p)
p->components = 3;
if (!p->has_alpha || p->opts.alpha_mode == ALPHA_NO) {
GLSL(color.a = 1.0;)
- } else if (p->opts.alpha_mode == ALPHA_BLEND) {
- GLSL(color = vec4(color.rgb * color.a, 1.0);)
} else { // alpha present in image
p->components = 4;
GLSL(color = vec4(color.rgb * color.a, color.a);)
@@ -2519,12 +2535,20 @@ static void pass_draw_to_screen(struct gl_video *p, int fbo)
pass_colormanage(p, p->image_params.color, false);
- // Draw checkerboard pattern to indicate transparency
- if (p->has_alpha && p->opts.alpha_mode == ALPHA_BLEND_TILES) {
- GLSLF("// transparency checkerboard\n");
- GLSL(bvec2 tile = lessThan(fract(gl_FragCoord.xy / 32.0), vec2(0.5));)
- GLSL(vec3 background = vec3(tile.x == tile.y ? 1.0 : 0.75);)
- GLSL(color.rgb = mix(background, color.rgb, color.a);)
+ if (p->has_alpha){
+ if (p->opts.alpha_mode == ALPHA_BLEND_TILES) {
+ // Draw checkerboard pattern to indicate transparency
+ GLSLF("// transparency checkerboard\n");
+ GLSL(bvec2 tile = lessThan(fract(gl_FragCoord.xy / 32.0), vec2(0.5));)
+ GLSL(vec3 background = vec3(tile.x == tile.y ? 1.0 : 0.75);)
+ GLSL(color.rgb = mix(background, color.rgb, color.a);)
+ } else if (p->opts.alpha_mode == ALPHA_BLEND) {
+ // Blend into background color (usually black)
+ struct m_color c = p->opts.background;
+ GLSLF("vec4 background = vec4(%f, %f, %f, %f);\n",
+ c.r / 255.0, c.g / 255.0, c.b / 255.0, c.a / 255.0);
+ GLSL(color = mix(background, vec4(color.rgb, 1.0), color.a);)
+ }
}
pass_opt_hook_point(p, "OUTPUT", NULL);
@@ -2550,22 +2574,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);
@@ -2607,16 +2632,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);
@@ -2631,11 +2657,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");
}
@@ -2656,8 +2680,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;
@@ -2736,7 +2760,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))
@@ -2758,7 +2781,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)
@@ -2782,10 +2805,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->still && p->opts.blend_subs)
+ is_new = true;
+
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);
@@ -2849,8 +2878,11 @@ done:
// The playloop calls this last before waiting some time until it decides
// to call flip_page(). Tell OpenGL to start execution of the GPU commands
// while we sleep (this happens asynchronously).
- if (p->opts.early_flush)
+ if ((p->opts.early_flush == -1 && !frame->display_synced) ||
+ p->opts.early_flush == 1)
+ {
gl->Flush();
+ }
p->frames_rendered++;
@@ -2944,11 +2976,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);
@@ -2956,6 +2992,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++;