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}