Skip to main content

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];