diff options
Diffstat (limited to 'video/out/opengl/video.c')
-rw-r--r-- | video/out/opengl/video.c | 285 |
1 files changed, 203 insertions, 82 deletions
diff --git a/video/out/opengl/video.c b/video/out/opengl/video.c index 24618937d8..c10e16fe41 100644 --- a/video/out/opengl/video.c +++ b/video/out/opengl/video.c @@ -1,23 +1,18 @@ /* * This file is part of mpv. * - * mpv is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * mpv is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. * * mpv is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with mpv. If not, see <http://www.gnu.org/licenses/>. + * GNU Lesser General Public License for more details. * - * You can alternatively redistribute this file and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. + * You should have received a copy of the GNU Lesser General Public + * License along with mpv. If not, see <http://www.gnu.org/licenses/>. */ #include <assert.h> @@ -98,6 +93,7 @@ struct texplane { int w, h; GLint gl_internal_format; GLenum gl_target; + bool use_integer; GLenum gl_format; GLenum gl_type; GLuint gl_texture; @@ -120,6 +116,7 @@ struct fbosurface { struct src_tex { GLuint gl_tex; GLenum gl_target; + bool use_integer; int w, h; struct mp_rect_f src; }; @@ -165,10 +162,12 @@ struct gl_video { bool is_yuv, is_packed_yuv; bool has_alpha; char color_swizzle[5]; + bool use_integer_conversion; struct video_image image; bool dumb_mode; + bool forced_dumb_mode; struct fbotex chroma_merge_fbo; struct fbotex chroma_deband_fbo; @@ -178,6 +177,7 @@ struct gl_video { struct fbotex output_fbo; struct fbotex deband_fbo; struct fbosurface surfaces[FBOSURFACES_MAX]; + struct fbotex integer_conv_fbo[TEXUNIT_VIDEO_NUM]; // these are duplicated so we can keep rendering back and forth between // them to support an unlimited number of shader passes per step @@ -225,6 +225,7 @@ struct gl_video { bool hwdec_active; bool dsi_warned; + bool custom_shader_fn_warned; }; struct fmt_entry { @@ -236,9 +237,6 @@ struct fmt_entry { // Very special formats, for which OpenGL happens to have direct support static const struct fmt_entry mp_to_gl_formats[] = { - {IMGFMT_BGR555, GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV}, - {IMGFMT_BGR565, GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5_REV}, - {IMGFMT_RGB555, GL_RGBA, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV}, {IMGFMT_RGB565, GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5}, {0}, }; @@ -267,6 +265,17 @@ static const struct fmt_entry gl_byte_formats_gles3[] = { {0, 0, 0, 0}, // 4 x 16 }; +static const struct fmt_entry gl_ui_byte_formats_gles3[] = { + {0, GL_R8UI, GL_RED_INTEGER, GL_UNSIGNED_BYTE}, // 1 x 8 + {0, GL_RG8UI, GL_RG_INTEGER, GL_UNSIGNED_BYTE}, // 2 x 8 + {0, GL_RGB8UI, GL_RGB_INTEGER, GL_UNSIGNED_BYTE}, // 3 x 8 + {0, GL_RGBA8UI, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE}, // 4 x 8 + {0, GL_R16UI, GL_RED_INTEGER, GL_UNSIGNED_SHORT}, // 1 x 16 + {0, GL_RG16UI, GL_RG_INTEGER, GL_UNSIGNED_SHORT}, // 2 x 16 + {0, GL_RGB16UI, GL_RGB_INTEGER, GL_UNSIGNED_SHORT}, // 3 x 16 + {0, GL_RGBA16UI, GL_RGBA_INTEGER, GL_UNSIGNED_SHORT}, // 4 x 16 +}; + static const struct fmt_entry gl_byte_formats_gles2[] = { {0, GL_LUMINANCE, GL_LUMINANCE, GL_UNSIGNED_BYTE}, // 1 x 8 {0, GL_LUMINANCE_ALPHA, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE}, // 2 x 8 @@ -344,7 +353,9 @@ const struct gl_video_opts gl_video_opts_def = { {{"mitchell", .params={NAN, NAN}}, {.params = {NAN, NAN}}, .clamp = 1, }, // tscale }, + .scaler_resizes_only = 1, .scaler_lut_size = 6, + .interpolation_threshold = 0.0001, .alpha_mode = 3, .background = {0, 0, 0, 255}, .gamma = 1.0f, @@ -368,7 +379,9 @@ const struct gl_video_opts gl_video_opts_hq_def = { {{"mitchell", .params={NAN, NAN}}, {.params = {NAN, NAN}}, .clamp = 1, }, // tscale }, + .scaler_resizes_only = 1, .scaler_lut_size = 6, + .interpolation_threshold = 0.0001, .alpha_mode = 3, .background = {0, 0, 0, 255}, .gamma = 1.0f, @@ -446,6 +459,7 @@ const struct m_sub_options gl_video_conf = { OPT_FLAG("rectangle-textures", use_rectangle, 0), OPT_COLOR("background", background, 0), OPT_FLAG("interpolation", interpolation, 0), + OPT_FLOAT("interpolation-threshold", interpolation_threshold, 0), OPT_CHOICE("blend-subtitles", blend_subs, 0, ({"no", 0}, {"yes", 1}, @@ -508,7 +522,9 @@ static void get_scale_factors(struct gl_video *p, double xy[2]); #define GLSL(x) gl_sc_add(p->sc, #x "\n"); #define GLSLF(...) gl_sc_addf(p->sc, __VA_ARGS__) +#define GLSLHF(...) gl_sc_haddf(p->sc, __VA_ARGS__) +// Return a fixed point texture format with given characteristics. static const struct fmt_entry *find_tex_format(GL *gl, int bytes_per_comp, int n_channels) { @@ -525,6 +541,19 @@ static const struct fmt_entry *find_tex_format(GL *gl, int bytes_per_comp, return &fmts[n_channels - 1 + (bytes_per_comp - 1) * 4]; } +static bool is_integer_format(const struct fmt_entry *fmt) +{ + // Tests only the formats which we actually declare somewhere. + switch (fmt->format) { + case GL_RED_INTEGER: + case GL_RG_INTEGER: + case GL_RGB_INTEGER: + case GL_RGBA_INTEGER: + return true; + } + return false; +} + static const char *load_cached_file(struct gl_video *p, const char *path) { if (!path || !path[0]) @@ -626,6 +655,9 @@ static void uninit_rendering(struct gl_video *p) fbotex_uninit(&p->unsharp_fbo); fbotex_uninit(&p->deband_fbo); + for (int n = 0; n < 4; n++) + fbotex_uninit(&p->integer_conv_fbo[n]); + for (int n = 0; n < 2; n++) { fbotex_uninit(&p->pre_fbo[n]); fbotex_uninit(&p->post_fbo[n]); @@ -724,8 +756,9 @@ static void pass_set_image_textures(struct gl_video *p, struct video_image *vimg for (int n = 0; n < p->plane_count; n++) { struct texplane *t = &vimg->planes[n]; p->pass_tex[n] = (struct src_tex){ - .gl_tex = vimg->planes[n].gl_texture, + .gl_tex = t->gl_texture, .gl_target = t->gl_target, + .use_integer = t->use_integer, .w = t->w, .h = t->h, .src = {0, 0, t->w, t->h}, @@ -737,11 +770,11 @@ static void init_video(struct gl_video *p) { GL *gl = p->gl; - check_gl_features(p); - init_format(p->image_params.imgfmt, p); p->gl_target = p->opts.use_rectangle ? GL_TEXTURE_RECTANGLE : GL_TEXTURE_2D; + check_gl_features(p); + if (p->hwdec_active) { if (p->hwdec->driver->reinit(p->hwdec, &p->image_params) < 0) MP_ERR(p, "Initializing texture for hardware decoding failed.\n"); @@ -785,8 +818,9 @@ static void init_video(struct gl_video *p) plane->w, plane->h, 0, plane->gl_format, plane->gl_type, NULL); - gl->TexParameteri(p->gl_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - gl->TexParameteri(p->gl_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + int filter = plane->use_integer ? GL_NEAREST : GL_LINEAR; + gl->TexParameteri(p->gl_target, GL_TEXTURE_MIN_FILTER, filter); + gl->TexParameteri(p->gl_target, GL_TEXTURE_MAG_FILTER, filter); gl->TexParameteri(p->gl_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); gl->TexParameteri(p->gl_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); } @@ -837,16 +871,24 @@ static void pass_prepare_src_tex(struct gl_video *p) char texture_name[32]; char texture_size[32]; + char pixel_size[32]; snprintf(texture_name, sizeof(texture_name), "texture%d", n); snprintf(texture_size, sizeof(texture_size), "texture_size%d", n); + snprintf(pixel_size, sizeof(pixel_size), "pixel_size%d", n); - gl_sc_uniform_sampler(sc, texture_name, s->gl_target, n); + if (s->use_integer) { + gl_sc_uniform_sampler_ui(sc, texture_name, n); + } else { + gl_sc_uniform_sampler(sc, texture_name, s->gl_target, n); + } float f[2] = {1, 1}; if (s->gl_target != GL_TEXTURE_RECTANGLE) { f[0] = s->w; f[1] = s->h; } gl_sc_uniform_vec2(sc, texture_size, f); + gl_sc_uniform_vec2(sc, pixel_size, (GLfloat[]){1.0f / f[0], + 1.0f / f[1]}); gl->ActiveTexture(GL_TEXTURE0 + n); gl->BindTexture(s->gl_target, s->gl_tex); @@ -947,8 +989,21 @@ static void load_shader(struct gl_video *p, const char *body) gl_sc_hadd(p->sc, body); gl_sc_uniform_f(p->sc, "random", (double)av_lfg_get(&p->lfg) / UINT32_MAX); gl_sc_uniform_f(p->sc, "frame", p->frames_uploaded); - gl_sc_uniform_vec2(p->sc, "image_size", (GLfloat[]){p->texture_w, - p->texture_h}); + gl_sc_uniform_vec2(p->sc, "image_size", (GLfloat[]){p->image_params.w, + p->image_params.h}); +} + +static const char *get_custom_shader_fn(struct gl_video *p, const char *body) +{ + if (!p->gl->es && strstr(body, "sample") && !strstr(body, "sample_pixel")) { + if (!p->custom_shader_fn_warned) { + MP_WARN(p, "sample() is deprecated in custom shaders. " + "Use sample_pixel()\n"); + p->custom_shader_fn_warned = true; + } + return "sample"; + } + return "sample_pixel"; } // Applies an arbitrary number of shaders in sequence, using the given pair @@ -965,10 +1020,12 @@ static bool apply_shaders(struct gl_video *p, char **shaders, if (!body) continue; finish_pass_fbo(p, &textures[tex], w, h, tex_num, 0); + GLSLHF("#define pixel_size pixel_size%d\n", tex_num); load_shader(p, body); + const char *fn_name = get_custom_shader_fn(p, body); GLSLF("// custom shader\n"); - GLSLF("vec4 color = sample(texture%d, texcoord%d, texture_size%d);\n", - tex_num, tex_num, tex_num); + GLSLF("color = %s(texture%d, texcoord%d, texture_size%d);\n", + fn_name, tex_num, tex_num, tex_num); tex = (tex+1) % 2; success = true; } @@ -1135,7 +1192,7 @@ static void pass_sample_separated(struct gl_video *p, int src_tex, // The src rectangle is implicit in p->pass_tex + transform. // The dst rectangle is implicit by what the caller will do next, but w and h // must still be what is going to be used (to dimension FBOs correctly). -// This will declare "vec4 color;", which contains the scaled contents. +// This will write the scaled contents to the vec4 "color". // The scaler unit is initialized by this function; in order to avoid cache // thrashing, the scaler unit should usually use the same parameters. static void pass_sample(struct gl_video *p, int src_tex, struct scaler *scaler, @@ -1152,7 +1209,7 @@ static void pass_sample(struct gl_video *p, int src_tex, struct scaler *scaler, // Dispatch the scaler. They're all wildly different. const char *name = scaler->conf.kernel.name; if (strcmp(name, "bilinear") == 0) { - GLSL(vec4 color = texture(tex, pos);) + GLSL(color = texture(tex, pos);) } else if (strcmp(name, "bicubic_fast") == 0) { pass_sample_bicubic_fast(p->sc); } else if (strcmp(name, "oversample") == 0) { @@ -1161,8 +1218,9 @@ static void pass_sample(struct gl_video *p, int src_tex, struct scaler *scaler, const char *body = load_cached_file(p, p->opts.scale_shader); if (body) { load_shader(p, body); + const char *fn_name = get_custom_shader_fn(p, body); GLSLF("// custom scale-shader\n"); - GLSL(vec4 color = sample(tex, pos, size);) + GLSLF("color = %s(tex, pos, size);\n", fn_name); } else { p->opts.scale_shader = NULL; } @@ -1296,8 +1354,8 @@ static bool pass_prescale_luma(struct gl_video *p, float tex_mul, if (p->opts.deband) { // apply debanding before upscaling. - pass_sample_deband(p->sc, p->opts.deband_opts, 0, p->gl_target, - tex_mul, p->texture_w, p->texture_h, &p->lfg); + pass_sample_deband(p->sc, p->opts.deband_opts, 0, p->pass_tex[0].gl_target, + tex_mul, &p->lfg); finish_pass_fbo(p, &p->deband_fbo, p->texture_w, p->texture_h, 0, 0); tex_backup[0] = p->pass_tex[0]; @@ -1321,15 +1379,63 @@ static bool pass_prescale_luma(struct gl_video *p, float tex_mul, return true; } +// The input textures are in an integer format (non-fixed-point), like R16UI. +// Convert it to float in an extra pass. +static void pass_integer_conversion(struct gl_video *p, bool *chroma_merging) +{ + double tex_mul = 1 / mp_get_csp_mul(p->image_params.colorspace, + p->image_desc.component_bits, + p->image_desc.component_full_bits); + uint64_t tex_max = 1ull << p->image_desc.component_full_bits; + tex_mul *= 1.0 / (tex_max - 1); + + struct src_tex pass_tex[TEXUNIT_VIDEO_NUM]; + assert(sizeof(pass_tex) == sizeof(p->pass_tex)); + memcpy(pass_tex, p->pass_tex, sizeof(pass_tex)); + + *chroma_merging = p->plane_count == 3; + + for (int n = 0; n < TEXUNIT_VIDEO_NUM; n++) { + if (!p->pass_tex[n].gl_tex) + continue; + if (*chroma_merging && n == 2) + continue; + GLSLF("// integer conversion plane %d\n", n); + GLSLF("uvec4 icolor = texture(texture%d, texcoord%d);\n", n, n); + GLSLF("color = vec4(icolor) * tex_mul;\n"); + if (*chroma_merging && n == 1) { + GLSLF("uvec4 icolor2 = texture(texture2, texcoord2);\n"); + GLSLF("color.g = vec4(icolor2).r * tex_mul;\n"); + } + gl_sc_uniform_f(p->sc, "tex_mul", tex_mul); + int c_w = p->pass_tex[n].src.x1 - p->pass_tex[n].src.x0; + int c_h = p->pass_tex[n].src.y1 - p->pass_tex[n].src.y0; + finish_pass_fbo(p, &p->integer_conv_fbo[n], c_w, c_h, n, 0); + pass_tex[n] = p->pass_tex[n]; + memcpy(p->pass_tex, pass_tex, sizeof(p->pass_tex)); + } + + p->use_normalized_range = true; +} + // sample from video textures, set "color" variable to yuv value static void pass_read_video(struct gl_video *p) { + p->use_normalized_range = false; + struct gl_transform chromafix; pass_set_image_textures(p, &p->image, &chromafix); + bool chroma_merged = false; + + if (p->use_integer_conversion) + pass_integer_conversion(p, &chroma_merged); + float tex_mul = 1 / mp_get_csp_mul(p->image_params.colorspace, p->image_desc.component_bits, p->image_desc.component_full_bits); + if (p->use_normalized_range) + tex_mul = 1.0; struct src_tex prescaled_tex; struct gl_transform offset = {{{0}}}; @@ -1341,7 +1447,6 @@ static void pass_read_video(struct gl_video *p) const int scale_factor_x = prescaled ? (int)offset.m[0][0] : 1; const int scale_factor_y = prescaled ? (int)offset.m[1][1] : 1; - bool color_defined = false; if (p->plane_count > 1) { // Chroma processing (merging -> debanding -> scaling) struct src_tex luma = p->pass_tex[0]; @@ -1350,15 +1455,14 @@ static void pass_read_video(struct gl_video *p) int c_h = p->pass_tex[1].src.y1 - p->pass_tex[1].src.y0; const struct scaler_config *cscale = &p->opts.scaler[2]; - bool merged = false; - if (p->plane_count > 2) { + if (p->plane_count > 2 && !chroma_merged) { // For simplicity and performance, we merge the chroma planes // into a single texture before scaling or debanding, so the shader // doesn't need to run multiple times. GLSLF("// chroma merging\n"); - GLSL(vec4 color = vec4(texture(texture1, texcoord1).x, - texture(texture2, texcoord2).x, - 0.0, 1.0);) + GLSL(color = vec4(texture(texture1, texcoord1).x, + texture(texture2, texcoord2).x, + 0.0, 1.0);) // We also pull up to the full dynamic range of the texture to avoid // heavy clipping when using low-bit-depth FBOs GLSLF("color.xy *= %f;\n", tex_mul); @@ -1366,13 +1470,11 @@ static void pass_read_video(struct gl_video *p) assert(c_h == p->pass_tex[2].src.y1 - p->pass_tex[2].src.y0); finish_pass_fbo(p, &p->chroma_merge_fbo, c_w, c_h, 1, 0); p->use_normalized_range = true; - merged = true; } if (p->opts.deband) { - pass_sample_deband(p->sc, p->opts.deband_opts, 1, p->gl_target, - merged ? 1.0 : tex_mul, - p->texture_w, p->texture_h, &p->lfg); + pass_sample_deband(p->sc, p->opts.deband_opts, 1, p->pass_tex[1].gl_target, + p->use_normalized_range ? 1.0 : tex_mul, &p->lfg); GLSL(color.zw = vec2(0.0, 1.0);) // skip unused finish_pass_fbo(p, &p->chroma_deband_fbo, c_w, c_h, 1, 0); p->use_normalized_range = true; @@ -1385,7 +1487,6 @@ static void pass_read_video(struct gl_video *p) p->texture_w * scale_factor_x, p->texture_h * scale_factor_y, chromafix); GLSL(vec2 chroma = color.xy;) - color_defined = true; // pass_sample defines vec4 color } else { GLSL(vec2 chroma = texture(texture1, texcoord1).xy;) } @@ -1394,40 +1495,26 @@ static void pass_read_video(struct gl_video *p) p->pass_tex[3] = alpha; } - // As an unfortunate side-effect of re-using the vec4 color constant in - // both the luma and chroma stages, vec4 color may or may not be defined - // at this point. If it's missing, define it since the code from here on - // relies on it. - if (!color_defined) - GLSL(vec4 color;) - - // Sample the main (luma/RGB) plane. This is inside a sub-block to avoid - // colliding with the vec4 color that may be left over from the chroma - // stuff - GLSL(vec4 main;) - GLSLF("{\n"); + // Sample the main (luma/RGB) plane. if (!prescaled && p->opts.deband) { - pass_sample_deband(p->sc, p->opts.deband_opts, 0, p->gl_target, tex_mul, - p->texture_w, p->texture_h, &p->lfg); + pass_sample_deband(p->sc, p->opts.deband_opts, 0, p->pass_tex[0].gl_target, + tex_mul, &p->lfg); p->use_normalized_range = true; } else { if (!prescaled) { - GLSL(vec4 color = texture(texture0, texcoord0);) + GLSL(color = texture(texture0, texcoord0);) } else { // just use bilinear for non-essential planes. - GLSLF("vec4 color = texture(texture0, " - "texcoord0 + vec2(%f,%f) / texture_size0);\n", + GLSLF("color = texture(texture0, " + "texcoord0 + vec2(%f,%f) * pixel_size0);\n", -offset.t[0] / scale_factor_x, -offset.t[1] / scale_factor_y); } if (p->use_normalized_range) GLSLF("color *= %f;\n", tex_mul); } - GLSL(main = color;) - GLSLF("}\n"); // Set up the right combination of planes - GLSL(color = main;) if (prescaled) { // Restore texture4 and merge it into the main texture. p->pass_tex[4] = prescaled_tex; @@ -1447,7 +1534,7 @@ static void pass_read_video(struct gl_video *p) GLSL(color.a = texture(texture3, texcoord3).r;) } else { GLSLF("color.a = texture(texture3, " - "texcoord3 + vec2(%f,%f) / texture_size3).r;", + "texcoord3 + vec2(%f,%f) * pixel_size3).r;", -offset.t[0] / scale_factor_x, -offset.t[1] / scale_factor_y); } @@ -1834,12 +1921,12 @@ static void pass_draw_osd(struct gl_video *p, int draw_flags, double pts, switch (fmt) { case SUBBITMAP_RGBA: { GLSLF("// OSD (RGBA)\n"); - GLSL(vec4 color = texture(osdtex, texcoord).bgra;) + GLSL(color = texture(osdtex, texcoord).bgra;) break; } case SUBBITMAP_LIBASS: { GLSLF("// OSD (libass)\n"); - GLSL(vec4 color = + GLSL(color = vec4(ass_color.rgb, ass_color.a * texture(osdtex, texcoord).r);) break; } @@ -1879,7 +1966,7 @@ static void pass_render_frame_dumb(struct gl_video *p, int fbo) } gl_transform_rect(transform, &p->pass_tex[3].src); - GLSL(vec4 color = texture(texture0, texcoord0);) + GLSL(color = texture(texture0, texcoord0);) if (p->image_desc.flags & MP_IMGFLAG_YUV_NV) { GLSL(color.gb = texture(texture1, texcoord1).RG;) } else if (p->plane_count >= 3) { @@ -1925,7 +2012,7 @@ static void pass_render_frame(struct gl_video *p) p->texture_w, p->texture_h, 0, 0); pass_draw_osd(p, OSD_DRAW_SUB_ONLY, vpts, rect, p->texture_w, p->texture_h, p->blend_subs_fbo.fbo, false); - GLSL(vec4 color = texture(texture0, texcoord0);) + GLSL(color = texture(texture0, texcoord0);) } apply_shaders(p, p->opts.pre_shaders, &p->pre_fbo[0], 0, @@ -1960,7 +2047,7 @@ static void pass_render_frame(struct gl_video *p) FBOTEX_FUZZY); pass_draw_osd(p, OSD_DRAW_SUB_ONLY, vpts, rect, p->texture_w, p->texture_h, p->blend_subs_fbo.fbo, false); - GLSL(vec4 color = texture(texture0, texcoord0);) + GLSL(color = texture(texture0, texcoord0);) if (p->use_linear) pass_linearize(p->sc, p->image_params.gamma); } @@ -2098,7 +2185,7 @@ static void gl_video_interpolate_frame(struct gl_video *p, struct vo_frame *t, if (!valid || t->still) { // surface_now is guaranteed to be valid, so we can safely use it. pass_load_fbotex(p, &p->surfaces[surface_now].fbotex, vp_w, vp_h, 0); - GLSL(vec4 color = texture(texture0, texcoord0);) + GLSL(color = texture(texture0, texcoord0);) p->is_interpolated = false; } else { double mix = t->vsync_offset / t->ideal_frame_duration; @@ -2128,9 +2215,9 @@ static void gl_video_interpolate_frame(struct gl_video *p, struct vo_frame *t, mix = mix >= 1 - threshold ? 1 : mix; mix = 1 - mix; gl_sc_uniform_f(p->sc, "inter_coeff", mix); - GLSL(vec4 color = mix(texture(texture0, texcoord0), - texture(texture1, texcoord1), - inter_coeff);) + GLSL(color = mix(texture(texture0, texcoord0), + texture(texture1, texcoord1), + inter_coeff);) } else { gl_sc_uniform_f(p->sc, "fcoord", mix); pass_sample_separated_gen(p->sc, tscale, 0, 0); @@ -2172,9 +2259,15 @@ void gl_video_render_frame(struct gl_video *p, struct vo_frame *frame, int fbo) if (has_frame) { gl_sc_set_vao(p->sc, &p->vao); - if (p->opts.interpolation && frame->display_synced && - (p->frames_drawn || !frame->still)) - { + bool interpolate = p->opts.interpolation && frame->display_synced && + (p->frames_drawn || !frame->still); + if (interpolate) { + double ratio = frame->ideal_frame_duration / frame->vsync_interval; + if (fabs(ratio - 1.0) < p->opts.interpolation_threshold) + interpolate = false; + } + + if (interpolate) { gl_video_interpolate_frame(p, frame, fbo); } else { bool is_new = !frame->redraw && !frame->repeat; @@ -2342,8 +2435,8 @@ static void gl_video_upload_image(struct gl_video *p, struct mp_image *mpi) if (pbo) gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, plane->gl_buffer); gl->ActiveTexture(GL_TEXTURE0 + n); - gl->BindTexture(p->gl_target, plane->gl_texture); - glUploadTex(gl, p->gl_target, plane->gl_format, plane->gl_type, + gl->BindTexture(plane->gl_target, plane->gl_texture); + glUploadTex(gl, plane->gl_target, plane->gl_format, plane->gl_type, mpi->planes[n], mpi->stride[n], 0, 0, plane->w, plane->h, 0); } gl->ActiveTexture(GL_TEXTURE0); @@ -2373,6 +2466,8 @@ static bool test_fbo(struct gl_video *p) static bool check_dumb_mode(struct gl_video *p) { struct gl_video_opts *o = &p->opts; + if (p->use_integer_conversion) + return false; if (o->dumb_mode) return true; if (o->target_prim || o->target_trc || o->linear_scaling || @@ -2405,8 +2500,11 @@ static void check_gl_features(struct gl_video *p) bool have_texrg = gl->mpgl_caps & MPGL_CAP_TEX_RG; if (have_fbo) { - if (!p->opts.fbo_format) - p->opts.fbo_format = gl->es ? GL_RGB10_A2 : GL_RGBA16; + if (!p->opts.fbo_format) { + p->opts.fbo_format = GL_RGBA16; + if (gl->es) + p->opts.fbo_format = have_float_tex ? GL_RGBA16F : GL_RGB10_A2; + } have_fbo = test_fbo(p); } @@ -2415,8 +2513,9 @@ static void check_gl_features(struct gl_video *p) MP_WARN(p, "Disabling PBOs (GLES unsupported).\n"); } + p->forced_dumb_mode = p->opts.dumb_mode || !have_fbo || !have_texrg; bool voluntarily_dumb = check_dumb_mode(p); - if (p->opts.dumb_mode || !have_fbo || !have_texrg || voluntarily_dumb) { + if (p->forced_dumb_mode || voluntarily_dumb) { if (voluntarily_dumb) { MP_VERBOSE(p, "No advanced processing required. Enabling dumb mode.\n"); } else if (!p->opts.dumb_mode) { @@ -2619,6 +2718,17 @@ static void packed_fmt_swizzle(char w[5], const struct fmt_entry *texfmt, w[4] = '\0'; } +// Like find_tex_format(), but takes bits (not bytes), and but if no fixed point +// format is available, return an unsigned integer format. +static const struct fmt_entry *find_plane_format(GL *gl, int bytes_per_comp, + int n_channels) +{ + const struct fmt_entry *e = find_tex_format(gl, bytes_per_comp, n_channels); + if (e->format || gl->es < 300) + return e; + return &gl_ui_byte_formats_gles3[n_channels - 1 + (bytes_per_comp - 1) * 4]; +} + static bool init_format(int fmt, struct gl_video *init) { struct GL *gl = init->gl; @@ -2646,7 +2756,7 @@ static bool init_format(int fmt, struct gl_video *init) int bits = desc.component_bits; if ((desc.flags & MP_IMGFLAG_NE) && bits >= 8 && bits <= 16) { init->has_alpha = desc.num_planes > 3; - plane_format[0] = find_tex_format(gl, (bits + 7) / 8, 1); + plane_format[0] = find_plane_format(gl, (bits + 7) / 8, 1); for (int p = 1; p < desc.num_planes; p++) plane_format[p] = plane_format[0]; // RGB/planar @@ -2660,8 +2770,8 @@ static bool init_format(int fmt, struct gl_video *init) if (desc.flags & MP_IMGFLAG_YUV_NV) { int bits = desc.component_bits; if ((desc.flags & MP_IMGFLAG_NE) && bits >= 8 && bits <= 16) { - plane_format[0] = find_tex_format(gl, (bits + 7) / 8, 1); - plane_format[1] = find_tex_format(gl, (bits + 7) / 8, 2); + plane_format[0] = find_plane_format(gl, (bits + 7) / 8, 1); + plane_format[1] = find_plane_format(gl, (bits + 7) / 8, 2); if (desc.flags & MP_IMGFLAG_YUV_NV_SWAP) snprintf(init->color_swizzle, sizeof(init->color_swizzle), "rbga"); goto supported; @@ -2716,10 +2826,20 @@ supported: return false; } + int use_integer = -1; for (int p = 0; p < desc.num_planes; p++) { if (!plane_format[p]->format) return false; + int use_int_plane = !!is_integer_format(plane_format[p]); + if (use_integer < 0) + use_integer = use_int_plane; + if (use_integer != use_int_plane) + return false; // mixed planes not supported } + init->use_integer_conversion = use_integer; + + if (init->use_integer_conversion && init->forced_dumb_mode) + return false; for (int p = 0; p < desc.num_planes; p++) { struct texplane *plane = &init->image.planes[p]; @@ -2728,6 +2848,7 @@ supported: plane->gl_format = format->format; plane->gl_internal_format = format->internal_format; plane->gl_type = format->type; + plane->use_integer = init->use_integer_conversion; } init->is_yuv = desc.flags & MP_IMGFLAG_YUV; |