Replies: 6 comments 3 replies
-
That indeed looks tricky. Is your shader open source? Also you can try enabling all logging - |
Beta Was this translation helpful? Give feedback.
-
I am hitting a similar problem @tombh - what did you use to cross compile your |
Beta Was this translation helpful? Give feedback.
-
Also, resolving this issue should improve our experience as well gfx-rs/wgpu#6363. |
Beta Was this translation helpful? Give feedback.
-
I've managed to get a minimal reproducible Rust shader: #![cfg_attr(target_arch = "spirv", no_std)]
use spirv_std::{glam::Vec2, spirv};
type Workgroup = [Vec2; 1];
pub struct Cell<'world> {
pub positions: &'world mut [Vec2],
pub velocities: &'world [Vec2],
pub workgroup: &'world mut Workgroup,
}
impl Cell<'_> {
pub fn physics_for_cell(&mut self) {
self.workgroup[0] = self.velocities[0];
self.positions[0] = self.workgroup[0];
}
}
#[spirv(compute(threads(1)))]
pub fn main(
#[spirv(storage_buffer, descriptor_set = 0, binding = 0)] positions: &mut [Vec2],
#[spirv(storage_buffer, descriptor_set = 0, binding = 1)] velocities: &[Vec2],
#[spirv(workgroup)] workgroup: &mut Workgroup,
) {
let mut cell = Cell {
positions,
velocities,
workgroup,
};
cell.physics_for_cell();
} And so the validation error now is:
How do I dump the Naga representation? Would that be an internal WGSL version? Here's the GLSL version from GLSL: #version 450
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
struct _5
{
vec2 _m0[];
uint _m1;
};
struct _6
{
_5 _m0;
_5 _m1;
vec2 _m2[1];
};
layout(binding = 0, std430) buffer _9_3
{
vec2 _m0[];
} _3;
layout(binding = 1, std430) readonly buffer _9_4
{
vec2 _m0[];
} _4;
shared vec2 _7[1];
void main()
{
if (0u < uint(_4._m0.length()))
{
_7[0u] = _4._m0[0u];
if (0u < uint(_3._m0.length()))
{
_3._m0[0u] = _7[0u];
}
else
{
}
}
else
{
}
} And here's the SPIR-V: ; SPIR-V
; Version: 1.3
; Generator: Google rspirv; 0
; Bound: 56
; Schema: 0
OpCapability Shader
OpMemoryModel Logical Simple
OpEntryPoint GLCompute %1 "main"
OpExecutionMode %1 LocalSize 1 1 1
OpDecorate %_runtimearr_v2float ArrayStride 8
OpDecorate %_struct_9 Block
OpMemberDecorate %_struct_9 0 Offset 0
OpDecorate %3 Binding 0
OpDecorate %3 DescriptorSet 0
OpDecorate %4 NonWritable
OpDecorate %4 Binding 1
OpDecorate %4 DescriptorSet 0
OpMemberDecorate %_struct_5 0 Offset 0
OpMemberDecorate %_struct_5 1 Offset 4
OpDecorate %_arr_v2float_uint_1 ArrayStride 8
OpMemberDecorate %_struct_6 0 Offset 0
OpMemberDecorate %_struct_6 1 Offset 8
OpMemberDecorate %_struct_6 2 Offset 16
%void = OpTypeVoid
%12 = OpTypeFunction %void
%float = OpTypeFloat 32
%v2float = OpTypeVector %float 2
%_runtimearr_v2float = OpTypeRuntimeArray %v2float
%_ptr_StorageBuffer__runtimearr_v2float = OpTypePointer StorageBuffer %_runtimearr_v2float
%_struct_9 = OpTypeStruct %_runtimearr_v2float
%_ptr_StorageBuffer__struct_9 = OpTypePointer StorageBuffer %_struct_9
%3 = OpVariable %_ptr_StorageBuffer__struct_9 StorageBuffer
%uint = OpTypeInt 32 0
%uint_0 = OpConstant %uint 0
%4 = OpVariable %_ptr_StorageBuffer__struct_9 StorageBuffer
%_struct_5 = OpTypeStruct %_ptr_StorageBuffer__runtimearr_v2float %uint
%uint_1 = OpConstant %uint 1
%_arr_v2float_uint_1 = OpTypeArray %v2float %uint_1
%_ptr_Workgroup__arr_v2float_uint_1 = OpTypePointer Workgroup %_arr_v2float_uint_1
%_struct_6 = OpTypeStruct %_struct_5 %_struct_5 %_ptr_Workgroup__arr_v2float_uint_1
%7 = OpVariable %_ptr_Workgroup__arr_v2float_uint_1 Workgroup
%bool = OpTypeBool
%_ptr_StorageBuffer_v2float = OpTypePointer StorageBuffer %v2float
%_ptr_Workgroup_v2float = OpTypePointer Workgroup %v2float
%1 = OpFunction %void None %12
%25 = OpLabel
%26 = OpAccessChain %_ptr_StorageBuffer__runtimearr_v2float %3 %uint_0
%27 = OpArrayLength %uint %3 0
%28 = OpAccessChain %_ptr_StorageBuffer__runtimearr_v2float %4 %uint_0
%29 = OpArrayLength %uint %4 0
%53 = OpCompositeConstruct %_struct_5 %26 %27
%54 = OpCompositeConstruct %_struct_5 %28 %29
%55 = OpCompositeConstruct %_struct_6 %53 %54 %7
%36 = OpULessThan %bool %uint_0 %29
OpSelectionMerge %37 None
OpBranchConditional %36 %38 %39
%38 = OpLabel
%41 = OpInBoundsAccessChain %_ptr_StorageBuffer_v2float %28 %uint_0
%42 = OpLoad %v2float %41
%43 = OpInBoundsAccessChain %_ptr_Workgroup_v2float %7 %uint_0
OpStore %43 %42
%45 = OpLoad %v2float %43
%47 = OpULessThan %bool %uint_0 %27
OpSelectionMerge %48 None
OpBranchConditional %47 %49 %50
%49 = OpLabel
%52 = OpInBoundsAccessChain %_ptr_StorageBuffer_v2float %26 %uint_0
OpStore %52 %45
OpBranch %48
%50 = OpLabel
OpBranch %48
%48 = OpLabel
OpBranch %37
%39 = OpLabel
OpBranch %37
%37 = OpLabel
OpReturn
OpFunctionEnd I've made a discussion on the |
Beta Was this translation helpful? Give feedback.
-
This is the function I use in my tests to validate SPIR-V shaders: /// Validate an SPIR-V ".spv" binary
fn validate_src(path: &std::path::PathBuf) {
log::info!("validating source");
log::info!(" reading '{}'", path.display());
let bytes = std::fs::read(path).unwrap();
log::info!(" {:0.2}k bytes read", bytes.len() as f32 / 1000.0);
let opts = naga::front::spv::Options::default();
let module = match naga::front::spv::parse_u8_slice(&bytes, &opts) {
Ok(m) => m,
Err(e) => {
log::error!("{e}");
panic!("SPIR-V parse error");
}
};
log::info!(" SPIR-V parsed");
let mut validator =
naga::valid::Validator::new(Default::default(), naga::valid::Capabilities::empty());
let is_valid;
let info = match validator.validate(&module) {
Ok(i) => {
is_valid = true;
log::info!(" SPIR-V validated");
i
}
Err(e) => {
log::error!("{}", e.emit_to_string(""));
is_valid = false;
let mut validator = naga::valid::Validator::new(
ValidationFlags::empty(),
naga::valid::Capabilities::empty(),
);
validator.validate(&module).unwrap()
}
};
let wgsl = naga::back::wgsl::write_string(
&module,
&info,
naga::back::wgsl::WriterFlags::empty(),
)
.unwrap();
log::info!(" output WGSL generated");
let print_var_name = path
.file_stem()
.unwrap()
.to_str()
.unwrap()
.replace('-', "_");
let maybe_output_path = if std::env::var("print_wgsl").is_ok() || !is_valid {
let dir = std::path::PathBuf::from("../../test_output");
std::fs::create_dir_all(&dir).unwrap();
let output_path = dir.join(print_var_name).with_extension("wgsl");
log::info!("writing WGSL to '{}'", output_path.display());
Some(output_path)
} else {
log::info!(" to save the generated WGSL, use an env var 'print_wgsl=1'");
None
};
if let Some(output_path) = maybe_output_path {
std::fs::write(&output_path, &wgsl).unwrap();
log::info!(" wrote generated WGSL to {}", output_path.display());
}
if !is_valid {
panic!("SPIR-V validation error");
}
let module = match naga::front::wgsl::parse_str(&wgsl) {
Ok(m) => m,
Err(e) => {
log::error!("{}", e.emit_to_string(&wgsl));
panic!("wgsl parse error");
}
};
log::info!(" output WGSL parsed");
let mut validator =
naga::valid::Validator::new(Default::default(), naga::valid::Capabilities::empty());
let _info = match validator.validate(&module) {
Ok(i) => i,
Err(e) => {
log::error!("{}", e.emit_to_string(&wgsl));
panic!("wgsl validation error");
}
};
log::info!(" wgsl output validated");
} That will attempt to parse and validate the shader. If the shader doesn't validate, it will try to re-validate with "very relaxed rules", aka none, and then print the WGSL that the module produces, which does often help. Actually dumping the module is possible by using its |
Beta Was this translation helpful? Give feedback.
-
Nice! Is it okay that I added your code, with accreditation, to my standalone rust-gpu compiler? https://github.com/tombh/rust-gpu-compiler/blob/main/src/validate.rs But it turns out that even the second re-validation with empty validation flags causes the same validation error, so I can't print out the cross-compiled WGSL 😞 |
Beta Was this translation helpful? Give feedback.
-
I had some working code and then I wanted to see if I could speed it up with workgroup memory. I'm using
wgpu
(I don't think anything else usesnaga
?) and I get this cryptic validation error:Diff of changes that introduce the error: tombh/wrach@e693efe
Log with `RUST_LOG=trace naga-cli physics.spv`:
Disassembled:
Cross-compiled to GLSL:
Does anybody have any tips for how to start to debug this. My diff is quite big and it's hard to turn certain parts of it off and on.
Beta Was this translation helpful? Give feedback.
All reactions