diff options
Diffstat (limited to 'video/out/gl_video.c')
-rw-r--r-- | video/out/gl_video.c | 2443 |
1 files changed, 1132 insertions, 1311 deletions
diff --git a/video/out/gl_video.c b/video/out/gl_video.c index 5ddb6c5cad..5f64dcb1d6 100644 --- a/video/out/gl_video.c +++ b/video/out/gl_video.c @@ -41,15 +41,10 @@ #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 -// Texture units 0-3 are used by the video, with unit 0 for free use. +// Texture units 0-3 are used by the video, and for free use by the passes // Units 4-5 are used for scaler LUTs. #define TEXUNIT_SCALERS 4 #define TEXUNIT_3DLUT 6 @@ -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 @@ -116,10 +123,16 @@ struct scaler { struct fbosurface { struct fbotex fbotex; int64_t pts; - bool valid; }; -#define FBOSURFACES_MAX 2 +#define FBOSURFACES_MAX 4 + +struct src_tex { + GLuint gl_tex; + GLenum gl_target; + int tex_w, tex_h; + struct mp_rect_f src; +}; struct gl_video { GL *gl; @@ -131,13 +144,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 +158,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,41 +169,32 @@ 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; - bool user_gamma_enabled; // shader handles user_gamma - bool sigmoid_enabled; + bool user_gamma_enabled; 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; + size_t surface_now; + bool is_interpolated; // state for luma (0) and chroma (1) scalers struct scaler scalers[2]; - // 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; - // Source and destination color spaces for the CMS matrix - struct mp_csp_primaries csp_src, csp_dest; - 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 +204,6 @@ struct gl_video { struct gl_hwdec *hwdec; bool hwdec_active; - - void *scratch; }; struct fmt_entry { @@ -323,6 +322,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, @@ -356,7 +356,19 @@ const struct m_sub_options gl_video_conf = { .opts = (const m_option_t[]) { OPT_FLOATRANGE("gamma", gamma, 0, 0.1, 2.0), OPT_FLAG("gamma-auto", gamma_auto, 0), - OPT_FLAG("srgb", srgb, 0), + OPT_CHOICE("target-prim", target_prim, 0, + ({"auto", MP_CSP_PRIM_AUTO}, + {"bt601-525", MP_CSP_PRIM_BT_601_525}, + {"bt601-625", MP_CSP_PRIM_BT_601_625}, + {"bt709", MP_CSP_PRIM_BT_709}, + {"bt2020", MP_CSP_PRIM_BT_2020}, + {"bt470m", MP_CSP_PRIM_BT_470M})), + OPT_CHOICE("target-trc", target_trc, 0, + ({"auto", MP_CSP_TRC_AUTO}, + {"bt1886", MP_CSP_TRC_BT_1886}, + {"srgb", MP_CSP_TRC_SRGB}, + {"linear", MP_CSP_TRC_LINEAR}, + {"gamma22", MP_CSP_TRC_GAMMA22})), OPT_FLAG("npot", npot, 0), OPT_FLAG("pbo", pbo, 0), OPT_STRING_VALIDATE("scale", scalers[0], 0, validate_scaler_opt), @@ -423,6 +435,7 @@ const struct m_sub_options gl_video_conf = { OPT_REPLACED("cparam2", "cscale-param2"), OPT_REPLACED("cradius", "cscale-radius"), OPT_REPLACED("cantiring", "cscale-antiring"), + OPT_REPLACED("srgb", "target-prim=srgb:target-trc=srgb"), {0} }, @@ -431,10 +444,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,724 +482,400 @@ 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) +static void gl_video_reset_surfaces(struct gl_video *p) { - if (p->gl_target != GL_TEXTURE_2D) - tex_w = tex_h = 1.0f; + for (int i = 0; i < FBOSURFACES_MAX; i++) + p->surfaces[i].pts = 0; + p->surface_idx = 0; + p->surface_now = 0; +} - if (flags & 4) { - float tmp = ty0; - ty0 = ty1; - ty1 = tmp; - } +static size_t fbosurface_next(size_t id) +{ + return (id+1) % FBOSURFACES_MAX; +} - 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} }, - }; +static void recreate_osd(struct gl_video *p) +{ + if (p->osd) + mpgl_osd_destroy(p->osd); + p->osd = mpgl_osd_init(p->gl, p->log, p->osd_state); + mpgl_osd_set_options(p->osd, p->opts.pbo); +} - 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])); - } +static void reinit_rendering(struct gl_video *p) +{ + MP_VERBOSE(p, "Reinit rendering.\n"); - gl_vao_draw_data(&p->vao, GL_TRIANGLE_STRIP, va, 4); + debug_check_gl(p, "before scaler initialization"); - debug_check_gl(p, "after rendering"); -} + uninit_rendering(p); -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]); + recreate_osd(p); } -static void update_uniforms(struct gl_video *p, GLuint program) +static void uninit_rendering(struct gl_video *p) { GL *gl = p->gl; - GLint loc; - if (program == 0) - return; + for (int n = 0; n < 2; n++) + uninit_scaler(p, n); - gl->UseProgram(program); + gl->DeleteTextures(1, &p->dither_texture); + p->dither_texture = 0; - 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; - } + gl_video_reset_surfaces(p); +} - 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]); - } +void gl_video_set_lut3d(struct gl_video *p, struct lut3d *lut3d) +{ + GL *gl = p->gl; - 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); + if (!lut3d) { + if (p->use_lut_3d) { + p->use_lut_3d = false; + reinit_rendering(p); } - 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]); + return; } - gl->Uniform1f(gl->GetUniformLocation(program, "input_gamma"), - p->input_gamma); + if (!(gl->mpgl_caps & MPGL_CAP_3D_TEX)) + return; + + if (!p->lut_3d_texture) + gl->GenTextures(1, &p->lut_3d_texture); - gl->Uniform1f(gl->GetUniformLocation(program, "conv_gamma"), - p->conv_gamma); + gl->ActiveTexture(GL_TEXTURE0 + TEXUNIT_3DLUT); + gl->BindTexture(GL_TEXTURE_3D, p->lut_3d_texture); + gl->TexImage3D(GL_TEXTURE_3D, 0, GL_RGB16, lut3d->size[0], lut3d->size[1], + lut3d->size[2], 0, GL_RGB, GL_UNSIGNED_SHORT, lut3d->data); + gl->TexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + gl->TexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + gl->TexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + gl->TexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + gl->TexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + gl->ActiveTexture(GL_TEXTURE0); - // 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; + p->use_lut_3d = true; + check_gl_features(p); - // 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; + debug_check_gl(p, "after 3d lut creation"); - 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); + reinit_rendering(p); +} - gl->Uniform1f(gl->GetUniformLocation(program, "inv_gamma"), - 1.0f / p->user_gamma); +static void pass_load_fbotex(struct gl_video *p, struct fbotex *src_fbo, int id, + int w, int h) +{ + p->pass_tex[id] = (struct src_tex){ + .gl_tex = src_fbo->texture, + .gl_target = GL_TEXTURE_2D, + .tex_w = src_fbo->tex_w, + .tex_h = src_fbo->tex_h, + .src = {0, 0, w, h}, + }; +} - 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); - } - } +static void pass_set_image_textures(struct gl_video *p, struct video_image *vimg, + float chroma[3][2]) +{ + GLuint imgtex[4] = {0}; - 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)); - } + assert(vimg->mpi); - gl->Uniform2f(gl->GetUniformLocation(program, "chroma_fix"), - p->chroma_fix[0], p->chroma_fix[1]); + float ls_w = 1.0 / (1 << p->image_desc.chroma_xs); + float ls_h = 1.0 / (1 << p->image_desc.chroma_ys); - 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 chroma_loc = p->opts.chroma_location; + if (!chroma_loc) + chroma_loc = p->image_params.chroma_location; + if (chroma_loc != MP_CHROMA_CENTER) { int cx, cy; - mp_get_chroma_location(chr, &cx, &cy); + mp_get_chroma_location(chroma_loc, &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)); + chroma[2][0] = ls_w < 1 ? ls_w * -cx / 2 : 0; + chroma[2][1] = ls_h < 1 ? ls_h * -cy / 2 : 0; + } else { + chroma[2][0] = chroma[2][1] = 0.0; } - gl->Uniform2f(gl->GetUniformLocation(program, "dither_size"), - p->dither_size, p->dither_size); - - gl->Uniform1i(gl->GetUniformLocation(program, "lut_3d"), TEXUNIT_3DLUT); + // Make sure luma/chroma sizes are aligned. + // Example: For 4:2:0 with size 3x3, the subsampled chroma plane is 2x2 + // so luma (3,3) has to align with chroma (2,2). + chroma[0][0] = ls_w * (float)vimg->planes[0].tex_w + / vimg->planes[1].tex_w; + chroma[1][1] = ls_h * (float)vimg->planes[0].tex_h + / vimg->planes[1].tex_h; + chroma[0][1] = chroma[1][0] = 0.0; // No rotation etc. - 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); + if (p->hwdec_active) { + p->hwdec->driver->map_image(p->hwdec, vimg->mpi, imgtex); + } else { + for (int n = 0; n < p->plane_count; n++) + imgtex[n] = vimg->planes[n].gl_texture; } - 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)); - } + for (int n = 0; n < 4; n++) { + struct texplane *t = &vimg->planes[n]; + p->pass_tex[n] = (struct src_tex){ + .gl_tex = imgtex[n], + .gl_target = t->gl_target, + .tex_w = t->tex_w, + .tex_h = t->tex_h, + .src = {0, 0, t->w, t->h}, + }; } - return res; } -static char *t_concat(void *talloc_ctx, const char *s1, const char *s2) +static int align_pow2(int s) { - return talloc_asprintf(talloc_ctx, "%s%s", s1, s2); + int r = 1; + while (r < s) + r *= 2; + return r; } -static GLuint create_shader(struct gl_video *p, GLenum type, const char *header, - const char *source) +static void init_video(struct gl_video *p) { GL *gl = p->gl; - void *tmp = talloc_new(NULL); - const char *full_source = t_concat(tmp, header, source); + check_gl_features(p); - 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); + init_format(p->image_params.imgfmt, p); + p->gl_target = p->opts.use_rectangle ? GL_TEXTURE_RECTANGLE : GL_TEXTURE_2D; - 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); + 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"); + init_format(p->image_params.imgfmt, p); + p->gl_target = p->hwdec->gl_texture_target; } - talloc_free(tmp); - - return shader; -} + mp_image_params_guess_csp(&p->image_params); -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); -} + p->image_w = p->image_params.w; + p->image_h = p->image_params.h; -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 eq_caps = MP_CSP_EQ_CAPS_GAMMA; + if (p->is_yuv && p->image_params.colorspace != MP_CSP_BT_2020_C) + eq_caps |= MP_CSP_EQ_CAPS_COLORMATRIX; + if (p->image_desc.flags & MP_IMGFLAG_XYZ) + eq_caps |= MP_CSP_EQ_CAPS_BRIGHTNESS; + p->video_eq.capabilities = eq_caps; - 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); - } -} + debug_check_gl(p, "before video texture creation"); -#define PRELUDE_END "// -- prelude end\n" + struct video_image *vimg = &p->image; -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; -} + for (int n = 0; n < p->plane_count; n++) { + struct texplane *plane = &vimg->planes[n]; -static void shader_def(char **shader, const char *name, - const char *value) -{ - *shader = talloc_asprintf_append(*shader, "#define %s %s\n", name, value); -} + plane->gl_target = p->gl_target; -static void shader_def_opt(char **shader, const char *name, bool b) -{ - if (b) - shader_def(shader, name, "1"); -} + plane->w = mp_chroma_div_up(p->image_w, p->image_desc.xs[n]); + plane->h = mp_chroma_div_up(p->image_h, p->image_desc.ys[n]); -#define APPENDF(s_ptr, ...) \ - *(s_ptr) = talloc_asprintf_append(*(s_ptr), __VA_ARGS__) + plane->tex_w = plane->w; + plane->tex_h = plane->h; -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); + if (!p->hwdec_active) { + if (!p->opts.npot) { + plane->tex_w = align_pow2(plane->tex_w); + plane->tex_h = align_pow2(plane->tex_h); } - } - APPENDF(shader, "#define %s %s\n", target, name); - } -} -static void compile_shaders(struct gl_video *p) -{ - GL *gl = p->gl; + gl->ActiveTexture(GL_TEXTURE0 + n); + gl->GenTextures(1, &plane->gl_texture); + gl->BindTexture(p->gl_target, plane->gl_texture); - 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; - } - } + gl->TexImage2D(p->gl_target, 0, plane->gl_internal_format, + plane->tex_w, plane->tex_h, 0, + plane->gl_format, plane->gl_type, NULL); - // Optionally transform to sigmoidal color space if requested. - p->sigmoid_enabled = p->opts.sigmoid_upscaling; - bool use_sigmoid = p->sigmoid_enabled && p->upscaling; + gl->TexParameteri(p->gl_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + gl->TexParameteri(p->gl_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + 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); + } - // 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; + MP_VERBOSE(p, "Texture for plane %d: %dx%d\n", + n, plane->tex_w, plane->tex_h); } + gl->ActiveTexture(GL_TEXTURE0); - // 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; + debug_check_gl(p, "after video texture creation"); - // 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); + reinit_rendering(p); +} - bool use_cms_matrix = prim_src != prim_dest; +static void uninit_video(struct gl_video *p) +{ + GL *gl = p->gl; - 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); - } - } + uninit_rendering(p); - struct gl_vao *v = &p->vao; // VAO to use to draw primitives + struct video_image *vimg = &p->image; - char *header_conv |