Reflection for Atomics is not implemented
Currently the reflection data for atomic values is incorrect. Atomic<T> should be reflected as T.
I tried some trivial shaders with Atomic<int> and they seem to be reflected as integers for HLSL and SPIRV targets.
@Devon7925 Can you provide a repro for your use case?
shader.slang
RWStructuredBuffer<Atomic<uint>> bar;
[shader("compute")]
[numthreads(64, 1, 1)]
void main(uint2 dispatchThreadId : SV_DispatchThreadID)
{
}
main.rs
use slang::Downcast;
fn main() {
let global_session = slang::GlobalSession::new().unwrap();
let search_path = std::ffi::CString::new(".").unwrap();
// All compiler options are available through this builder.
let session_options = slang::CompilerOptions::default()
.optimization(slang::OptimizationLevel::High)
.matrix_layout_row(true);
let target_desc = slang::TargetDesc::default()
.format(slang::CompileTarget::Dxil)
.profile(global_session.find_profile("sm_6_5"));
let targets = [target_desc];
let search_paths = [search_path.as_ptr()];
let session_desc = slang::SessionDesc::default()
.targets(&targets)
.search_paths(&search_paths)
.options(&session_options);
let session = global_session.create_session(&session_desc).unwrap();
let module = session.load_module("shader.slang").unwrap();
let entry_point = module.find_entry_point_by_name("main").unwrap();
let program = session.create_composite_component_type(&[
module.downcast().clone(), entry_point.downcast().clone(),
]).unwrap();
let linked_program = program.link().unwrap();
// Entry point to the reflection API.
let reflection = linked_program.layout(0).unwrap();
let parameter_reflection_type = reflection.parameters().next().unwrap().ty().unwrap();
println!("Outer type name: {}", parameter_reflection_type.name()); // RWStructuredBuffer
println!("Inner kind: {:?}", parameter_reflection_type.element_type().kind()); // Struct
println!("Inner type name: {}", parameter_reflection_type.element_type().name()); // Atomic
println!("Field count: {}", parameter_reflection_type.element_type().field_count()); // 0
println!("Inner kind: {:?}", parameter_reflection_type.resource_result_type().kind()); // Struct
println!("Inner type name: {}", parameter_reflection_type.resource_result_type().name()); // Atomic
let parameter_reflection_type_layout = reflection.parameters().next().unwrap().type_layout();
println!("Inner Layout type kind: {:?}", parameter_reflection_type_layout.element_type_layout().kind()); // Scalar
println!("Inner Layout scalar type: {:?}", parameter_reflection_type_layout.element_type_layout().scalar_type().unwrap()); // Uint32
}
Cargo.toml
[package]
name = "slang-atomic-reflection"
version = "0.1.0"
edition = "2024"
[dependencies]
slang = { git = "https://github.com/FloatyMonkey/slang-rs" }
Thanks Devon, I was able to reproduce what you're seeing with your sample.
I'm a bit new to the reflection API myself, so I walked through the calls to try and understand what's going on. I then compared against what the reflection-api example does.
The first "Inner Kind" that returns a struct is doing this (on the left is rust, comment is underlying C function prefaced by the type returned):
.parameters().next() // VariableLayout spReflection_GetParameterByIndex
.ty() //.variable() // Variable spReflectionVariableLayout_GetVariable
//.ty() // Type spReflectionVariable_GetType
.element_type() // Type spReflectionType_GetElementType
.kind() // TypeKind spReflectionType_GetKind
Note that the .ty() above is expanded by the rust bindings to a .variable().ty()
The -reflection-json parameter and the C++ reflection-api example from the slang repository use different walks:
->getParameterByIndex(1) // VariableLayout spReflection_GetParameterByIndex
->getTypeLayout() // TypeLayout spReflectionVariableLayout_GetTypeLayout
->getElementTypeLayout() // TypeLayout spReflectionTypeLayout_GetElementTypeLayout
->getType() // Type spReflectionTypeLayout_GetType
->getKind() // TypeKind spReflectionType_GetKind
There are some intermediate steps I've skipped. Once it has the TypeLayout, it checks the kind, and gets resource, then gets the resource shape and based on that gets the Element TypeLayout.
This is the same as your "Inner layout" walk. I think that's the preferred one for now:
.parameters().next() // VariableLayout spReflection_GetParameterByIndex
.type_layout() // TypeLayout spReflectionVariableLayout_GetTypeLayout
// check: .kind() == resource
// check: .ty().resource_shape() & SLANG_RESOURCE_BASE_SHAPE_MASK
.element_type_layout() // TypeLayout spReflectionTypeLayout_GetElementTypeLayout
.ty() // Type spReflectionTypeLayout_GetType
.kind() // TypeKind spReflectionType_GetKind
I'm looking into why the walks have different results.