summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNiklas Haas <git@nand.wakku.to>2016-04-19 20:45:40 +0200
committerNiklas Haas <git@nand.wakku.to>2016-05-15 20:42:02 +0200
commitd53142f9bac1d8b17d3eeed258625b6739d34487 (patch)
tree9ddcbce3e02145a72d56fd6413b26fe01d5cfcdb
parent070edd73000ca273289d1538c5513509b1b034b7 (diff)
downloadmpv-d53142f9bac1d8b17d3eeed258625b6739d34487.tar.bz2
mpv-d53142f9bac1d8b17d3eeed258625b6739d34487.tar.xz
vo_opengl: add optional hook points
These are "sequence points" where the image could be rendered out to an FBO, hooked, and re-loaded if any such hook exists. This is perfect for things like the current user shaders system, as well as optional effects like unsharp masking. Note that since we have to pick *some* FBO to store the optionally hooked texture, we just store it in an array indexed by an increasing counter. Since we only ever store as many as MAX_TEXTURE_HOOKS + all internal hook points entries, this is guaranteed to be enough space. This commit also removes some of the now unused FBOs.
-rw-r--r--video/out/opengl/video.c223
-rw-r--r--video/out/opengl/video_shaders.c5
-rw-r--r--video/out/opengl/video_shaders.h2
3 files changed, 140 insertions, 90 deletions
diff --git a/video/out/opengl/video.c b/video/out/opengl/video.c
index f7acabf0b6..e524bb5cf9 100644
--- a/video/out/opengl/video.c
+++ b/video/out/opengl/video.c
@@ -51,7 +51,7 @@
// Maximal number of saved textures (for user script purposes)
#define MAX_TEXTURE_HOOKS 16
-#define MAX_SAVED_TEXTURES 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.
@@ -209,20 +209,12 @@ 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];
int surface_idx;
@@ -253,9 +245,10 @@ struct gl_video {
// hooks and saved textures
struct saved_tex saved_tex[MAX_SAVED_TEXTURES];
int saved_tex_num;
- struct fbotex hook_fbos[MAX_TEXTURE_HOOKS];
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;
@@ -586,19 +579,12 @@ static void uninit_rendering(struct gl_video *p)
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++)
fbotex_uninit(&p->prescale_fbo[pass]);
@@ -606,6 +592,9 @@ static void uninit_rendering(struct gl_video *p)
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]);
+
gl_video_reset_surfaces(p);
}
@@ -993,6 +982,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++)
@@ -1110,10 +1127,13 @@ static struct img_tex pass_hook(struct gl_video *p, const char *name,
gl_transform_rect(hook_off, &sz);
int w = lroundf(fabs(sz.x1 - sz.x0));
int h = lroundf(fabs(sz.y1 - sz.y0));
- finish_pass_fbo(p, &p->hook_fbos[i], w, h, 0);
+
+ assert(p->hook_fbo_num < MAX_SAVED_TEXTURES);
+ struct fbotex *fbo = &p->hook_fbos[p->hook_fbo_num++];
+ finish_pass_fbo(p, fbo, w, h, 0);
const char *store_name = hook->save_tex ? hook->save_tex : name;
- struct img_tex saved_tex = img_tex_fbo(&p->hook_fbos[i], tex.type, comps);
+ struct img_tex saved_tex = img_tex_fbo(fbo, tex.type, comps);
// If the texture we're saving overwrites the "current" texture, also
// update the tex parameter so that the future loop cycles will use the
@@ -1136,6 +1156,37 @@ static struct img_tex pass_hook(struct gl_video *p, const char *name,
return tex;
}
+// This can be used at any time in the middle of rendering to specify an
+// optional hook point, which if triggered will render out to a new FBO and
+// load the result back into vec4 color. Offsets applied by the hooks are
+// accumulated in tex_trans, and the FBO is dimensioned according
+// to p->texture_w/h
+static void pass_opt_hook_point(struct gl_video *p, const char *name,
+ struct gl_transform *tex_trans)
+{
+ if (!name)
+ return;
+
+ int i;
+ for (i = 0; i < p->tex_hook_num; i++) {
+ if (strcmp(p->tex_hooks[i].hook_tex, name) == 0)
+ break;
+ }
+
+ if (i == p->tex_hook_num)
+ return;
+
+ assert(p->hook_fbo_num < MAX_SAVED_TEXTURES);
+ struct fbotex *fbo = &p->hook_fbos[p->hook_fbo_num++];
+
+ finish_pass_fbo(p, fbo, p->texture_w, p->texture_h, 0);
+ struct img_tex img = img_tex_fbo(fbo, PLANE_RGB, p->components);
+ img = pass_hook(p, name, img, tex_trans);
+ copy_img_tex(p, &(int){0}, img);
+ p->texture_w = img.w;
+ p->texture_h = img.h;
+ p->components = img.components;
+}
static void load_shader(struct gl_video *p, const char *body)
{
@@ -1159,34 +1210,6 @@ static const char *get_custom_shader_fn(struct gl_video *p, const char *body)
return "sample_pixel";
}
-// Applies an arbitrary number of shaders in sequence, using the given pair
-// of FBOs as intermediate buffers. Returns whether any shaders were applied.
-static bool apply_shaders(struct gl_video *p, char **shaders, int w, int h,
- struct fbotex textures[2])
-{
- if (!shaders)
- return false;
- bool success = false;
- int tex = 0;
- for (int n = 0; shaders[n]; n++) {
- const char *body = load_cached_file(p, shaders[n]);
- if (!body)
- continue;
- finish_pass_fbo(p, &textures[tex], w, h, 0);
- int id = pass_bind(p, img_tex_fbo(&textures[tex], PLANE_RGB,
- p->components));
- GLSLHF("#define pixel_size pixel_size%d\n", id);
- load_shader(p, body);
- const char *fn_name = get_custom_shader_fn(p, body);
- GLSLF("// custom shader\n");
- GLSLF("color = %s(texture%d, texcoord%d, texture_size%d);\n",
- fn_name, id, id, id);
- tex = (tex+1) % 2;
- success = true;
- }
- return success;
-}
-
// Semantic equality
static bool double_seq(double a, double b)
{
@@ -1496,34 +1519,6 @@ static bool img_tex_equiv(struct img_tex a, struct img_tex b)
strcmp(a.swizzle, b.swizzle) == 0;
}
-// 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 pass_add_hook(struct gl_video *p, struct tex_hook hook)
{
if (p->tex_hook_num < MAX_TEXTURE_HOOKS) {
@@ -1577,6 +1572,48 @@ static void prescale_hook(struct gl_video *p, struct img_tex tex,
gl_transform_trans(step_trans, trans);
}
+static void unsharp_hook(struct gl_video *p, struct img_tex tex,
+ struct gl_transform *trans, void *priv)
+{
+ GLSLF("#define tex HOOKED\n");
+ GLSLF("#define pos HOOKED_pos\n");
+ GLSLF("#define pt HOOKED_pt\n");
+ pass_sample_unsharp(p->sc, p->opts.unsharp);
+}
+
+static void user_shader_hook(struct gl_video *p, struct img_tex tex,
+ struct gl_transform *trans, void *priv)
+{
+ const char *body = priv;
+ assert(body);
+
+ GLSLHF("#define pixel_size HOOKED_pt\n");
+ load_shader(p, body);
+ const char *fn_name = get_custom_shader_fn(p, body);
+ GLSLF("// custom shader\n");
+ GLSLF("color = %s(HOOKED, HOOKED_pos, HOOKED_size);\n", fn_name);
+}
+
+static void pass_hook_user_shaders(struct gl_video *p, const char *name,
+ char **shaders)
+{
+ assert(name);
+ if (!shaders)
+ return;
+
+ for (int n = 0; shaders[n] != NULL; n++) {
+ const char *body = load_cached_file(p, shaders[n]);
+ if (body) {
+ pass_add_hook(p, (struct tex_hook) {
+ .hook_tex = name,
+ .bind_tex = {"HOOKED"},
+ .hook = user_shader_hook,
+ .priv = (void *)body,
+ });
+ }
+ }
+}
+
static void pass_setup_hooks(struct gl_video *p)
{
// Reset any existing hooks
@@ -1596,6 +1633,17 @@ static void pass_setup_hooks(struct gl_video *p)
.priv = &p->prescale_fbo[i],
});
}
+
+ if (p->opts.unsharp != 0.0) {
+ pass_add_hook(p, (struct tex_hook) {
+ .hook_tex = "MAIN",
+ .bind_tex = {"HOOKED"},
+ .hook = unsharp_hook,
+ });
+ }
+
+ pass_hook_user_shaders(p, "MAIN", p->opts.pre_shaders);
+ pass_hook_user_shaders(p, "SCALED", p->opts.post_shaders);
}
// sample from video textures, set "color" variable to yuv value
@@ -1940,8 +1988,10 @@ static void pass_scale_main(struct gl_video *p)
// Pre-conversion, like linear light/sigmoidization
GLSLF("// scaler pre-conversion\n");
- if (p->use_linear)
+ if (p->use_linear) {
pass_linearize(p->sc, p->image_params.gamma);
+ pass_opt_hook_point(p, "LINEAR", NULL);
+ }
bool use_sigmoid = p->use_linear && p->opts.sigmoid_upscaling && upscaling;
float sig_center, sig_slope, sig_offset, sig_scale;
@@ -1956,8 +2006,11 @@ static void pass_scale_main(struct gl_video *p)
sig_scale = 1.0/(1+expf(sig_slope * (sig_center-1))) - sig_offset;
GLSLF("color.rgb = %f - log(1.0/(color.rgb * %f + %f) - 1.0)/%f;\n",
sig_center, sig_scale, sig_offset, sig_slope);
+ pass_opt_hook_point(p, "SIGMOID", NULL);
}
+ pass_opt_hook_point(p, "PREKERNEL", NULL);
+
int vp_w = p->dst_rect.x1 - p->dst_rect.x0;
int vp_h = p->dst_rect.y1 - p->dst_rect.y0;
struct gl_transform transform;
@@ -1973,6 +2026,8 @@ static void pass_scale_main(struct gl_video *p)
p->texture_w = vp_w;
p->texture_h = vp_h;
+ pass_opt_hook_point(p, "POSTKERNEL", NULL);
+
GLSLF("// scaler post-conversion\n");
if (use_sigmoid) {
// Inverse of the transformation above
@@ -2215,6 +2270,7 @@ static void pass_render_frame(struct gl_video *p)
p->texture_offset = identity_trans;
p->components = 0;
p->saved_tex_num = 0;
+ p->hook_fbo_num = 0;
if (p->image_params.rotate % 180 == 90)
MPSWAP(int, p->texture_w, p->texture_h);
@@ -2226,7 +2282,9 @@ static void pass_render_frame(struct gl_video *p)
p->use_linear = p->opts.linear_scaling || p->opts.sigmoid_upscaling;
pass_read_video(p);
+ pass_opt_hook_point(p, "NATIVE", &p->texture_offset);
pass_convert_yuv(p);
+ pass_opt_hook_point(p, "MAINPRESUB", &p->texture_offset);
// For subtitles
double vpts = p->image.mpi->pts;
@@ -2246,14 +2304,7 @@ static void pass_render_frame(struct gl_video *p)
GLSL(color = texture(texture0, texcoord0);)
pass_read_fbo(p, &p->blend_subs_fbo);
}
-
- apply_shaders(p, p->opts.pre_shaders, p->texture_w, p->texture_h, p->pre_fbo);
-
- if (p->opts.unsharp != 0.0) {
- finish_pass_fbo(p, &p->unsharp_fbo, p->texture_w, p->texture_h, 0);
- int id = pass_read_fbo(p, &p->unsharp_fbo);
- pass_sample_unsharp(p->sc, id, p->opts.unsharp);
- }
+ pass_opt_hook_point(p, "MAIN", &p->texture_offset);
pass_scale_main(p);
@@ -2284,7 +2335,7 @@ static void pass_render_frame(struct gl_video *p)
pass_linearize(p->sc, p->image_params.gamma);
}
- apply_shaders(p, p->opts.post_shaders, p->texture_w, p->texture_h, p->post_fbo);
+ pass_opt_hook_point(p, "SCALED", NULL);
}
static void pass_draw_to_screen(struct gl_video *p, int fbo)
@@ -2310,6 +2361,8 @@ static void pass_draw_to_screen(struct gl_video *p, int fbo)
GLSL(color.rgb = mix(background, color.rgb, color.a);)
}
+ pass_opt_hook_point(p, "OUTPUT", NULL);
+
pass_dither(p);
finish_pass_direct(p, fbo, p->vp_w, p->vp_h, &p->dst_rect);
}
diff --git a/video/out/opengl/video_shaders.c b/video/out/opengl/video_shaders.c
index a31088edc9..8d3b1e0e40 100644
--- a/video/out/opengl/video_shaders.c
+++ b/video/out/opengl/video_shaders.c
@@ -383,11 +383,8 @@ void pass_sample_deband(struct gl_shader_cache *sc, struct deband_opts *opts,
GLSLF("}\n");
}
-void pass_sample_unsharp(struct gl_shader_cache *sc, int tex_num, float param)
-{
+void pass_sample_unsharp(struct gl_shader_cache *sc, float param) {
GLSLF("// unsharp\n");
- sampler_prelude(sc, tex_num);
-
GLSLF("{\n");
GLSL(vec2 st1 = pt * 1.2;)
GLSL(vec4 p = texture(tex, pos);)
diff --git a/video/out/opengl/video_shaders.h b/video/out/opengl/video_shaders.h
index e010fdba55..bbfeebb576 100644
--- a/video/out/opengl/video_shaders.h
+++ b/video/out/opengl/video_shaders.h
@@ -41,6 +41,6 @@ void pass_delinearize(struct gl_shader_cache *sc, enum mp_csp_trc trc);
void pass_sample_deband(struct gl_shader_cache *sc, struct deband_opts *opts,
int tex_num, float tex_mul, GLenum tex_target, AVLFG *lfg);
-void pass_sample_unsharp(struct gl_shader_cache *sc, int tex_num, float param);
+void pass_sample_unsharp(struct gl_shader_cache *sc, float param);
#endif