Loading...
Searching...
No Matches
isf.hpp
1#pragma once
2#include <ossia/detail/variant.hpp>
3
4#include <score_plugin_gfx_export.h>
5
6#include <array>
7#include <optional>
8#include <stdexcept>
9#include <string>
10#include <vector>
11
12namespace isf
13{
14// Exported: thrown from the plug-in, caught by type outside it (tests, editors).
15class SCORE_PLUGIN_GFX_TEST_EXPORT invalid_file : public std::runtime_error
16{
17public:
18 using std::runtime_error::runtime_error;
19};
20
21struct event_input
22{
23};
24
25struct bool_input
26{
27 using value_type = bool;
28 using has_default = std::true_type;
29 bool def{};
30};
31
32struct long_input
33{
34 using value_type = int64_t;
35 using has_minmax = std::true_type;
36 std::vector<ossia::variant<int64_t, double, std::string>> values;
37 std::vector<std::string> labels;
38
39 // Enum mode (values/labels non-empty): `def` is the INDEX into `values`.
40 // Numeric mode (values empty, min/max set): `def` is the default VALUE.
41 //
42 // The shader always receives the selected numeric VALUE from `values[i]`
43 // (for int/double entries) or the INDEX (for string-only VALUES, since
44 // GLSL can't consume strings). The renderer's UBO-init path resolves this
45 // index→value step so the initial shader state matches what arrives after
46 // any user interaction — see ISFNode.cpp / GeometryFilterNode.cpp long_input
47 // port visitors.
48 std::size_t def{};
49
50 // Numeric mode: when values/labels are empty and min/max are set,
51 // create an IntSpinBox instead of a ComboBox. In that mode `def` is the
52 // default value directly (not an index).
53 std::optional<int64_t> min;
54 std::optional<int64_t> max;
55};
56
57struct float_input
58{
59 using value_type = double;
60 using has_minmax = std::true_type;
61 double min{0.};
62 double max{0.};
63 double def{0.};
64};
65
66struct point2d_input
67{
68 using value_type = std::array<double, 2>;
69 using has_minmax = std::true_type;
70 std::optional<value_type> def{};
71 std::optional<value_type> min{};
72 std::optional<value_type> max{};
73};
74
75struct point3d_input
76{
77 using value_type = std::array<double, 3>;
78 using has_minmax = std::true_type;
79 std::optional<value_type> def{};
80 std::optional<value_type> min{};
81 std::optional<value_type> max{};
82
83 // AS_COLOR: hint to the UI that this vec3 should be shown as a color
84 // swatch (RGB picker) rather than three spin boxes. Useful for e.g.
85 // direction-as-RGB visualisations where editing components individually
86 // is awkward. Does not affect the GLSL type (still vec3).
87 bool as_color{false};
88};
89
90struct color_input
91{
92 using value_type = std::array<double, 4>;
93 using has_minmax = std::true_type;
94 std::optional<value_type> def{};
95 std::optional<value_type> min{};
96 std::optional<value_type> max{};
97};
98
99// Sampler configuration fields shared by image/texture/cubemap inputs.
100// All fields are optional: empty/unset string keeps the current default.
101// Address modes accept: "repeat", "clamp_to_edge"/"clamp", "mirror"/"mirrored_repeat",
102// "mirror_once"/"mirror_clamp_to_edge".
103// Filter modes accept: "nearest", "linear" (and "none" for mipmap_mode).
104// Border color accepts: "transparent_black"/"transparent", "opaque_black", "opaque_white".
105// Compare op accepts: "never", "less", "less_equal"/"lequal", "equal",
106// "greater", "greater_equal"/"gequal", "not_equal"/"neq", "always".
107// When set (and not "never") a comparison sampler is created and
108// the GLSL type becomes sampler*Shadow. Supported on 2D,
109// 2D-array, cubemap (image/texture/cubemap inputs) and
110// cubemap-array (AUXILIARY only). Silently dropped with a
111// stderr warning on 3D inputs (sampler3DShadow is not a core
112// GLSL type) — use a 2D / 2D-array / cube shadow instead.
113// With the engine's reverse-Z convention, the typical
114// compare op for a standard "shadowed if closer" test is
115// "greater_equal" (not "less_equal").
116struct sampler_config
117{
118 std::string wrap; // Applied to all 3 axes if individual WRAP_S/T/R unset
119 std::string wrap_s;
120 std::string wrap_t;
121 std::string wrap_r;
122 std::string filter; // Applied to both min and mag if individual MIN/MAG_FILTER unset
123 std::string min_filter;
124 std::string mag_filter;
125 std::string mipmap_mode;
126 std::optional<float> anisotropy;
127 std::string border_color;
128 std::optional<float> lod_bias;
129 std::optional<float> min_lod;
130 std::optional<float> max_lod;
131 std::string compare; // empty / "never" = no comparison sampler
132
133 friend bool operator==(const sampler_config&, const sampler_config&) = default;
134};
135
136// COMPOSITE header key: how a colour output is composited onto what its
137// target already holds. `unspecified` resolves to `over`.
138enum class composite_mode : uint8_t
139{
140 unspecified,
141 over,
142 add,
143 multiply,
144 screen,
145 replace
146};
147
148struct image_input
149{
150 int dimensions{2}; // 2 or 3
151 bool depth{false}; // true = shader wants sampleable depth on this input
152 bool is_array{false}; // true = sampler2DArray rather than sampler2D
153 // STATIC: producer publishes a long-lived QRhiTexture that downstream binds
154 // directly; engine skips the consumer-side render-target allocation. Use for
155 // precomputed LUTs, IBL bakes, asset caches — anything where the upstream
156 // is a CPU producer (avnd gpu_texture_output, etc.) rather than an ISF /
157 // raster pass that draws into the consumer's RT each frame. Orthogonal to
158 // dimensions / is_array (cube + 3D + array inputs already grab from source
159 // implicitly because they can't be 2D color attachments anyway).
160 bool is_static{false};
161 sampler_config sampler;
162};
163
164struct cubemap_input
165{
166 // DEPTH: true = request a sampleable depth cube alongside the color cube.
167 // Mirrors image_input::depth: pairs the main `samplerCube` (or
168 // `samplerCubeShadow` under COMPARE) with a `samplerCube <name>_depth`
169 // companion for raw depth reads. Useful for omni-directional scene probes
170 // where the upstream provides both a colour cube and its depth cube.
171 // For plain shadow-cube sampling (HW PCF only) set COMPARE instead and
172 // leave DEPTH false — the texture already has to be depth-format for the
173 // compare sampler to return meaningful values.
174 //
175 // Note: cube-arrays (samplerCubeArray) are intentionally NOT exposed. No
176 // QRhi backend (Vulkan/D3D12/Metal/GL) constructs a cube-array view
177 // correctly from the CubeMap | TextureArray flag combination, so the
178 // shader-side type would always disagree with the bound resource. Bind N
179 // individual cubemap inputs instead, or decompose to a sampler2DArray
180 // with face math in the shader.
181 bool depth{false};
182 sampler_config sampler;
183};
184
185// Sampler state accepted by all audio input flavours. Reuses the same
186// string vocabulary as sampler_config (see above) — any unrecognised or
187// empty string keeps the built-in default (linear / clamp_to_edge). Full
188// sampler_config is overkill here: audio textures are 1-mip 2D samplers
189// with no COMPARE / BORDER_COLOR / LOD semantics, so only FILTER and WRAP
190// are honoured. Nearest filtering is the common ask for band-exact FFT
191// reads where linear interpolation would smear adjacent bins.
192struct audio_sampler_config
193{
194 std::string filter; // "nearest" or "linear" (default)
195 std::string wrap; // "repeat", "clamp_to_edge"/"clamp", "mirror"/"mirrored_repeat"
196};
197
198struct audio_input
199{
200 int max{};
201 audio_sampler_config sampler;
202};
203
204struct audioFFT_input
205{
206 int max{};
207 audio_sampler_config sampler;
208};
209
210struct audioHist_input
211{
212 int max{};
213 audio_sampler_config sampler;
214};
215
216// UBO-style input declared in INPUTS as `"TYPE": "uniform"`.
217//
218// Emitted as `layout(std140, binding=N) uniform <name>_t { ... } <name>;`
219// and bound via QRhiShaderResourceBinding::uniformBuffer (not bufferLoad).
220//
221// Use for small (≤ MaxUniformBufferRange, typically 16KB), read-only data
222// like cameras, light/material counts, indexing constants. For larger or
223// writable data, use `storage_input` (SSBO) instead.
224struct uniform_input
225{
226 // Reuse storage_input's layout_field shape via full struct definition here
227 // to keep the type self-contained.
228 struct layout_field
229 {
230 std::string name;
231 std::string type;
232 };
233
234 std::vector<layout_field> layout;
235
236 // VISIBILITY: which shader stage(s) see this binding in a graphics pipeline.
237 // Accepted values: "vertex+fragment"/"both" (default), "fragment", "vertex",
238 // "compute" (implicit for CSF).
239 std::string visibility{"vertex+fragment"};
240};
241
242// CSF-specific input types
243struct storage_input
244{
245 std::string access; // "read_only", "write_only", "read_write"
246
247 struct layout_field
248 {
249 std::string name;
250 std::string type;
251 };
252
253 std::vector<layout_field> layout;
254
255 std::string buffer_usage; // "", "indirect_draw", "indirect_draw_indexed", "dispatch_args"
256
257 // PERSISTENT: creates a ping-pong pair of SSBOs swapped each frame.
258 // In GLSL, `name` is the current (read-write) buffer, `name_prev` is the
259 // previous frame's read-only buffer.
260 bool persistent{false};
261
262 // VISIBILITY: which shader stage(s) see this binding in a graphics pipeline.
263 // Accepted values: "fragment" (default), "vertex", "vertex+fragment"/"both",
264 // "compute" (implicit for CSF), "none" (no shader binding).
265 std::string visibility{"fragment"};
266};
267
268struct texture_input
269{
270 int dimensions{2}; // 2 or 3
271 // IS_ARRAY: sampler2DArray; STATIC: bound straight from the producer's
272 // texture. Same meaning as on image_input. CUBE: samplerCube, as a
273 // cubemap_input (shader board C07).
274 bool is_array{false};
275 bool is_static{false};
276 bool cube{false};
277 sampler_config sampler;
278};
279
280struct csf_image_input
281{
282 std::string access; // "read_only", "write_only", "read_write"
283 std::string format; // "RGBA8", "R32F", etc.
284
285 std::string width_expression;
286 std::string height_expression;
287 std::string depth_expression; // non-empty means 3D texture
288
289 int dimensions{2}; // 2 or 3 (alternative to depth_expression for declaring 3D)
290
291 // Set internally when the RESOURCES entry uses TYPE: "image_cube".
292 // Writable cubemap (imageCube in GLSL, QRhiTexture::CubeMap |
293 // UsedWithLoadStore). Width must equal height (face edge length). Use for
294 // in-compute reflection-probe baking, environment IBL, etc. Read-only
295 // sampling of the same data is done via TYPE: "cubemap".
296 bool cubemap{false};
297
298 // IS_ARRAY: writable 2D texture array (image2DArray in GLSL, allocated
299 // via QRhi::newTextureArray + UsedWithLoadStore). Layer count comes from
300 // layers_expression (LAYERS: "$USER" / literal). Useful for shadow
301 // cascades, layered G-buffers, compute-written texture atlases.
302 //
303 // Cube-arrays (imageCubeArray) are intentionally NOT supported: no QRhi
304 // backend plumbs CubeMap | TextureArray views correctly, and the shader-
305 // side type would disagree with the bound resource. The parser rejects
306 // is_array + cubemap combinations with a stderr warning.
307 bool is_array{false};
308 std::string layers_expression; // LAYERS: expression for arraySize, may contain $USER
309
310 // VISIBILITY: which shader stage(s) see this binding: "fragment", "vertex",
311 // "vertex+fragment" (also "both", "graphics", "all"), "compute", "none".
312 // Without it, the parser sets "compute" in a compute shader and "fragment"
313 // in a graphics one.
314 std::string visibility;
315
316 // PERSISTENT: creates a ping-pong pair of images swapped each frame.
317 // In GLSL, `<name>` is the current (write or read_write) image and
318 // `<name>_prev` is the previous frame's read-only image — mirrors the
319 // storage_input convention. Works for both 2D and 3D images.
320 bool persistent{false};
321
322 // GENERATE_MIPS: when true, the runtime runs QRhi's generateMips() on
323 // this image after every frame's compute dispatches complete, so
324 // downstream samplers with MIPMAP_MODE: linear / nearest see a valid
325 // mip chain instead of zero-filled upper levels. Ignored for 3D images,
326 // cubemaps, and 2D arrays where generateMips semantics differ across
327 // QRhi backends (per-face / per-layer / per-slice).
328 bool generate_mips{false};
329
330 // COMPOSITE: how the copy of this image into a consumer is composited.
331 composite_mode composite{composite_mode::unspecified};
332
333 bool is3D() const noexcept { return dimensions == 3 || !depth_expression.empty(); }
334 bool isCube() const noexcept { return cubemap; }
335};
336
337// CSF geometry port input: SoA layout, one SSBO per attribute.
338// Declares which geometry attributes the compute shader wants to access.
339struct geometry_input
340{
341 // Explicit cross-geometry forwarding directive.
342 // Used when an output geometry needs data from a different input geometry.
343 struct copy_from
344 {
345 std::string geometry; // Source geometry resource name (e.g. "geoIn")
346 std::string attribute; // Source attribute name (for attribute forwarding)
347 std::string auxiliary; // Source auxiliary name (for auxiliary forwarding; defaults to this name)
348 };
349
350 struct attribute_request
351 {
352 std::string name; // Attribute name used in GLSL (e.g. "position", "velocity")
353 std::string semantic; // Maps to ossia::attribute_semantic name (e.g. "position", "custom")
354 std::string type; // GLSL type (e.g. "vec3", "vec4", "float")
355 std::string access; // "read_only", "write_only", "read_write"
356 std::string rate; // "vertex" (default) or "instance"
357 bool required{true}; // false = optional, zero fallback if missing
358
359 // ACCESS "gather": read_write, and the shader reads indices other than its
360 // own invocation's. Those reads must not observe this dispatch's writes, so
361 // _in and _out become two buffers and the engine keeps them apart -- a
362 // ping-pong swap for a feedback receiver, a per-frame snapshot otherwise.
363 // Plain read_write reads only its own index, where aliasing _in onto _out
364 // is well defined and costs nothing.
365 bool gathers{false};
366
367 // If set, this attribute is forwarded from another geometry's buffer
368 // rather than being allocated/computed by this shader.
369 std::optional<copy_from> forward;
370 };
371
372 // Structured buffers that travel with the geometry (matched by name
373 // against ossia::geometry::auxiliary_buffer entries). Default kind is
374 // SSBO (`layout(std430) buffer`); set `is_uniform = true` to declare a
375 // std140 UBO instead (`layout(std140) uniform`).
376 struct auxiliary_request
377 {
378 std::string name;
379 std::string access; // "read_only", "write_only", "read_write"
380 // (meaningful for SSBO kind only; UBO is always read-only from GLSL)
381 std::vector<storage_input::layout_field> layout;
382 std::string size; // expression for flexible array count, may contain $USER
383 // (SSBO only; UBOs require fixed-size layouts per std140)
384
385 // If set, this auxiliary is forwarded from another geometry's upstream.
386 std::optional<copy_from> forward;
387
388 // Raw-raster only: when true the node owns a ping-pong pair of buffers
389 // (allocated from the LAYOUT + SIZE) that are swapped each frame, and
390 // the auxiliary is NOT resolved from upstream geometry. In GLSL,
391 // `<name>` is the current (writable) buffer, `<name>_prev` is the
392 // previous frame's read-only buffer. Useful for temporal accumulation
393 // / history buffers that live only in the rendering node.
394 // (SSBO only; persistent ping-pong makes no sense for read-only UBOs.)
395 bool persistent{false};
396
397 // When true, declare/bind this auxiliary as a std140 uniform block
398 // (`layout(std140, binding=N) uniform name_t { … } name;`) and bind
399 // with QRhiShaderResourceBinding::uniformBuffer. When false (default),
400 // it's an std430 SSBO. The upstream geometry's
401 // ossia::geometry::auxiliary_buffer is kind-agnostic — the shader's
402 // declaration alone determines how the buffer is bound.
403 bool is_uniform{false};
404 };
405
406 // Texture variant of auxiliary: resolved from ossia::geometry::auxiliary_textures
407 // by name, no score input port. Declared in the top-level AUXILIARY array
408 // with TYPE: "image" / "texture" / "cubemap". Unlike regular INPUTS
409 // textures, does not create an input port — the texture handle travels
410 // bundled with the geometry (e.g. ScenePreprocessor ships `base_color_array`
411 // / `skybox` / `shadow_atlas`).
412 struct auxiliary_texture_request
413 {
414 std::string name;
415 int dimensions{2}; // 2 or 3
416 bool is_array{false}; // sampler2DArray when true
417 bool is_cubemap{false};// samplerCube when true
418 bool is_depth{false}; // sampleable depth (promotes comparison when cfg set)
419 // Storage-image kind: emit `image2D/3D/Cube/Array` with imageLoad/
420 // imageStore semantics instead of `sampler2D/…` with texture(). Set
421 // by TYPE: "storage_image" in the AUXILIARY JSON. Paired with:
422 // - `format`: GLSL layout qualifier (e.g. "rgba8", "r32f", "rgba16f").
423 // - `access`: "read_only" / "write_only" / "read_write", controlling
424 // imageLoad / imageStore / imageLoadStore binding type + the
425 // GLSL `readonly`/`writeonly` decoration.
426 bool is_storage{false};
427 std::string format{"rgba8"}; // only meaningful when is_storage
428 std::string access{"read_write"}; // only meaningful when is_storage
429
430 // Sizing expressions for write_only / read_write storage images. Same
431 // convention as csf_image_input (top-level INPUTS images): an integer
432 // literal or a `$variable` reference resolved against the shader's
433 // long/float input ports + the standard $WIDTH/$HEIGHT/$DEPTH/$LAYERS
434 // family. Empty → engine falls back to renderer state (renderSize for
435 // 2D, voxel-resolution heuristics for 3D). When the engine
436 // auto-allocates a writable nested-aux storage image, these strings
437 // drive its dimensions; for sampled (read-only) entries they're
438 // ignored — the texture comes from the upstream producer at whatever
439 // size that producer baked.
440 std::string width_expression;
441 std::string height_expression;
442 std::string depth_expression; // 3rd dimension for 3D textures
443 std::string layers_expression; // array slice count for 2D arrays
444
445 sampler_config sampler;
446
457 std::string ladder_base;
458 int ladder_index{-1};
459 int ladder_size{0};
460
461 bool in_ladder() const noexcept { return !ladder_base.empty(); }
462 bool owns_ladder() const noexcept { return ladder_index == 0 && in_ladder(); }
463 };
464
465 std::vector<attribute_request> attributes;
466 std::vector<auxiliary_request> auxiliary;
467 std::vector<auxiliary_texture_request> auxiliary_textures;
468
469 std::string vertex_count; // expression string, may contain $USER
470 std::string instance_count; // expression string, may contain $USER
471
472 // PERSISTENT: read_write attributes and auxiliaries work in place on the
473 // upstream's buffers, so their changes accumulate frame to frame and every
474 // other consumer of the upstream sees them. Without it they work on a copy
475 // of the upstream data refreshed every frame.
476 bool persistent{false};
477
478 // Optional format identity stamped onto the consumer geometry's
479 // filter_tag (rapidhash truncated to 32 bits). Only meaningful on
480 // RESOURCES of TYPE: geometry used as outputs (geoOut). Empty leaves
481 // filter_tag at 0 (the "untagged" sentinel) — no routing change for
482 // CSFs that don't author an output format.
483 std::string format_id;
484
485 // Primitive topology of an output geometry: triangles, triangle_strip,
486 // triangle_fan, lines, line_strip or points. Empty inherits the upstream
487 // geometry's topology, or points without one.
488 std::string topology;
489
490 struct indirect_request
491 {
492 std::string count; // expression string (same resolver as vertex_count)
493 // "DRAW_COUNT": true — additionally allocate a GPU-writable u32 draw
494 // count exposed to the compute pass as <name>_indirect_count and
495 // published downstream through the "_indirect_draw_count" auxiliary, for
496 // consumption by drawIndexedIndirectCount (Qt 6.13-era). The command
497 // slots beyond the written count must stay zeroed by the shader so the
498 // capacity-draw fallback rungs paint the same picture.
499 bool draw_count{false};
500
501 // "INDEXED": true — the commands in this buffer drive an INDEXED draw
502 // (the geometry carries an index buffer, e.g. forwarded from upstream),
503 // so the record must be laid out as QRhiDrawIndexedIndirectCommand:
504 // { indexCount, instanceCount, firstIndex, baseVertex, firstInstance }.
505 //
506 // Default (false) is the NON-INDEXED layout, whose first four words are
507 // a native QRhiDrawIndirectCommand:
508 // { vertexCount, instanceCount, firstVertex, firstInstance }
509 // followed by an unused fifth word so that both shapes keep the same
510 // 20-byte stride. Getting this wrong shifts firstInstance by one word
511 // between the GPU indirect rung and the CPU readback rung.
512 bool indexed{false};
513 };
514 std::optional<indirect_request> indirect;
515};
516
517struct input
518{
519 using input_impl = ossia::variant<
520 float_input, long_input, event_input, bool_input, color_input, point2d_input,
521 point3d_input, image_input, cubemap_input, audio_input, audioFFT_input,
522 audioHist_input, storage_input, texture_input, csf_image_input,
523 geometry_input, uniform_input>;
524
525 std::string name;
526 std::string label;
527
528 input_impl data;
529};
530
531// Matches QShaderDescription::VariableType
532enum class attribute_type
533{
534 Unknown = 0,
535
536 Float,
537 Vec2,
538 Vec3,
539 Vec4,
540 Mat2,
541 Mat2x3,
542 Mat2x4,
543 Mat3,
544 Mat3x2,
545 Mat3x4,
546 Mat4,
547 Mat4x2,
548 Mat4x3,
549
550 Int,
551 Int2,
552 Int3,
553 Int4,
554
555 Uint,
556 Uint2,
557 Uint3,
558 Uint4,
559
560 Bool,
561 Bool2,
562 Bool3,
563 Bool4,
564
565 Double,
566 Double2,
567 Double3,
568 Double4,
569 DMat2,
570 DMat2x3,
571 DMat2x4,
572 DMat3,
573 DMat3x2,
574 DMat3x4,
575 DMat4,
576 DMat4x2,
577 DMat4x3,
578
579 Sampler1D,
580 Sampler2D,
581 Sampler2DMS,
582 Sampler3D,
583 SamplerCube,
584 Sampler1DArray,
585 Sampler2DArray,
586 Sampler2DMSArray,
587 Sampler3DArray,
588 SamplerCubeArray,
589 SamplerRect,
590 SamplerBuffer,
591 SamplerExternalOES,
592 Sampler,
593
594 Image1D,
595 Image2D,
596 Image2DMS,
597 Image3D,
598 ImageCube,
599 Image1DArray,
600 Image2DArray,
601 Image2DMSArray,
602 Image3DArray,
603 ImageCubeArray,
604 ImageRect,
605 ImageBuffer,
606
607 Struct,
608
609 Half,
610 Half2,
611 Half3,
612 Half4
613};
614
615struct vertex_attribute
616{
617 int location{};
618 attribute_type type{};
619 std::string name;
620
621 // Optional explicit ossia attribute_semantic name ("position", "velocity",
622 // "texcoord0", ..., "custom"). Only meaningful on `vertex_input` (raw
623 // raster), where it controls how the runtime matches the declared input
624 // to an upstream geometry attribute — same lookup algorithm as CSF
625 // attribute_request. When empty, the parser implicitly uses `name` as the
626 // semantic key. Set to "custom" to force exact-name matching against
627 // custom attributes.
628 std::string semantic;
629
630 // Interpolation qualifier (only applicable to vertex_output / fragment_input).
631 // Allowed: "smooth" (default), "flat", "noperspective", "centroid", "sample".
632 // "sample" forces per-sample fragment shading on this varying — the fragment
633 // shader runs once per MSAA sample for that coverage. Required when MSAA
634 // outputs need per-sample correct interpolation (specular highlights,
635 // normal-mapped surfaces). Empty string = default smooth.
636 std::string interpolation;
637};
638
639struct vertex_input : vertex_attribute
640{
641 // When false, the raw-raster renderer tolerates an upstream geometry that
642 // does not carry a matching attribute: instead of failing the pipeline
643 // build, it synthesises a tiny PerInstance step_rate=1 buffer filled with
644 // a neutral "identity" value (zero for translation, white for color, 1
645 // for roughness, etc.) and binds that in place of the missing upstream
646 // attribute. Lets a single shader cover both instanced and non-instanced
647 // upstreams without per-shape variants.
648 //
649 // When false AND `default_val` is set, those explicit numbers are used
650 // verbatim (after component-truncation / zero-padding against the
651 // declared TYPE). When false AND `default_val` is empty, the runtime
652 // looks the semantic up in a built-in whitelist (see
653 // score::gfx::vertexFallbackDefault) — non-whitelisted semantics without
654 // an explicit DEFAULT are rejected at pipeline-build time with a clear
655 // error to avoid silently-wrong rendering.
656 //
657 // When true (default), the upstream geometry MUST provide the attribute
658 // or the pipeline build fails — existing strict behaviour.
659 bool required{true};
660
661 // Explicit DEFAULT numbers from the JSON header. Stored as doubles for
662 // JSON fidelity; converted to the runtime format (float / int) at
663 // buffer-build time. Empty = use the whitelist neutral (see `required`).
664 // Length is not pre-validated against TYPE here — the runtime truncates
665 // or zero-pads to match the declared GLSL type width.
666 std::vector<double> default_val;
667};
668struct vertex_output : vertex_attribute
669{
670};
671struct fragment_input : vertex_attribute
672{
673};
674struct fragment_output : vertex_attribute
675{
676};
677
678// --- Pipeline state control (PIPELINE_STATE descriptor key) ---------------
679//
680// All fields are optional (std::optional): missing = keep current/legacy
681// default. Two instances live in `descriptor`: a global `default_state`
682// (from PIPELINE_STATE), and a per-pass `override_state` that merges on top.
683
684// ALPHA header key: how a shader output's colour relates to its alpha.
685// `unspecified` resolves per mode through resolve_alpha().
686enum class alpha_mode : uint8_t
687{
688 unspecified,
689 straight,
690 premultiplied
691};
692
693struct blend_attachment
694{
695 bool enable{false};
696 std::string src_color{"src_alpha"};
697 std::string dst_color{"one_minus_src_alpha"};
698 std::string op_color{"add"};
699 std::string src_alpha{"one"};
700 std::string dst_alpha{"one_minus_src_alpha"};
701 std::string op_alpha{"add"};
702 std::string color_write{"rgba"}; // "rgba", "rgb", "r", ...
703};
704
705struct stencil_op_state
706{
707 std::string fail_op{"keep"};
708 std::string depth_fail_op{"keep"};
709 std::string pass_op{"keep"};
710 std::string compare_op{"always"};
711};
712
713struct pipeline_state
714{
715 std::optional<bool> depth_test;
716 std::optional<bool> depth_write;
717 std::optional<std::string> depth_compare; // "less", "less_equal", "greater", ...
718 std::optional<float> depth_bias;
719 std::optional<float> slope_scaled_depth_bias;
720
721 std::optional<std::string> cull_mode; // "none", "front", "back"
722 std::optional<std::string> front_face; // "ccw", "cw"
723 std::optional<std::string> polygon_mode;// "fill", "line"
724 std::optional<float> line_width;
725
726 // Procedural-draw override (Vertex Shader Art style). When
727 // `vertex_count` is set, the renderer issues a single
728 // cb.draw(vertex_count, instance_count, 0, 0) and ignores the
729 // incoming geometry's index / indirect buffers entirely. The vertex
730 // shader drives positions purely from gl_VertexIndex +
731 // gl_InstanceIndex. Use cases:
732 // - Fullscreen passes: VERTEX_COUNT=3, TOPOLOGY=triangles (skybox).
733 // - VSA-style plasma / curves: VERTEX_COUNT=10000,
734 // TOPOLOGY=line_strip.
735 // - Procedural particle grids: VERTEX_COUNT=65536, TOPOLOGY=points.
736 //
737 // Safety: if VERTEX_INPUTS is non-empty (the shader declares vertex
738 // attribute reads), the renderer clamps vertex_count to the incoming
739 // geometry's vertex_count to avoid reading past buffer ends. Shaders
740 // that rely purely on gl_VertexIndex should declare an empty
741 // `VERTEX_INPUTS: []` so the pipeline is built with no vertex
742 // bindings and the draw count is used verbatim.
743 //
744 // Either count may instead be a string expression ("$count * 3",
745 // "$COUNT_particles"), evaluated every frame against the raw raster's
746 // expression surface; the literal field stays unset then.
747 std::optional<uint32_t> vertex_count;
748 std::optional<uint32_t> instance_count;
749 std::string vertex_count_expression;
750 std::string instance_count_expression;
751 // Topology override. When unset, the incoming geometry's topology is
752 // used. Values: "triangles", "triangle_strip", "triangle_fan",
753 // "lines", "line_strip", "points".
754 std::optional<std::string> topology;
755
756 // Colour write mask of every attachment ("rgba", "rgb", "r", "none", ...),
757 // whatever blend applies. BLEND / BLEND_PER_ATTACHMENT entries can also carry
758 // their own COLOR_WRITE; this one wins over them.
759 std::optional<std::string> color_write;
760
761 // Blending: either a single state applied to all color attachments, or a
762 // per-attachment vector. If both are present the per-attachment wins.
763 std::optional<blend_attachment> blend_all;
764 std::vector<blend_attachment> blend_per_attachment;
765
766 // Stencil (optional)
767 std::optional<bool> stencil_test;
768 std::optional<uint32_t> stencil_read_mask;
769 std::optional<uint32_t> stencil_write_mask;
770 std::optional<stencil_op_state> stencil_front;
771 std::optional<stencil_op_state> stencil_back;
772
773 // Variable-rate shading (VRS).
774 // "SHADING_RATE": [w, h] — per-draw shading rate where w,h ∈ {1, 2, 4}.
775 // [1,1] = 1×1 (full rate, default).
776 // [2,2] = 1 invocation per 2×2 pixel block.
777 // [4,4] = 1 per 4×4 block.
778 // Combined with a shading-rate map (set on the render target) the actual
779 // rate is the per-draw rate combined with the per-tile rate via the chosen
780 // combiner op. Requires QRhi::Feature::VariableRateShading (Vulkan, D3D12).
781 std::optional<std::array<int, 2>> shading_rate;
782};
783
784struct pass
785{
786 std::string target;
787 bool persistent{};
788 bool float_storage{};
789 bool nearest_filter{};
790 std::string width_expression{};
791 std::string height_expression{};
792
793 // Render to a specific layer of a texture-array output (-1 = layer 0).
794 int layer{-1};
795
796 // Render to a specific Z-slice of a 3D output. Expression string so the
797 // slice can be computed from inputs (e.g. "$USER_slice"). Empty = slice 0
798 // when the target is 3D, or irrelevant when 2D.
799 std::string z_expression{};
800
801 // Optional format override for the intermediate render target of this
802 // pass (e.g. "rgba16f" for precision-sensitive blur stages). Empty = use
803 // FLOAT: true mapping (rgba32f / rgba8) as before.
804 std::string format{};
805
806 // Per-pass pipeline state overrides (merged with descriptor.default_state).
807 pipeline_state override_state;
808};
809
810struct output_declaration
811{
812 std::string name; // User-chosen name (e.g. "color", "sceneDepth")
813 std::string type; // "color" (default) or "depth"
814
815 // LAYERS: >1 allocates a texture array with this many layers.
816 int layers{1};
817
818 // DEPTH: >1 allocates a 3D texture of this depth. Mutually exclusive with
819 // LAYERS (a ThreeDimensional texture is not a TextureArray). A fragment
820 // PASSES entry with Z renders into a single Z-slice via a color attachment
821 // with setLayer(z).
822 int depth{1};
823
824 // FORMAT: optional explicit texture format ("rgba8", "rgba16f", "r32f", "d32f", ...).
825 // Empty = use the default (RGBA8 for color, D32F for depth).
826 std::string format;
827
828 // SAMPLES: MSAA sample count (1, 2, 4, 8, 16, 32, 64). 1 = no MSAA.
829 // 0 = not declared: the renderer's own sample count applies.
830 // The renderer allocates an MSAA texture and inserts an automatic resolve
831 // pass when downstream consumers expect a non-MSAA input. Each declared
832 // OUTPUT can have its own sample count; the depth attachment for a colour
833 // OUTPUT inherits the same sample count.
834 int samples{0};
835
836 // CUBEMAP: when true the output is allocated with the QRhi cubemap flag
837 // so downstream consumers can bind it as a samplerCube. Implies
838 // `layers == 6` on allocation even when the shader didn't set LAYERS
839 // explicitly. Used by the IBL precompute path (irradiance_convolve,
840 // prefilter_ggx) together with MULTIVIEW:6.
841 bool is_cubemap{false};
842
843 // GENERATE_MIPS: when true the runtime calls generateMips() on this
844 // output's texture after the render pass completes, auto-averaging
845 // the base level into a full mip chain. Implies the QRhi
846 // `MipMapped` + `UsedWithGenerateMips` flags on allocation. Use this
847 // for "source-data" targets whose base level is authored by the
848 // fragment shader and whose sub-mips should be GPU-filtered (skybox
849 // converter, base color textures, SSAO LUTs…). NOT for the
850 // prefilter-style case where each mip has distinct shader-authored
851 // content — use EXECUTION_MODEL: PER_MIP instead.
852 bool generate_mips{false};
853
854 // WIDTH / HEIGHT: explicit target size for offscreen outputs. Set
855 // by the shader author when the intrinsic size of the algorithm
856 // isn't tied to the window / swap-chain (IBL precompute, shadow
857 // atlases, post-process LUTs, …). Zero → fall back to the
858 // renderer's render-size (classic behaviour). Integer literal or
859 // string expression; the expression is evaluated once at init
860 // against the same variable surface as CSF dispatch expressions
861 // ($WIDTH_<input> / $HEIGHT_<input> / scalar input values).
862 //
863 // All colour OUTPUTs of a single RAW_RASTER_PIPELINE shader share
864 // a render pass and must therefore resolve to the same final size;
865 // the runtime uses the first colour OUTPUT's resolved size as the
866 // RT size and allocates every attachment at that size. Cubemaps
867 // are additionally clamped to square via min(w, h) (QRhi contract).
868 int width{0};
869 int height{0};
870 std::string width_expression;
871 std::string height_expression;
872
873 // ALPHA: overrides the descriptor-level ALPHA for this output.
874 alpha_mode alpha{alpha_mode::unspecified};
875
876 // COMPOSITE: overrides the descriptor-level COMPOSITE for this output.
877 composite_mode composite{composite_mode::unspecified};
878};
879
880// QUEUE header key: the order of the cables sharing a consumer input.
881// `unspecified` resolves to transparent for a LAYER shader, else opaque.
882enum class render_queue : uint8_t
883{
884 unspecified,
885 opaque,
886 transparent
887};
888
889// One LAYER.TARGETS entry: a private colour target the draw writes as the
890// fragment output of the same NAME.
891struct layer_target
892{
893 std::string name;
894 std::string format{"rgba16f"};
895 std::array<float, 4> clear{0.f, 0.f, 0.f, 0.f};
896 std::optional<blend_attachment> blend;
897 composite_mode composite{composite_mode::unspecified};
898};
899
900// LAYER.RESOLVE: the shader's own full-screen pass into the consumer, compiled
901// from the fragment source with ISF_RESOLVE_PASS defined.
902struct layer_resolve
903{
904 bool declared{false};
905 std::string output{"isf_FragColor"};
906 std::optional<blend_attachment> blend;
907 composite_mode composite{composite_mode::unspecified};
908 bool depth_write{false};
909 std::string depth_input;
910 std::string fragment;
911};
912
913struct layer_state
914{
915 bool declared{false};
916 std::vector<layer_target> targets;
917 bool depth_test{true};
918 layer_resolve resolve;
919
920 bool enabled() const noexcept { return declared; }
921};
922
923struct descriptor
924{
925 enum Mode
926 {
927 ISF,
928 VSA,
929 CSF,
930 RawRaster
931 } mode{ISF};
932 std::string description;
933 std::string credits;
934 std::vector<std::string> categories;
935
936 // ALPHA: what every colour output writes, unless an OUTPUTS entry says
937 // otherwise. Unspecified: straight for ISF, premultiplied for CSF, raw
938 // raster and VSA.
939 alpha_mode alpha{alpha_mode::unspecified};
940
941 // COMPOSITE: how every colour output is composited onto its target, unless
942 // an OUTPUTS entry or a storage image says otherwise. Unspecified: over.
943 composite_mode composite{composite_mode::unspecified};
944 std::vector<input> inputs;
945 std::vector<output_declaration> outputs; // Parsed from OUTPUTS array; empty = single color output
946 std::vector<pass> passes;
947 std::vector<std::string> pass_targets;
948 bool default_vertex_shader{};
949
950 // For VSA
951 int point_count{};
952 std::string primitive_mode;
953 std::string line_size;
954 std::array<double, 4> background_color{};
955
956 // For CSF
957 struct type_definition
958 {
959 std::string name;
960 std::vector<storage_input::layout_field> layout;
961 };
962 std::vector<type_definition> types;
963
964 struct dispatch_info
965 {
966 std::array<int, 3> local_size{16, 16, 1};
967 std::string execution_type{"2D_IMAGE"}; // "2D_IMAGE", "1D_BUFFER", "PER_VERTEX", "PER_INSTANCE", "MANUAL", "USER", "INDIRECT"
968 std::string target_resource;
969 // MANUAL: the dispatch size. INDIRECT: the mandatory worst-case CEILING
970 // used when the backend cannot dispatch indirectly (Qt < 6.13 or the
971 // feature is absent/killed): the pass is dispatched at this size and the
972 // shader must bound itself by reading its own arguments buffer.
973 std::array<int, 3> workgroups{1, 1, 1};
974 // INDIRECT only: byte offset of the {x,y,z} u32 triplet inside the
975 // TARGET storage buffer ("OFFSET" key, 4-byte aligned).
976 int indirect_byte_offset{0};
977 std::array<std::string, 3> stride{"1", "1", "1"}; // Per-axis stride (supports formulas)
978 std::array<int, 3> user_dispatch_ports{-1, -1, -1}; // Port indices for USER mode (X, Y, Z)
979 };
980 std::vector<dispatch_info> csf_passes;
981 // Every PASSES entry read as a dispatch, in authored order; parse_csf
982 // makes it csf_passes (a header key handler cannot know the MODE).
983 std::vector<dispatch_info> csf_pass_candidates;
984 // The CSF's own code names gl_NumWorkGroups / isf_NumWorkGroups.
985 bool csf_reads_num_workgroups{};
986
987 // For raw shaders
988
989 std::vector<vertex_input> vertex_inputs;
990 std::vector<vertex_output> vertex_outputs;
991 std::vector<fragment_input> fragment_inputs;
992 std::vector<fragment_output> fragment_outputs;
993
994 // Auxiliary SSBOs expected from upstream geometry (matched by name).
995 // Populated from top-level AUXILIARY key in RAW_RASTER_PIPELINE mode.
996 std::vector<geometry_input::auxiliary_request> auxiliary;
997
998 // Auxiliary textures travelling with the geometry (matched by name
999 // against ossia::geometry::auxiliary_textures). Populated from the same
1000 // top-level AUXILIARY array when entries have TYPE: "image" / "texture"
1001 // / "cubemap". Unlike INPUTS-declared textures they don't consume a
1002 // score input port — the renderer looks them up on the geometry every
1003 // frame.
1004 std::vector<geometry_input::auxiliary_texture_request> auxiliary_textures;
1005
1006 // Header content that was accepted but has no effect in the engine, one
1007 // message each (e.g. a sampler key QRhi has no setting for, shader board
1008 // U36). Also printed to stderr when the header is parsed.
1009 std::vector<std::string> warnings;
1010
1011 // PIPELINE_STATE: global pipeline state (depth, blend, cull, stencil, ...).
1012 // Applies to every output pass; may be overridden per-pass via pass::override_state.
1013 pipeline_state default_state;
1014
1015 // MULTIVIEW: render to N layers of a texture array in a single draw.
1016 // 0 or 1 = disabled. N>=2 = enabled (requires QRhi::MultiView capability).
1017 int multiview_count{0};
1018
1019 // EXECUTION_MODEL (RAW_RASTER_PIPELINE only — silently ignored in other
1020 // modes). Drives the invocation count of the single raster pass:
1021 //
1022 // "SINGLE" (default) — one invocation per frame, RT bound at
1023 // mip 0.
1024 // "PER_MIP" — N invocations, RT bound at mip `i` on iteration
1025 // `i`. N is derived from the `target` texture's
1026 // mip chain (floor(log2(min(w, h))) + 1).
1027 // ProcessUBO.passIndex carries the mip index.
1028 // "PER_CUBE_FACE" — 6 invocations, RT bound at cube layer `i`
1029 // (face order +X, -X, +Y, -Y, +Z, -Z). Target
1030 // OUTPUT must be CUBEMAP: true. Mutually
1031 // exclusive with MULTIVIEW (which already
1032 // amplifies one draw to 6 faces).
1033 // "PER_LAYER" — N invocations, RT bound at array layer `i`. N
1034 // comes from the target OUTPUT's `layers`
1035 // declaration. Works on either colour TextureArray
1036 // targets (setLayer attachment) or depth
1037 // TextureArray targets (rendered to a scratch
1038 // and copied into the array layer post-pass —
1039 // QRhi 6.11 has no per-layer depth attachment
1040 // API). ProcessUBO.passIndex carries the layer
1041 // index. Drives shadow_cascades.frag.
1042 // "MANUAL" — N invocations, same RT each time, where N is
1043 // evaluated from the `count` expression string
1044 // via the math_expression parser every frame
1045 // (same variable bindings as CSF's stride /
1046 // image-size expressions: $WIDTH, $HEIGHT,
1047 // $<inputName>, ...).
1048 struct raster_execution_model
1049 {
1050 std::string type; // "SINGLE" / "PER_MIP" / "PER_CUBE_FACE" / "PER_LAYER" / "MANUAL"
1051 std::string target; // PER_MIP / PER_CUBE_FACE / PER_LAYER: OUTPUT name to iterate
1052 std::string count_expression; // MANUAL: integer-valued expression
1053 };
1054 raster_execution_model execution_model;
1055
1056 // User-declared GLSL extension names, emitted as `#extension NAME : require`
1057 // immediately after `#version` in every generated stage. Examples:
1058 // "GL_KHR_shader_subgroup_arithmetic", "GL_EXT_shader_atomic_float".
1059 std::vector<std::string> extensions{
1060 "GL_GOOGLE_include_directive", "GL_GOOGLE_cpp_style_line_directive"};
1061
1062 // CLIP_DISTANCES: number of gl_ClipDistance[N] outputs the vertex shader
1063 // writes (1..8 typical). When > 0 the parser injects
1064 // `out float gl_ClipDistance[N];` in the vertex stage so user code can
1065 // assign without writing the declaration. Each declared distance enables
1066 // one user-defined clipping plane: fragments where gl_ClipDistance[i] < 0
1067 // are discarded.
1068 int clip_distances{0};
1069
1070 // PRIMITIVE_DATA (RAW_RASTER_PIPELINE): the fragment stage gets PRIMITIVE_ID,
1071 // the index of its primitive in the draw, and BARYCENTRIC, its position in
1072 // the primitive, both derived from gl_VertexIndex in the vertex stage. The
1073 // renderer expands indexed and strip meshes into lists so every primitive
1074 // has vertices of its own.
1075 bool primitive_data{false};
1076
1077 // CULL_DISTANCES: like clip distances but per-primitive: a primitive whose
1078 // every vertex has all gl_CullDistance[i] < 0 is fully culled before
1079 // rasterisation. Useful for cheap frustum-/occlusion-style culling.
1080 int cull_distances{0};
1081
1082 // LAYER (RAW_RASTER_PIPELINE): the draw renders into private targets of the
1083 // consumer's size instead of the consumer, then a resolve pass writes the
1084 // consumer.
1085 // TARGETS [{ NAME, FORMAT, CLEAR, BLEND | COMPOSITE }]: one per
1086 // FRAGMENT_OUTPUTS entry, same names, same order (synthesised
1087 // as vec4 outputs when FRAGMENT_OUTPUTS is absent). FORMAT
1088 // rgba8 | rgba16f (default) | rgba32f | r8 | rg8 | r16 | rg16 |
1089 // r16f | r32f; CLEAR a vec4, default transparent black; BLEND
1090 // a PIPELINE_STATE.BLEND value, COMPOSITE a COMPOSITE mode,
1091 // default the premultiplied over. Without TARGETS, one rgba16f
1092 // target per FRAGMENT_OUTPUTS entry.
1093 // DEPTH_TEST test the draw against the consumer's depth, read-only
1094 // (default true).
1095 // RESOLVE { OUTPUT, BLEND | COMPOSITE, DEPTH_WRITE, DEPTH_INPUT }: the
1096 // shader's `#if defined(ISF_RESOLVE_PASS)` section runs as a
1097 // full-screen pass into the consumer, each target a sampler2D
1098 // of its NAME, DEPTH_INPUT (a name) the consumer's depth,
1099 // writing OUTPUT (default isf_FragColor) and, with DEPTH_WRITE,
1100 // gl_FragDepth through the consumer's depth test. Without
1101 // RESOLVE the first target is composited with COMPOSITE.
1102 // Needs no OUTPUTS, SINGLE execution, no MULTIVIEW.
1103 layer_state layer;
1104
1105 // QUEUE: "opaque" | "transparent". Transparent sources draw after the opaque
1106 // ones into an input they share.
1107 render_queue queue{render_queue::unspecified};
1108
1109 // DEPTH_LAYOUT: conservative-depth qualifier on gl_FragDepth. Allowed:
1110 // "any" — driver default (no guarantee, disables early-Z when
1111 // gl_FragDepth is written).
1112 // "greater" — promise the value written is >= the value rasterisation
1113 // would have produced. Lets the HW keep early-Z reject
1114 // for fragments already deeper than the depth buffer.
1115 // "less" — symmetric promise in the other direction.
1116 // "unchanged" — promise the written value equals the rasterised value
1117 // (mostly for documentation; same fast path as "greater"
1118 // on hardware where reverse-Z applies).
1119 // Empty = no qualifier emitted.
1120 std::string depth_layout;
1121};
1122
1123// A LAYOUT field TYPE split into its base type and its array extents, in
1124// declaration order: "vec4[2][3]" is {vec4, {2, 3}}, "float[4 * 8]" {float, {32}},
1125// "uint[]" {uint, {0}}. Each extent is an integer constant expression
1126// (+ - * / %, parentheses); 0 is a runtime-sized [], -1 one that is not a
1127// positive integer constant.
1128struct array_type
1129{
1130 std::string base;
1131 std::vector<int64_t> extents;
1132};
1133SCORE_PLUGIN_GFX_EXPORT
1134array_type parse_array_type(std::string_view type);
1135
1136// The ALPHA convention of a colour output: the OUTPUTS entry's, else the
1137// descriptor's, else the mode's default.
1138SCORE_PLUGIN_GFX_EXPORT
1139alpha_mode resolve_alpha(
1140 const descriptor& d, const output_declaration* out = nullptr) noexcept;
1141
1142// The COMPOSITE of a colour output: the OUTPUTS entry's (or the storage
1143// image's), else the descriptor's, else over.
1144SCORE_PLUGIN_GFX_EXPORT
1145composite_mode resolve_composite(
1146 const descriptor& d, const output_declaration* out = nullptr) noexcept;
1147SCORE_PLUGIN_GFX_EXPORT
1148composite_mode resolve_composite(const descriptor& d, const csf_image_input& img) noexcept;
1149
1150// A straight output composited with multiply or screen has no exact blend
1151// factors: the generated code (or the engine's copy) premultiplies it, and it
1152// is blended as premultiplied.
1153SCORE_PLUGIN_GFX_EXPORT
1154bool premultiplied_by_engine(alpha_mode alpha, composite_mode composite) noexcept;
1155
1156// Whether the shader's DEPTH_COMPARE keeps the greater depth (reverse-Z, the
1157// default) rather than the smaller one.
1158SCORE_PLUGIN_GFX_EXPORT
1159bool depth_nearer_is_greater(const descriptor& d) noexcept;
1160
1161// Whether the shader draws in the transparent QUEUE.
1162SCORE_PLUGIN_GFX_EXPORT
1163bool draws_transparent(const descriptor& d) noexcept;
1164
1165// Whether a raw raster renders into the faces of a cube OUTPUT. Its clip-space
1166// y is then negated on every backend whose clip space is not y-up, so that the
1167// faces land in the order of the GL cube-map face table.
1168SCORE_PLUGIN_GFX_EXPORT
1169bool renders_cube_faces(const descriptor& d) noexcept;
1170
1171// Whether a raw raster draws points as screen-aligned quads on the HLSL
1172// target. Direct3D has no point size: a point is one pixel whatever the shader
1173// writes to gl_PointSize. For a declared PIPELINE_STATE TOPOLOGY "points" the
1174// generated HLSL vertex stage therefore expands each point into a quad of
1175// gl_PointSize pixels -- six vertices per instance, one instance per point --
1176// and hands gl_PointCoord to the fragment stage through a varying. The
1177// renderer must draw such a pipeline that way on D3D11 / D3D12.
1178SCORE_PLUGIN_GFX_EXPORT
1179bool raw_raster_point_quads(const descriptor& d) noexcept;
1180
1181// Whether a pipeline state declares BLEND or BLEND_PER_ATTACHMENT.
1182SCORE_PLUGIN_GFX_EXPORT
1183bool declares_blend(const pipeline_state& s) noexcept;
1184
1185// Whether an input samples the engine's premultiplied render target (a 2D
1186// image or texture input fed by cables), rather than a producer's texture
1187// bound as is (3D, array, STATIC, cubemap, audio). IMG_PIXEL, IMG_NORM_PIXEL,
1188// IMG_THIS_PIXEL and IMG_THIS_NORM_PIXEL unpremultiply such inputs.
1189SCORE_PLUGIN_GFX_EXPORT
1190bool is_premultiplied_render_target(const input& in) noexcept;
1191
1192class SCORE_PLUGIN_GFX_EXPORT parser
1193{
1194 std::string m_sourceVertex;
1195 std::string m_sourceFragment;
1196 std::string m_source_geometry_filter;
1197 int m_version{450};
1198
1199 std::string m_vertex;
1200 std::string m_fragment;
1201 std::string m_geometry_filter;
1202
1203 // True when parse_raw_raster_pipeline synthesised the `camera` auxiliary
1204 // rather than the shader declaring its own. Gates the typed accessors: a
1205 // hand-written camera block has its own layout and must not be shadowed.
1206 bool m_injected_camera_aux{false};
1207
1208 descriptor m_desc;
1209
1210public:
1211 enum class ShaderType
1212 {
1213 Autodetect,
1214 ISF,
1215 ShaderToy,
1216 GLSLSandBox,
1217 GeometryFilter,
1218 VertexShaderArt,
1219 CSF,
1220 RawRasterPipeline,
1221 RawRaytracePipeline,
1222 RawMeshPipeline
1223 };
1224 parser(std::string vert, std::string frag, int glslVersion, ShaderType);
1225 explicit parser(std::string isf_geom_filter, ShaderType t);
1226
1227 descriptor data() const;
1228 descriptor::Mode mode() const;
1229 std::string vertex() const;
1230 std::string fragment() const;
1231 std::string geometry_filter() const;
1232 std::string compute_shader() const;
1233 static std::pair<int, descriptor> parse_isf_header(std::string_view source);
1234 void parse_shadertoy_json(const std::string& json);
1235
1236 std::string write_isf() const;
1237
1238private:
1239 void parse_isf();
1240 void parse_raw_raster_pipeline();
1241 void parse_shadertoy();
1242 void parse_glsl_sandbox();
1243 void parse_geometry_filter();
1244 void parse_vsa();
1245 void parse_csf();
1246};
1247}
ChannelLayout resolve(ChannelLayout l, int channels) noexcept
Definition ChannelLayout.hpp:56
@ Unknown
Versions unreadable; treated as usable.