From ad25998c028366786d18527a5cf16a24ab8a7921 Mon Sep 17 00:00:00 2001 From: "Felipe A. Costa" Date: Mon, 7 Aug 2023 22:06:47 -0300 Subject: [PATCH 1/4] [spv-out] Support for buffer device addresses --- src/back/glsl/mod.rs | 4 +++ src/back/hlsl/writer.rs | 5 ++++ src/back/msl/writer.rs | 9 ++++-- src/back/spv/block.rs | 26 ++++++++++++++--- src/back/spv/helpers.rs | 1 + src/back/spv/instructions.rs | 19 ++++++++++++ src/back/spv/writer.rs | 26 ++++++++++++++++- src/lib.rs | 6 ++++ src/proc/layouter.rs | 12 ++++++++ src/proc/mod.rs | 10 +++++++ src/valid/analyzer.rs | 1 + src/valid/interface.rs | 4 +++ src/valid/mod.rs | 5 ++++ src/valid/type.rs | 56 ++++++++++++++++++++++++++++++------ 14 files changed, 168 insertions(+), 16 deletions(-) diff --git a/src/back/glsl/mod.rs b/src/back/glsl/mod.rs index 77e206d3c6..e5fa3e1c59 100644 --- a/src/back/glsl/mod.rs +++ b/src/back/glsl/mod.rs @@ -108,6 +108,7 @@ impl crate::AddressSpace { crate::AddressSpace::WorkGroup | crate::AddressSpace::Uniform | crate::AddressSpace::Storage { .. } + | crate::AddressSpace::PhysicalStorage { .. } | crate::AddressSpace::Handle | crate::AddressSpace::PushConstant => false, } @@ -1072,6 +1073,8 @@ impl<'a, W: Write> Writer<'a, W> { crate::AddressSpace::Storage { .. } => { self.write_interface_block(handle, global)?; } + // The PhysicalStorage address space is only for pointers. + crate::AddressSpace::PhysicalStorage { .. } => unreachable!(), // A global variable in the `Function` address space is a // contradiction in terms. crate::AddressSpace::Function => unreachable!(), @@ -4180,6 +4183,7 @@ const fn glsl_storage_qualifier(space: crate::AddressSpace) -> Option<&'static s As::Function => None, As::Private => None, As::Storage { .. } => Some("buffer"), + As::PhysicalStorage { .. } => Some("buffer_reference"), As::Uniform => Some("uniform"), As::Handle => Some("uniform"), As::WorkGroup => Some("shared"), diff --git a/src/back/hlsl/writer.rs b/src/back/hlsl/writer.rs index 590850fc4d..c6e146bc26 100644 --- a/src/back/hlsl/writer.rs +++ b/src/back/hlsl/writer.rs @@ -688,6 +688,11 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> { write!(self.out, "{prefix}ByteAddressBuffer")?; register } + crate::AddressSpace::PhysicalStorage { .. } => { + return Err(Error::Unimplemented( + "physical storage address space is not implemented for HLSL".to_string(), + )); + } crate::AddressSpace::Handle => { let handle_ty = match *inner { TypeInner::BindingArray { ref base, .. } => &module.types[*base].inner, diff --git a/src/back/msl/writer.rs b/src/back/msl/writer.rs index d0f5413136..67ac781ad7 100644 --- a/src/back/msl/writer.rs +++ b/src/back/msl/writer.rs @@ -399,6 +399,7 @@ impl crate::AddressSpace { match *self { Self::Uniform | Self::Storage { .. } + | Self::PhysicalStorage { .. } | Self::Private | Self::WorkGroup | Self::PushConstant @@ -414,7 +415,7 @@ impl crate::AddressSpace { // rely on the actual use of a global by functions. This means we // may end up with "const" even if the binding is read-write, // and that should be OK. - Self::Storage { .. } => true, + Self::Storage { .. } | Self::PhysicalStorage { .. } => true, // These should always be read-write. Self::Private | Self::WorkGroup => false, // These translate to `constant` address space, no need for qualifiers. @@ -424,11 +425,14 @@ impl crate::AddressSpace { } } - const fn to_msl_name(self) -> Option<&'static str> { + fn to_msl_name(self) -> Option<&'static str> { match self { Self::Handle => None, Self::Uniform | Self::PushConstant => Some("constant"), Self::Storage { .. } => Some("device"), + Self::PhysicalStorage { .. } => { + unreachable!("physical storage address space is not implemented for MSL") + } Self::Private | Self::Function => Some("thread"), Self::WorkGroup => Some("threadgroup"), } @@ -3677,6 +3681,7 @@ impl Writer { } crate::AddressSpace::Function | crate::AddressSpace::Private + | crate::AddressSpace::PhysicalStorage { .. } | crate::AddressSpace::WorkGroup => {} } } diff --git a/src/back/spv/block.rs b/src/back/spv/block.rs index 4dba7ea0ca..20655859b8 100644 --- a/src/back/spv/block.rs +++ b/src/back/spv/block.rs @@ -1082,15 +1082,22 @@ impl<'w> BlockContext<'w> { match self.write_expression_pointer(pointer, block, None)? { ExpressionPointer::Ready { pointer_id } => { let id = self.gen_id(); - let atomic_space = + let (atomic_space, physical_alignment) = match *self.fun_info[pointer].ty.inner_with(&self.ir_module.types) { crate::TypeInner::Pointer { base, space } => { - match self.ir_module.types[base].inner { + let physical_alignment = match space { + crate::AddressSpace::PhysicalStorage { alignment } => { + Some(alignment) + } + _ => None, + }; + let atomic_space = match self.ir_module.types[base].inner { crate::TypeInner::Atomic { .. } => Some(space), _ => None, - } + }; + (atomic_space, physical_alignment) } - _ => None, + _ => (None, None), }; let instruction = if let Some(space) = atomic_space { let (semantics, scope) = space.to_spirv_semantics_and_scope(); @@ -1103,6 +1110,14 @@ impl<'w> BlockContext<'w> { scope_constant_id, semantics_id, ) + } else if let Some(alignment) = physical_alignment { + Instruction::load_aligned( + result_type_id, + id, + pointer_id, + alignment, + None, + ) } else { Instruction::load(result_type_id, id, pointer_id, None) }; @@ -1549,6 +1564,9 @@ impl<'w> BlockContext<'w> { crate::Expression::FunctionArgument(index) => { break self.function.parameter_id(index); } + crate::Expression::Load { .. } => { + break self.cached[expr_handle]; + } ref other => unimplemented!("Unexpected pointer expression {:?}", other), } }; diff --git a/src/back/spv/helpers.rs b/src/back/spv/helpers.rs index 5b6226db85..88f2a3561e 100644 --- a/src/back/spv/helpers.rs +++ b/src/back/spv/helpers.rs @@ -26,6 +26,7 @@ pub(super) const fn map_storage_class(space: crate::AddressSpace) -> spirv::Stor crate::AddressSpace::Function => spirv::StorageClass::Function, crate::AddressSpace::Private => spirv::StorageClass::Private, crate::AddressSpace::Storage { .. } => spirv::StorageClass::StorageBuffer, + crate::AddressSpace::PhysicalStorage { .. } => spirv::StorageClass::PhysicalStorageBuffer, crate::AddressSpace::Uniform => spirv::StorageClass::Uniform, crate::AddressSpace::WorkGroup => spirv::StorageClass::Workgroup, crate::AddressSpace::PushConstant => spirv::StorageClass::PushConstant, diff --git a/src/back/spv/instructions.rs b/src/back/spv/instructions.rs index b963793ad3..3abb896bd8 100644 --- a/src/back/spv/instructions.rs +++ b/src/back/spv/instructions.rs @@ -446,6 +446,25 @@ impl super::Instruction { instruction } + pub(super) fn load_aligned( + result_type_id: Word, + id: Word, + pointer_id: Word, + alignment: u32, + memory_access: Option, + ) -> Self { + let mut instruction = Self::new(Op::Load); + instruction.set_type(result_type_id); + instruction.set_result(id); + instruction.add_operand(pointer_id); + + let memory_access = memory_access.unwrap_or(spirv::MemoryAccess::ALIGNED); + instruction.add_operand(memory_access.bits()); + instruction.add_operand(alignment); + + instruction + } + pub(super) fn atomic_load( result_type_id: Word, id: Word, diff --git a/src/back/spv/writer.rs b/src/back/spv/writer.rs index 2e133985af..69ba1c4dd8 100644 --- a/src/back/spv/writer.rs +++ b/src/back/spv/writer.rs @@ -879,6 +879,15 @@ impl Writer { _ => {} } } + crate::TypeInner::Pointer { + space: crate::AddressSpace::PhysicalStorage { .. }, + .. + } => { + self.require_any( + "Buffer Pointer", + &[spirv::Capability::PhysicalStorageBufferAddresses], + )?; + } crate::TypeInner::AccelerationStructure => { self.require_any("Acceleration Structure", &[spirv::Capability::RayQueryKHR])?; } @@ -1905,6 +1914,17 @@ impl Writer { self.write_type_declaration_arena(&ir_module.types, handle)?; } + let has_physical_storage_pointer = self + .capabilities_used + .contains(&spirv::Capability::PhysicalStorageBufferAddresses); + if self.physical_layout.version < 0x10500 && has_physical_storage_pointer { + // enable the physical storage buffer class on < SPV-1.5 + Instruction::extension("SPV_KHR_physical_storage_buffer") + .to_words(&mut self.logical_layout.extensions); + Instruction::extension("GL_EXT_buffer_reference") + .to_words(&mut self.logical_layout.extensions); + } + // write all const-expressions as constants self.constant_ids .resize(ir_module.const_expressions.len(), 0); @@ -1980,7 +2000,11 @@ impl Writer { .to_words(&mut self.logical_layout.capabilities); } - let addressing_model = spirv::AddressingModel::Logical; + let addressing_model = if has_physical_storage_pointer { + spirv::AddressingModel::PhysicalStorageBuffer64 + } else { + spirv::AddressingModel::Logical + }; let memory_model = spirv::MemoryModel::GLSL450; //self.check(addressing_model.required_capabilities())?; //self.check(memory_model.required_capabilities())?; diff --git a/src/lib.rs b/src/lib.rs index 9d70190421..a537548ec3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -403,6 +403,12 @@ pub enum AddressSpace { Handle, /// Push constants. PushConstant, + /// Buffer device pointers, mutable. Only used for pointers, not allowed on GlobalVariables. + /// Pointers in this address space reference buffer memory directly. + /// Requires capability [`crate::valid::Capabilities::PHYSICAL_STORAGE_BUFFER_ADDRESSES`]. + /// Required for Vulkan extension `VK_KHR_buffer_device_address`: + /// + PhysicalStorage { alignment: u32 }, } /// Built-in inputs and outputs. diff --git a/src/proc/layouter.rs b/src/proc/layouter.rs index 4c52e5ec81..9166033309 100644 --- a/src/proc/layouter.rs +++ b/src/proc/layouter.rs @@ -14,6 +14,7 @@ impl Alignment { pub const EIGHT: Self = Self(unsafe { NonZeroU32::new_unchecked(8) }); pub const SIXTEEN: Self = Self(unsafe { NonZeroU32::new_unchecked(16) }); + pub const BUFFER_POINTER: Self = Self::EIGHT; pub const MIN_UNIFORM: Self = Self::SIXTEEN; pub const fn new(n: u32) -> Option { @@ -200,6 +201,17 @@ impl Layouter { alignment: Alignment::from(rows) * alignment, } } + Ti::Pointer { + space: crate::AddressSpace::PhysicalStorage { .. }, + .. + } + | Ti::ValuePointer { + space: crate::AddressSpace::PhysicalStorage { .. }, + .. + } => TypeLayout { + size, + alignment: Alignment::BUFFER_POINTER, + }, Ti::Pointer { .. } | Ti::ValuePointer { .. } => TypeLayout { size, alignment: Alignment::ONE, diff --git a/src/proc/mod.rs b/src/proc/mod.rs index d50f165a56..d565ba7920 100644 --- a/src/proc/mod.rs +++ b/src/proc/mod.rs @@ -162,6 +162,7 @@ impl crate::Literal { } pub const POINTER_SPAN: u32 = 4; +pub const BUFFER_POINTER_SPAN: u32 = 8; impl super::TypeInner { pub const fn scalar_kind(&self) -> Option { @@ -218,6 +219,14 @@ impl super::TypeInner { rows, width, } => Alignment::from(rows) * width as u32 * columns as u32, + Self::Pointer { + space: crate::AddressSpace::PhysicalStorage { .. }, + .. + } + | Self::ValuePointer { + space: crate::AddressSpace::PhysicalStorage { .. }, + .. + } => BUFFER_POINTER_SPAN, Self::Pointer { .. } | Self::ValuePointer { .. } => POINTER_SPAN, Self::Array { base: _, @@ -346,6 +355,7 @@ impl super::AddressSpace { | crate::AddressSpace::WorkGroup => Sa::LOAD | Sa::STORE, crate::AddressSpace::Uniform => Sa::LOAD, crate::AddressSpace::Storage { access } => access, + crate::AddressSpace::PhysicalStorage { .. } => Sa::LOAD, crate::AddressSpace::Handle => Sa::LOAD, crate::AddressSpace::PushConstant => Sa::LOAD, } diff --git a/src/valid/analyzer.rs b/src/valid/analyzer.rs index ff1db071c8..c86c58254f 100644 --- a/src/valid/analyzer.rs +++ b/src/valid/analyzer.rs @@ -576,6 +576,7 @@ impl FunctionInfo { As::Uniform | As::PushConstant => true, // storage data is only uniform when read-only As::Storage { access } => !access.contains(crate::StorageAccess::STORE), + As::PhysicalStorage { .. } => false, As::Handle => false, }; Uniformity { diff --git a/src/valid/interface.rs b/src/valid/interface.rs index 7c6c5244e4..f42d101650 100644 --- a/src/valid/interface.rs +++ b/src/valid/interface.rs @@ -489,6 +489,9 @@ impl super::Validator { } (TypeFlags::DATA | TypeFlags::HOST_SHAREABLE, true) } + crate::AddressSpace::PhysicalStorage { .. } => { + return Err(GlobalVariableError::InvalidUsage(var.space)) + } crate::AddressSpace::Uniform => { if let Err((ty_handle, disalignment)) = type_info.uniform_layout { if self.flags.contains(super::ValidationFlags::STRUCT_LAYOUTS) { @@ -739,6 +742,7 @@ impl super::Validator { crate::AddressSpace::Function => unreachable!(), crate::AddressSpace::Uniform => GlobalUse::READ | GlobalUse::QUERY, crate::AddressSpace::Storage { access } => storage_usage(access), + crate::AddressSpace::PhysicalStorage { .. } => unreachable!(), crate::AddressSpace::Handle => match module.types[var.ty].inner { crate::TypeInner::BindingArray { base, .. } => match module.types[base].inner { crate::TypeInner::Image { diff --git a/src/valid/mod.rs b/src/valid/mod.rs index 8c065bb159..98216b4a6b 100644 --- a/src/valid/mod.rs +++ b/src/valid/mod.rs @@ -115,6 +115,11 @@ bitflags::bitflags! { const RAY_QUERY = 0x1000; /// Support for generating two sources for blending from fragement shaders const DUAL_SOURCE_BLENDING = 0x2000; + /// Support for pointers to device buffers. + /// Required for using the [`crate::AddressSpace::PhysicalStorage`] pointer address space. + /// Required by Vulkan extension `VK_KHR_buffer_device_address`: + /// + const PHYSICAL_STORAGE_BUFFER_ADDRESSES = 0x2000; } } diff --git a/src/valid/type.rs b/src/valid/type.rs index c0d8a12956..b8244d6c4a 100644 --- a/src/valid/type.rs +++ b/src/valid/type.rs @@ -168,7 +168,9 @@ fn check_member_layout( const fn ptr_space_argument_flag(space: crate::AddressSpace) -> TypeFlags { use crate::AddressSpace as As; match space { - As::Function | As::Private | As::WorkGroup => TypeFlags::ARGUMENT, + As::Function | As::Private | As::WorkGroup | As::PhysicalStorage { .. } => { + TypeFlags::ARGUMENT + } As::Uniform | As::Storage { .. } | As::Handle | As::PushConstant => TypeFlags::empty(), } } @@ -342,11 +344,28 @@ impl super::Validator { // best to set `ARGUMENT` accurately anyway. let argument_flag = ptr_space_argument_flag(space); - // Pointers cannot be stored in variables, structure members, or - // array elements, so we do not mark them as `DATA`. + let data_flag = if let As::PhysicalStorage { .. } = space { + // If the pointer is in the [`crate::AddressSpace::PhysicalStorage`] + // address space, we require the appropriate SPIR-V capability. + self.require_type_capability(Capabilities::PHYSICAL_STORAGE_BUFFER_ADDRESSES)?; + TypeFlags::DATA | TypeFlags::HOST_SHAREABLE + } else { + // Pointers that aren't in the [`crate::AddressSpace::PhysicalStorage`] + // address space cannot be stored in variables, structure members, or + // array elements, so we do not mark them as `DATA`. + TypeFlags::empty() + }; + + let alignment = if let As::PhysicalStorage { .. } = space { + // Buffer pointers require proper alignment. + Alignment::BUFFER_POINTER + } else { + Alignment::ONE + }; + TypeInfo::new( - argument_flag | TypeFlags::SIZED | TypeFlags::COPY, - Alignment::ONE, + data_flag | argument_flag | TypeFlags::SIZED | TypeFlags::COPY, + alignment, ) } Ti::ValuePointer { @@ -355,6 +374,8 @@ impl super::Validator { width, space, } => { + use crate::AddressSpace as As; + // ValuePointer should be treated the same way as the equivalent // Pointer / Scalar / Vector combination, so each step in those // variants' match arms should have a counterpart here. @@ -370,11 +391,28 @@ impl super::Validator { // best to set `ARGUMENT` accurately anyway. let argument_flag = ptr_space_argument_flag(space); - // Pointers cannot be stored in variables, structure members, or - // array elements, so we do not mark them as `DATA`. + let data_flag = if let As::PhysicalStorage { .. } = space { + // If the pointer is in the [`crate::AddressSpace::PhysicalStorage`] + // address space, we require the appropriate SPIR-V capability. + self.require_type_capability(Capabilities::PHYSICAL_STORAGE_BUFFER_ADDRESSES)?; + TypeFlags::DATA | TypeFlags::HOST_SHAREABLE + } else { + // Pointers that aren't in the [`crate::AddressSpace::PhysicalStorage`] + // address space cannot be stored in variables, structure members, or + // array elements, so we do not mark them as `DATA`. + TypeFlags::empty() + }; + + let alignment = if let As::PhysicalStorage { .. } = space { + // Buffer pointers require proper alignment. + Alignment::BUFFER_POINTER + } else { + Alignment::ONE + }; + TypeInfo::new( - argument_flag | TypeFlags::SIZED | TypeFlags::COPY, - Alignment::ONE, + data_flag | argument_flag | TypeFlags::SIZED | TypeFlags::COPY, + alignment, ) } Ti::Array { base, size, stride } => { From d5a77e46282c052543a19ae4fe04df67babc4075 Mon Sep 17 00:00:00 2001 From: "Felipe A. Costa" Date: Fri, 25 Aug 2023 15:44:26 -0300 Subject: [PATCH 2/4] [wgsl-in] Support for buffer device addresses --- src/back/wgsl/writer.rs | 9 +++++---- src/front/wgsl/parse/conv.rs | 5 +++++ src/front/wgsl/parse/mod.rs | 32 ++++++++++++++++++++++++++------ 3 files changed, 36 insertions(+), 10 deletions(-) diff --git a/src/back/wgsl/writer.rs b/src/back/wgsl/writer.rs index 36380952af..d16a682315 100644 --- a/src/back/wgsl/writer.rs +++ b/src/back/wgsl/writer.rs @@ -1870,9 +1870,7 @@ const fn sampling_str(sampling: crate::Sampling) -> &'static str { } } -const fn address_space_str( - space: crate::AddressSpace, -) -> (Option<&'static str>, Option<&'static str>) { +fn address_space_str(space: crate::AddressSpace) -> (Option<&'static str>, Option) { use crate::AddressSpace as As; ( @@ -1881,11 +1879,14 @@ const fn address_space_str( As::Uniform => "uniform", As::Storage { access } => { if access.contains(crate::StorageAccess::STORE) { - return (Some("storage"), Some("read_write")); + return (Some("storage"), Some("read_write".to_string())); } else { "storage" } } + As::PhysicalStorage { alignment } => { + return (Some("buffer"), Some(alignment.to_string())) + } As::PushConstant => "push_constant", As::WorkGroup => "workgroup", As::Handle => return (None, None), diff --git a/src/front/wgsl/parse/conv.rs b/src/front/wgsl/parse/conv.rs index 51977173d6..ff83ace440 100644 --- a/src/front/wgsl/parse/conv.rs +++ b/src/front/wgsl/parse/conv.rs @@ -9,6 +9,11 @@ pub fn map_address_space(word: &str, span: Span) -> Result Ok(crate::AddressSpace::Storage { access: crate::StorageAccess::default(), }), + "buffer" => Ok(crate::AddressSpace::PhysicalStorage { + // Follow the GLSL spec and use 16 byte alignment as the default + // https://github.com/KhronosGroup/GLSL/blob/ad383324612d8493c78d52d0c903969fe1dddd0a/extensions/ext/GLSL_EXT_buffer_reference.txt#L139 + alignment: 16, + }), "push_constant" => Ok(crate::AddressSpace::PushConstant), "function" => Ok(crate::AddressSpace::Function), _ => Err(Error::UnknownAddressSpace(span)), diff --git a/src/front/wgsl/parse/mod.rs b/src/front/wgsl/parse/mod.rs index 74738ab231..985a635b5b 100644 --- a/src/front/wgsl/parse/mod.rs +++ b/src/front/wgsl/parse/mod.rs @@ -1220,12 +1220,32 @@ impl Parser { let mut space = conv::map_address_space(ident, span)?; lexer.expect(Token::Separator(','))?; let base = self.type_decl(lexer, ctx)?; - if let crate::AddressSpace::Storage { ref mut access } = space { - *access = if lexer.skip(Token::Separator(',')) { - lexer.next_storage_access()? - } else { - crate::StorageAccess::LOAD - }; + match space { + crate::AddressSpace::Storage { ref mut access } => { + *access = if lexer.skip(Token::Separator(',')) { + lexer.next_storage_access()? + } else { + crate::StorageAccess::LOAD + }; + } + crate::AddressSpace::PhysicalStorage { ref mut alignment } => { + *alignment = if lexer.skip(Token::Separator(',')) { + // TODO: we should probably use the constant expression evaluator here + (match lexer.next() { + (Token::Number(Ok(Number::I32(num))), _span) => { + u32::try_from(num).map_err(|_| Error::ExpectedNonNegative(span)) + } + (Token::Number(Err(e)), span) => Err(Error::BadNumber(span, e)), + other => Err(Error::Unexpected( + other.1, + ExpectedToken::PrimaryExpression, + )), + })? + } else { + *alignment + }; + } + _ => {} } lexer.expect_generic_paren('>')?; ast::Type::Pointer { base, space } From 2f64d57205ec777dc2df83cac753320d95e8116c Mon Sep 17 00:00:00 2001 From: "Felipe A. Costa" Date: Wed, 16 Aug 2023 21:47:06 -0300 Subject: [PATCH 3/4] [spv-out] Fix intermediate access expression on buffer pointers Allows compilation of this kind of access: ``` struct OtherStruct { data: float, } struct MyBuffer { sub_buf: ptr } struct PushConstants { buf: ptr, } var pc: PushConstants; var d = (*(*pc.buf).sub_buf).data; ``` Before you would have to use intermediate variables: ``` var b = (*pc.buf); var d = (*b.sub_buf).data; ``` --- src/back/spv/block.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/back/spv/block.rs b/src/back/spv/block.rs index 20655859b8..fe37842e27 100644 --- a/src/back/spv/block.rs +++ b/src/back/spv/block.rs @@ -213,6 +213,22 @@ impl<'w> BlockContext<'w> { let arg = &self.ir_function.arguments[index as usize]; self.ir_module.types[arg.ty].inner.pointer_space().is_some() } + crate::Expression::Load { pointer } => { + match *self.fun_info[pointer].ty.inner_with(&self.ir_module.types) { + crate::TypeInner::Pointer { base, .. } => { + if let crate::TypeInner::Pointer { + space: crate::AddressSpace::PhysicalStorage { .. }, + .. + } = self.ir_module.types[base].inner + { + true + } else { + false + } + } + _ => false, + } + } // The chain rule: if this `Access...`'s `base` operand was // previously omitted, then omit this one, too. From 1e0609d20d2c99017c0adfe7ba334807df64b99a Mon Sep 17 00:00:00 2001 From: "Felipe A. Costa" Date: Tue, 8 Aug 2023 00:56:37 -0300 Subject: [PATCH 4/4] Add WGSL test for buffer pointers --- tests/in/buffer-pointer.param.ron | 6 +++ tests/in/buffer-pointer.wgsl | 28 +++++++++++ tests/out/spv/buffer-pointer.spvasm | 75 +++++++++++++++++++++++++++++ tests/snapshots.rs | 1 + 4 files changed, 110 insertions(+) create mode 100644 tests/in/buffer-pointer.param.ron create mode 100644 tests/in/buffer-pointer.wgsl create mode 100644 tests/out/spv/buffer-pointer.spvasm diff --git a/tests/in/buffer-pointer.param.ron b/tests/in/buffer-pointer.param.ron new file mode 100644 index 0000000000..58f4bf62b5 --- /dev/null +++ b/tests/in/buffer-pointer.param.ron @@ -0,0 +1,6 @@ +( + god_mode: true, + spv: ( + version: (1, 5), + ), +) diff --git a/tests/in/buffer-pointer.wgsl b/tests/in/buffer-pointer.wgsl new file mode 100644 index 0000000000..457c12394c --- /dev/null +++ b/tests/in/buffer-pointer.wgsl @@ -0,0 +1,28 @@ +struct OtherStruct { + data: vec2, +} + +struct MyBuffer { + data: vec4, + sub_buffer: ptr, // Custom alignment = 8 +} + +// TODO: recursive references +// struct MyBuffer2 { +// data: f32, +// next: ptr, +// } + +struct PushConstants { + buf1: ptr, // Default alignment = 16 + four_bytes: u32, // Test struct member offset + buf2: ptr, // Custom alignment = 4 +} +var pc: PushConstants; + +@fragment +fn main() -> @location(0) vec4 { + var d1 = (*pc.buf1).data; + var d2 = (*(*pc.buf2).sub_buffer).data; + return d1 + vec4(d2, 0.0, 0.0); +} diff --git a/tests/out/spv/buffer-pointer.spvasm b/tests/out/spv/buffer-pointer.spvasm new file mode 100644 index 0000000000..a79963e9b7 --- /dev/null +++ b/tests/out/spv/buffer-pointer.spvasm @@ -0,0 +1,75 @@ +; SPIR-V +; Version: 1.5 +; Generator: rspirv +; Bound: 52 +OpCapability Shader +OpCapability PhysicalStorageBufferAddresses +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel PhysicalStorageBuffer64 GLSL450 +OpEntryPoint Fragment %18 "main" %16 %12 +OpExecutionMode %18 OriginUpperLeft +OpMemberDecorate %5 0 Offset 0 +OpMemberDecorate %8 0 Offset 0 +OpMemberDecorate %8 1 Offset 16 +OpMemberDecorate %11 0 Offset 0 +OpMemberDecorate %11 1 Offset 8 +OpMemberDecorate %11 2 Offset 16 +OpDecorate %13 Block +OpMemberDecorate %13 0 Offset 0 +OpDecorate %16 Location 0 +%2 = OpTypeVoid +%4 = OpTypeFloat 32 +%3 = OpTypeVector %4 2 +%5 = OpTypeStruct %3 +%6 = OpTypeVector %4 4 +%7 = OpTypePointer PhysicalStorageBuffer %5 +%8 = OpTypeStruct %6 %7 +%9 = OpTypePointer PhysicalStorageBuffer %8 +%10 = OpTypeInt 32 0 +%11 = OpTypeStruct %9 %10 %9 +%13 = OpTypeStruct %11 +%14 = OpTypePointer PushConstant %13 +%12 = OpVariable %14 PushConstant +%17 = OpTypePointer Output %6 +%16 = OpVariable %17 Output +%19 = OpTypeFunction %2 +%20 = OpTypePointer PushConstant %11 +%21 = OpConstant %10 0 +%23 = OpConstant %4 0.0 +%25 = OpTypePointer Function %6 +%26 = OpConstantNull %6 +%28 = OpTypePointer Function %3 +%29 = OpConstantNull %3 +%31 = OpTypePointer PushConstant %9 +%34 = OpTypePointer PhysicalStorageBuffer %6 +%37 = OpTypePointer PushConstant %9 +%38 = OpConstant %10 2 +%41 = OpTypePointer PhysicalStorageBuffer %7 +%42 = OpConstant %10 1 +%45 = OpTypePointer PhysicalStorageBuffer %3 +%18 = OpFunction %2 None %19 +%15 = OpLabel +%24 = OpVariable %25 Function %26 +%27 = OpVariable %28 Function %29 +%22 = OpAccessChain %20 %12 %21 +OpBranch %30 +%30 = OpLabel +%32 = OpAccessChain %31 %22 %21 +%33 = OpLoad %9 %32 +%35 = OpAccessChain %34 %33 %21 +%36 = OpLoad %6 %35 Aligned 16 +OpStore %24 %36 +%39 = OpAccessChain %37 %22 %38 +%40 = OpLoad %9 %39 +%43 = OpAccessChain %41 %40 %42 +%44 = OpLoad %7 %43 Aligned 4 +%46 = OpAccessChain %45 %44 %21 +%47 = OpLoad %3 %46 Aligned 8 +OpStore %27 %47 +%48 = OpLoad %6 %24 +%49 = OpLoad %3 %27 +%50 = OpCompositeConstruct %6 %49 %23 %23 +%51 = OpFAdd %6 %48 %50 +OpStore %16 %51 +OpReturn +OpFunctionEnd \ No newline at end of file diff --git a/tests/snapshots.rs b/tests/snapshots.rs index 2bc7f45444..8469b115fe 100644 --- a/tests/snapshots.rs +++ b/tests/snapshots.rs @@ -781,6 +781,7 @@ fn convert_wgsl() { "const-exprs", Targets::SPIRV | Targets::METAL | Targets::GLSL | Targets::HLSL | Targets::WGSL, ), + ("buffer-pointer", Targets::SPIRV), ]; for &(name, targets) in inputs.iter() {