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.c1925
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);