diff --git a/README.md b/README.md index 358f692..cebe5df 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # Slang Rust Bindings -Rust bindings for the [Slang](https://github.com/shader-slang/slang/) shader language compiler. In contrast to existing bindings, these internally use Slang's COM/C++ API because the old C API is soon to be deprecated. +Rust bindings for the [Slang](https://github.com/shader-slang/slang/) shader language. Supporting both the modern compilation and reflection API. -Currently mostly reflects the needs of our own [engine](https://github.com/FloatyMonkey/engine) but issues and pull requests are more than welcome. +Currently mostly reflects the needs of our own [engine](https://github.com/FloatyMonkey/engine) but contributions are more than welcome. ## Example @@ -37,6 +37,9 @@ let program = session.create_composite_component_type(&[ let linked_program = program.link().unwrap(); +// Entry point to the reflection API. +let reflection = linked_program.layout(0).unwrap(); + let shader_bytecode = linked_program.get_entry_point_code(0, 0).unwrap(); ``` diff --git a/slang-sys/build.rs b/slang-sys/build.rs index 8b7a401..a232e2b 100644 --- a/slang-sys/build.rs +++ b/slang-sys/build.rs @@ -18,7 +18,8 @@ fn main() { .header(slang_dir.join("include/slang.h").to_str().unwrap()) .clang_arg("-v") .clang_arg("-xc++") - .clang_arg("-std=c++14") + .clang_arg("-std=c++17") + .allowlist_function("spReflection.*") .allowlist_function("slang_.*") .allowlist_type("slang.*") .allowlist_var("SLANG_.*") diff --git a/slang-sys/src/lib.rs b/slang-sys/src/lib.rs index dbe0909..ef9c70f 100644 --- a/slang-sys/src/lib.rs +++ b/slang-sys/src/lib.rs @@ -4,7 +4,7 @@ include!(concat!(env!("OUT_DIR"), "/bindings.rs")); use std::ffi::{c_char, c_int, c_void}; -// Based on Slang version 2024.1.29 +// Based on Slang version 2024.14.3 #[repr(C)] pub struct IBlobVtable { @@ -88,6 +88,8 @@ pub struct IComponentTypeVtable { pub renameEntryPoint: unsafe extern "C" fn(*mut c_void, newName: *const c_char, outEntryPoint: *mut *mut slang_IComponentType) -> SlangResult, pub linkWithOptions: unsafe extern "C" fn(*mut c_void, outLinkedComponentType: *mut *mut slang_IComponentType, compilerOptionEntryCount: u32, compilerOptionEntries: *mut slang_CompilerOptionEntry, outDiagnostics: *mut *mut ISlangBlob) -> SlangResult, pub getTargetCode: unsafe extern "C" fn(*mut c_void, targetIndex: SlangInt, outCode: *mut *mut ISlangBlob, outDiagnostics: *mut *mut ISlangBlob) -> SlangResult, + pub getTargetMetadata: unsafe extern "C" fn(*mut c_void, targetIndex: SlangInt, outMetadata: *mut *mut slang_IMetadata, outDiagnostics: *mut *mut ISlangBlob) -> SlangResult, + pub getEntryPointMetadata: unsafe extern "C" fn(*mut c_void, entryPointIndex: SlangInt, targetIndex: SlangInt, outMetadata: *mut *mut slang_IMetadata, outDiagnostics: *mut *mut ISlangBlob) -> SlangResult, } #[repr(C)] @@ -115,4 +117,5 @@ pub struct IModuleVtable { pub findAndCheckEntryPoint: unsafe extern "C" fn(*mut c_void, name: *const c_char, stage: SlangStage, outEntryPoint: *mut *mut slang_IEntryPoint, outDiagnostics: *mut *mut ISlangBlob) -> SlangResult, pub getDependencyFileCount: unsafe extern "C" fn(*mut c_void) -> SlangInt32, pub getDependencyFilePath: unsafe extern "C" fn(*mut c_void, index: SlangInt32) -> *const c_char, + pub getModuleReflection: unsafe extern "C" fn(*mut c_void) -> *mut slang_DeclReflection, } diff --git a/src/lib.rs b/src/lib.rs index 52ce85c..91a194a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,5 @@ +pub mod reflection; + use std::ffi::{CStr, CString}; use std::ptr::{null, null_mut}; @@ -270,6 +272,19 @@ unsafe impl Interface for ComponentType { } impl ComponentType { + pub fn layout(&self, target: i64) -> Result<&reflection::Shader> { + let mut diagnostics = null_mut(); + let ptr = vcall!(self, getLayout(target, &mut diagnostics)); + + if ptr.is_null() { + Err(Error::Blob(Blob(IUnknown( + std::ptr::NonNull::new(diagnostics as *mut _).unwrap(), + )))) + } else { + Ok(unsafe { &*(ptr as *const _) }) + } + } + pub fn link(&self) -> Result { let mut linked_component_type = null_mut(); let mut diagnostics = null_mut(); @@ -284,7 +299,7 @@ impl ComponentType { ))) } - pub fn get_entry_point_code(&self, index: i64, target: i64) -> Result { + pub fn entry_point_code(&self, index: i64, target: i64) -> Result { let mut code = null_mut(); let mut diagnostics = null_mut(); @@ -300,6 +315,11 @@ impl ComponentType { std::ptr::NonNull::new(code as *mut _).unwrap(), ))) } + + #[deprecated = "Use `entry_point_code` instead"] + pub fn get_entry_point_code(&self, index: i64, target: i64) -> Result { + self.entry_point_code(index, target) + } } #[repr(transparent)] @@ -386,6 +406,11 @@ impl Module { let identity = vcall!(self, getUniqueIdentity()); unsafe { CStr::from_ptr(identity).to_str().unwrap() } } + + pub fn module_reflection(&self) -> &reflection::Decl { + let ptr = vcall!(self, getModuleReflection()); + unsafe { &*(ptr as *const _) } + } } pub struct TargetDescBuilder { diff --git a/src/reflection/decl.rs b/src/reflection/decl.rs new file mode 100644 index 0000000..e1eeeb2 --- /dev/null +++ b/src/reflection/decl.rs @@ -0,0 +1,48 @@ +use super::{rcall, Function, Generic, Type, Variable}; +use slang_sys as sys; + +#[repr(transparent)] +pub struct Decl(sys::SlangReflectionDecl); + +impl Decl { + pub fn name(&self) -> &str { + let name = rcall!(spReflectionDecl_getName(self)); + unsafe { std::ffi::CStr::from_ptr(name).to_str().unwrap() } + } + + pub fn kind(&self) -> sys::SlangDeclKind { + rcall!(spReflectionDecl_getKind(self)) + } + + pub fn child_count(&self) -> u32 { + rcall!(spReflectionDecl_getChildrenCount(self)) + } + + pub fn child_by_index(&self, index: u32) -> Option<&Decl> { + rcall!(spReflectionDecl_getChild(self, index) as Option<&Decl>) + } + + pub fn children(&self) -> impl ExactSizeIterator { + (0..self.child_count()).map(move |i| rcall!(spReflectionDecl_getChild(self, i) as &Decl)) + } + + pub fn ty(&self) -> &Type { + rcall!(spReflection_getTypeFromDecl(self) as &Type) + } + + pub fn as_variable(&self) -> &Variable { + rcall!(spReflectionDecl_castToVariable(self) as &Variable) + } + + pub fn as_function(&self) -> &Function { + rcall!(spReflectionDecl_castToFunction(self) as &Function) + } + + pub fn as_generic(&self) -> &Generic { + rcall!(spReflectionDecl_castToGeneric(self) as &Generic) + } + + pub fn parent(&self) -> &Decl { + rcall!(spReflectionDecl_getParent(self) as &Decl) + } +} diff --git a/src/reflection/entry_point.rs b/src/reflection/entry_point.rs new file mode 100644 index 0000000..70e76df --- /dev/null +++ b/src/reflection/entry_point.rs @@ -0,0 +1,62 @@ +use super::{rcall, Function, TypeLayout, VariableLayout}; +use slang_sys as sys; + +#[repr(transparent)] +pub struct EntryPoint(sys::SlangReflectionEntryPoint); + +impl EntryPoint { + pub fn name(&self) -> &str { + let name = rcall!(spReflectionEntryPoint_getName(self)); + unsafe { std::ffi::CStr::from_ptr(name).to_str().unwrap() } + } + + pub fn name_override(&self) -> Option<&str> { + let name = rcall!(spReflectionEntryPoint_getNameOverride(self)); + (!name.is_null()).then(|| unsafe { std::ffi::CStr::from_ptr(name).to_str().unwrap() }) + } + + pub fn parameter_count(&self) -> u32 { + rcall!(spReflectionEntryPoint_getParameterCount(self)) + } + + pub fn parameter_by_index(&self, index: u32) -> Option<&VariableLayout> { + rcall!(spReflectionEntryPoint_getParameterByIndex(self, index) as Option<&VariableLayout>) + } + + pub fn parameters(&self) -> impl ExactSizeIterator { + (0..self.parameter_count()).map(move |i| { + rcall!(spReflectionEntryPoint_getParameterByIndex(self, i) as &VariableLayout) + }) + } + + pub fn function(&self) -> &Function { + rcall!(spReflectionEntryPoint_getFunction(self) as &Function) + } + + pub fn stage(&self) -> sys::SlangStage { + rcall!(spReflectionEntryPoint_getStage(self)) + } + + // TODO: compute_thread_group_size + // TODO: compute_wave_size + + pub fn uses_any_sample_rate_input(&self) -> bool { + rcall!(spReflectionEntryPoint_usesAnySampleRateInput(self)) != 0 + } + + pub fn var_layout(&self) -> &VariableLayout { + rcall!(spReflectionEntryPoint_getVarLayout(self) as &VariableLayout) + } + + pub fn type_layout(&self) -> &TypeLayout { + self.var_layout().type_layout() + } + + pub fn result_var_layout(&self) -> &VariableLayout { + rcall!(spReflectionEntryPoint_getResultVarLayout(self) as &VariableLayout) + } + + pub fn has_default_constant_buffer(&self) -> bool { + rcall!(spReflectionEntryPoint_hasDefaultConstantBuffer(self)) != 0 + } +} diff --git a/src/reflection/function.rs b/src/reflection/function.rs new file mode 100644 index 0000000..99468df --- /dev/null +++ b/src/reflection/function.rs @@ -0,0 +1,65 @@ +use super::{rcall, Type, UserAttribute, Variable}; +use slang_sys as sys; + +#[repr(transparent)] +pub struct Function(sys::SlangReflectionFunction); + +impl Function { + pub fn name(&self) -> &str { + let name = rcall!(spReflectionFunction_GetName(self)); + unsafe { std::ffi::CStr::from_ptr(name).to_str().unwrap() } + } + + pub fn return_type(&self) -> &Type { + rcall!(spReflectionFunction_GetResultType(self) as &Type) + } + + pub fn parameter_count(&self) -> u32 { + rcall!(spReflectionFunction_GetParameterCount(self)) + } + + pub fn parameter_by_index(&self, index: u32) -> Option<&Variable> { + rcall!(spReflectionFunction_GetParameter(self, index) as Option<&Variable>) + } + + pub fn parameters(&self) -> impl ExactSizeIterator { + (0..self.parameter_count()) + .map(move |i| rcall!(spReflectionFunction_GetParameter(self, i) as &Variable)) + } + + pub fn user_attribute_count(&self) -> u32 { + rcall!(spReflectionFunction_GetUserAttributeCount(self)) + } + + pub fn user_attribute_by_index(&self, index: u32) -> Option<&UserAttribute> { + rcall!(spReflectionFunction_GetUserAttribute(self, index) as Option<&UserAttribute>) + } + + pub fn user_attributes(&self) -> impl ExactSizeIterator { + (0..self.user_attribute_count()) + .map(move |i| rcall!(spReflectionFunction_GetUserAttribute(self, i) as &UserAttribute)) + } + + // TODO: find_user_attribute_by_name + // TODO: find_modifier + // TODO: generic_container + // TODO: apply_specializations + // TODO: specialize_with_arg_types + + pub fn is_overloaded(&self) -> bool { + rcall!(spReflectionFunction_isOverloaded(self)) + } + + pub fn overload_count(&self) -> u32 { + rcall!(spReflectionFunction_getOverloadCount(self)) + } + + pub fn overload_by_index(&self, index: u32) -> Option<&Function> { + rcall!(spReflectionFunction_getOverload(self, index) as Option<&Function>) + } + + pub fn overloads(&self) -> impl ExactSizeIterator { + (0..self.overload_count()) + .map(move |i| rcall!(spReflectionFunction_getOverload(self, i) as &Function)) + } +} diff --git a/src/reflection/generic.rs b/src/reflection/generic.rs new file mode 100644 index 0000000..b33dba0 --- /dev/null +++ b/src/reflection/generic.rs @@ -0,0 +1,91 @@ +use super::{rcall, Decl, Type, TypeParameter, Variable}; +use slang_sys as sys; + +#[repr(transparent)] +pub struct Generic(sys::SlangReflectionGeneric); + +impl Generic { + pub fn as_decl(&self) -> &Decl { + rcall!(spReflectionGeneric_asDecl(self) as &Decl) + } + + pub fn name(&self) -> &str { + let name = rcall!(spReflectionGeneric_GetName(self)); + unsafe { std::ffi::CStr::from_ptr(name).to_str().unwrap() } + } + + pub fn type_parameter_count(&self) -> u32 { + rcall!(spReflectionGeneric_GetTypeParameterCount(self)) + } + + pub fn type_parameter_by_index(&self, index: u32) -> Option<&TypeParameter> { + rcall!(spReflectionGeneric_GetTypeParameter(self, index) as Option<&TypeParameter>) + } + + pub fn type_parameters(&self) -> impl ExactSizeIterator { + (0..self.type_parameter_count()) + .map(move |i| rcall!(spReflectionGeneric_GetTypeParameter(self, i) as &TypeParameter)) + } + + pub fn value_parameter_count(&self) -> u32 { + rcall!(spReflectionGeneric_GetValueParameterCount(self)) + } + + pub fn value_parameter_by_index(&self, index: u32) -> Option<&Variable> { + rcall!(spReflectionGeneric_GetValueParameter(self, index) as Option<&Variable>) + } + + pub fn value_parameters(&self) -> impl ExactSizeIterator { + (0..self.value_parameter_count()) + .map(move |i| rcall!(spReflectionGeneric_GetValueParameter(self, i) as &Variable)) + } + + pub fn type_parameter_constraint_count(&self, type_param: &Variable) -> u32 { + rcall!(spReflectionGeneric_GetTypeParameterConstraintCount( + self, + type_param as *const _ as *mut _ + )) + } + + pub fn type_parameter_constraint_by_index( + &self, + type_param: &Variable, + index: u32, + ) -> Option<&Type> { + rcall!(spReflectionGeneric_GetTypeParameterConstraintType( + self, + type_param as *const _ as *mut _, + index + ) as Option<&Type>) + } + + pub fn inner_decl(&self) -> &Decl { + rcall!(spReflectionGeneric_GetInnerDecl(self) as &Decl) + } + + pub fn inner_kind(&self) -> sys::SlangDeclKind { + rcall!(spReflectionGeneric_GetInnerKind(self)) + } + + pub fn outer_generic_container(&self) -> &Generic { + rcall!(spReflectionGeneric_GetOuterGenericContainer(self) as &Generic) + } + + pub fn concrete_type(&self, type_param: &Variable) -> &Type { + rcall!(spReflectionGeneric_GetConcreteType(self, type_param as *const _ as *mut _) as &Type) + } + + pub fn concrete_int_val(&self, value_param: &Variable) -> i64 { + rcall!(spReflectionGeneric_GetConcreteIntVal( + self, + value_param as *const _ as *mut _ + )) + } + + pub fn apply_specializations(&self, generic: &Generic) -> &Generic { + rcall!( + spReflectionGeneric_applySpecializations(self, generic as *const _ as *mut _) + as &Generic + ) + } +} diff --git a/src/reflection/mod.rs b/src/reflection/mod.rs new file mode 100644 index 0000000..88fdaae --- /dev/null +++ b/src/reflection/mod.rs @@ -0,0 +1,38 @@ +mod decl; +mod entry_point; +mod function; +mod generic; +mod shader; +mod ty; +mod type_parameter; +mod user_attribute; +mod variable; + +pub use decl::Decl; +pub use entry_point::EntryPoint; +pub use function::Function; +pub use generic::Generic; +pub use shader::Shader; +pub use ty::{Type, TypeLayout}; +pub use type_parameter::TypeParameter; +pub use user_attribute::UserAttribute; +pub use variable::{Variable, VariableLayout}; + +macro_rules! rcall { + ($f:ident($s:ident $(,$arg:expr)*)) => { + unsafe { sys::$f($s as *const _ as *mut _ $(,$arg)*) } + }; + + ($f:ident($s:ident $(,$arg:expr)*) as Option<&$cast:ty>) => { + unsafe { + let ptr = sys::$f($s as *const _ as *mut _ $(,$arg)*); + (!ptr.is_null()).then(|| &*(ptr as *const $cast)) + } + }; + + ($f:ident($s:ident $(,$arg:expr)*) as &$cast:ty) => { + unsafe { &*(sys::$f($s as *const _ as *mut _ $(,$arg)*) as *const $cast) } + }; +} + +pub(super) use rcall; diff --git a/src/reflection/shader.rs b/src/reflection/shader.rs new file mode 100644 index 0000000..bad2e38 --- /dev/null +++ b/src/reflection/shader.rs @@ -0,0 +1,125 @@ +use super::{ + rcall, EntryPoint, Function, Type, TypeLayout, TypeParameter, Variable, VariableLayout, +}; +use slang_sys as sys; + +#[repr(transparent)] +pub struct Shader(sys::SlangReflection); + +impl Shader { + pub fn parameter_count(&self) -> u32 { + rcall!(spReflection_GetParameterCount(self)) + } + + pub fn parameter_by_index(&self, index: u32) -> Option<&VariableLayout> { + rcall!(spReflection_GetParameterByIndex(self, index) as Option<&VariableLayout>) + } + + pub fn parameters(&self) -> impl ExactSizeIterator { + (0..self.parameter_count()) + .map(move |i| rcall!(spReflection_GetParameterByIndex(self, i) as &VariableLayout)) + } + + pub fn type_parameter_count(&self) -> u32 { + rcall!(spReflection_GetTypeParameterCount(self)) + } + + pub fn type_parameter_by_index(&self, index: u32) -> Option<&TypeParameter> { + rcall!(spReflection_GetTypeParameterByIndex(self, index) as Option<&TypeParameter>) + } + + pub fn type_parameters(&self) -> impl ExactSizeIterator { + (0..self.type_parameter_count()) + .map(move |i| rcall!(spReflection_GetTypeParameterByIndex(self, i) as &TypeParameter)) + } + + pub fn find_type_parameter_by_name(&self, name: &str) -> Option<&TypeParameter> { + let name = std::ffi::CString::new(name).unwrap(); + rcall!(spReflection_FindTypeParameter(self, name.as_ptr()) as Option<&TypeParameter>) + } + + pub fn entry_point_count(&self) -> u32 { + rcall!(spReflection_getEntryPointCount(self)) as _ + } + + pub fn entry_point_by_index(&self, index: u32) -> Option<&EntryPoint> { + rcall!(spReflection_getEntryPointByIndex(self, index as _) as Option<&EntryPoint>) + } + + pub fn entry_points(&self) -> impl ExactSizeIterator { + (0..self.entry_point_count()) + .map(move |i| rcall!(spReflection_getEntryPointByIndex(self, i as _) as &EntryPoint)) + } + + pub fn find_entry_point_by_name(&self, name: &str) -> Option<&EntryPoint> { + let name = std::ffi::CString::new(name).unwrap(); + rcall!(spReflection_findEntryPointByName(self, name.as_ptr()) as Option<&EntryPoint>) + } + + pub fn global_constant_buffer_binding(&self) -> u64 { + rcall!(spReflection_getGlobalConstantBufferBinding(self)) + } + + pub fn global_constant_buffer_size(&self) -> usize { + rcall!(spReflection_getGlobalConstantBufferSize(self)) + } + + pub fn find_type_by_name(&self, name: &str) -> Option<&Type> { + let name = std::ffi::CString::new(name).unwrap(); + rcall!(spReflection_FindTypeByName(self, name.as_ptr()) as Option<&Type>) + } + + pub fn find_function_by_name(&self, name: &str) -> Option<&Function> { + let name = std::ffi::CString::new(name).unwrap(); + rcall!(spReflection_FindFunctionByName(self, name.as_ptr()) as Option<&Function>) + } + + pub fn find_function_by_name_in_type(&self, ty: &Type, name: &str) -> Option<&Function> { + let name = std::ffi::CString::new(name).unwrap(); + rcall!( + spReflection_FindFunctionByNameInType(self, ty as *const _ as *mut _, name.as_ptr()) + as Option<&Function> + ) + } + + pub fn find_var_by_name_in_type(&self, ty: &Type, name: &str) -> Option<&Variable> { + let name = std::ffi::CString::new(name).unwrap(); + rcall!( + spReflection_FindVarByNameInType(self, ty as *const _ as *mut _, name.as_ptr()) + as Option<&Variable> + ) + } + + fn type_layout(&self, ty: &Type, rules: sys::SlangLayoutRules) -> Option<&TypeLayout> { + rcall!( + spReflection_GetTypeLayout(self, ty as *const _ as *mut _, rules) + as Option<&TypeLayout> + ) + } + + // TODO: specialize_type + // TODO: specialize_generic + // TODO: is_sub_type + + pub fn hashed_string_count(&self) -> u64 { + rcall!(spReflection_getHashedStringCount(self)) + } + + pub fn hashed_string(&self, index: u64) -> Option<&str> { + let mut len = 0; + let result = rcall!(spReflection_getHashedString(self, index, &mut len)); + + (!result.is_null()).then(|| { + let slice = unsafe { std::slice::from_raw_parts(result as *const u8, len as usize) }; + std::str::from_utf8(slice).unwrap() + }) + } + + pub fn global_params_type_layout(&self) -> &TypeLayout { + rcall!(spReflection_getGlobalParamsTypeLayout(self) as &TypeLayout) + } + + pub fn global_params_var_layout(&self) -> &VariableLayout { + rcall!(spReflection_getGlobalParamsVarLayout(self) as &VariableLayout) + } +} diff --git a/src/reflection/ty.rs b/src/reflection/ty.rs new file mode 100644 index 0000000..7845948 --- /dev/null +++ b/src/reflection/ty.rs @@ -0,0 +1,376 @@ +use super::{rcall, UserAttribute, Variable, VariableLayout}; +use slang_sys as sys; + +#[repr(transparent)] +pub struct Type(sys::SlangReflectionType); + +impl Type { + pub fn kind(&self) -> sys::SlangTypeKind { + rcall!(spReflectionType_GetKind(self)) + } + + pub fn field_count(&self) -> u32 { + rcall!(spReflectionType_GetFieldCount(self)) + } + + pub fn field_by_index(&self, index: u32) -> Option<&Variable> { + rcall!(spReflectionType_GetFieldByIndex(self, index) as Option<&Variable>) + } + + pub fn fields(&self) -> impl ExactSizeIterator { + (0..self.field_count()) + .map(move |i| rcall!(spReflectionType_GetFieldByIndex(self, i) as &Variable)) + } + + // TODO: is_array + + // TODO: unwrap_array + + pub fn element_count(&self) -> usize { + rcall!(spReflectionType_GetElementCount(self)) + } + + // TODO: total_array_element_count + + pub fn element_type(&self) -> &Type { + rcall!(spReflectionType_GetElementType(self) as &Type) + } + + pub fn row_count(&self) -> u32 { + rcall!(spReflectionType_GetRowCount(self)) + } + + pub fn column_count(&self) -> u32 { + rcall!(spReflectionType_GetColumnCount(self)) + } + + pub fn scalar_type(&self) -> sys::SlangScalarType { + rcall!(spReflectionType_GetScalarType(self)) + } + + pub fn resource_result_type(&self) -> &Type { + rcall!(spReflectionType_GetResourceResultType(self) as &Type) + } + + pub fn resource_shape(&self) -> sys::SlangResourceShape { + rcall!(spReflectionType_GetResourceShape(self)) + } + + pub fn resource_access(&self) -> sys::SlangResourceAccess { + rcall!(spReflectionType_GetResourceAccess(self)) + } + + pub fn name(&self) -> &str { + let name = rcall!(spReflectionType_GetName(self)); + unsafe { std::ffi::CStr::from_ptr(name).to_str().unwrap() } + } + + // TODO: full_name + + pub fn user_attribute_count(&self) -> u32 { + rcall!(spReflectionType_GetUserAttributeCount(self)) + } + + pub fn user_attribute_by_index(&self, index: u32) -> Option<&UserAttribute> { + rcall!(spReflectionType_GetUserAttribute(self, index) as Option<&UserAttribute>) + } + + pub fn user_attributes(&self) -> impl ExactSizeIterator { + (0..self.user_attribute_count()) + .map(move |i| rcall!(spReflectionType_GetUserAttribute(self, i) as &UserAttribute)) + } + + pub fn find_user_attribute_by_name(&self, name: &str) -> Option<&UserAttribute> { + let name = std::ffi::CString::new(name).unwrap(); + rcall!( + spReflectionType_FindUserAttributeByName(self, name.as_ptr()) as Option<&UserAttribute> + ) + } +} + +#[repr(transparent)] +pub struct TypeLayout(sys::SlangReflectionTypeLayout); + +impl TypeLayout { + pub fn ty(&self) -> &Type { + rcall!(spReflectionTypeLayout_GetType(self) as &Type) + } + + pub fn kind(&self) -> sys::SlangTypeKind { + rcall!(spReflectionTypeLayout_getKind(self)) + } + + pub fn size(&self, category: sys::SlangParameterCategory) -> usize { + rcall!(spReflectionTypeLayout_GetSize(self, category)) + } + + pub fn stride(&self, category: sys::SlangParameterCategory) -> usize { + rcall!(spReflectionTypeLayout_GetStride(self, category)) + } + + pub fn alignment(&self, category: sys::SlangParameterCategory) -> i32 { + rcall!(spReflectionTypeLayout_getAlignment(self, category)) + } + + pub fn field_count(&self) -> u32 { + rcall!(spReflectionTypeLayout_GetFieldCount(self)) + } + + pub fn field_by_index(&self, index: u32) -> Option<&VariableLayout> { + rcall!(spReflectionTypeLayout_GetFieldByIndex(self, index) as Option<&VariableLayout>) + } + + pub fn fields(&self) -> impl ExactSizeIterator { + (0..self.field_count()).map(move |i| { + rcall!(spReflectionTypeLayout_GetFieldByIndex(self, i) as &VariableLayout) + }) + } + + // TODO: find_field_index_by_name + // TODO: explicit_counter + // TODO: is_array + // TODO: unwrap_array + + pub fn element_count(&self) -> usize { + self.ty().element_count() + } + + // TODO: total_array_element_count + + pub fn element_stride(&self, category: sys::SlangParameterCategory) -> usize { + rcall!(spReflectionTypeLayout_GetElementStride(self, category)) + } + + pub fn element_type_layout(&self) -> &TypeLayout { + rcall!(spReflectionTypeLayout_GetElementTypeLayout(self) as &TypeLayout) + } + + pub fn element_var_layout(&self) -> &VariableLayout { + rcall!(spReflectionTypeLayout_GetElementVarLayout(self) as &VariableLayout) + } + + pub fn container_var_layout(&self) -> &VariableLayout { + rcall!(spReflectionTypeLayout_getContainerVarLayout(self) as &VariableLayout) + } + + pub fn parameter_category(&self) -> sys::SlangParameterCategory { + rcall!(spReflectionTypeLayout_GetParameterCategory(self)) + } + + pub fn category_count(&self) -> u32 { + rcall!(spReflectionTypeLayout_GetCategoryCount(self)) + } + + pub fn category_by_index(&self, index: u32) -> sys::SlangParameterCategory { + rcall!(spReflectionTypeLayout_GetCategoryByIndex(self, index)) + } + + pub fn categories(&self) -> impl ExactSizeIterator + '_ { + (0..self.category_count()) + .map(move |i| rcall!(spReflectionTypeLayout_GetCategoryByIndex(self, i))) + } + + pub fn row_count(&self) -> u32 { + self.ty().row_count() + } + + pub fn column_count(&self) -> u32 { + self.ty().column_count() + } + + pub fn scalar_type(&self) -> sys::SlangScalarType { + self.ty().scalar_type() + } + + pub fn resource_result_type(&self) -> &Type { + self.ty().resource_result_type() + } + + pub fn resource_shape(&self) -> sys::SlangResourceShape { + self.ty().resource_shape() + } + + pub fn resource_access(&self) -> sys::SlangResourceAccess { + self.ty().resource_access() + } + + pub fn name(&self) -> &str { + self.ty().name() + } + + pub fn matrix_layout_mode(&self) -> sys::SlangMatrixLayoutMode { + rcall!(spReflectionTypeLayout_GetMatrixLayoutMode(self)) + } + + pub fn generic_param_index(&self) -> i32 { + rcall!(spReflectionTypeLayout_getGenericParamIndex(self)) + } + + pub fn pending_data_type_layout(&self) -> &TypeLayout { + rcall!(spReflectionTypeLayout_getPendingDataTypeLayout(self) as &TypeLayout) + } + + pub fn specialized_type_pending_data_var_layout(&self) -> &VariableLayout { + rcall!( + spReflectionTypeLayout_getSpecializedTypePendingDataVarLayout(self) as &VariableLayout + ) + } + + pub fn binding_range_count(&self) -> i64 { + rcall!(spReflectionTypeLayout_getBindingRangeCount(self)) + } + + pub fn binding_range_type(&self, index: i64) -> sys::SlangBindingType { + rcall!(spReflectionTypeLayout_getBindingRangeType(self, index)) + } + + pub fn is_binding_range_specializable(&self, index: i64) -> bool { + rcall!(spReflectionTypeLayout_isBindingRangeSpecializable( + self, index + )) != 0 + } + + pub fn binding_range_binding_count(&self, index: i64) -> i64 { + rcall!(spReflectionTypeLayout_getBindingRangeBindingCount( + self, index + )) + } + + pub fn field_binding_range_offset(&self, field_index: i64) -> i64 { + rcall!(spReflectionTypeLayout_getFieldBindingRangeOffset( + self, + field_index + )) + } + + pub fn explicit_counter_binding_range_offset(&self) -> i64 { + rcall!(spReflectionTypeLayout_getExplicitCounterBindingRangeOffset( + self + )) + } + + pub fn binding_range_leaf_type_layout(&self, index: i64) -> &TypeLayout { + rcall!(spReflectionTypeLayout_getBindingRangeLeafTypeLayout(self, index) as &TypeLayout) + } + + pub fn binding_range_leaf_variable(&self, index: i64) -> &Variable { + rcall!(spReflectionTypeLayout_getBindingRangeLeafVariable(self, index) as &Variable) + } + + pub fn binding_range_image_format(&self, index: i64) -> sys::SlangImageFormat { + rcall!(spReflectionTypeLayout_getBindingRangeImageFormat( + self, index + )) + } + + pub fn binding_range_descriptor_set_index(&self, index: i64) -> i64 { + rcall!(spReflectionTypeLayout_getBindingRangeDescriptorSetIndex( + self, index + )) + } + + pub fn binding_range_first_descriptor_range_index(&self, index: i64) -> i64 { + rcall!(spReflectionTypeLayout_getBindingRangeFirstDescriptorRangeIndex(self, index)) + } + + pub fn binding_range_descriptor_range_count(&self, index: i64) -> i64 { + rcall!(spReflectionTypeLayout_getBindingRangeDescriptorRangeCount( + self, index + )) + } + + pub fn descriptor_set_count(&self) -> i64 { + rcall!(spReflectionTypeLayout_getDescriptorSetCount(self)) + } + + pub fn descriptor_set_space_offset(&self, set_index: i64) -> i64 { + rcall!(spReflectionTypeLayout_getDescriptorSetSpaceOffset( + self, set_index + )) + } + + pub fn descriptor_set_descriptor_range_count(&self, set_index: i64) -> i64 { + rcall!(spReflectionTypeLayout_getDescriptorSetDescriptorRangeCount( + self, set_index + )) + } + + pub fn descriptor_set_descriptor_range_index_offset( + &self, + set_index: i64, + range_index: i64, + ) -> i64 { + rcall!( + spReflectionTypeLayout_getDescriptorSetDescriptorRangeIndexOffset( + self, + set_index, + range_index + ) + ) + } + + pub fn descriptor_set_descriptor_range_descriptor_count( + &self, + set_index: i64, + range_index: i64, + ) -> i64 { + rcall!( + spReflectionTypeLayout_getDescriptorSetDescriptorRangeDescriptorCount( + self, + set_index, + range_index + ) + ) + } + + pub fn descriptor_set_descriptor_range_type( + &self, + set_index: i64, + range_index: i64, + ) -> sys::SlangBindingType { + rcall!(spReflectionTypeLayout_getDescriptorSetDescriptorRangeType( + self, + set_index, + range_index + )) + } + + pub fn descriptor_set_descriptor_range_category( + &self, + set_index: i64, + range_index: i64, + ) -> sys::SlangParameterCategory { + rcall!( + spReflectionTypeLayout_getDescriptorSetDescriptorRangeCategory( + self, + set_index, + range_index + ) + ) + } + + pub fn sub_object_range_count(&self) -> i64 { + rcall!(spReflectionTypeLayout_getSubObjectRangeCount(self)) + } + + pub fn sub_object_range_binding_range_index(&self, sub_object_range_index: i64) -> i64 { + rcall!(spReflectionTypeLayout_getSubObjectRangeBindingRangeIndex( + self, + sub_object_range_index + )) + } + + pub fn sub_object_range_space_offset(&self, sub_object_range_index: i64) -> i64 { + rcall!(spReflectionTypeLayout_getSubObjectRangeSpaceOffset( + self, + sub_object_range_index + )) + } + + pub fn sub_object_range_offset(&self, sub_object_range_index: i64) -> &VariableLayout { + rcall!( + spReflectionTypeLayout_getSubObjectRangeOffset(self, sub_object_range_index) + as &VariableLayout + ) + } +} diff --git a/src/reflection/type_parameter.rs b/src/reflection/type_parameter.rs new file mode 100644 index 0000000..8f7d6b3 --- /dev/null +++ b/src/reflection/type_parameter.rs @@ -0,0 +1,29 @@ +use super::{rcall, Type}; +use slang_sys as sys; + +#[repr(transparent)] +pub struct TypeParameter(sys::SlangReflectionTypeParameter); + +impl TypeParameter { + pub fn name(&self) -> &str { + let name = rcall!(spReflectionTypeParameter_GetName(self)); + unsafe { std::ffi::CStr::from_ptr(name).to_str().unwrap() } + } + + pub fn index(&self) -> u32 { + rcall!(spReflectionTypeParameter_GetIndex(self)) + } + + pub fn constraint_count(&self) -> u32 { + rcall!(spReflectionTypeParameter_GetConstraintCount(self)) + } + + pub fn constraint_by_index(&self, index: u32) -> &Type { + rcall!(spReflectionTypeParameter_GetConstraintByIndex(self, index) as &Type) + } + + pub fn constraints(&self) -> impl ExactSizeIterator { + (0..self.constraint_count()) + .map(move |i| rcall!(spReflectionTypeParameter_GetConstraintByIndex(self, i) as &Type)) + } +} diff --git a/src/reflection/user_attribute.rs b/src/reflection/user_attribute.rs new file mode 100644 index 0000000..0acbdd8 --- /dev/null +++ b/src/reflection/user_attribute.rs @@ -0,0 +1,54 @@ +use super::{rcall, Type}; +use slang_sys as sys; + +fn succeeded(result: sys::SlangResult) -> bool { + result >= 0 +} + +#[repr(transparent)] +pub struct UserAttribute(sys::SlangReflectionUserAttribute); + +impl UserAttribute { + pub fn name(&self) -> &str { + let name = rcall!(spReflectionUserAttribute_GetName(self)); + unsafe { std::ffi::CStr::from_ptr(name).to_str().unwrap() } + } + + pub fn argument_count(&self) -> u32 { + rcall!(spReflectionUserAttribute_GetArgumentCount(self)) + } + + pub fn argument_type(&self, index: u32) -> &Type { + rcall!(spReflectionUserAttribute_GetArgumentType(self, index) as &Type) + } + + pub fn argument_value_int(&self, index: u32) -> Option { + let mut out = 0; + let result = rcall!(spReflectionUserAttribute_GetArgumentValueInt( + self, index, &mut out + )); + + succeeded(result).then(|| out) + } + + pub fn argument_value_float(&self, index: u32) -> Option { + let mut out = 0.0; + let result = rcall!(spReflectionUserAttribute_GetArgumentValueFloat( + self, index, &mut out + )); + + succeeded(result).then(|| out) + } + + pub fn argument_value_string(&self, index: u32) -> Option<&str> { + let mut len = 0; + let result = rcall!(spReflectionUserAttribute_GetArgumentValueString( + self, index, &mut len + )); + + (!result.is_null()).then(|| { + let slice = unsafe { std::slice::from_raw_parts(result as *const u8, len as usize) }; + std::str::from_utf8(slice).unwrap() + }) + } +} diff --git a/src/reflection/variable.rs b/src/reflection/variable.rs new file mode 100644 index 0000000..a0c85d7 --- /dev/null +++ b/src/reflection/variable.rs @@ -0,0 +1,105 @@ +use super::{rcall, Type, TypeLayout, UserAttribute}; +use slang_sys as sys; + +#[repr(transparent)] +pub struct Variable(sys::SlangReflectionVariable); + +impl Variable { + pub fn name(&self) -> &str { + let name = rcall!(spReflectionVariable_GetName(self)); + unsafe { std::ffi::CStr::from_ptr(name).to_str().unwrap() } + } + + pub fn ty(&self) -> &Type { + rcall!(spReflectionVariable_GetType(self) as &Type) + } + + // TODO: find_modifier + + pub fn user_attribute_count(&self) -> u32 { + rcall!(spReflectionVariable_GetUserAttributeCount(self)) + } + + pub fn user_attribute_by_index(&self, index: u32) -> Option<&UserAttribute> { + rcall!(spReflectionVariable_GetUserAttribute(self, index) as Option<&UserAttribute>) + } + + pub fn user_attributes(&self) -> impl ExactSizeIterator { + (0..self.user_attribute_count()) + .map(move |i| rcall!(spReflectionVariable_GetUserAttribute(self, i) as &UserAttribute)) + } + + // TODO: find_user_attribute_by_name + + pub fn has_default_value(&self) -> bool { + rcall!(spReflectionVariable_HasDefaultValue(self)) + } + + // TODO: generic_container + // TODO: apply_specializations +} + +#[repr(transparent)] +pub struct VariableLayout(sys::SlangReflectionVariableLayout); + +impl VariableLayout { + pub fn variable(&self) -> &Variable { + rcall!(spReflectionVariableLayout_GetVariable(self) as &Variable) + } + + // TODO: get_name + // TODO: find_modifier + + pub fn type_layout(&self) -> &TypeLayout { + rcall!(spReflectionVariableLayout_GetTypeLayout(self) as &TypeLayout) + } + + pub fn category(&self) -> sys::SlangParameterCategory { + self.type_layout().parameter_category() + } + + pub fn category_count(&self) -> u32 { + self.type_layout().category_count() + } + + pub fn category_by_index(&self, index: u32) -> sys::SlangParameterCategory { + self.type_layout().category_by_index(index) + } + + pub fn offset(&self, category: sys::SlangParameterCategory) -> usize { + rcall!(spReflectionVariableLayout_GetOffset(self, category)) + } + + pub fn ty(&self) -> &Type { + self.variable().ty() + } + + pub fn binding_index(&self) -> u32 { + rcall!(spReflectionParameter_GetBindingIndex(self)) + } + + pub fn binding_space(&self) -> u32 { + rcall!(spReflectionParameter_GetBindingSpace(self)) + } + + pub fn binding_space_with_category(&self, category: sys::SlangParameterCategory) -> usize { + rcall!(spReflectionVariableLayout_GetSpace(self, category)) + } + + pub fn semantic_name(&self) -> Option<&str> { + let name = rcall!(spReflectionVariableLayout_GetSemanticName(self)); + unsafe { (!name.is_null()).then(|| std::ffi::CStr::from_ptr(name).to_str().unwrap()) } + } + + pub fn semantic_index(&self) -> usize { + rcall!(spReflectionVariableLayout_GetSemanticIndex(self)) + } + + pub fn stage(&self) -> sys::SlangStage { + rcall!(spReflectionVariableLayout_getStage(self)) + } + + pub fn pending_data_layout(&self) -> &VariableLayout { + rcall!(spReflectionVariableLayout_getPendingDataLayout(self) as &VariableLayout) + } +}