spv-out: Support arrayLength of a dynamically indexed bindings array

This commit is contained in:
Dzmitry Malyshau 2024-03-23 11:51:29 -07:00 committed by Teodor Tanasoaia
parent bfe0b90740
commit 3a467ad93d
5 changed files with 58 additions and 17 deletions

View File

@ -48,25 +48,39 @@ impl<'w> BlockContext<'w> {
// inside a buffer that is itself an element in a buffer bindings array.
// SPIR-V requires that runtime-sized arrays are wrapped in structs.
// See `helpers::global_needs_wrapper` and its uses.
let (opt_array_index, global_handle, opt_last_member_index) = match self
let (opt_array_index_id, global_handle, opt_last_member_index) = match self
.ir_function
.expressions[array]
{
// Note that SPIR-V forbids `OpArrayLength` on a variable pointer,
// so we aren't handling `crate::Expression::Access` here.
crate::Expression::AccessIndex { base, index } => {
match self.ir_function.expressions[base] {
// The global variable is an array of buffer bindings of structs,
// and we are accessing the last member.
// we are accessing one of them with a static index,
// and the last member of it.
crate::Expression::AccessIndex {
base: base_outer,
index: index_outer,
} => match self.ir_function.expressions[base_outer] {
crate::Expression::GlobalVariable(handle) => {
(Some(index_outer), handle, Some(index))
let index_id = self.get_index_constant(index_outer);
(Some(index_id), handle, Some(index))
}
_ => return Err(Error::Validation("array length expression case-1a")),
},
// The global variable is an array of buffer bindings of structs,
// we are accessing one of them with a dynamic index,
// and the last member of it.
crate::Expression::Access {
base: base_outer,
index: index_outer,
} => match self.ir_function.expressions[base_outer] {
crate::Expression::GlobalVariable(handle) => {
let index_id = self.cached[index_outer];
(Some(index_id), handle, Some(index))
}
_ => return Err(Error::Validation("array length expression case-1b")),
},
// The global variable is a buffer, and we are accessing the last member.
crate::Expression::GlobalVariable(handle) => {
let global = &self.ir_module.global_variables[handle];
match self.ir_module.types[global.ty].inner {
@ -79,15 +93,27 @@ impl<'w> BlockContext<'w> {
_ => return Err(Error::Validation("array length expression case-1c")),
}
}
// The global variable is an array of buffer bindings of arrays.
crate::Expression::Access { base, index } => match self.ir_function.expressions[base] {
crate::Expression::GlobalVariable(handle) => {
let index_id = self.cached[index];
let global = &self.ir_module.global_variables[handle];
match self.ir_module.types[global.ty].inner {
crate::TypeInner::BindingArray { .. } => (Some(index_id), handle, None),
_ => return Err(Error::Validation("array length expression case-2a")),
}
}
_ => return Err(Error::Validation("array length expression case-2b")),
},
// The global variable is a run-time array.
crate::Expression::GlobalVariable(handle) => {
let global = &self.ir_module.global_variables[handle];
if !global_needs_wrapper(self.ir_module, global) {
return Err(Error::Validation("array length expression case-2"));
return Err(Error::Validation("array length expression case-3"));
}
(None, handle, None)
}
_ => return Err(Error::Validation("array length expression case-3")),
_ => return Err(Error::Validation("array length expression case-4")),
};
let gvar = self.writer.global_variables[global_handle.index()].clone();
@ -103,17 +129,16 @@ impl<'w> BlockContext<'w> {
(0, gvar.var_id)
}
};
let structure_id = match opt_array_index {
let structure_id = match opt_array_index_id {
// We are indexing inside a binding array, generate the access op.
Some(index) => {
Some(index_id) => {
let element_type_id = match self.ir_module.types[global.ty].inner {
crate::TypeInner::BindingArray { base, size: _ } => {
let class = map_storage_class(global.space);
self.get_pointer_id(base, class)?
}
_ => return Err(Error::Validation("array length expression case-4")),
_ => return Err(Error::Validation("array length expression case-5")),
};
let index_id = self.get_index_constant(index);
let structure_id = self.gen_id();
block.body.push(Instruction::access_chain(
element_type_id,

View File

@ -510,7 +510,6 @@ impl super::Validator {
ti.uniform_layout = Ok(Alignment::MIN_UNIFORM);
let mut min_offset = 0;
let mut prev_struct_data: Option<(u32, u32)> = None;
for (i, member) in members.iter().enumerate() {
@ -662,6 +661,7 @@ impl super::Validator {
// Currently Naga only supports binding arrays of structs for non-handle types.
match gctx.types[base].inner {
crate::TypeInner::Struct { .. } => {}
crate::TypeInner::Array { .. } => {}
_ => return Err(TypeError::BindingArrayBaseTypeNotStruct(base)),
};
}

View File

@ -24,6 +24,8 @@ fn main(fragment_in: FragmentIn) -> @location(0) u32 {
u1 += storage_array[non_uniform_index].x;
u1 += arrayLength(&storage_array[0].far);
u1 += arrayLength(&storage_array[uniform_index].far);
u1 += arrayLength(&storage_array[non_uniform_index].far);
return u1;
}

View File

@ -1,7 +1,7 @@
; SPIR-V
; Version: 1.1
; Generator: rspirv
; Bound: 68
; Bound: 76
OpCapability Shader
OpCapability ShaderNonUniform
OpExtension "SPV_KHR_storage_buffer_storage_class"
@ -104,7 +104,17 @@ OpStore %31 %61
%65 = OpLoad %3 %31
%66 = OpIAdd %3 %65 %64
OpStore %31 %66
%67 = OpLoad %3 %31
OpStore %23 %67
%67 = OpAccessChain %38 %11 %36
%68 = OpArrayLength %3 %67 1
%69 = OpLoad %3 %31
%70 = OpIAdd %3 %69 %68
OpStore %31 %70
%71 = OpAccessChain %38 %11 %37
%72 = OpArrayLength %3 %71 1
%73 = OpLoad %3 %31
%74 = OpIAdd %3 %73 %72
OpStore %31 %74
%75 = OpLoad %3 %31
OpStore %23 %75
OpReturn
OpFunctionEnd

View File

@ -33,6 +33,10 @@ fn main(fragment_in: FragmentIn) -> @location(0) @interpolate(flat) u32 {
u1_ = (_e23 + _e22);
let _e29 = u1_;
u1_ = (_e29 + arrayLength((&storage_array[0].far)));
let _e31 = u1_;
return _e31;
let _e35 = u1_;
u1_ = (_e35 + arrayLength((&storage_array[uniform_index].far)));
let _e41 = u1_;
u1_ = (_e41 + arrayLength((&storage_array[non_uniform_index].far)));
let _e43 = u1_;
return _e43;
}