vo_opengl: abstract hook texture access behind macro

This macro takes care of rotation, swizzling, integer conversion and
normalization automatically. I found the performance impact to be
nonexistant for superxbr and debanding, although rotation *did* have an
impact due to the extra matrix multiplication. (So it gets skipped where
possible)

All of the internal hooks have been rewritten to use this new mechanism,
and the prescaler hooks have finally been separated from each other.
This also means the prescale FBO kludge is no longer required.

This fixes image corruption for image formats like 0bgr, and also fixes
prescaling under rotation. (As well as other user hooks that have
orientation-dependent access)

The "raw" attributes (tex, tex_pos, pixel_size) are still un-rotated, in
case something needs them, but ideally the hooks should be rewritten to
use the new API as much as possible. The hooked texture has been renamed
from just NAME to NAME_raw to make script authors notice the change (and
also deemphasize direct texture access).

This is also a step towards getting rid of the use_integer pass.
This commit is contained in:
Niklas Haas 2016-05-14 07:05:04 +02:00
parent dfc7b59909
commit 362015cd77
No known key found for this signature in database
GPG Key ID: 9A09076581B27402
8 changed files with 138 additions and 137 deletions

View File

@ -763,14 +763,26 @@ Available video output drivers are:
definitions to that shader pass, where NAME is the name of the bound
texture:
sampler NAME
The bound texture itself.
vec4 NAME_tex(vec2 pos)
The sampling function to use to access the texture at a certain
spot (in texture coordinate space, range [0,1]). This takes care
of any necessary normalization conversions.
vec4 NAME_texOff(vec2 offset)
Sample the texture at a certain offset in pixels. This works like
NAME_tex but additionally takes care of necessary rotations, so
that sampling at e.g. vec2(-1,0) is always one pixel to the left.
vec2 NAME_pos
The local texture coordinate of that texture, range [0,1].
vec2 NAME_size
The (rotated) size in pixels of the texture.
mat2 NAME_rot
The rotation matrix associated with this texture. (Rotates
pixel space to texture coordinates)
vec2 NAME_pt
The (unrotated) size of a single pixel, range [0,1].
sampler NAME_raw
The raw bound texture itself. The use of this should be
avoided unless absolutely necessary.
In addition, the global uniforms described in ``post-shaders`` are
also available.

View File

