henad_core/authoring/model/binding.rs
1//! Binding declarations a shader makes at `@group(0)`, read from the WGSL at build time.
2//!
3//! The engine reads a pass's declarations in `@binding` order and resolves each name itself. A name
4//! in [`RESERVED`] is a resource the engine owns. Any other name is the [`BufferSpec`] label of one of the
5//! model's own buffers, with an optional `_in` or `_out` suffix. The suffix is a naming convention, and the
6//! access mode picks the side the name resolves to.
7//!
8//! [`BufferSpec`]: crate::authoring::model::gpu_agent_model::BufferSpec
9
10/// Kind of resource a binding declares, as the engine needs it for a layout entry.
11#[derive(Clone, Copy, Debug, PartialEq, Eq)]
12pub enum BindingKind {
13 /// A storage buffer, `var<storage, read>` when `read_only` and `var<storage, read_write>` otherwise.
14 Storage {
15 /// Whether the shader can only read the buffer.
16 read_only: bool,
17 },
18 /// A uniform block, `var<uniform>`.
19 Uniform,
20 /// A storage texture, `texture_storage_2d`.
21 StorageTexture,
22}
23
24impl BindingKind {
25 /// Returns whether this counts against `max_storage_buffers_per_shader_stage`.
26 ///
27 /// A uniform and a storage texture each count against their own limit.
28 pub fn is_storage_buffer(self) -> bool {
29 matches!(self, Self::Storage { .. })
30 }
31}
32
33/// One `@group(0) @binding(n)` declaration. Its position in a pass's slice is `n`.
34#[derive(Clone, Copy, Debug)]
35pub struct BindingDecl {
36 /// Variable name the shader gives the binding.
37 pub name: &'static str,
38 /// Kind of resource it binds.
39 pub kind: BindingKind,
40}
41
42/// Returns the buffer label that `decl` refers to, and whether the pass writes it.
43///
44/// Returns `None` for a reserved name, which the engine resolves without consulting the model's buffers.
45pub fn buffer_target(decl: &BindingDecl) -> Option<(&'static str, bool)> {
46 if RESERVED.contains(&decl.name) {
47 return None;
48 }
49 let label = decl
50 .name
51 .strip_suffix("_in")
52 .or_else(|| decl.name.strip_suffix("_out"))
53 .unwrap_or(decl.name);
54 let writes = !matches!(decl.kind, BindingKind::Storage { read_only: true });
55 Some((label, writes))
56}
57
58/// Names that the engine resolves itself.
59///
60/// | Name | Resource |
61/// |---|---|
62/// | `params` | The pass's own uniform block |
63/// | `dims` | Grid and display texture size, for a grid model |
64/// | `output` | The display texture |
65/// | `cell_start`, `sorted` | The neighbour index |
66/// | `counters` | The persistent counters, and a grid model's reduce totals |
67/// | `partials` | The reduction's leaf output |
68///
69/// A model cannot label a buffer with one of these names, or with a label ending in `_in` or `_out`. A binding
70/// named after such a label would resolve to the engine's own resource, or to the label without its suffix. Both
71/// engines panic at construction on a reserved or suffixed label.
72pub const RESERVED: &[&str] = &[
73 "params",
74 "dims",
75 "output",
76 "cell_start",
77 "sorted",
78 "counters",
79 "partials",
80];