Skip to main content

concinnity_core/render/slang_programs/
vk.rs

1// Single-source engine shader programs for the Vulkan backend.
2//
3// Each program compiles a `.slang` file under `src/shaders/` (the
4// backend-neutral single-source directory) to SPIR-V, at build time where the
5// host has slangc and at renderer init otherwise, cached in the
6// content-addressed shader cache. The runtime `POOL_SIZE` / `MAX_PROBES`
7// values are injected as `#define` lines into the source text, so the cache
8// keys them.
9//
10// The `[[vk::binding]]` annotations (and ParameterBlock member order) in the
11// sources are the engine's descriptor-set layouts. slangc names every
12// single-entry SPIR-V entry point `main`, which is what pipeline stage creation
13// asks for.
14
15/// Which runtime capacities a program bakes into its source as `#define`s.
16/// They ride the source text rather than a command line so the shader cache
17/// keys them.
18#[derive(Clone, Copy, PartialEq, Eq)]
19pub enum Sizes {
20    /// No size defines.
21    None,
22    /// The reflection-probe array length only: the SSR resolve binds the
23    /// forward global set's probe cubes but none of the texture pool.
24    Probes,
25    /// The bindless texture-pool capacity and the probe array length.
26    PoolAndProbes,
27}
28
29/// One SPIR-V program: which shader file, which entry point, under which
30/// variant gates and baked capacities.
31pub struct SlangProgram {
32    /// File name under `src/shaders/` for the `cn debug` disk-first resolve;
33    /// also the embedded fallback's origin.
34    pub file: &'static str,
35    /// Entry point compiled out of that file.
36    pub entry: &'static str,
37    /// Diagnostic label (compile errors + cache miss logs + export report).
38    pub label: &'static str,
39    /// Fixed variant gates (e.g. HIZ_INIT_MSAA), each injected as
40    /// `#define <gate> 1`. More than one where a variant is the intersection of
41    /// two, like the textured ray-traced glass fragment.
42    pub gates: &'static [&'static str],
43    /// Runtime capacities injected from the context.
44    pub sizes: Sizes,
45    /// Inject `#define USE_MSAA {0|1}` from `Ctx::msaa`. A HOST difference
46    /// rather than a target one: only the fog fragment declares its depth source
47    /// by the main pass's sample count.
48    pub msaa: bool,
49}
50
51/// `vertex_main_bindless` from `main_bindless.slang`.
52pub static MAIN_BINDLESS_VERT: SlangProgram = SlangProgram {
53    file: "main_bindless.slang",
54    entry: "vertex_main_bindless",
55    label: "vert_bindless.slang",
56    gates: &[],
57    sizes: Sizes::PoolAndProbes,
58    msaa: false,
59};
60/// `fragment_main_bindless` from `main_bindless.slang`.
61pub static MAIN_BINDLESS_FRAG: SlangProgram = SlangProgram {
62    file: "main_bindless.slang",
63    entry: "fragment_main_bindless",
64    label: "frag_bindless.slang",
65    gates: &[],
66    sizes: Sizes::PoolAndProbes,
67    msaa: false,
68};
69/// `cull_kernel` from `cull.slang`: phase-1 draw cull.
70pub static CULL: SlangProgram = SlangProgram {
71    file: "cull.slang",
72    entry: "cull_kernel",
73    label: "cull.slang",
74    gates: &[],
75    sizes: Sizes::None,
76    msaa: false,
77};
78/// `cull_kernel` from `cull.slang` under `CULL_PHASE2`: the two-pass occlusion
79/// re-test against the rebuilt Hi-Z pyramid.
80pub static CULL_PHASE2: SlangProgram = SlangProgram {
81    file: "cull.slang",
82    entry: "cull_kernel",
83    label: "cull_phase2.slang",
84    gates: &["CULL_PHASE2"],
85    sizes: Sizes::None,
86    msaa: false,
87};
88/// `cull_kernel` from `cull.slang` under `SHADOW_CULL`: light-frustum only,
89/// against the lean three-binding shadow cull set.
90pub static CULL_SHADOW: SlangProgram = SlangProgram {
91    file: "cull.slang",
92    entry: "cull_kernel",
93    label: "cull_shadow.slang",
94    gates: &["SHADOW_CULL"],
95    sizes: Sizes::None,
96    msaa: false,
97};
98/// `light_cull_kernel` from `light_cull.slang`.
99pub static LIGHT_CULL: SlangProgram = SlangProgram {
100    file: "light_cull.slang",
101    entry: "light_cull_kernel",
102    label: "light_cull.slang",
103    gates: &[],
104    sizes: Sizes::None,
105    msaa: false,
106};
107/// `hiz_init_msaa` from `hiz_build.slang`.
108pub static HIZ_INIT_MSAA: SlangProgram = SlangProgram {
109    file: "hiz_build.slang",
110    entry: "hiz_init_msaa",
111    label: "hiz_init_msaa.slang",
112    gates: &["HIZ_INIT_MSAA"],
113    sizes: Sizes::None,
114    msaa: false,
115};
116/// `hiz_init_single` from `hiz_build.slang`.
117pub static HIZ_INIT_SINGLE: SlangProgram = SlangProgram {
118    file: "hiz_build.slang",
119    entry: "hiz_init_single",
120    label: "hiz_init_single.slang",
121    gates: &["HIZ_INIT_SINGLE"],
122    sizes: Sizes::None,
123    msaa: false,
124};
125/// `hiz_downsample` from `hiz_build.slang`.
126pub static HIZ_DOWNSAMPLE: SlangProgram = SlangProgram {
127    file: "hiz_build.slang",
128    entry: "hiz_downsample",
129    label: "hiz_downsample.slang",
130    gates: &["HIZ_DOWNSAMPLE"],
131    sizes: Sizes::None,
132    msaa: false,
133};
134
135/// `probe_mip0` from `probe_prefilter.slang`.
136pub static PROBE_MIP0: SlangProgram = SlangProgram {
137    file: "probe_prefilter.slang",
138    entry: "probe_mip0",
139    label: "probe_mip0.slang",
140    gates: &["PROBE_MIP0"],
141    sizes: Sizes::None,
142    msaa: false,
143};
144/// `probe_downsample` from `probe_prefilter.slang`.
145pub static PROBE_DOWNSAMPLE: SlangProgram = SlangProgram {
146    file: "probe_prefilter.slang",
147    entry: "probe_downsample",
148    label: "probe_downsample.slang",
149    gates: &["PROBE_DOWNSAMPLE"],
150    sizes: Sizes::None,
151    msaa: false,
152};
153/// `probe_ggx` from `probe_prefilter.slang`.
154pub static PROBE_GGX: SlangProgram = SlangProgram {
155    file: "probe_prefilter.slang",
156    entry: "probe_ggx",
157    label: "probe_ggx.slang",
158    gates: &["PROBE_GGX"],
159    sizes: Sizes::None,
160    msaa: false,
161};
162
163// The G-buffer pre-pass and shadow families. Every entry is its own program so
164// it declares only the resources it binds; the `[[vk::binding]]` annotations
165// reproduce the descriptor sets the GLSL declared, so the SPIR-V is a drop-in
166// against the untouched pipeline layouts.
167/// `gbuffer_prepass_vertex_bindless` from `gbuffer_prepass.slang`.
168pub static GBUFFER_BINDLESS_VERT: SlangProgram = SlangProgram {
169    file: "gbuffer_prepass.slang",
170    entry: "gbuffer_prepass_vertex_bindless",
171    label: "gbuffer_prepass_vert_bindless.slang",
172    gates: &["GB_BINDLESS"],
173    sizes: Sizes::None,
174    msaa: false,
175};
176/// `gbuffer_prepass_fragment_bindless` from `gbuffer_prepass.slang`.
177pub static GBUFFER_BINDLESS_FRAG: SlangProgram = SlangProgram {
178    file: "gbuffer_prepass.slang",
179    entry: "gbuffer_prepass_fragment_bindless",
180    label: "gbuffer_prepass_frag_bindless.slang",
181    gates: &["GB_FRAGMENT_BINDLESS"],
182    sizes: Sizes::None,
183    msaa: false,
184};
185/// `shadow_vertex_main` from `shadow.slang`.
186pub static SHADOW_VERT: SlangProgram = SlangProgram {
187    file: "shadow.slang",
188    entry: "shadow_vertex_main",
189    label: "shadow_vert.slang",
190    gates: &["SHADOW_STATIC"],
191    sizes: Sizes::None,
192    msaa: false,
193};
194/// `shadow_vertex_main_skinned` from `shadow.slang`.
195pub static SKINNED_SHADOW_VERT: SlangProgram = SlangProgram {
196    file: "shadow.slang",
197    entry: "shadow_vertex_main_skinned",
198    label: "shadow_vert_skinned.slang",
199    gates: &["SHADOW_SKINNED"],
200    sizes: Sizes::None,
201    msaa: false,
202};
203/// `shadow_vertex_bindless` from `shadow.slang`.
204pub static SHADOW_BINDLESS_VERT: SlangProgram = SlangProgram {
205    file: "shadow.slang",
206    entry: "shadow_vertex_bindless",
207    label: "shadow_vert_bindless.slang",
208    gates: &["SHADOW_BINDLESS"],
209    sizes: Sizes::None,
210    msaa: false,
211};
212
213// The fullscreen-triangle vertex stage every ported post pass pairs with; one
214// module serves them all, the way `composite.vert` served the GLSL ones.
215/// `fullscreen_vertex` from `fullscreen.slang`.
216pub static FULLSCREEN_VERT: SlangProgram = SlangProgram {
217    file: "fullscreen.slang",
218    entry: "fullscreen_vertex",
219    label: "fullscreen_vert.slang",
220    gates: &[],
221    sizes: Sizes::None,
222    msaa: false,
223};
224/// `taa_fragment_main` from `taa.slang`.
225pub static TAA_FRAG: SlangProgram = SlangProgram {
226    file: "taa.slang",
227    entry: "taa_fragment_main",
228    label: "taa_frag.slang",
229    gates: &[],
230    sizes: Sizes::None,
231    msaa: false,
232};
233/// `bloom_prefilter_fragment` from `bloom.slang`.
234pub static BLOOM_PREFILTER: SlangProgram = SlangProgram {
235    file: "bloom.slang",
236    entry: "bloom_prefilter_fragment",
237    label: "bloom_prefilter.slang",
238    gates: &["BLOOM_PREFILTER"],
239    sizes: Sizes::None,
240    msaa: false,
241};
242/// `bloom_downsample_fragment` from `bloom.slang`.
243pub static BLOOM_DOWNSAMPLE: SlangProgram = SlangProgram {
244    file: "bloom.slang",
245    entry: "bloom_downsample_fragment",
246    label: "bloom_downsample.slang",
247    gates: &["BLOOM_DOWNSAMPLE"],
248    sizes: Sizes::None,
249    msaa: false,
250};
251/// `bloom_upsample_fragment` from `bloom.slang`.
252pub static BLOOM_UPSAMPLE: SlangProgram = SlangProgram {
253    file: "bloom.slang",
254    entry: "bloom_upsample_fragment",
255    label: "bloom_upsample.slang",
256    gates: &["BLOOM_UPSAMPLE"],
257    sizes: Sizes::None,
258    msaa: false,
259};
260/// `composite_fragment` from `composite.slang`.
261pub static COMPOSITE_FRAG: SlangProgram = SlangProgram {
262    file: "composite.slang",
263    entry: "composite_fragment",
264    label: "composite_frag.slang",
265    gates: &[],
266    sizes: Sizes::None,
267    msaa: false,
268};
269/// `ssao_kernel_fragment` from `ssao.slang`.
270pub static SSAO_KERNEL: SlangProgram = SlangProgram {
271    file: "ssao.slang",
272    entry: "ssao_kernel_fragment",
273    label: "ssao_kernel.slang",
274    gates: &["SSAO_KERNEL"],
275    sizes: Sizes::None,
276    msaa: false,
277};
278/// `ssao_blur_fragment` from `ssao.slang`.
279pub static SSAO_BLUR: SlangProgram = SlangProgram {
280    file: "ssao.slang",
281    entry: "ssao_blur_fragment",
282    label: "ssao_blur.slang",
283    gates: &["SSAO_BLUR"],
284    sizes: Sizes::None,
285    msaa: false,
286};
287/// `ssr_resolve_fragment` from `ssr.slang`.
288pub static SSR_RESOLVE: SlangProgram = SlangProgram {
289    file: "ssr.slang",
290    entry: "ssr_resolve_fragment",
291    label: "ssr_resolve.slang",
292    gates: &[],
293    sizes: Sizes::Probes,
294    msaa: false,
295};
296/// `ssgi_gather_fragment` from `ssgi.slang`.
297pub static SSGI_GATHER: SlangProgram = SlangProgram {
298    file: "ssgi.slang",
299    entry: "ssgi_gather_fragment",
300    label: "ssgi_gather.slang",
301    gates: &["SSGI_GATHER"],
302    sizes: Sizes::None,
303    msaa: false,
304};
305/// `ssgi_composite_fragment` from `ssgi.slang`.
306pub static SSGI_COMPOSITE: SlangProgram = SlangProgram {
307    file: "ssgi.slang",
308    entry: "ssgi_composite_fragment",
309    label: "ssgi_composite.slang",
310    gates: &["SSGI_COMPOSITE"],
311    sizes: Sizes::None,
312    msaa: false,
313};
314/// `reflection_blur_fragment` from `reflection.slang`.
315pub static REFLECTION_BLUR: SlangProgram = SlangProgram {
316    file: "reflection.slang",
317    entry: "reflection_blur_fragment",
318    label: "reflection_blur.slang",
319    gates: &["REFLECTION_BLUR"],
320    sizes: Sizes::None,
321    msaa: false,
322};
323/// `reflection_composite_fragment` from `reflection.slang`.
324pub static REFLECTION_COMPOSITE: SlangProgram = SlangProgram {
325    file: "reflection.slang",
326    entry: "reflection_composite_fragment",
327    label: "reflection_composite.slang",
328    gates: &["REFLECTION_COMPOSITE"],
329    sizes: Sizes::None,
330    msaa: false,
331};
332
333// The compute kernels and the fog family. The fog fragment is the only program
334// whose assembly depends on the host's MSAA mode.
335/// `fog_froxel_kernel` from `fog.slang`.
336pub static FOG_FROXEL: SlangProgram = SlangProgram {
337    file: "fog.slang",
338    entry: "fog_froxel_kernel",
339    label: "fog_froxel.slang",
340    gates: &["FOG_FROXEL"],
341    sizes: Sizes::None,
342    msaa: false,
343};
344/// `fog_fragment` from `fog.slang`.
345pub static FOG_FRAG: SlangProgram = SlangProgram {
346    file: "fog.slang",
347    entry: "fog_fragment",
348    label: "fog_frag.slang",
349    gates: &[],
350    sizes: Sizes::None,
351    msaa: true,
352};
353/// `histogram_build` from `auto_exposure.slang`.
354pub static AUTO_EXPOSURE_BUILD: SlangProgram = SlangProgram {
355    file: "auto_exposure.slang",
356    entry: "histogram_build",
357    label: "auto_exposure_build.slang",
358    gates: &["AE_BUILD"],
359    sizes: Sizes::None,
360    msaa: false,
361};
362/// `histogram_average` from `auto_exposure.slang`.
363pub static AUTO_EXPOSURE_AVERAGE: SlangProgram = SlangProgram {
364    file: "auto_exposure.slang",
365    entry: "histogram_average",
366    label: "auto_exposure_average.slang",
367    gates: &["AE_AVERAGE"],
368    sizes: Sizes::None,
369    msaa: false,
370};
371/// `rt_skin` from `rt_skin.slang`.
372pub static RT_SKIN: SlangProgram = SlangProgram {
373    file: "rt_skin.slang",
374    entry: "rt_skin",
375    label: "rt_skin.slang",
376    gates: &[],
377    sizes: Sizes::None,
378    msaa: false,
379};
380/// `particle_simulate` from `particle_simulate.slang`.
381pub static PARTICLE_SIMULATE: SlangProgram = SlangProgram {
382    file: "particle_simulate.slang",
383    entry: "particle_simulate",
384    label: "particle_simulate.slang",
385    gates: &[],
386    sizes: Sizes::None,
387    msaa: false,
388};
389
390// The remaining raster families: the particle billboard pair, the projected
391// decal, world-space lines and the text / sprite overlay. Each has real vertex
392// geometry, so unlike the post passes they keep their own vertex entry rather
393// than pairing with `fullscreen.slang`. Only the two depth-reading fragments
394// take the host's sample count; their vertex stages never name the depth source.
395/// `particle_vertex` from `particle.slang`.
396pub static PARTICLE_VERT: SlangProgram = SlangProgram {
397    file: "particle.slang",
398    entry: "particle_vertex",
399    label: "particle_vert.slang",
400    gates: &[],
401    sizes: Sizes::None,
402    msaa: false,
403};
404/// `particle_fragment` from `particle.slang`.
405pub static PARTICLE_FRAG: SlangProgram = SlangProgram {
406    file: "particle.slang",
407    entry: "particle_fragment",
408    label: "particle_frag.slang",
409    gates: &[],
410    sizes: Sizes::None,
411    msaa: false,
412};
413/// `decal_vertex` from `decal.slang`.
414pub static DECAL_VERT: SlangProgram = SlangProgram {
415    file: "decal.slang",
416    entry: "decal_vertex",
417    label: "decal_vert.slang",
418    gates: &[],
419    sizes: Sizes::None,
420    msaa: false,
421};
422/// `decal_fragment` from `decal.slang`.
423pub static DECAL_FRAG: SlangProgram = SlangProgram {
424    file: "decal.slang",
425    entry: "decal_fragment",
426    label: "decal_frag.slang",
427    gates: &[],
428    sizes: Sizes::None,
429    msaa: true,
430};
431/// `line_vertex` from `line.slang`.
432pub static LINE_VERT: SlangProgram = SlangProgram {
433    file: "line.slang",
434    entry: "line_vertex",
435    label: "line_vert.slang",
436    gates: &[],
437    sizes: Sizes::None,
438    msaa: false,
439};
440/// `line_fragment` from `line.slang`.
441pub static LINE_FRAG: SlangProgram = SlangProgram {
442    file: "line.slang",
443    entry: "line_fragment",
444    label: "line_frag.slang",
445    gates: &[],
446    sizes: Sizes::None,
447    msaa: true,
448};
449/// `text_vertex_main` from `text.slang`.
450pub static TEXT_VERT: SlangProgram = SlangProgram {
451    file: "text.slang",
452    entry: "text_vertex_main",
453    label: "text_vert.slang",
454    gates: &[],
455    sizes: Sizes::None,
456    msaa: false,
457};
458/// `text_fragment_main` from `text.slang`.
459pub static TEXT_FRAG: SlangProgram = SlangProgram {
460    file: "text.slang",
461    entry: "text_fragment_main",
462    label: "text_frag.slang",
463    gates: &[],
464    sizes: Sizes::None,
465    msaa: false,
466};
467
468// Every declared program, iterated by the export-time precompile. Both Hi-Z
469// init variants are enumerated: which one a device runs depends on its MSAA
470// The ray-traced families, compiled only where `VK_KHR_ray_query` is present:
471// slangc emits `SPV_KHR_ray_query` (SPIR-V 1.5), so these need the Vulkan 1.2
472// device the extension already implies. The flat variants declare no bindless
473// pool at all, which is what keeps a non-bindless world from binding one.
474/// `rt_reflections_fragment` from `rt_reflections.slang`.
475pub static RT_REFLECTIONS_FRAG: SlangProgram = SlangProgram {
476    file: "rt_reflections.slang",
477    entry: "rt_reflections_fragment",
478    label: "rt_reflections.slang",
479    gates: &[],
480    sizes: Sizes::Probes,
481    msaa: false,
482};
483/// `rt_reflections_fragment` from `rt_reflections.slang`.
484pub static RT_REFLECTIONS_FRAG_TEXTURED: SlangProgram = SlangProgram {
485    file: "rt_reflections.slang",
486    entry: "rt_reflections_fragment",
487    label: "rt_reflections_textured.slang",
488    gates: &["RT_TEXTURED"],
489    sizes: Sizes::PoolAndProbes,
490    msaa: false,
491};
492/// `glass_vertex` from `glass.slang`.
493pub static GLASS_VERT: SlangProgram = SlangProgram {
494    file: "glass.slang",
495    entry: "glass_vertex",
496    label: "glass_vert.slang",
497    gates: &[],
498    sizes: Sizes::Probes,
499    msaa: true,
500};
501/// `glass_fragment` from `glass.slang`.
502pub static GLASS_FRAG: SlangProgram = SlangProgram {
503    file: "glass.slang",
504    entry: "glass_fragment",
505    label: "glass_frag.slang",
506    gates: &[],
507    sizes: Sizes::Probes,
508    msaa: true,
509};
510/// `glass_rt_fragment` from `glass.slang`.
511pub static GLASS_FRAG_RT: SlangProgram = SlangProgram {
512    file: "glass.slang",
513    entry: "glass_rt_fragment",
514    label: "glass_frag_rt.slang",
515    gates: &["GLASS_RT"],
516    sizes: Sizes::Probes,
517    msaa: true,
518};
519/// `glass_rt_fragment` from `glass.slang`.
520pub static GLASS_FRAG_RT_TEXTURED: SlangProgram = SlangProgram {
521    file: "glass.slang",
522    entry: "glass_rt_fragment",
523    label: "glass_frag_rt_textured.slang",
524    gates: &["GLASS_RT", "RT_TEXTURED"],
525    sizes: Sizes::PoolAndProbes,
526    msaa: true,
527};
528
529// The see-through glass MESH family, the transparent pass's third producer.
530// Ray-traced only -- the per-pixel trace is what makes the mesh see-through
531// rather than the opaque reflective glass the main pass draws -- so there is no
532// base pair. Its vertex stage is its own (it applies the per-draw model matrix)
533// but shares every descriptor binding with the rest of the pass.
534/// `glass_mesh_vertex` from `glass_mesh.slang`.
535pub static GLASS_MESH_VERT: SlangProgram = SlangProgram {
536    file: "glass_mesh.slang",
537    entry: "glass_mesh_vertex",
538    label: "glass_mesh_vert.slang",
539    gates: &[],
540    sizes: Sizes::Probes,
541    msaa: true,
542};
543/// `glass_mesh_rt_fragment` from `glass_mesh.slang`.
544pub static GLASS_MESH_FRAG_RT: SlangProgram = SlangProgram {
545    file: "glass_mesh.slang",
546    entry: "glass_mesh_rt_fragment",
547    label: "glass_mesh_frag_rt.slang",
548    gates: &[],
549    sizes: Sizes::Probes,
550    msaa: true,
551};
552/// `glass_mesh_rt_fragment` from `glass_mesh.slang`.
553pub static GLASS_MESH_FRAG_RT_TEXTURED: SlangProgram = SlangProgram {
554    file: "glass_mesh.slang",
555    entry: "glass_mesh_rt_fragment",
556    label: "glass_mesh_frag_rt_textured.slang",
557    gates: &["RT_TEXTURED"],
558    sizes: Sizes::PoolAndProbes,
559    msaa: true,
560};
561
562// The water surface family, the transparent pass's other producer. Same shape as
563// the glass table above, and deliberately the same descriptor bindings, so
564// `transparent.rs` builds one set of set layouts and both producers draw under
565// them.
566/// `water_vertex` from `water.slang`.
567pub static WATER_VERT: SlangProgram = SlangProgram {
568    file: "water.slang",
569    entry: "water_vertex",
570    label: "water_vert.slang",
571    gates: &[],
572    sizes: Sizes::Probes,
573    msaa: true,
574};
575/// `water_fragment` from `water.slang`.
576pub static WATER_FRAG: SlangProgram = SlangProgram {
577    file: "water.slang",
578    entry: "water_fragment",
579    label: "water_frag.slang",
580    gates: &[],
581    sizes: Sizes::Probes,
582    msaa: true,
583};
584/// `water_rt_fragment` from `water.slang`.
585pub static WATER_FRAG_RT: SlangProgram = SlangProgram {
586    file: "water.slang",
587    entry: "water_rt_fragment",
588    label: "water_frag_rt.slang",
589    gates: &["WATER_RT"],
590    sizes: Sizes::Probes,
591    msaa: true,
592};
593/// `water_rt_fragment` from `water.slang`.
594pub static WATER_FRAG_RT_TEXTURED: SlangProgram = SlangProgram {
595    file: "water.slang",
596    entry: "water_rt_fragment",
597    label: "water_frag_rt_textured.slang",
598    gates: &["WATER_RT", "RT_TEXTURED"],
599    sizes: Sizes::PoolAndProbes,
600    msaa: true,
601};
602
603// mode, and a bundle should be warm for either.
604/// Every declared program, which the renderer and the build script both
605/// iterate: one compiles them at init, the other ahead of time.
606pub static ALL: &[&SlangProgram] = &[
607    &MAIN_BINDLESS_VERT,
608    &MAIN_BINDLESS_FRAG,
609    &LIGHT_CULL,
610    &CULL,
611    &CULL_PHASE2,
612    &CULL_SHADOW,
613    &RT_SKIN,
614    &HIZ_INIT_MSAA,
615    &HIZ_INIT_SINGLE,
616    &HIZ_DOWNSAMPLE,
617    &PROBE_MIP0,
618    &PROBE_DOWNSAMPLE,
619    &PROBE_GGX,
620    &GBUFFER_BINDLESS_VERT,
621    &GBUFFER_BINDLESS_FRAG,
622    &SHADOW_VERT,
623    &SKINNED_SHADOW_VERT,
624    &SHADOW_BINDLESS_VERT,
625    &FULLSCREEN_VERT,
626    &TAA_FRAG,
627    &BLOOM_PREFILTER,
628    &BLOOM_DOWNSAMPLE,
629    &BLOOM_UPSAMPLE,
630    &COMPOSITE_FRAG,
631    &SSAO_KERNEL,
632    &SSAO_BLUR,
633    &SSR_RESOLVE,
634    &SSGI_GATHER,
635    &SSGI_COMPOSITE,
636    &REFLECTION_BLUR,
637    &REFLECTION_COMPOSITE,
638    &FOG_FROXEL,
639    &FOG_FRAG,
640    &AUTO_EXPOSURE_BUILD,
641    &AUTO_EXPOSURE_AVERAGE,
642    &PARTICLE_SIMULATE,
643    &PARTICLE_VERT,
644    &PARTICLE_FRAG,
645    &DECAL_VERT,
646    &DECAL_FRAG,
647    &LINE_VERT,
648    &LINE_FRAG,
649    &TEXT_VERT,
650    &TEXT_FRAG,
651    &RT_REFLECTIONS_FRAG,
652    &RT_REFLECTIONS_FRAG_TEXTURED,
653    &GLASS_VERT,
654    &GLASS_FRAG,
655    &GLASS_FRAG_RT,
656    &GLASS_FRAG_RT_TEXTURED,
657    &GLASS_MESH_VERT,
658    &GLASS_MESH_FRAG_RT,
659    &GLASS_MESH_FRAG_RT_TEXTURED,
660    &WATER_VERT,
661    &WATER_FRAG,
662    &WATER_FRAG_RT,
663    &WATER_FRAG_RT_TEXTURED,
664];
665
666#[cfg(test)]
667mod tests {
668    use super::*;
669    use alloc::vec::Vec;
670
671    // `label` keys each precompiled SPIR-V artifact, paired with the sample
672    // count for the programs that read it, so two programs sharing a label
673    // would hand one's bytes to the other.
674    #[test]
675    fn every_program_has_a_distinct_label() {
676        let mut labels: Vec<&str> = ALL.iter().map(|p| p.label).collect();
677        let declared = labels.len();
678        labels.sort_unstable();
679        labels.dedup();
680        assert_eq!(
681            labels.len(),
682            declared,
683            "two programs share a label, so they would share a precompiled artifact"
684        );
685    }
686
687    #[test]
688    fn every_program_names_an_embedded_shader() {
689        for p in ALL {
690            assert!(
691                crate::render::shaders::embedded(p.file).is_some(),
692                "{}: no embedded {}",
693                p.label,
694                p.file
695            );
696        }
697    }
698
699    // Only the sample count varies per host among the enumerable dimensions;
700    // the sizes are baked at their ceilings. A program that gained a dimension
701    // the build script does not enumerate would leave that variant uncovered.
702    #[test]
703    fn the_only_per_host_variant_is_the_sample_count() {
704        let sampled = ALL.iter().filter(|p| p.msaa).count();
705        assert!(sampled > 0, "the MSAA dimension went missing");
706        assert!(sampled < ALL.len(), "every program cannot read the depth");
707        for p in ALL {
708            assert!(!p.entry.is_empty(), "{}", p.label);
709        }
710    }
711}