diff --git a/meson.build b/meson.build index 46cf6d058820236d8ff29c98706be5760b44ec6d..ab5c46eacd4daea479640645c058e824fc9d601f 100644 --- a/meson.build +++ b/meson.build @@ -2,7 +2,7 @@ project('libplacebo', ['c', 'cpp'], license: 'LGPL2.1+', default_options: ['c_std=c99', 'cpp_std=c++11', 'warning_level=2'], meson_version: '>=0.49', - version: '2.64.0', + version: '2.65.0', ) # Version number diff --git a/src/include/libplacebo/shaders.h b/src/include/libplacebo/shaders.h index d5d3e98765ff8759bf3d02edd14db723d82ab44b..cb2d80b11a65f32853f4a9bf65acefcce32f524e 100644 --- a/src/include/libplacebo/shaders.h +++ b/src/include/libplacebo/shaders.h @@ -118,6 +118,9 @@ uint64_t pl_shader_signature(const struct pl_shader *sh); enum pl_shader_sig { PL_SHADER_SIG_NONE = 0, // no input / void output PL_SHADER_SIG_COLOR, // vec4 color (normalized so that 1.0 is the ref white) + + // The following are only valid as input signatures: + PL_SHADER_SIG_SAMPLER2D, // (sampler2D src_tex, vec2 tex_coord) pair }; // Represents a finalized shader fragment. This is not a complete shader, but a diff --git a/src/include/libplacebo/shaders/sampling.h b/src/include/libplacebo/shaders/sampling.h index bc208cdf3b5c0444af33e293930ee829e37e2e7a..8a3a41f82158fa0056f0ee00b070b68294a155cc 100644 --- a/src/include/libplacebo/shaders/sampling.h +++ b/src/include/libplacebo/shaders/sampling.h @@ -29,11 +29,26 @@ // Common parameters for sampling operations struct pl_sample_src { + // There are two mutually exclusive ways of providing the source to sample + // from: + // + // 1. Provide the texture and sampled region directly. This generates + // a shader with input signature `PL_SHADER_SIG_NONE`, which binds the + // texture as a descriptor (and the coordinates as a vertex attribute) const struct pl_tex *tex; // texture to sample struct pl_rect2df rect; // sub-rect to sample from (optional) - int components; // number of components to sample (optional) - int new_w, new_h; // dimensions of the resulting output (optional) - float scale; // factor to multiply into sampled signal (optional) + + // 2. Have the shader take it as an argument. Doing this requires + // specifying the missing metadata of the texture backing the sampler, so + // that the shader generation can generate the correct code. In particular, + // the important fields include the texture dimensions and the sample mode. + struct pl_tex_params sampler_params; + float sampled_w, sampled_h; // dimensions of the sampled region (optional) + + // Common metadata for both sampler input types: + int components; // number of components to sample (optional) + int new_w, new_h; // dimensions of the resulting output (optional) + float scale; // factor to multiply into sampled signal (optional) }; struct pl_deband_params { diff --git a/src/shaders.c b/src/shaders.c index 2584c49f42652cabfc6926dca7ee15e396c63cb7..6a4137179169d8d8451d5763a7d8a76dce781c56 100644 --- a/src/shaders.c +++ b/src/shaders.c @@ -345,16 +345,17 @@ void pl_shader_append_bstr(struct pl_shader *sh, enum pl_shader_buf buf, bstr_xappend(sh, &sh->buffers[buf], str); } +static const char *insigs[] = { + [PL_SHADER_SIG_NONE] = "", + [PL_SHADER_SIG_COLOR] = "vec4 color", + [PL_SHADER_SIG_SAMPLER2D] = "sampler2D src_tex, vec2 tex_coord", +}; + static const char *outsigs[] = { [PL_SHADER_SIG_NONE] = "void", [PL_SHADER_SIG_COLOR] = "vec4", }; -static const char *insigs[] = { - [PL_SHADER_SIG_NONE] = "", - [PL_SHADER_SIG_COLOR] = "vec4 color", -}; - static const char *retvals[] = { [PL_SHADER_SIG_NONE] = "", [PL_SHADER_SIG_COLOR] = "return color;", @@ -874,7 +875,8 @@ next_dim: ; // `continue` out of the inner loop pos_macros[i] = sh_lut_pos(sh, sizes[i]); GLSLH("#define %s(pos) (%s(%s, %s(\\\n", - name, sh_tex_fn(sh, lut->weights.tex), tex, types[texdim - 1]); + name, sh_tex_fn(sh, lut->weights.tex->params), + tex, types[texdim - 1]); for (int i = 0; i < texdim; i++) { char sep = i == 0 ? ' ' : ','; @@ -953,15 +955,3 @@ const char *sh_bvec(const struct pl_shader *sh, int dims) pl_assert(dims > 0 && dims < PL_ARRAY_SIZE(bvecs)); return sh_glsl(sh).version >= 130 ? bvecs[dims] : vecs[dims]; } - -const char *sh_tex_fn(const struct pl_shader *sh, const struct pl_tex *tex) -{ - static const char *suffixed[] = { - [1] = "texture1D", - [2] = "texture2D", - [3] = "texture3D", - }; - - int dims = pl_tex_params_dimension(tex->params); - return sh_glsl(sh).version >= 130 ? "texture" : suffixed[dims]; -} diff --git a/src/shaders.h b/src/shaders.h index 3c7b3d20d4e59174ef9673b64f20a6c1b38ff451..ee969f0e70951ef6e8d6efe59255102520041cb0 100644 --- a/src/shaders.h +++ b/src/shaders.h @@ -209,4 +209,15 @@ const char *sh_bvec(const struct pl_shader *sh, int dims); // Returns the appropriate `texture`-equivalent function for the shader and // given texture. -const char *sh_tex_fn(const struct pl_shader *sh, const struct pl_tex *tex); +static inline const char *sh_tex_fn(const struct pl_shader *sh, + const struct pl_tex_params params) +{ + static const char *suffixed[] = { + [1] = "texture1D", + [2] = "texture2D", + [3] = "texture3D", + }; + + int dims = pl_tex_params_dimension(params); + return sh_glsl(sh).version >= 130 ? "texture" : suffixed[dims]; +} diff --git a/src/shaders/custom.c b/src/shaders/custom.c index 33056c01d6035089c9590b67dceffd89c98c2b78..2c5a26fda8fc7ae38005c4de001f7bf724d887a0 100644 --- a/src/shaders/custom.c +++ b/src/shaders/custom.c @@ -778,7 +778,7 @@ static bool bind_pass_tex(struct pl_shader *sh, struct bstr name, // Sampling function boilerplate GLSLH("#define %.*s_tex(pos) (%f * vec4(%s(%s, pos))) \n", - BSTR_P(name), scale, sh_tex_fn(sh, ptex->tex), id); + BSTR_P(name), scale, sh_tex_fn(sh, ptex->tex->params), id); GLSLH("#define %.*s_texOff(off) (%.*s_tex(%s + %s * vec2(off))) \n", BSTR_P(name), BSTR_P(name), pos, pt); @@ -936,7 +936,7 @@ static struct pl_hook_res hook_hook(void *priv, const struct pl_hook_params *par GLSLH("#define %.*s %s \n", BSTR_P(texname), id); GLSLH("#define %.*s_tex(pos) (%s(%s, pos)) \n", BSTR_P(texname), - sh_tex_fn(sh, p->lut_textures[j].tex), id); + sh_tex_fn(sh, p->lut_textures[j].tex->params), id); goto next_bind; } } diff --git a/src/shaders/sampling.c b/src/shaders/sampling.c index 9625e2e9f06fb1440d4a32c93b2255311d87e948..63dd68f5b31700ed600d4fbb26a0a94f9d2d26f8 100644 --- a/src/shaders/sampling.c +++ b/src/shaders/sampling.c @@ -25,20 +25,38 @@ const struct pl_deband_params pl_deband_default_params = { .grain = 6.0, }; +static inline struct pl_tex_params src_params(const struct pl_sample_src *src) +{ + return src->tex ? src->tex->params : src->sampler_params; +} + // Helper function to compute the src/dst sizes and upscaling ratios static bool setup_src(struct pl_shader *sh, const struct pl_sample_src *src, ident_t *src_tex, ident_t *pos, ident_t *size, ident_t *pt, float *ratio_x, float *ratio_y, int *components, float *scale, bool resizeable, const char **fn) { - pl_assert(pl_tex_params_dimension(src->tex->params) == 2); - float src_w = pl_rect_w(src->rect); - float src_h = pl_rect_h(src->rect); - src_w = PL_DEF(src_w, src->tex->params.w); - src_h = PL_DEF(src_h, src->tex->params.h); + pl_assert(pl_tex_params_dimension(src_params(src)) == 2); + + enum pl_shader_sig sig; + float src_w, src_h; + if (src->tex) { + sig = PL_SHADER_SIG_NONE; + src_w = pl_rect_w(src->rect); + src_h = pl_rect_h(src->rect); + } else { + sig = PL_SHADER_SIG_SAMPLER2D; + src_w = src->sampled_w; + src_h = src->sampled_h; + } + + src_w = PL_DEF(src_w, src_params(src).w); + src_h = PL_DEF(src_h, src_params(src).h); + pl_assert(src_w && src_h); int out_w = PL_DEF(src->new_w, roundf(fabs(src_w))); int out_h = PL_DEF(src->new_h, roundf(fabs(src_h))); + pl_assert(out_w && out_h); if (ratio_x) *ratio_x = out_w / fabs(src_w); @@ -48,33 +66,60 @@ static bool setup_src(struct pl_shader *sh, const struct pl_sample_src *src, *scale = PL_DEF(src->scale, 1.0); if (components) { - const struct pl_fmt *fmt = src->tex->params.format; - *components = PL_DEF(src->components, fmt->num_components); + int tex_comps = src_params(src).format->num_components; + *components = PL_DEF(src->components, tex_comps); } if (resizeable) out_w = out_h = 0; - if (!sh_require(sh, PL_SHADER_SIG_NONE, out_w, out_h)) + if (!sh_require(sh, sig, out_w, out_h)) return false; - struct pl_rect2df rect = { - .x0 = src->rect.x0, - .y0 = src->rect.y0, - .x1 = src->rect.x0 + src_w, - .y1 = src->rect.y0 + src_h, - }; + if (src->tex) { + struct pl_rect2df rect = { + .x0 = src->rect.x0, + .y0 = src->rect.y0, + .x1 = src->rect.x0 + src_w, + .y1 = src->rect.y0 + src_h, + }; + + if (fn) + *fn = sh_tex_fn(sh, src->tex->params); + + *src_tex = sh_bind(sh, src->tex, "src_tex", &rect, pos, size, pt); + } else { + int tex_w = src->sampler_params.w, + tex_h = src->sampler_params.h; + pl_assert(tex_w && tex_h); + + if (size) { + *size = sh_var(sh, (struct pl_shader_var) { + .var = pl_var_vec2("tex_size"), + .data = &(float[2]) { tex_w, tex_h }, + }); + } - if (fn) - *fn = sh_tex_fn(sh, src->tex); + if (pt) { + *pt = sh_var(sh, (struct pl_shader_var) { + .var = pl_var_vec2("tex_pt"), + .data = &(float[2]) { 1.0 / tex_w, 1.0 / tex_h }, + }); + } + + if (fn) + *fn = sh_tex_fn(sh, src->sampler_params); + + *src_tex = "src_tex"; + *pos = "tex_coord"; + } - *src_tex = sh_bind(sh, src->tex, "src_tex", &rect, pos, size, pt); return true; } void pl_shader_deband(struct pl_shader *sh, const struct pl_sample_src *src, const struct pl_deband_params *params) { - if (src->tex->params.sample_mode != PL_TEX_SAMPLE_LINEAR) { + if (src_params(src).sample_mode != PL_TEX_SAMPLE_LINEAR) { SH_FAIL(sh, "Debanding requires sample_mode = PL_TEX_SAMPLE_LINEAR!"); return; } @@ -174,7 +219,7 @@ static void bicubic_calcweights(struct pl_shader *sh, const char *t, const char bool pl_shader_sample_bicubic(struct pl_shader *sh, const struct pl_sample_src *src) { - if (src->tex->params.sample_mode != PL_TEX_SAMPLE_LINEAR) { + if (src_params(src).sample_mode != PL_TEX_SAMPLE_LINEAR) { SH_FAIL(sh, "Trying to use fast bicubic sampling from a texture without " "PL_TEX_SAMPLE_LINEAR"); return false; @@ -313,16 +358,28 @@ bool pl_shader_sample_polar(struct pl_shader *sh, } const struct pl_gpu *gpu = SH_GPU(sh); - const struct pl_tex *tex = src->tex; - pl_assert(gpu && tex); + pl_assert(gpu); bool has_compute = gpu->caps & PL_GPU_CAP_COMPUTE && !params->no_compute; + if (!src->tex && has_compute) { + // FIXME: Could maybe solve this by communicating the wbase from + // invocation 0 to the rest of the workgroup using shmem, which would + // also allow us to avoid the use of the hacky %s_map below. + PL_WARN(sh, "Combining pl_shader_sample_polar with the sampler2D " + "interface prevents the use of compute shaders, which is a " + "potentially massive performance hit. If you're sure you want " + "this, set `params.no_compute` to suppress this warning."); + has_compute = false; + } + bool flipped = src->rect.x0 > src->rect.x1 || src->rect.y0 > src->rect.y1; if (flipped && has_compute) { + // FIXME: I'm sure this case could actually be supported with some + // extra math in the positional calculations, should implement it PL_WARN(sh, "Trying to use a flipped src.rect with polar sampling! " "This prevents the use of compute shaders, which is a " "potentially massive performance hit. If you're really sure you " - "want this, set params.no_compute to suppress this warning."); + "want this, set `params.no_compute` to suppress this warning."); has_compute = false; } @@ -514,18 +571,17 @@ bool pl_shader_sample_ortho(struct pl_shader *sh, int pass, } const struct pl_gpu *gpu = SH_GPU(sh); - const struct pl_tex *tex = src->tex; - pl_assert(gpu && tex); + pl_assert(gpu); struct pl_sample_src srcfix = *src; switch (pass) { case PL_SEP_VERT: srcfix.rect.x0 = 0; - srcfix.rect.x1 = srcfix.new_w = tex->params.w; + srcfix.rect.x1 = srcfix.new_w = src_params(src).w; break; case PL_SEP_HORIZ: srcfix.rect.y0 = 0; - srcfix.rect.y1 = srcfix.new_h = tex->params.h; + srcfix.rect.y1 = srcfix.new_h = src_params(src).h; break; case PL_SEP_PASSES: default: diff --git a/src/tests/dummy.c b/src/tests/dummy.c index 48ae80afb42d9f6cb9d5d69195de749b7bfd2d5b..1b84a204078660fb71d7299c810893b8517dda15 100644 --- a/src/tests/dummy.c +++ b/src/tests/dummy.c @@ -48,6 +48,16 @@ int main() printf("lut[%d] = %f\n", i, data[i]); } + // Try out generation of the sampler2D interface + src.sampler_params = dummy->params; + src.tex = NULL; + + pl_shader_reset(sh, &(struct pl_shader_params) { .gpu = gpu }); + REQUIRE(pl_shader_sample_polar(sh, &src, &filter_params)); + REQUIRE((res = pl_shader_finalize(sh))); + REQUIRE(res->input == PL_SHADER_SIG_SAMPLER2D); + printf("generated sampler2D shader:\n\n%s\n", res->glsl); + pl_shader_free(&sh); pl_shader_obj_destroy(&lut); pl_tex_destroy(gpu, &dummy);