@ -96,9 +96,9 @@ const float* get_nnedi3_weights(const struct nnedi3_opts *conf, int *size)
return (const float*)(nnedi3_weights + offset * 4);
}
void pass_nnedi3(GL *gl, struct gl_shader_cache *sc, int planes, int tex_num,
int step, float tex_mul, const struct nnedi3_opts *conf,
struct gl_transform *transform, GLenum tex_target)
void pass_nnedi3(GL *gl, struct gl_shader_cache *sc, int step,
const struct nnedi3_opts *conf,
struct gl_transform *transform)
{
assert(0 <= step && step < 2);
@ -131,23 +131,23 @@ void pass_nnedi3(GL *gl, struct gl_shader_cache *sc, int planes, int tex_num,
GLSLH(#pragma optionNV(fastprecision on))
}
GLSLHF("float nnedi3(%s tex, vec2 pos, vec2 tex_size, vec2 pixel_size, int plane, float tex_mul) {\n", mp_sampler_type(tex_target));
GLSLHF("float nnedi3() {\n");
if (step == 0) {
*transform = (struct gl_transform){{{1.0,0.0}, {0.0,2.0}}, {0.0,-0.5}};
GLSLH(if (fract(pos.y * tex_size.y) < 0.5)
return texture(tex, pos + vec2(0, 0.25) * pixel_size)[plane] * tex_mul;)
GLSLH(if ((transpose(HOOKED_rot) * fract(HOOKED_pos * HOOKED_size)).y < 0.5)
return HOOKED_texOff(vec2(0, 0.25)).x;)
GLSLHF("#define GET(i, j) "
"(texture(tex, pos+vec2((i)-(%f),(j)-(%f)+0.25) * pixel_size)[plane]*tex_mul)\n",
"HOOKED_texOff(vec2((i)-(%f),(j)-(%f)+0.25)).x\n",
width / 2.0 - 1, (height - 1) / 2.0);
} else {
*transform = (struct gl_transform){{{2.0,0.0}, {0.0,1.0}}, {-0.5,0.0}};
GLSLH(if (fract(pos.x * tex_size.x) < 0.5)
return texture(tex, pos + vec2(0.25, 0) * pixel_size)[plane] * tex_mul;)
GLSLH(if (fract(HOOKED_pos.x * HOOKED_size.x) < 0.5)
return HOOKED_texOff(vec2(0.25, 0)).x;)
GLSLHF("#define GET(i, j) "
"(texture(tex, pos+vec2((j)-(%f)+0.25,(i)-(%f)) * pixel_size)[plane]*tex_mul)\n",
"HOOKED_texOff(vec2((j)-(%f)+0.25,(i)-(%f))).x\n",
(height - 1) / 2.0, width / 2.0 - 1);
}
@ -226,12 +226,7 @@ void pass_nnedi3(GL *gl, struct gl_shader_cache *sc, int planes, int tex_num,
GLSLHF("}\n"); // nnedi3
GLSL(color = vec4(1.0);)
for (int i = 0; i < planes; i++) {
GLSLF("color[%d] = nnedi3(texture%d, texcoord%d, texture_size%d, pixel_size%d, %d, %f);\n",
i, tex_num, tex_num, tex_num, tex_num, i, tex_mul);
}
GLSL(color.x = nnedi3();)
}
#else
@ -244,9 +239,9 @@ const float* get_nnedi3_weights(const struct nnedi3_opts *conf, int *size)
return NULL;
}
void pass_nnedi3(GL *gl, struct gl_shader_cache *sc, int planes, int tex_num,
int step, float tex_mul, const struct nnedi3_opts *conf,
struct gl_transform *transform, GLenum tex_target)
void pass_nnedi3(GL *gl, struct gl_shader_cache *sc, int step,
const struct nnedi3_opts *conf,
struct gl_transform *transform)
{
}

View File

@ -38,8 +38,8 @@ extern const struct m_sub_options nnedi3_conf;
const float* get_nnedi3_weights(const struct nnedi3_opts *conf, int *size);
void pass_nnedi3(GL *gl, struct gl_shader_cache *sc, int planes, int tex_num,
int step, float tex_mul, const struct nnedi3_opts *conf,
struct gl_transform *transform, GLenum tex_target);
void pass_nnedi3(GL *gl, struct gl_shader_cache *sc, int step,
const struct nnedi3_opts *conf,
struct gl_transform *transform);
#endif

View File

@ -174,7 +174,7 @@ static void superxbr_step_h(struct gl_shader_cache *sc,
}
// Pick the two best directions and mix them together
GLSLHF("float str = smoothstep(0.0, %f + 1e-6, abs(tex_mul*d_edge));\n",
GLSLHF("float str = smoothstep(0.0, %f + 1e-6, abs(d_edge));\n",
conf->edge_strength);
GLSLH(res = mix(mix(d2c, d1c, step(0.0, d_edge)), \
mix(hc, vc, step(0.0, o_edge)), 1.0 - str);)
@ -187,7 +187,7 @@ static void superxbr_step_h(struct gl_shader_cache *sc,
GLSLHF("} // step\n");
}
void pass_superxbr(struct gl_shader_cache *sc, int id, int step, float tex_mul,
void pass_superxbr(struct gl_shader_cache *sc, int step,
const struct superxbr_opts *conf,
struct gl_transform *transform)
{
@ -196,49 +196,48 @@ void pass_superxbr(struct gl_shader_cache *sc, int id, int step, float tex_mul,
assert(0 <= step && step < 2);
GLSLF("// superxbr (step %d)\n", step);
GLSLHF("#define tex texture%d\n", id);
GLSLHF("#define tex_size texture_size%d\n", id);
GLSLHF("#define tex_mul %f\n", tex_mul);
GLSLHF("#define pt pixel_size%d\n", id);
// We use a sub-function in the header so we can return early
GLSLHF("float superxbr(vec2 pos) {\n");
GLSLHF("float superxbr() {\n");
GLSLH(float i[4*4];)
GLSLH(float res;)
GLSLH(#define i(x,y) i[(x)*4+(y)])
if (step == 0) {
*transform = (struct gl_transform){{{2.0,0.0}, {0.0,2.0}}, {-0.5,-0.5}};
GLSLH(vec2 dir = fract(pos * tex_size) - 0.5;)
GLSLH(vec2 dir = fract(HOOKED_pos * HOOKED_size) - 0.5;)
GLSLH(dir = transpose(HOOKED_rot) * dir;)
// Optimization: Discard (skip drawing) unused pixels, except those
// at the edge.
GLSLH(vec2 dist = tex_size * min(pos, vec2(1.0) - pos);)
GLSLH(vec2 dist = HOOKED_size * min(HOOKED_pos, vec2(1.0) - HOOKED_pos);)
GLSLH(if (dir.x * dir.y < 0.0 && dist.x > 1.0 && dist.y > 1.0)
return 0.0;)
GLSLH(if (dir.x < 0.0 || dir.y < 0.0 || dist.x < 1.0 || dist.y < 1.0)
return texture(tex, pos - pt * dir).x;)
return HOOKED_texOff(-dir).x;)
// Load the input samples
GLSLH(for (int x = 0; x < 4; x++))
GLSLH(for (int y = 0; y < 4; y++))
GLSLH(i(x,y) = texture(tex, pos + pt * vec2(x-1.25, y-1.25)).x;)
GLSLH(i(x,y) = HOOKED_texOff(vec2(x-1.25, y-1.25)).x;)
} else {
*transform = (struct gl_transform){{{1.0,0.0}, {0.0,1.0}}, {0.0,0.0}};
GLSLH(vec2 dir = fract(pos * tex_size / 2.0) - 0.5;)
// This is the second pass, so it will never be rotated
GLSLH(vec2 dir = fract(HOOKED_pos * HOOKED_size / 2.0) - 0.5;)
GLSLH(if (dir.x * dir.y > 0.0)
return texture(tex, pos).x;)
return HOOKED_texOff(0).x;)
GLSLH(for (int x = 0; x < 4; x++))
GLSLH(for (int y = 0; y < 4; y++))
GLSLH(i(x,y) = texture(tex, pos + pt * vec2(x+y-3, y-x)).x;)
GLSLH(i(x,y) = HOOKED_texOff(vec2(x+y-3, y-x)).x;)
}
superxbr_step_h(sc, conf, &params[step]);
GLSLH(return res;)
GLSLHF("}\n");
GLSLF("color.x = tex_mul * superxbr(texcoord%d);\n", id);
GLSL(color.x = superxbr();)
}

View File

@ -24,7 +24,7 @@
extern const struct superxbr_opts superxbr_opts_def;
extern const struct m_sub_options superxbr_conf;
void pass_superxbr(struct gl_shader_cache *sc, int id, int step, float tex_mul,
void pass_superxbr(struct gl_shader_cache *sc, int step,
const struct superxbr_opts *conf,
struct gl_transform *transform);

View File

@ -217,7 +217,6 @@ struct gl_video {
struct fbotex blend_subs_fbo;
struct fbotex output_fbo;
struct fbosurface surfaces[FBOSURFACES_MAX];
struct fbotex prescale_fbo[MAX_PRESCALE_PASSES];
int surface_idx;
int surface_now;
@ -602,9 +601,6 @@ static void uninit_rendering(struct gl_video *p)
fbotex_uninit(&p->indirect_fbo);
fbotex_uninit(&p->blend_subs_fbo);
for (int pass = 0; pass < MAX_PRESCALE_PASSES; pass++)
fbotex_uninit(&p->prescale_fbo[pass]);
for (int n = 0; n < FBOSURFACES_MAX; n++)
fbotex_uninit(&p->surfaces[n].fbotex);
@ -910,9 +906,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) {
@ -926,6 +924,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]});
@ -1045,12 +1044,29 @@ static void uninit_scaler(struct gl_video *p, struct scaler *scaler)
scaler->initialized = false;
}
static void hook_prelude(struct gl_video *p, const char *name, int id)
static void hook_prelude(struct gl_video *p, const char *name, int id,
struct img_tex tex)
{
GLSLHF("#define %s texture%d\n", name, id);
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");
// Since the extra matrix multiplication impacts performance,
// skip it unless the texture was actually rotated
if (gl_transform_eq(tex.transform, identity_trans)) {
GLSLHF("#define %s_texOff(off) %s_tex(%s_pos + %s_pt * vec2(off))\n",
name, name, name, name);
} else {
GLSLHF("#define %s_texOff(off) "
"%s_tex(%s_pos + %s_rot * vec2(off)/%s_size)\n",
name, name, name, name, name);
}
}
static bool saved_tex_find(struct gl_video *p, const char *name,
@ -1116,8 +1132,8 @@ static struct img_tex pass_hook(struct gl_video *p, const char *name,
// 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);
hook_prelude(p, name, id);
hook_prelude(p, "HOOKED", id, tex);
hook_prelude(p, name, id, tex);
continue;
}
@ -1130,7 +1146,7 @@ static struct img_tex pass_hook(struct gl_video *p, const char *name,
return tex;
}
hook_prelude(p, bind_name, pass_bind(p, bind_tex));
hook_prelude(p, bind_name, pass_bind(p, bind_tex), bind_tex);
}
// Run the actual hook. This generates a series of GLSL shader
@ -1502,33 +1518,6 @@ static void upload_nnedi3_weights(struct gl_video *p)
}
}
// Applies a single pass of the prescaler, and accumulates the offset in
// pass_transform.
static void pass_prescale_luma_step(struct gl_video *p, struct img_tex tex,
struct gl_transform *step_transform,
int step)
{
int id = pass_bind(p, tex);
int planes = tex.components;
switch(p->opts.prescale_luma) {
case 1:
assert(planes == 1);
pass_superxbr(p->sc, id, step, tex.multiplier,
p->opts.superxbr_opts, step_transform);
break;
case 2:
upload_nnedi3_weights(p);
pass_nnedi3(p->gl, p->sc, planes, id, step, tex.multiplier,
p->opts.nnedi3_opts, step_transform, tex.gl_target);
break;
default:
abort();
}
skip_unused(p, planes);
}
// Returns true if two img_texs are semantically equivalent (same metadata)
static bool img_tex_equiv(struct img_tex a, struct img_tex b)
{
@ -1569,33 +1558,22 @@ static void pass_add_hooks(struct gl_video *p, struct tex_hook hook,
static void deband_hook(struct gl_video *p, struct img_tex tex,
struct gl_transform *trans, void *priv)
{
// We could use the hook binding mechanism here but the existing code
// already assumes we just know an ID so just do this for simplicity
int id = pass_bind(p, tex);
pass_sample_deband(p->sc, p->opts.deband_opts, id, tex.multiplier,
tex.gl_target, &p->lfg);
skip_unused(p, tex.components);
pass_sample_deband(p->sc, p->opts.deband_opts, &p->lfg);
}
static void prescale_hook(struct gl_video *p, struct img_tex tex,
static void superxbr_hook(struct gl_video *p, struct img_tex tex,
struct gl_transform *trans, void *priv)
{
struct gl_transform step_trans = identity_trans;
pass_prescale_luma_step(p, tex, &step_trans, 0);
gl_transform_trans(step_trans, trans);
int step = (uintptr_t)priv;
pass_superxbr(p->sc, step, p->opts.superxbr_opts, trans);
}
// We render out an FBO *inside* this hook, which is normally quite
// unusual but here it allows us to work around the lack of real closures.
// Unfortunately it means we need to duplicate some work to compute the
// new FBO size
struct fbotex *fbo = priv;
int w = tex.w * (int)step_trans.m[0][0],
h = tex.h * (int)step_trans.m[1][1];
finish_pass_fbo(p, fbo, w, h, 0);
tex = img_tex_fbo(fbo, tex.type, tex.components);
pass_prescale_luma_step(p, tex, &step_trans, 1);
gl_transform_trans(step_trans, trans);
static void nnedi3_hook(struct gl_video *p, struct img_tex tex,
struct gl_transform *trans, void *priv)
{
int step = (uintptr_t)priv;
upload_nnedi3_weights(p);
pass_nnedi3(p->gl, p->sc, step, p->opts.nnedi3_opts, trans);
}
static void unsharp_hook(struct gl_video *p, struct img_tex tex,
@ -1617,7 +1595,7 @@ static void user_hook_old(struct gl_video *p, struct img_tex tex,
load_shader(p, bstr0(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);
GLSLF("color = %s(HOOKED_raw, HOOKED_pos, HOOKED_size);\n", fn_name);
}
// Returns 1.0 on failure to at least create a legal FBO
@ -1784,17 +1762,36 @@ static void pass_hook_user_shaders(struct gl_video *p, char **shaders)
static void gl_video_setup_hooks(struct gl_video *p)
{
if (p->opts.deband) {
pass_add_hooks(p, (struct tex_hook) {.hook = deband_hook},
pass_add_hooks(p, (struct tex_hook) {.hook = deband_hook,
.bind_tex = {"HOOKED"}},
HOOKS("LUMA", "CHROMA", "RGB", "XYZ"));
}
int prescale_passes = get_prescale_passes(p);
for (int i = 0; i < prescale_passes; i++) {
pass_add_hook(p, (struct tex_hook) {
.hook_tex = "LUMA",
.hook = prescale_hook,
.priv = &p->prescale_fbo[i],
});
if (p->opts.prescale_luma == 1) { // superxbr
for (int i = 0; i < prescale_passes; i++) {
for (int step = 0; step < 2; step++) {
pass_add_hook(p, (struct tex_hook) {
.hook_tex = "LUMA",
.bind_tex = {"HOOKED"},
.hook = superxbr_hook,
.priv = (void *)(uintptr_t)step,
});
}
}
}
if (p->opts.prescale_luma == 2) { // nnedi3
for (int i = 0; i < prescale_passes; i++) {
for (int step = 0; step < 2; step++) {
pass_add_hook(p, (struct tex_hook) {
.hook_tex = "LUMA",
.bind_tex = {"HOOKED"},
.hook = nnedi3_hook,
.priv = (void *)(uintptr_t)step,
});
}
}
}
if (p->opts.unsharp != 0.0) {

View File

@ -290,7 +290,7 @@ void pass_delinearize(struct gl_shader_cache *sc, enum mp_csp_trc trc)
// Wide usage friendly PRNG, shamelessly stolen from a GLSL tricks forum post.
// Obtain random numbers by calling rand(h), followed by h = permute(h) to
// update the state.
// update the state. Assumes the texture was hooked.
static void prng_init(struct gl_shader_cache *sc, AVLFG *lfg)
{
GLSLH(float mod289(float x) { return x - floor(x / 289.0) * 289.0; })
@ -298,7 +298,7 @@ static void prng_init(struct gl_shader_cache *sc, AVLFG *lfg)
GLSLH(float rand(float x) { return fract(x / 41.0); })
// Initialize the PRNG by hashing the position + a random uniform
GLSL(vec3 _m = vec3(pos, random) + vec3(1.0);)
GLSL(vec3 _m = vec3(HOOKED_pos, random) + vec3(1.0);)
GLSL(float h = permute(permute(permute(_m.x)+_m.y)+_m.z);)
gl_sc_uniform_f(sc, "random", (double)av_lfg_get(lfg) / UINT32_MAX);
}
@ -331,44 +331,40 @@ const struct m_sub_options deband_conf = {
.defaults = &deband_opts_def,
};
// Stochastically sample a debanded result from a given texture
// Stochastically sample a debanded result from a hooked texture.
void pass_sample_deband(struct gl_shader_cache *sc, struct deband_opts *opts,
int tex_num, float tex_mul, GLenum tex_target, AVLFG *lfg)
AVLFG *lfg)
{
// Set up common variables and initialize the PRNG
// Initialize the PRNG
GLSLF("{\n");
sampler_prelude(sc, tex_num);
prng_init(sc, lfg);
// Helper: Compute a stochastic approximation of the avg color around a
// pixel
GLSLHF("vec4 average(%s tex, vec2 pos, vec2 pt, float range, inout float h) {",
mp_sampler_type(tex_target));
GLSLHF("vec4 average(float range, inout float h) {\n");
// Compute a random rangle and distance
GLSLH(float dist = rand(h) * range; h = permute(h);)
GLSLH(float dir = rand(h) * 6.2831853; h = permute(h);)
GLSLHF("pt *= dist;\n");
GLSLH(vec2 o = vec2(cos(dir), sin(dir));)
GLSLH(vec2 o = dist * vec2(cos(dir), sin(dir));)
// Sample at quarter-turn intervals around the source pixel
GLSLH(vec4 ref[4];)
GLSLH(ref[0] = texture(tex, pos + pt * vec2( o.x, o.y));)
GLSLH(ref[1] = texture(tex, pos + pt * vec2(-o.y, o.x));)
GLSLH(ref[2] = texture(tex, pos + pt * vec2(-o.x, -o.y));)
GLSLH(ref[3] = texture(tex, pos + pt * vec2( o.y, -o.x));)
GLSLH(ref[0] = HOOKED_texOff(vec2( o.x, o.y));)
GLSLH(ref[1] = HOOKED_texOff(vec2(-o.y, o.x));)
GLSLH(ref[2] = HOOKED_texOff(vec2(-o.x, -o.y));)
GLSLH(ref[3] = HOOKED_texOff(vec2( o.y, -o.x));)
// Return the (normalized) average
GLSLHF("return %f * (ref[0] + ref[1] + ref[2] + ref[3])/4.0;\n", tex_mul);
GLSLH(})
GLSLH(return (ref[0] + ref[1] + ref[2] + ref[3])/4.0;)
GLSLHF("}\n");
// Sample the source pixel
GLSLF("color = %f * texture(tex, pos);\n", tex_mul);
GLSL(color = HOOKED_tex(HOOKED_pos);)
GLSLF("vec4 avg, diff;\n");
for (int i = 1; i <= opts->iterations; i++) {
// Sample the average pixel and use it instead of the original if
// the difference is below the given threshold
GLSLF("avg = average(tex, pos, pt, %f, h);\n", i * opts->range);
GLSLF("avg = average(%f, h);\n", i * opts->range);
GLSL(diff = abs(color - avg);)
GLSLF("color = mix(avg, color, greaterThan(diff, vec4(%f)));\n",
opts->threshold / (i * 16384.0));
@ -383,20 +379,21 @@ void pass_sample_deband(struct gl_shader_cache *sc, struct deband_opts *opts,
GLSLF("}\n");
}
// Assumes the texture was hooked
void pass_sample_unsharp(struct gl_shader_cache *sc, float param) {
GLSLF("// unsharp\n");
GLSLF("{\n");
GLSL(vec2 st1 = pt * 1.2;)
GLSL(vec4 p = texture(tex, pos);)
GLSL(vec4 sum1 = texture(tex, pos + st1 * vec2(+1, +1))
+ texture(tex, pos + st1 * vec2(+1, -1))
+ texture(tex, pos + st1 * vec2(-1, +1))
+ texture(tex, pos + st1 * vec2(-1, -1));)
GLSL(vec2 st2 = pt * 1.5;)
GLSL(vec4 sum2 = texture(tex, pos + st2 * vec2(+1, 0))
+ texture(tex, pos + st2 * vec2( 0, +1))
+ texture(tex, pos + st2 * vec2(-1, 0))
+ texture(tex, pos + st2 * vec2( 0, -1));)
GLSL(float st1 = 1.2;)
GLSL(vec4 p = HOOKED_tex(HOOKED_pos);)
GLSL(vec4 sum1 = HOOKED_texOff(st1 * vec2(+1, +1))
+ HOOKED_texOff(st1 * vec2(+1, -1))
+ HOOKED_texOff(st1 * vec2(-1, +1))
+ HOOKED_texOff(st1 * vec2(-1, -1));)
GLSL(float st2 = 1.5;)
GLSL(vec4 sum2 = HOOKED_texOff(st2 * vec2(+1, 0))
+ HOOKED_texOff(st2 * vec2( 0, +1))
+ HOOKED_texOff(st2 * vec2(-1, 0))
+ HOOKED_texOff(st2 * vec2( 0, -1));)
GLSL(vec4 t = p * 0.859375 + sum2 * -0.1171875 + sum1 * -0.09765625;)
GLSLF("color = p + t * %f;\n", param);
GLSLF("}\n");

View File

@ -38,8 +38,9 @@ void pass_sample_oversample(struct gl_shader_cache *sc, struct scaler *scaler,
void pass_linearize(struct gl_shader_cache *sc, enum mp_csp_trc trc);
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);
AVLFG *lfg);
void pass_sample_unsharp(struct gl_shader_cache *sc, float param);