summaryrefslogtreecommitdiffstats
path: root/video/out/gl_video.c
diff options
context:
space:
mode:
Diffstat (limited to 'video/out/gl_video.c')
-rw-r--r--video/out/gl_video.c410
1 files changed, 311 insertions, 99 deletions
diff --git a/video/out/gl_video.c b/video/out/gl_video.c
index 6643b73150..4022d5fa8e 100644
--- a/video/out/gl_video.c
+++ b/video/out/gl_video.c
@@ -37,6 +37,7 @@
#include "aspect.h"
#include "video/memcpy_pic.h"
#include "bitmap_packer.h"
+#include "dither.h"
static const char vo_opengl_shaders[] =
// Generated from gl_video_shaders.glsl
@@ -131,7 +132,7 @@ struct fbotex {
GLuint fbo;
GLuint texture;
int tex_w, tex_h; // size of .texture
- int vp_w, vp_h; // viewport of fbo / used part of the texture
+ int vp_x, vp_y, vp_w, vp_h; // viewport of fbo / used part of the texture
};
struct gl_video {
@@ -155,7 +156,7 @@ struct gl_video {
GLuint dither_texture;
float dither_quantization;
- float dither_multiply;
+ float dither_center;
int dither_size;
uint32_t image_w, image_h;
@@ -165,9 +166,11 @@ struct gl_video {
struct mp_imgfmt_desc image_desc;
- bool is_yuv;
+ bool is_yuv, is_rgb;
bool is_linear_rgb;
+ float input_gamma, conv_gamma;
+
// per pixel (full pixel when packed, each component when planar)
int plane_bits;
int plane_count;
@@ -182,6 +185,7 @@ struct gl_video {
struct mp_csp_details colorspace;
struct mp_csp_equalizer video_eq;
+ enum mp_chroma_location chroma_loc;
struct mp_rect src_rect; // displayed part of the source video
struct mp_rect dst_rect; // video rectangle on output window
@@ -190,6 +194,10 @@ struct gl_video {
int frames_rendered;
+ // Cached because computing it can take relatively long
+ int last_dither_matrix_size;
+ float *last_dither_matrix;
+
void *scratch;
};
@@ -227,6 +235,7 @@ static const char *osd_shaders[SUBBITMAP_COUNT] = {
static const struct gl_video_opts gl_video_opts_def = {
.npot = 1,
.dither_depth = -1,
+ .dither_size = 6,
.fbo_format = GL_RGB,
.scale_sep = 1,
.scalers = { "bilinear", "bilinear" },
@@ -249,6 +258,7 @@ const struct m_sub_options gl_video_conf = {
OPT_STRING_VALIDATE("cscale", scalers[1], 0, validate_scaler_opt),
OPT_FLOAT("lparam1", scaler_params[0], 0),
OPT_FLOAT("lparam2", scaler_params[1], 0),
+ OPT_FLAG("scaler-resizes-only", scaler_resizes_only, 0),
OPT_FLAG("fancy-downscaling", fancy_downscaling, 0),
OPT_FLAG("indirect", indirect, 0),
OPT_FLAG("scale-sep", scale_sep, 0),
@@ -266,6 +276,14 @@ const struct m_sub_options gl_video_conf = {
{"rgba32f", GL_RGBA32F})),
OPT_CHOICE_OR_INT("dither-depth", dither_depth, 0, -1, 16,
({"no", -1}, {"auto", 0})),
+ OPT_CHOICE("dither", dither_algo, 0,
+ ({"fruit", 0}, {"ordered", 1}, {"no", -1})),
+ OPT_INTRANGE("dither-size-fruit", dither_size, 0, 2, 8),
+ OPT_FLAG("temporal-dither", temporal_dither, 0),
+ OPT_CHOICE("chroma-location", chroma_location, 0,
+ ({"auto", MP_CHROMA_AUTO},
+ {"center", MP_CHROMA_CENTER},
+ {"left", MP_CHROMA_LEFT})),
OPT_FLAG("alpha", enable_alpha, 0),
{0}
},
@@ -374,7 +392,8 @@ static void write_quad(struct vertex *va,
#undef COLOR_INIT
}
-static bool fbotex_init(struct gl_video *p, struct fbotex *fbo, int w, int h)
+static bool fbotex_init(struct gl_video *p, struct fbotex *fbo, int w, int h,
+ GLenum iformat)
{
GL *gl = p->gl;
bool res = true;
@@ -383,17 +402,19 @@ static bool fbotex_init(struct gl_video *p, struct fbotex *fbo, int w, int h)
assert(!fbo->fbo);
assert(!fbo->texture);
- tex_size(p, w, h, &fbo->tex_w, &fbo->tex_h);
+ *fbo = (struct fbotex) {
+ .vp_w = w,
+ .vp_h = h,
+ };
- fbo->vp_w = w;
- fbo->vp_h = h;
+ tex_size(p, w, h, &fbo->tex_w, &fbo->tex_h);
mp_msg(MSGT_VO, MSGL_V, "[gl] Create FBO: %dx%d\n", fbo->tex_w, fbo->tex_h);
gl->GenFramebuffers(1, &fbo->fbo);
gl->GenTextures(1, &fbo->texture);
gl->BindTexture(GL_TEXTURE_2D, fbo->texture);
- gl->TexImage2D(GL_TEXTURE_2D, 0, p->opts.fbo_format,
+ gl->TexImage2D(GL_TEXTURE_2D, 0, iformat,
fbo->tex_w, fbo->tex_h, 0,
GL_RGB, GL_UNSIGNED_BYTE, NULL);
default_tex_params(gl, GL_TEXTURE_2D, GL_LINEAR);
@@ -452,6 +473,11 @@ static void update_uniforms(struct gl_video *p, GLuint program)
.texture_bits = (p->plane_bits + 7) & ~7,
};
mp_csp_copy_equalizer_values(&cparams, &p->video_eq);
+ if (p->image_desc.flags & MP_IMGFLAG_XYZ) {
+ cparams.colorspace.format = MP_CSP_XYZ;
+ cparams.input_bits = 8;
+ cparams.texture_bits = 8;
+ }
loc = gl->GetUniformLocation(program, "transform");
if (loc >= 0) {
@@ -463,11 +489,16 @@ static void update_uniforms(struct gl_video *p, GLuint program)
loc = gl->GetUniformLocation(program, "colormatrix");
if (loc >= 0) {
float yuv2rgb[3][4] = {{0}};
- if (p->is_yuv)
- mp_get_yuv2rgb_coeffs(&cparams, yuv2rgb);
+ mp_get_yuv2rgb_coeffs(&cparams, yuv2rgb);
gl->UniformMatrix4x3fv(loc, 1, GL_TRUE, &yuv2rgb[0][0]);
}
+ gl->Uniform1f(gl->GetUniformLocation(program, "input_gamma"),
+ p->input_gamma);
+
+ gl->Uniform1f(gl->GetUniformLocation(program, "conv_gamma"),
+ p->conv_gamma);
+
gl->Uniform3f(gl->GetUniformLocation(program, "inv_gamma"),
1.0 / cparams.rgamma,
1.0 / cparams.ggamma,
@@ -481,7 +512,28 @@ static void update_uniforms(struct gl_video *p, GLuint program)
gl->Uniform1i(gl->GetUniformLocation(program, textures_n), n);
gl->Uniform2f(gl->GetUniformLocation(program, textures_size_n),
- p->image.planes[n].w, p->image.planes[n].h);
+ p->image.planes[n].tex_w, p->image.planes[n].tex_h);
+ }
+
+ loc = gl->GetUniformLocation(program, "chroma_center_offset");
+ if (loc >= 0) {
+ int chr = p->opts.chroma_location;
+ if (!chr)
+ chr = p->chroma_loc;
+ int cx, cy;
+ mp_get_chroma_location(chr, &cx, &cy);
+ // By default texture coordinates are such that chroma is centered with
+ // any chroma subsampling. If a specific direction is given, make it
+ // so that the luma and chroma sample line up exactly.
+ // For 4:4:4, setting chroma location should have no effect at all.
+ // luma sample size (in chroma coord. space)
+ float ls_w = 1.0 / (1 << p->image_desc.chroma_xs);
+ float ls_h = 1.0 / (1 << p->image_desc.chroma_ys);
+ // move chroma center to luma center (in chroma coord. space)
+ float o_x = ls_w < 1 ? ls_w * -cx / 2 : 0;
+ float o_y = ls_h < 1 ? ls_h * -cy / 2 : 0;
+ gl->Uniform2f(loc, o_x / FFMAX(p->image.planes[1].w, 1),
+ o_y / FFMAX(p->image.planes[1].h, 1));
}
gl->Uniform2f(gl->GetUniformLocation(program, "dither_size"),
@@ -499,8 +551,8 @@ static void update_uniforms(struct gl_video *p, GLuint program)
gl->Uniform1i(gl->GetUniformLocation(program, "dither"), TEXUNIT_DITHER);
gl->Uniform1f(gl->GetUniformLocation(program, "dither_quantization"),
p->dither_quantization);
- gl->Uniform1f(gl->GetUniformLocation(program, "dither_multiply"),
- p->dither_multiply);
+ gl->Uniform1f(gl->GetUniformLocation(program, "dither_center"),
+ p->dither_center);
float sparam1 = p->opts.scaler_params[0];
gl->Uniform1f(gl->GetUniformLocation(program, "filter_param1"),
@@ -608,12 +660,15 @@ static void bind_attrib_locs(GL *gl, GLuint program)
gl->BindAttribLocation(program, VERTEX_ATTRIB_TEXCOORD, "vertex_texcoord");
}
+#define PRELUDE_END "// -- prelude end\n"
+
static GLuint create_program(GL *gl, const char *name, const char *header,
const char *vertex, const char *frag)
{
- mp_msg(MSGT_VO, MSGL_V, "[gl] compiling shader program '%s'\n", name);
- mp_msg(MSGT_VO, MSGL_V, "[gl] header:\n");
- mp_log_source(MSGT_VO, MSGL_V, header);
+ mp_msg(MSGT_VO, MSGL_V, "[gl] compiling shader program '%s', header:\n", name);
+ const char *real_header = strstr(header, PRELUDE_END);
+ real_header = real_header ? real_header + strlen(PRELUDE_END) : header;
+ mp_log_source(MSGT_VO, MSGL_V, real_header);
GLuint prog = gl->CreateProgram();
prog_create_shader(gl, prog, GL_VERTEX_SHADER, header, vertex);
prog_create_shader(gl, prog, GL_FRAGMENT_SHADER, header, frag);
@@ -679,8 +734,8 @@ static void compile_shaders(struct gl_video *p)
char *shader_prelude = get_section(tmp, src, "prelude");
char *s_video = get_section(tmp, src, "frag_video");
- char *header = talloc_asprintf(tmp, "#version %d\n%s", gl->glsl_version,
- shader_prelude);
+ char *header = talloc_asprintf(tmp, "#version %d\n%s%s", gl->glsl_version,
+ shader_prelude, PRELUDE_END);
// Need to pass alpha through the whole chain. (Not needed for OSD shaders.)
shader_def_opt(&header, "USE_ALPHA", p->opts.enable_alpha);
@@ -704,8 +759,22 @@ static void compile_shaders(struct gl_video *p)
char *header_final = talloc_strdup(tmp, "");
char *header_sep = NULL;
- bool convert_input_to_linear = !p->is_linear_rgb &&
- (p->opts.srgb || p->use_lut_3d);
+ float input_gamma = 1.0;
+ float conv_gamma = 1.0;
+
+ if (p->image_desc.flags & MP_IMGFLAG_XYZ) {
+ input_gamma *= 2.6;
+ conv_gamma *= 1.0 / 2.2;
+ }
+
+ if (!p->is_linear_rgb && (p->opts.srgb || p->use_lut_3d))
+ conv_gamma *= 1.0 / 0.45;
+
+ p->input_gamma = input_gamma;
+ p->conv_gamma = conv_gamma;
+
+ bool convert_input_gamma = p->input_gamma != 1.0;
+ bool convert_input_to_linear = p->conv_gamma != 1.0;
if (p->image_format == IMGFMT_NV12 || p->image_format == IMGFMT_NV21) {
shader_def(&header_conv, "USE_CONV", "CONV_NV12");
@@ -716,8 +785,9 @@ static void compile_shaders(struct gl_video *p)
shader_def_opt(&header_conv, "USE_GBRP", p->image_format == IMGFMT_GBRP);
shader_def_opt(&header_conv, "USE_SWAP_UV", p->image_format == IMGFMT_NV21);
shader_def_opt(&header_conv, "USE_YGRAY", p->is_yuv && p->plane_count == 1);
- shader_def_opt(&header_conv, "USE_COLORMATRIX", p->is_yuv);
- shader_def_opt(&header_conv, "USE_LINEAR_CONV", convert_input_to_linear);
+ shader_def_opt(&header_conv, "USE_INPUT_GAMMA", convert_input_gamma);
+ shader_def_opt(&header_conv, "USE_COLORMATRIX", !p->is_rgb);
+ shader_def_opt(&header_conv, "USE_CONV_GAMMA", convert_input_to_linear);
if (p->opts.enable_alpha && p->plane_count == 4)
shader_def(&header_conv, "USE_ALPHA_PLANE", "3");
@@ -726,6 +796,7 @@ static void compile_shaders(struct gl_video *p)
shader_def_opt(&header_final, "USE_3DLUT", p->use_lut_3d);
shader_def_opt(&header_final, "USE_SRGB", p->opts.srgb);
shader_def_opt(&header_final, "USE_DITHER", p->dither_texture != 0);
+ shader_def_opt(&header_final, "USE_TEMPORAL_DITHER", p->opts.temporal_dither);
if (p->opts.scale_sep && p->scalers[0].kernel) {
header_sep = talloc_strdup(tmp, "");
@@ -745,7 +816,7 @@ static void compile_shaders(struct gl_video *p)
// Don't sample from input video textures before converting the input to
// linear light. (Unneeded when sRGB textures are used.)
- if (convert_input_to_linear)
+ if (convert_input_gamma || convert_input_to_linear)
use_indirect = true;
// It doesn't make sense to scale the chroma with cscale in the 1. scale
@@ -898,18 +969,6 @@ static void init_scaler(struct gl_video *p, struct scaler *scaler)
debug_check_gl(p, "after initializing scaler");
}
-static void make_dither_matrix(unsigned char *m, int size)
-{
- m[0] = 0;
- for (int sz = 1; sz < size; sz *= 2) {
- int offset[] = {sz*size, sz, sz * (size+1), 0};
- for (int i = 0; i < 4; i++)
- for (int y = 0; y < sz * size; y += size)
- for (int x = 0; x < sz; x++)
- m[x+y+offset[i]] = m[x+y] * 4 + (3-i) * 256/size/size;
- }
-}
-
static void init_dither(struct gl_video *p)
{
GL *gl = p->gl;
@@ -919,30 +978,57 @@ static void init_dither(struct gl_video *p)
if (p->opts.dither_depth > 0)
dst_depth = p->opts.dither_depth;
- if (p->opts.dither_depth < 0)
+ if (p->opts.dither_depth < 0 || p->opts.dither_algo < 0)
return;
mp_msg(MSGT_VO, MSGL_V, "[gl] Dither to %d.\n", dst_depth);
+ int tex_size;
+ void *tex_data;
+ GLint tex_iformat;
+ GLenum tex_type;
+ unsigned char temp[256];
+
+ if (p->opts.dither_algo == 0) {
+ int sizeb = p->opts.dither_size;
+ int size = 1 << sizeb;
+
+ if (p->last_dither_matrix_size != size) {
+ p->last_dither_matrix = talloc_realloc(p, p->last_dither_matrix,
+ float, size * size);
+ mp_make_fruit_dither_matrix(p->last_dither_matrix, sizeb);
+ p->last_dither_matrix_size = size;
+ }
+
+ tex_size = size;
+ tex_iformat = GL_R16;
+ tex_type = GL_FLOAT;
+ tex_data = p->last_dither_matrix;
+ } else {
+ assert(sizeof(temp) >= 8 * 8);
+ mp_make_ordered_dither_matrix(temp, 8);
+
+ tex_size = 8;
+ tex_iformat = GL_RED;
+ tex_type = GL_UNSIGNED_BYTE;
+ tex_data = temp;
+ }
+
// This defines how many bits are considered significant for output on
- // screen. The superfluous bits will be used for rounded according to the
+ // screen. The superfluous bits will be used for rounding according to the
// dither matrix. The precision of the source implicitly decides how many
// dither patterns can be visible.
p->dither_quantization = (1 << dst_depth) - 1;
- int size = 8;
- p->dither_multiply = p->dither_quantization + 1.0 / (size*size);
- unsigned char dither[256];
- make_dither_matrix(dither, size);
-
- p->dither_size = size;
+ p->dither_center = 0.5 / (tex_size * tex_size);
+ p->dither_size = tex_size;
gl->ActiveTexture(GL_TEXTURE0 + TEXUNIT_DITHER);
gl->GenTextures(1, &p->dither_texture);
gl->BindTexture(GL_TEXTURE_2D, p->dither_texture);
gl->PixelStorei(GL_UNPACK_ALIGNMENT, 1);
gl->PixelStorei(GL_UNPACK_ROW_LENGTH, 0);
- gl->TexImage2D(GL_TEXTURE_2D, 0, GL_RED, size, size, 0, GL_RED,
- GL_UNSIGNED_BYTE, dither);
+ gl->TexImage2D(GL_TEXTURE_2D, 0, tex_iformat, tex_size, tex_size, 0, GL_RED,
+ tex_type, tex_data);
gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
@@ -958,6 +1044,22 @@ static void recreate_osd(struct gl_video *p)
p->osd->use_pbo = p->opts.pbo;
}
+static bool does_resize(struct mp_rect src, struct mp_rect dst)
+{
+ return src.x1 - src.x0 != dst.x1 - dst.x0 ||
+ src.y1 - src.y0 != dst.y1 - dst.y0;
+}
+
+static const char *expected_scaler(struct gl_video *p, int unit)
+{
+ if (p->opts.scaler_resizes_only && unit == 0 &&
+ !does_resize(p->src_rect, p->dst_rect))
+ {
+ return "bilinear";
+ }
+ return p->opts.scalers[unit];
+}
+
static void reinit_rendering(struct gl_video *p)
{
mp_msg(MSGT_VO, MSGL_V, "[gl] Reinit rendering.\n");
@@ -969,6 +1071,9 @@ static void reinit_rendering(struct gl_video *p)
if (!p->image.planes[0].gl_texture)
return;
+ for (int n = 0; n < 2; n++)
+ p->scalers[n].name = expected_scaler(p, n);
+
init_dither(p);
init_scaler(p, &p->scalers[0]);
@@ -978,7 +1083,8 @@ static void reinit_rendering(struct gl_video *p)
update_all_uniforms(p);
if (p->indirect_program && !p->indirect_fbo.fbo)
- fbotex_init(p, &p->indirect_fbo, p->texture_w, p->texture_h);
+ fbotex_init(p, &p->indirect_fbo, p->image_w, p->image_h,
+ p->opts.fbo_format);
recreate_osd(p);
}
@@ -1048,7 +1154,7 @@ static void init_video(struct gl_video *p)
check_gl_features(p);
- if (!p->is_yuv && (p->opts.srgb || p->use_lut_3d)) {
+ if (p->is_rgb && (p->opts.srgb || p->use_lut_3d)) {
p->is_linear_rgb = true;
p->image.planes[0].gl_internal_format = GL_SRGB;
}
@@ -1117,17 +1223,36 @@ static void uninit_video(struct gl_video *p)
fbotex_uninit(p, &p->scale_sep_fbo);
}
-static void render_to_fbo(struct gl_video *p, struct fbotex *fbo, int w, int h,
- int tex_w, int tex_h)
+static void change_dither_trafo(struct gl_video *p)
+{
+ GL *gl = p->gl;
+ int program = p->final_program;
+
+ int phase = p->frames_rendered % 8u;
+ float r = phase * (M_PI / 2); // rotate
+ float m = phase < 4 ? 1 : -1; // mirror
+
+ gl->UseProgram(program);
+
+ float matrix[2][2] = {{cos(r), -sin(r) },
+ {sin(r) * m, cos(r) * m}};
+ gl->UniformMatrix2fv(gl->GetUniformLocation(program, "dither_trafo"),
+ 1, GL_TRUE, &matrix[0][0]);
+
+ gl->UseProgram(0);
+}
+
+static void render_to_fbo(struct gl_video *p, struct fbotex *fbo,
+ int x, int y, int w, int h, int tex_w, int tex_h)
{
GL *gl = p->gl;
- gl->Viewport(0, 0, fbo->vp_w, fbo->vp_h);
+ gl->Viewport(fbo->vp_x, fbo->vp_y, fbo->vp_w, fbo->vp_h);
gl->BindFramebuffer(GL_FRAMEBUFFER, fbo->fbo);
struct vertex vb[VERTICES_PER_QUAD];
write_quad(vb, -1, -1, 1, 1,
- 0, 0, w, h,
+ x, y, x + w, y + h,
tex_w, tex_h,
NULL, false);
draw_triangles(p, vb, VERTICES_PER_QUAD);
@@ -1137,7 +1262,8 @@ static void render_to_fbo(struct gl_video *p, struct fbotex *fbo, int w, int h,
}
-static void handle_pass(struct gl_video *p, struct fbotex **source,
+// *chain contains the source, and is overwritten with a copy of the result
+static void handle_pass(struct gl_video *p, struct fbotex *chain,
struct fbotex *fbo, GLuint program)
{
GL *gl = p->gl;
@@ -1145,11 +1271,12 @@ static void handle_pass(struct gl_video *p, struct fbotex **source,
if (!program)
return;
- gl->BindTexture(GL_TEXTURE_2D, (*source)->texture);
+ gl->BindTexture(GL_TEXTURE_2D, chain->texture);
gl->UseProgram(program);
- render_to_fbo(p, fbo, (*source)->vp_w, (*source)->vp_h,
- (*source)->tex_w, (*source)->tex_h);
- *source = fbo;
+ render_to_fbo(p, fbo, chain->vp_x, chain->vp_y,
+ chain->vp_w, chain->vp_h,
+ chain->tex_w, chain->tex_h);
+ *chain = *fbo;
}
void gl_video_render_frame(struct gl_video *p)
@@ -1159,6 +1286,9 @@ void gl_video_render_frame(struct gl_video *p)
struct video_image *vimg = &p->image;
bool is_flipped = vimg->image_flipped;
+ if (p->opts.temporal_dither)
+ change_dither_trafo(p);
+
if (p->dst_rect.x0 > p->vp_x || p->dst_rect.y0 > p->vp_y
|| p->dst_rect.x1 < p->vp_x + p->vp_w
|| p->dst_rect.y1 < p->vp_y + p->vp_h)
@@ -1171,24 +1301,34 @@ void gl_video_render_frame(struct gl_video *p)
set_image_textures(p, vimg);
- struct fbotex dummy = {
- .vp_w = p->image_w, .vp_h = p->image_h,
- .tex_w = p->texture_w, .tex_h = p->texture_h,
+ struct fbotex chain = {
+ .vp_w = p->image_w,
+ .vp_h = p->image_h,
+ .tex_w = p->texture_w,
+ .tex_h = p->texture_h,
.texture = vimg->planes[0].gl_texture,
};
- struct fbotex *source = &dummy;
- handle_pass(p, &source, &p->indirect_fbo, p->indirect_program);
- handle_pass(p, &source, &p->scale_sep_fbo, p->scale_sep_program);
+ handle_pass(p, &chain, &p->indirect_fbo, p->indirect_program);
+
+ // Clip to visible height so that separate scaling scales the visible part
+ // only (and the target FBO texture can have a bounded size).
+ // Don't clamp width; too hard to get correct final scaling on l/r borders.
+ chain.vp_y = p->src_rect.y0,
+ chain.vp_h = p->src_rect.y1 - p->src_rect.y0,
- gl->BindTexture(GL_TEXTURE_2D, source->texture);
+ handle_pass(p, &chain, &p->scale_sep_fbo, p->scale_sep_program);
+
+ gl->BindTexture(GL_TEXTURE_2D, chain.texture);
gl->UseProgram(p->final_program);
- float final_texw = p->image_w * source->tex_w / (float)source->vp_w;
- float final_texh = p->image_h * source->tex_h / (float)source->vp_h;
+ struct mp_rect src = {p->src_rect.x0, chain.vp_y,
+ p->src_rect.x1, chain.vp_y + chain.vp_h};
+ int src_texw = chain.tex_w;
+ int src_texh = chain.tex_h;
if (p->opts.stereo_mode) {
- int w = p->src_rect.x1 - p->src_rect.x0;
+ int w = src.x1 - src.x0;
int imgw = p->image_w;
glEnable3DLeft(gl, p->opts.stereo_mode);
@@ -1196,9 +1336,9 @@ void gl_video_render_frame(struct gl_video *p)
write_quad(vb,
p->dst_rect.x0, p->dst_rect.y0,
p->dst_rect.x1, p->dst_rect.y1,
- p->src_rect.x0 / 2, p->src_rect.y0,
- p->src_rect.x0 / 2 + w / 2, p->src_rect.y1,
- final_texw, final_texh,
+ src.x0 / 2, src.y0,
+ src.x0 / 2 + w / 2, src.y1,
+ src_texw, src_texh,
NULL, is_flipped);
draw_triangles(p, vb, VERTICES_PER_QUAD);
@@ -1207,9 +1347,9 @@ void gl_video_render_frame(struct gl_video *p)
write_quad(vb,
p->dst_rect.x0, p->dst_rect.y0,
p->dst_rect.x1, p->dst_rect.y1,
- p->src_rect.x0 / 2 + imgw / 2, p->src_rect.y0,
- p->src_rect.x0 / 2 + imgw / 2 + w / 2, p->src_rect.y1,
- final_texw, final_texh,
+ src.x0 / 2 + imgw / 2, src.y0,
+ src.x0 / 2 + imgw / 2 + w / 2, src.y1,
+ src_texw, src_texh,
NULL, is_flipped);
draw_triangles(p, vb, VERTICES_PER_QUAD);
@@ -1218,15 +1358,17 @@ void gl_video_render_frame(struct gl_video *p)
write_quad(vb,
p->dst_rect.x0, p->dst_rect.y0,
p->dst_rect.x1, p->dst_rect.y1,
- p->src_rect.x0, p->src_rect.y0,
- p->src_rect.x1, p->src_rect.y1,
- final_texw, final_texh,
+ src.x0, src.y0,
+ src.x1, src.y1,
+ src_texw, src_texh,
NULL, is_flipped);
draw_triangles(p, vb, VERTICES_PER_QUAD);
}
gl->UseProgram(0);
+ p->frames_rendered++;
+
debug_check_gl(p, "after video rendering");
}
@@ -1239,7 +1381,8 @@ static void update_window_sized_objects(struct gl_video *p)
// Round up to an arbitrary alignment to make window resizing or
// panscan controls smoother (less texture reallocations).
int height = FFALIGN(h, 256);
- fbotex_init(p, &p->scale_sep_fbo, p->image_w, height);
+ fbotex_init(p, &p->scale_sep_fbo, p->image_w, height,
+ p->opts.fbo_format);
}
p->scale_sep_fbo.vp_w = p->image_w;
p->scale_sep_fbo.vp_h = h;
@@ -1261,6 +1404,10 @@ static void check_resize(struct gl_video *p)
need_scaler_update |= (tkernel.inv_scale != old.inv_scale);
}
}
+ for (int n = 0; n < 2; n++) {
+ if (strcmp(p->scalers[n].name, expected_scaler(p, n)) != 0)
+ need_scaler_reinit = true;
+ }
if (need_scaler_reinit) {
reinit_rendering(p);
} else if (need_scaler_update) {
@@ -1301,11 +1448,6 @@ static bool get_image(struct gl_video *p, struct mp_image *mpi)
if (!p->opts.pbo)
return false;
- // We don't support alpha planes. (Disabling PBOs with normal draw calls is
- // an undesired, but harmless side-effect.)
- if (mpi->num_planes != p->plane_count)
- return false;
-
struct video_image *vimg = &p->image;
for (int n = 0; n < p->plane_count; n++) {
@@ -1334,7 +1476,7 @@ void gl_video_upload_image(struct gl_video *p, struct mp_image *mpi)
GL *gl = p->gl;
int n;
- assert(mpi->num_planes >= p->plane_count);
+ assert(mpi->num_planes == p->plane_count);
struct video_image *vimg = &p->image;
@@ -1398,18 +1540,15 @@ struct mp_image *gl_video_download_image(struct gl_video *p)
return image;
}
-static void draw_osd_cb(void *ctx, struct sub_bitmaps *imgs)
+static void draw_osd_cb(void *ctx, struct mpgl_osd_part *osd,
+ struct sub_bitmaps *imgs)
{
struct gl_video *p = ctx;
GL *gl = p->gl;
- struct mpgl_osd_part *osd = mpgl_osd_generate(p->osd, imgs);
- if (!osd)
- return;
-
assert(osd->format != SUBBITMAP_EMPTY);
- if (!osd->num_vertices) {
+ if (!osd->num_vertices && imgs) {
osd->vertices = talloc_realloc(osd, osd->vertices, struct vertex,
osd->packer->count * VERTICES_PER_QUAD);
@@ -1449,7 +1588,7 @@ void gl_video_draw_osd(struct gl_video *p, struct osd_state *osd)
GL *gl = p->gl;
assert(p->osd);
- osd_draw(osd, p->osd_rect, osd->vo_pts, 0, p->osd->formats, draw_osd_cb, p);
+ mpgl_osd_draw_cb(p->osd, osd, p->osd_rect, draw_osd_cb, p);
// 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
@@ -1457,6 +1596,48 @@ void gl_video_draw_osd(struct gl_video *p, struct osd_state *osd)
gl->Flush();
}
+static bool test_fbo(struct gl_video *p, GLenum format)
+{
+ static const float vals[] = {
+ 127 / 255.0f, // full 8 bit integer
+ 32767 / 65535.0f, // full 16 bit integer
+ 0xFFFFFF / (float)(1 << 25), // float mantissa
+ 2, // out of range value
+ };
+ static const char *val_names[] = {
+ "8-bit precision",
+ "16-bit precision",
+ "full float",
+ "out of range value (2)",
+ };
+
+ GL *gl = p->gl;
+ bool success = false;
+ struct fbotex fbo = {0};
+ gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
+ gl->PixelStorei(GL_PACK_ALIGNMENT, 1);
+ gl->PixelStorei(GL_PACK_ROW_LENGTH, 0);
+ if (fbotex_init(p, &fbo, 16, 16, format)) {
+ gl->BindFramebuffer(GL_FRAMEBUFFER, fbo.fbo);
+ gl->ReadBuffer(GL_COLOR_ATTACHMENT0);
+ for (int i = 0; i < 4; i++) {
+ float p = -1;
+ float val = vals[i];
+ gl->ClearColor(val, 0.0f, 0.0f, 1.0f);
+ gl->Clear(GL_COLOR_BUFFER_BIT);
+ gl->ReadPixels(0, 0, 1, 1, GL_RED, GL_FLOAT, &p);
+ mp_msg(MSGT_VO, MSGL_V, " %s: %a\n", val_names[i], val - p);
+ }
+ gl->BindFramebuffer(GL_FRAMEBUFFER, 0);
+ glCheckError(gl, "after FBO read");
+ success = true;
+ }
+ fbotex_uninit(p, &fbo);
+ glCheckError(gl, "FBO test");
+ gl->ClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+ return success;
+}
+
// Disable features that are not supported with the current OpenGL version.
static void check_gl_features(struct gl_video *p)
{
@@ -1473,9 +1654,17 @@ static void check_gl_features(struct gl_video *p)
int n_disabled = 0;
if (have_fbo) {
- struct fbotex fbo = {0};
- have_fbo = fbotex_init(p, &fbo, 16, 16);
- fbotex_uninit(p, &fbo);
+ mp_msg(MSGT_VO, MSGL_V, "Testing user-set FBO format\n");
+ have_fbo = test_fbo(p, p->opts.fbo_format);
+ }
+
+ // fruit dithering mode and the 3D lut use this texture format
+ if ((p->opts.dither_depth >= 0 && p->opts.dither_algo == 0) ||
+ p->use_lut_3d)
+ {
+ // doesn't disalbe anything; it's just for the log
+ mp_msg(MSGT_VO, MSGL_V, "Testing GL_R16 FBO (dithering/LUT)\n");
+ test_fbo(p, GL_R16);
}
// Disable these only if the user didn't disable scale-sep on the command
@@ -1634,6 +1823,12 @@ static bool init_format(int fmt, struct gl_video *init)
plane_format[0] = byte_formats[1];
}
+ // XYZ (same organization as RGB packed, but requires conversion matrix)
+ if (!supported && fmt == IMGFMT_XYZ12) {
+ supported = true;
+ plane_format[0] = IMGFMT_RGB48;
+ }
+
// All formats in mp_to_gl_formats[] are supported
// If it's not in the table, it will be rejected below.
// Includes packed RGB and YUV formats
@@ -1666,6 +1861,7 @@ static bool init_format(int fmt, struct gl_video *init)
return false;
init->is_yuv = desc.flags & MP_IMGFLAG_YUV;
+ init->is_rgb = desc.flags & MP_IMGFLAG_RGB;
init->is_linear_rgb = false;
init->plane_count = desc.num_planes;
init->image_desc = desc;
@@ -1678,17 +1874,20 @@ bool gl_video_check_format(int mp_format)
return init_format(mp_format, NULL);
}
-void gl_video_config(struct gl_video *p, int format, int w, int h, int dw, int dh)
+void gl_video_config(struct gl_video *p, struct mp_image_params *params)
{
- if (p->image_format != format || p->image_w != w || p->image_h != h) {
+ if (p->image_format != params->imgfmt || p->image_w != params->w ||
+ p->image_h != params->h)
+ {
uninit_video(p);
- p->image_w = w;
- p->image_h = h;
- init_format(format, p);
+ p->image_w = params->w;
+ p->image_h = params->h;
+ init_format(params->imgfmt, p);
init_video(p);
}
- p->image_dw = dw;
- p->image_dh = dh;
+ p->image_dw = params->d_w;
+ p->image_dh = params->d_h;
+ p->chroma_loc = params->chroma_location;
}
void gl_video_set_output_depth(struct gl_video *p, int r, int g, int b)
@@ -1797,3 +1996,16 @@ static int validate_scaler_opt(const m_option_t *opt, struct bstr name,
snprintf(s, sizeof(s), "%.*s", BSTR_P(param));
return handle_scaler_opt(s) ? 1 : M_OPT_INVALID;
}
+
+// Resize and redraw the contents of the window without further configuration.
+// Intended to be used in situations where the frontend can't really be
+// involved with reconfiguring the VO properly.
+// gl_video_resize() should be called when user interaction is done.
+void gl_video_resize_redraw(struct gl_video *p, int w, int h)
+{
+ p->gl->Viewport(p->vp_x, p->vp_y, w, h);
+ p->vp_w = w;
+ p->vp_h = h;
+ gl_video_render_frame(p);
+ mpgl_osd_redraw_cb(p->osd, draw_osd_cb, p);
+}