If using set_push_constants() with a Metal shader that uses ParameterBlock:
-
Check pipeline has ParameterBlock layouts: The Metal backend needs reflection data to populate the argument buffer. Enable debug logging:
RUST_LOG=goldy::backend::metal=trace cargo run --example myexample
Look for:
"Allocated bindless argument buffer"and"SetPushConstants: Wrote GPU address". -
Verify argument buffer binding: Check logs for
"SetPushConstants: Bound ParameterBlock argument buffer at slot X". If missing, the buffer isn't being bound to the shader. -
Ensure buffer is heap-allocated: Bindless buffers must be allocated from the Metal heap. Check for
"Encoded buffer N at arg buffer offset"during buffer creation.
When using set_push_constants() with storage buffers on DX12, the shader may read incorrect data. This is often caused by SRV/UAV descriptor mismatch:
Background: DX12 requires different descriptor types for read vs write access:
StructuredBuffer<T>(read-only) → needs SRV (Shader Resource View)RWStructuredBuffer<T>(read-write) → needs UAV (Unordered Access View)
Goldy creates both SRV and UAV descriptors for storage buffers, stored as:
bindless_offset→ UAV indexbindless_srv_offset→ SRV index
Current behavior for set_push_constants():
- Render shaders: Always use SRV offsets (render shaders only read)
- Compute shaders: First buffer uses SRV (read input), subsequent buffers use UAV (write outputs)
This matches the common ping-pong pattern (e.g., Game of Life: read from buffer A, write to buffer B).
If a StructuredBuffer<uint> reads garbage on DX12 but works on Vulkan, check the buffer's element stride. DX12's structured buffer views require the correct StructureByteStride:
uint/int/float→ stride = 4uint2/float2→ stride = 8- Raw bytes → stride = 1 (but incompatible with
StructuredBuffer)
Python buffers automatically detect stride from numpy dtype. If using raw bytes, ensure you're not accessing them as a typed StructuredBuffer in the shader.
Dump the SPIR-V using GOLDY_DUMP_SHADERS (see Inspecting Compiled Shader Assembly) and check:
-
Push Constants Not Generated: Verify the SPIR-V contains
OpVariable ... PushConstant. If it showsUniformstorage class instead, the shader isn't correctly declaring push constants. -
Descriptor Set/Binding Mismatch: Check
OpDecoratelines forBindingandDescriptorSet. Expected bindings:- Binding 0: Storage buffers
- Binding 1: Uniform buffers
- Binding 2: Sampled images
- Binding 3: Samplers
See shaders/README.md for Slang-specific preprocessor behavior that can cause cross-platform issues.
Wrong #[repr(C)] layouts for uniforms or structured-buffer types often show up as subtle bugs (garbage values, misaligned reads). Goldy can compare your Rust layout to Slang’s reflection on the same shader compile that emits SPIR-V / DXIL / MSL—no second compile.
Set GOLDY_VALIDATE_LAYOUTS to a truthy value before creating the device or compiling shaders:
| Value | Effect |
|---|---|
| (unset) | No validation |
1 |
Validate |
true |
Validate |
yes |
Validate |
GOLDY_VALIDATE_LAYOUTS=1 cargo run --example gradient --releaseIf a layout check fails, compilation returns an error describing size / field offset / name mismatches.
- Match the Rust struct name to the Slang
structname you want checked (reflection usesFindTypeByName). - Add
#[derive(LayoutCheckable)](re-exported from thegoldycrate). - Pass
&[YourStruct::LAYOUT_CHECK]as the last argument toShaderModule::from_slang_with_options(otherfrom_slang*helpers pass empty checks).
When the env var is off, those checks are skipped and from_slang_with_options behaves like a normal compile path.
The gradient and checkerboard examples demonstrate this with TimeUniforms vs struct TimeUniforms in the shader sources.
Standalone reflection without shader creation remains available via Device::reflect_struct and SlangCompiler::reflect_struct_layout.
When a shader produces unexpected results, inspecting the compiled bytecode can reveal codegen issues that aren't visible in the source. This is useful when:
- Push constants/uniforms show wrong values
- Resource bindings don't work as expected
- Shader logic appears correct but output is wrong
Set the GOLDY_DUMP_SHADERS environment variable to a directory path:
GOLDY_DUMP_SHADERS=/tmp/shaders cargo run --example game_of_lifeThis writes compiled bytecode for each shader entry point:
{entry}_dx12.dxil- DirectX 12 (DXIL){entry}_vulkan.spv- Vulkan (SPIR-V)
Use dxc (DirectX Shader Compiler) to disassemble:
dxc -dumpbin cs_main_dx12.dxil > cs_main.txtKey things to check:
- Buffer Definitions: Verify struct layouts and sizes match expectations
- cbufferLoadLegacy: Each call loads a 16-byte register; check
regIndexvalues - extractvalue: Which component (0-3) is extracted from loaded data
Use spirv-dis from the Vulkan SDK:
spirv-dis cs_main_vulkan.spv > cs_main.txtKey things to check:
- OpVariable storage class: Push constants should use
PushConstant, notUniform - OpAccessChain: Array/struct element access - verify indices are correct
- OpCompositeExtract: Component extraction from vectors/structs
For debugging resource binding issues, add temporary logging:
tracing::debug!(
"Buffer {} at bindless index {} (storage={})",
handle, index, is_storage
);Enable with RUST_LOG=goldy=debug cargo run.
This error occurs when the FFI loads the wrong Slang DLL. Goldy requires Slang 2026.4+ with SM 6.6 bindless support (DescriptorHandle intrinsic).
Cause: The Slang library search falls back to an older slang.dll from the Vulkan SDK instead of the bundled slang-compiler.dll.
Automatic Bundling: As of version 0.1.0, Goldy's FFI bindings automatically bundle Slang libraries:
- .NET:
build-native.ps1/build-native.shcopies Slang toruntimes/{rid}/native/ - Python:
build-slang.pycopies Slang to the package beforematurin build - C++: CMake installs Slang alongside
goldy_ffi
If you still encounter this error, the Slang libraries may not have been bundled correctly. Rebuild using the appropriate build script.
Manual Override: To use a custom Slang version, set the environment variable:
export GOLDY_SLANG_PATH=/path/to/slang-compiler.dllSlang Loader Search Order:
GOLDY_SLANG_PATHenvironment variable- Same directory as executable (for FFI deployments)
slang/bin/{platform}/relative to executable- Downloaded by build.rs to
OUT_DIR(Rust crate only)
The required Slang version and file list are defined in slang/manifest.json.