diff --git a/src/back/hlsl/mod.rs b/src/back/hlsl/mod.rs index 6f31ed3743..f3a6f9106c 100644 --- a/src/back/hlsl/mod.rs +++ b/src/back/hlsl/mod.rs @@ -279,6 +279,24 @@ pub struct Writer<'a, W> { /// Set of expressions that have associated temporary variables named_expressions: crate::NamedExpressions, wrapped: Wrapped, + + /// A reference to some part of a global variable, lowered to a series of + /// byte offset calculations. + /// + /// See the [`storage`] module for background on why we need this. + /// + /// Each [`SubAccess`] in the vector is a lowering of some [`Access`] or + /// [`AccessIndex`] expression to the level of byte strides and offsets. See + /// [`SubAccess`] for details. + /// + /// This field is a member of [`Writer`] solely to allow re-use of + /// the `Vec`'s dynamic allocation. The value is no longer needed + /// once HLSL for the access has been generated. + /// + /// [`Storage`]: crate::AddressSpace::Storage + /// [`SubAccess`]: storage::SubAccess + /// [`Access`]: crate::Expression::Access + /// [`AccessIndex`]: crate::Expression::AccessIndex temp_access_chain: Vec, need_bake_expressions: back::NeedBakeExpressions, } diff --git a/src/back/hlsl/storage.rs b/src/back/hlsl/storage.rs index 647a6bf870..813dd73649 100644 --- a/src/back/hlsl/storage.rs +++ b/src/back/hlsl/storage.rs @@ -1,7 +1,47 @@ /*! -Logic related to `ByteAddressBuffer` operations. +Generating accesses to [`ByteAddressBuffer`] contents. -HLSL backend uses byte address buffers for all storage buffers in IR. +Naga IR globals in the [`Storage`] address space are rendered as +[`ByteAddressBuffer`]s or [`RWByteAddressBuffer`]s in HLSL. These +buffers don't have HLSL types (structs, arrays, etc.); instead, they +are just raw blocks of bytes, with methods to load and store values of +specific types at particular byte offsets. This means that Naga must +translate chains of [`Access`] and [`AccessIndex`] expressions into +HLSL expressions that compute byte offsets into the buffer. + +To generate code for a [`Storage`] access: + +- Call [`Writer::fill_access_chain`] on the expression referring to + the value. This populates [`Writer::temp_access_chain`] with the + appropriate byte offset calculations, as a vector of [`SubAccess`] + values. + +- Call [`Writer::write_storage_address`] to emit an HLSL expression + for a given slice of [`SubAccess`] values. + +Naga IR expressions can operate on composite values of any type, but +[`ByteAddressBuffer`] and [`RWByteAddressBuffer`] have only a fixed +set of `Load` and `Store` methods, to access one through four +consecutive 32-bit values. To synthesize a Naga access, you can +initialize [`temp_access_chain`] to refer to the composite, and then +temporarily push and pop additional steps on +[`Writer::temp_access_chain`] to generate accesses to the individual +elements/members. + +The [`temp_access_chain`] field is a member of [`Writer`] solely to +allow re-use of the `Vec`'s dynamic allocation. Its value is no longer +needed once HLSL for the access has been generated. + +[`Storage`]: crate::AddressSpace::Storage +[`ByteAddressBuffer`]: https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/sm5-object-byteaddressbuffer +[`RWByteAddressBuffer`]: https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/sm5-object-rwbyteaddressbuffer +[`Access`]: crate::Expression::Access +[`AccessIndex`]: crate::Expression::AccessIndex +[`Writer::fill_access_chain`]: super::Writer::fill_access_chain +[`Writer::write_storage_address`]: super::Writer::write_storage_address +[`Writer::temp_access_chain`]: super::Writer::temp_access_chain +[`temp_access_chain`]: super::Writer::temp_access_chain +[`Writer`]: super::Writer */ use super::{super::FunctionCtx, BackendResult, Error}; @@ -14,9 +54,25 @@ use std::{fmt, mem}; const STORE_TEMP_NAME: &str = "_value"; +/// One step in accessing a [`Storage`] global's component or element. +/// +/// [`Writer::temp_access_chain`] holds a series of these structures, +/// describing how to compute the byte offset of a particular element +/// or member of some global variable in the [`Storage`] address +/// space. +/// +/// [`Writer::temp_access_chain`]: super::Writer::temp_access_chain +/// [`Storage`]: crate::AddressSpace::Storage #[derive(Debug)] pub(super) enum SubAccess { + /// Add the given byte offset. This is used for struct members, or + /// known components of a vector or matrix. In all those cases, + /// the byte offset is a compile-time constant. Offset(u32), + + /// Scale `value` by `stride`, and add that to the current byte + /// offset. This is used to compute the offset of an array element + /// whose index is computed at runtime. Index { value: Handle, stride: u32, @@ -83,7 +139,16 @@ impl super::Writer<'_, W> { Ok(()) } - /// Helper function to write down the Load operation on a `ByteAddressBuffer`. + /// Emit code to access a [`Storage`] global's component. + /// + /// Emit HLSL to access the component of `var_handle`, a global + /// variable in the [`Storage`] address space, whose type is + /// `result_ty` and whose location within the global is given by + /// [`self.temp_access_chain`]. See the [`storage`] module's + /// documentation for background. + /// + /// [`Storage`]: crate::AddressSpace::Storage + /// [`self.temp_access_chain`]: super::Writer::temp_access_chain pub(super) fn write_storage_load( &mut self, module: &crate::Module, @@ -366,6 +431,17 @@ impl super::Writer<'_, W> { Ok(()) } + /// Set [`temp_access_chain`] to compute the byte offset of `cur_expr`. + /// + /// The `cur_expr` expression must be a reference to a global + /// variable in the [`Storage`] address space, or a chain of + /// [`Access`] and [`AccessIndex`] expressions referring to some + /// component of such a global. + /// + /// [`temp_access_chain`]: super::Writer::temp_access_chain + /// [`Storage`]: crate::AddressSpace::Storage + /// [`Access`]: crate::Expression::Access + /// [`AccessIndex`]: crate::Expression::AccessIndex pub(super) fn fill_access_chain( &mut self, module: &crate::Module,