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.c285
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;