├── .gitignore ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── rustfmt.toml ├── shaders └── test.slang ├── slang-sys ├── Cargo.toml ├── build.rs └── src │ └── lib.rs └── src ├── lib.rs ├── reflection ├── decl.rs ├── entry_point.rs ├── function.rs ├── generic.rs ├── mod.rs ├── shader.rs ├── ty.rs ├── type_parameter.rs ├── user_attribute.rs └── variable.rs └── tests.rs /.gitignore: -------------------------------------------------------------------------------- 1 | debug/ 2 | target/ 3 | Cargo.lock 4 | 5 | .cargo/ 6 | .vscode/ 7 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "slang" 3 | version = "0.1.0" 4 | edition = "2024" 5 | license = "MIT OR Apache-2.0" 6 | publish = false 7 | 8 | [dependencies] 9 | slang-sys = { path = "slang-sys" } 10 | 11 | [workspace] 12 | members = [ 13 | "slang-sys" 14 | ] 15 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Slang Rust Bindings 2 | 3 | Rust bindings for the [Slang](https://github.com/shader-slang/slang/) shader language. Supporting both the modern compilation and reflection API. 4 | 5 | Currently mostly reflects the needs of our own [engine](https://github.com/FloatyMonkey/engine) but contributions are more than welcome. 6 | 7 | ## Example 8 | 9 | ```rust 10 | let global_session = slang::GlobalSession::new().unwrap(); 11 | 12 | let search_path = std::ffi::CString::new("shaders/directory").unwrap(); 13 | 14 | // All compiler options are available through this builder. 15 | let session_options = slang::CompilerOptions::default() 16 | .optimization(slang::OptimizationLevel::High) 17 | .matrix_layout_row(true); 18 | 19 | let target_desc = slang::TargetDesc::default() 20 | .format(slang::CompileTarget::Dxil) 21 | .profile(global_session.find_profile("sm_6_5")); 22 | 23 | let targets = [target_desc]; 24 | let search_paths = [search_path.as_ptr()]; 25 | 26 | let session_desc = slang::SessionDesc::default() 27 | .targets(&targets) 28 | .search_paths(&search_paths) 29 | .options(&session_options); 30 | 31 | let session = global_session.create_session(&session_desc).unwrap(); 32 | let module = session.load_module("filename.slang").unwrap(); 33 | let entry_point = module.find_entry_point_by_name("main").unwrap(); 34 | 35 | let program = session.create_composite_component_type(&[ 36 | module.downcast().clone(), entry_point.downcast().clone(), 37 | ]).unwrap(); 38 | 39 | let linked_program = program.link().unwrap(); 40 | 41 | // Entry point to the reflection API. 42 | let reflection = linked_program.layout(0).unwrap(); 43 | 44 | let shader_bytecode = linked_program.entry_point_code(0, 0).unwrap(); 45 | ``` 46 | 47 | ## Installation 48 | 49 | Add the following to the `[dependencies]` section of your `Cargo.toml`: 50 | 51 | ```toml 52 | slang = { git = "https://github.com/FloatyMonkey/slang-rs.git" } 53 | ``` 54 | 55 | Point this library to a Slang installation. An easy way is by installing the [LunarG Vulkan SDK](https://vulkan.lunarg.com) which comes bundled with the Slang compiler. During installation `VULKAN_SDK` is added to the `PATH` and automatically picked up by this library. 56 | 57 | Alternatively, download Slang from their [releases page](https://github.com/shader-slang/slang/releases) and manually set the `SLANG_DIR` environment variable to the path of your Slang directory. Copy `slang.dll` to your executable's directory. To compile to DXIL bytecode, also copy `dxil.dll` and `dxcompiler.dll` from the [Microsoft DirectXShaderCompiler](https://github.com/microsoft/DirectXShaderCompiler/releases) to your executable's directory. 58 | 59 | ## Credits 60 | 61 | Maintained by Lauro Oyen ([@laurooyen](https://github.com/laurooyen)). 62 | 63 | Licensed under [MIT](LICENSE-MIT) or [Apache-2.0](LICENSE-APACHE). 64 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | hard_tabs = true 2 | -------------------------------------------------------------------------------- /shaders/test.slang: -------------------------------------------------------------------------------- 1 | StructuredBuffer input_0; 2 | StructuredBuffer input_1; 3 | RWStructuredBuffer output; 4 | 5 | [shader("compute")] 6 | [numthreads(1, 1, 1)] 7 | void main(uint3 thread_id : SV_DispatchThreadID) { 8 | let index = thread_id.x; 9 | output[index] = input_0[index] * input_1[index]; 10 | } 11 | -------------------------------------------------------------------------------- /slang-sys/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "slang-sys" 3 | version = "0.1.0" 4 | edition = "2024" 5 | license = "MIT OR Apache-2.0" 6 | publish = false 7 | 8 | [build-dependencies] 9 | bindgen = "0.71.1" 10 | -------------------------------------------------------------------------------- /slang-sys/build.rs: -------------------------------------------------------------------------------- 1 | extern crate bindgen; 2 | 3 | use std::env; 4 | use std::path::{Path, PathBuf}; 5 | 6 | fn main() { 7 | println!("cargo:rerun-if-env-changed=SLANG_DIR"); 8 | println!("cargo:rerun-if-env-changed=VULKAN_SDK"); 9 | 10 | let mut include_file = PathBuf::from("include"); 11 | let slang_dir = if let Ok(slang_dir) = env::var("SLANG_DIR").map(PathBuf::from) { 12 | include_file = include_file.join("slang.h"); 13 | slang_dir 14 | } else if let Ok(vulkan_sdk_dir) = env::var("VULKAN_SDK").map(PathBuf::from) { 15 | include_file = include_file.join("slang/slang.h"); 16 | vulkan_sdk_dir 17 | } else { 18 | panic!( 19 | "Environment `SLANG_DIR` should be set to the directory of a Slang installation, or `VULKAN_SDK` should be set to the directory of the Vulkan SKD installation." 20 | ); 21 | }; 22 | 23 | let out_dir = env::var("OUT_DIR") 24 | .map(PathBuf::from) 25 | .expect("Couldn't determine output directory."); 26 | 27 | link_libraries(&slang_dir); 28 | 29 | bindgen::builder() 30 | .header(slang_dir.join(include_file).to_str().unwrap()) 31 | .clang_arg("-v") 32 | .clang_arg("-xc++") 33 | .clang_arg("-std=c++17") 34 | .allowlist_function("spReflection.*") 35 | .allowlist_function("spComputeStringHash") 36 | .allowlist_function("slang_.*") 37 | .allowlist_type("slang.*") 38 | .allowlist_var("SLANG_.*") 39 | .with_codegen_config( 40 | bindgen::CodegenConfig::FUNCTIONS 41 | | bindgen::CodegenConfig::TYPES 42 | | bindgen::CodegenConfig::VARS, 43 | ) 44 | .parse_callbacks(Box::new(ParseCallback {})) 45 | .default_enum_style(bindgen::EnumVariation::Rust { 46 | non_exhaustive: false, 47 | }) 48 | .constified_enum("SlangProfileID") 49 | .constified_enum("SlangCapabilityID") 50 | .vtable_generation(true) 51 | .layout_tests(false) 52 | .derive_copy(true) 53 | .generate() 54 | .expect("Couldn't generate bindings.") 55 | .write_to_file(out_dir.join("bindings.rs")) 56 | .expect("Couldn't write bindings."); 57 | } 58 | 59 | fn link_libraries(slang_dir: &Path) { 60 | let lib_dir = slang_dir.join("lib"); 61 | 62 | if !lib_dir.is_dir() { 63 | panic!("Couldn't find the `lib` subdirectory in the Slang installation directory.") 64 | } 65 | 66 | println!("cargo:rustc-link-search=native={}", lib_dir.display()); 67 | println!("cargo:rustc-link-lib=dylib=slang"); 68 | } 69 | 70 | #[derive(Debug)] 71 | struct ParseCallback {} 72 | 73 | impl bindgen::callbacks::ParseCallbacks for ParseCallback { 74 | fn enum_variant_name( 75 | &self, 76 | enum_name: Option<&str>, 77 | original_variant_name: &str, 78 | _variant_value: bindgen::callbacks::EnumVariantValue, 79 | ) -> Option { 80 | let enum_name = enum_name?; 81 | 82 | // Map enum names to the part of their variant names that needs to be trimmed. 83 | // When an enum name is not in this map the code below will try to trim the enum name itself. 84 | let mut map = std::collections::HashMap::new(); 85 | map.insert("SlangMatrixLayoutMode", "SlangMatrixLayout"); 86 | map.insert("SlangCompileTarget", "Slang"); 87 | 88 | let trim = map.get(enum_name).unwrap_or(&enum_name); 89 | let new_variant_name = pascal_case_from_snake_case(original_variant_name); 90 | let new_variant_name = new_variant_name.trim_start_matches(trim); 91 | Some(new_variant_name.to_string()) 92 | } 93 | } 94 | 95 | /// Converts `snake_case` or `SNAKE_CASE` to `PascalCase`. 96 | /// If the input is already in `PascalCase` it will be returned as is. 97 | fn pascal_case_from_snake_case(snake_case: &str) -> String { 98 | let mut result = String::new(); 99 | 100 | let should_lower = snake_case 101 | .chars() 102 | .filter(|c| c.is_alphabetic()) 103 | .all(|c| c.is_uppercase()); 104 | 105 | for part in snake_case.split('_') { 106 | for (i, c) in part.chars().enumerate() { 107 | if i == 0 { 108 | result.push(c.to_ascii_uppercase()); 109 | } else if should_lower { 110 | result.push(c.to_ascii_lowercase()); 111 | } else { 112 | result.push(c); 113 | } 114 | } 115 | } 116 | 117 | result 118 | } 119 | -------------------------------------------------------------------------------- /slang-sys/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(rustfmt, rustfmt_skip)] 2 | #![allow(non_camel_case_types, non_snake_case, non_upper_case_globals)] 3 | include!(concat!(env!("OUT_DIR"), "/bindings.rs")); 4 | 5 | use std::ffi::{c_char, c_int, c_void}; 6 | 7 | // Based on Slang version 2024.14.5 8 | 9 | #[repr(C)] 10 | pub struct ICastableVtable { 11 | pub _base: ISlangUnknown__bindgen_vtable, 12 | 13 | pub castAs: unsafe extern "C" fn(*mut c_void, guid: *const SlangUUID) -> *mut c_void, 14 | } 15 | 16 | #[repr(C)] 17 | pub struct IBlobVtable { 18 | pub _base: ISlangUnknown__bindgen_vtable, 19 | 20 | pub getBufferPointer: unsafe extern "C" fn(*mut c_void) -> *const c_void, 21 | pub getBufferSize: unsafe extern "C" fn(*mut c_void) -> usize, 22 | } 23 | 24 | #[repr(C)] 25 | pub struct IGlobalSessionVtable { 26 | pub _base: ISlangUnknown__bindgen_vtable, 27 | 28 | pub createSession: unsafe extern "C" fn(*mut c_void, desc: *const slang_SessionDesc, outSession: *mut *mut slang_ISession) -> SlangResult, 29 | pub findProfile: unsafe extern "C" fn(*mut c_void, name: *const c_char) -> SlangProfileID, 30 | pub setDownstreamCompilerPath: unsafe extern "C" fn(*mut c_void, passThrough: SlangPassThrough, path: *const c_char), 31 | #[deprecated( note = "Use setLanguagePrelude instead")] 32 | pub setDownstreamCompilerPrelude: unsafe extern "C" fn(*mut c_void, passThrough: SlangPassThrough, preludeText: *const c_char), 33 | #[deprecated( note = "Use getLanguagePrelude instead")] 34 | pub getDownstreamCompilerPrelude: unsafe extern "C" fn(*mut c_void, passThrough: SlangPassThrough, outPrelude: *mut *mut ISlangBlob), 35 | pub getBuildTagString: unsafe extern "C" fn(*mut c_void) -> *const c_char, 36 | pub setDefaultDownstreamCompiler: unsafe extern "C" fn(*mut c_void, sourceLanguage: SlangSourceLanguage, defaultCompiler: SlangPassThrough) -> SlangResult, 37 | pub getDefaultDownstreamCompiler: unsafe extern "C" fn(*mut c_void, sourceLanguage: SlangSourceLanguage) -> SlangPassThrough, 38 | pub setLanguagePrelude: unsafe extern "C" fn(*mut c_void, sourceLanguage: SlangSourceLanguage, preludeText: *const c_char), 39 | pub getLanguagePrelude: unsafe extern "C" fn(*mut c_void, sourceLanguage: SlangSourceLanguage, outPrelude: *mut *mut ISlangBlob), 40 | pub createCompileRequest: unsafe extern "C" fn(*mut c_void, *mut *mut slang_ICompileRequest) -> SlangResult, 41 | pub addBuiltins: unsafe extern "C" fn(*mut c_void, sourcePath: *const c_char, sourceString: *const c_char), 42 | pub setSharedLibraryLoader: unsafe extern "C" fn(*mut c_void, loader: *mut ISlangSharedLibraryLoader), 43 | pub getSharedLibraryLoader: unsafe extern "C" fn(*mut c_void) -> *mut ISlangSharedLibraryLoader, 44 | pub checkCompileTargetSupport: unsafe extern "C" fn(*mut c_void, target: SlangCompileTarget) -> SlangResult, 45 | pub checkPassThroughSupport: unsafe extern "C" fn(*mut c_void, passThrough: SlangPassThrough) -> SlangResult, 46 | pub compileCoreModule: unsafe extern "C" fn(*mut c_void, flags: slang_CompileCoreModuleFlags) -> SlangResult, 47 | pub loadCoreModule: unsafe extern "C" fn(*mut c_void, coreModule: *const c_void, coreModuleSizeInBytes: usize) -> SlangResult, 48 | pub saveCoreModule: unsafe extern "C" fn(*mut c_void, archiveType: SlangArchiveType, outBlob: *mut *mut ISlangBlob) -> SlangResult, 49 | pub findCapability: unsafe extern "C" fn(*mut c_void, name: *const c_char) -> SlangCapabilityID, 50 | pub setDownstreamCompilerForTransition: unsafe extern "C" fn(*mut c_void, source: SlangCompileTarget, target: SlangCompileTarget, compiler: SlangPassThrough), 51 | pub getDownstreamCompilerForTransition: unsafe extern "C" fn(*mut c_void, source: SlangCompileTarget, target: SlangCompileTarget) -> SlangPassThrough, 52 | pub getCompilerElapsedTime: unsafe extern "C" fn(*mut c_void, outTotalTime: *mut f64, outDownstreamTime: *mut f64), 53 | pub setSPIRVCoreGrammar: unsafe extern "C" fn(*mut c_void, jsonPath: *const c_char) -> SlangResult, 54 | pub parseCommandLineArguments: unsafe extern "C" fn(*mut c_void, argc: c_int, argv: *const *const c_char, outSessionDesc: *mut slang_SessionDesc, outAuxAllocation: *mut *mut ISlangUnknown) -> SlangResult, 55 | pub getSessionDescDigest: unsafe extern "C" fn(*mut c_void, sessionDesc: *const slang_SessionDesc, outBlob: *mut *mut ISlangBlob) -> SlangResult, 56 | } 57 | 58 | #[repr(C)] 59 | pub struct ISessionVtable { 60 | pub _base: ISlangUnknown__bindgen_vtable, 61 | 62 | pub getGlobalSession: unsafe extern "C" fn(*mut c_void) -> *mut slang_IGlobalSession, 63 | pub loadModule: unsafe extern "C" fn(*mut c_void, moduleName: *const c_char, outDiagnostics: *mut *mut ISlangBlob) -> *mut slang_IModule, 64 | pub loadModuleFromSource: unsafe extern "C" fn(*mut c_void, moduleName: *const c_char, path: *const c_char, source: *mut ISlangBlob, outDiagnostics: *mut *mut ISlangBlob) -> *mut slang_IModule, 65 | pub createCompositeComponentType: unsafe extern "C" fn(*mut c_void, componentTypes: *const *const slang_IComponentType, componentTypeCount: SlangInt, outCompositeComponentType: *mut *mut slang_IComponentType, outDiagnostics: *mut *mut ISlangBlob) -> SlangResult, 66 | pub specializeType: unsafe extern "C" fn(*mut c_void, type_: *mut slang_TypeReflection, specializationArgs: *const slang_SpecializationArg, specializationArgCount: SlangInt, outDiagnostics: *mut *mut ISlangBlob) -> *mut slang_TypeReflection, 67 | pub getTypeLayout: unsafe extern "C" fn(*mut c_void, type_: *mut slang_TypeReflection, targetIndex: SlangInt, rules: slang_LayoutRules, outDiagnostics: *mut *mut ISlangBlob) -> *mut slang_TypeLayoutReflection, 68 | pub getContainerType: unsafe extern "C" fn(*mut c_void, elementType: *mut slang_TypeReflection, containerType: slang_ContainerType, outDiagnostics: *mut *mut ISlangBlob) -> *mut slang_TypeReflection, 69 | pub getDynamicType: unsafe extern "C" fn(*mut c_void) -> *mut slang_TypeReflection, 70 | pub getTypeRTTIMangledName: unsafe extern "C" fn(*mut c_void, type_: *mut slang_TypeReflection, outNameBlob: *mut *mut ISlangBlob) -> SlangResult, 71 | pub getTypeConformanceWitnessMangledName: unsafe extern "C" fn(*mut c_void, type_: *mut slang_TypeReflection, interfaceType: *mut slang_TypeReflection, outNameBlob: *mut *mut ISlangBlob) -> SlangResult, 72 | pub getTypeConformanceWitnessSequentialID: unsafe extern "C" fn(*mut c_void, type_: *mut slang_TypeReflection, interfaceType: *mut slang_TypeReflection, outId: *mut u32) -> SlangResult, 73 | pub createCompileRequest: unsafe extern "C" fn(*mut c_void, outCompileRequest: *mut *mut slang_ICompileRequest) -> SlangResult, 74 | pub createTypeConformanceComponentType: unsafe extern "C" fn(*mut c_void, type_: *mut slang_TypeReflection, interfaceType: *mut slang_TypeReflection, outConformance: *mut *mut slang_ITypeConformance, conformanceIdOverride: SlangInt, outDiagnostics: *mut *mut ISlangBlob) -> SlangResult, 75 | pub loadModuleFromIRBlob: unsafe extern "C" fn(*mut c_void, moduleName: *const c_char, path: *const c_char, source: *mut ISlangBlob, outDiagnostics: *mut *mut ISlangBlob) -> *mut slang_IModule, 76 | pub getLoadedModuleCount: unsafe extern "C" fn(*mut c_void) -> SlangInt, 77 | pub getLoadedModule: unsafe extern "C" fn(*mut c_void, index: SlangInt) -> *mut slang_IModule, 78 | pub isBinaryModuleUpToDate: unsafe extern "C" fn(*mut c_void, modulePath: *const c_char, binaryModuleBlob: *mut ISlangBlob) -> bool, 79 | pub loadModuleFromSourceString: unsafe extern "C" fn(*mut c_void, moduleName: *const c_char, path: *const c_char, string: *const c_char, outDiagnostics: *mut *mut ISlangBlob) -> *mut slang_IModule, 80 | } 81 | 82 | #[repr(C)] 83 | pub struct IMetadataVtable { 84 | pub _base: ICastableVtable, 85 | 86 | pub isParameterLocationUsed: unsafe extern "C" fn(*mut c_void, category: SlangParameterCategory, spaceIndex: SlangUInt, registerIndex: SlangUInt, outUsed: *mut bool) -> SlangResult, 87 | } 88 | 89 | #[repr(C)] 90 | pub struct IComponentTypeVtable { 91 | pub _base: ISlangUnknown__bindgen_vtable, 92 | 93 | pub getSession: unsafe extern "C" fn(*mut c_void) -> *mut slang_ISession, 94 | pub getLayout: unsafe extern "C" fn(*mut c_void, targetIndex: SlangInt, outDiagnostics: *mut *mut ISlangBlob) -> *mut slang_ProgramLayout, 95 | pub getSpecializationParamCount: unsafe extern "C" fn(*mut c_void) -> SlangInt, 96 | pub getEntryPointCode: unsafe extern "C" fn(*mut c_void, entryPointIndex: SlangInt, targetIndex: SlangInt, outCode: *mut *mut ISlangBlob, outDiagnostics: *mut *mut ISlangBlob) -> SlangResult, 97 | pub getResultAsFileSystem: unsafe extern "C" fn(*mut c_void, entryPointIndex: SlangInt, targetIndex: SlangInt, outFileSystem: *mut *mut ISlangMutableFileSystem) -> SlangResult, 98 | pub getEntryPointHash: unsafe extern "C" fn(*mut c_void, entryPointIndex: SlangInt, targetIndex: SlangInt, outHash: *mut *mut ISlangBlob), 99 | pub specialize: unsafe extern "C" fn(*mut c_void, specializationArgs: *const slang_SpecializationArg, specializationArgCount: SlangInt, outSpecializedComponentType: *mut *mut slang_IComponentType, outDiagnostics: *mut *mut ISlangBlob) -> SlangResult, 100 | pub link: unsafe extern "C" fn(*mut c_void, outLinkedComponentType: *mut *mut slang_IComponentType, outDiagnostics: *mut *mut ISlangBlob) -> SlangResult, 101 | pub getEntryPointHostCallable: unsafe extern "C" fn(*mut c_void, entryPointIndex: c_int, targetIndex: c_int, outSharedLibrary: *mut *mut ISlangSharedLibrary, outDiagnostics: *mut *mut ISlangBlob) -> SlangResult, 102 | pub renameEntryPoint: unsafe extern "C" fn(*mut c_void, newName: *const c_char, outEntryPoint: *mut *mut slang_IComponentType) -> SlangResult, 103 | 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, 104 | pub getTargetCode: unsafe extern "C" fn(*mut c_void, targetIndex: SlangInt, outCode: *mut *mut ISlangBlob, outDiagnostics: *mut *mut ISlangBlob) -> SlangResult, 105 | pub getTargetMetadata: unsafe extern "C" fn(*mut c_void, targetIndex: SlangInt, outMetadata: *mut *mut slang_IMetadata, outDiagnostics: *mut *mut ISlangBlob) -> SlangResult, 106 | pub getEntryPointMetadata: unsafe extern "C" fn(*mut c_void, entryPointIndex: SlangInt, targetIndex: SlangInt, outMetadata: *mut *mut slang_IMetadata, outDiagnostics: *mut *mut ISlangBlob) -> SlangResult, 107 | } 108 | 109 | #[repr(C)] 110 | pub struct IEntryPointVtable { 111 | pub _base: IComponentTypeVtable, 112 | 113 | pub getFunctionReflection: unsafe extern "C" fn(*mut c_void) -> *mut slang_FunctionReflection, 114 | } 115 | 116 | #[repr(C)] 117 | pub struct ITypeConformanceVtable { 118 | pub _base: IComponentTypeVtable, 119 | } 120 | 121 | #[repr(C)] 122 | pub struct IModuleVtable { 123 | pub _base: IComponentTypeVtable, 124 | 125 | pub findEntryPointByName: unsafe extern "C" fn(*mut c_void, name: *const c_char, outEntryPoint: *mut *mut slang_IEntryPoint) -> SlangResult, 126 | pub getDefinedEntryPointCount: unsafe extern "C" fn(*mut c_void) -> SlangInt32, 127 | pub getDefinedEntryPoint: unsafe extern "C" fn(*mut c_void, index: SlangInt32, outEntryPoint: *mut *mut slang_IEntryPoint) -> SlangResult, 128 | pub serialize: unsafe extern "C" fn(*mut c_void, outSerializedBlob: *mut *mut ISlangBlob) -> SlangResult, 129 | pub writeToFile: unsafe extern "C" fn(*mut c_void, fileName: *const c_char) -> SlangResult, 130 | pub getName: unsafe extern "C" fn(*mut c_void) -> *const c_char, 131 | pub getFilePath: unsafe extern "C" fn(*mut c_void) -> *const c_char, 132 | pub getUniqueIdentity: unsafe extern "C" fn(*mut c_void) -> *const c_char, 133 | 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, 134 | pub getDependencyFileCount: unsafe extern "C" fn(*mut c_void) -> SlangInt32, 135 | pub getDependencyFilePath: unsafe extern "C" fn(*mut c_void, index: SlangInt32) -> *const c_char, 136 | pub getModuleReflection: unsafe extern "C" fn(*mut c_void) -> *mut slang_DeclReflection, 137 | } 138 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod reflection; 2 | 3 | #[cfg(test)] 4 | mod tests; 5 | 6 | use std::ffi::{CStr, CString}; 7 | use std::marker::PhantomData; 8 | use std::ptr::{null, null_mut}; 9 | 10 | use slang_sys as sys; 11 | 12 | pub use sys::{ 13 | SlangBindingType as BindingType, SlangCompileTarget as CompileTarget, 14 | SlangDebugInfoLevel as DebugInfoLevel, SlangFloatingPointMode as FloatingPointMode, 15 | SlangImageFormat as ImageFormat, SlangLineDirectiveMode as LineDirectiveMode, 16 | SlangMatrixLayoutMode as MatrixLayoutMode, SlangOptimizationLevel as OptimizationLevel, 17 | SlangParameterCategory as ParameterCategory, SlangResourceAccess as ResourceAccess, 18 | SlangResourceShape as ResourceShape, SlangScalarType as ScalarType, 19 | SlangSourceLanguage as SourceLanguage, SlangStage as Stage, SlangTypeKind as TypeKind, 20 | SlangUUID as UUID, slang_CompilerOptionName as CompilerOptionName, 21 | }; 22 | 23 | macro_rules! vcall { 24 | ($self:expr, $method:ident($($args:expr),*)) => { 25 | unsafe { ($self.vtable().$method)($self.as_raw(), $($args),*) } 26 | }; 27 | } 28 | 29 | const fn uuid(data1: u32, data2: u16, data3: u16, data4: [u8; 8]) -> UUID { 30 | UUID { 31 | data1, 32 | data2, 33 | data3, 34 | data4, 35 | } 36 | } 37 | 38 | pub enum Error { 39 | Code(sys::SlangResult), 40 | Blob(Blob), 41 | } 42 | 43 | impl std::fmt::Debug for Error { 44 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 45 | match self { 46 | Error::Code(code) => write!(f, "{}", code), 47 | Error::Blob(blob) => write!(f, "{}", blob.as_str().unwrap_or_default()), 48 | } 49 | } 50 | } 51 | 52 | impl std::fmt::Display for Error { 53 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 54 | std::fmt::Debug::fmt(self, f) 55 | } 56 | } 57 | 58 | impl std::error::Error for Error {} 59 | 60 | pub type Result = std::result::Result; 61 | 62 | pub(crate) fn succeeded(result: sys::SlangResult) -> bool { 63 | result >= 0 64 | } 65 | 66 | fn result_from_blob(code: sys::SlangResult, blob: *mut sys::slang_IBlob) -> Result<()> { 67 | if code < 0 && !blob.is_null() { 68 | Err(Error::Blob(Blob(IUnknown( 69 | std::ptr::NonNull::new(blob as *mut _).unwrap(), 70 | )))) 71 | } else if code < 0 { 72 | Err(Error::Code(code)) 73 | } else { 74 | Ok(()) 75 | } 76 | } 77 | 78 | pub struct ProfileID(sys::SlangProfileID); 79 | 80 | impl ProfileID { 81 | pub const UNKNOWN: ProfileID = ProfileID(sys::SlangProfileID_SlangProfileUnknown); 82 | } 83 | 84 | pub struct CapabilityID(sys::SlangCapabilityID); 85 | 86 | impl CapabilityID { 87 | pub const UNKNOWN: CapabilityID = CapabilityID(sys::SlangCapabilityID_SlangCapabilityUnknown); 88 | } 89 | 90 | unsafe trait Interface: Sized { 91 | type Vtable; 92 | const IID: UUID; 93 | 94 | #[inline(always)] 95 | unsafe fn vtable(&self) -> &Self::Vtable { 96 | unsafe { &**(self.as_raw() as *mut *mut Self::Vtable) } 97 | } 98 | 99 | #[inline(always)] 100 | unsafe fn as_raw(&self) -> *mut T { 101 | unsafe { std::mem::transmute_copy(self) } 102 | } 103 | 104 | fn as_unknown(&self) -> &IUnknown { 105 | // SAFETY: It is always safe to treat an `Interface` as an `IUnknown`. 106 | unsafe { std::mem::transmute(self) } 107 | } 108 | } 109 | 110 | pub unsafe trait Downcast { 111 | fn downcast(&self) -> &T; 112 | } 113 | 114 | #[repr(transparent)] 115 | pub struct IUnknown(std::ptr::NonNull); 116 | 117 | unsafe impl Interface for IUnknown { 118 | type Vtable = sys::ISlangUnknown__bindgen_vtable; 119 | const IID: UUID = uuid( 120 | 0x00000000, 121 | 0x0000, 122 | 0x0000, 123 | [0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46], 124 | ); 125 | } 126 | 127 | impl Clone for IUnknown { 128 | fn clone(&self) -> Self { 129 | vcall!(self, ISlangUnknown_addRef()); 130 | Self(self.0) 131 | } 132 | } 133 | 134 | impl Drop for IUnknown { 135 | fn drop(&mut self) { 136 | vcall!(self, ISlangUnknown_release()); 137 | } 138 | } 139 | 140 | #[repr(transparent)] 141 | #[derive(Clone)] 142 | pub struct Blob(IUnknown); 143 | 144 | unsafe impl Interface for Blob { 145 | type Vtable = sys::IBlobVtable; 146 | const IID: UUID = uuid( 147 | 0x8ba5fb08, 148 | 0x5195, 149 | 0x40e2, 150 | [0xac, 0x58, 0x0d, 0x98, 0x9c, 0x3a, 0x01, 0x02], 151 | ); 152 | } 153 | 154 | impl Blob { 155 | pub fn as_slice(&self) -> &[u8] { 156 | let ptr = vcall!(self, getBufferPointer()); 157 | let size = vcall!(self, getBufferSize()); 158 | unsafe { std::slice::from_raw_parts(ptr as *const u8, size) } 159 | } 160 | 161 | pub fn as_str(&self) -> std::result::Result<&str, std::str::Utf8Error> { 162 | std::str::from_utf8(self.as_slice()) 163 | } 164 | } 165 | 166 | #[repr(transparent)] 167 | #[derive(Clone)] 168 | pub struct GlobalSession(IUnknown); 169 | 170 | unsafe impl Interface for GlobalSession { 171 | type Vtable = sys::IGlobalSessionVtable; 172 | const IID: UUID = uuid( 173 | 0xc140b5fd, 174 | 0x0c78, 175 | 0x452e, 176 | [0xba, 0x7c, 0x1a, 0x1e, 0x70, 0xc7, 0xf7, 0x1c], 177 | ); 178 | } 179 | 180 | impl GlobalSession { 181 | pub fn new() -> Option { 182 | let mut global_session = null_mut(); 183 | unsafe { sys::slang_createGlobalSession(sys::SLANG_API_VERSION as _, &mut global_session) }; 184 | Some(GlobalSession(IUnknown(std::ptr::NonNull::new( 185 | global_session as *mut _, 186 | )?))) 187 | } 188 | 189 | pub fn new_without_core_module() -> Option { 190 | let mut global_session = null_mut(); 191 | unsafe { 192 | sys::slang_createGlobalSessionWithoutCoreModule( 193 | sys::SLANG_API_VERSION as _, 194 | &mut global_session, 195 | ) 196 | }; 197 | Some(GlobalSession(IUnknown(std::ptr::NonNull::new( 198 | global_session as *mut _, 199 | )?))) 200 | } 201 | 202 | pub fn create_session(&self, desc: &SessionDesc) -> Option { 203 | let mut session = null_mut(); 204 | vcall!(self, createSession(&**desc, &mut session)); 205 | Some(Session(IUnknown(std::ptr::NonNull::new( 206 | session as *mut _, 207 | )?))) 208 | } 209 | 210 | pub fn find_profile(&self, name: &str) -> ProfileID { 211 | let name = CString::new(name).unwrap(); 212 | ProfileID(vcall!(self, findProfile(name.as_ptr()))) 213 | } 214 | 215 | pub fn find_capability(&self, name: &str) -> CapabilityID { 216 | let name = CString::new(name).unwrap(); 217 | CapabilityID(vcall!(self, findCapability(name.as_ptr()))) 218 | } 219 | } 220 | 221 | #[repr(transparent)] 222 | #[derive(Clone)] 223 | pub struct Session(IUnknown); 224 | 225 | unsafe impl Interface for Session { 226 | type Vtable = sys::ISessionVtable; 227 | const IID: UUID = uuid( 228 | 0x67618701, 229 | 0xd116, 230 | 0x468f, 231 | [0xab, 0x3b, 0x47, 0x4b, 0xed, 0xce, 0x0e, 0x3d], 232 | ); 233 | } 234 | 235 | impl Session { 236 | pub fn load_module(&self, name: &str) -> Result { 237 | let name = CString::new(name).unwrap(); 238 | let mut diagnostics = null_mut(); 239 | 240 | let module = vcall!(self, loadModule(name.as_ptr(), &mut diagnostics)); 241 | 242 | if module.is_null() { 243 | let blob = Blob(IUnknown( 244 | std::ptr::NonNull::new(diagnostics as *mut _).unwrap(), 245 | )); 246 | Err(Error::Blob(blob)) 247 | } else { 248 | let module = Module(IUnknown(std::ptr::NonNull::new(module as *mut _).unwrap())); 249 | unsafe { (module.as_unknown().vtable().ISlangUnknown_addRef)(module.as_raw()) }; 250 | Ok(module) 251 | } 252 | } 253 | 254 | pub fn create_composite_component_type( 255 | &self, 256 | components: &[ComponentType], 257 | ) -> Result { 258 | let mut composite_component_type = null_mut(); 259 | let mut diagnostics = null_mut(); 260 | 261 | result_from_blob( 262 | vcall!( 263 | self, 264 | createCompositeComponentType( 265 | components.as_ptr() as _, 266 | components.len() as _, 267 | &mut composite_component_type, 268 | &mut diagnostics 269 | ) 270 | ), 271 | diagnostics, 272 | )?; 273 | 274 | Ok(ComponentType(IUnknown( 275 | std::ptr::NonNull::new(composite_component_type as *mut _).unwrap(), 276 | ))) 277 | } 278 | } 279 | 280 | #[repr(transparent)] 281 | #[derive(Clone)] 282 | pub struct Metadata(IUnknown); 283 | 284 | unsafe impl Interface for Metadata { 285 | type Vtable = sys::IMetadataVtable; 286 | const IID: UUID = uuid( 287 | 0x8044a8a3, 288 | 0xddc0, 289 | 0x4b7f, 290 | [0xaf, 0x8e, 0x2, 0x6e, 0x90, 0x5d, 0x73, 0x32], 291 | ); 292 | } 293 | 294 | impl Metadata { 295 | pub fn is_parameter_location_used( 296 | &self, 297 | category: ParameterCategory, 298 | space_index: u64, 299 | register_index: u64, 300 | ) -> Option { 301 | let mut used = false; 302 | let res = vcall!( 303 | self, 304 | isParameterLocationUsed(category, space_index, register_index, &mut used) 305 | ); 306 | succeeded(res).then(|| used) 307 | } 308 | } 309 | 310 | #[repr(transparent)] 311 | #[derive(Clone)] 312 | pub struct ComponentType(IUnknown); 313 | 314 | unsafe impl Interface for ComponentType { 315 | type Vtable = sys::IComponentTypeVtable; 316 | const IID: UUID = uuid( 317 | 0x5bc42be8, 318 | 0x5c50, 319 | 0x4929, 320 | [0x9e, 0x5e, 0xd1, 0x5e, 0x7c, 0x24, 0x01, 0x5f], 321 | ); 322 | } 323 | 324 | impl ComponentType { 325 | pub fn layout(&self, target: i64) -> Result<&reflection::Shader> { 326 | let mut diagnostics = null_mut(); 327 | let ptr = vcall!(self, getLayout(target, &mut diagnostics)); 328 | 329 | if ptr.is_null() { 330 | Err(Error::Blob(Blob(IUnknown( 331 | std::ptr::NonNull::new(diagnostics as *mut _).unwrap(), 332 | )))) 333 | } else { 334 | Ok(unsafe { &*(ptr as *const _) }) 335 | } 336 | } 337 | 338 | pub fn link(&self) -> Result { 339 | let mut linked_component_type = null_mut(); 340 | let mut diagnostics = null_mut(); 341 | 342 | result_from_blob( 343 | vcall!(self, link(&mut linked_component_type, &mut diagnostics)), 344 | diagnostics, 345 | )?; 346 | 347 | Ok(ComponentType(IUnknown( 348 | std::ptr::NonNull::new(linked_component_type as *mut _).unwrap(), 349 | ))) 350 | } 351 | 352 | pub fn target_code(&self, target: i64) -> Result { 353 | let mut code = null_mut(); 354 | let mut diagnostics = null_mut(); 355 | 356 | result_from_blob( 357 | vcall!(self, getTargetCode(target, &mut code, &mut diagnostics)), 358 | diagnostics, 359 | )?; 360 | 361 | Ok(Blob(IUnknown( 362 | std::ptr::NonNull::new(code as *mut _).unwrap(), 363 | ))) 364 | } 365 | 366 | pub fn entry_point_code(&self, index: i64, target: i64) -> Result { 367 | let mut code = null_mut(); 368 | let mut diagnostics = null_mut(); 369 | 370 | result_from_blob( 371 | vcall!( 372 | self, 373 | getEntryPointCode(index, target, &mut code, &mut diagnostics) 374 | ), 375 | diagnostics, 376 | )?; 377 | 378 | Ok(Blob(IUnknown( 379 | std::ptr::NonNull::new(code as *mut _).unwrap(), 380 | ))) 381 | } 382 | 383 | pub fn target_metadata(&self, target_index: i64) -> Result { 384 | let mut metadata = null_mut(); 385 | let mut diagnostics = null_mut(); 386 | 387 | result_from_blob( 388 | vcall!( 389 | self, 390 | getTargetMetadata(target_index, &mut metadata, &mut diagnostics) 391 | ), 392 | diagnostics, 393 | )?; 394 | 395 | Ok(Metadata(IUnknown( 396 | std::ptr::NonNull::new(metadata as *mut _).unwrap(), 397 | ))) 398 | } 399 | 400 | pub fn entry_point_metadata( 401 | &self, 402 | entry_point_index: i64, 403 | target_index: i64, 404 | ) -> Result { 405 | let mut metadata = null_mut(); 406 | let mut diagnostics = null_mut(); 407 | 408 | result_from_blob( 409 | vcall!( 410 | self, 411 | getEntryPointMetadata( 412 | entry_point_index, 413 | target_index, 414 | &mut metadata, 415 | &mut diagnostics 416 | ) 417 | ), 418 | diagnostics, 419 | )?; 420 | 421 | Ok(Metadata(IUnknown( 422 | std::ptr::NonNull::new(metadata as *mut _).unwrap(), 423 | ))) 424 | } 425 | } 426 | 427 | #[repr(transparent)] 428 | #[derive(Clone)] 429 | pub struct EntryPoint(IUnknown); 430 | 431 | unsafe impl Interface for EntryPoint { 432 | type Vtable = sys::IEntryPointVtable; 433 | const IID: UUID = uuid( 434 | 0x8f241361, 435 | 0xf5bd, 436 | 0x4ca0, 437 | [0xa3, 0xac, 0x02, 0xf7, 0xfa, 0x24, 0x02, 0xb8], 438 | ); 439 | } 440 | 441 | unsafe impl Downcast for EntryPoint { 442 | fn downcast(&self) -> &ComponentType { 443 | unsafe { std::mem::transmute(self) } 444 | } 445 | } 446 | 447 | impl EntryPoint { 448 | pub fn function_reflection(&self) -> &reflection::Function { 449 | let ptr = vcall!(self, getFunctionReflection()); 450 | unsafe { &*(ptr as *const _) } 451 | } 452 | } 453 | 454 | #[repr(transparent)] 455 | #[derive(Clone)] 456 | pub struct TypeConformance(IUnknown); 457 | 458 | unsafe impl Interface for TypeConformance { 459 | type Vtable = sys::ITypeConformanceVtable; 460 | const IID: UUID = uuid( 461 | 0x73eb3147, 462 | 0xe544, 463 | 0x41b5, 464 | [0xb8, 0xf0, 0xa2, 0x44, 0xdf, 0x21, 0x94, 0x0b], 465 | ); 466 | } 467 | 468 | unsafe impl Downcast for TypeConformance { 469 | fn downcast(&self) -> &ComponentType { 470 | unsafe { std::mem::transmute(self) } 471 | } 472 | } 473 | 474 | #[repr(transparent)] 475 | #[derive(Clone)] 476 | pub struct Module(IUnknown); 477 | 478 | unsafe impl Interface for Module { 479 | type Vtable = sys::IModuleVtable; 480 | const IID: UUID = uuid( 481 | 0x0c720e64, 482 | 0x8722, 483 | 0x4d31, 484 | [0x89, 0x90, 0x63, 0x8a, 0x98, 0xb1, 0xc2, 0x79], 485 | ); 486 | } 487 | 488 | unsafe impl Downcast for Module { 489 | fn downcast(&self) -> &ComponentType { 490 | unsafe { std::mem::transmute(self) } 491 | } 492 | } 493 | 494 | impl Module { 495 | pub fn find_entry_point_by_name(&self, name: &str) -> Option { 496 | let name = CString::new(name).unwrap(); 497 | let mut entry_point = null_mut(); 498 | vcall!(self, findEntryPointByName(name.as_ptr(), &mut entry_point)); 499 | Some(EntryPoint(IUnknown(std::ptr::NonNull::new( 500 | entry_point as *mut _, 501 | )?))) 502 | } 503 | 504 | pub fn entry_point_count(&self) -> u32 { 505 | vcall!(self, getDefinedEntryPointCount()) as _ 506 | } 507 | 508 | pub fn entry_point_by_index(&self, index: u32) -> Option { 509 | let mut entry_point = null_mut(); 510 | vcall!(self, getDefinedEntryPoint(index as _, &mut entry_point)); 511 | Some(EntryPoint(IUnknown(std::ptr::NonNull::new( 512 | entry_point as *mut _, 513 | )?))) 514 | } 515 | 516 | pub fn entry_points(&self) -> impl ExactSizeIterator { 517 | (0..self.entry_point_count()).map(move |i| self.entry_point_by_index(i).unwrap()) 518 | } 519 | 520 | pub fn name(&self) -> &str { 521 | let name = vcall!(self, getName()); 522 | unsafe { CStr::from_ptr(name).to_str().unwrap() } 523 | } 524 | 525 | pub fn file_path(&self) -> &str { 526 | let path = vcall!(self, getFilePath()); 527 | unsafe { CStr::from_ptr(path).to_str().unwrap() } 528 | } 529 | 530 | pub fn unique_identity(&self) -> &str { 531 | let identity = vcall!(self, getUniqueIdentity()); 532 | unsafe { CStr::from_ptr(identity).to_str().unwrap() } 533 | } 534 | 535 | pub fn dependency_file_count(&self) -> i32 { 536 | vcall!(self, getDependencyFileCount()) as i32 537 | } 538 | 539 | pub fn dependency_file_path(&self, index: i32) -> &str { 540 | let path = vcall!(self, getDependencyFilePath(index as i32)); 541 | unsafe { CStr::from_ptr(path).to_str().unwrap() } 542 | } 543 | 544 | pub fn dependency_file_paths(&self) -> impl ExactSizeIterator { 545 | (0..self.dependency_file_count()).map(move |i| self.dependency_file_path(i)) 546 | } 547 | 548 | pub fn module_reflection(&self) -> &reflection::Decl { 549 | let ptr = vcall!(self, getModuleReflection()); 550 | unsafe { &*(ptr as *const _) } 551 | } 552 | } 553 | 554 | #[repr(transparent)] 555 | pub struct TargetDesc<'a> { 556 | inner: sys::slang_TargetDesc, 557 | _phantom: PhantomData<&'a ()>, 558 | } 559 | 560 | impl std::ops::Deref for TargetDesc<'_> { 561 | type Target = sys::slang_TargetDesc; 562 | 563 | fn deref(&self) -> &Self::Target { 564 | &self.inner 565 | } 566 | } 567 | 568 | impl Default for TargetDesc<'_> { 569 | fn default() -> Self { 570 | Self { 571 | inner: sys::slang_TargetDesc { 572 | structureSize: std::mem::size_of::(), 573 | ..unsafe { std::mem::zeroed() } 574 | }, 575 | _phantom: PhantomData, 576 | } 577 | } 578 | } 579 | 580 | impl<'a> TargetDesc<'a> { 581 | pub fn format(mut self, format: CompileTarget) -> Self { 582 | self.inner.format = format; 583 | self 584 | } 585 | 586 | pub fn profile(mut self, profile: ProfileID) -> Self { 587 | self.inner.profile = profile.0; 588 | self 589 | } 590 | 591 | pub fn options(mut self, options: &'a CompilerOptions) -> Self { 592 | self.inner.compilerOptionEntries = options.options.as_ptr() as _; 593 | self.inner.compilerOptionEntryCount = options.options.len() as _; 594 | self 595 | } 596 | } 597 | 598 | #[repr(transparent)] 599 | pub struct SessionDesc<'a> { 600 | inner: sys::slang_SessionDesc, 601 | _phantom: PhantomData<&'a ()>, 602 | } 603 | 604 | impl std::ops::Deref for SessionDesc<'_> { 605 | type Target = sys::slang_SessionDesc; 606 | 607 | fn deref(&self) -> &Self::Target { 608 | &self.inner 609 | } 610 | } 611 | 612 | impl Default for SessionDesc<'_> { 613 | fn default() -> Self { 614 | Self { 615 | inner: sys::slang_SessionDesc { 616 | structureSize: std::mem::size_of::(), 617 | ..unsafe { std::mem::zeroed() } 618 | }, 619 | _phantom: PhantomData, 620 | } 621 | } 622 | } 623 | 624 | impl<'a> SessionDesc<'a> { 625 | pub fn targets(mut self, targets: &'a [TargetDesc]) -> Self { 626 | self.inner.targets = targets.as_ptr() as _; 627 | self.inner.targetCount = targets.len() as _; 628 | self 629 | } 630 | 631 | pub fn search_paths(mut self, paths: &'a [*const i8]) -> Self { 632 | self.inner.searchPaths = paths.as_ptr(); 633 | self.inner.searchPathCount = paths.len() as _; 634 | self 635 | } 636 | 637 | pub fn options(mut self, options: &'a CompilerOptions) -> Self { 638 | self.inner.compilerOptionEntries = options.options.as_ptr() as _; 639 | self.inner.compilerOptionEntryCount = options.options.len() as _; 640 | self 641 | } 642 | } 643 | 644 | macro_rules! option { 645 | ($name:ident, $func:ident($p_name:ident: $p_type:ident)) => { 646 | #[inline(always)] 647 | pub fn $func(self, $p_name: $p_type) -> Self { 648 | self.push_ints(CompilerOptionName::$name, $p_name as _, 0) 649 | } 650 | }; 651 | 652 | ($name:ident, $func:ident($p_name:ident: &str)) => { 653 | #[inline(always)] 654 | pub fn $func(self, $p_name: &str) -> Self { 655 | self.push_str1(CompilerOptionName::$name, $p_name) 656 | } 657 | }; 658 | 659 | ($name:ident, $func:ident($p_name1:ident: &str, $p_name2:ident: &str)) => { 660 | #[inline(always)] 661 | pub fn $func(self, $p_name1: &str, $p_name2: &str) -> Self { 662 | self.push_str2(CompilerOptionName::$name, $p_name1, $p_name2) 663 | } 664 | }; 665 | } 666 | 667 | #[derive(Default)] 668 | pub struct CompilerOptions { 669 | strings: Vec, 670 | options: Vec, 671 | } 672 | 673 | impl CompilerOptions { 674 | fn push_ints(mut self, name: CompilerOptionName, i0: i32, i1: i32) -> Self { 675 | self.options.push(sys::slang_CompilerOptionEntry { 676 | name, 677 | value: sys::slang_CompilerOptionValue { 678 | kind: sys::slang_CompilerOptionValueKind::Int, 679 | intValue0: i0, 680 | intValue1: i1, 681 | stringValue0: null(), 682 | stringValue1: null(), 683 | }, 684 | }); 685 | 686 | self 687 | } 688 | 689 | fn push_strings(mut self, name: CompilerOptionName, s0: *const i8, s1: *const i8) -> Self { 690 | self.options.push(sys::slang_CompilerOptionEntry { 691 | name, 692 | value: sys::slang_CompilerOptionValue { 693 | kind: sys::slang_CompilerOptionValueKind::String, 694 | intValue0: 0, 695 | intValue1: 0, 696 | stringValue0: s0, 697 | stringValue1: s1, 698 | }, 699 | }); 700 | 701 | self 702 | } 703 | 704 | fn push_str1(mut self, name: CompilerOptionName, s0: &str) -> Self { 705 | let s0 = CString::new(s0).unwrap(); 706 | let s0_ptr = s0.as_ptr(); 707 | self.strings.push(s0); 708 | 709 | self.push_strings(name, s0_ptr, null()) 710 | } 711 | 712 | fn push_str2(mut self, name: CompilerOptionName, s0: &str, s1: &str) -> Self { 713 | let s0 = CString::new(s0).unwrap(); 714 | let s0_ptr = s0.as_ptr(); 715 | self.strings.push(s0); 716 | 717 | let s1 = CString::new(s1).unwrap(); 718 | let s1_ptr = s1.as_ptr(); 719 | self.strings.push(s1); 720 | 721 | self.push_strings(name, s0_ptr, s1_ptr) 722 | } 723 | } 724 | 725 | impl CompilerOptions { 726 | option!(MacroDefine, macro_define(key: &str, value: &str)); 727 | option!(Include, include(path: &str)); 728 | option!(Language, language(language: SourceLanguage)); 729 | option!(MatrixLayoutColumn, matrix_layout_column(enable: bool)); 730 | option!(MatrixLayoutRow, matrix_layout_row(enable: bool)); 731 | 732 | #[inline(always)] 733 | pub fn profile(self, profile: ProfileID) -> Self { 734 | self.push_ints(CompilerOptionName::Profile, profile.0 as _, 0) 735 | } 736 | 737 | option!(Stage, stage(stage: Stage)); 738 | option!(Target, target(target: CompileTarget)); 739 | option!(WarningsAsErrors, warnings_as_errors(warning_codes: &str)); 740 | option!(DisableWarnings, disable_warnings(warning_codes: &str)); 741 | option!(EnableWarning, enable_warning(warning_code: &str)); 742 | option!(DisableWarning, disable_warning(warning_code: &str)); 743 | option!(ReportDownstreamTime, report_downstream_time(enable: bool)); 744 | option!(ReportPerfBenchmark, report_perf_benchmark(enable: bool)); 745 | option!(SkipSPIRVValidation, skip_spirv_validation(enable: bool)); 746 | 747 | // Target 748 | #[inline(always)] 749 | pub fn capability(self, capability: CapabilityID) -> Self { 750 | self.push_ints(CompilerOptionName::Capability, capability.0 as _, 0) 751 | } 752 | 753 | option!(DefaultImageFormatUnknown, default_image_format_unknown(enable: bool)); 754 | option!(DisableDynamicDispatch, disable_dynamic_dispatch(enable: bool)); 755 | option!(DisableSpecialization, disable_specialization(enable: bool)); 756 | option!(FloatingPointMode, floating_point_mode(mode: FloatingPointMode)); 757 | option!(DebugInformation, debug_information(level: DebugInfoLevel)); 758 | option!(LineDirectiveMode, line_directive_mode(mode: LineDirectiveMode)); 759 | option!(Optimization, optimization(level: OptimizationLevel)); 760 | option!(Obfuscate, obfuscate(enable: bool)); 761 | option!(VulkanUseEntryPointName, vulkan_use_entry_point_name(enable: bool)); 762 | option!(GLSLForceScalarLayout, glsl_force_scalar_layout(enable: bool)); 763 | option!(EmitSpirvDirectly, emit_spirv_directly(enable: bool)); 764 | 765 | // Debugging 766 | option!(NoCodeGen, no_code_gen(enable: bool)); 767 | 768 | // Experimental 769 | option!(NoMangle, no_mangle(enable: bool)); 770 | option!(ValidateUniformity, validate_uniformity(enable: bool)); 771 | } 772 | -------------------------------------------------------------------------------- /src/reflection/decl.rs: -------------------------------------------------------------------------------- 1 | use super::{Function, Generic, Type, Variable, rcall}; 2 | use slang_sys as sys; 3 | 4 | #[repr(transparent)] 5 | pub struct Decl(sys::SlangReflectionDecl); 6 | 7 | impl Decl { 8 | pub fn name(&self) -> &str { 9 | let name = rcall!(spReflectionDecl_getName(self)); 10 | unsafe { std::ffi::CStr::from_ptr(name).to_str().unwrap() } 11 | } 12 | 13 | pub fn kind(&self) -> sys::SlangDeclKind { 14 | rcall!(spReflectionDecl_getKind(self)) 15 | } 16 | 17 | pub fn child_count(&self) -> u32 { 18 | rcall!(spReflectionDecl_getChildrenCount(self)) 19 | } 20 | 21 | pub fn child_by_index(&self, index: u32) -> Option<&Decl> { 22 | rcall!(spReflectionDecl_getChild(self, index) as Option<&Decl>) 23 | } 24 | 25 | pub fn children(&self) -> impl ExactSizeIterator { 26 | (0..self.child_count()).map(move |i| rcall!(spReflectionDecl_getChild(self, i) as &Decl)) 27 | } 28 | 29 | pub fn ty(&self) -> &Type { 30 | rcall!(spReflection_getTypeFromDecl(self) as &Type) 31 | } 32 | 33 | pub fn as_variable(&self) -> &Variable { 34 | rcall!(spReflectionDecl_castToVariable(self) as &Variable) 35 | } 36 | 37 | pub fn as_function(&self) -> &Function { 38 | rcall!(spReflectionDecl_castToFunction(self) as &Function) 39 | } 40 | 41 | pub fn as_generic(&self) -> &Generic { 42 | rcall!(spReflectionDecl_castToGeneric(self) as &Generic) 43 | } 44 | 45 | pub fn parent(&self) -> &Decl { 46 | rcall!(spReflectionDecl_getParent(self) as &Decl) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/reflection/entry_point.rs: -------------------------------------------------------------------------------- 1 | use super::{Function, TypeLayout, VariableLayout, rcall}; 2 | use slang_sys as sys; 3 | 4 | #[repr(transparent)] 5 | pub struct EntryPoint(sys::SlangReflectionEntryPoint); 6 | 7 | impl EntryPoint { 8 | pub fn name(&self) -> &str { 9 | let name = rcall!(spReflectionEntryPoint_getName(self)); 10 | unsafe { std::ffi::CStr::from_ptr(name).to_str().unwrap() } 11 | } 12 | 13 | pub fn name_override(&self) -> Option<&str> { 14 | let name = rcall!(spReflectionEntryPoint_getNameOverride(self)); 15 | (!name.is_null()).then(|| unsafe { std::ffi::CStr::from_ptr(name).to_str().unwrap() }) 16 | } 17 | 18 | pub fn parameter_count(&self) -> u32 { 19 | rcall!(spReflectionEntryPoint_getParameterCount(self)) 20 | } 21 | 22 | pub fn parameter_by_index(&self, index: u32) -> Option<&VariableLayout> { 23 | rcall!(spReflectionEntryPoint_getParameterByIndex(self, index) as Option<&VariableLayout>) 24 | } 25 | 26 | pub fn parameters(&self) -> impl ExactSizeIterator { 27 | (0..self.parameter_count()).map(move |i| { 28 | rcall!(spReflectionEntryPoint_getParameterByIndex(self, i) as &VariableLayout) 29 | }) 30 | } 31 | 32 | pub fn function(&self) -> &Function { 33 | rcall!(spReflectionEntryPoint_getFunction(self) as &Function) 34 | } 35 | 36 | pub fn stage(&self) -> sys::SlangStage { 37 | rcall!(spReflectionEntryPoint_getStage(self)) 38 | } 39 | 40 | pub fn compute_thread_group_size(&self) -> [u64; 3] { 41 | let mut out_size = [0; 3]; 42 | rcall!(spReflectionEntryPoint_getComputeThreadGroupSize( 43 | self, 44 | 3, 45 | &mut out_size as *mut u64 46 | )); 47 | out_size 48 | } 49 | 50 | // TODO: compute_wave_size 51 | 52 | pub fn uses_any_sample_rate_input(&self) -> bool { 53 | rcall!(spReflectionEntryPoint_usesAnySampleRateInput(self)) != 0 54 | } 55 | 56 | pub fn var_layout(&self) -> &VariableLayout { 57 | rcall!(spReflectionEntryPoint_getVarLayout(self) as &VariableLayout) 58 | } 59 | 60 | pub fn type_layout(&self) -> &TypeLayout { 61 | self.var_layout().type_layout() 62 | } 63 | 64 | pub fn result_var_layout(&self) -> &VariableLayout { 65 | rcall!(spReflectionEntryPoint_getResultVarLayout(self) as &VariableLayout) 66 | } 67 | 68 | pub fn has_default_constant_buffer(&self) -> bool { 69 | rcall!(spReflectionEntryPoint_hasDefaultConstantBuffer(self)) != 0 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/reflection/function.rs: -------------------------------------------------------------------------------- 1 | use super::{Type, UserAttribute, Variable, rcall}; 2 | use slang_sys as sys; 3 | 4 | #[repr(transparent)] 5 | pub struct Function(sys::SlangReflectionFunction); 6 | 7 | impl Function { 8 | pub fn name(&self) -> &str { 9 | let name = rcall!(spReflectionFunction_GetName(self)); 10 | unsafe { std::ffi::CStr::from_ptr(name).to_str().unwrap() } 11 | } 12 | 13 | pub fn return_type(&self) -> &Type { 14 | rcall!(spReflectionFunction_GetResultType(self) as &Type) 15 | } 16 | 17 | pub fn parameter_count(&self) -> u32 { 18 | rcall!(spReflectionFunction_GetParameterCount(self)) 19 | } 20 | 21 | pub fn parameter_by_index(&self, index: u32) -> Option<&Variable> { 22 | rcall!(spReflectionFunction_GetParameter(self, index) as Option<&Variable>) 23 | } 24 | 25 | pub fn parameters(&self) -> impl ExactSizeIterator { 26 | (0..self.parameter_count()) 27 | .map(move |i| rcall!(spReflectionFunction_GetParameter(self, i) as &Variable)) 28 | } 29 | 30 | pub fn user_attribute_count(&self) -> u32 { 31 | rcall!(spReflectionFunction_GetUserAttributeCount(self)) 32 | } 33 | 34 | pub fn user_attribute_by_index(&self, index: u32) -> Option<&UserAttribute> { 35 | rcall!(spReflectionFunction_GetUserAttribute(self, index) as Option<&UserAttribute>) 36 | } 37 | 38 | pub fn user_attributes(&self) -> impl ExactSizeIterator { 39 | (0..self.user_attribute_count()) 40 | .map(move |i| rcall!(spReflectionFunction_GetUserAttribute(self, i) as &UserAttribute)) 41 | } 42 | 43 | // TODO: find_user_attribute_by_name 44 | // TODO: find_modifier 45 | // TODO: generic_container 46 | // TODO: apply_specializations 47 | // TODO: specialize_with_arg_types 48 | 49 | pub fn is_overloaded(&self) -> bool { 50 | rcall!(spReflectionFunction_isOverloaded(self)) 51 | } 52 | 53 | pub fn overload_count(&self) -> u32 { 54 | rcall!(spReflectionFunction_getOverloadCount(self)) 55 | } 56 | 57 | pub fn overload_by_index(&self, index: u32) -> Option<&Function> { 58 | rcall!(spReflectionFunction_getOverload(self, index) as Option<&Function>) 59 | } 60 | 61 | pub fn overloads(&self) -> impl ExactSizeIterator { 62 | (0..self.overload_count()) 63 | .map(move |i| rcall!(spReflectionFunction_getOverload(self, i) as &Function)) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/reflection/generic.rs: -------------------------------------------------------------------------------- 1 | use super::{Decl, Type, TypeParameter, Variable, rcall}; 2 | use slang_sys as sys; 3 | 4 | #[repr(transparent)] 5 | pub struct Generic(sys::SlangReflectionGeneric); 6 | 7 | impl Generic { 8 | pub fn as_decl(&self) -> &Decl { 9 | rcall!(spReflectionGeneric_asDecl(self) as &Decl) 10 | } 11 | 12 | pub fn name(&self) -> &str { 13 | let name = rcall!(spReflectionGeneric_GetName(self)); 14 | unsafe { std::ffi::CStr::from_ptr(name).to_str().unwrap() } 15 | } 16 | 17 | pub fn type_parameter_count(&self) -> u32 { 18 | rcall!(spReflectionGeneric_GetTypeParameterCount(self)) 19 | } 20 | 21 | pub fn type_parameter_by_index(&self, index: u32) -> Option<&TypeParameter> { 22 | rcall!(spReflectionGeneric_GetTypeParameter(self, index) as Option<&TypeParameter>) 23 | } 24 | 25 | pub fn type_parameters(&self) -> impl ExactSizeIterator { 26 | (0..self.type_parameter_count()) 27 | .map(move |i| rcall!(spReflectionGeneric_GetTypeParameter(self, i) as &TypeParameter)) 28 | } 29 | 30 | pub fn value_parameter_count(&self) -> u32 { 31 | rcall!(spReflectionGeneric_GetValueParameterCount(self)) 32 | } 33 | 34 | pub fn value_parameter_by_index(&self, index: u32) -> Option<&Variable> { 35 | rcall!(spReflectionGeneric_GetValueParameter(self, index) as Option<&Variable>) 36 | } 37 | 38 | pub fn value_parameters(&self) -> impl ExactSizeIterator { 39 | (0..self.value_parameter_count()) 40 | .map(move |i| rcall!(spReflectionGeneric_GetValueParameter(self, i) as &Variable)) 41 | } 42 | 43 | pub fn type_parameter_constraint_count(&self, type_param: &Variable) -> u32 { 44 | rcall!(spReflectionGeneric_GetTypeParameterConstraintCount( 45 | self, 46 | type_param as *const _ as *mut _ 47 | )) 48 | } 49 | 50 | pub fn type_parameter_constraint_by_index( 51 | &self, 52 | type_param: &Variable, 53 | index: u32, 54 | ) -> Option<&Type> { 55 | rcall!(spReflectionGeneric_GetTypeParameterConstraintType( 56 | self, 57 | type_param as *const _ as *mut _, 58 | index 59 | ) as Option<&Type>) 60 | } 61 | 62 | pub fn inner_decl(&self) -> &Decl { 63 | rcall!(spReflectionGeneric_GetInnerDecl(self) as &Decl) 64 | } 65 | 66 | pub fn inner_kind(&self) -> sys::SlangDeclKind { 67 | rcall!(spReflectionGeneric_GetInnerKind(self)) 68 | } 69 | 70 | pub fn outer_generic_container(&self) -> &Generic { 71 | rcall!(spReflectionGeneric_GetOuterGenericContainer(self) as &Generic) 72 | } 73 | 74 | pub fn concrete_type(&self, type_param: &Variable) -> &Type { 75 | rcall!(spReflectionGeneric_GetConcreteType(self, type_param as *const _ as *mut _) as &Type) 76 | } 77 | 78 | pub fn concrete_int_val(&self, value_param: &Variable) -> i64 { 79 | rcall!(spReflectionGeneric_GetConcreteIntVal( 80 | self, 81 | value_param as *const _ as *mut _ 82 | )) 83 | } 84 | 85 | pub fn apply_specializations(&self, generic: &Generic) -> &Generic { 86 | rcall!( 87 | spReflectionGeneric_applySpecializations(self, generic as *const _ as *mut _) 88 | as &Generic 89 | ) 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/reflection/mod.rs: -------------------------------------------------------------------------------- 1 | mod decl; 2 | mod entry_point; 3 | mod function; 4 | mod generic; 5 | mod shader; 6 | mod ty; 7 | mod type_parameter; 8 | mod user_attribute; 9 | mod variable; 10 | 11 | pub use decl::Decl; 12 | pub use entry_point::EntryPoint; 13 | pub use function::Function; 14 | pub use generic::Generic; 15 | pub use shader::{Shader, compute_string_hash}; 16 | pub use ty::{Type, TypeLayout}; 17 | pub use type_parameter::TypeParameter; 18 | pub use user_attribute::UserAttribute; 19 | pub use variable::{Variable, VariableLayout}; 20 | 21 | macro_rules! rcall { 22 | ($f:ident($s:ident $(,$arg:expr)*)) => { 23 | unsafe { sys::$f($s as *const _ as *mut _ $(,$arg)*) } 24 | }; 25 | 26 | ($f:ident($s:ident $(,$arg:expr)*) as Option<&$cast:ty>) => { 27 | unsafe { 28 | let ptr = sys::$f($s as *const _ as *mut _ $(,$arg)*); 29 | (!ptr.is_null()).then(|| &*(ptr as *const $cast)) 30 | } 31 | }; 32 | 33 | ($f:ident($s:ident $(,$arg:expr)*) as &$cast:ty) => { 34 | unsafe { &*(sys::$f($s as *const _ as *mut _ $(,$arg)*) as *const $cast) } 35 | }; 36 | } 37 | 38 | pub(super) use rcall; 39 | -------------------------------------------------------------------------------- /src/reflection/shader.rs: -------------------------------------------------------------------------------- 1 | use super::{ 2 | EntryPoint, Function, Type, TypeLayout, TypeParameter, Variable, VariableLayout, rcall, 3 | }; 4 | use slang_sys as sys; 5 | 6 | #[repr(transparent)] 7 | pub struct Shader(sys::SlangReflection); 8 | 9 | impl Shader { 10 | pub fn parameter_count(&self) -> u32 { 11 | rcall!(spReflection_GetParameterCount(self)) 12 | } 13 | 14 | pub fn parameter_by_index(&self, index: u32) -> Option<&VariableLayout> { 15 | rcall!(spReflection_GetParameterByIndex(self, index) as Option<&VariableLayout>) 16 | } 17 | 18 | pub fn parameters(&self) -> impl ExactSizeIterator { 19 | (0..self.parameter_count()) 20 | .map(move |i| rcall!(spReflection_GetParameterByIndex(self, i) as &VariableLayout)) 21 | } 22 | 23 | pub fn type_parameter_count(&self) -> u32 { 24 | rcall!(spReflection_GetTypeParameterCount(self)) 25 | } 26 | 27 | pub fn type_parameter_by_index(&self, index: u32) -> Option<&TypeParameter> { 28 | rcall!(spReflection_GetTypeParameterByIndex(self, index) as Option<&TypeParameter>) 29 | } 30 | 31 | pub fn type_parameters(&self) -> impl ExactSizeIterator { 32 | (0..self.type_parameter_count()) 33 | .map(move |i| rcall!(spReflection_GetTypeParameterByIndex(self, i) as &TypeParameter)) 34 | } 35 | 36 | pub fn find_type_parameter_by_name(&self, name: &str) -> Option<&TypeParameter> { 37 | let name = std::ffi::CString::new(name).unwrap(); 38 | rcall!(spReflection_FindTypeParameter(self, name.as_ptr()) as Option<&TypeParameter>) 39 | } 40 | 41 | pub fn entry_point_count(&self) -> u32 { 42 | rcall!(spReflection_getEntryPointCount(self)) as _ 43 | } 44 | 45 | pub fn entry_point_by_index(&self, index: u32) -> Option<&EntryPoint> { 46 | rcall!(spReflection_getEntryPointByIndex(self, index as _) as Option<&EntryPoint>) 47 | } 48 | 49 | pub fn entry_points(&self) -> impl ExactSizeIterator { 50 | (0..self.entry_point_count()) 51 | .map(move |i| rcall!(spReflection_getEntryPointByIndex(self, i as _) as &EntryPoint)) 52 | } 53 | 54 | pub fn find_entry_point_by_name(&self, name: &str) -> Option<&EntryPoint> { 55 | let name = std::ffi::CString::new(name).unwrap(); 56 | rcall!(spReflection_findEntryPointByName(self, name.as_ptr()) as Option<&EntryPoint>) 57 | } 58 | 59 | pub fn global_constant_buffer_binding(&self) -> u64 { 60 | rcall!(spReflection_getGlobalConstantBufferBinding(self)) 61 | } 62 | 63 | pub fn global_constant_buffer_size(&self) -> usize { 64 | rcall!(spReflection_getGlobalConstantBufferSize(self)) 65 | } 66 | 67 | pub fn find_type_by_name(&self, name: &str) -> Option<&Type> { 68 | let name = std::ffi::CString::new(name).unwrap(); 69 | rcall!(spReflection_FindTypeByName(self, name.as_ptr()) as Option<&Type>) 70 | } 71 | 72 | pub fn find_function_by_name(&self, name: &str) -> Option<&Function> { 73 | let name = std::ffi::CString::new(name).unwrap(); 74 | rcall!(spReflection_FindFunctionByName(self, name.as_ptr()) as Option<&Function>) 75 | } 76 | 77 | pub fn find_function_by_name_in_type(&self, ty: &Type, name: &str) -> Option<&Function> { 78 | let name = std::ffi::CString::new(name).unwrap(); 79 | rcall!( 80 | spReflection_FindFunctionByNameInType(self, ty as *const _ as *mut _, name.as_ptr()) 81 | as Option<&Function> 82 | ) 83 | } 84 | 85 | pub fn find_var_by_name_in_type(&self, ty: &Type, name: &str) -> Option<&Variable> { 86 | let name = std::ffi::CString::new(name).unwrap(); 87 | rcall!( 88 | spReflection_FindVarByNameInType(self, ty as *const _ as *mut _, name.as_ptr()) 89 | as Option<&Variable> 90 | ) 91 | } 92 | 93 | pub fn type_layout(&self, ty: &Type, rules: sys::SlangLayoutRules) -> Option<&TypeLayout> { 94 | rcall!( 95 | spReflection_GetTypeLayout(self, ty as *const _ as *mut _, rules) 96 | as Option<&TypeLayout> 97 | ) 98 | } 99 | 100 | // TODO: specialize_type 101 | // TODO: specialize_generic 102 | // TODO: is_sub_type 103 | 104 | pub fn hashed_string_count(&self) -> u64 { 105 | rcall!(spReflection_getHashedStringCount(self)) 106 | } 107 | 108 | pub fn hashed_string(&self, index: u64) -> Option<&str> { 109 | let mut len = 0; 110 | let result = rcall!(spReflection_getHashedString(self, index, &mut len)); 111 | 112 | (!result.is_null()).then(|| { 113 | let slice = unsafe { std::slice::from_raw_parts(result as *const u8, len as usize) }; 114 | std::str::from_utf8(slice).unwrap() 115 | }) 116 | } 117 | 118 | pub fn global_params_type_layout(&self) -> &TypeLayout { 119 | rcall!(spReflection_getGlobalParamsTypeLayout(self) as &TypeLayout) 120 | } 121 | 122 | pub fn global_params_var_layout(&self) -> &VariableLayout { 123 | rcall!(spReflection_getGlobalParamsVarLayout(self) as &VariableLayout) 124 | } 125 | } 126 | 127 | pub fn compute_string_hash(string: &str) -> u32 { 128 | rcall!(spComputeStringHash(string, string.len())) 129 | } 130 | -------------------------------------------------------------------------------- /src/reflection/ty.rs: -------------------------------------------------------------------------------- 1 | use super::{UserAttribute, Variable, VariableLayout, rcall}; 2 | use slang_sys as sys; 3 | 4 | #[repr(transparent)] 5 | pub struct Type(sys::SlangReflectionType); 6 | 7 | impl Type { 8 | pub fn kind(&self) -> sys::SlangTypeKind { 9 | rcall!(spReflectionType_GetKind(self)) 10 | } 11 | 12 | pub fn field_count(&self) -> u32 { 13 | rcall!(spReflectionType_GetFieldCount(self)) 14 | } 15 | 16 | pub fn field_by_index(&self, index: u32) -> Option<&Variable> { 17 | rcall!(spReflectionType_GetFieldByIndex(self, index) as Option<&Variable>) 18 | } 19 | 20 | pub fn fields(&self) -> impl ExactSizeIterator { 21 | (0..self.field_count()) 22 | .map(move |i| rcall!(spReflectionType_GetFieldByIndex(self, i) as &Variable)) 23 | } 24 | 25 | // TODO: is_array 26 | 27 | // TODO: unwrap_array 28 | 29 | pub fn element_count(&self) -> usize { 30 | rcall!(spReflectionType_GetElementCount(self)) 31 | } 32 | 33 | // TODO: total_array_element_count 34 | 35 | pub fn element_type(&self) -> &Type { 36 | rcall!(spReflectionType_GetElementType(self) as &Type) 37 | } 38 | 39 | pub fn row_count(&self) -> u32 { 40 | rcall!(spReflectionType_GetRowCount(self)) 41 | } 42 | 43 | pub fn column_count(&self) -> u32 { 44 | rcall!(spReflectionType_GetColumnCount(self)) 45 | } 46 | 47 | pub fn scalar_type(&self) -> sys::SlangScalarType { 48 | rcall!(spReflectionType_GetScalarType(self)) 49 | } 50 | 51 | pub fn resource_result_type(&self) -> &Type { 52 | rcall!(spReflectionType_GetResourceResultType(self) as &Type) 53 | } 54 | 55 | pub fn resource_shape(&self) -> sys::SlangResourceShape { 56 | rcall!(spReflectionType_GetResourceShape(self)) 57 | } 58 | 59 | pub fn resource_access(&self) -> sys::SlangResourceAccess { 60 | rcall!(spReflectionType_GetResourceAccess(self)) 61 | } 62 | 63 | pub fn name(&self) -> &str { 64 | let name = rcall!(spReflectionType_GetName(self)); 65 | unsafe { std::ffi::CStr::from_ptr(name).to_str().unwrap() } 66 | } 67 | 68 | // TODO: full_name 69 | 70 | pub fn user_attribute_count(&self) -> u32 { 71 | rcall!(spReflectionType_GetUserAttributeCount(self)) 72 | } 73 | 74 | pub fn user_attribute_by_index(&self, index: u32) -> Option<&UserAttribute> { 75 | rcall!(spReflectionType_GetUserAttribute(self, index) as Option<&UserAttribute>) 76 | } 77 | 78 | pub fn user_attributes(&self) -> impl ExactSizeIterator { 79 | (0..self.user_attribute_count()) 80 | .map(move |i| rcall!(spReflectionType_GetUserAttribute(self, i) as &UserAttribute)) 81 | } 82 | 83 | pub fn find_user_attribute_by_name(&self, name: &str) -> Option<&UserAttribute> { 84 | let name = std::ffi::CString::new(name).unwrap(); 85 | rcall!( 86 | spReflectionType_FindUserAttributeByName(self, name.as_ptr()) as Option<&UserAttribute> 87 | ) 88 | } 89 | } 90 | 91 | #[repr(transparent)] 92 | pub struct TypeLayout(sys::SlangReflectionTypeLayout); 93 | 94 | impl TypeLayout { 95 | pub fn ty(&self) -> Option<&Type> { 96 | rcall!(spReflectionTypeLayout_GetType(self) as Option<&Type>) 97 | } 98 | 99 | pub fn kind(&self) -> sys::SlangTypeKind { 100 | rcall!(spReflectionTypeLayout_getKind(self)) 101 | } 102 | 103 | pub fn size(&self, category: sys::SlangParameterCategory) -> usize { 104 | rcall!(spReflectionTypeLayout_GetSize(self, category)) 105 | } 106 | 107 | pub fn stride(&self, category: sys::SlangParameterCategory) -> usize { 108 | rcall!(spReflectionTypeLayout_GetStride(self, category)) 109 | } 110 | 111 | pub fn alignment(&self, category: sys::SlangParameterCategory) -> i32 { 112 | rcall!(spReflectionTypeLayout_getAlignment(self, category)) 113 | } 114 | 115 | pub fn field_count(&self) -> u32 { 116 | rcall!(spReflectionTypeLayout_GetFieldCount(self)) 117 | } 118 | 119 | pub fn field_by_index(&self, index: u32) -> Option<&VariableLayout> { 120 | rcall!(spReflectionTypeLayout_GetFieldByIndex(self, index) as Option<&VariableLayout>) 121 | } 122 | 123 | pub fn fields(&self) -> impl ExactSizeIterator { 124 | (0..self.field_count()).map(move |i| { 125 | rcall!(spReflectionTypeLayout_GetFieldByIndex(self, i) as &VariableLayout) 126 | }) 127 | } 128 | 129 | // TODO: find_field_index_by_name 130 | // TODO: explicit_counter 131 | // TODO: is_array 132 | // TODO: unwrap_array 133 | 134 | pub fn element_count(&self) -> Option { 135 | Some(self.ty()?.element_count()) 136 | } 137 | 138 | // TODO: total_array_element_count 139 | 140 | pub fn element_stride(&self, category: sys::SlangParameterCategory) -> usize { 141 | rcall!(spReflectionTypeLayout_GetElementStride(self, category)) 142 | } 143 | 144 | pub fn element_type_layout(&self) -> &TypeLayout { 145 | rcall!(spReflectionTypeLayout_GetElementTypeLayout(self) as &TypeLayout) 146 | } 147 | 148 | pub fn element_var_layout(&self) -> &VariableLayout { 149 | rcall!(spReflectionTypeLayout_GetElementVarLayout(self) as &VariableLayout) 150 | } 151 | 152 | pub fn container_var_layout(&self) -> &VariableLayout { 153 | rcall!(spReflectionTypeLayout_getContainerVarLayout(self) as &VariableLayout) 154 | } 155 | 156 | pub fn parameter_category(&self) -> sys::SlangParameterCategory { 157 | rcall!(spReflectionTypeLayout_GetParameterCategory(self)) 158 | } 159 | 160 | pub fn category_count(&self) -> u32 { 161 | rcall!(spReflectionTypeLayout_GetCategoryCount(self)) 162 | } 163 | 164 | pub fn category_by_index(&self, index: u32) -> sys::SlangParameterCategory { 165 | rcall!(spReflectionTypeLayout_GetCategoryByIndex(self, index)) 166 | } 167 | 168 | pub fn categories(&self) -> impl ExactSizeIterator + '_ { 169 | (0..self.category_count()) 170 | .map(move |i| rcall!(spReflectionTypeLayout_GetCategoryByIndex(self, i))) 171 | } 172 | 173 | pub fn row_count(&self) -> Option { 174 | Some(self.ty()?.row_count()) 175 | } 176 | 177 | pub fn column_count(&self) -> Option { 178 | Some(self.ty()?.column_count()) 179 | } 180 | 181 | pub fn scalar_type(&self) -> Option { 182 | Some(self.ty()?.scalar_type()) 183 | } 184 | 185 | pub fn resource_result_type(&self) -> Option<&Type> { 186 | Some(self.ty()?.resource_result_type()) 187 | } 188 | 189 | pub fn resource_shape(&self) -> Option { 190 | Some(self.ty()?.resource_shape()) 191 | } 192 | 193 | pub fn resource_access(&self) -> Option { 194 | Some(self.ty()?.resource_access()) 195 | } 196 | 197 | pub fn name(&self) -> Option<&str> { 198 | Some(self.ty()?.name()) 199 | } 200 | 201 | pub fn matrix_layout_mode(&self) -> sys::SlangMatrixLayoutMode { 202 | rcall!(spReflectionTypeLayout_GetMatrixLayoutMode(self)) 203 | } 204 | 205 | pub fn generic_param_index(&self) -> i32 { 206 | rcall!(spReflectionTypeLayout_getGenericParamIndex(self)) 207 | } 208 | 209 | pub fn pending_data_type_layout(&self) -> &TypeLayout { 210 | rcall!(spReflectionTypeLayout_getPendingDataTypeLayout(self) as &TypeLayout) 211 | } 212 | 213 | pub fn specialized_type_pending_data_var_layout(&self) -> &VariableLayout { 214 | rcall!( 215 | spReflectionTypeLayout_getSpecializedTypePendingDataVarLayout(self) as &VariableLayout 216 | ) 217 | } 218 | 219 | pub fn binding_range_count(&self) -> i64 { 220 | rcall!(spReflectionTypeLayout_getBindingRangeCount(self)) 221 | } 222 | 223 | pub fn binding_range_type(&self, index: i64) -> sys::SlangBindingType { 224 | rcall!(spReflectionTypeLayout_getBindingRangeType(self, index)) 225 | } 226 | 227 | pub fn is_binding_range_specializable(&self, index: i64) -> bool { 228 | rcall!(spReflectionTypeLayout_isBindingRangeSpecializable( 229 | self, index 230 | )) != 0 231 | } 232 | 233 | pub fn binding_range_binding_count(&self, index: i64) -> i64 { 234 | rcall!(spReflectionTypeLayout_getBindingRangeBindingCount( 235 | self, index 236 | )) 237 | } 238 | 239 | pub fn field_binding_range_offset(&self, field_index: i64) -> i64 { 240 | rcall!(spReflectionTypeLayout_getFieldBindingRangeOffset( 241 | self, 242 | field_index 243 | )) 244 | } 245 | 246 | pub fn explicit_counter_binding_range_offset(&self) -> i64 { 247 | rcall!(spReflectionTypeLayout_getExplicitCounterBindingRangeOffset( 248 | self 249 | )) 250 | } 251 | 252 | pub fn binding_range_leaf_type_layout(&self, index: i64) -> &TypeLayout { 253 | rcall!(spReflectionTypeLayout_getBindingRangeLeafTypeLayout(self, index) as &TypeLayout) 254 | } 255 | 256 | pub fn binding_range_leaf_variable(&self, index: i64) -> &Variable { 257 | rcall!(spReflectionTypeLayout_getBindingRangeLeafVariable(self, index) as &Variable) 258 | } 259 | 260 | pub fn binding_range_image_format(&self, index: i64) -> sys::SlangImageFormat { 261 | rcall!(spReflectionTypeLayout_getBindingRangeImageFormat( 262 | self, index 263 | )) 264 | } 265 | 266 | pub fn binding_range_descriptor_set_index(&self, index: i64) -> i64 { 267 | rcall!(spReflectionTypeLayout_getBindingRangeDescriptorSetIndex( 268 | self, index 269 | )) 270 | } 271 | 272 | pub fn binding_range_first_descriptor_range_index(&self, index: i64) -> i64 { 273 | rcall!(spReflectionTypeLayout_getBindingRangeFirstDescriptorRangeIndex(self, index)) 274 | } 275 | 276 | pub fn binding_range_descriptor_range_count(&self, index: i64) -> i64 { 277 | rcall!(spReflectionTypeLayout_getBindingRangeDescriptorRangeCount( 278 | self, index 279 | )) 280 | } 281 | 282 | pub fn descriptor_set_count(&self) -> i64 { 283 | rcall!(spReflectionTypeLayout_getDescriptorSetCount(self)) 284 | } 285 | 286 | pub fn descriptor_set_space_offset(&self, set_index: i64) -> i64 { 287 | rcall!(spReflectionTypeLayout_getDescriptorSetSpaceOffset( 288 | self, set_index 289 | )) 290 | } 291 | 292 | pub fn descriptor_set_descriptor_range_count(&self, set_index: i64) -> i64 { 293 | rcall!(spReflectionTypeLayout_getDescriptorSetDescriptorRangeCount( 294 | self, set_index 295 | )) 296 | } 297 | 298 | pub fn descriptor_set_descriptor_range_index_offset( 299 | &self, 300 | set_index: i64, 301 | range_index: i64, 302 | ) -> i64 { 303 | rcall!( 304 | spReflectionTypeLayout_getDescriptorSetDescriptorRangeIndexOffset( 305 | self, 306 | set_index, 307 | range_index 308 | ) 309 | ) 310 | } 311 | 312 | pub fn descriptor_set_descriptor_range_descriptor_count( 313 | &self, 314 | set_index: i64, 315 | range_index: i64, 316 | ) -> i64 { 317 | rcall!( 318 | spReflectionTypeLayout_getDescriptorSetDescriptorRangeDescriptorCount( 319 | self, 320 | set_index, 321 | range_index 322 | ) 323 | ) 324 | } 325 | 326 | pub fn descriptor_set_descriptor_range_type( 327 | &self, 328 | set_index: i64, 329 | range_index: i64, 330 | ) -> sys::SlangBindingType { 331 | rcall!(spReflectionTypeLayout_getDescriptorSetDescriptorRangeType( 332 | self, 333 | set_index, 334 | range_index 335 | )) 336 | } 337 | 338 | pub fn descriptor_set_descriptor_range_category( 339 | &self, 340 | set_index: i64, 341 | range_index: i64, 342 | ) -> sys::SlangParameterCategory { 343 | rcall!( 344 | spReflectionTypeLayout_getDescriptorSetDescriptorRangeCategory( 345 | self, 346 | set_index, 347 | range_index 348 | ) 349 | ) 350 | } 351 | 352 | pub fn sub_object_range_count(&self) -> i64 { 353 | rcall!(spReflectionTypeLayout_getSubObjectRangeCount(self)) 354 | } 355 | 356 | pub fn sub_object_range_binding_range_index(&self, sub_object_range_index: i64) -> i64 { 357 | rcall!(spReflectionTypeLayout_getSubObjectRangeBindingRangeIndex( 358 | self, 359 | sub_object_range_index 360 | )) 361 | } 362 | 363 | pub fn sub_object_range_space_offset(&self, sub_object_range_index: i64) -> i64 { 364 | rcall!(spReflectionTypeLayout_getSubObjectRangeSpaceOffset( 365 | self, 366 | sub_object_range_index 367 | )) 368 | } 369 | 370 | pub fn sub_object_range_offset(&self, sub_object_range_index: i64) -> &VariableLayout { 371 | rcall!( 372 | spReflectionTypeLayout_getSubObjectRangeOffset(self, sub_object_range_index) 373 | as &VariableLayout 374 | ) 375 | } 376 | } 377 | -------------------------------------------------------------------------------- /src/reflection/type_parameter.rs: -------------------------------------------------------------------------------- 1 | use super::{Type, rcall}; 2 | use slang_sys as sys; 3 | 4 | #[repr(transparent)] 5 | pub struct TypeParameter(sys::SlangReflectionTypeParameter); 6 | 7 | impl TypeParameter { 8 | pub fn name(&self) -> &str { 9 | let name = rcall!(spReflectionTypeParameter_GetName(self)); 10 | unsafe { std::ffi::CStr::from_ptr(name).to_str().unwrap() } 11 | } 12 | 13 | pub fn index(&self) -> u32 { 14 | rcall!(spReflectionTypeParameter_GetIndex(self)) 15 | } 16 | 17 | pub fn constraint_count(&self) -> u32 { 18 | rcall!(spReflectionTypeParameter_GetConstraintCount(self)) 19 | } 20 | 21 | pub fn constraint_by_index(&self, index: u32) -> &Type { 22 | rcall!(spReflectionTypeParameter_GetConstraintByIndex(self, index) as &Type) 23 | } 24 | 25 | pub fn constraints(&self) -> impl ExactSizeIterator { 26 | (0..self.constraint_count()) 27 | .map(move |i| rcall!(spReflectionTypeParameter_GetConstraintByIndex(self, i) as &Type)) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/reflection/user_attribute.rs: -------------------------------------------------------------------------------- 1 | use super::{Type, rcall}; 2 | use slang_sys as sys; 3 | 4 | #[repr(transparent)] 5 | pub struct UserAttribute(sys::SlangReflectionUserAttribute); 6 | 7 | impl UserAttribute { 8 | pub fn name(&self) -> &str { 9 | let name = rcall!(spReflectionUserAttribute_GetName(self)); 10 | unsafe { std::ffi::CStr::from_ptr(name).to_str().unwrap() } 11 | } 12 | 13 | pub fn argument_count(&self) -> u32 { 14 | rcall!(spReflectionUserAttribute_GetArgumentCount(self)) 15 | } 16 | 17 | pub fn argument_type(&self, index: u32) -> &Type { 18 | rcall!(spReflectionUserAttribute_GetArgumentType(self, index) as &Type) 19 | } 20 | 21 | pub fn argument_value_int(&self, index: u32) -> Option { 22 | let mut out = 0; 23 | let result = rcall!(spReflectionUserAttribute_GetArgumentValueInt( 24 | self, index, &mut out 25 | )); 26 | 27 | crate::succeeded(result).then(|| out) 28 | } 29 | 30 | pub fn argument_value_float(&self, index: u32) -> Option { 31 | let mut out = 0.0; 32 | let result = rcall!(spReflectionUserAttribute_GetArgumentValueFloat( 33 | self, index, &mut out 34 | )); 35 | 36 | crate::succeeded(result).then(|| out) 37 | } 38 | 39 | pub fn argument_value_string(&self, index: u32) -> Option<&str> { 40 | let mut len = 0; 41 | let result = rcall!(spReflectionUserAttribute_GetArgumentValueString( 42 | self, index, &mut len 43 | )); 44 | 45 | (!result.is_null()).then(|| { 46 | let slice = unsafe { std::slice::from_raw_parts(result as *const u8, len as usize) }; 47 | std::str::from_utf8(slice).unwrap() 48 | }) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/reflection/variable.rs: -------------------------------------------------------------------------------- 1 | use super::{Type, TypeLayout, UserAttribute, rcall}; 2 | use slang_sys as sys; 3 | 4 | #[repr(transparent)] 5 | pub struct Variable(sys::SlangReflectionVariable); 6 | 7 | impl Variable { 8 | pub fn name(&self) -> &str { 9 | let name = rcall!(spReflectionVariable_GetName(self)); 10 | unsafe { std::ffi::CStr::from_ptr(name).to_str().unwrap() } 11 | } 12 | 13 | pub fn ty(&self) -> &Type { 14 | rcall!(spReflectionVariable_GetType(self) as &Type) 15 | } 16 | 17 | // TODO: find_modifier 18 | 19 | pub fn user_attribute_count(&self) -> u32 { 20 | rcall!(spReflectionVariable_GetUserAttributeCount(self)) 21 | } 22 | 23 | pub fn user_attribute_by_index(&self, index: u32) -> Option<&UserAttribute> { 24 | rcall!(spReflectionVariable_GetUserAttribute(self, index) as Option<&UserAttribute>) 25 | } 26 | 27 | pub fn user_attributes(&self) -> impl ExactSizeIterator { 28 | (0..self.user_attribute_count()) 29 | .map(move |i| rcall!(spReflectionVariable_GetUserAttribute(self, i) as &UserAttribute)) 30 | } 31 | 32 | // TODO: find_user_attribute_by_name 33 | 34 | pub fn has_default_value(&self) -> bool { 35 | rcall!(spReflectionVariable_HasDefaultValue(self)) 36 | } 37 | 38 | // TODO: generic_container 39 | // TODO: apply_specializations 40 | } 41 | 42 | #[repr(transparent)] 43 | pub struct VariableLayout(sys::SlangReflectionVariableLayout); 44 | 45 | impl VariableLayout { 46 | pub fn variable(&self) -> Option<&Variable> { 47 | rcall!(spReflectionVariableLayout_GetVariable(self) as Option<&Variable>) 48 | } 49 | 50 | // TODO: get_name 51 | // TODO: find_modifier 52 | 53 | pub fn type_layout(&self) -> &TypeLayout { 54 | rcall!(spReflectionVariableLayout_GetTypeLayout(self) as &TypeLayout) 55 | } 56 | 57 | pub fn category(&self) -> sys::SlangParameterCategory { 58 | self.type_layout().parameter_category() 59 | } 60 | 61 | pub fn category_count(&self) -> u32 { 62 | self.type_layout().category_count() 63 | } 64 | 65 | pub fn category_by_index(&self, index: u32) -> sys::SlangParameterCategory { 66 | self.type_layout().category_by_index(index) 67 | } 68 | 69 | pub fn offset(&self, category: sys::SlangParameterCategory) -> usize { 70 | rcall!(spReflectionVariableLayout_GetOffset(self, category)) 71 | } 72 | 73 | pub fn ty(&self) -> Option<&Type> { 74 | Some(self.variable()?.ty()) 75 | } 76 | 77 | pub fn binding_index(&self) -> u32 { 78 | rcall!(spReflectionParameter_GetBindingIndex(self)) 79 | } 80 | 81 | pub fn binding_space(&self) -> u32 { 82 | rcall!(spReflectionParameter_GetBindingSpace(self)) 83 | } 84 | 85 | pub fn binding_space_with_category(&self, category: sys::SlangParameterCategory) -> usize { 86 | rcall!(spReflectionVariableLayout_GetSpace(self, category)) 87 | } 88 | 89 | pub fn semantic_name(&self) -> Option<&str> { 90 | let name = rcall!(spReflectionVariableLayout_GetSemanticName(self)); 91 | unsafe { (!name.is_null()).then(|| std::ffi::CStr::from_ptr(name).to_str().unwrap()) } 92 | } 93 | 94 | pub fn semantic_index(&self) -> usize { 95 | rcall!(spReflectionVariableLayout_GetSemanticIndex(self)) 96 | } 97 | 98 | pub fn stage(&self) -> sys::SlangStage { 99 | rcall!(spReflectionVariableLayout_getStage(self)) 100 | } 101 | 102 | pub fn pending_data_layout(&self) -> &VariableLayout { 103 | rcall!(spReflectionVariableLayout_getPendingDataLayout(self) as &VariableLayout) 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/tests.rs: -------------------------------------------------------------------------------- 1 | use crate as slang; 2 | use slang::Downcast; 3 | 4 | #[test] 5 | fn compile() { 6 | let global_session = slang::GlobalSession::new().unwrap(); 7 | 8 | let search_path = std::ffi::CString::new("shaders").unwrap(); 9 | 10 | // All compiler options are available through this builder. 11 | let session_options = slang::CompilerOptions::default() 12 | .optimization(slang::OptimizationLevel::High) 13 | .matrix_layout_row(true); 14 | 15 | let target_desc = slang::TargetDesc::default() 16 | .format(slang::CompileTarget::Dxil) 17 | .profile(global_session.find_profile("sm_6_5")); 18 | 19 | let targets = [target_desc]; 20 | let search_paths = [search_path.as_ptr()]; 21 | 22 | let session_desc = slang::SessionDesc::default() 23 | .targets(&targets) 24 | .search_paths(&search_paths) 25 | .options(&session_options); 26 | 27 | let session = global_session.create_session(&session_desc).unwrap(); 28 | let module = session.load_module("test.slang").unwrap(); 29 | let entry_point = module.find_entry_point_by_name("main").unwrap(); 30 | 31 | let program = session 32 | .create_composite_component_type(&[ 33 | module.downcast().clone(), 34 | entry_point.downcast().clone(), 35 | ]) 36 | .unwrap(); 37 | 38 | let linked_program = program.link().unwrap(); 39 | 40 | // Entry point to the reflection API. 41 | let reflection = linked_program.layout(0).unwrap(); 42 | assert_eq!(reflection.entry_point_count(), 1); 43 | assert_eq!(reflection.parameter_count(), 3); 44 | 45 | let shader_bytecode = linked_program.entry_point_code(0, 0).unwrap(); 46 | assert_ne!(shader_bytecode.as_slice().len(), 0); 47 | } 48 | --------------------------------------------------------------------------------