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