Commit 729e4aee authored by Niklas Haas's avatar Niklas Haas

shaders: refactor shader creation API

- I wanted to add new parameters, and rather than having the function
  signature grow out of control, I decided to switch to the same
  `params`-style as the rest of libplacebo, for future forwards
  compatibility.

- We need to expose the identifier, because users of raw shaders might
  want to combine multiple shaders into the same GLSL program.

This kills off the hacky `_ex` functions and uses a params struct. Also
organizes some fields around, for reasons.
parent 44b4f779
......@@ -2,7 +2,7 @@ project('libplacebo', ['c', 'cpp'],
license: 'LGPL2.1+',
default_options: ['c_std=c99'],
meson_version: '>=0.47',
version: '1.19.0',
version: '1.20.0',
)
# Version number
......
......@@ -117,15 +117,19 @@ void pl_dispatch_destroy(struct pl_dispatch **ptr)
struct pl_shader *pl_dispatch_begin_ex(struct pl_dispatch *dp, bool unique)
{
uint8_t ident = unique ? dp->current_ident++ : 0;
struct pl_shader_params params = {
.id = unique ? dp->current_ident++ : 0,
.gpu = dp->gpu,
.index = dp->current_index,
};
struct pl_shader *sh;
if (TARRAY_POP(dp->shaders, dp->num_shaders, &sh)) {
pl_shader_reset_ex(sh, dp->current_index, ident);
pl_shader_reset(sh, &params);
return sh;
}
return pl_shader_alloc_ex(dp->ctx, dp->gpu, dp->current_index, ident);
return pl_shader_alloc(dp->ctx, &params);
}
void pl_dispatch_reset_frame(struct pl_dispatch *dp)
......
......@@ -28,24 +28,41 @@
struct pl_shader;
// Creates a new, blank, mutable pl_shader object. The resulting pl_shader s
// implicitly destroyed when the pl_context is destroyed.
struct pl_shader_params {
// The `id` represents an abstract identifier for the shader, to avoid
// collisions with other shaders being used as part of the same larger,
// overarching shader. This is relevant for users which want to combine
// multiple `pl_shader` objects together, in which case all `pl_shader`
// objects should have a unique `id`.
uint8_t id;
// If `gpu` is non-NULL, then this `gpu` will be used to create objects
// such as textures and buffers, or check for required capabilities, for
// operations which depend on either of those. This is fully optional, i.e.
// these GLSL primitives are designed to be used without a dependency on
// `gpu` wherever possible - however, some features may not work, and will
// be disabled even if requested.
const struct pl_gpu *gpu;
// The `index` represents an abstract frame index, which shaders may use
// internally to do things like temporal dithering or seeding PRNGs. If the
// user does not care about temporal dithering/debanding, or wants
// determinstic rendering, this may safely be left as 0. Otherwise, it
// should be incremented by 1 on successive frames.
uint8_t index;
};
// Creates a new, blank, mutable pl_shader object.
//
// If `gpu` is non-NULL, then this `gpu` will be used to create objects such as
// textures and buffers, or check for required capabilities, for operations
// which depend on either of those. This is fully optional, i.e. these GLSL
// primitives are designed to be used without a dependency on `gpu` wherever
// possible - however, some features may not work, and will be disabled even
// if requested.
// Note: The lifetime of this pl_shader is tied to `pl_context`. It's not
// needed to `pl_shader_destroy` before `pl_context_destroy`, except where
// users want to release associated memory allocations.
//
// The `index` represents an abstract frame index, which shaders may use
// internally to do things like temporal dithering or seeding PRNGs. If the
// user does not care about temporal dithering/debanding, or wants determinstic
// rendering, this may safely be left as 0. Otherwise, it should be incremented
// by 1 on successive frames.
// Note: Rather than allocating and destroying many shaders, users are
// encouraged to reuse them (using `pl_shader_reset`) for efficiency.
struct pl_shader *pl_shader_alloc(struct pl_context *ctx,
const struct pl_gpu *gpu,
uint8_t index);
const struct pl_shader_params *params);
// Frees a pl_shader and all resources associated with it.
void pl_shader_free(struct pl_shader **sh);
......@@ -53,7 +70,7 @@ void pl_shader_free(struct pl_shader **sh);
// Resets a pl_shader to a blank slate, without releasing internal memory.
// If you're going to be re-generating shaders often, this function will let
// you skip the re-allocation overhead.
void pl_shader_reset(struct pl_shader *sh, uint8_t index);
void pl_shader_reset(struct pl_shader *sh, const struct pl_shader_params *params);
// Returns whether or not a shader is in a "failed" state. Trying to modify a
// shader in illegal ways (e.g. signature mismatch) will result in the shader
......@@ -102,6 +119,9 @@ enum pl_shader_sig {
// collection of raw shader text together with description of the input
// attributes, variables and vertexes it expects to be available.
struct pl_shader_res {
// A copy of the parameters used to create the shader.
struct pl_shader_params params;
// The shader text, as literal GLSL. This will always be a function
// definition, such that the the function with the indicated name and
// signature may be called by the user.
......
......@@ -22,28 +22,21 @@
#include "context.h"
#include "shaders.h"
struct pl_shader *pl_shader_alloc_ex(struct pl_context *ctx,
const struct pl_gpu *gpu,
uint8_t index, uint8_t ident)
struct pl_shader *pl_shader_alloc(struct pl_context *ctx,
const struct pl_shader_params *params)
{
pl_assert(ctx);
struct pl_shader *sh = talloc_ptrtype(ctx, sh);
*sh = (struct pl_shader) {
.ctx = ctx,
.gpu = gpu,
.mutable = true,
.tmp = talloc_ref_new(ctx),
.ident = ident,
.index = index,
};
return sh;
}
if (params)
sh->res.params = *params;
struct pl_shader *pl_shader_alloc(struct pl_context *ctx,
const struct pl_gpu *gpu, uint8_t index)
{
return pl_shader_alloc_ex(ctx, gpu, index, 0);
return sh;
}
void pl_shader_free(struct pl_shader **psh)
......@@ -56,15 +49,12 @@ void pl_shader_free(struct pl_shader **psh)
TA_FREEP(psh);
}
void pl_shader_reset_ex(struct pl_shader *sh, uint8_t index, uint8_t ident)
void pl_shader_reset(struct pl_shader *sh, const struct pl_shader_params *params)
{
struct pl_shader new = {
.ctx = sh->ctx,
.gpu = sh->gpu,
.tmp = talloc_ref_new(sh->ctx),
.mutable = true,
.ident = ident,
.index = index,
// Preserve array allocations
.res = {
......@@ -74,6 +64,9 @@ void pl_shader_reset_ex(struct pl_shader *sh, uint8_t index, uint8_t ident)
},
};
if (params)
new.res.params = *params;
// Preserve buffer allocations
for (int i = 0; i < PL_ARRAY_SIZE(new.buffers); i++)
new.buffers[i] = (struct bstr) { .start = sh->buffers[i].start };
......@@ -82,11 +75,6 @@ void pl_shader_reset_ex(struct pl_shader *sh, uint8_t index, uint8_t ident)
*sh = new;
}
void pl_shader_reset(struct pl_shader *sh, uint8_t index)
{
pl_shader_reset_ex(sh, index, 0);
}
bool pl_shader_is_failed(const struct pl_shader *sh)
{
return sh->failed;
......@@ -98,12 +86,13 @@ bool sh_try_compute(struct pl_shader *sh, int bw, int bh, bool flex, size_t mem)
int *sh_bw = &sh->res.compute_group_size[0];
int *sh_bh = &sh->res.compute_group_size[1];
if (!sh->gpu || !(sh->gpu->caps & PL_GPU_CAP_COMPUTE)) {
const struct pl_gpu *gpu = SH_GPU(sh);
if (!gpu || !(gpu->caps & PL_GPU_CAP_COMPUTE)) {
PL_TRACE(sh, "Disabling compute shader due to missing PL_GPU_CAP_COMPUTE");
return false;
}
if (sh->res.compute_shmem + mem > sh->gpu->limits.max_shmem_size) {
if (sh->res.compute_shmem + mem > gpu->limits.max_shmem_size) {
PL_TRACE(sh, "Disabling compute shader due to insufficient shmem");
return false;
}
......@@ -171,7 +160,7 @@ uint64_t pl_shader_signature(const struct pl_shader *sh)
ident_t sh_fresh(struct pl_shader *sh, const char *name)
{
return talloc_asprintf(sh->tmp, "_%s_%d_%u", PL_DEF(name, "var"),
sh->fresh++, sh->ident);
sh->fresh++, SH_PARAMS(sh).id);
}
ident_t sh_var(struct pl_shader *sh, struct pl_shader_var sv)
......@@ -207,12 +196,13 @@ ident_t sh_desc(struct pl_shader *sh, struct pl_shader_desc sd)
ident_t sh_attr_vec2(struct pl_shader *sh, const char *name,
const struct pl_rect2df *rc)
{
if (!sh->gpu) {
const struct pl_gpu *gpu = SH_GPU(sh);
if (!gpu) {
SH_FAIL(sh, "Failed adding vertex attr '%s': No GPU available!", name);
return NULL;
}
const struct pl_fmt *fmt = pl_find_vertex_fmt(sh->gpu, PL_FMT_FLOAT, 2);
const struct pl_fmt *fmt = pl_find_vertex_fmt(gpu, PL_FMT_FLOAT, 2);
if (!fmt) {
SH_FAIL(sh, "Failed adding vertex attr '%s': no vertex fmt!", name);
return NULL;
......@@ -229,7 +219,7 @@ ident_t sh_attr_vec2(struct pl_shader *sh, const char *name,
struct pl_shader_va va = {
.attr = {
.name = sh_fresh(sh, name),
.fmt = pl_find_vertex_fmt(sh->gpu, PL_FMT_FLOAT, 2),
.fmt = pl_find_vertex_fmt(gpu, PL_FMT_FLOAT, 2),
},
.data = { &data[0], &data[2], &data[4], &data[6] },
};
......@@ -242,7 +232,7 @@ ident_t sh_bind(struct pl_shader *sh, const struct pl_tex *tex,
const char *name, const struct pl_rect2df *rect,
ident_t *out_pos, ident_t *out_size, ident_t *out_pt)
{
if (!sh->gpu) {
if (!SH_GPU(sh)) {
SH_FAIL(sh, "Failed binding texture '%s': No GPU available!", name);
return NULL;
}
......@@ -358,7 +348,7 @@ ident_t sh_subpass(struct pl_shader *sh, const struct pl_shader *sub)
{
pl_assert(sh->mutable);
if (sh->ident == sub->ident) {
if (SH_PARAMS(sh).id == SH_PARAMS(sub).id) {
SH_FAIL(sh, "Failed merging shaders: conflicting identifiers!");
return NULL;
}
......@@ -526,7 +516,7 @@ void *sh_require_obj(struct pl_shader *sh, struct pl_shader_obj **ptr,
return NULL;
struct pl_shader_obj *obj = *ptr;
if (obj && obj->gpu != sh->gpu) {
if (obj && obj->gpu != SH_GPU(sh)) {
SH_FAIL(sh, "Passed pl_shader_obj belongs to different GPU!");
return NULL;
}
......@@ -539,7 +529,7 @@ void *sh_require_obj(struct pl_shader *sh, struct pl_shader_obj **ptr,
if (!obj) {
obj = talloc_zero(NULL, struct pl_shader_obj);
obj->gpu = sh->gpu;
obj->gpu = SH_GPU(sh);
obj->type = type;
obj->priv = talloc_zero_size(obj, priv_size);
obj->uninit = uninit;
......@@ -572,7 +562,7 @@ ident_t sh_prng(struct pl_shader *sh, bool temporal, ident_t *p_state)
const char *seed = "0.0";
if (temporal) {
float seedval = modff(phi * sh->index, &(float){0});
float seedval = modff(phi * SH_PARAMS(sh).index, &(float){0});
seed = sh_var(sh, (struct pl_shader_var) {
.var = pl_var_float("seed"),
.data = &seedval,
......@@ -645,7 +635,7 @@ ident_t sh_lut(struct pl_shader *sh, struct pl_shader_obj **obj,
int comps, bool update, void *priv,
void (*fill)(void *priv, float *data, int w, int h, int d))
{
const struct pl_gpu *gpu = sh->gpu;
const struct pl_gpu *gpu = SH_GPU(sh);
float *tmp = NULL;
ident_t ret = NULL;
......
......@@ -37,22 +37,16 @@ enum pl_shader_buf {
};
struct pl_shader {
// Read-only fields
struct pl_context *ctx;
const struct pl_gpu *gpu;
// Internal state
struct pl_shader_res res; // for accumulating vertex_attribs etc.
struct ta_ref *tmp;
bool failed;
bool mutable;
int output_w;
int output_h;
struct pl_shader_res res; // for accumulating vertex_attribs etc.
struct bstr buffers[SH_BUF_COUNT];
bool is_compute;
bool flexible_work_groups;
uint8_t ident;
uint8_t index;
int fresh;
};
......@@ -61,17 +55,6 @@ struct pl_shader {
PL_ERR(sh, __VA_ARGS__); \
} while (0)
// Like `pl_shader_alloc`, but also has an extra `uint8_t ident` which can be
// used to namespace shaders in order to allow safely merging together multiple
// shaders using `sh_subpass`. This is not exposed publicly since there's no
// reasonable public API for `sh_subpass`.
struct pl_shader *pl_shader_alloc_ex(struct pl_context *ctx,
const struct pl_gpu *gpu,
uint8_t index, uint8_t ident);
// Like `pl_shader_reset`, but also has this extra `uint8_t ident`.
void pl_shader_reset_ex(struct pl_shader *sh, uint8_t index, uint8_t ident);
// Attempt enabling compute shaders for this pass, if possible
bool sh_try_compute(struct pl_shader *sh, int bw, int bh, bool flex, size_t mem);
......@@ -206,8 +189,12 @@ ident_t sh_lut(struct pl_shader *sh, struct pl_shader_obj **obj,
// this function is with mix(), which only accepts bvec in GLSL 130+.
const char *sh_bvec(const struct pl_shader *sh, int dims);
// Helper functions for convenience
#define SH_PARAMS(sh) ((sh)->res.params)
#define SH_GPU(sh) (SH_PARAMS(sh).gpu)
// Returns the GLSL version, defaulting to 130 if no information is known
static inline int sh_glsl_ver(const struct pl_shader *sh)
{
return sh->gpu ? sh->gpu->glsl.version : 130;
return SH_GPU(sh) ? SH_GPU(sh)->glsl.version : 130;
}
......@@ -704,22 +704,22 @@ void pl_shader_av1_grain(struct pl_shader *sh,
// SSBO layout in this case
struct pl_var grain_y = pl_var_float("grain_y");
grain_y.dim_a = GRAIN_WIDTH_LUT * GRAIN_HEIGHT_LUT;
ok &= sh_buf_desc_append(obj->tmp, sh->gpu, &obj->desc,
ok &= sh_buf_desc_append(obj->tmp, SH_GPU(sh), &obj->desc,
&obj->layout_y, grain_y);
struct pl_var grain_cb = pl_var_float("grain_cb");
grain_cb.dim_a = chroma_lut_size;
ok &= sh_buf_desc_append(obj->tmp, sh->gpu, &obj->desc,
ok &= sh_buf_desc_append(obj->tmp, SH_GPU(sh), &obj->desc,
&obj->layout_cb, grain_cb);
struct pl_var grain_cr = pl_var_float("grain_cr");
grain_cr.dim_a = chroma_lut_size;
ok &= sh_buf_desc_append(obj->tmp, sh->gpu, &obj->desc,
ok &= sh_buf_desc_append(obj->tmp, SH_GPU(sh), &obj->desc,
&obj->layout_cr, grain_cr);
struct pl_var offsets = pl_var_uint("offsets");
offsets.dim_a = offsets_x * offsets_y;
ok &= sh_buf_desc_append(obj->tmp, sh->gpu, &obj->desc,
ok &= sh_buf_desc_append(obj->tmp, SH_GPU(sh), &obj->desc,
&obj->layout_off, offsets);
if (!ok) {
......@@ -751,7 +751,7 @@ void pl_shader_av1_grain(struct pl_shader *sh,
if (last_buf && last_buf->params.size > ssbo_params.size)
ssbo_params.size = last_buf->params.size;
ssbo = pl_buf_pool_get(sh->gpu, &obj->ssbos, &ssbo_params);
ssbo = pl_buf_pool_get(SH_GPU(sh), &obj->ssbos, &ssbo_params);
if (!ssbo) {
PL_ERR(sh, "Failed creating/getting SSBO buffer for AV1 grain!");
return;
......@@ -766,7 +766,7 @@ void pl_shader_av1_grain(struct pl_shader *sh,
if (has_luma) {
pl_assert(obj->layout_y.stride == sizeof(float));
pl_buf_write(sh->gpu, ssbo, obj->layout_y.offset, obj->grain,
pl_buf_write(SH_GPU(sh), ssbo, obj->layout_y.offset, obj->grain,
sizeof(obj->grain));
}
......@@ -774,19 +774,19 @@ void pl_shader_av1_grain(struct pl_shader *sh,
generate_grain_uv(&obj->grain[0][0], obj->grain_tmp_uv,
obj->grain_tmp_y, PL_CHANNEL_CB, params);
pl_assert(obj->layout_cb.stride == sizeof(float));
pl_buf_write(sh->gpu, ssbo, obj->layout_cb.offset, obj->grain,
pl_buf_write(SH_GPU(sh), ssbo, obj->layout_cb.offset, obj->grain,
sizeof(float) * chroma_lut_size);
generate_grain_uv(&obj->grain[0][0], obj->grain_tmp_uv,
obj->grain_tmp_y, PL_CHANNEL_CR, params);
pl_assert(obj->layout_cr.stride == sizeof(float));
pl_buf_write(sh->gpu, ssbo, obj->layout_cr.offset, obj->grain,
pl_buf_write(SH_GPU(sh), ssbo, obj->layout_cr.offset, obj->grain,
sizeof(float) * chroma_lut_size);
}
generate_offsets(obj->offsets, offsets_x, offsets_y, params);
pl_assert(obj->layout_off.stride == sizeof(uint32_t));
pl_buf_write(sh->gpu, ssbo, obj->layout_off.offset, obj->offsets,
pl_buf_write(SH_GPU(sh), ssbo, obj->layout_off.offset, obj->offsets,
obj->num_offsets * obj->layout_off.stride);
obj->params = *params;
......
......@@ -554,7 +554,7 @@ bool pl_shader_detect_peak(struct pl_shader *sh,
if (!obj)
return false;
const struct pl_gpu *gpu = sh->gpu;
const struct pl_gpu *gpu = SH_GPU(sh);
obj->gpu = gpu;
if (!obj->buf) {
......@@ -1072,7 +1072,7 @@ done: ;
GLSL("vec2 pos = fract(gl_FragCoord.xy * 1.0/%d.0);\n", size);
if (params->temporal) {
int phase = sh->index % 8;
int phase = SH_PARAMS(sh).index % 8;
float r = phase * (M_PI / 2); // rotate
float m = phase < 4 ? 1 : -1; // mirror
float mat[2][2] = {
......
......@@ -305,7 +305,7 @@ bool pl_shader_sample_polar(struct pl_shader *sh,
return false;
}
const struct pl_gpu *gpu = sh->gpu;
const struct pl_gpu *gpu = SH_GPU(sh);
const struct pl_tex *tex = src->tex;
pl_assert(gpu && tex);
......@@ -510,7 +510,7 @@ bool pl_shader_sample_ortho(struct pl_shader *sh, int pass,
return false;
}
const struct pl_gpu *gpu = sh->gpu;
const struct pl_gpu *gpu = SH_GPU(sh);
const struct pl_tex *tex = src->tex;
pl_assert(gpu && tex);
......
......@@ -27,7 +27,8 @@ int main()
.lut = &lut,
};
struct pl_shader *sh = pl_shader_alloc(ctx, gpu, 0);
struct pl_shader *sh;
sh = pl_shader_alloc(ctx, &(struct pl_shader_params) { .gpu = gpu });
REQUIRE(pl_shader_sample_polar(sh, &src, &filter_params));
const struct pl_shader_res *res = pl_shader_finalize(sh);
REQUIRE(res);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment