diff options
Diffstat (limited to 'video/out/gl_video.c')
-rw-r--r-- | video/out/gl_video.c | 410 |
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); +} |