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.c203
1 files changed, 145 insertions, 58 deletions
diff --git a/video/out/opengl/video.c b/video/out/opengl/video.c
index 417c1b62b0..1a55d8393d 100644
--- a/video/out/opengl/video.c
+++ b/video/out/opengl/video.c
@@ -240,6 +240,12 @@ struct gl_video {
struct fbotex vdpau_deinterleave_fbo[2];
GLuint hdr_peak_ssbo;
+ // user pass descriptions and textures
+ struct tex_hook tex_hooks[SHADER_MAX_PASSES];
+ int tex_hook_num;
+ struct gl_user_shader_tex user_textures[SHADER_MAX_PASSES];
+ int user_tex_num;
+
int surface_idx;
int surface_now;
int frames_drawn;
@@ -274,9 +280,7 @@ struct gl_video {
struct gl_timer *upload_timer;
struct gl_timer *blit_timer;
- // hooks and saved textures
- struct tex_hook tex_hooks[SHADER_MAX_PASSES];
- int tex_hook_num;
+ // intermediate textures
struct saved_tex saved_tex[SHADER_MAX_SAVED];
int saved_tex_num;
struct fbotex hook_fbos[SHADER_MAX_SAVED];
@@ -503,7 +507,11 @@ static void gl_video_reset_hooks(struct gl_video *p)
for (int i = 0; i < p->tex_hook_num; i++)
talloc_free(p->tex_hooks[i].priv);
+ for (int i = 0; i < p->user_tex_num; i++)
+ p->gl->DeleteTextures(1, &p->user_textures[i].gl_tex);
+
p->tex_hook_num = 0;
+ p->user_tex_num = 0;
}
static inline int fbosurface_wrap(int id)
@@ -1378,6 +1386,51 @@ static void saved_tex_store(struct gl_video *p, const char *name,
};
}
+static bool pass_hook_setup_binds(struct gl_video *p, const char *name,
+ struct img_tex tex, struct tex_hook *hook)
+{
+ for (int t = 0; t < TEXUNIT_VIDEO_NUM; t++) {
+ char *bind_name = (char *)hook->bind_tex[t];
+
+ if (!bind_name)
+ continue;
+
+ // This is a special name that means "currently hooked texture"
+ if (strcmp(bind_name, "HOOKED") == 0) {
+ int id = pass_bind(p, tex);
+ hook_prelude(p, "HOOKED", id, tex);
+ hook_prelude(p, name, id, tex);
+ continue;
+ }
+
+ // BIND can also be used to load user-defined textures, in which
+ // case we will directly load them as a uniform instead of
+ // generating the hook_prelude boilerplate
+ for (int u = 0; u < p->user_tex_num; u++) {
+ struct gl_user_shader_tex *utex = &p->user_textures[u];
+ if (bstr_equals0(utex->name, bind_name)) {
+ gl_sc_uniform_tex(p->sc, bind_name, utex->gl_target, utex->gl_tex);
+ goto next_bind;
+ }
+ }
+
+ struct img_tex bind_tex;
+ if (!saved_tex_find(p, bind_name, &bind_tex)) {
+ // Clean up texture bindings and move on to the next hook
+ MP_DBG(p, "Skipping hook on %s due to no texture named %s.\n",
+ name, bind_name);
+ p->pass_tex_num -= t;
+ return false;
+ }
+
+ hook_prelude(p, bind_name, pass_bind(p, bind_tex), bind_tex);
+
+next_bind: ;
+ }
+
+ return true;
+}
+
// Process hooks for a plane, saving the result and returning a new img_tex
// If 'trans' is NULL, the shader is forbidden from transforming tex
static struct img_tex pass_hook(struct gl_video *p, const char *name,
@@ -1407,32 +1460,8 @@ found:
continue;
}
- // Bind all necessary textures and add them to the prelude
- for (int t = 0; t < TEXUNIT_VIDEO_NUM; t++) {
- const char *bind_name = hook->bind_tex[t];
- struct img_tex bind_tex;
-
- if (!bind_name)
- continue;
-
- // This is a special name that means "currently hooked texture"
- if (strcmp(bind_name, "HOOKED") == 0) {
- int id = pass_bind(p, tex);
- hook_prelude(p, "HOOKED", id, tex);
- hook_prelude(p, name, id, tex);
- continue;
- }
-
- if (!saved_tex_find(p, bind_name, &bind_tex)) {
- // Clean up texture bindings and move on to the next hook
- MP_DBG(p, "Skipping hook on %s due to no texture named %s.\n",
- name, bind_name);
- p->pass_tex_num -= t;
- goto next_hook;
- }
-
- hook_prelude(p, bind_name, pass_bind(p, bind_tex), bind_tex);
- }
+ if (!pass_hook_setup_binds(p, name, tex, hook))
+ continue;
// Run the actual hook. This generates a series of GLSL shader
// instructions sufficient for drawing the hook's output
@@ -1471,8 +1500,6 @@ found:
}
saved_tex_store(p, store_name, saved_tex);
-
-next_hook: ;
}
return tex;
@@ -1825,13 +1852,15 @@ static bool img_tex_equiv(struct img_tex a, struct img_tex b)
gl_transform_eq(a.transform, b.transform);
}
-static void pass_add_hook(struct gl_video *p, struct tex_hook hook)
+static bool add_hook(struct gl_video *p, struct tex_hook hook)
{
if (p->tex_hook_num < SHADER_MAX_PASSES) {
p->tex_hooks[p->tex_hook_num++] = hook;
+ return true;
} else {
MP_ERR(p, "Too many passes! Limit is %d.\n", SHADER_MAX_PASSES);
talloc_free(hook.priv);
+ return false;
}
}
@@ -1896,7 +1925,7 @@ static bool szexp_lookup(void *priv, struct bstr var, float size[2])
static bool user_hook_cond(struct gl_video *p, struct img_tex tex, void *priv)
{
- struct gl_user_shader *shader = priv;
+ struct gl_user_shader_hook *shader = priv;
assert(shader);
float res = false;
@@ -1907,7 +1936,7 @@ static bool user_hook_cond(struct gl_video *p, struct img_tex tex, void *priv)
static void user_hook(struct gl_video *p, struct img_tex tex,
struct gl_transform *trans, void *priv)
{
- struct gl_user_shader *shader = priv;
+ struct gl_user_shader_hook *shader = priv;
assert(shader);
pass_describe(p, "user shader: %.*s (%s)", BSTR_P(shader->pass_desc),
@@ -1928,33 +1957,91 @@ static void user_hook(struct gl_video *p, struct img_tex tex,
gl_transform_trans(shader->offset, trans);
}
-static void pass_hook_user_shaders(struct gl_video *p, char **shaders)
+static bool add_user_hook(void *priv, struct gl_user_shader_hook hook)
+{
+ struct gl_video *p = priv;
+ struct gl_user_shader_hook *copy = talloc_ptrtype(p, copy);
+ *copy = hook;
+
+ struct tex_hook texhook = {
+ .save_tex = bstrdup0(copy, hook.save_tex),
+ .components = hook.components,
+ .hook = user_hook,
+ .cond = user_hook_cond,
+ .priv = copy,
+ };
+
+ for (int h = 0; h < SHADER_MAX_HOOKS; h++)
+ texhook.hook_tex[h] = bstrdup0(copy, hook.hook_tex[h]);
+ for (int h = 0; h < SHADER_MAX_BINDS; h++)
+ texhook.bind_tex[h] = bstrdup0(copy, hook.bind_tex[h]);
+
+ return add_hook(p, texhook);
+}
+
+static bool add_user_tex(void *priv, struct gl_user_shader_tex tex)
+{
+ struct gl_video *p = priv;
+ GL *gl = p->gl;
+
+ if (p->user_tex_num == SHADER_MAX_PASSES) {
+ MP_ERR(p, "Too many textures! Limit is %d.\n", SHADER_MAX_PASSES);
+ goto err;
+ }
+
+ const struct gl_format *format = gl_find_format(gl, tex.mpgl_type,
+ tex.gl_filter == GL_LINEAR ? F_TF : 0, tex.bytes, tex.components);
+
+ if (!format) {
+ MP_ERR(p, "Could not satisfy format requirements for user "
+ "shader texture '%.*s'!\n", BSTR_P(tex.name));
+ goto err;
+ }
+
+ GLenum type = format->type,
+ ifmt = format->internal_format,
+ fmt = format->format;
+
+ GLenum tgt = tex.gl_target;
+ gl->GenTextures(1, &tex.gl_tex);
+ gl->BindTexture(tgt, tex.gl_tex);
+ gl->PixelStorei(GL_UNPACK_ALIGNMENT, 1);
+
+ if (tgt == GL_TEXTURE_3D) {
+ gl->TexImage3D(tgt, 0, ifmt, tex.w, tex.h, tex.d, 0, fmt, type, tex.texdata);
+ gl->TexParameteri(tgt, GL_TEXTURE_WRAP_S, tex.gl_border);
+ gl->TexParameteri(tgt, GL_TEXTURE_WRAP_T, tex.gl_border);
+ gl->TexParameteri(tgt, GL_TEXTURE_WRAP_R, tex.gl_border);
+ } else if (tgt == GL_TEXTURE_2D) {
+ gl->TexImage2D(tgt, 0, ifmt, tex.w, tex.h, 0, fmt, type, tex.texdata);
+ gl->TexParameteri(tgt, GL_TEXTURE_WRAP_S, tex.gl_border);
+ gl->TexParameteri(tgt, GL_TEXTURE_WRAP_T, tex.gl_border);
+ } else {
+ gl->TexImage1D(tgt, 0, ifmt, tex.w, 0, fmt, type, tex.texdata);
+ gl->TexParameteri(tgt, GL_TEXTURE_WRAP_S, tex.gl_border);
+ }
+ talloc_free(tex.texdata);
+
+ gl->TexParameteri(tgt, GL_TEXTURE_MIN_FILTER, tex.gl_filter);
+ gl->TexParameteri(tgt, GL_TEXTURE_MAG_FILTER, tex.gl_filter);
+ gl->BindTexture(tgt, 0);
+
+ p->user_textures[p->user_tex_num++] = tex;
+ return true;
+
+err:
+ talloc_free(tex.texdata);
+ return false;
+}
+
+static void load_user_shaders(struct gl_video *p, char **shaders)
{
if (!shaders)
return;
for (int n = 0; shaders[n] != NULL; n++) {
struct bstr file = load_cached_file(p, shaders[n]);
- struct gl_user_shader out;
- while (parse_user_shader_pass(p->log, &file, &out)) {
- struct gl_user_shader *hook = talloc_ptrtype(p, hook);
- *hook = out;
-
- struct tex_hook texhook = {
- .save_tex = bstrdup0(hook, hook->save_tex),
- .components = hook->components,
- .hook = user_hook,
- .cond = user_hook_cond,
- .priv = hook,
- };
-
- for (int h = 0; h < SHADER_MAX_HOOKS; h++)
- texhook.hook_tex[h] = bstrdup0(hook, hook->hook_tex[h]);
- for (int h = 0; h < SHADER_MAX_BINDS; h++)
- texhook.bind_tex[h] = bstrdup0(hook, hook->bind_tex[h]);
-
- pass_add_hook(p, texhook);
- }
+ parse_user_shader(p->log, file, p, add_user_hook, add_user_tex);
}
}
@@ -1963,7 +2050,7 @@ static void gl_video_setup_hooks(struct gl_video *p)
gl_video_reset_hooks(p);
if (p->opts.deband) {
- pass_add_hook(p, (struct tex_hook) {
+ add_hook(p, (struct tex_hook) {
.hook_tex = {"LUMA", "CHROMA", "RGB", "XYZ"},
.bind_tex = {"HOOKED"},
.hook = deband_hook,
@@ -1971,14 +2058,14 @@ static void gl_video_setup_hooks(struct gl_video *p)
}
if (p->opts.unsharp != 0.0) {
- pass_add_hook(p, (struct tex_hook) {
+ add_hook(p, (struct tex_hook) {
.hook_tex = {"MAIN"},
.bind_tex = {"HOOKED"},
.hook = unsharp_hook,
});
}
- pass_hook_user_shaders(p, p->opts.user_shaders);
+ load_user_shaders(p, p->opts.user_shaders);
}
// sample from video textures, set "color" variable to yuv value