spirt/qptr/shapes.rs
1//! Variable shapes (untyped memory layouts vs abstract resources).
2//
3// FIXME(eddyb) does this need its own module still?
4
5use crate::{AddrSpace, Type};
6use std::num::NonZeroU32;
7
8/// `GlobalVar`s are currently used for both chunks of plain data (i.e. memory),
9/// and the "shader interface" (inherited by `Shader` SPIR-V from GLSL, whereas
10/// `Kernel` SPIR-V ended up with `OpenCL`'s "resources are passed to entry-points
11/// as regular function arguments", with `BuiltIn`+`Input` as a sole exception).
12#[derive(Copy, Clone, PartialEq, Eq)]
13pub enum GlobalVarShape {
14 /// One or more (i.e. optionally arrayed) "abstract resource" `Handle`s
15 /// (see `Handle` documentation for more on what it can represent).
16 ///
17 /// The single handle case is equivalent to a length `1` array of handles,
18 /// and as such is represented by having `fixed_count` be `Some(1)`.
19 Handles {
20 handle: Handle,
21 fixed_count: Option<NonZeroU32>,
22 },
23
24 // FIXME(eddyb) unify terminology around "concrete"/"memory"/"untyped (data)".
25 UntypedData(MemLayout),
26
27 /// Non-memory pipeline interface, which must keep the exact original type,
28 /// even if that type is concrete and could be handled just like memory.
29 ///
30 /// Typically `Input` or `Output`, but extensions (e.g. ray-tracing) may add
31 /// more such interface storage classes with strict type requirements.
32 //
33 // FIXME(eddyb) consider replacing this with by-value entry-point args/return
34 // (though that would not solve some of the weirder ones).
35 TypedInterface(Type),
36}
37
38/// "Abstract resource" handle, that can be found in non-memory `GlobalVar`s.
39///
40/// This largely corresponds to the Vulkan concept of a "descriptor", and arrays
41/// of handles (e.g. `GlobalVarShape::Handles` with `fixed_count != Some(1)`)
42/// map to the "descriptor indexing" usecase.
43//
44// FIXME(eddyb) consider implementing "descriptor indexing" more like HLSL's
45// "resource heap" (with types only specified at use sites, "casts" almost).
46#[derive(Copy, Clone, PartialEq, Eq, Hash)]
47pub enum Handle<BL = MaybeDynMemLayout> {
48 /// Fully opaque resources (e.g. samplers, images).
49 Opaque(Type),
50
51 /// Buffer resources, describing ranges of (technically) untyped memory in
52 /// some address space (e.g. `Uniform`, `StorageBuffer`), but being limited
53 /// by SPIR-V logical addressing (unlike e.g. `PhysicalStorageBuffer`).
54 ///
55 /// SPIR-V makes this particularly painful, through a couple of design flaws:
56 /// - forcing a static type (for the buffer contents) and disallowing any
57 /// pointer casts, despite the fact that any plausible representation for
58 /// "logical pointer into a buffer" (e.g. `(BufferDescriptor, Offset)`)
59 /// must be *fundamentally* untyped (as it must allow access to relatively
60 /// large amounts of memory, and also support dynamic array indexing),
61 /// even when not a "GPU memory address" (like `PhysicalStorageBuffer`)
62 /// - encoding the buffer type using a (GLSL-style) "interface block", where
63 /// instead of a special type (or a pointer with the right storage class),
64 /// an `OpTypeStruct` (having the statically typed buffer contents as fields)
65 /// with the `Block` decoration is used, and then this "interface block"
66 /// type can be further nested in `OpTypeArray` or `OpTypeRuntimeArray`
67 /// to allow descriptor indexing - which leads to constructs like a GLSL
68 /// `buffer { uint data[]; } bufs[];` being encoded with two levels of
69 /// `OpTypeRuntimeArray`, separated not by any explicit indirection, but
70 /// only by the `Block` decoration on the `OpTypeStruct` for `buffer {...}`
71 //
72 // FIXME(eddyb) should `PushConstant` use `GlobalVarShape::UntypedData`
73 // instead of being treated like a buffer?
74 //
75 // FIXME(eddyb) should this be a `Type` of its own, that can be loaded from
76 // a handle `QPtr`, and then has data pointer / length ops *on that*?
77 Buffer(AddrSpace, BL),
78}
79
80/// Untyped memory shape with constant alignment and size.
81///
82/// `align`/`legacy_align` correspond to "scalar"/"base" alignments in Vulkan,
83/// and are both kept track of to detect ambiguity in implicit layouts, e.g.
84/// field offsets when the `Offset` decoration isn't being used.
85/// Note, however, that `legacy_align` can be raised to "extended" alignment,
86/// or completeley ignored, using [`LayoutConfig`](crate::qptr::LayoutConfig).
87///
88/// Only `align` is *required*, that is `size % align == 0` must be always enforced.
89//
90// FIXME(eddyb) consider supporting specialization-constant-length arrays.
91#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
92pub struct MemLayout {
93 // FIXME(eddyb) use proper newtypes (and log2 for align!).
94 pub align: u32,
95 pub legacy_align: u32,
96 pub size: u32,
97}
98
99/// Untyped memory shape with constant alignment but potentially-dynamic size,
100/// roughly corresponding to a Rust `(FixedBase, [DynUnit])` type's layout.
101#[derive(Copy, Clone, Debug, PartialEq, Eq)]
102pub struct MaybeDynMemLayout {
103 pub fixed_base: MemLayout,
104 pub dyn_unit_stride: Option<NonZeroU32>,
105}