diff options
Diffstat (limited to 'video/out/gl_video.c')
-rw-r--r-- | video/out/gl_video.c | 1925 |
1 files changed, 628 insertions, 1297 deletions
diff --git a/video/out/gl_video.c b/video/out/gl_video.c index 5ddb6c5cad..a52bd82020 100644 --- a/video/out/gl_video.c +++ b/video/out/gl_video.c @@ -41,11 +41,6 @@ #include "bitmap_packer.h" #include "dither.h" -static const char vo_opengl_shaders[] = -// Generated from gl_video_shaders.glsl -#include "video/out/gl_video_shaders.h" -; - // Pixel width of 1D lookup textures. #define LOOKUP_TEXTURE_SIZE 256 @@ -70,14 +65,21 @@ static const char *const fixed_scale_filters[] = { int filter_sizes[] = {2, 4, 6, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60, 64, 0}; +struct vertex_pt { + float x, y; +}; + struct vertex { - float position[2]; - float texcoord[2]; + struct vertex_pt position; + struct vertex_pt texcoord[4]; }; static const struct gl_vao_entry vertex_vao[] = { - {"vertex_position", 2, GL_FLOAT, false, offsetof(struct vertex, position)}, - {"vertex_texcoord", 2, GL_FLOAT, false, offsetof(struct vertex, texcoord)}, + {"position", 2, GL_FLOAT, false, offsetof(struct vertex, position)}, + {"texcoord0", 2, GL_FLOAT, false, offsetof(struct vertex, texcoord[0])}, + {"texcoord1", 2, GL_FLOAT, false, offsetof(struct vertex, texcoord[1])}, + {"texcoord2", 2, GL_FLOAT, false, offsetof(struct vertex, texcoord[2])}, + {"texcoord3", 2, GL_FLOAT, false, offsetof(struct vertex, texcoord[3])}, {0} }; @@ -85,6 +87,7 @@ struct texplane { int w, h; int tex_w, tex_h; GLint gl_internal_format; + GLenum gl_target; GLenum gl_format; GLenum gl_type; GLuint gl_texture; @@ -102,11 +105,15 @@ struct video_image { struct scaler { int index; const char *name; + double scale_factor; float params[2]; float antiring; + + bool initialized; struct filter_kernel *kernel; GLuint gl_lut; - const char *lut_name; + GLenum gl_target; + struct fbotex sep_fbo; bool insufficient; // kernel points here @@ -121,6 +128,13 @@ struct fbosurface { #define FBOSURFACES_MAX 2 +struct src_tex { + GLuint gl_tex; + GLenum gl_target; + int tex_w, tex_h; + struct mp_rect src; +}; + struct gl_video { GL *gl; @@ -131,13 +145,12 @@ struct gl_video { int depth_g; int texture_16bit_depth; // actual bits available in 16 bit textures + struct gl_shader_cache *sc; + GLenum gl_target; // texture target (GL_TEXTURE_2D, ...) for video and FBOs struct gl_vao vao; - GLuint osd_programs[SUBBITMAP_COUNT]; - GLuint indirect_program, scale_sep_program, final_program, inter_program; - struct osd_state *osd_state; struct mpgl_osd *osd; double osd_pts; @@ -146,8 +159,6 @@ struct gl_video { bool use_lut_3d; GLuint dither_texture; - float dither_quantization; - float dither_center; int dither_size; struct mp_image_params real_image_params; // configured format @@ -159,7 +170,6 @@ struct gl_video { bool is_yuv, is_rgb, is_packed_yuv; bool has_alpha; char color_swizzle[5]; - float chroma_fix[2]; float input_gamma, conv_gamma; float user_gamma; @@ -169,8 +179,9 @@ struct gl_video { struct video_image image; struct fbotex indirect_fbo; // RGB target - struct fbotex scale_sep_fbo; // first pass when doing 2 pass scaling + struct fbotex chroma_merge_fbo; struct fbosurface surfaces[FBOSURFACES_MAX]; + size_t surface_idx; // state for luma (0) and chroma (1) scalers @@ -179,9 +190,6 @@ struct gl_video { // true if scaler is currently upscaling bool upscaling; - // reinit_rendering must be called - bool need_reinit_rendering; - bool is_interpolated; struct mp_csp_equalizer video_eq; @@ -192,8 +200,11 @@ struct gl_video { struct mp_rect src_rect; // displayed part of the source video struct mp_rect dst_rect; // video rectangle on output window struct mp_osd_res osd_rect; // OSD size/margins - int vp_x, vp_y, vp_w, vp_h; // GL viewport - bool vp_vflipped; + int vp_w, vp_h; + + // temporary during rendering + struct src_tex pass_tex[4]; + bool use_indirect; int frames_rendered; @@ -203,8 +214,6 @@ struct gl_video { struct gl_hwdec *hwdec; bool hwdec_active; - - void *scratch; }; struct fmt_entry { @@ -323,6 +332,7 @@ const struct gl_video_opts gl_video_opts_def = { .sigmoid_center = 0.75, .sigmoid_slope = 6.5, .scalers = { "bilinear", "bilinear" }, + .dscaler = "bilinear", .scaler_params = {{NAN, NAN}, {NAN, NAN}}, .scaler_radius = {3, 3}, .alpha_mode = 2, @@ -431,10 +441,12 @@ const struct m_sub_options gl_video_conf = { }; static void uninit_rendering(struct gl_video *p); -static void delete_shaders(struct gl_video *p); +static void uninit_scaler(struct gl_video *p, int scaler_unit); static void check_gl_features(struct gl_video *p); static bool init_format(int fmt, struct gl_video *init); -static double get_scale_factor(struct gl_video *p); + +#define GLSL(x) gl_sc_add(p->sc, #x "\n"); +#define GLSLF(...) gl_sc_addf(p->sc, __VA_ARGS__) static const struct fmt_entry *find_tex_format(GL *gl, int bytes_per_comp, int n_channels) @@ -467,977 +479,34 @@ void gl_video_set_debug(struct gl_video *p, bool enable) gl_set_debug_logger(gl, enable ? p->log : NULL); } -// Draw a textured quad. -// x0, y0, x1, y1 = destination coordinates of the quad in pixels -// tx0, ty0, tx1, ty1 = source texture coordinates in pixels -// tex_w, tex_h = size of the texture in pixels -// flags = bits 0-1: rotate, bits 2: flip vertically -static void draw_quad(struct gl_video *p, - float x0, float y0, float x1, float y1, - float tx0, float ty0, float tx1, float ty1, - float tex_w, float tex_h, int flags) -{ - if (p->gl_target != GL_TEXTURE_2D) - tex_w = tex_h = 1.0f; - - if (flags & 4) { - float tmp = ty0; - ty0 = ty1; - ty1 = tmp; - } - - struct vertex va[4] = { - { {x0, y0}, {tx0 / tex_w, ty0 / tex_h} }, - { {x0, y1}, {tx0 / tex_w, ty1 / tex_h} }, - { {x1, y0}, {tx1 / tex_w, ty0 / tex_h} }, - { {x1, y1}, {tx1 / tex_w, ty1 / tex_h} }, - }; - - int rot = flags & 3; - while (rot--) { - static const int perm[4] = {1, 3, 0, 2}; - struct vertex vb[4]; - memcpy(vb, va, sizeof(vb)); - for (int n = 0; n < 4; n++) - memcpy(va[n].texcoord, vb[perm[n]].texcoord, sizeof(float[2])); - } - - gl_vao_draw_data(&p->vao, GL_TRIANGLE_STRIP, va, 4); - - debug_check_gl(p, "after rendering"); -} - -static void transpose3x3(float r[3][3]) -{ - MPSWAP(float, r[0][1], r[1][0]); - MPSWAP(float, r[0][2], r[2][0]); - MPSWAP(float, r[1][2], r[2][1]); -} - -static void update_uniforms(struct gl_video *p, GLuint program) -{ - GL *gl = p->gl; - GLint loc; - - if (program == 0) - return; - - gl->UseProgram(program); - - struct mp_csp_params cparams = MP_CSP_PARAMS_DEFAULTS; - cparams.gray = p->is_yuv && !p->is_packed_yuv && p->plane_count == 1; - cparams.input_bits = p->image_desc.component_bits; - cparams.texture_bits = (cparams.input_bits + 7) & ~7; - mp_csp_set_image_params(&cparams, &p->image_params); - mp_csp_copy_equalizer_values(&cparams, &p->video_eq); - if (p->image_desc.flags & MP_IMGFLAG_XYZ) { - cparams.colorspace = MP_CSP_XYZ; - cparams.input_bits = 8; - cparams.texture_bits = 8; - } - - loc = gl->GetUniformLocation(program, "transform"); - if (loc >= 0 && p->vp_w > 0 && p->vp_h > 0) { - float matrix[3][3]; - int vvp[2] = {p->vp_h, 0}; - if (p->vp_vflipped) - MPSWAP(int, vvp[0], vvp[1]); - gl_matrix_ortho2d(matrix, 0, p->vp_w, vvp[0], vvp[1]); - gl->UniformMatrix3fv(loc, 1, GL_FALSE, &matrix[0][0]); - } - - loc = gl->GetUniformLocation(program, "colormatrix"); - if (loc >= 0) { - struct mp_cmat m = {{{0}}}; - if (p->image_desc.flags & MP_IMGFLAG_XYZ) { - // Hard-coded as relative colorimetric for now, since this transforms - // from the source file's D55 material to whatever color space our - // projector/display lives in, which should be D55 for a proper - // home cinema setup either way. - mp_get_xyz2rgb_coeffs(&cparams, p->csp_src, - MP_INTENT_RELATIVE_COLORIMETRIC, &m); - } else { - mp_get_yuv2rgb_coeffs(&cparams, &m); - } - transpose3x3(m.m); // GLES2 can not transpose in glUniformMatrix3fv - gl->UniformMatrix3fv(loc, 1, GL_FALSE, &m.m[0][0]); - loc = gl->GetUniformLocation(program, "colormatrix_c"); - gl->Uniform3f(loc, m.c[0], m.c[1], m.c[2]); - } - - gl->Uniform1f(gl->GetUniformLocation(program, "input_gamma"), - p->input_gamma); - - gl->Uniform1f(gl->GetUniformLocation(program, "conv_gamma"), - p->conv_gamma); - - // Coefficients for the sigmoidal transform are taken from the - // formula here: http://www.imagemagick.org/Usage/color_mods/#sigmoidal - float sig_center = p->opts.sigmoid_center; - float sig_slope = p->opts.sigmoid_slope; - - // This function needs to go through (0,0) and (1,1) so we compute the - // values at 1 and 0, and then scale/shift them, respectively. - float sig_offset = 1.0/(1+expf(sig_slope * sig_center)); - float sig_scale = 1.0/(1+expf(sig_slope * (sig_center-1))) - sig_offset; - - gl->Uniform1f(gl->GetUniformLocation(program, "sig_center"), sig_center); - gl->Uniform1f(gl->GetUniformLocation(program, "sig_slope"), sig_slope); - gl->Uniform1f(gl->GetUniformLocation(program, "sig_scale"), sig_scale); - gl->Uniform1f(gl->GetUniformLocation(program, "sig_offset"), sig_offset); - - gl->Uniform1f(gl->GetUniformLocation(program, "inv_gamma"), - 1.0f / p->user_gamma); - - for (int n = 0; n < p->plane_count; n++) { - char textures_n[32]; - char textures_size_n[32]; - snprintf(textures_n, sizeof(textures_n), "texture%d", n); - snprintf(textures_size_n, sizeof(textures_size_n), "textures_size[%d]", n); - - gl->Uniform1i(gl->GetUniformLocation(program, textures_n), n); - if (p->gl_target == GL_TEXTURE_2D) { - gl->Uniform2f(gl->GetUniformLocation(program, textures_size_n), - p->image.planes[n].tex_w, p->image.planes[n].tex_h); - } else { - // Makes the pixel size calculation code think they are 1x1 - gl->Uniform2f(gl->GetUniformLocation(program, textures_size_n), 1, 1); - } - } - - loc = gl->GetUniformLocation(program, "chroma_div"); - if (loc >= 0) { - int xs = p->image_desc.chroma_xs; - int ys = p->image_desc.chroma_ys; - gl->Uniform2f(loc, 1.0 / (1 << xs), 1.0 / (1 << ys)); - } - - gl->Uniform2f(gl->GetUniformLocation(program, "chroma_fix"), - p->chroma_fix[0], p->chroma_fix[1]); - - loc = gl->GetUniformLocation(program, "chroma_center_offset"); - if (loc >= 0) { - int chr = p->opts.chroma_location; - if (!chr) - chr = p->image_params.chroma_location; - 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; - int c = p->gl_target == GL_TEXTURE_2D ? 1 : 0; - gl->Uniform2f(loc, o_x / FFMAX(p->image.planes[1].w * c, 1), - o_y / FFMAX(p->image.planes[1].h * c, 1)); - } - - gl->Uniform2f(gl->GetUniformLocation(program, "dither_size"), - p->dither_size, p->dither_size); - - gl->Uniform1i(gl->GetUniformLocation(program, "lut_3d"), TEXUNIT_3DLUT); - - loc = gl->GetUniformLocation(program, "cms_matrix"); - if (loc >= 0) { - float cms_matrix[3][3] = {{0}}; - // Hard-coded to relative colorimetric - for a BT.2020 3DLUT we expect - // the input to be actual BT.2020 and not something red- or blueshifted, - // and for sRGB monitors we most likely want relative scaling either way. - mp_get_cms_matrix(p->csp_src, p->csp_dest, MP_INTENT_RELATIVE_COLORIMETRIC, cms_matrix); - gl->UniformMatrix3fv(loc, 1, GL_TRUE, &cms_matrix[0][0]); - } - - for (int n = 0; n < 2; n++) { - const char *lut = p->scalers[n].lut_name; - if (lut) - gl->Uniform1i(gl->GetUniformLocation(program, lut), - TEXUNIT_SCALERS + n); - } - - gl->Uniform1i(gl->GetUniformLocation(program, "dither"), TEXUNIT_DITHER); - gl->Uniform1f(gl->GetUniformLocation(program, "dither_quantization"), - p->dither_quantization); - gl->Uniform1f(gl->GetUniformLocation(program, "dither_center"), - p->dither_center); - - float sparam1_l = p->opts.scaler_params[0][0]; - float sparam1_c = p->opts.scaler_params[1][0]; - gl->Uniform1f(gl->GetUniformLocation(program, "filter_param1_l"), - isnan(sparam1_l) ? 0.5f : sparam1_l); - gl->Uniform1f(gl->GetUniformLocation(program, "filter_param1_c"), - isnan(sparam1_c) ? 0.5f : sparam1_c); - - gl->Uniform3f(gl->GetUniformLocation(program, "translation"), 0, 0, 0); - - gl->UseProgram(0); - - debug_check_gl(p, "update_uniforms()"); -} - -static void update_all_uniforms(struct gl_video *p) -{ - for (int n = 0; n < SUBBITMAP_COUNT; n++) - update_uniforms(p, p->osd->programs[n]); - update_uniforms(p, p->indirect_program); - update_uniforms(p, p->scale_sep_program); - update_uniforms(p, p->final_program); - update_uniforms(p, p->inter_program); -} - -#define SECTION_HEADER "#!section " - -static char *get_section(void *talloc_ctx, struct bstr source, - const char *section) -{ - char *res = talloc_strdup(talloc_ctx, ""); - bool copy = false; - while (source.len) { - struct bstr line = bstr_strip_linebreaks(bstr_getline(source, &source)); - if (bstr_eatstart(&line, bstr0(SECTION_HEADER))) { - copy = bstrcmp0(line, section) == 0; - } else if (copy) { - res = talloc_asprintf_append_buffer(res, "%.*s\n", BSTR_P(line)); - } - } - return res; -} - -static char *t_concat(void *talloc_ctx, const char *s1, const char *s2) -{ - return talloc_asprintf(talloc_ctx, "%s%s", s1, s2); -} - -static GLuint create_shader(struct gl_video *p, GLenum type, const char *header, - const char *source) -{ - GL *gl = p->gl; - - void *tmp = talloc_new(NULL); - const char *full_source = t_concat(tmp, header, source); - - GLuint shader = gl->CreateShader(type); - gl->ShaderSource(shader, 1, &full_source, NULL); - gl->CompileShader(shader); - GLint status; - gl->GetShaderiv(shader, GL_COMPILE_STATUS, &status); - GLint log_length; - gl->GetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length); - - int pri = status ? (log_length > 1 ? MSGL_V : MSGL_DEBUG) : MSGL_ERR; - const char *typestr = type == GL_VERTEX_SHADER ? "vertex" : "fragment"; - if (mp_msg_test(p->log, pri)) { - MP_MSG(p, pri, "%s shader source:\n", typestr); - mp_log_source(p->log, pri, full_source); - } - if (log_length > 1) { - GLchar *logstr = talloc_zero_size(tmp, log_length + 1); - gl->GetShaderInfoLog(shader, log_length, NULL, logstr); - MP_MSG(p, pri, "%s shader compile log (status=%d):\n%s\n", - typestr, status, logstr); - } - - talloc_free(tmp); - - return shader; -} - -static void prog_create_shader(struct gl_video *p, GLuint program, GLenum type, - const char *header, const char *source) -{ - GL *gl = p->gl; - GLuint shader = create_shader(p, type, header, source); - gl->AttachShader(program, shader); - gl->DeleteShader(shader); -} - -static void link_shader(struct gl_video *p, GLuint program) -{ - GL *gl = p->gl; - gl->LinkProgram(program); - GLint status; - gl->GetProgramiv(program, GL_LINK_STATUS, &status); - GLint log_length; - gl->GetProgramiv(program, GL_INFO_LOG_LENGTH, &log_length); - - int pri = status ? (log_length > 1 ? MSGL_V : MSGL_DEBUG) : MSGL_ERR; - if (mp_msg_test(p->log, pri)) { - GLchar *logstr = talloc_zero_size(NULL, log_length + 1); - gl->GetProgramInfoLog(program, log_length, NULL, logstr); - MP_MSG(p, pri, "shader link log (status=%d): %s\n", status, logstr); - talloc_free(logstr); - } -} - -#define PRELUDE_END "// -- prelude end\n" - -static GLuint create_program(struct gl_video *p, const char *name, - const char *header, const char *vertex, - const char *frag, struct gl_vao *vao) -{ - GL *gl = p->gl; - MP_VERBOSE(p, "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(p->log, MSGL_V, real_header); - GLuint prog = gl->CreateProgram(); - prog_create_shader(p, prog, GL_VERTEX_SHADER, header, vertex); - prog_create_shader(p, prog, GL_FRAGMENT_SHADER, header, frag); - gl_vao_bind_attribs(vao, prog); - link_shader(p, prog); - return prog; -} - -static void shader_def(char **shader, const char *name, - const char *value) -{ - *shader = talloc_asprintf_append(*shader, "#define %s %s\n", name, value); -} - -static void shader_def_opt(char **shader, const char *name, bool b) -{ - if (b) - shader_def(shader, name, "1"); -} - -#define APPENDF(s_ptr, ...) \ - *(s_ptr) = talloc_asprintf_append(*(s_ptr), __VA_ARGS__) - -static void shader_setup_scaler(char **shader, struct scaler *scaler, int pass) -{ - int unit = scaler->index; - const char *target = unit == 0 ? "SAMPLE" : "SAMPLE_C"; - if (!scaler->kernel) { - APPENDF(shader, "#define %s(p0, p1, p2) " - "sample_%s(p0, p1, p2, filter_param1_%c)\n", - target, scaler->name, "lc"[unit]); - } else { - int size = scaler->kernel->size; - const char *lut_tex = scaler->lut_name; - char name[40]; - snprintf(name, sizeof(name), "sample_scaler%d", unit); - APPENDF(shader, "#define DEF_SCALER%d \\\n ", unit); - char lut_fn[40]; - if (scaler->kernel->polar) { - double radius = scaler->kernel->radius; - int bound = (int)ceil(radius); - // SAMPLE_CONVOLUTION_POLAR_R(NAME, R, LUT, WEIGHTS_FN, ANTIRING) - APPENDF(shader, "SAMPLE_CONVOLUTION_POLAR_R(%s, %f, %s, WEIGHTS%d, %f)\n", - name, radius, lut_tex, unit, scaler->antiring); - - // Pre-compute unrolled weights matrix - APPENDF(shader, "#define WEIGHTS%d(LUT) \\\n ", unit); - for (int y = 1-bound; y <= bound; y++) { - for (int x = 1-bound; x <= bound; x++) { - // Since we can't know the subpixel position in advance, - // assume a worst case scenario. - int yy = y > 0 ? y-1 : y; - int xx = x > 0 ? x-1 : x; - double d = sqrt(xx*xx + yy*yy); - - if (d < radius - 1) { - // Samples definitely inside the main ring - APPENDF(shader, "SAMPLE_POLAR_%s(LUT, %f, %d, %d) \\\n ", - // The center 4 coefficients are the primary - // contributors, used to clamp the result for - // anti-ringing - (x >= 0 && y >= 0 && x <= 1 && y <= 1) - ? "PRIMARY" : "HELPER", - radius, x, y); - } else if (d < radius) { - // Samples on the edge, these are potential values - APPENDF(shader, "SAMPLE_POLAR_POTENTIAL(LUT, %f, %d, %d) \\\n ", - radius, x, y); - } - } - } - APPENDF(shader, "\n"); - } else { - if (size == 2 || size == 6) { - snprintf(lut_fn, sizeof(lut_fn), "weights%d", size); - } else { - snprintf(lut_fn, sizeof(lut_fn), "weights_scaler%d", unit); - APPENDF(shader, "WEIGHTS_N(%s, %d) \\\n ", lut_fn, size); - } - if (pass != -1) { - // The direction/pass assignment is rather arbitrary, but fixed in - // other parts of the code (like FBO setup). - const char *direction = pass == 0 ? "0, 1" : "1, 0"; - // SAMPLE_CONVOLUTION_SEP_N(NAME, DIR, N, LUT, WEIGHTS_FUNC, ANTIRING) - APPENDF(shader, "SAMPLE_CONVOLUTION_SEP_N(%s, vec2(%s), %d, %s, %s, %f)\n", - name, direction, size, lut_tex, lut_fn, scaler->antiring); - } else { - // SAMPLE_CONVOLUTION_N(NAME, N, LUT, WEIGHTS_FUNC) - APPENDF(shader, "SAMPLE_CONVOLUTION_N(%s, %d, %s, %s)\n", - name, size, lut_tex, lut_fn); - } - } - APPENDF(shader, "#define %s %s\n", target, name); - } -} - -static void compile_shaders(struct gl_video *p) -{ - GL *gl = p->gl; - - debug_check_gl(p, "before shaders"); - - delete_shaders(p); - - void *tmp = talloc_new(NULL); - - struct bstr src = bstr0(vo_opengl_shaders); - char *vertex_shader = get_section(tmp, src, "vertex_all"); - char *shader_prelude = get_section(tmp, src, "prelude"); - char *s_video = get_section(tmp, src, "frag_video"); - - bool rg = gl->mpgl_caps & MPGL_CAP_TEX_RG; - bool tex1d = gl->mpgl_caps & MPGL_CAP_1D_TEX; - bool tex3d = gl->mpgl_caps & MPGL_CAP_3D_TEX; - bool arrays = gl->mpgl_caps & MPGL_CAP_1ST_CLASS_ARRAYS; - char *header = - talloc_asprintf(tmp, "#version %d%s\n" - "#define HAVE_RG %d\n" - "#define HAVE_1DTEX %d\n" - "#define HAVE_3DTEX %d\n" - "#define HAVE_ARRAYS %d\n" - "%s%s", - gl->glsl_version, gl->es >= 300 ? " es" : "", - rg, tex1d, tex3d, arrays, shader_prelude, PRELUDE_END); - - bool use_cms = p->opts.srgb || p->use_lut_3d; - // 3DLUT overrides sRGB - bool use_srgb = p->opts.srgb && !p->use_lut_3d; - - float input_gamma = 1.0; - float conv_gamma = 1.0; - - bool is_xyz = p->image_desc.flags & MP_IMGFLAG_XYZ; - if (is_xyz) { - input_gamma *= 2.6; - // Note that this results in linear light, so we make sure to enable - // use_linear_light for XYZ inputs as well. - } - - p->input_gamma = input_gamma; - p->conv_gamma = conv_gamma; - - bool use_input_gamma = p->input_gamma != 1.0; - bool use_conv_gamma = p->conv_gamma != 1.0; - bool use_const_luma = p->image_params.colorspace == MP_CSP_BT_2020_C; - enum mp_csp_trc gamma_fun = p->image_params.gamma; - - // If either color correction option (3dlut or srgb) is enabled, or if - // sigmoidal upscaling is requested, or if the source is linear XYZ, we - // always scale in linear light - bool use_linear_light = p->opts.linear_scaling || p->opts.sigmoid_upscaling - || use_cms || is_xyz; - - // The inverse of the above transformation is normally handled by - // the CMS cases, but if CMS is disabled we need to go back manually - bool use_inv_bt1886 = false; - if (use_linear_light && !use_cms) { - if (gamma_fun == MP_CSP_TRC_SRGB) { - use_srgb = true; - } else { - use_inv_bt1886 = true; - } - } - - // Optionally transform to sigmoidal color space if requested. - p->sigmoid_enabled = p->opts.sigmoid_upscaling; - bool use_sigmoid = p->sigmoid_enabled && p->upscaling; - - // Figure out the right color spaces we need to convert, if any - enum mp_csp_prim prim_src = p->image_params.primaries, prim_dest; - if (use_cms) { - // sRGB mode wants sRGB aka BT.709 primaries, but the 3DLUT is - // always built against BT.2020. - prim_dest = p->opts.srgb ? MP_CSP_PRIM_BT_709 : MP_CSP_PRIM_BT_2020; - } else { - // If no CMS is being done we just want to output stuff as-is, - // in the native colorspace of the source. - prim_dest = prim_src; - } - - // XYZ input has no defined input color space, so we can directly convert - // it to whatever output space we actually need. - if (p->image_desc.flags & MP_IMGFLAG_XYZ) - prim_src = prim_dest; - - // Set the colorspace primaries and figure out whether we need to perform - // an extra conversion. - p->csp_src = mp_get_csp_primaries(prim_src); - p->csp_dest = mp_get_csp_primaries(prim_dest); - - bool use_cms_matrix = prim_src != prim_dest; - - if (p->gl_target == GL_TEXTURE_RECTANGLE) { - shader_def(&header, "VIDEO_SAMPLER", "sampler2DRect"); - shader_def_opt(&header, "USE_RECTANGLE", true); - } else { - shader_def(&header, "VIDEO_SAMPLER", "sampler2D"); - } - - // Need to pass alpha through the whole chain. (Not needed for OSD shaders.) - if (p->opts.alpha_mode == 1) - shader_def_opt(&header, "USE_ALPHA", p->has_alpha); - - char *header_osd = talloc_strdup(tmp, header); - shader_def_opt(&header_osd, "USE_OSD_LINEAR_CONV_BT1886", - use_cms && gamma_fun == MP_CSP_TRC_BT_1886); - shader_def_opt(&header_osd, "USE_OSD_LINEAR_CONV_SRGB", - use_cms && gamma_fun == MP_CSP_TRC_SRGB); - shader_def_opt(&header_osd, "USE_OSD_CMS_MATRIX", use_cms_matrix); - shader_def_opt(&header_osd, "USE_OSD_3DLUT", p->use_lut_3d); - shader_def_opt(&header_osd, "USE_OSD_SRGB", use_cms && use_srgb); - - for (int n = 0; n < SUBBITMAP_COUNT; n++) { - const char *name = osd_shaders[n]; - if (name) { - char *s_osd = get_section(tmp, src, name); - p->osd_programs[n] = create_program(p, name, header_osd, - vertex_shader, s_osd, - &p->osd->vao); - } - } - - struct gl_vao *v = &p->vao; // VAO to use to draw primitives - - char *header_conv = talloc_strdup(tmp, ""); - char *header_final = talloc_strdup(tmp, ""); - char *header_inter = talloc_strdup(tmp, ""); - char *header_sep = NULL; - - if (p->image_desc.id == IMGFMT_NV12 || p->image_desc.id == IMGFMT_NV21) { - shader_def(&header_conv, "USE_CONV", "CONV_NV12"); - } else if (p->plane_count > 1) { - shader_def(&header_conv, "USE_CONV", "CONV_PLANAR"); - } - - if (p->color_swizzle[0]) - shader_def(&header_conv, "USE_COLOR_SWIZZLE", p->color_swizzle); - shader_def_opt(&header_conv, "USE_INPUT_GAMMA", use_input_gamma); - shader_def_opt(&header_conv, "USE_COLORMATRIX", !p->is_rgb); - shader_def_opt(&header_conv, "USE_CONV_GAMMA", use_conv_gamma); - shader_def_opt(&header_conv, "USE_CONST_LUMA", use_const_luma); - shader_def_opt(&header_conv, "USE_LINEAR_LIGHT_BT1886", - use_linear_light && gamma_fun == MP_CSP_TRC_BT_1886); - shader_def_opt(&header_conv, "USE_LINEAR_LIGHT_SRGB", - use_linear_light && gamma_fun == MP_CSP_TRC_SRGB); - shader_def_opt(&header_conv, "USE_SIGMOID", use_sigmoid); - if (p->opts.alpha_mode > 0 && p->has_alpha && p->plane_count > 3) - shader_def(&header_conv, "USE_ALPHA_PLANE", "3"); - if (p->opts.alpha_mode == 2 && p->has_alpha) - shader_def(&header_conv, "USE_ALPHA_BLEND", "1"); - shader_def_opt(&header_conv, "USE_CHROMA_FIX", - p->chroma_fix[0] != 1.0f || p->chroma_fix[1] != 1.0f); - - shader_def_opt(&header_final, "USE_SIGMOID_INV", use_sigmoid); - shader_def_opt(&header_final, "USE_INV_GAMMA", p->user_gamma_enabled); - shader_def_opt(&header_final, "USE_CMS_MATRIX", use_cms_matrix); - shader_def_opt(&header_final, "USE_3DLUT", p->use_lut_3d); - shader_def_opt(&header_final, "USE_SRGB", use_srgb); - shader_def_opt(&header_final, "USE_INV_BT1886", use_inv_bt1886); - 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->scalers[0].kernel && !p->scalers[0].kernel->polar) { - header_sep = talloc_strdup(tmp, ""); - shader_def_opt(&header_sep, "FIXED_SCALE", true); - shader_setup_scaler(&header_sep, &p->scalers[0], 0); - shader_setup_scaler(&header_inter, &p->scalers[0], 1); - } else { - shader_setup_scaler(&header_inter, &p->scalers[0], -1); - } - - bool use_interpolation = p->opts.smoothmotion; - - if (use_interpolation) { - shader_def_opt(&header_inter, "FIXED_SCALE", true); - shader_def_opt(&header_final, "USE_LINEAR_INTERPOLATION", 1); - } - - // The indirect pass is used to preprocess the image before scaling. - bool use_indirect = false; - - // Don't sample from input video textures before converting the input to - // its proper gamma. - if (use_input_gamma || use_conv_gamma || use_linear_light || use_const_luma) - use_indirect = true; - - // Trivial scalers are implemented directly and efficiently by the GPU. - // This only includes bilinear and nearest neighbour in OpenGL, but we - // don't support nearest neighbour upsampling. - bool trivial_scaling = strcmp(p->scalers[0].name, "bilinear") == 0 && - strcmp(p->scalers[1].name, "bilinear") == 0; - - // If the video is subsampled, chroma information needs to be pulled up to - // the input size before scaling can be done. Even for 4:4:4 or planar RGB - // this is also faster because it means the scalers can operate on all - // channels simultaneously. This is unnecessary for trivial scaling. - if (p->plane_count > 1 && !trivial_scaling) - use_indirect = true; - - if (p->image_desc.flags & MP_IMGFLAG_SUBSAMPLED) { - shader_setup_scaler(&header_conv, &p->scalers[1], -1); - } else { - // Force using the normal scaler on chroma. If the "indirect" stage is - // used, the actual scaling will happen in the next stage. - shader_def(&header_conv, "SAMPLE_C", - use_indirect ? "SAMPLE_TRIVIAL" : "SAMPLE"); - } - - if (use_indirect) { - // We don't use filtering for the Y-plane (luma), because it's never - // scaled in this scenario. - shader_def(&header_conv, "SAMPLE", "SAMPLE_TRIVIAL"); - shader_def_opt(&header_conv, "FIXED_SCALE", true); - header_conv = t_concat(tmp, header, header_conv); - p->indirect_program = - create_program(p, "indirect", header_conv, vertex_shader, s_video, v); - } else if (header_sep) { - header_sep = t_concat(tmp, header_sep, header_conv); - } else { - header_inter = t_concat(tmp, header_inter, header_conv); - } - - if (header_sep) { - header_sep = t_concat(tmp, header, header_sep); - p->scale_sep_program = - create_program(p, "scale_sep", header_sep, vertex_shader, s_video, v); - } - - if (use_interpolation) { - header_inter = t_concat(tmp, header, header_inter); - p->inter_program = - create_program(p, "inter", header_inter, vertex_shader, s_video, v); - } else { - header_final = t_concat(tmp, header_final, header_inter); - } - - header_final = t_concat(tmp, header, header_final); - p->final_program = - create_program(p, "final", header_final, vertex_shader, s_video, v); - - debug_check_gl(p, "shader compilation"); - - talloc_free(tmp); -} - -static void delete_program(GL *gl, GLuint *prog) -{ - gl->DeleteProgram(*prog); - *prog = 0; -} - -static void delete_shaders(struct gl_video *p) -{ - GL *gl = p->gl; - - for (int n = 0; n < SUBBITMAP_COUNT; n++) - delete_program(gl, &p->osd->programs[n]); - delete_program(gl, &p->indirect_program); - delete_program(gl, &p->scale_sep_program); - delete_program(gl, &p->final_program); - delete_program(gl, &p->inter_program); -} - -static void get_scale_factors(struct gl_video *p, double xy[2]) -{ - xy[0] = (p->dst_rect.x1 - p->dst_rect.x0) / - (double)(p->src_rect.x1 - p->src_rect.x0); - xy[1] = (p->dst_rect.y1 - p->dst_rect.y0) / - (double)(p->src_rect.y1 - p->src_rect.y0); -} - -static double get_scale_factor(struct gl_video *p) -{ - double xy[2]; - get_scale_factors(p, xy); - return FFMIN(xy[0], xy[1]); -} - -static void update_scale_factor(struct gl_video *p, struct scaler *scaler) -{ - double scale = 1.0; - double xy[2]; - get_scale_factors(p, xy); - double f = MPMIN(xy[0], xy[1]); - if (p->opts.fancy_downscaling && scaler->index == 0 && f < 1.0 && - fabs(xy[0] - f) < 0.01 && fabs(xy[1] - f) < 0.01) - { - MP_VERBOSE(p, "Using fancy-downscaling (scaler %d).\n", scaler->index); - scale = FFMAX(1.0, 1.0 / f); - } - scaler->insufficient = !mp_init_filter(scaler->kernel, filter_sizes, scale); -} - -static void init_scaler(struct gl_video *p, struct scaler *scaler) -{ - GL *gl = p->gl; - - assert(scaler->name); - - scaler->kernel = NULL; - scaler->insufficient = false; - - const struct filter_kernel *t_kernel = mp_find_filter_kernel(scaler->name); - if (!t_kernel) - return; - - scaler->kernel_storage = *t_kernel; - scaler->kernel = &scaler->kernel_storage; - - for (int n = 0; n < 2; n++) { - if (!isnan(p->opts.scaler_params[scaler->index][n])) - scaler->kernel->params[n] = p->opts.scaler_params[scaler->index][n]; - } - - scaler->antiring = p->opts.scaler_antiring[scaler->index]; - - if (scaler->kernel->radius < 0) - scaler->kernel->radius = p->opts.scaler_radius[scaler->index]; - - update_scale_factor(p, scaler); - - int size = scaler->kernel->size; - int elems_per_pixel = 4; - if (size == 1) { - elems_per_pixel = 1; - } else if (size == 2) { - elems_per_pixel = 2; - } else if (size == 6) { - elems_per_pixel = 3; - } - int width = size / elems_per_pixel; - assert(size == width * elems_per_pixel); - const struct fmt_entry *fmt = &gl_float16_formats[elems_per_pixel - 1]; - int target; - - if (scaler->kernel->polar) { - target = GL_TEXTURE_1D; - scaler->lut_name = scaler->index == 0 ? "lut_1d_l" : "lut_1d_c"; - } else { - target = GL_TEXTURE_2D; - scaler->lut_name = scaler->index == 0 ? "lut_2d_l" : "lut_2d_c"; - } - - gl->ActiveTexture(GL_TEXTURE0 + TEXUNIT_SCALERS + scaler->index); - - if (!scaler->gl_lut) - gl->GenTextures(1, &scaler->gl_lut); - - gl->BindTexture(target, scaler->gl_lut); - - float *weights = talloc_array(NULL, float, LOOKUP_TEXTURE_SIZE * size); |