diff options
Diffstat (limited to 'video/out/gl_video.c')
-rw-r--r-- | video/out/gl_video.c | 139 |
1 files changed, 132 insertions, 7 deletions
diff --git a/video/out/gl_video.c b/video/out/gl_video.c index 7a40338ac1..2eeb86f353 100644 --- a/video/out/gl_video.c +++ b/video/out/gl_video.c @@ -122,6 +122,13 @@ struct fbotex { int vp_x, vp_y, vp_w, vp_h; // viewport of fbo / used part of the texture }; +struct fbosurface { + struct fbotex fbotex; + int64_t pts; +}; + +#define FBOSURFACES_MAX 2 + struct gl_video { GL *gl; @@ -139,7 +146,7 @@ struct gl_video { GLuint vao; GLuint osd_programs[SUBBITMAP_COUNT]; - GLuint indirect_program, scale_sep_program, final_program; + GLuint indirect_program, scale_sep_program, final_program, inter_program; struct osd_state *osd_state; struct mpgl_osd *osd; @@ -176,6 +183,9 @@ struct gl_video { struct fbotex indirect_fbo; // RGB target struct fbotex scale_sep_fbo; // first pass when doing 2 pass scaling + struct fbotex inter_fbo; // interpolation target + struct fbosurface surfaces[FBOSURFACES_MAX]; + size_t surface_num; // state for luma (0) and chroma (1) scalers struct scaler scalers[2]; @@ -402,7 +412,9 @@ const struct m_sub_options gl_video_conf = { {"blend", 2})), OPT_FLAG("rectangle-textures", use_rectangle, 0), OPT_COLOR("background", background, 0), - + OPT_FLAG("smoothmotion", smoothmotion, 0), + OPT_FLOAT("smoothmotion-threshold", smoothmotion_threshold, + CONF_RANGE, .min = 0, .max = 0.5), OPT_REMOVED("approx-gamma", "this is always enabled now"), OPT_REMOVED("cscale-down", "chroma is never downscaled"), OPT_REMOVED("scale-sep", "this is set automatically whenever sane"), @@ -649,6 +661,27 @@ static void fbotex_uninit(struct gl_video *p, struct fbotex *fbo) } } +static void fbosurfaces_uninit(struct gl_video *p, struct fbosurface *surfaces) +{ + + for (int i = 0; i < FBOSURFACES_MAX; i++) + if (surfaces[i].fbotex.fbo) + fbotex_uninit(p, &surfaces[i].fbotex); +} + +static void fbosurfaces_init(struct gl_video *p, struct fbosurface *surfaces, + int w, int h, GLenum iformat) +{ + for (int i = 0; i < FBOSURFACES_MAX; i++) + if (!surfaces[i].fbotex.fbo) + fbotex_init(p, &surfaces[i].fbotex, w, h, iformat); +} + +static size_t fbosurface_next(struct gl_video *p) +{ + return (p->surface_num + 1) % FBOSURFACES_MAX; +} + static void matrix_ortho2d(float m[3][3], float x0, float x1, float y0, float y1) { @@ -831,6 +864,7 @@ static void update_all_uniforms(struct gl_video *p) update_uniforms(p, p->indirect_program); update_uniforms(p, p->scale_sep_program); update_uniforms(p, p->final_program); + update_uniforms(p, p->inter_program); } #define SECTION_HEADER "#!section " @@ -1169,6 +1203,7 @@ static void compile_shaders(struct gl_video *p) char *header_conv = talloc_strdup(tmp, ""); char *header_final = talloc_strdup(tmp, ""); + char *header_inter = talloc_strdup(tmp, ""); char *header_sep = NULL; if (p->image_desc.id == IMGFMT_NV12 || p->image_desc.id == IMGFMT_NV21) { @@ -1211,12 +1246,20 @@ static void compile_shaders(struct gl_video *p) shader_setup_scaler(&header_final, &p->scalers[0], -1); } + bool use_interpolation = p->opts.smoothmotion; + + if (use_interpolation) { + shader_def_opt(&header_inter, "FIXED_SCALE", true); + shader_def_opt(&header_inter, "USE_LINEAR_INTERPOLATION", 1); + } + // The indirect pass is used to preprocess the image before scaling. bool use_indirect = false; // Don't sample from input video textures before converting the input to // its proper gamma. - if (use_input_gamma || use_conv_gamma || use_linear_light || use_const_luma) + if (use_input_gamma || use_conv_gamma || use_linear_light || + use_const_luma || use_interpolation) use_indirect = true; // Trivial scalers are implemented directly and efficiently by the GPU. @@ -1261,6 +1304,12 @@ static void compile_shaders(struct gl_video *p) create_program(p, "scale_sep", header_sep, vertex_shader, s_video); } + if (use_interpolation) { + header_inter = t_concat(tmp, header, header_inter); + p->inter_program = + create_program(p, "inter", header_inter, vertex_shader, s_video); + } + header_final = t_concat(tmp, header, header_final); p->final_program = create_program(p, "final", header_final, vertex_shader, s_video); @@ -1285,6 +1334,7 @@ static void delete_shaders(struct gl_video *p) delete_program(gl, &p->indirect_program); delete_program(gl, &p->scale_sep_program); delete_program(gl, &p->final_program); + delete_program(gl, &p->inter_program); } static void get_scale_factors(struct gl_video *p, double xy[2]) @@ -1528,6 +1578,14 @@ static void reinit_rendering(struct gl_video *p) if (p->indirect_program && !p->indirect_fbo.fbo) fbotex_init(p, &p->indirect_fbo, w, h, p->opts.fbo_format); + if (p->inter_program && !p->inter_fbo.fbo) { + fbotex_init(p, &p->inter_fbo, w, h, p->opts.fbo_format); + } + + if (p->inter_program) { + fbosurfaces_init(p, p->surfaces, w, h, p->opts.fbo_format); + } + recreate_osd(p); } @@ -1733,8 +1791,10 @@ static void uninit_video(struct gl_video *p) } mp_image_unrefp(&vimg->mpi); + fbotex_uninit(p, &p->inter_fbo); fbotex_uninit(p, &p->indirect_fbo); fbotex_uninit(p, &p->scale_sep_fbo); + fbosurfaces_uninit(p, p->surfaces); // Invalidate image_params to ensure that gl_video_config() will call // init_video() on uninitialized gl_video. @@ -1819,8 +1879,54 @@ static void handle_pass(struct gl_video *p, struct pass *chain, }; } +static void gl_video_interpolate_frame(struct gl_video *p, + struct pass *chain, + struct frame_timing *t) +{ + GL *gl = p->gl; + double inter_coeff = 0.0; + int64_t prev_pts = p->surfaces[fbosurface_next(p)].pts; + + if (prev_pts < t->pts) { + MP_STATS(p, "new-pts"); + // fbosurface 0 is already bound from the caller + p->surfaces[p->surface_num].pts = t->pts; + p->surface_num = fbosurface_next(p); + gl->ActiveTexture(GL_TEXTURE0 + 1); + gl->BindTexture(p->gl_target, p->surfaces[p->surface_num].fbotex.texture); + gl->ActiveTexture(GL_TEXTURE0); + MP_DBG(p, "frame ppts: %lld, pts: %lld, vsync: %lld, DIFF: %lld\n", + (long long)prev_pts, (long long)t->pts, + (long long)t->next_vsync, (long long)t->next_vsync - t->pts); + if (prev_pts < t->next_vsync && t->pts > t->next_vsync) { + double N = t->next_vsync - prev_pts; + double P = t->pts - prev_pts; + double prev_pts_component = N / P; + float ts = p->opts.smoothmotion_threshold; + inter_coeff = 1 - prev_pts_component; + inter_coeff = inter_coeff < 0.0 + ts ? 0.0 : inter_coeff; + inter_coeff = inter_coeff > 1.0 - ts ? 1.0 : inter_coeff; + MP_DBG(p, "inter frame ppts: %lld, pts: %lld, " + "vsync: %lld, mix: %f\n", + (long long)prev_pts, (long long)t->pts, + (long long)t->next_vsync, inter_coeff); + MP_STATS(p, "frame-mix"); + + // the value is scaled to fit in the graph with the completely + // unrelated "phase" value (which is stupid) + MP_STATS(p, "value-timed %lld %f mix-value", + (long long)t->pts, inter_coeff * 10000); + } + } + + gl->UseProgram(p->inter_program); + GLint loc = gl->GetUniformLocation(p->inter_program, "inter_coeff"); + gl->Uniform1f(loc, inter_coeff); + handle_pass(p, chain, &p->inter_fbo, p->inter_program); +} + // (fbo==0 makes BindFramebuffer select the screen backbuffer) -void gl_video_render_frame(struct gl_video *p, int fbo) +void gl_video_render_frame(struct gl_video *p, int fbo, struct frame_timing *t) { GL *gl = p->gl; struct video_image *vimg = &p->image; @@ -1844,7 +1950,7 @@ void gl_video_render_frame(struct gl_video *p, int fbo) } // Order of processing: - // [indirect -> [scale_sep ->]] final + // [indirect -> [interpolate -> [scale_sep ->]]] final GLuint imgtex[4] = {0}; set_image_textures(p, vimg, imgtex); @@ -1859,7 +1965,18 @@ void gl_video_render_frame(struct gl_video *p, int fbo) }, }; - handle_pass(p, &chain, &p->indirect_fbo, p->indirect_program); + int64_t prev_pts = p->surfaces[fbosurface_next(p)].pts; + struct fbotex *indirect_target; + if (p->inter_program && t && prev_pts < t->pts) { + indirect_target = &p->surfaces[p->surface_num].fbotex; + } else { + indirect_target = &p->indirect_fbo; + } + + handle_pass(p, &chain, indirect_target, p->indirect_program); + + if (t && p->inter_program) + gl_video_interpolate_frame(p, &chain, t); // Clip to visible height so that separate scaling scales the visible part // only (and the target FBO texture can have a bounded size). @@ -2403,6 +2520,14 @@ void gl_video_unset_gl_state(struct gl_video *p) } } +void gl_video_reset(struct gl_video *p) +{ + for (int i = 0; i < FBOSURFACES_MAX; i++) { + p->surfaces[i].pts = 0; + } + p->surface_num = 0; +} + // dest = src.<w> (always using 4 components) static void packed_fmt_swizzle(char w[5], const struct fmt_entry *texfmt, const struct packed_fmt_entry *fmt) @@ -2697,7 +2822,7 @@ void gl_video_resize_redraw(struct gl_video *p, int w, int h) { p->vp_w = w; p->vp_h = h; - gl_video_render_frame(p, 0); + gl_video_render_frame(p, 0, NULL); } void gl_video_set_hwdec(struct gl_video *p, struct gl_hwdec *hwdec) |