summaryrefslogtreecommitdiffstats
path: root/src/include/libplacebo/shaders/custom.h
blob: a4eec695c4d6dd06d1092c11c7d3e7a8679ac790 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
/*
 * This file is part of libplacebo.
 *
 * libplacebo is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * libplacebo is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with libplacebo.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef LIBPLACEBO_SHADERS_CUSTOM_H_
#define LIBPLACEBO_SHADERS_CUSTOM_H_

#include <stdlib.h>

// Functions for writing custom shaders and hooking them into the `pl_renderer`
// pipeline, as well as compatibility functions for parsing shaders in mpv
// format.

#include <libplacebo/shaders.h>
#include <libplacebo/dispatch.h>
#include <libplacebo/colorspace.h>

PL_API_BEGIN

// Parameters describing custom shader text to be embedded into a `pl_shader`
// object. All of the strings are optional and can be left as NULL, but without
// a `body` in particular, the shader will do nothing useful on its own.
struct pl_custom_shader {
    // The prelude contains text such as extra #defines, #extension pragmas,
    // or other parts of the shader that must be placed at the very
    // beginning (before input layout declarations etc.)
    //
    // Note: #extension pragmas do not need to be emitted to enable support for
    // resource types already attached to the shader (e.g. SSBOs), compute
    // shaders, or GPU capabilities known to libplacebo (e.g. subgroups).
    const char *prelude;

    // The header contains text such as helper function definitions, extra
    // uniforms, shared memory variables or buffer descriptions.
    const char *header;

    // A friendly name for the shader. (Optional)
    const char *description;

    // The "primary" GLSL code. This will be effectively appended to the "main"
    // function. It lives in an environment given by the `input` signature, and
    // is expected to return results in a way given by the `output` signature.
    //
    // Note: In the case of PL_SHADER_SIG_COLOR, the output `vec4 color` is
    // allocated by `pl_shader_custom`, the user merely needs to assign to it.
    //
    // Note: For ease of development it can be useful to have the main logic
    // live inside a helper function defined as part of `header`, and specify
    // the `body` as a single line that simply calls the helper function.
    const char *body;
    enum pl_shader_sig input;
    enum pl_shader_sig output;

    // Extra descriptors, variables and vertex attributes to attach to the
    // resulting `pl_shader_res`.
    //
    // Note: The names inside these will possibly be replaced by fresh
    // identifiers internally, so users should avoid looking for exact string
    // matches for the given names inside the `pl_shader_res`.
    const struct pl_shader_desc *descriptors;
    int num_descriptors;
    const struct pl_shader_var *variables;
    int num_variables;
    const struct pl_shader_va *vertex_attribs;
    int num_vertex_attribs;
    const struct pl_shader_const *constants;
    int num_constants;

    // If true, this shader must be a compute shader. The desired workgroup
    // size and shared memory usage can be optionally specified, or 0 if no
    // specific work group size or shared memory size restrictions apply.
    //
    // See also: `pl_shader_res.compute_group_size`
    bool compute;
    size_t compute_shmem;
    int compute_group_size[2];

    // Fixes the output size requirements of the shader to exact dimensions.
    // Optional, if left as 0, means the shader can be dispatched at any size.
    int output_w;
    int output_h;
};

// Append custom shader code, including extra descriptors and variables, to an
// existing `pl_shader` object. Returns whether successful. This function may
// fail in the event that e.g. the custom shader requires compute shaders on
// an unsupported GPU, or exceeds the GPU's shared memory capabilities.
PL_API bool pl_shader_custom(pl_shader sh, const struct pl_custom_shader *params);

// Which "rendering stages" are available for user shader hooking purposes.
// Except where otherwise noted, all stages are "non-resizable", i.e. the
// shaders already have specific output size requirements.
enum pl_hook_stage {
    // Hook stages for the untouched planes, as made available by the source.
    // These are all resizable, i.e. there are no specific output stage
    // requirements.
    PL_HOOK_RGB_INPUT       = 1 << 0,
    PL_HOOK_LUMA_INPUT      = 1 << 1,
    PL_HOOK_CHROMA_INPUT    = 1 << 2,
    PL_HOOK_ALPHA_INPUT     = 1 << 3,
    PL_HOOK_XYZ_INPUT       = 1 << 4,

    // Hook stages for the scaled/aligned planes
    PL_HOOK_CHROMA_SCALED   = 1 << 5,
    PL_HOOK_ALPHA_SCALED    = 1 << 6,

    PL_HOOK_NATIVE          = 1 << 7,  // Combined image in its native color space
    PL_HOOK_RGB             = 1 << 8,  // After conversion to RGB (resizable)
    PL_HOOK_LINEAR          = 1 << 9,  // After linearization but before scaling
    PL_HOOK_SIGMOID         = 1 << 10, // After sigmoidization
    PL_HOOK_PRE_KERNEL      = 1 << 11, // Immediately before the main scaler kernel
    PL_HOOK_POST_KERNEL     = 1 << 12, // Immediately after the main scaler kernel
    PL_HOOK_SCALED          = 1 << 13, // After scaling, before color management
    PL_HOOK_PRE_OUTPUT      = 1 << 14, // After color management, before blending/rotation
    PL_HOOK_OUTPUT          = 1 << 15, // After blending/rotation, before dithering
};

// Returns true if a given hook stage is resizable
static inline bool pl_hook_stage_resizable(enum pl_hook_stage stage) {
    switch (stage) {
    case PL_HOOK_RGB_INPUT:
    case PL_HOOK_LUMA_INPUT:
    case PL_HOOK_CHROMA_INPUT:
    case PL_HOOK_ALPHA_INPUT:
    case PL_HOOK_XYZ_INPUT:
    case PL_HOOK_NATIVE:
    case PL_HOOK_RGB:
        return true;

    case PL_HOOK_CHROMA_SCALED:
    case PL_HOOK_ALPHA_SCALED:
    case PL_HOOK_LINEAR:
    case PL_HOOK_SIGMOID:
    case PL_HOOK_PRE_KERNEL:
    case PL_HOOK_POST_KERNEL:
    case PL_HOOK_SCALED:
    case PL_HOOK_PRE_OUTPUT:
    case PL_HOOK_OUTPUT:
        return false;
    }

    abort();
}

// The different forms of communicating image data between the renderer and
// the hooks
enum pl_hook_sig {
    PL_HOOK_SIG_NONE,   // No data is passed, no data is received/returned
    PL_HOOK_SIG_COLOR,  // `vec4 color` already pre-sampled in a `pl_shader`
    PL_HOOK_SIG_TEX,    // `pl_tex` containing the image data
    PL_HOOK_SIG_COUNT,
};

struct pl_hook_params {
    // GPU objects associated with the `pl_renderer`, which the user may
    // use for their own purposes.
    pl_gpu gpu;
    pl_dispatch dispatch;

    // Helper function to fetch a new temporary texture, using renderer-backed
    // storage. This is guaranteed to have sane image usage requirements and a
    // 16-bit or floating point format. The user does not need to free/destroy
    // this texture in any way. May return NULL.
    pl_tex (*get_tex)(void *priv, int width, int height);
    void *priv;

    // Which stage triggered the hook to run.
    enum pl_hook_stage stage;

    // For `PL_HOOK_SIG_COLOR`, this contains the existing shader object with
    // the color already pre-sampled into `vec4 color`. The user may modify
    // this as much as they want, as long as they don't dispatch/finalize/reset
    // it.
    //
    // Note that this shader might have specific output size requirements,
    // depending on the exact shader stage hooked by the user, and may already
    // be a compute shader.
    pl_shader sh;

    // For `PL_HOOK_SIG_TEX`, this contains the texture that the user should
    // sample from.
    //
    // Note: This texture object is owned by the renderer, and users must not
    // modify its contents. It will not be touched for the duration of a frame,
    // but the contents are lost in between frames.
    pl_tex tex;

    // The effective current rectangle of the image we're rendering in this
    // shader, i.e. the effective rect of the content we're interested in,
    // as a crop of either `sh` or `tex` (depending on the signature).
    //
    // Note: This is still set even for `PL_HOOK_SIG_NONE`!
    pl_rect2df rect;

    // The current effective colorspace and representation, of either the
    // pre-sampled color (in `sh`), or the contents of `tex`, respectively.
    //
    // Note: This is still set even for `PL_HOOK_SIG_NONE`!
    struct pl_color_repr repr;
    struct pl_color_space color;
    int components;

    // The representation and colorspace of the original image, for reference.
    const struct pl_color_repr *orig_repr;
    const struct pl_color_space *orig_color;

    // The (cropped) source and destination rectangles of the overall
    // rendering. These are functionallty equivalent to `image.crop` and
    // `target.crop`, respectively, but `src_rect` in particular may change as
    // a result of previous hooks being executed. (e.g. prescalers)
    pl_rect2df src_rect;
    pl_rect2d dst_rect;
};

struct pl_hook_res {
    // If true, the hook is assumed to have "failed" or errored in some way,
    // and all other fields are ignored.
    bool failed;

    // What type of output this hook is returning.
    // Note: If this is `PL_HOOK_SIG_NONE`, all other fields are ignored.
    enum pl_hook_sig output;

    // For `PL_HOOK_SIG_COLOR`, this *must* be set to a valid `pl_shader`
    // object containing the sampled color value (i.e. with an output signature
    // of `PL_SHADER_SIG_COLOR`), and *should* be allocated from the given
    // `pl_dispatch` object. Ignored otherwise.
    pl_shader sh;

    // For `PL_HOOK_SIG_TEX`, this *must* contain the texture object containing
    // the result of rendering the hook. This *should* be a texture allocated
    // using the given `get_tex` callback, to ensure the format and texture
    // usage flags are compatible with what the renderer expects.
    pl_tex tex;

    // For shaders that return some sort of output, this contains the
    // new/altered versions of the existing "current texture" metadata.
    struct pl_color_repr repr;
    struct pl_color_space color;
    int components;

    // This contains the new effective rect of the contents. This may be
    // different from the original `rect` for resizable passes. Ignored for
    // non-resizable passes.
    pl_rect2df rect;
};

enum pl_hook_par_mode {
    PL_HOOK_PAR_VARIABLE,   // normal shader variable
    PL_HOOK_PAR_DYNAMIC,    // dynamic shader variable, e.g. per-frame changing
    PL_HOOK_PAR_CONSTANT,   // fixed at compile time (e.g. for array sizes),
                            // must be scalar (non-vector/matrix)
    PL_HOOK_PAR_DEFINE,     // defined in the preprocessor, must be `int`
    PL_HOOK_PAR_MODE_COUNT,
};

typedef union pl_var_data {
    int i;
    unsigned u;
    float f;
} pl_var_data;

struct pl_hook_par {
    // Name as used in the shader.
    const char *name;

    // Type of this shader parameter, and how it's manifested in the shader.
    enum pl_var_type type;
    enum pl_hook_par_mode mode;

    // Human-readable explanation of this parameter. (Optional)
    const char *description;

    // Mutable data pointer to current value of variable.
    pl_var_data *data;

    // Default/initial value, and lower/upper bounds.
    pl_var_data initial;
    pl_var_data minimum;
    pl_var_data maximum;

    // Human-readable names for the variants of an integer option. This array
    // can be indexed directly by integer values, ranging from `minimum.i` to
    // `maximum.i`. May be NULL, in which case options are unnamed.
    const char * const *names;
};

// Struct describing a hook.
//
// Note: Users may freely create their own instances of this struct, there is
// nothing particularly special about `pl_mpv_user_shader_parse`.
struct pl_hook {
    enum pl_hook_stage stages;  // Which stages to hook on
    enum pl_hook_sig input;     // Which input signature this hook expects
    void *priv;                 // Arbitrary user context

    // Custom tunable shader parameters exported by this hook. These may be
    // updated at any time by the user, to influence the behavior of the hook.
    // Contents are arbitrary and subject to the method of hook construction.
    const struct pl_hook_par *parameters;
    int num_parameters;

    // Called at the beginning of passes, to reset/initialize the hook. (Optional)
    void (*reset)(void *priv);

    // The hook function itself. Called by the renderer at any of the indicated
    // hook stages. See `pl_hook_res` for more info on the return values.
    struct pl_hook_res (*hook)(void *priv, const struct pl_hook_params *params);

    // Unique signature identifying this hook, used to disable misbehaving hooks.
    // All hooks with the same signature will be disabled, should they fail to
    // execute during run-time.
    uint64_t signature;
};

// Compatibility layer with `mpv` user shaders. See the mpv man page for more
// information on the format. Will return `NULL` if the shader fails parsing.
//
// The resulting `pl_hook` objects should be destroyed with the corresponding
// destructor when no longer needed.
PL_API const struct pl_hook *
pl_mpv_user_shader_parse(pl_gpu gpu, const char *shader_text, size_t shader_len);

PL_API void pl_mpv_user_shader_destroy(const struct pl_hook **hook);

PL_API_END

#endif // LIBPLACEBO_SHADERS_CUSTOM_H_