diff options
author | wm4 <wm4@nowhere> | 2013-03-01 21:19:20 +0100 |
---|---|---|
committer | wm4 <wm4@nowhere> | 2013-03-28 21:46:17 +0100 |
commit | 6ef06aa145067b816aaf3b941aba11e36bfca545 (patch) | |
tree | 116401ac1bc8fd443e936cd8ae5e8f1424afdd51 /video/out/vo_opengl.c | |
parent | 16e951c2cdfa6710f0d5cc77b513e0f2a5f1377d (diff) | |
download | mpv-6ef06aa145067b816aaf3b941aba11e36bfca545.tar.bz2 mpv-6ef06aa145067b816aaf3b941aba11e36bfca545.tar.xz |
vo_opengl: split into multiple files, convert to new option API
gl_video.c contains all rendering code, gl_lcms.c the .icc loader and
creation of 3D LUT (and all LittleCMS specific code). vo_opengl.c is
reduced to interfacing between the various parts.
Diffstat (limited to 'video/out/vo_opengl.c')
-rw-r--r-- | video/out/vo_opengl.c | 2074 |
1 files changed, 117 insertions, 1957 deletions
diff --git a/video/out/vo_opengl.c b/video/out/vo_opengl.c index 4a9c2e4f39..6c0901eae6 100644 --- a/video/out/vo_opengl.c +++ b/video/out/vo_opengl.c @@ -27,1185 +27,62 @@ #include <math.h> #include <stdbool.h> #include <assert.h> -#include "config.h" #include <libavutil/common.h> -#ifdef CONFIG_LCMS2 -#include <lcms2.h> -#include "stream/stream.h" -#endif +#include "config.h" #include "talloc.h" #include "core/mp_common.h" #include "core/bstr.h" #include "core/mp_msg.h" -#include "core/subopt-helper.h" +#include "core/m_config.h" #include "vo.h" #include "video/vfcap.h" #include "video/mp_image.h" #include "sub/sub.h" -#include "bitmap_packer.h" #include "gl_common.h" #include "gl_osd.h" #include "filter_kernels.h" #include "video/memcpy_pic.h" - -static const char vo_opengl_shaders[] = -// Generated from libvo/vo_opengl_shaders.glsl -#include "video/out/vo_opengl_shaders.h" -; - -// Pixel width of 1D lookup textures. -#define LOOKUP_TEXTURE_SIZE 256 - -// Texture units 0-2 are used by the video, with unit 0 for free use. -// Units 3-4 are used for scaler LUTs. -#define TEXUNIT_SCALERS 3 -#define TEXUNIT_3DLUT 5 -#define TEXUNIT_DITHER 6 - -// lscale/cscale arguments that map directly to shader filter routines. -// Note that the convolution filters are not included in this list. -static const char *fixed_scale_filters[] = { - "bilinear", - "bicubic_fast", - "sharpen3", - "sharpen5", - NULL -}; - -struct lut_tex_format { - int pixels; - GLint internal_format; - GLenum format; -}; - -// Indexed with filter_kernel->size. -// This must match the weightsN functions in the shader. -// Each entry uses (size+3)/4 pixels per LUT entry, and size/pixels components -// per pixel. -struct lut_tex_format lut_tex_formats[] = { - [2] = {1, GL_RG16F, GL_RG}, - [4] = {1, GL_RGBA16F, GL_RGBA}, - [6] = {2, GL_RGB16F, GL_RGB}, - [8] = {2, GL_RGBA16F, GL_RGBA}, - [12] = {3, GL_RGBA16F, GL_RGBA}, - [16] = {4, GL_RGBA16F, GL_RGBA}, -}; - -// must be sorted, and terminated with 0 -static const int filter_sizes[] = {2, 4, 6, 8, 12, 16, 0}; - -struct vertex { - float position[2]; - uint8_t color[4]; - float texcoord[2]; -}; - -#define VERTEX_ATTRIB_POSITION 0 -#define VERTEX_ATTRIB_COLOR 1 -#define VERTEX_ATTRIB_TEXCOORD 2 - -// 2 triangles primitives per quad = 6 vertices per quad -// (GL_QUAD is deprecated, strips can't be used with OSD image lists) -#define VERTICES_PER_QUAD 6 - -struct texplane { - int shift_x, shift_y; - GLuint gl_texture; - int gl_buffer; - int buffer_size; - void *buffer_ptr; -}; - -struct scaler { - int index; - const char *name; - float params[2]; - struct filter_kernel *kernel; - GLuint gl_lut; - const char *lut_name; - - // kernel points here - struct filter_kernel kernel_storage; -}; - -struct fbotex { - GLuint fbo; - GLuint texture; - int tex_w, tex_h; // size of .texture - int vp_w, vp_h; // viewport of fbo / used part of the texture -}; +#include "gl_video.h" +#include "gl_lcms.h" struct gl_priv { struct vo *vo; MPGLContext *glctx; GL *gl; - int use_indirect; - int use_gamma; - int use_srgb; - int use_scale_sep; - int use_fancy_downscaling; - int use_lut_3d; - int use_npot; - int use_pbo; + struct gl_video *renderer; + + // Options + struct gl_video_opts *renderer_opts; + struct mp_icc_opts *icc_opts; int use_glFinish; int use_gl_debug; int allow_sw; - - int dither_depth; int swap_interval; - GLint fbo_format; - int stereo_mode; - - struct gl_priv *defaults; - struct gl_priv *orig_cmdline; - - GLuint vertex_buffer; - GLuint vao; - - GLuint osd_programs[SUBBITMAP_COUNT]; - GLuint indirect_program, scale_sep_program, final_program; - - struct mpgl_osd *osd; - - GLuint lut_3d_texture; - int lut_3d_w, lut_3d_h, lut_3d_d; - void *lut_3d_data; - - GLuint dither_texture; - float dither_quantization; - float dither_multiply; - int dither_size; - - uint32_t image_width; - uint32_t image_height; - uint32_t image_format; - int texture_width; - int texture_height; - - bool is_yuv; - bool is_linear_rgb; - - // per pixel (full pixel when packed, each component when planar) - int plane_bytes; - int plane_bits; - - GLint gl_internal_format; - GLenum gl_format; - GLenum gl_type; - - int plane_count; - struct texplane planes[3]; - - struct fbotex indirect_fbo; // RGB target - struct fbotex scale_sep_fbo; // first pass when doing 2 pass scaling - - // state for luma (0) and chroma (1) scalers - struct scaler scalers[2]; - // luma scaler parameters (the same are used for chroma) - float scaler_params[2]; + char *backend; - struct mp_csp_details colorspace; - struct mp_csp_equalizer video_eq; - - int mpi_flipped; int vo_flipped; - 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 - int frames_rendered; - - void *scratch; -}; - -struct fmt_entry { - int mp_format; - GLint internal_format; - GLenum format; - GLenum type; -}; - -static const struct fmt_entry mp_to_gl_formats[] = { - {IMGFMT_RGB48, GL_RGB16, GL_RGB, GL_UNSIGNED_SHORT}, - {IMGFMT_RGB24, GL_RGB, GL_RGB, GL_UNSIGNED_BYTE}, - {IMGFMT_RGBA, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE}, - {IMGFMT_RGB15, GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV}, - {IMGFMT_RGB16, GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5_REV}, - {IMGFMT_BGR15, GL_RGBA, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV}, - {IMGFMT_BGR16, GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5}, - {IMGFMT_BGR24, GL_RGB, GL_BGR, GL_UNSIGNED_BYTE}, - {IMGFMT_BGRA, GL_RGBA, GL_BGRA, GL_UNSIGNED_BYTE}, - {0}, -}; - -static const char *osd_shaders[SUBBITMAP_COUNT] = { - [SUBBITMAP_LIBASS] = "frag_osd_libass", - [SUBBITMAP_RGBA] = "frag_osd_rgba", }; - -static const char help_text[]; - -static void uninit_rendering(struct gl_priv *p); -static void delete_shaders(struct gl_priv *p); -static bool reparse_cmdline(struct gl_priv *p, char *arg); - - -static void default_tex_params(struct GL *gl, GLenum target, GLint filter) -{ - gl->TexParameteri(target, GL_TEXTURE_MIN_FILTER, filter); - gl->TexParameteri(target, GL_TEXTURE_MAG_FILTER, filter); - gl->TexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - gl->TexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); -} - -static void debug_check_gl(struct gl_priv *p, const char *msg) -{ - if (p->use_gl_debug || p->frames_rendered < 5) - glCheckError(p->gl, msg); -} - -static void tex_size(struct gl_priv *p, int w, int h, int *texw, int *texh) -{ - if (p->use_npot) { - *texw = w; - *texh = h; - } else { - *texw = 32; - while (*texw < w) - *texw *= 2; - *texh = 32; - while (*texh < h) - *texh *= 2; - } -} - -static void draw_triangles(struct gl_priv *p, struct vertex *vb, int vert_count) -{ - GL *gl = p->gl; - - assert(vert_count % 3 == 0); - - gl->BindBuffer(GL_ARRAY_BUFFER, p->vertex_buffer); - gl->BufferData(GL_ARRAY_BUFFER, vert_count * sizeof(struct vertex), vb, - GL_DYNAMIC_DRAW); - gl->BindBuffer(GL_ARRAY_BUFFER, 0); - - if (gl->BindVertexArray) - gl->BindVertexArray(p->vao); - - gl->DrawArrays(GL_TRIANGLES, 0, vert_count); - - if (gl->BindVertexArray) - gl->BindVertexArray(0); - - debug_check_gl(p, "after rendering"); -} - -// Write a textured quad to a vertex array. -// va = destination vertex array, VERTICES_PER_QUAD entries will be overwritten -// x0, y0, x1, y1 = destination coordinates of the quad -// tx0, ty0, tx1, ty1 = source texture coordinates (usually in pixels) -// texture_w, texture_h = size of the texture, or an inverse factor -// color = optional color for all vertices, NULL for opaque white -// flip = flip vertically -static void write_quad(struct vertex *va, - float x0, float y0, float x1, float y1, - float tx0, float ty0, float tx1, float ty1, - float texture_w, float texture_h, - const uint8_t color[4], bool flip) -{ - static const uint8_t white[4] = { 255, 255, 255, 255 }; - - if (!color) - color = white; - - tx0 /= texture_w; - ty0 /= texture_h; - tx1 /= texture_w; - ty1 /= texture_h; - - if (flip) { - float tmp = ty0; - ty0 = ty1; - ty1 = tmp; - } - -#define COLOR_INIT {color[0], color[1], color[2], color[3]} - va[0] = (struct vertex) { {x0, y0}, COLOR_INIT, {tx0, ty0} }; - va[1] = (struct vertex) { {x0, y1}, COLOR_INIT, {tx0, ty1} }; - va[2] = (struct vertex) { {x1, y0}, COLOR_INIT, {tx1, ty0} }; - va[3] = (struct vertex) { {x1, y1}, COLOR_INIT, {tx1, ty1} }; - va[4] = va[2]; - va[5] = va[1]; -#undef COLOR_INIT -} - -static bool fbotex_init(struct gl_priv *p, struct fbotex *fbo, int w, int h) -{ - GL *gl = p->gl; - bool res = true; - - assert(gl->mpgl_caps & MPGL_CAP_FB); - assert(!fbo->fbo); - assert(!fbo->texture); - - tex_size(p, w, h, &fbo->tex_w, &fbo->tex_h); - - fbo->vp_w = w; - fbo->vp_h = h; - - mp_msg(MSGT_VO, MSGL_V, "[gl] Create FBO: %dx%d\n", fbo->tex_w, fbo->tex_h); - - gl->GenFramebuffers(1, &fbo->fbo); - gl->GenTextures(1, &fbo->texture); - gl->BindTexture(GL_TEXTURE_2D, fbo->texture); - gl->TexImage2D(GL_TEXTURE_2D, 0, p->fbo_format, fbo->tex_w, fbo->tex_h, 0, - GL_RGB, GL_UNSIGNED_BYTE, NULL); - default_tex_params(gl, GL_TEXTURE_2D, GL_LINEAR); - gl->BindFramebuffer(GL_FRAMEBUFFER, fbo->fbo); - gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D, fbo->texture, 0); - - if (gl->CheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { - mp_msg(MSGT_VO, MSGL_ERR, "[gl] Error: framebuffer completeness " - "check failed!\n"); - res = false; - } - - gl->BindFramebuffer(GL_FRAMEBUFFER, 0); - - debug_check_gl(p, "after creating framebuffer & associated texture"); - - return res; -} - -static void fbotex_uninit(struct gl_priv *p, struct fbotex *fbo) -{ - GL *gl = p->gl; - - if (gl->mpgl_caps & MPGL_CAP_FB) { - gl->DeleteFramebuffers(1, &fbo->fbo); - gl->DeleteTextures(1, &fbo->texture); - *fbo = (struct fbotex) {0}; - } -} - -static void matrix_ortho2d(float m[3][3], float x0, float x1, - float y0, float y1) -{ - memset(m, 0, 9 * sizeof(float)); - m[0][0] = 2.0f / (x1 - x0); - m[1][1] = 2.0f / (y1 - y0); - m[2][0] = -(x1 + x0) / (x1 - x0); - m[2][1] = -(y1 + y0) / (y1 - y0); - m[2][2] = 1.0f; -} - -static void update_uniforms(struct gl_priv *p, GLuint program) -{ - GL *gl = p->gl; - GLint loc; - - if (program == 0) - return; - - gl->UseProgram(program); - - struct mp_csp_params cparams = { - .colorspace = p->colorspace, - .input_bits = p->plane_bits, - .texture_bits = (p->plane_bits + 7) & ~7, - }; - mp_csp_copy_equalizer_values(&cparams, &p->video_eq); - - loc = gl->GetUniformLocation(program, "transform"); - if (loc >= 0) { - float matrix[3][3]; - matrix_ortho2d(matrix, 0, p->vp_w, p->vp_h, 0); - gl->UniformMatrix3fv(loc, 1, GL_FALSE, &matrix[0][0]); - } - - loc = gl->GetUniformLocation(program, "colormatrix"); - if (loc >= 0) { - float yuv2rgb[3][4] = {{0}}; - if (p->is_yuv) - mp_get_yuv2rgb_coeffs(&cparams, yuv2rgb); - gl->UniformMatrix4x3fv(loc, 1, GL_TRUE, &yuv2rgb[0][0]); - } - - gl->Uniform3f(gl->GetUniformLocation(program, "inv_gamma"), - 1.0 / cparams.rgamma, - 1.0 / cparams.ggamma, - 1.0 / cparams.bgamma); - - for (int n = 0; n < p->plane_count; n++) { - char textures_n[32]; - char textures_size_n[32]; - snprintf(textures_n, sizeof(textures_n), "textures[%d]", n); - snprintf(textures_size_n, sizeof(textures_size_n), "textures_size[%d]", n); - - gl->Uniform1i(gl->GetUniformLocation(program, textures_n), n); - gl->Uniform2f(gl->GetUniformLocation(program, textures_size_n), - p->texture_width >> p->planes[n].shift_x, - p->texture_height >> p->planes[n].shift_y); - } - - gl->Uniform2f(gl->GetUniformLocation(program, "dither_size"), - p->dither_size, p->dither_size); - - gl->Uniform1i(gl->GetUniformLocation(program, "lut_3d"), TEXUNIT_3DLUT); - - 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_multiply"), - p->dither_multiply); - - float sparam1 = p->scaler_params[0]; - gl->Uniform1f(gl->GetUniformLocation(program, "filter_param1"), - isnan(sparam1) ? 0.5f : sparam1); - - gl->UseProgram(0); - - debug_check_gl(p, "update_uniforms()"); -} - -static void update_all_uniforms(struct gl_priv *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); -} - -#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(GL *gl, GLenum type, const char *header, - const char *source) -{ - 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_DBG2) : MSGL_ERR; - const char *typestr = type == GL_VERTEX_SHADER ? "vertex" : "fragment"; - if (mp_msg_test(MSGT_VO, pri)) { - mp_msg(MSGT_VO, pri, "[gl] %s shader source:\n", typestr); - mp_log_source(MSGT_VO, pri, full_source); - } - if (log_length > 1) { - GLchar *log = talloc_zero_size(tmp, log_length + 1); - gl->GetShaderInfoLog(shader, log_length, NULL, log); - mp_msg(MSGT_VO, pri, "[gl] %s shader compile log (status=%d):\n%s\n", - typestr, status, log); - } - - talloc_free(tmp); - - return shader; -} - -static void prog_create_shader(GL *gl, GLuint program, GLenum type, - const char *header, const char *source) -{ - GLuint shader = create_shader(gl, type, header, source); - gl->AttachShader(program, shader); - gl->DeleteShader(shader); -} - -static void link_shader(GL *gl, GLuint program) -{ - 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_DBG2) : MSGL_ERR; - if (mp_msg_test(MSGT_VO, pri)) { - GLchar *log = talloc_zero_size(NULL, log_length + 1); - gl->GetProgramInfoLog(program, log_length, NULL, log); - mp_msg(MSGT_VO, pri, "[gl] shader link log (status=%d): %s\n", - status, log); - talloc_free(log); - } -} - -static void bind_attrib_locs(GL *gl, GLuint program) -{ - gl->BindAttribLocation(program, VERTEX_ATTRIB_POSITION, "vertex_position"); - gl->BindAttribLocation(program, VERTEX_ATTRIB_COLOR, "vertex_color"); - gl->BindAttribLocation(program, VERTEX_ATTRIB_TEXCOORD, "vertex_texcoord"); -} - -static GLuint create_program(GL *gl, const char *name, const char *header, - const char *vertex, const char *frag) -{ - mp_msg(MSGT_VO, MSGL_V, "[gl] compiling shader program '%s'\n", name); - mp_msg(MSGT_VO, MSGL_V, "[gl] header:\n"); - mp_log_source(MSGT_VO, MSGL_V, header); - GLuint prog = gl->CreateProgram(); - prog_create_shader(gl, prog, GL_VERTEX_SHADER, header, vertex); - prog_create_shader(gl, prog, GL_FRAGMENT_SHADER, header, frag); - bind_attrib_locs(gl, prog); - link_shader(gl, 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"); -} - -static void shader_setup_scaler(char **shader, struct scaler *scaler, int pass) -{ - const char *target = scaler->index == 0 ? "SAMPLE_L" : "SAMPLE_C"; - if (!scaler->kernel) { - *shader = talloc_asprintf_append(*shader, "#define %s sample_%s\n", - target, scaler->name); - } else { - int size = scaler->kernel->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"; - *shader = talloc_asprintf_append(*shader, "#define %s(p0, p1, p2) " - "sample_convolution_sep%d(vec2(%s), %s, p0, p1, p2)\n", - target, size, direction, scaler->lut_name); - } else { - *shader = talloc_asprintf_append(*shader, "#define %s(p0, p1, p2) " - "sample_convolution%d(%s, p0, p1, p2)\n", - target, size, scaler->lut_name); - } - } -} - -// return false if RGB or 4:4:4 YUV -static bool input_is_subsampled(struct gl_priv *p) -{ - for (int i = 0; i < p->plane_count; i++) - if (p->planes[i].shift_x || p->planes[i].shift_y) - return true; - return false; -} - -static void compile_shaders(struct gl_priv *p) -{ - GL *gl = p->gl; - - 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"); - - char *header = talloc_asprintf(tmp, "#version %d\n%s", gl->glsl_version, - shader_prelude); - - char *header_osd = talloc_strdup(tmp, header); - shader_def_opt(&header_osd, "USE_OSD_LINEAR_CONV", p->use_srgb && - !p->use_lut_3d); - shader_def_opt(&header_osd, "USE_OSD_3DLUT", p->use_lut_3d); - shader_def_opt(&header_osd, "USE_OSD_SRGB", p->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(gl, name, header_osd, vertex_shader, s_osd); - } - } - - char *header_conv = talloc_strdup(tmp, ""); - char *header_final = talloc_strdup(tmp, ""); - char *header_sep = NULL; - - bool convert_input_to_linear = !p->is_linear_rgb - && (p->use_srgb || p->use_lut_3d); - - shader_def_opt(&header_conv, "USE_PLANAR", p->plane_count > 1); - shader_def_opt(&header_conv, "USE_GBRP", p->image_format == IMGFMT_GBRP); - shader_def_opt(&header_conv, "USE_YGRAY", p->is_yuv && p->plane_count == 1); - shader_def_opt(&header_conv, "USE_COLORMATRIX", p->is_yuv); - shader_def_opt(&header_conv, "USE_LINEAR_CONV", convert_input_to_linear); - - shader_def_opt(&header_final, "USE_LINEAR_CONV_INV", p->use_lut_3d); - shader_def_opt(&header_final, "USE_GAMMA_POW", p->use_gamma); - shader_def_opt(&header_final, "USE_3DLUT", p->use_lut_3d); - shader_def_opt(&header_final, "USE_SRGB", p->use_srgb); - shader_def_opt(&header_final, "USE_DITHER", p->dither_texture != 0); - - if (p->use_scale_sep && p->scalers[0].kernel) { - 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_final, &p->scalers[0], 1); - } else { - shader_setup_scaler(&header_final, &p->scalers[0], -1); - } - - // We want to do scaling in linear light. Scaling is closely connected to - // texture sampling due to how the shader is structured (or if GL bilinear - // scaling is used). The purpose of the "indirect" pass is to convert the - // input video to linear RGB. - // Another purpose is reducing input to a single texture for scaling. - bool use_indirect = p->use_indirect; - - // Don't sample from input video textures before converting the input to - // linear light. (Unneeded when sRGB textures are used.) - if (convert_input_to_linear) - use_indirect = true; - - // It doesn't make sense to scale the chroma with cscale in the 1. scale - // step and with lscale in the 2. step. If the chroma is subsampled, a - // convolution filter wouldn't even work entirely correctly, because the - // luma scaler would sample two texels instead of one per tap for chroma. - // Also, even with 4:4:4 YUV or planar RGB, the indirection might be faster, - // because the shader can't use one scaler for sampling from 3 textures. It - // has to fetch the coefficients for each texture separately, even though - // they're the same (this is not an inherent restriction, but would require - // to restructure the shader). - if (header_sep && p->plane_count > 1) - use_indirect = true; - - if (input_is_subsampled(p)) { - shader_setup_scaler(&header_conv, &p->scalers[1], -1); - } else { - // Force using the luma 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_bilinear" : "SAMPLE_L"); - } - - 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_L", "sample_bilinear"); - shader_def_opt(&header_conv, "FIXED_SCALE", true); - header_conv = t_concat(tmp, header, header_conv); - p->indirect_program = - create_program(gl, "indirect", header_conv, vertex_shader, s_video); - } else if (header_sep) { - header_sep = t_concat(tmp, header_sep, header_conv); - } else { - header_final = t_concat(tmp, header_final, header_conv); - } - - if (header_sep) { - header_sep = t_concat(tmp, header, header_sep); - p->scale_sep_program = - create_program(gl, "scale_sep", header_sep, vertex_shader, s_video); - } - - header_final = t_concat(tmp, header, header_final); - p->final_program = - create_program(gl, "final", header_final, vertex_shader, s_video); - - 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_priv *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); -} - -static double get_scale_factor(struct gl_priv *p) -{ - double sx = (p->dst_rect.x1 - p->dst_rect.x0) / - (double)(p->src_rect.x1 - p->src_rect.x0); - double sy = (p->dst_rect.y1 - p->dst_rect.y0) / - (double)(p->src_rect.y1 - p->src_rect.y0); - // xxx: actually we should use different scalers in X/Y directions if the - // scale factors are different due to anamorphic content - return FFMIN(sx, sy); -} - -static bool update_scale_factor(struct gl_priv *p, struct filter_kernel *kernel) -{ - double scale = get_scale_factor(p); - if (!p->use_fancy_downscaling && scale < 1.0) - scale = 1.0; - return mp_init_filter(kernel, filter_sizes, FFMAX(1.0, 1.0 / scale)); -} - -static void init_scaler(struct gl_priv *p, struct scaler *scaler) -{ - GL *gl = p->gl; - - assert(scaler->name); - - scaler->kernel = NULL; - - 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->scaler_params[n])) - scaler->kernel->params[n] = p->scaler_params[n]; - } - - update_scale_factor(p, scaler->kernel); - - int size = scaler->kernel->size; - assert(size < FF_ARRAY_ELEMS(lut_tex_formats)); - struct lut_tex_format *fmt = &lut_tex_formats[size]; - bool use_2d = fmt->pixels > 1; - bool is_luma = scaler->index == 0; - scaler->lut_name = use_2d - ? (is_luma ? "lut_l_2d" : "lut_c_2d") - : (is_luma ? "lut_l_1d" : "lut_c_1d"); - - gl->ActiveTexture(GL_TEXTURE0 + TEXUNIT_SCALERS + scaler->index); - GLenum target = use_2d ? GL_TEXTURE_2D : GL_TEXTURE_1D; - - if (!scaler->gl_lut) - gl->GenTextures(1, &scaler->gl_lut); - - gl->BindTexture(target, scaler->gl_lut); - gl->PixelStorei(GL_UNPACK_ALIGNMENT, 4); - gl->PixelStorei(GL_UNPACK_ROW_LENGTH, 0); - - float *weights = talloc_array(NULL, float, LOOKUP_TEXTURE_SIZE * size); - mp_compute_lut(scaler->kernel, LOOKUP_TEXTURE_SIZE, weights); - if (use_2d) { - gl->TexImage2D(GL_TEXTURE_2D, 0, fmt->internal_format, fmt->pixels, - LOOKUP_TEXTURE_SIZE, 0, fmt->format, GL_FLOAT, - weights); - } else { - gl->TexImage1D(GL_TEXTURE_1D, 0, fmt->internal_format, - LOOKUP_TEXTURE_SIZE, 0, fmt->format, GL_FLOAT, - weights); - } - talloc_free(weights); - - gl->TexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - gl->TexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - gl->TexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - gl->TexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - gl->ActiveTexture(GL_TEXTURE0); - - debug_check_gl(p, "after initializing scaler"); -} - -static void make_dither_matrix(unsigned char *m, int size) -{ - m[0] = 0; - for (int sz = 1; sz < size; sz *= 2) { - int offset[] = {sz*size, sz, sz * (size+1), 0}; - for (int i = 0; i < 4; i++) - for (int y = 0; y < sz * size; y += size) - for (int x = 0; x < sz; x++) - m[x+y+offset[i]] = m[x+y] * 4 + (3-i) * 256/size/size; - } -} - -static void init_dither(struct gl_priv *p) -{ - GL *gl = p->gl; - - // Assume 8 bits per component if unknown. - int dst_depth = p->glctx->depth_g ? p->glctx->depth_g : 8; - if (p->dither_depth > 0) - dst_depth = p->dither_depth; - - if (p->dither_depth < 0) - return; - - mp_msg(MSGT_VO, MSGL_V, "[gl] Dither to %d.\n", dst_depth); - - // This defines how many bits are considered significant for output on - // screen. The superfluous bits will be used for rounded according to the - // dither matrix. The precision of the source implicitly decides how many - // dither patterns can be visible. - p->dither_quantization = (1 << dst_depth) - 1; - int size = 8; - p->dither_multiply = p->dither_quantization + 1.0 / (size*size); - unsigned char dither[256]; - make_dither_matrix(dither, size); - - p->dither_size = size; - - gl->ActiveTexture(GL_TEXTURE0 + TEXUNIT_DITHER); - gl->GenTextures(1, &p->dither_texture); - gl->BindTexture(GL_TEXTURE_2D, p->dither_texture); - gl->PixelStorei(GL_UNPACK_ALIGNMENT, 1); - gl->PixelStorei(GL_UNPACK_ROW_LENGTH, 0); - gl->TexImage2D(GL_TEXTURE_2D, 0, GL_RED, size, size, 0, GL_RED, - GL_UNSIGNED_BYTE, dither); - gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - gl->ActiveTexture(GL_TEXTURE0); -} - -static void reinit_rendering(struct gl_priv *p) -{ - mp_msg(MSGT_VO, MSGL_V, "[gl] Reinit rendering.\n"); - - if (p->gl->SwapInterval && p->swap_interval >= 0) - p->gl->SwapInterval(p->swap_interval); - - debug_check_gl(p, "before scaler initialization"); - - uninit_rendering(p); - - init_dither(p); - - init_scaler(p, &p->scalers[0]); - init_scaler(p, &p->scalers[1]); - - compile_shaders(p); - - if (p->indirect_program && !p->indirect_fbo.fbo) - fbotex_init(p, &p->indirect_fbo, p->texture_width, p->texture_height); - - if (!p->osd) { - p->osd = mpgl_osd_init(p->gl, false); - p->osd->use_pbo = p->use_pbo; - } -} - -static void uninit_rendering(struct gl_priv *p) -{ - GL *gl = p->gl; - - delete_shaders(p); - - for (int n = 0; n < 2; n++) { - gl->DeleteTextures(1, &p->scalers[n].gl_lut); - p->scalers[n].gl_lut = 0; - p->scalers[n].lut_name = NULL; - p->scalers[n].kernel = NULL; - } - - gl->DeleteTextures(1, &p->dither_texture); - p->dither_texture = 0; - - if (p->osd) - mpgl_osd_destroy(p->osd); - p->osd = NULL; -} - -static void init_lut_3d(struct gl_priv *p) -{ - GL *gl = p->gl; - - gl->GenTextures(1, &p->lut_3d_texture); - gl->ActiveTexture(GL_TEXTURE0 + TEXUNIT_3DLUT); - gl->BindTexture(GL_TEXTURE_3D, p->lut_3d_texture); - gl->PixelStorei(GL_UNPACK_ALIGNMENT, 4); - gl->PixelStorei(GL_UNPACK_ROW_LENGTH, 0); - gl->TexImage3D(GL_TEXTURE_3D, 0, GL_RGB16, p->lut_3d_w, p->lut_3d_h, - p->lut_3d_d, 0, GL_RGB, GL_UNSIGNED_SHORT, p->lut_3d_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); - - debug_check_gl(p, "after 3d lut creation"); -} - -static void init_video(struct gl_priv *p) -{ - GL *gl = p->gl; - - if (p->use_lut_3d && !p->lut_3d_texture) - init_lut_3d(p); - - if (!p->is_yuv && (p->use_srgb || p->use_lut_3d)) { - p->is_linear_rgb = true; - p->gl_internal_format = GL_SRGB; - } - - int eq_caps = MP_CSP_EQ_CAPS_GAMMA; - if (p->is_yuv) - eq_caps |= MP_CSP_EQ_CAPS_COLORMATRIX; - p->video_eq.capabilities = eq_caps; - - debug_check_gl(p, "before video texture creation"); - - tex_size(p, p->image_width, p->image_height, - &p->texture_width, &p->texture_height); - - for (int n = 0; n < p->plane_count; n++) { - struct texplane *plane = &p->planes[n]; - - int w = p->texture_width >> plane->shift_x; |