summaryrefslogtreecommitdiffstats
path: root/video/out/opengl/video.c
diff options
context:
space:
mode:
Diffstat (limited to 'video/out/opengl/video.c')
-rw-r--r--video/out/opengl/video.c2113
1 files changed, 1294 insertions, 819 deletions
diff --git a/video/out/opengl/video.c b/video/out/opengl/video.c
index 8807b65005..f46fdc1c9f 100644
--- a/video/out/opengl/video.c
+++ b/video/out/opengl/video.c
@@ -17,6 +17,7 @@
#include <assert.h>
#include <math.h>
+#include <stdarg.h>
#include <stdbool.h>
#include <string.h>
#include <assert.h>
@@ -31,24 +32,22 @@
#include "common/global.h"
#include "options/options.h"
#include "common.h"
+#include "formats.h"
#include "utils.h"
#include "hwdec.h"
#include "osd.h"
#include "stream/stream.h"
-#include "superxbr.h"
-#include "nnedi3.h"
#include "video_shaders.h"
+#include "user_shaders.h"
#include "video/out/filter_kernels.h"
#include "video/out/aspect.h"
#include "video/out/bitmap_packer.h"
#include "video/out/dither.h"
#include "video/out/vo.h"
-// Maximal number of passes that prescaler can be applied.
-#define MAX_PRESCALE_PASSES 5
-
-// Maximal number of steps each pass of prescaling contains
-#define MAX_PRESCALE_STEPS 2
+// Maximal number of saved textures (for user script purposes)
+#define MAX_TEXTURE_HOOKS 16
+#define MAX_SAVED_TEXTURES 32
// scale/cscale arguments that map directly to shader filter routines.
// Note that the convolution filters are not included in this list.
@@ -91,6 +90,7 @@ static const struct gl_vao_entry vertex_vao[] = {
struct texplane {
int w, h;
+ int tex_w, tex_h;
GLint gl_internal_format;
GLenum gl_target;
bool use_integer;
@@ -98,12 +98,14 @@ struct texplane {
GLenum gl_type;
GLuint gl_texture;
int gl_buffer;
+ char swizzle[5];
};
struct video_image {
struct texplane planes[4];
bool image_flipped;
struct mp_image *mpi; // original input image
+ bool hwdec_mapped;
};
enum plane_type {
@@ -125,10 +127,29 @@ struct img_tex {
GLenum gl_target;
bool use_integer;
int tex_w, tex_h; // source texture size
- int w, h; // logical size (with pre_transform applied)
- struct gl_transform pre_transform; // source texture space
+ int w, h; // logical size (after transformation)
struct gl_transform transform; // rendering transformation
- bool texture_la; // it's a GL_LUMINANCE_ALPHA texture (access with .ra not .rg)
+ char swizzle[5];
+};
+
+// A named img_tex, for user scripting purposes
+struct saved_tex {
+ const char *name;
+ struct img_tex tex;
+};
+
+// A texture hook. This is some operation that transforms a named texture as
+// soon as it's generated
+struct tex_hook {
+ char *hook_tex;
+ char *save_tex;
+ char *bind_tex[TEXUNIT_VIDEO_NUM];
+ int components; // how many components are relevant (0 = same as input)
+ void *priv; // this can be set to whatever the hook wants
+ void (*hook)(struct gl_video *p, struct img_tex tex, // generates GLSL
+ struct gl_transform *trans, void *priv);
+ void (*free)(struct tex_hook *hook);
+ bool (*cond)(struct gl_video *p, struct img_tex tex, void *priv);
};
struct fbosurface {
@@ -140,7 +161,7 @@ struct fbosurface {
struct cached_file {
char *path;
- char *body;
+ struct bstr body;
};
struct gl_video {
@@ -149,15 +170,15 @@ struct gl_video {
struct mpv_global *global;
struct mp_log *log;
struct gl_video_opts opts;
+ struct gl_video_opts *opts_alloc;
struct gl_lcms *cms;
bool gl_debug;
int texture_16bit_depth; // actual bits available in 16 bit textures
+ int fb_depth; // actual bits available in GL main framebuffer
struct gl_shader_cache *sc;
- GLenum gl_target; // texture target (GL_TEXTURE_2D, ...) for video and FBOs
-
struct gl_vao vao;
struct osd_state *osd_state;
@@ -170,7 +191,9 @@ struct gl_video {
GLuint dither_texture;
int dither_size;
- GLuint nnedi3_weights_buffer;
+ struct gl_timer *upload_timer;
+ struct gl_timer *render_timer;
+ struct gl_timer *present_timer;
struct mp_image_params real_image_params; // configured format
struct mp_image_params image_params; // texture format (mind hwdec case)
@@ -188,21 +211,13 @@ struct gl_video {
bool forced_dumb_mode;
struct fbotex merge_fbo[4];
- struct fbotex deband_fbo[4];
struct fbotex scale_fbo[4];
struct fbotex integer_fbo[4];
struct fbotex indirect_fbo;
struct fbotex blend_subs_fbo;
- struct fbotex unsharp_fbo;
struct fbotex output_fbo;
struct fbosurface surfaces[FBOSURFACES_MAX];
-
- // these are duplicated so we can keep rendering back and forth between
- // them to support an unlimited number of shader passes per step
- struct fbotex pre_fbo[2];
- struct fbotex post_fbo[2];
-
- struct fbotex prescale_fbo[MAX_PRESCALE_PASSES][MAX_PRESCALE_STEPS];
+ struct fbotex vdpau_deinterleave_fbo[2];
int surface_idx;
int surface_now;
@@ -229,6 +244,14 @@ struct gl_video {
bool use_linear;
float user_gamma;
+ // hooks and saved textures
+ struct saved_tex saved_tex[MAX_SAVED_TEXTURES];
+ int saved_tex_num;
+ struct tex_hook tex_hooks[MAX_TEXTURE_HOOKS];
+ int tex_hook_num;
+ struct fbotex hook_fbos[MAX_SAVED_TEXTURES];
+ int hook_fbo_num;
+
int frames_uploaded;
int frames_rendered;
AVLFG lfg;
@@ -237,7 +260,7 @@ struct gl_video {
int last_dither_matrix_size;
float *last_dither_matrix;
- struct cached_file files[10];
+ struct cached_file *files;
int num_files;
struct gl_hwdec *hwdec;
@@ -245,89 +268,7 @@ struct gl_video {
bool dsi_warned;
bool custom_shader_fn_warned;
-};
-
-struct fmt_entry {
- int mp_format;
- GLint internal_format;
- GLenum format;
- GLenum type;
-};
-
-// Very special formats, for which OpenGL happens to have direct support
-static const struct fmt_entry mp_to_gl_formats[] = {
- {IMGFMT_RGB565, GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5},
- {0},
-};
-
-static const struct fmt_entry gl_byte_formats[] = {
- {0, GL_RED, GL_RED, GL_UNSIGNED_BYTE}, // 1 x 8
- {0, GL_RG, GL_RG, GL_UNSIGNED_BYTE}, // 2 x 8
- {0, GL_RGB, GL_RGB, GL_UNSIGNED_BYTE}, // 3 x 8
- {0, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE}, // 4 x 8
- {0, GL_R16, GL_RED, GL_UNSIGNED_SHORT}, // 1 x 16
- {0, GL_RG16, GL_RG, GL_UNSIGNED_SHORT}, // 2 x 16
- {0, GL_RGB16, GL_RGB, GL_UNSIGNED_SHORT}, // 3 x 16
- {0, GL_RGBA16, GL_RGBA, GL_UNSIGNED_SHORT}, // 4 x 16
-};
-
-static const struct fmt_entry gl_byte_formats_gles3[] = {
- {0, GL_R8, GL_RED, GL_UNSIGNED_BYTE}, // 1 x 8
- {0, GL_RG8, GL_RG, GL_UNSIGNED_BYTE}, // 2 x 8
- {0, GL_RGB8, GL_RGB, GL_UNSIGNED_BYTE}, // 3 x 8
- {0, GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE}, // 4 x 8
- // There are no filterable texture formats that can be uploaded as
- // GL_UNSIGNED_SHORT, so apparently we're out of luck.
- {0, 0, 0, 0}, // 1 x 16
- {0, 0, 0, 0}, // 2 x 16
- {0, 0, 0, 0}, // 3 x 16
- {0, 0, 0, 0}, // 4 x 16
-};
-
-static const struct fmt_entry gl_ui_byte_formats_gles3[] = {
- {0, GL_R8UI, GL_RED_INTEGER, GL_UNSIGNED_BYTE}, // 1 x 8
- {0, GL_RG8UI, GL_RG_INTEGER, GL_UNSIGNED_BYTE}, // 2 x 8
- {0, GL_RGB8UI, GL_RGB_INTEGER, GL_UNSIGNED_BYTE}, // 3 x 8
- {0, GL_RGBA8UI, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE}, // 4 x 8
- {0, GL_R16UI, GL_RED_INTEGER, GL_UNSIGNED_SHORT}, // 1 x 16
- {0, GL_RG16UI, GL_RG_INTEGER, GL_UNSIGNED_SHORT}, // 2 x 16
- {0, GL_RGB16UI, GL_RGB_INTEGER, GL_UNSIGNED_SHORT}, // 3 x 16
- {0, GL_RGBA16UI, GL_RGBA_INTEGER, GL_UNSIGNED_SHORT}, // 4 x 16
-};
-
-static const struct fmt_entry gl_byte_formats_gles2[] = {
- {0, GL_LUMINANCE, GL_LUMINANCE, GL_UNSIGNED_BYTE}, // 1 x 8
- {0, GL_LUMINANCE_ALPHA, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE}, // 2 x 8
- {0, GL_RGB, GL_RGB, GL_UNSIGNED_BYTE}, // 3 x 8
- {0, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE}, // 4 x 8
- {0, 0, 0, 0}, // 1 x 16
- {0, 0, 0, 0}, // 2 x 16
- {0, 0, 0, 0}, // 3 x 16
- {0, 0, 0, 0}, // 4 x 16
-};
-
-static const struct fmt_entry gl_byte_formats_legacy[] = {
- {0, GL_LUMINANCE, GL_LUMINANCE, GL_UNSIGNED_BYTE}, // 1 x 8
- {0, GL_LUMINANCE_ALPHA, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE}, // 2 x 8
- {0, GL_RGB, GL_RGB, GL_UNSIGNED_BYTE}, // 3 x 8
- {0, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE}, // 4 x 8
- {0, GL_LUMINANCE16, GL_LUMINANCE, GL_UNSIGNED_SHORT},// 1 x 16
- {0, GL_LUMINANCE16_ALPHA16, GL_LUMINANCE_ALPHA, GL_UNSIGNED_SHORT},// 2 x 16
- {0, GL_RGB16, GL_RGB, GL_UNSIGNED_SHORT},// 3 x 16
- {0, GL_RGBA16, GL_RGBA, GL_UNSIGNED_SHORT},// 4 x 16
-};
-
-static const struct fmt_entry gl_float16_formats[] = {
- {0, GL_R16F, GL_RED, GL_FLOAT}, // 1 x f
- {0, GL_RG16F, GL_RG, GL_FLOAT}, // 2 x f
- {0, GL_RGB16F, GL_RGB, GL_FLOAT}, // 3 x f
- {0, GL_RGBA16F, GL_RGBA, GL_FLOAT}, // 4 x f
-};
-
-static const struct fmt_entry gl_apple_formats[] = {
- {IMGFMT_UYVY, GL_RGB, GL_RGB_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE},
- {IMGFMT_YUYV, GL_RGB, GL_RGB_422_APPLE, GL_UNSIGNED_SHORT_8_8_REV_APPLE},
- {0}
+ bool broken_frame; // temporary error state
};
struct packed_fmt_entry {
@@ -359,6 +300,7 @@ static const struct packed_fmt_entry mp_packed_formats[] = {
};
const struct gl_video_opts gl_video_opts_def = {
+ .dither_algo = DITHER_FRUIT,
.dither_depth = -1,
.dither_size = 6,
.temporal_dither_period = 1,
@@ -375,14 +317,16 @@ const struct gl_video_opts gl_video_opts_def = {
.scaler_resizes_only = 1,
.scaler_lut_size = 6,
.interpolation_threshold = 0.0001,
- .alpha_mode = 3,
+ .alpha_mode = ALPHA_BLEND_TILES,
.background = {0, 0, 0, 255},
.gamma = 1.0f,
- .prescale_passes = 1,
- .prescale_downscaling_threshold = 2.0f,
+ .target_brightness = 250,
+ .hdr_tone_mapping = TONE_MAPPING_HABLE,
+ .tone_mapping_param = NAN,
};
const struct gl_video_opts gl_video_opts_hq_def = {
+ .dither_algo = DITHER_FRUIT,
.dither_depth = 0,
.dither_size = 6,
.temporal_dither_period = 1,
@@ -401,13 +345,13 @@ const struct gl_video_opts gl_video_opts_hq_def = {
.scaler_resizes_only = 1,
.scaler_lut_size = 6,
.interpolation_threshold = 0.0001,
- .alpha_mode = 3,
+ .alpha_mode = ALPHA_BLEND_TILES,
.background = {0, 0, 0, 255},
.gamma = 1.0f,
- .blend_subs = 0,
.deband = 1,
- .prescale_passes = 1,
- .prescale_downscaling_threshold = 2.0f,
+ .target_brightness = 250,
+ .hdr_tone_mapping = TONE_MAPPING_HABLE,
+ .tone_mapping_param = NAN,
};
static int validate_scaler_opt(struct mp_log *log, const m_option_t *opt,
@@ -436,6 +380,14 @@ const struct m_sub_options gl_video_conf = {
OPT_FLAG("gamma-auto", gamma_auto, 0),
OPT_CHOICE_C("target-prim", target_prim, 0, mp_csp_prim_names),
OPT_CHOICE_C("target-trc", target_trc, 0, mp_csp_trc_names),
+ OPT_INTRANGE("target-brightness", target_brightness, 0, 1, 100000),
+ OPT_CHOICE("hdr-tone-mapping", hdr_tone_mapping, 0,
+ ({"clip", TONE_MAPPING_CLIP},
+ {"reinhard", TONE_MAPPING_REINHARD},
+ {"hable", TONE_MAPPING_HABLE},
+ {"gamma", TONE_MAPPING_GAMMA},
+ {"linear", TONE_MAPPING_LINEAR})),
+ OPT_FLOAT("tone-mapping-param", tone_mapping_param, 0),
OPT_FLAG("pbo", pbo, 0),
SCALER_OPTS("scale", SCALER_SCALE),
SCALER_OPTS("dscale", SCALER_DSCALE),
@@ -449,9 +401,7 @@ const struct m_sub_options gl_video_conf = {
OPT_FLOATRANGE("sigmoid-center", sigmoid_center, 0, 0.0, 1.0),
OPT_FLOATRANGE("sigmoid-slope", sigmoid_slope, 0, 1.0, 20.0),
OPT_CHOICE("fbo-format", fbo_format, 0,
- ({"rgb", GL_RGB},
- {"rgba", GL_RGBA},
- {"rgb8", GL_RGB8},
+ ({"rgb8", GL_RGB8},
{"rgba8", GL_RGBA8},
{"rgb10", GL_RGB10},
{"rgb10_a2", GL_RGB10_A2},
@@ -466,42 +416,33 @@ const struct m_sub_options gl_video_conf = {
OPT_CHOICE_OR_INT("dither-depth", dither_depth, 0, -1, 16,
({"no", -1}, {"auto", 0})),
OPT_CHOICE("dither", dither_algo, 0,
- ({"fruit", 0}, {"ordered", 1}, {"no", -1})),
+ ({"fruit", DITHER_FRUIT},
+ {"ordered", DITHER_ORDERED},
+ {"no", DITHER_NONE})),
OPT_INTRANGE("dither-size-fruit", dither_size, 0, 2, 8),
OPT_FLAG("temporal-dither", temporal_dither, 0),
OPT_INTRANGE("temporal-dither-period", temporal_dither_period, 0, 1, 128),
OPT_CHOICE("alpha", alpha_mode, 0,
- ({"no", 0},
- {"yes", 1},
- {"blend", 2},
- {"blend-tiles", 3})),
+ ({"no", ALPHA_NO},
+ {"yes", ALPHA_YES},
+ {"blend", ALPHA_BLEND},
+ {"blend-tiles", ALPHA_BLEND_TILES})),
OPT_FLAG("rectangle-textures", use_rectangle, 0),
OPT_COLOR("background", background, 0),
OPT_FLAG("interpolation", interpolation, 0),
OPT_FLOAT("interpolation-threshold", interpolation_threshold, 0),
OPT_CHOICE("blend-subtitles", blend_subs, 0,
- ({"no", 0},
- {"yes", 1},
- {"video", 2})),
+ ({"no", BLEND_SUBS_NO},
+ {"yes", BLEND_SUBS_YES},
+ {"video", BLEND_SUBS_VIDEO})),
OPT_STRING("scale-shader", scale_shader, 0),
OPT_STRINGLIST("pre-shaders", pre_shaders, 0),
OPT_STRINGLIST("post-shaders", post_shaders, 0),
+ OPT_STRINGLIST("user-shaders", user_shaders, 0),
OPT_FLAG("deband", deband, 0),
OPT_SUBSTRUCT("deband", deband_opts, deband_conf, 0),
OPT_FLOAT("sharpen", unsharp, 0),
- OPT_CHOICE("prescale-luma", prescale_luma, 0,
- ({"none", 0},
- {"superxbr", 1}
-#if HAVE_NNEDI
- , {"nnedi3", 2}
-#endif
- )),
- OPT_INTRANGE("prescale-passes",
- prescale_passes, 0, 1, MAX_PRESCALE_PASSES),
- OPT_FLOATRANGE("prescale-downscaling-threshold",
- prescale_downscaling_threshold, 0, 0.0, 32.0),
- OPT_SUBSTRUCT("superxbr", superxbr_opts, superxbr_conf, 0),
- OPT_SUBSTRUCT("nnedi3", nnedi3_opts, nnedi3_conf, 0),
+ OPT_SUBSTRUCT("", icc_opts, mp_icc_conf, 0),
OPT_REMOVED("approx-gamma", "this is always enabled now"),
OPT_REMOVED("cscale-down", "chroma is never downscaled"),
@@ -509,6 +450,7 @@ const struct m_sub_options gl_video_conf = {
OPT_REMOVED("indirect", "this is set automatically whenever sane"),
OPT_REMOVED("srgb", "use target-prim=bt709:target-trc=srgb instead"),
OPT_REMOVED("source-shader", "use :deband to enable debanding"),
+ OPT_REMOVED("prescale-luma", "use user shaders for prescaling"),
OPT_REPLACED("lscale", "scale"),
OPT_REPLACED("lscale-down", "scale-down"),
@@ -524,7 +466,6 @@ const struct m_sub_options gl_video_conf = {
OPT_REPLACED("smoothmotion-threshold", "tscale-param1"),
OPT_REPLACED("scale-down", "dscale"),
OPT_REPLACED("fancy-downscaling", "correct-downscaling"),
- OPT_REPLACED("prescale", "prescale-luma"),
{0}
},
@@ -535,78 +476,44 @@ const struct m_sub_options gl_video_conf = {
static void uninit_rendering(struct gl_video *p);
static void uninit_scaler(struct gl_video *p, struct scaler *scaler);
static void check_gl_features(struct gl_video *p);
-static bool init_format(int fmt, struct gl_video *init);
-static void gl_video_upload_image(struct gl_video *p, struct mp_image *mpi);
-static void assign_options(struct gl_video_opts *dst, struct gl_video_opts *src);
+static bool init_format(struct gl_video *p, int fmt, bool test_only);
+static void init_image_desc(struct gl_video *p, int fmt);
+static bool gl_video_upload_image(struct gl_video *p, struct mp_image *mpi);
+static void set_options(struct gl_video *p, struct gl_video_opts *src);
+static const char *handle_scaler_opt(const char *name, bool tscale);
+static void reinit_from_options(struct gl_video *p);
static void get_scale_factors(struct gl_video *p, bool transpose_rot, double xy[2]);
+static void gl_video_setup_hooks(struct gl_video *p);
#define GLSL(x) gl_sc_add(p->sc, #x "\n");
#define GLSLF(...) gl_sc_addf(p->sc, __VA_ARGS__)
#define GLSLHF(...) gl_sc_haddf(p->sc, __VA_ARGS__)
-// Return a fixed point texture format with given characteristics.
-static const struct fmt_entry *find_tex_format(GL *gl, int bytes_per_comp,
- int n_channels)
-{
- assert(bytes_per_comp == 1 || bytes_per_comp == 2);
- assert(n_channels >= 1 && n_channels <= 4);
- const struct fmt_entry *fmts = gl_byte_formats;
- if (gl->es >= 300) {
- fmts = gl_byte_formats_gles3;
- } else if (gl->es) {
- fmts = gl_byte_formats_gles2;
- } else if (!(gl->mpgl_caps & MPGL_CAP_TEX_RG)) {
- fmts = gl_byte_formats_legacy;
- }
- return &fmts[n_channels - 1 + (bytes_per_comp - 1) * 4];
-}
-
-static bool is_integer_format(const struct fmt_entry *fmt)
-{
- // Tests only the formats which we actually declare somewhere.
- switch (fmt->format) {
- case GL_RED_INTEGER:
- case GL_RG_INTEGER:
- case GL_RGB_INTEGER:
- case GL_RGBA_INTEGER:
- return true;
- }
- return false;
-}
-
-static const char *load_cached_file(struct gl_video *p, const char *path)
+static struct bstr load_cached_file(struct gl_video *p, const char *path)
{
if (!path || !path[0])
- return NULL;
+ return (struct bstr){0};
for (int n = 0; n < p->num_files; n++) {
if (strcmp(p->files[n].path, path) == 0)
return p->files[n].body;
}
// not found -> load it
- if (p->num_files == MP_ARRAY_SIZE(p->files)) {
- // empty cache when it overflows
- for (int n = 0; n < p->num_files; n++) {
- talloc_free(p->files[n].path);
- talloc_free(p->files[n].body);
- }
- p->num_files = 0;
- }
- struct bstr s = stream_read_file(path, p, p->global, 100000); // 100 kB
+ struct bstr s = stream_read_file(path, p, p->global, 1024000); // 1024 kB
if (s.len) {
- struct cached_file *new = &p->files[p->num_files++];
- *new = (struct cached_file) {
+ struct cached_file new = {
.path = talloc_strdup(p, path),
- .body = s.start
+ .body = s,
};
- return new->body;
+ MP_TARRAY_APPEND(p, p->files, p->num_files, new);
+ return new.body;
}
- return NULL;
+ return (struct bstr){0};
}
static void debug_check_gl(struct gl_video *p, const char *msg)
{
if (p->gl_debug)
- glCheckError(p->gl, p->log, msg);
+ gl_check_error(p->gl, p->log, msg);
}
void gl_video_set_debug(struct gl_video *p, bool enable)
@@ -628,13 +535,23 @@ static void gl_video_reset_surfaces(struct gl_video *p)
p->output_fbo_valid = false;
}
+static void gl_video_reset_hooks(struct gl_video *p)
+{
+ for (int i = 0; i < p->tex_hook_num; i++) {
+ if (p->tex_hooks[i].free)
+ p->tex_hooks[i].free(&p->tex_hooks[i]);
+ }
+
+ p->tex_hook_num = 0;
+}
+
static inline int fbosurface_wrap(int id)
{
id = id % FBOSURFACES_MAX;
return id < 0 ? id + FBOSURFACES_MAX : id;
}
-static void recreate_osd(struct gl_video *p)
+static void reinit_osd(struct gl_video *p)
{
mpgl_osd_destroy(p->osd);
p->osd = NULL;
@@ -644,17 +561,6 @@ static void recreate_osd(struct gl_video *p)
}
}
-static void reinit_rendering(struct gl_video *p)
-{
- MP_VERBOSE(p, "Reinit rendering.\n");
-
- debug_check_gl(p, "before scaler initialization");
-
- uninit_rendering(p);
-
- recreate_osd(p);
-}
-
static void uninit_rendering(struct gl_video *p)
{
GL *gl = p->gl;
@@ -665,45 +571,41 @@ static void uninit_rendering(struct gl_video *p)
gl->DeleteTextures(1, &p->dither_texture);
p->dither_texture = 0;
- gl->DeleteBuffers(1, &p->nnedi3_weights_buffer);
- p->nnedi3_weights_buffer = 0;
-
for (int n = 0; n < 4; n++) {
fbotex_uninit(&p->merge_fbo[n]);
- fbotex_uninit(&p->deband_fbo[n]);
fbotex_uninit(&p->scale_fbo[n]);
fbotex_uninit(&p->integer_fbo[n]);
}
fbotex_uninit(&p->indirect_fbo);
fbotex_uninit(&p->blend_subs_fbo);
- fbotex_uninit(&p->unsharp_fbo);
-
- for (int n = 0; n < 2; n++) {
- fbotex_uninit(&p->pre_fbo[n]);
- fbotex_uninit(&p->post_fbo[n]);
- }
-
- for (int pass = 0; pass < MAX_PRESCALE_PASSES; pass++) {
- for (int step = 0; step < MAX_PRESCALE_STEPS; step++)
- fbotex_uninit(&p->prescale_fbo[pass][step]);
- }
for (int n = 0; n < FBOSURFACES_MAX; n++)
fbotex_uninit(&p->surfaces[n].fbotex);
+ for (int n = 0; n < MAX_SAVED_TEXTURES; n++)
+ fbotex_uninit(&p->hook_fbos[n]);
+
+ for (int n = 0; n < 2; n++)
+ fbotex_uninit(&p->vdpau_deinterleave_fbo[n]);
+
gl_video_reset_surfaces(p);
+ gl_video_reset_hooks(p);
+
+ gl_sc_reset_error(p->sc);
}
-void gl_video_update_profile(struct gl_video *p)
+// Warning: profile.start must point to a ta allocation, and the function
+// takes over ownership.
+void gl_video_set_icc_profile(struct gl_video *p, bstr icc_data)
{
- if (p->use_lut_3d)
- return;
-
- p->use_lut_3d = true;
- check_gl_features(p);
+ if (gl_lcms_set_memory_profile(p->cms, icc_data))
+ reinit_from_options(p);
+}
- reinit_rendering(p);
+bool gl_video_icc_auto_enabled(struct gl_video *p)
+{
+ return p->opts.icc_opts ? p->opts.icc_opts->profile_auto : false;
}
static bool gl_video_get_lut3d(struct gl_video *p, enum mp_csp_prim prim,
@@ -711,14 +613,15 @@ static bool gl_video_get_lut3d(struct gl_video *p, enum mp_csp_prim prim,
{
GL *gl = p->gl;
- if (!p->cms || !p->use_lut_3d)
+ if (!p->use_lut_3d)
return false;
- if (!gl_lcms_has_changed(p->cms, prim, trc))
+ if (p->lut_3d_texture && !gl_lcms_has_changed(p->cms, prim, trc))
return true;
struct lut3d *lut3d = NULL;
if (!gl_lcms_get_lut3d(p->cms, &lut3d, prim, trc) || !lut3d) {
+ p->use_lut_3d = false;
return false;
}
@@ -738,12 +641,14 @@ static bool gl_video_get_lut3d(struct gl_video *p, enum mp_csp_prim prim,
debug_check_gl(p, "after 3d lut creation");
+ talloc_free(lut3d);
+
return true;
}
// Fill an img_tex struct from an FBO + some metadata
-static struct img_tex img_tex_fbo(struct fbotex *fbo, struct gl_transform t,
- enum plane_type type, int components)
+static struct img_tex img_tex_fbo(struct fbotex *fbo, enum plane_type type,
+ int components)
{
assert(type != PLANE_NONE);
return (struct img_tex){
@@ -756,8 +661,7 @@ static struct img_tex img_tex_fbo(struct fbotex *fbo, struct gl_transform t,
.tex_h = fbo->rh,
.w = fbo->lw,
.h = fbo->lh,
- .pre_transform = identity_trans,
- .transform = t,
+ .transform = identity_trans,
.components = components,
};
}
@@ -797,18 +701,19 @@ static void get_plane_source_transform(struct gl_video *p, int w, int h,
}
// Places a video_image's image textures + associated metadata into tex[]. The
-// number of textures is equal to p->plane_count.
+// number of textures is equal to p->plane_count. Any necessary plane offsets
+// are stored in off. (e.g. chroma position)
static void pass_get_img_tex(struct gl_video *p, struct video_image *vimg,
- struct img_tex tex[4])
+ struct img_tex tex[4], struct gl_transform off[4])
{
assert(vimg->mpi);
// Determine the chroma offset
- struct gl_transform chroma = (struct gl_transform){{{0}}};
-
float ls_w = 1.0 / (1 << p->image_desc.chroma_xs);
float ls_h = 1.0 / (1 << p->image_desc.chroma_ys);
+ struct gl_transform chroma = {{{ls_w, 0.0}, {0.0, ls_h}}};
+
if (p->image_params.chroma_location != MP_CHROMA_CENTER) {
int cx, cy;
mp_get_chroma_location(p->image_params.chroma_location, &cx, &cy);
@@ -821,11 +726,7 @@ static void pass_get_img_tex(struct gl_video *p, struct video_image *vimg,
chroma.t[1] = ls_h < 1 ? ls_h * -cy / 2 : 0;
}
- // 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.m[0][0] = ls_w * (float)vimg->planes[0].w / vimg->planes[1].w;
- chroma.m[1][1] = ls_h * (float)vimg->planes[0].h / vimg->planes[1].h;
+ // FIXME: account for rotation in the chroma offset
// The existing code assumes we just have a single tex multiplier for
// all of the planes. This may change in the future
@@ -856,17 +757,18 @@ static void pass_get_img_tex(struct gl_video *p, struct video_image *vimg,
.gl_target = t->gl_target,
.multiplier = tex_mul,
.use_integer = t->use_integer,
- .tex_w = t->w,
- .tex_h = t->h,
+ .tex_w = t->tex_w,
+ .tex_h = t->tex_h,
.w = t->w,
.h = t->h,
- .transform = type == PLANE_CHROMA ? chroma : identity_trans,
.components = p->image_desc.components[n],
- .texture_la = t->gl_format == GL_LUMINANCE_ALPHA,
};
- get_plane_source_transform(p, t->w, t->h, &tex[n].pre_transform);
+ snprintf(tex[n].swizzle, sizeof(tex[n].swizzle), "%s", t->swizzle);
+ get_plane_source_transform(p, t->w, t->h, &tex[n].transform);
if (p->image_params.rotate % 180 == 90)
MPSWAP(int, tex[n].w, tex[n].h);
+
+ off[n] = type == PLANE_CHROMA ? chroma : identity_trans;
}
}
@@ -874,19 +776,21 @@ static void init_video(struct gl_video *p)
{
GL *gl = p->gl;
- init_format(p->image_params.imgfmt, p);
- p->gl_target = p->opts.use_rectangle ? GL_TEXTURE_RECTANGLE : GL_TEXTURE_2D;
-
- check_gl_features(p);
-
- if (p->hwdec_active) {
+ if (p->hwdec && p->hwdec->driver->imgfmt == p->image_params.imgfmt) {
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->image_params.imgfmt = p->image_desc.id;
- p->gl_target = p->hwdec->gl_texture_target;
+ init_image_desc(p, p->image_params.imgfmt);
+ const char **exts = p->hwdec->glsl_extensions;
+ for (int n = 0; exts && exts[n]; n++)
+ gl_sc_enable_extension(p->sc, (char *)exts[n]);
+ p->hwdec_active = true;
+ } else {
+ init_format(p, p->image_params.imgfmt, false);
}
+ // Format-dependent checks.
+ check_gl_features(p);
+
mp_image_params_guess_csp(&p->image_params);
int eq_caps = MP_CSP_EQ_CAPS_GAMMA;
@@ -900,42 +804,65 @@ static void init_video(struct gl_video *p)
debug_check_gl(p, "before video texture creation");
- struct video_image *vimg = &p->image;
+ if (!p->hwdec_active) {
+ struct video_image *vimg = &p->image;
- struct mp_image layout = {0};
- mp_image_set_params(&layout, &p->image_params);
+ GLenum gl_target =
+ p->opts.use_rectangle ? GL_TEXTURE_RECTANGLE : GL_TEXTURE_2D;
- for (int n = 0; n < p->plane_count; n++) {
- struct texplane *plane = &vimg->planes[n];
+ struct mp_image layout = {0};
+ mp_image_set_params(&layout, &p->image_params);
- plane->gl_target = p->gl_target;
+ for (int n = 0; n < p->plane_count; n++) {
+ struct texplane *plane = &vimg->planes[n];
- plane->w = mp_image_plane_w(&layout, n);
- plane->h = mp_image_plane_h(&layout, n);
+ plane->gl_target = gl_target;
+
+ plane->w = plane->tex_w = mp_image_plane_w(&layout, n);
+ plane->h = plane->tex_h = mp_image_plane_h(&layout, n);
- if (!p->hwdec_active) {
gl->ActiveTexture(GL_TEXTURE0 + n);
gl->GenTextures(1, &plane->gl_texture);
- gl->BindTexture(p->gl_target, plane->gl_texture);
+ gl->BindTexture(gl_target, plane->gl_texture);
- gl->TexImage2D(p->gl_target, 0, plane->gl_internal_format,
+ gl->TexImage2D(gl_target, 0, plane->gl_internal_format,
plane->w, plane->h, 0,
plane->gl_format, plane->gl_type, NULL);
int filter = plane->use_integer ? GL_NEAREST : GL_LINEAR;
- gl->TexParameteri(p->gl_target, GL_TEXTURE_MIN_FILTER, filter);
- gl->TexParameteri(p->gl_target, GL_TEXTURE_MAG_FILTER, filter);
- 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);
- }
+ gl->TexParameteri(gl_target, GL_TEXTURE_MIN_FILTER, filter);
+ gl->TexParameteri(gl_target, GL_TEXTURE_MAG_FILTER, filter);
+ gl->TexParameteri(gl_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ gl->TexParameteri(gl_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- MP_VERBOSE(p, "Texture for plane %d: %dx%d\n", n, plane->w, plane->h);
+ MP_VERBOSE(p, "Texture for plane %d: %dx%d\n", n, plane->w, plane->h);
+ }
+ gl->ActiveTexture(GL_TEXTURE0);
}
- gl->ActiveTexture(GL_TEXTURE0);
debug_check_gl(p, "after video texture creation");
- reinit_rendering(p);
+ gl_video_setup_hooks(p);
+}
+
+// Release any texture mappings associated with the current frame.
+static void unmap_current_image(struct gl_video *p)
+{
+ struct video_image *vimg = &p->image;
+
+ if (vimg->hwdec_mapped) {
+ assert(p->hwdec_active);
+ if (p->hwdec->driver->unmap)
+ p->hwdec->driver->unmap(p->hwdec);
+ memset(vimg->planes, 0, sizeof(vimg->planes));
+ vimg->hwdec_mapped = false;
+ }
+}
+
+static void unref_current_image(struct gl_video *p)
+{
+ unmap_current_image(p);
+ mp_image_unrefp(&p->image.mpi);
}
static void uninit_video(struct gl_video *p)
@@ -946,21 +873,21 @@ static void uninit_video(struct gl_video *p)
struct video_image *vimg = &p->image;
+ unref_current_image(p);
+
for (int n = 0; n < p->plane_count; n++) {
struct texplane *plane = &vimg->planes[n];
- if (!p->hwdec_active)
- gl->DeleteTextures(1, &plane->gl_texture);
- plane->gl_texture = 0;
+ gl->DeleteTextures(1, &plane->gl_texture);
gl->DeleteBuffers(1, &plane->gl_buffer);
- plane->gl_buffer = 0;
}
- mp_image_unrefp(&vimg->mpi);
+ *vimg = (struct video_image){0};
// Invalidate image_params to ensure that gl_video_config() will call
// init_video() on uninitialized gl_video.
p->real_image_params = (struct mp_image_params){0};
p->image_params = p->real_image_params;
+ p->hwdec_active = false;
}
static void pass_prepare_src_tex(struct gl_video *p)
@@ -975,9 +902,11 @@ static void pass_prepare_src_tex(struct gl_video *p)
char texture_name[32];
char texture_size[32];
+ char texture_rot[32];
char pixel_size[32];
snprintf(texture_name, sizeof(texture_name), "texture%d", n);
snprintf(texture_size, sizeof(texture_size), "texture_size%d", n);
+ snprintf(texture_rot, sizeof(texture_rot), "texture_rot%d", n);
snprintf(pixel_size, sizeof(pixel_size), "pixel_size%d", n);
if (s->use_integer) {
@@ -991,6 +920,7 @@ static void pass_prepare_src_tex(struct gl_video *p)
f[1] = s->tex_h;
}
gl_sc_uniform_vec2(sc, texture_size, f);
+ gl_sc_uniform_mat2(sc, texture_rot, true, (float *)s->transform.m);
gl_sc_uniform_vec2(sc, pixel_size, (GLfloat[]){1.0f / f[0],
1.0f / f[1]});
@@ -1022,7 +952,6 @@ static void render_pass_quad(struct gl_video *p, int vp_w, int vp_h,
if (!s->gl_tex)
continue;
struct gl_transform tr = s->transform;
- gl_transform_trans(s->pre_transform, &tr);
float tx = (n / 2) * s->w;
float ty = (n % 2) * s->h;
gl_transform_vec(tr, &tx, &ty);
@@ -1038,7 +967,6 @@ static void render_pass_quad(struct gl_video *p, int vp_w, int vp_h,
debug_check_gl(p, "after rendering");
}
-// flags: see render_pass_quad
static void finish_pass_direct(struct gl_video *p, GLint fbo, int vp_w, int vp_h,
const struct mp_rect *dst)
{
@@ -1067,6 +995,34 @@ static void finish_pass_fbo(struct gl_video *p, struct fbotex *dst_fbo,
&(struct mp_rect){0, 0, w, h});
}
+// Copy a texture to the vec4 color, while increasing offset. Also applies
+// the texture multiplier to the sampled color
+static void copy_img_tex(struct gl_video *p, int *offset, struct img_tex img)
+{
+ int count = img.components;
+ assert(*offset + count <= 4);
+
+ int id = pass_bind(p, img);
+ char src[5] = {0};
+ char dst[5] = {0};
+ const char *tex_fmt = img.swizzle[0] ? img.swizzle : "rgba";
+ const char *dst_fmt = "rgba";
+ for (int i = 0; i < count; i++) {
+ src[i] = tex_fmt[i];
+ dst[i] = dst_fmt[*offset + i];
+ }
+
+ if (img.use_integer) {
+ uint64_t tex_max = 1ull << p->image_desc.component_full_bits;
+ img.multiplier *= 1.0 / (tex_max - 1);
+ }
+
+ GLSLF("color.%s = %f * vec4(texture(texture%d, texcoord%d)).%s;\n",
+ dst, img.multiplier, id, id, src);
+
+ *offset += count;
+}
+
static void skip_unused(struct gl_video *p, int num_components)
{
for (int i = num_components; i < 4; i++)
@@ -1083,9 +1039,202 @@ static void uninit_scaler(struct gl_video *p, struct scaler *scaler)
scaler->initialized = false;
}
-static void load_shader(struct gl_video *p, const char *body)
+static void hook_prelude(struct gl_video *p, const char *name, int id,
+ struct img_tex tex)
{
- gl_sc_hadd(p->sc, body);
+ GLSLHF("#define %s_raw texture%d\n", name, id);
+ GLSLHF("#define %s_pos texcoord%d\n", name, id);
+ GLSLHF("#define %s_size texture_size%d\n", name, id);
+ GLSLHF("#define %s_rot texture_rot%d\n", name, id);
+ GLSLHF("#define %s_pt pixel_size%d\n", name, id);
+
+ // Set up the sampling functions
+ GLSLHF("#define %s_tex(pos) (%f * vec4(texture(%s_raw, pos)).%s)\n",
+ name, tex.multiplier, name, tex.swizzle[0] ? tex.swizzle : "rgba");