├── .github ├── dependabot.yml └── workflows │ ├── ci.yaml │ └── publish.yaml ├── .gitignore ├── .travis.yml ├── CODE_OF_CONDUCT.md ├── Cargo.toml ├── LICENSE ├── README.md ├── banner.png ├── examples ├── copy-over-56.hlsl ├── copy-under-56.hlsl ├── copy.hlsl ├── file-ast.rs ├── include.hlsl ├── include.rs ├── intellisense-tu.rs ├── spirv.rs ├── validate-fake-signing.rs └── validate.rs ├── release.toml └── src ├── fake_sign ├── mod.rs └── modified_md5.rs ├── ffi.rs ├── intellisense ├── ffi.rs ├── mod.rs └── wrapper.rs ├── lib.rs ├── os.rs ├── utils.rs └── wrapper.rs /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: cargo 4 | directory: "/" 5 | schedule: 6 | interval: weekly 7 | - package-ecosystem: github-actions 8 | directory: "/" 9 | schedule: 10 | interval: weekly 11 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | on: [push, pull_request] 2 | 3 | name: Continuous integration 4 | 5 | jobs: 6 | check: 7 | name: Check and Lint 8 | strategy: 9 | matrix: 10 | os: [ubuntu-latest, windows-latest] 11 | rust: [stable] 12 | runs-on: ${{ matrix.os }} 13 | steps: 14 | - uses: actions/checkout@v4 15 | - uses: actions/cache@v4 16 | # https://github.com/actions/cache/blob/main/examples.md#rust---cargo 17 | with: 18 | path: | 19 | ~/.cargo/bin/ 20 | ~/.cargo/registry/index/ 21 | ~/.cargo/registry/cache/ 22 | ~/.cargo/git/db/ 23 | target/ 24 | key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} 25 | - name: Cargo check 26 | run: cargo check --workspace --all-targets 27 | - name: Cargo fmt 28 | run: cargo fmt --all -- --check 29 | - name: Cargo clippy 30 | run: cargo clippy --workspace --all-targets -- -D warnings 31 | -------------------------------------------------------------------------------- /.github/workflows/publish.yaml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | 3 | on: 4 | push: 5 | tags: 6 | paths: "/Cargo.toml" 7 | 8 | jobs: 9 | Publish: 10 | if: github.repository_owner == 'Traverse-Research' 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v4 14 | - name: Publish 15 | run: cargo publish --token ${{ secrets.cratesio_token }} 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | dxcompiler.dll 5 | /.vscode -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | sudo: false 3 | cache: cargo 4 | 5 | matrix: 6 | allow_failures: 7 | - rust: nightly 8 | fast_finish: true 9 | 10 | include: 11 | - rust: nightly 12 | os: windows 13 | 14 | - rust: stable 15 | os: windows 16 | 17 | - name: "rustfmt" 18 | rust: stable 19 | os: windows 20 | before_script: rustup component add rustfmt-preview 21 | script: cargo fmt --all -- --check 22 | 23 | - name: "warnings" 24 | rust: stable 25 | script: cargo check --all 26 | os: windows 27 | 28 | - name: "release" 29 | rust: stable 30 | os: windows 31 | script: 32 | - cargo build --verbose --all --release 33 | script: 34 | - cargo build --all --verbose -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at opensource@embark-studios.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hassle-rs" 3 | version = "0.11.0" 4 | authors = ["Traverse-Research "] 5 | edition = "2018" 6 | description = "HLSL compiler library, this crate provides an FFI layer and idiomatic rust wrappers for the new DXC HLSL compiler and validator." 7 | license = "MIT" 8 | readme = "README.md" 9 | homepage = "https://github.com/Traverse-Research/hassle-rs" 10 | repository = "https://github.com/Traverse-Research/hassle-rs" 11 | keywords = ["shader", "pipeline", "hlsl", "dxc", "intellisense"] 12 | categories = ["rendering", "rendering::graphics-api"] 13 | include = ["src", "LICENSE"] 14 | documentation = "https://docs.rs/hassle-rs" 15 | 16 | [dependencies] 17 | bitflags = "2" 18 | com = { version = "0.6", features = ["production"] } 19 | # libloading 0.8 switches from `winapi` to `windows-sys`; permit either 20 | libloading = ">=0.7,<0.9" 21 | thiserror = "1.0.2" 22 | widestring = "1" 23 | 24 | [target.'cfg(windows)'.dependencies] 25 | winapi = { version = "0.3", features = ["wtypes", "oleauto", "combaseapi"] } 26 | 27 | [target.'cfg(not(windows))'.dependencies] 28 | libc = "0.2" 29 | 30 | [dev-dependencies] 31 | rspirv = "0.12" 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Jasper Bekkers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 🌤 hassle-rs 2 | ======== 3 | [![Actions Status](https://github.com/Traverse-Research/hassle-rs/workflows/Continuous%20integration/badge.svg)](https://github.com/Traverse-Research/hassle-rs/actions) 4 | [![Latest version](https://img.shields.io/crates/v/hassle-rs.svg)](https://crates.io/crates/hassle-rs) 5 | [![Documentation](https://docs.rs/hassle-rs/badge.svg)](https://docs.rs/hassle-rs) 6 | [![Lines of code](https://tokei.rs/b1/github/Traverse-Research/hassle-rs)](https://github.com/Traverse-Research/hassle-rs) 7 | ![MIT](https://img.shields.io/badge/license-MIT-blue.svg) 8 | [![Contributor Covenant](https://img.shields.io/badge/contributor%20covenant-v1.4%20adopted-ff69b4.svg)](../master/CODE_OF_CONDUCT.md) 9 | 10 | [![Banner](banner.png)](https://traverseresearch.nl) 11 | 12 | This crate provides an FFI layer and idiomatic rust wrappers for the new [DirectXShaderCompiler](https://github.com/Microsoft/DirectXShaderCompiler) library. 13 | 14 | - [Documentation](https://docs.rs/hassle-rs) 15 | 16 | ## Usage 17 | 18 | Add this to your `Cargo.toml`: 19 | 20 | ```toml 21 | [dependencies] 22 | hassle-rs = "0.11.0" 23 | ``` 24 | 25 | Then acquire `dxcompiler.dll` on Windows or `libdxcompiler.so` on Linux directly from [AppVeyor](https://ci.appveyor.com/project/antiagainst/directxshadercompiler/branch/master/artifacts), or compile it from source according to [the instructions](https://github.com/microsoft/DirectXShaderCompiler/blob/main/docs/DxcOnUnix.rst#building-dxc) in the [DirectXShaderCompiler](https://github.com/Microsoft/DirectXShaderCompiler) GitHub repository and make sure it's in the executable environment. See our [support table](##Supported-DXC-versions-on-non-Windows) below for specific compatibility notes on non-Windows OSes. 26 | 27 | DxcValidator also requires `dxil.dll` which can be grabbed from any recent DXC release: https://github.com/microsoft/DirectXShaderCompiler/releases/latest 28 | More info: https://www.wihlidal.com/blog/pipeline/2018-09-16-dxil-signing-post-compile/ 29 | 30 | ## Supported DXC versions on non-Windows 31 | 32 | Outside of Windows (e.g. Unix) the emulated COM API needed its fair share of fixes to match the layout on Windows. This results in repetitive API breakage that is hard to detect from within `hassle-rs`: be sure to math the `hassle-rs` release below to a minimum DXC commit to prevent runtime failures outside of Windows! 33 | 34 | | Since `hassle-rs` | DXC release | Git commit | 35 | |-|-|-| 36 | | 0.10.0 | [v1.7.2212](https://github.com/microsoft/DirectXShaderCompiler/releases/tag/v1.7.2212) | https://github.com/microsoft/DirectXShaderCompiler/commit/47f31378a9b51894b0465b33ac1d10ce6349a468 | 37 | | 0.5.1 (if using `intellisense`) | [release-1.6.2012](https://github.com/microsoft/DirectXShaderCompiler/tree/release-1.6.2012) | https://github.com/microsoft/DirectXShaderCompiler/commit/2ade6f84d6b95bfd96eec1d6d15e3aa3b519d180 | 38 | 39 | When compiling on MacOS with `clang`, or Linux with `gcc`, be sure to have at least https://github.com/microsoft/DirectXShaderCompiler/commit/af14220b45d3ce46e0bad51ce79655e41d07c478 (also included in `release-1.6.2012`). 40 | 41 |
42 | Interesting DXC commits pertaining Unix support 43 | 44 | These patches have had an effect on `hassle-rs` compatibility over time: 45 | 46 | - [`[Linux] WinAdapter: Remove virtual dtors from IUnknown to fix vtable ABI`](https://github.com/microsoft/DirectXShaderCompiler/commit/47f31378a9b51894b0465b33ac1d10ce6349a468) 47 | - [`Linux: Implement prefix-counted BSTR allocation in SysAllocStringLen`](https://github.com/microsoft/DirectXShaderCompiler/commit/2ade6f84d6b95bfd96eec1d6d15e3aa3b519d180) 48 | - [`[linux-port] Support full IID comparison on GCC`](https://github.com/microsoft/DirectXShaderCompiler/commit/af14220b45d3ce46e0bad51ce79655e41d07c478) 49 | 50 |
51 | 52 | ## Usage examples 53 | 54 | ### Compile HLSL into SPIR-V 55 | 56 | ```rust 57 | let spirv = compile_hlsl( 58 | "shader_filename.hlsl", 59 | code, 60 | "copyCs", 61 | "cs_6_5", 62 | &vec!["-spirv"], 63 | &vec![ 64 | ("MY_DEFINE", Some("Value")), 65 | ("OTHER_DEFINE", None) 66 | ], 67 | ); 68 | ``` 69 | 70 | ### Compile HLSL into DXIL and validate it 71 | 72 | ```rust 73 | let dxil = compile_hlsl("test.cs.hlsl", test_cs, "main", "cs_6_5", args, &[]).unwrap(); 74 | let result = validate_dxil(&dxil); // Only a Windows machine in Developer Mode can run non-validated DXIL 75 | 76 | if let Some(err) = result.err() { 77 | println!("validation failed: {}", err); 78 | } 79 | ``` 80 | 81 | ## License 82 | 83 | Licensed under MIT license ([LICENSE](LICENSE) or http://opensource.org/licenses/MIT) 84 | 85 | ## Contributions 86 | 87 | - Graham Wihlidal 88 | - Tiago Carvalho 89 | - Marijn Suijten 90 | - Tomasz Stachowiak 91 | - Manon Oomen 92 | 93 | ## Contribution 94 | 95 | Unless you explicitly state otherwise, any contribution intentionally submitted 96 | for inclusion in this crate by you, shall be licensed as above, without any additional terms or conditions. 97 | 98 | Contributions are always welcome; please look at the [issue tracker](https://github.com/Traverse-Research/hassle-rs/issues) to see what known improvements are documented. 99 | -------------------------------------------------------------------------------- /banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Traverse-Research/hassle-rs/be9a1c5a929c1950ba28b3f75163b69e574a46e9/banner.png -------------------------------------------------------------------------------- /examples/copy-over-56.hlsl: -------------------------------------------------------------------------------- 1 | Texture2D g_input : register(t0, space0); 2 | RWTexture2D g_output : register(u0, space0); 3 | 4 | [numthreads(8, 8, 1)] 5 | void copyCs(uint3 dispatchThreadId : SV_DispatchThreadID) 6 | { 7 | g_output[dispatchThreadId.xy] = g_input[dispatchThreadId.xy]; 8 | } -------------------------------------------------------------------------------- /examples/copy-under-56.hlsl: -------------------------------------------------------------------------------- 1 | Texture2D g_input : register(t0, space0); 2 | RWTexture2D g_output : register(u0, space0); 3 | 4 | [numthreads(8, 8, 1)] 5 | void copyCs(uint3 dispatchThreadId : SV_DispatchThreadID) 6 | { 7 | g_output[dispatchThreadId.xy] = g_input[dispatchThreadId.xy * 1000]; 8 | } 9 | 10 | -------------------------------------------------------------------------------- /examples/copy.hlsl: -------------------------------------------------------------------------------- 1 | Texture2D g_input : register(t0, space0); 2 | RWTexture2D g_output : register(u0, space0); 3 | 4 | [numthreads(8, 8, 1)] 5 | void copyCs(uint3 dispatchThreadId : SV_DispatchThreadID) 6 | { 7 | g_output[dispatchThreadId.xy] = g_input[dispatchThreadId.xy]; 8 | } -------------------------------------------------------------------------------- /examples/file-ast.rs: -------------------------------------------------------------------------------- 1 | use hassle_rs::intellisense::*; 2 | use hassle_rs::*; 3 | 4 | fn print_cursor_tree(cursor: &DxcCursor, source: &str) { 5 | print_indented_cursor_tree(cursor, source, 0) 6 | } 7 | 8 | fn print_indented_cursor_tree(cursor: &DxcCursor, source: &str, indent: usize) { 9 | print_indented_cursor(cursor, source, indent); 10 | 11 | let child_cursors = cursor.get_all_children().unwrap(); 12 | 13 | for child_cursor in &child_cursors { 14 | print_indented_cursor_tree(child_cursor, source, indent + 1); 15 | } 16 | } 17 | 18 | fn print_indented_cursor(cursor: &DxcCursor, source: &str, indent: usize) { 19 | let range = cursor.get_extent().unwrap(); 20 | let kind_flags = cursor.get_kind_flags().unwrap(); 21 | let cursor_type = cursor.get_cursor_type().unwrap().get_spelling().unwrap(); 22 | let spelling = cursor.get_spelling().unwrap(); 23 | 24 | let display_name = cursor.get_display_name().unwrap(); 25 | let format_name = cursor 26 | .get_formatted_name(DxcCursorFormatting::DEFAULT) 27 | .unwrap(); 28 | let qualified_name = cursor.get_qualified_name(true).unwrap(); 29 | 30 | let DxcSourceOffsets { 31 | start_offset, 32 | end_offset, 33 | } = range.get_offsets().unwrap(); 34 | 35 | let source_range = (start_offset as usize)..(end_offset as usize); 36 | 37 | let source_text = &source[source_range]; 38 | 39 | println!( 40 | "{: formatted name {:?}", 53 | "", 54 | format_name, 55 | indent = indent + 1 56 | ); 57 | 58 | println!( 59 | "{: qualified name {:?}", 60 | "", 61 | qualified_name, 62 | indent = indent + 1 63 | ); 64 | 65 | println!( 66 | "{: source {:?}", 67 | "", 68 | source_text, 69 | indent = indent + 1 70 | ); 71 | } 72 | 73 | fn main() { 74 | let name = "copy.hlsl"; 75 | 76 | let source = include_str!("copy.hlsl"); 77 | 78 | let args = vec![]; 79 | 80 | let dxc = Dxc::new(None).unwrap(); 81 | 82 | let intellisense = dxc.create_intellisense().unwrap(); 83 | 84 | let local_options = intellisense.get_default_editing_tu_options().unwrap(); 85 | 86 | let index = intellisense.create_index().unwrap(); 87 | 88 | let unsaved_file = intellisense.create_unsaved_file(name, source).unwrap(); 89 | 90 | let translation_unit = index 91 | .parse_translation_unit(name, &args, &[&unsaved_file], local_options) 92 | .unwrap(); 93 | 94 | let cursor = translation_unit.get_cursor().unwrap(); 95 | 96 | print_cursor_tree(&cursor, source) 97 | } 98 | -------------------------------------------------------------------------------- /examples/include.hlsl: -------------------------------------------------------------------------------- 1 | #include "examples/copy.hlsl" -------------------------------------------------------------------------------- /examples/include.rs: -------------------------------------------------------------------------------- 1 | use hassle_rs::*; 2 | use rspirv::binary::Disassemble; 3 | use rspirv::dr::load_bytes; 4 | 5 | fn main() { 6 | let source = include_str!("include.hlsl"); 7 | 8 | match compile_hlsl("include.hlsl", source, "copyCs", "cs_6_0", &["-spirv"], &[]) { 9 | Ok(spirv) => { 10 | let module = load_bytes(spirv).unwrap(); 11 | println!("{}", module.disassemble()); 12 | } 13 | // Could very well happen that one needs to recompile or download a dxcompiler.dll 14 | Err(s) => panic!("Failed to compile to SPIR-V: {}", s), 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /examples/intellisense-tu.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::uninlined_format_args)] 2 | 3 | use hassle_rs::*; 4 | 5 | fn main() { 6 | let name = "copy.hlsl"; 7 | 8 | let source = include_str!("copy.hlsl"); 9 | 10 | let args = vec![]; 11 | 12 | let dxc = Dxc::new(None).unwrap(); 13 | 14 | let intellisense = dxc.create_intellisense().unwrap(); 15 | 16 | let local_options = intellisense.get_default_editing_tu_options().unwrap(); 17 | 18 | let index = intellisense.create_index().unwrap(); 19 | 20 | let unsaved_file = intellisense.create_unsaved_file(name, source).unwrap(); 21 | 22 | let translation_unit = index 23 | .parse_translation_unit(name, &args, &[&unsaved_file], local_options) 24 | .unwrap(); 25 | 26 | let cursor = translation_unit.get_cursor().unwrap(); 27 | 28 | { 29 | let range = cursor.get_extent().unwrap(); 30 | println!("Range {:?}", range); 31 | 32 | let location = cursor.get_location().unwrap(); 33 | println!("Location {:?}", location); 34 | 35 | let name = cursor.get_display_name().unwrap(); 36 | println!("Name {:?}", name); 37 | assert_eq!(name, "copy.hlsl"); 38 | 39 | let cursor_kind = cursor.get_kind().unwrap(); 40 | println!("CursorKind {:?}", cursor_kind); 41 | 42 | let cursor_kind_flags = cursor.get_kind_flags().unwrap(); 43 | println!("CursorKindFlags {:?}", cursor_kind_flags); 44 | } 45 | 46 | let child_cursors = cursor.get_all_children().unwrap(); 47 | 48 | assert_eq!(child_cursors[0].get_display_name().unwrap(), "g_input"); 49 | assert_eq!(child_cursors[1].get_display_name().unwrap(), "g_output"); 50 | assert_eq!( 51 | child_cursors[2].get_display_name().unwrap(), 52 | "copyCs(uint3)" 53 | ); 54 | 55 | for child_cursor in child_cursors { 56 | let range = child_cursor.get_extent().unwrap(); 57 | println!("Child Range {:?}", range); 58 | 59 | let location = child_cursor.get_location().unwrap(); 60 | println!("Child Location {:?}", location); 61 | 62 | let name = child_cursor.get_display_name().unwrap(); 63 | println!("Child Name {:?}", name); 64 | 65 | let cursor_kind = child_cursor.get_kind().unwrap(); 66 | println!("Child CursorKind {:?}", cursor_kind); 67 | 68 | let cursor_kind_flags = child_cursor.get_kind_flags().unwrap(); 69 | println!("CursorKindFlags {:?}", cursor_kind_flags); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /examples/spirv.rs: -------------------------------------------------------------------------------- 1 | use hassle_rs::*; 2 | use rspirv::binary::Disassemble; 3 | use rspirv::dr::load_bytes; 4 | 5 | fn main() { 6 | let source = include_str!("copy.hlsl"); 7 | 8 | match compile_hlsl("copy.hlsl", source, "copyCs", "cs_6_0", &["-spirv"], &[]) { 9 | Ok(spirv) => { 10 | let module = load_bytes(spirv).unwrap(); 11 | println!("{}", module.disassemble()); 12 | } 13 | // Could very well happen that one needs to recompile or download a dxcompiler.dll 14 | Err(s) => panic!("Failed to compile to SPIR-V: {}", s), 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /examples/validate-fake-signing.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::uninlined_format_args)] 2 | 3 | #[repr(C)] 4 | pub struct MinimalHeader { 5 | four_cc: u32, 6 | hash_digest: [u32; 4], 7 | } 8 | 9 | fn get_digest(buffer: &[u8]) -> [u32; 4] { 10 | let header_ptr = buffer.as_ptr().cast::(); 11 | let header_ref = unsafe { &*header_ptr }; 12 | header_ref.hash_digest 13 | } 14 | 15 | use hassle_rs::{compile_hlsl, fake_sign_dxil_in_place, validate_dxil}; 16 | 17 | fn main() { 18 | let sources = [ 19 | include_str!("copy-over-56.hlsl"), 20 | include_str!("copy-under-56.hlsl"), 21 | ]; 22 | 23 | let mut all_matches = true; 24 | 25 | for (idx, source) in sources.iter().enumerate() { 26 | println!("Testing file: {}", idx); 27 | let mut dxil = compile_hlsl("copy.hlsl", source, "copyCs", "cs_6_0", &[], &[]).unwrap(); 28 | 29 | let without_digest = get_digest(&dxil); 30 | 31 | let result = fake_sign_dxil_in_place(&mut dxil); 32 | assert!(result); 33 | 34 | let fake_signed_digest = get_digest(&dxil); 35 | 36 | if cfg!(windows) { 37 | let validated_dxil = validate_dxil(&dxil).unwrap(); 38 | 39 | let with_digest = get_digest(&validated_dxil); 40 | 41 | println!( 42 | "\tAfter compilation: {:?}\n\tAfter dxil.dll: {:?}\n\tAfter fake signing: {:?}", 43 | without_digest, with_digest, fake_signed_digest 44 | ); 45 | 46 | if fake_signed_digest != with_digest { 47 | println!("---- Mismatch in file {} ----", idx); 48 | all_matches &= false; 49 | } 50 | } else { 51 | println!( 52 | "\tAfter compilation: {:?}\n\tAfter fake signing: {:?}", 53 | without_digest, fake_signed_digest 54 | ); 55 | } 56 | } 57 | 58 | if cfg!(windows) { 59 | if all_matches { 60 | println!("Success"); 61 | } 62 | } else { 63 | println!("Warning: Signatures not validated against `dxil.dll` - this is only possible on Windows"); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /examples/validate.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::uninlined_format_args)] 2 | 3 | use hassle_rs::*; 4 | 5 | #[repr(C)] 6 | #[repr(packed)] 7 | pub struct MinimalHeader { 8 | four_cc: u32, 9 | hash_digest: [u32; 4], 10 | } 11 | 12 | // zero_digest & get_digest from https://github.com/gwihlidal/dxil-signing/blob/master/rust/src/main.rs 13 | 14 | fn zero_digest(buffer: &mut [u8]) { 15 | let header_ptr = buffer.as_mut_ptr().cast::(); 16 | let header_ref = unsafe { &mut *header_ptr }; 17 | header_ref.hash_digest = [0; 4]; 18 | } 19 | 20 | fn get_digest(buffer: &[u8]) -> [u32; 4] { 21 | let header_ptr = buffer.as_ptr().cast::(); 22 | let header_ref = unsafe { &*header_ptr }; 23 | header_ref.hash_digest 24 | } 25 | 26 | fn main() { 27 | let source = include_str!("copy.hlsl"); 28 | 29 | let mut dxil = compile_hlsl("copy.hlsl", source, "copyCs", "cs_6_0", &[], &[]).unwrap(); 30 | 31 | zero_digest(&mut dxil); 32 | 33 | let without_digest = get_digest(&dxil); 34 | println!("Before validation: {:?}", without_digest); 35 | 36 | let validated_dxil = validate_dxil(&dxil).unwrap(); 37 | 38 | let with_digest = get_digest(&validated_dxil); 39 | 40 | println!("After validation: {:?}", with_digest); 41 | } 42 | -------------------------------------------------------------------------------- /release.toml: -------------------------------------------------------------------------------- 1 | pre-release-commit-message = "Release {{version}}" 2 | tag-message = "Release {{version}}" 3 | tag-name = "{{version}}" 4 | sign-commit = true 5 | sign-tag = true 6 | publish = false 7 | 8 | pre-release-replacements = [ 9 | {file="README.md", search="hassle-rs = .*", replace="{{crate_name}} = \"{{version}}\""}, 10 | ] 11 | -------------------------------------------------------------------------------- /src/fake_sign/mod.rs: -------------------------------------------------------------------------------- 1 | mod modified_md5; 2 | use modified_md5::Context; 3 | 4 | #[repr(C)] 5 | struct FileHeader { 6 | fourcc: u32, 7 | hash_value: [u32; 4], 8 | container_version: u32, 9 | file_length: u32, 10 | num_chunks: u32, 11 | } 12 | 13 | const DXIL_HEADER_CONTAINER_VERSION_OFFSET: usize = 20; 14 | const DXBC_FOURCC: u32 = u32::from_le_bytes([b'D', b'X', b'B', b'C']); 15 | 16 | fn read_fourcc(dxil: &[u8]) -> u32 { 17 | let header: *const FileHeader = dxil.as_ptr().cast(); 18 | unsafe { (*header).fourcc } 19 | } 20 | 21 | fn read_file_length(dxil: &[u8]) -> u32 { 22 | let header: *const FileHeader = dxil.as_ptr().cast(); 23 | unsafe { (*header).file_length } 24 | } 25 | 26 | fn write_hash_value(dxil: &mut [u8], state: [u32; 4]) { 27 | let header: *mut FileHeader = dxil.as_mut_ptr().cast(); 28 | 29 | unsafe { 30 | (*header).hash_value.copy_from_slice(&state); 31 | } 32 | } 33 | 34 | /// Helper function for signing DXIL binary blobs when 35 | /// `dxil.dll` might not be available (such as on Linux based 36 | /// platforms). 37 | /// This essentially performs the same functionality as [`crate::validate_dxil()`] 38 | /// but in a more cross platform way. 39 | /// 40 | /// Ported from 41 | pub fn fake_sign_dxil_in_place(dxil: &mut [u8]) -> bool { 42 | if read_fourcc(dxil) != DXBC_FOURCC { 43 | return false; 44 | } 45 | 46 | if read_file_length(dxil) != dxil.len() as u32 { 47 | return false; 48 | } 49 | 50 | // the hashable data starts immediately after the hash. 51 | let data = &dxil[DXIL_HEADER_CONTAINER_VERSION_OFFSET..]; 52 | 53 | let num_bits: u32 = data.len() as u32 * 8; 54 | let num_bits_part_2: u32 = (num_bits >> 2) | 1; 55 | let left_over_len: u32 = data.len() as u32 % 64; 56 | 57 | let (first_part, padding_part) = data.split_at(data.len() - left_over_len as usize); 58 | 59 | let mut ctx = Context::new(); 60 | ctx.consume(first_part); 61 | 62 | let mut block = [0u8; 64]; 63 | 64 | if left_over_len >= 56 { 65 | assert_eq!(padding_part.len(), left_over_len as usize); 66 | ctx.consume(padding_part); 67 | 68 | block[0..4].copy_from_slice(&0x80u32.to_le_bytes()); 69 | ctx.consume(&block[0..64 - left_over_len as usize]); 70 | 71 | // the final block contains the number of bits in the first dword, and the weird upper bits 72 | block[0..4].copy_from_slice(&num_bits.to_le_bytes()); 73 | 74 | // write to last dword 75 | block[15 * 4..].copy_from_slice(&num_bits_part_2.to_le_bytes()); 76 | 77 | ctx.consume(block); 78 | } else { 79 | ctx.consume(num_bits.to_le_bytes()); 80 | 81 | if left_over_len != 0 { 82 | ctx.consume(padding_part) 83 | } 84 | 85 | let padding_bytes = (64 - left_over_len - 4) as usize; 86 | 87 | block[0] = 0x80; 88 | block[padding_bytes - 4..padding_bytes].copy_from_slice(&num_bits_part_2.to_le_bytes()); 89 | ctx.consume(&block[0..padding_bytes]); 90 | } 91 | 92 | // dxil signing is odd - it doesn't run the finalization step of the md5 93 | // algorithm but instead pokes the hasher state directly into container 94 | 95 | write_hash_value(dxil, ctx.state); 96 | 97 | true 98 | } 99 | -------------------------------------------------------------------------------- /src/fake_sign/modified_md5.rs: -------------------------------------------------------------------------------- 1 | // https://github.com/stainless-steel/md5/blob/3236fc93c26af909923767e65be87e45c47aab89/LICENSE.md 2 | // 3 | // Taken under the Apache 2.0 and MIT dual license to be included into this project with small 4 | // modifications. 5 | // 6 | // Apache 2.0 7 | // 8 | // Copyright 2015–2019 The md5 Developers 9 | // 10 | // Licensed under the Apache License, Version 2.0 (the “License”); you may not use 11 | // this file except in compliance with the License. You may obtain a copy of the 12 | // License at 13 | // 14 | // http://www.apache.org/licenses/LICENSE-2.0 15 | // 16 | // Unless required by applicable law or agreed to in writing, software distributed 17 | // under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR 18 | // CONDITIONS OF ANY KIND, either express or implied. See the License for the 19 | // specific language governing permissions and limitations under the License. 20 | // 21 | // 22 | // MIT: 23 | // 24 | // Copyright 2015–2019 The md5 Developers 25 | 26 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 27 | // this software and associated documentation files (the “Software”), to deal in 28 | // the Software without restriction, including without limitation the rights to 29 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 30 | // the Software, and to permit persons to whom the Software is furnished to do so, 31 | // subject to the following conditions: 32 | 33 | // The above copyright notice and this permission notice shall be included in all 34 | // copies or substantial portions of the Software. 35 | 36 | // THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 37 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 38 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 39 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 40 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 41 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 42 | 43 | #[derive(Clone)] 44 | pub struct Context { 45 | buffer: [u8; 64], 46 | count: [u32; 2], 47 | pub state: [u32; 4], // hassle-rs modification 48 | } 49 | 50 | impl Context { 51 | #[inline] 52 | pub fn new() -> Context { 53 | Context { 54 | buffer: [0; 64], 55 | count: [0, 0], 56 | state: [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476], 57 | } 58 | } 59 | 60 | /// Consume data. 61 | #[cfg(target_pointer_width = "32")] 62 | #[inline] 63 | pub fn consume>(&mut self, data: T) { 64 | consume(self, data.as_ref()); 65 | } 66 | 67 | /// Consume data. 68 | #[cfg(target_pointer_width = "64")] 69 | pub fn consume>(&mut self, data: T) { 70 | for chunk in data.as_ref().chunks(u32::MAX as usize) { 71 | consume(self, chunk); 72 | } 73 | } 74 | } 75 | 76 | fn consume( 77 | Context { 78 | buffer, 79 | count, 80 | state, 81 | }: &mut Context, 82 | data: &[u8], 83 | ) { 84 | let mut input = [0u32; 16]; 85 | let mut k = ((count[0] >> 3) & 0x3f) as usize; 86 | let length = data.len() as u32; 87 | count[0] = count[0].wrapping_add(length << 3); 88 | if count[0] < length << 3 { 89 | count[1] = count[1].wrapping_add(1); 90 | } 91 | count[1] = count[1].wrapping_add(length >> 29); 92 | for &value in data { 93 | buffer[k] = value; 94 | k += 1; 95 | if k == 0x40 { 96 | let mut j = 0; 97 | for v in input.iter_mut() { 98 | // hassle-rs modification to deal with clippy 99 | *v = ((buffer[j + 3] as u32) << 24) 100 | | ((buffer[j + 2] as u32) << 16) 101 | | ((buffer[j + 1] as u32) << 8) 102 | | (buffer[j] as u32); 103 | j += 4; 104 | } 105 | transform(state, &input); 106 | k = 0; 107 | } 108 | } 109 | } 110 | 111 | fn transform(state: &mut [u32; 4], input: &[u32; 16]) { 112 | let (mut a, mut b, mut c, mut d) = (state[0], state[1], state[2], state[3]); 113 | macro_rules! add( 114 | ($a:expr, $b:expr) => ($a.wrapping_add($b)); 115 | ); 116 | { 117 | macro_rules! F( 118 | ($x:expr, $y:expr, $z:expr) => (($x & $y) | (!$x & $z)); 119 | ); 120 | macro_rules! T( 121 | ($a:expr, $b:expr, $c:expr, $d:expr, $x:expr, $s:expr, $ac:expr) => ({ 122 | $a = add!(add!(add!($a, F!($b, $c, $d)), $x), $ac); 123 | $a = $a.rotate_left($s); 124 | $a = add!($a, $b); 125 | }); 126 | ); 127 | const S1: u32 = 7; 128 | const S2: u32 = 12; 129 | const S3: u32 = 17; 130 | const S4: u32 = 22; 131 | T!(a, b, c, d, input[0], S1, 3614090360); 132 | T!(d, a, b, c, input[1], S2, 3905402710); 133 | T!(c, d, a, b, input[2], S3, 606105819); 134 | T!(b, c, d, a, input[3], S4, 3250441966); 135 | T!(a, b, c, d, input[4], S1, 4118548399); 136 | T!(d, a, b, c, input[5], S2, 1200080426); 137 | T!(c, d, a, b, input[6], S3, 2821735955); 138 | T!(b, c, d, a, input[7], S4, 4249261313); 139 | T!(a, b, c, d, input[8], S1, 1770035416); 140 | T!(d, a, b, c, input[9], S2, 2336552879); 141 | T!(c, d, a, b, input[10], S3, 4294925233); 142 | T!(b, c, d, a, input[11], S4, 2304563134); 143 | T!(a, b, c, d, input[12], S1, 1804603682); 144 | T!(d, a, b, c, input[13], S2, 4254626195); 145 | T!(c, d, a, b, input[14], S3, 2792965006); 146 | T!(b, c, d, a, input[15], S4, 1236535329); 147 | } 148 | { 149 | macro_rules! F( 150 | ($x:expr, $y:expr, $z:expr) => (($x & $z) | ($y & !$z)); 151 | ); 152 | macro_rules! T( 153 | ($a:expr, $b:expr, $c:expr, $d:expr, $x:expr, $s:expr, $ac:expr) => ({ 154 | $a = add!(add!(add!($a, F!($b, $c, $d)), $x), $ac); 155 | $a = $a.rotate_left($s); 156 | $a = add!($a, $b); 157 | }); 158 | ); 159 | const S1: u32 = 5; 160 | const S2: u32 = 9; 161 | const S3: u32 = 14; 162 | const S4: u32 = 20; 163 | T!(a, b, c, d, input[1], S1, 4129170786); 164 | T!(d, a, b, c, input[6], S2, 3225465664); 165 | T!(c, d, a, b, input[11], S3, 643717713); 166 | T!(b, c, d, a, input[0], S4, 3921069994); 167 | T!(a, b, c, d, input[5], S1, 3593408605); 168 | T!(d, a, b, c, input[10], S2, 38016083); 169 | T!(c, d, a, b, input[15], S3, 3634488961); 170 | T!(b, c, d, a, input[4], S4, 3889429448); 171 | T!(a, b, c, d, input[9], S1, 568446438); 172 | T!(d, a, b, c, input[14], S2, 3275163606); 173 | T!(c, d, a, b, input[3], S3, 4107603335); 174 | T!(b, c, d, a, input[8], S4, 1163531501); 175 | T!(a, b, c, d, input[13], S1, 2850285829); 176 | T!(d, a, b, c, input[2], S2, 4243563512); 177 | T!(c, d, a, b, input[7], S3, 1735328473); 178 | T!(b, c, d, a, input[12], S4, 2368359562); 179 | } 180 | { 181 | macro_rules! F( 182 | ($x:expr, $y:expr, $z:expr) => ($x ^ $y ^ $z); 183 | ); 184 | macro_rules! T( 185 | ($a:expr, $b:expr, $c:expr, $d:expr, $x:expr, $s:expr, $ac:expr) => ({ 186 | $a = add!(add!(add!($a, F!($b, $c, $d)), $x), $ac); 187 | $a = $a.rotate_left($s); 188 | $a = add!($a, $b); 189 | }); 190 | ); 191 | const S1: u32 = 4; 192 | const S2: u32 = 11; 193 | const S3: u32 = 16; 194 | const S4: u32 = 23; 195 | T!(a, b, c, d, input[5], S1, 4294588738); 196 | T!(d, a, b, c, input[8], S2, 2272392833); 197 | T!(c, d, a, b, input[11], S3, 1839030562); 198 | T!(b, c, d, a, input[14], S4, 4259657740); 199 | T!(a, b, c, d, input[1], S1, 2763975236); 200 | T!(d, a, b, c, input[4], S2, 1272893353); 201 | T!(c, d, a, b, input[7], S3, 4139469664); 202 | T!(b, c, d, a, input[10], S4, 3200236656); 203 | T!(a, b, c, d, input[13], S1, 681279174); 204 | T!(d, a, b, c, input[0], S2, 3936430074); 205 | T!(c, d, a, b, input[3], S3, 3572445317); 206 | T!(b, c, d, a, input[6], S4, 76029189); 207 | T!(a, b, c, d, input[9], S1, 3654602809); 208 | T!(d, a, b, c, input[12], S2, 3873151461); 209 | T!(c, d, a, b, input[15], S3, 530742520); 210 | T!(b, c, d, a, input[2], S4, 3299628645); 211 | } 212 | { 213 | macro_rules! F( 214 | ($x:expr, $y:expr, $z:expr) => ($y ^ ($x | !$z)); 215 | ); 216 | macro_rules! T( 217 | ($a:expr, $b:expr, $c:expr, $d:expr, $x:expr, $s:expr, $ac:expr) => ({ 218 | $a = add!(add!(add!($a, F!($b, $c, $d)), $x), $ac); 219 | $a = $a.rotate_left($s); 220 | $a = add!($a, $b); 221 | }); 222 | ); 223 | const S1: u32 = 6; 224 | const S2: u32 = 10; 225 | const S3: u32 = 15; 226 | const S4: u32 = 21; 227 | T!(a, b, c, d, input[0], S1, 4096336452); 228 | T!(d, a, b, c, input[7], S2, 1126891415); 229 | T!(c, d, a, b, input[14], S3, 2878612391); 230 | T!(b, c, d, a, input[5], S4, 4237533241); 231 | T!(a, b, c, d, input[12], S1, 1700485571); 232 | T!(d, a, b, c, input[3], S2, 2399980690); 233 | T!(c, d, a, b, input[10], S3, 4293915773); 234 | T!(b, c, d, a, input[1], S4, 2240044497); 235 | T!(a, b, c, d, input[8], S1, 1873313359); 236 | T!(d, a, b, c, input[15], S2, 4264355552); 237 | T!(c, d, a, b, input[6], S3, 2734768916); 238 | T!(b, c, d, a, input[13], S4, 1309151649); 239 | T!(a, b, c, d, input[4], S1, 4149444226); 240 | T!(d, a, b, c, input[11], S2, 3174756917); 241 | T!(c, d, a, b, input[2], S3, 718787259); 242 | T!(b, c, d, a, input[9], S4, 3951481745); 243 | } 244 | state[0] = add!(state[0], a); 245 | state[1] = add!(state[1], b); 246 | state[2] = add!(state[2], c); 247 | state[3] = add!(state[3], d); 248 | } 249 | -------------------------------------------------------------------------------- /src/ffi.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::transmute_ptr_to_ptr)] 2 | #![allow(clippy::too_many_arguments)] 3 | 4 | use crate::os::{HRESULT, LPCWSTR, LPWSTR}; 5 | use com::{interfaces, interfaces::IUnknown, IID}; 6 | use std::ffi::c_void; 7 | 8 | pub type DxcCreateInstanceProc = 9 | extern "system" fn(rclsid: &IID, riid: &IID, ppv: *mut Option) -> HRESULT; 10 | 11 | pub type DxcCreateInstanceProc2 = extern "system" fn( 12 | malloc: /* IMalloc */ *const c_void, 13 | rclsid: &IID, 14 | riid: &IID, 15 | ppv: *mut *mut c_void, 16 | ) -> HRESULT; 17 | 18 | pub const DFCC_DXIL: u32 = u32::from_le_bytes([b'D', b'X', b'I', b'L']); 19 | 20 | interfaces! { 21 | #[uuid("8ba5fb08-5195-40e2-ac58-0d989c3a0102")] 22 | pub(crate) unsafe interface IDxcBlob: IUnknown { 23 | pub(crate) fn get_buffer_pointer(&self) -> *mut c_void; 24 | pub(crate) fn get_buffer_size(&self) -> usize; 25 | } 26 | 27 | #[uuid("7241d424-2646-4191-97c0-98e96e42fc68")] 28 | pub(crate) unsafe interface IDxcBlobEncoding: IDxcBlob { 29 | pub(crate) fn get_encoding(&self, known: *mut u32, code_page: *mut u32) -> HRESULT; 30 | } 31 | 32 | #[uuid("e5204dc7-d18c-4c3c-bdfb-851673980fe7")] 33 | pub(crate) unsafe interface IDxcLibrary: IUnknown { 34 | pub(crate) fn set_malloc(&self, malloc: *const c_void) -> HRESULT; 35 | pub(crate) fn create_blob_from_blob( 36 | &self, 37 | blob: IDxcBlob, 38 | offset: u32, 39 | length: u32, 40 | result_blob: *mut Option, 41 | ) -> HRESULT; 42 | pub(crate) fn create_blob_from_file( 43 | &self, 44 | filename: LPCWSTR, 45 | code_page: *const u32, 46 | blob_encoding: *mut Option, 47 | ) -> HRESULT; 48 | pub(crate) fn create_blob_with_encoding_from_pinned( 49 | &self, 50 | text: *const c_void, 51 | size: u32, 52 | code_page: u32, 53 | blob_encoding: *mut Option, 54 | ) -> HRESULT; 55 | pub(crate) fn create_blob_with_encoding_on_heap_copy( 56 | &self, 57 | text: *const c_void, 58 | size: u32, 59 | code_page: u32, 60 | blob_encoding: *mut Option, 61 | ) -> HRESULT; 62 | pub(crate) fn create_blob_with_encoding_on_malloc( 63 | &self, 64 | text: *const c_void, 65 | malloc: *const /* IMalloc */ c_void, 66 | size: u32, 67 | code_page: u32, 68 | blob_encoding: *mut Option, 69 | ) -> HRESULT; 70 | pub(crate) fn create_include_handler( 71 | &self, 72 | include_handler: *mut Option, 73 | ) -> HRESULT; 74 | pub(crate) fn create_stream_from_blob_read_only( 75 | &self, 76 | blob: IDxcBlob, 77 | stream: *mut *mut /* IStream */ c_void, 78 | ) -> HRESULT; 79 | pub(crate) fn get_blob_as_utf8( 80 | &self, 81 | blob: IDxcBlob, 82 | blob_encoding: *mut Option, 83 | ) -> HRESULT; 84 | pub(crate) fn get_blob_as_utf16( 85 | &self, 86 | blob: IDxcBlob, 87 | blob_encoding: *mut Option, 88 | ) -> HRESULT; 89 | } 90 | 91 | #[uuid("cedb484a-d4e9-445a-b991-ca21ca157dc2")] 92 | pub(crate) unsafe interface IDxcOperationResult: IUnknown { 93 | pub(crate) fn get_status(&self, status: *mut u32) -> HRESULT; 94 | pub(crate) fn get_result(&self, result: *mut Option) -> HRESULT; 95 | pub(crate) fn get_error_buffer(&self, errors: *mut Option) -> HRESULT; 96 | } 97 | 98 | #[uuid("7f61fc7d-950d-467f-b3e3-3c02fb49187c")] 99 | pub(crate) unsafe interface IDxcIncludeHandler: IUnknown { 100 | pub(crate) fn load_source( 101 | &self, 102 | filename: LPCWSTR, 103 | include_source: *mut Option, 104 | ) -> HRESULT; 105 | } 106 | } 107 | 108 | #[repr(C)] 109 | pub struct DxcDefine { 110 | pub name: LPCWSTR, 111 | pub value: LPCWSTR, 112 | } 113 | 114 | interfaces! { 115 | #[uuid("8c210bf3-011f-4422-8d70-6f9acb8db617")] 116 | pub(crate) unsafe interface IDxcCompiler: IUnknown { 117 | pub(crate) fn compile( 118 | &self, 119 | blob: IDxcBlob, 120 | source_name: LPCWSTR, 121 | entry_point: LPCWSTR, 122 | target_profile: LPCWSTR, 123 | arguments: *const LPCWSTR, 124 | arg_count: u32, 125 | defines: *const DxcDefine, 126 | def_count: u32, 127 | include_handler: Option, 128 | result: *mut Option, 129 | ) -> HRESULT; 130 | 131 | pub(crate) fn preprocess( 132 | &self, 133 | blob: IDxcBlob, 134 | source_name: LPCWSTR, 135 | arguments: *const LPCWSTR, 136 | arg_count: u32, 137 | defines: *const DxcDefine, 138 | def_count: u32, 139 | include_handler: Option, 140 | result: *mut Option, 141 | ) -> HRESULT; 142 | 143 | pub(crate) fn disassemble( 144 | &self, 145 | blob: IDxcBlob, 146 | disassembly: *mut Option, 147 | ) -> HRESULT; 148 | } 149 | 150 | #[uuid("a005a9d9-b8bb-4594-b5c9-0e633bec4d37")] 151 | pub(crate) unsafe interface IDxcCompiler2: IDxcCompiler { 152 | pub(crate) fn compile_with_debug( 153 | &self, 154 | blob: IDxcBlob, 155 | source_name: LPCWSTR, 156 | entry_point: LPCWSTR, 157 | target_profile: LPCWSTR, 158 | arguments: *const LPCWSTR, 159 | arg_count: u32, 160 | defines: *const DxcDefine, 161 | def_count: u32, 162 | include_handler: Option, 163 | result: *mut Option, 164 | debug_blob_name: *mut LPWSTR, 165 | debug_blob: *mut Option, 166 | ) -> HRESULT; 167 | } 168 | 169 | #[uuid("f1b5be2a-62dd-4327-a1c2-42ac1e1e78e6")] 170 | pub(crate) unsafe interface IDxcLinker: IUnknown { 171 | pub(crate) fn register_library(&self, lib_name: LPCWSTR, lib: IDxcBlob) -> HRESULT; 172 | 173 | pub(crate) fn link( 174 | &self, 175 | entry_name: LPCWSTR, 176 | target_profile: LPCWSTR, 177 | lib_names: *const LPCWSTR, 178 | lib_count: u32, 179 | arguments: *const LPCWSTR, 180 | arg_count: u32, 181 | result: *mut Option, 182 | ) -> HRESULT; 183 | } 184 | } 185 | 186 | pub const DXC_VALIDATOR_FLAGS_DEFAULT: u32 = 0; 187 | pub const DXC_VALIDATOR_FLAGS_IN_PLACE_EDIT: u32 = 1; // Validator is allowed to update shader blob in-place. 188 | pub const DXC_VALIDATOR_FLAGS_ROOT_SIGNATURE_ONLY: u32 = 2; 189 | pub const DXC_VALIDATOR_FLAGS_MODULE_ONLY: u32 = 4; 190 | pub const DXC_VALIDATOR_FLAGS_VALID_MASK: u32 = 0x7; 191 | 192 | interfaces! { 193 | #[uuid("a6e82bd2-1fd7-4826-9811-2857e797f49a")] 194 | pub(crate) unsafe interface IDxcValidator: IUnknown { 195 | pub(crate) fn validate( 196 | &self, 197 | shader: IDxcBlob, 198 | flags: u32, 199 | result: *mut Option, 200 | ) -> HRESULT; 201 | } 202 | 203 | #[uuid("334b1f50-2292-4b35-99a1-25588d8c17fe")] 204 | pub(crate) unsafe interface IDxcContainerBuilder: IUnknown { 205 | pub(crate) fn load(&self, dxil_container_header: IDxcBlob) -> HRESULT; 206 | pub(crate) fn add_part(&self, four_cc: u32, source: IDxcBlob) -> HRESULT; 207 | pub(crate) fn remove_part(&self, four_cc: u32) -> HRESULT; 208 | pub(crate) fn seralize_container( 209 | &self, 210 | result: *mut Option, 211 | ) -> HRESULT; 212 | } 213 | 214 | #[uuid("091f7a26-1c1f-4948-904b-e6e3a8a771d5")] 215 | pub(crate) unsafe interface IDxcAssembler: IUnknown { 216 | pub(crate) fn assemble_to_container( 217 | &self, 218 | shader: IDxcBlob, 219 | result: *mut Option, 220 | ) -> HRESULT; 221 | } 222 | 223 | #[uuid("d2c21b26-8350-4bdc-976a-331ce6f4c54c")] 224 | pub(crate) unsafe interface IDxcContainerReflection: IUnknown { 225 | pub(crate) fn load(&self, container: IDxcBlob) -> HRESULT; 226 | pub(crate) fn get_part_count(&self, result: *mut u32) -> HRESULT; 227 | pub(crate) fn get_part_kind(&self, idx: u32, result: *mut u32) -> HRESULT; 228 | pub(crate) fn get_part_content(&self, idx: u32, result: *mut Option) -> HRESULT; 229 | pub(crate) fn find_first_part_kind(&self, kind: u32, result: *mut u32) -> HRESULT; 230 | pub(crate) fn get_part_reflection( 231 | &self, 232 | idx: u32, 233 | iid: *const IID, 234 | object: *mut Option, 235 | ) -> HRESULT; 236 | } 237 | 238 | #[uuid("5a58797d-a72c-478d-8ba2-efc6b0efe88e")] 239 | pub(crate) unsafe interface ID3D12ShaderReflection: IUnknown { 240 | pub(crate) fn get_desc(&self, p_desc: *mut c_void) -> HRESULT; 241 | pub(crate) fn get_constant_buffer_by_index(&self, index: u32) -> *mut c_void; 242 | pub(crate) fn get_constant_buffer_by_name(&self, name: *const c_void) -> *mut c_void; 243 | pub(crate) fn get_resource_binding_desc( 244 | &self, 245 | resource_index: u32, 246 | p_desc: *mut c_void, 247 | ) -> HRESULT; 248 | pub(crate) fn get_input_parameter_desc( 249 | &self, 250 | parameter_index: u32, 251 | p_desc: *mut c_void, 252 | ) -> HRESULT; 253 | pub(crate) fn get_output_parameter_desc( 254 | &self, 255 | parameter_index: u32, 256 | p_desc: *mut c_void, 257 | ) -> HRESULT; 258 | pub(crate) fn get_patch_constant_parameter_desc( 259 | &self, 260 | parameter_index: u32, 261 | p_desc: *mut c_void, 262 | ) -> HRESULT; 263 | pub(crate) fn get_variable_by_name(&self, name: *const c_void) -> *mut c_void; 264 | pub(crate) fn get_resource_binding_desc_by_name( 265 | &self, 266 | name: *const c_void, 267 | p_desc: *mut c_void, 268 | ) -> HRESULT; 269 | pub(crate) fn get_mov_instruction_count(&self) -> u32; 270 | pub(crate) fn get_movc_instruction_count(&self) -> u32; 271 | pub(crate) fn get_conversion_instruction_count(&self) -> u32; 272 | pub(crate) fn get_bitwise_instruction_count(&self) -> u32; 273 | pub(crate) fn get_gs_input_primitive(&self) -> u32; 274 | pub(crate) fn is_sample_frequency_shader(&self) -> bool; 275 | pub(crate) fn get_num_interface_slots(&self) -> u32; 276 | pub(crate) fn get_min_feature_level(&self, p_level: *mut c_void) -> HRESULT; 277 | pub(crate) fn get_thread_group_size( 278 | &self, 279 | size_x: *mut u32, 280 | size_y: *mut u32, 281 | size_z: *mut u32, 282 | ) -> u32; 283 | pub(crate) fn get_requires_flags(&self) -> u64; 284 | } 285 | 286 | #[uuid("ae2cd79f-cc22-453f-9b6b-b124e7a5204c")] 287 | pub(crate) unsafe interface IDxcOptimizerPass: IUnknown { 288 | pub(crate) fn get_option_name(&self, result: *mut LPWSTR) -> HRESULT; 289 | pub(crate) fn get_description(&self, result: *mut LPWSTR) -> HRESULT; 290 | pub(crate) fn get_option_arg_count(&self, count: *mut u32) -> HRESULT; 291 | pub(crate) fn get_option_arg_name(&self, arg_idx: u32, result: *mut LPWSTR) -> HRESULT; 292 | pub(crate) fn get_option_arg_description( 293 | &self, 294 | arg_idx: u32, 295 | result: *mut LPWSTR, 296 | ) -> HRESULT; 297 | } 298 | 299 | #[uuid("25740e2e-9cba-401b-9119-4fb42f39f270")] 300 | pub(crate) unsafe interface IDxcOptimizer: IUnknown { 301 | pub(crate) fn get_available_pass_count(&self, count: *mut u32) -> HRESULT; 302 | pub(crate) fn get_available_pass( 303 | &self, 304 | index: u32, 305 | result: *mut Option, 306 | ) -> HRESULT; 307 | pub(crate) fn run_optimizer( 308 | &self, 309 | blob: IDxcBlob, 310 | options: *const LPCWSTR, 311 | option_count: u32, 312 | output_module: *mut Option, 313 | output_text: *mut Option, 314 | ) -> HRESULT; 315 | } 316 | } 317 | 318 | pub const DXC_VERSION_INFO_FLAGS_NONE: u32 = 0; 319 | pub const DXC_VERSION_INFO_FLAGS_DEBUG: u32 = 1; // Matches VS_FF_DEBUG 320 | pub const DXC_VERSION_INFO_FLAGS_INTERNAL: u32 = 2; // Internal Validator (non-signing) 321 | 322 | interfaces! { 323 | #[uuid("b04f5b50-2059-4f12-a8ff-a1e0cde1cc7e")] 324 | pub(crate) unsafe interface IDxcVersionInfo: IUnknown { 325 | pub(crate) fn get_version(&self, major: *mut u32, minor: *mut u32) -> HRESULT; 326 | pub(crate) fn get_flags(&self, flags: *mut u32) -> HRESULT; 327 | } 328 | 329 | #[uuid("fb6904c4-42f0-4b62-9c46-983af7da7c83")] 330 | pub(crate) unsafe interface IDxcVersionInfo2: IUnknown { 331 | pub(crate) fn get_commit_info( 332 | &self, 333 | commit_count: *mut u32, 334 | commit_hash: *mut *mut u8, 335 | ) -> HRESULT; 336 | } 337 | } 338 | 339 | pub const CLSID_DxcCompiler: IID = IID { 340 | data1: 0x73e22d93, 341 | data2: 0xe6ce, 342 | data3: 0x47f3, 343 | data4: [0xb5, 0xbf, 0xf0, 0x66, 0x4f, 0x39, 0xc1, 0xb0], 344 | }; 345 | pub const CLSID_DxcLinker: IID = IID { 346 | data1: 0xef6a8087, 347 | data2: 0xb0ea, 348 | data3: 0x4d56, 349 | data4: [0x9e, 0x45, 0xd0, 0x7e, 0x1a, 0x8b, 0x78, 0x6], 350 | }; 351 | pub const CLSID_DxcDiaDataSource: IID = IID { 352 | data1: 0xcd1f6b73, 353 | data2: 0x2ab0, 354 | data3: 0x484d, 355 | data4: [0x8e, 0xdc, 0xeb, 0xe7, 0xa4, 0x3c, 0xa0, 0x9f], 356 | }; 357 | pub const CLSID_DxcLibrary: IID = IID { 358 | data1: 0x6245d6af, 359 | data2: 0x66e0, 360 | data3: 0x48fd, 361 | data4: [0x80, 0xb4, 0x4d, 0x27, 0x17, 0x96, 0x74, 0x8c], 362 | }; 363 | pub const CLSID_DxcValidator: IID = IID { 364 | data1: 0x8ca3e215, 365 | data2: 0xf728, 366 | data3: 0x4cf3, 367 | data4: [0x8c, 0xdd, 0x88, 0xaf, 0x91, 0x75, 0x87, 0xa1], 368 | }; 369 | pub const CLSID_DxcAssembler: IID = IID { 370 | data1: 0xd728db68, 371 | data2: 0xf903, 372 | data3: 0x4f80, 373 | data4: [0x94, 0xcd, 0xdc, 0xcf, 0x76, 0xec, 0x71, 0x51], 374 | }; 375 | pub const CLSID_DxcContainerReflection: IID = IID { 376 | data1: 0xb9f54489, 377 | data2: 0x55b8, 378 | data3: 0x400c, 379 | data4: [0xba, 0x3a, 0x16, 0x75, 0xe4, 0x72, 0x8b, 0x91], 380 | }; 381 | pub const CLSID_DxcOptimizer: IID = IID { 382 | data1: 0xae2cd79f, 383 | data2: 0xcc22, 384 | data3: 0x453f, 385 | data4: [0x9b, 0x6b, 0xb1, 0x24, 0xe7, 0xa5, 0x20, 0x4c], 386 | }; 387 | pub const CLSID_DxcContainerBuilder: IID = IID { 388 | data1: 0x94134294, 389 | data2: 0x411f, 390 | data3: 0x4574, 391 | data4: [0xb4, 0xd0, 0x87, 0x41, 0xe2, 0x52, 0x40, 0xd2], 392 | }; 393 | -------------------------------------------------------------------------------- /src/intellisense/ffi.rs: -------------------------------------------------------------------------------- 1 | use crate::os::{BSTR, HRESULT, LPCSTR, LPSTR}; 2 | use bitflags::bitflags; 3 | use com::{interfaces, interfaces::IUnknown, AbiTransferable, IID}; 4 | 5 | /// Manual implementation of: 6 | /// ```ignore 7 | /// unsafe impl AbiTransferable for T { 8 | /// type Abi = T::Bits; 9 | /// // ... 10 | /// } 11 | /// ``` 12 | macro_rules! abi_transferable { 13 | ($t:ident) => { 14 | unsafe impl AbiTransferable for $t { 15 | type Abi = u32; 16 | 17 | fn get_abi(&self) -> Self::Abi { 18 | self.bits() 19 | } 20 | 21 | fn set_abi(&mut self) -> *mut Self::Abi { 22 | &mut self.bits() 23 | } 24 | } 25 | }; 26 | } 27 | 28 | bitflags! { 29 | #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] 30 | pub struct DxcGlobalOptions : u32 { 31 | const NONE = 0x0; 32 | const THREAD_BACKGROUND_PRIORITY_FOR_INDEXING = 0x1; 33 | const THREAD_BACKGROUND_PRIORITY_FOR_EDITING = 0x2; 34 | const THREAD_BACKGROUND_PRIORITY_FOR_ALL 35 | = DxcGlobalOptions::THREAD_BACKGROUND_PRIORITY_FOR_INDEXING.bits() 36 | | DxcGlobalOptions::THREAD_BACKGROUND_PRIORITY_FOR_EDITING.bits(); 37 | } 38 | } 39 | abi_transferable!(DxcGlobalOptions); 40 | 41 | bitflags! { 42 | #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] 43 | pub struct DxcDiagnosticSeverity : u32 { 44 | const IGNORED = 0; 45 | const NOTE = 1; 46 | const WARNING = 2; 47 | const ERROR = 3; 48 | const FATAL = 4; 49 | } 50 | } 51 | 52 | bitflags! { 53 | #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] 54 | pub struct DxcTokenKind : u32 { 55 | const PUNCTUATION = 0; 56 | const KEYWORD = 1; 57 | const IDENTIFIER = 2; 58 | const LITERAL = 3; 59 | const COMMENT = 4; 60 | const UNKNOWN = 5; 61 | const BUILT_IN_TYPE = 6; 62 | } 63 | } 64 | 65 | bitflags! { 66 | #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] 67 | pub struct DxcTypeKind : u32 { 68 | const Invalid = 0; // Represents an invalid type (e.g., where no type is available). 69 | const Unexposed = 1; // A type whose specific kind is not exposed via this interface. 70 | // Builtin types 71 | const Void = 2; 72 | const Bool = 3; 73 | const Char_U = 4; 74 | const UChar = 5; 75 | const Char16 = 6; 76 | const Char32 = 7; 77 | const UShort = 8; 78 | const UInt = 9; 79 | const ULong = 10; 80 | const ULongLong = 11; 81 | const UInt128 = 12; 82 | const Char_S = 13; 83 | const SChar = 14; 84 | const WChar = 15; 85 | const Short = 16; 86 | const Int = 17; 87 | const Long = 18; 88 | const LongLong = 19; 89 | const Int128 = 20; 90 | const Float = 21; 91 | const Double = 22; 92 | const LongDouble = 23; 93 | const NullPtr = 24; 94 | const Overload = 25; 95 | const Dependent = 26; 96 | const ObjCId = 27; 97 | const ObjCClass = 28; 98 | const ObjCSel = 29; 99 | const FirstBuiltin = DxcTypeKind::Void.bits(); 100 | const LastBuiltin = DxcTypeKind::ObjCSel.bits(); 101 | 102 | const Complex = 100; 103 | const Pointer = 101; 104 | const BlockPointer = 102; 105 | const LValueReference = 103; 106 | const RValueReference = 104; 107 | const Record = 105; 108 | const Enum = 106; 109 | const Typedef = 107; 110 | const ObjCInterface = 108; 111 | const ObjCObjectPointer = 109; 112 | const FunctionNoProto = 110; 113 | const FunctionProto = 111; 114 | const ConstantArray = 112; 115 | const Vector = 113; 116 | const IncompleteArray = 114; 117 | const VariableArray = 115; 118 | const DependentSizedArray = 116; 119 | const MemberPointer = 117; 120 | } 121 | } 122 | 123 | bitflags! { 124 | #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] 125 | pub struct DxcCursorFormatting : u32 { 126 | const DEFAULT = 0x0; 127 | const USE_LANGUAGE_OPTIONS = 0x1; 128 | const SUPPRESS_SPECIFIERS = 0x2; 129 | const SUPPRESS_TAG_KEYWORD = 0x4; 130 | const INCLUDE_NAMESPACE_KEYWORD = 0x8; 131 | } 132 | } 133 | abi_transferable!(DxcCursorFormatting); 134 | 135 | bitflags! { 136 | #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] 137 | pub struct DxcTranslationUnitFlags : u32 { 138 | const NONE = 0x0; 139 | const DETAILED_PREPROCESSING_RECORD = 0x01; 140 | const INCOMPLETE = 0x02; 141 | const PRECOMPILED_PREAMBLE = 0x04; 142 | const CACHE_COMPLETION_RESULTS = 0x08; 143 | const FOR_SERIALIZATION = 0x10; 144 | const CXX_CHAINED_PCH = 0x20; 145 | const SKIP_FUNCTION_BODIES = 0x40; 146 | const INCLUDE_BRIEF_COMMENTS_IN_CODE_COMPLETION = 0x80; 147 | const USE_CALLER_THREAD = 0x800; 148 | } 149 | } 150 | abi_transferable!(DxcTranslationUnitFlags); 151 | 152 | bitflags! { 153 | #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] 154 | pub struct DxcDiagnosticDisplayOptions : u32 { 155 | const DISPLAY_SOURCE_LOCATION = 0x01; 156 | const DISPLAY_COLUMN = 0x02; 157 | const DISPLAY_SOURCE_RANGES = 0x04; 158 | const DISPLAY_OPTION = 0x08; 159 | const DISPLAY_CATEGORY_ID = 0x10; 160 | const DISPLAY_CATEGORY_NAME = 0x20; 161 | const DISPLAY_SEVERITY = 0x200; 162 | } 163 | } 164 | abi_transferable!(DxcDiagnosticDisplayOptions); 165 | 166 | bitflags! { 167 | #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] 168 | pub struct DxcCursorKindFlags : u32 { 169 | const NONE = 0; 170 | const DECLARATION = 0x1; 171 | const REFERENCE = 0x2; 172 | const EXPRESSION = 0x4; 173 | const STATEMENT = 0x8; 174 | const ATTRIBUTE = 0x10; 175 | const INVALID = 0x20; 176 | const TRANSLATION_UNIT = 0x40; 177 | const PREPROCESSING = 0x80; 178 | const UNEXPOSED = 0x100; 179 | } 180 | } 181 | 182 | bitflags! { 183 | #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] 184 | pub struct DxcCursorKind : u32 { 185 | const UNEXPOSED_DECL = 1; 186 | const STRUCT_DECL = 2; 187 | const UNION_DECL = 3; 188 | const CLASS_DECL = 4; 189 | const ENUM_DECL = 5; 190 | const FIELD_DECL = 6; 191 | const ENUM_CONSTANT_DECL = 7; 192 | const FUNCTION_DECL = 8; 193 | const VAR_DECL = 9; 194 | const PARM_DECL = 10; 195 | const OBJ_C_INTERFACE_DECL = 11; 196 | const OBJ_C_CATEGORY_DECL = 12; 197 | const OBJ_C_PROTOCOL_DECL = 13; 198 | const OBJ_C_PROPERTY_DECL = 14; 199 | const OBJ_C_IVAR_DECL = 15; 200 | const OBJ_C_INSTANCE_METHOD_DECL = 16; 201 | const OBJ_C_CLASS_METHOD_DECL = 17; 202 | const OBJ_C_IMPLEMENTATION_DECL = 18; 203 | const OBJ_C_CATEGORY_IMPL_DECL = 19; 204 | const TYPEDEF_DECL = 20; 205 | const CXX_METHOD = 21; 206 | const NAMESPACE = 22; 207 | const LINKAGE_SPEC = 23; 208 | const CONSTRUCTOR = 24; 209 | const DESTRUCTOR = 25; 210 | const CONVERSION_FUNCTION = 26; 211 | const TEMPLATE_TYPE_PARAMETER = 27; 212 | const NON_TYPE_TEMPLATE_PARAMETER = 28; 213 | const TEMPLATE_TEMPLATE_PARAMETER = 29; 214 | const FUNCTION_TEMPLATE = 30; 215 | const CLASS_TEMPLATE = 31; 216 | const CLASS_TEMPLATE_PARTIAL_SPECIALIZATION = 32; 217 | const NAMESPACE_ALIAS = 33; 218 | const USING_DIRECTIVE = 34; 219 | const USING_DECLARATION = 35; 220 | const TYPE_ALIAS_DECL = 36; 221 | const OBJ_C_SYNTHESIZE_DECL = 37; 222 | const OBJ_C_DYNAMIC_DECL = 38; 223 | const CXX_ACCESS_SPECIFIER = 39; 224 | 225 | const FIRST_DECL = DxcCursorKind::UNEXPOSED_DECL.bits(); 226 | const LAST_DECL = DxcCursorKind::CXX_ACCESS_SPECIFIER.bits(); 227 | 228 | const FIRST_REF = 40; 229 | const OBJ_C_SUPER_CLASS_REF = 40; 230 | const OBJ_C_PROTOCOL_REF = 41; 231 | const OBJ_C_CLASS_REF = 42; 232 | const TYPE_REF = 43; 233 | const CXX_BASE_SPECIFIER = 44; 234 | const TEMPLATE_REF = 45; 235 | const NAMESPACE_REF = 46; 236 | const MEMBER_REF = 47; 237 | const LABEL_REF = 48; 238 | const OVERLOADED_DECL_REF = 49; 239 | const VARIABLE_REF = 50; 240 | const LAST_REF = DxcCursorKind::VARIABLE_REF.bits(); 241 | const FIRST_INVALID = 70; 242 | const INVALID_FILE = 70; 243 | const NO_DECL_FOUND = 71; 244 | const NOT_IMPLEMENTED = 72; 245 | const INVALID_CODE = 73; 246 | const LAST_INVALID = DxcCursorKind::INVALID_CODE.bits(); 247 | const FIRST_EXPR = 100; 248 | const UNEXPOSED_EXPR = 100; 249 | const DECL_REF_EXPR = 101; 250 | const MEMBER_REF_EXPR = 102; 251 | const CALL_EXPR = 103; 252 | const OBJ_C_MESSAGE_EXPR = 104; 253 | const BLOCK_EXPR = 105; 254 | const INTEGER_LITERAL = 106; 255 | const FLOATING_LITERAL = 107; 256 | const IMAGINARY_LITERAL = 108; 257 | const STRING_LITERAL = 109; 258 | const CHARACTER_LITERAL = 110; 259 | const PAREN_EXPR = 111; 260 | const UNARY_OPERATOR = 112; 261 | const ARRAY_SUBSCRIPT_EXPR = 113; 262 | const BINARY_OPERATOR = 114; 263 | const COMPOUND_ASSIGN_OPERATOR = 115; 264 | const CONDITIONAL_OPERATOR = 116; 265 | const C_STYLE_CAST_EXPR = 117; 266 | const COMPOUND_LITERAL_EXPR = 118; 267 | const INIT_LIST_EXPR = 119; 268 | const ADDR_LABEL_EXPR = 120; 269 | const STMT_EXPR = 121; 270 | const GENERIC_SELECTION_EXPR = 122; 271 | const GNU_NULL_EXPR = 123; 272 | const CXX_STATIC_CAST_EXPR = 124; 273 | const CXX_DYNAMIC_CAST_EXPR = 125; 274 | const CXX_REINTERPRET_CAST_EXPR = 126; 275 | const CXX_CONST_CAST_EXPR = 127; 276 | const CXX_FUNCTIONAL_CAST_EXPR = 128; 277 | const CXX_TYPEID_EXPR = 129; 278 | const CXX_BOOL_LITERAL_EXPR = 130; 279 | const CXX_NULL_PTR_LITERAL_EXPR = 131; 280 | const CXX_THIS_EXPR = 132; 281 | const CXX_THROW_EXPR = 133; 282 | const CXX_NEW_EXPR = 134; 283 | const CXX_DELETE_EXPR = 135; 284 | const UNARY_EXPR = 136; 285 | const OBJ_C_STRING_LITERAL = 137; 286 | const OBJ_C_ENCODE_EXPR = 138; 287 | const OBJ_C_SELECTOR_EXPR = 139; 288 | const OBJ_C_PROTOCOL_EXPR = 140; 289 | const OBJ_C_BRIDGED_CAST_EXPR = 141; 290 | const PACK_EXPANSION_EXPR = 142; 291 | const SIZE_OF_PACK_EXPR = 143; 292 | const LAMBDA_EXPR = 144; 293 | const OBJ_C_BOOL_LITERAL_EXPR = 145; 294 | const OBJ_C_SELF_EXPR = 146; 295 | const LAST_EXPR = DxcCursorKind::OBJ_C_SELF_EXPR.bits(); 296 | const FIRST_STMT = 200; 297 | const UNEXPOSED_STMT = 200; 298 | const LABEL_STMT = 201; 299 | const COMPOUND_STMT = 202; 300 | const CASE_STMT = 203; 301 | const DEFAULT_STMT = 204; 302 | const IF_STMT = 205; 303 | const SWITCH_STMT = 206; 304 | const WHILE_STMT = 207; 305 | const DO_STMT = 208; 306 | const FOR_STMT = 209; 307 | const GOTO_STMT = 210; 308 | const INDIRECT_GOTO_STMT = 211; 309 | const CONTINUE_STMT = 212; 310 | const BREAK_STMT = 213; 311 | const RETURN_STMT = 214; 312 | const GCC_ASM_STMT = 215; 313 | const ASM_STMT = DxcCursorKind::GCC_ASM_STMT.bits(); 314 | 315 | const OBJ_C_AT_TRY_STMT = 216; 316 | const OBJ_C_AT_CATCH_STMT = 217; 317 | const OBJ_C_AT_FINALLY_STMT = 218; 318 | const OBJ_C_AT_THROW_STMT = 219; 319 | const OBJ_C_AT_SYNCHRONIZED_STMT = 220; 320 | const OBJ_C_AUTORELEASE_POOL_STMT = 221; 321 | const OBJ_C_FOR_COLLECTION_STMT = 222; 322 | const CXX_CATCH_STMT = 223; 323 | const CXX_TRY_STMT = 224; 324 | const CXX_FOR_RANGE_STMT = 225; 325 | const SEH_TRY_STMT = 226; 326 | const SEH_EXCEPT_STMT = 227; 327 | const SEH_FINALLY_STMT = 228; 328 | const MS_ASM_STMT = 229; 329 | const NULL_STMT = 230; 330 | const DECL_STMT = 231; 331 | const OMP_PARALLEL_DIRECTIVE = 232; 332 | const OMP_SIMD_DIRECTIVE = 233; 333 | const OMP_FOR_DIRECTIVE = 234; 334 | const OMP_SECTIONS_DIRECTIVE = 235; 335 | const OMP_SECTION_DIRECTIVE = 236; 336 | const OMP_SINGLE_DIRECTIVE = 237; 337 | const OMP_PARALLEL_FOR_DIRECTIVE = 238; 338 | const OMP_PARALLEL_SECTIONS_DIRECTIVE = 239; 339 | const OMP_TASK_DIRECTIVE = 240; 340 | const OMP_MASTER_DIRECTIVE = 241; 341 | const OMP_CRITICAL_DIRECTIVE = 242; 342 | const OMP_TASKYIELD_DIRECTIVE = 243; 343 | const OMP_BARRIER_DIRECTIVE = 244; 344 | const OMP_TASKWAIT_DIRECTIVE = 245; 345 | const OMP_FLUSH_DIRECTIVE = 246; 346 | const SEH_LEAVE_STMT = 247; 347 | const OMP_ORDERED_DIRECTIVE = 248; 348 | const OMP_ATOMIC_DIRECTIVE = 249; 349 | const OMP_FOR_SIMD_DIRECTIVE = 250; 350 | const OMP_PARALLEL_FOR_SIMD_DIRECTIVE = 251; 351 | const OMP_TARGET_DIRECTIVE = 252; 352 | const OMP_TEAMS_DIRECTIVE = 253; 353 | const OMP_TASKGROUP_DIRECTIVE = 254; 354 | const OMP_CANCELLATION_POINT_DIRECTIVE = 255; 355 | const OMP_CANCEL_DIRECTIVE = 256; 356 | const LAST_STMT = DxcCursorKind::OMP_CANCEL_DIRECTIVE.bits(); 357 | 358 | const TRANSLATION_UNIT = 300; 359 | 360 | const FIRST_ATTR = 400; 361 | const UNEXPOSED_ATTR = 400; 362 | 363 | const IB_ACTION_ATTR = 401; 364 | const IB_OUTLET_ATTR = 402; 365 | const IB_OUTLET_COLLECTION_ATTR = 403; 366 | const CXX_FINAL_ATTR = 404; 367 | const CXX_OVERRIDE_ATTR = 405; 368 | const ANNOTATE_ATTR = 406; 369 | const ASM_LABEL_ATTR = 407; 370 | const PACKED_ATTR = 408; 371 | const PURE_ATTR = 409; 372 | const CONST_ATTR = 410; 373 | const NO_DUPLICATE_ATTR = 411; 374 | const CUDA_CONSTANT_ATTR = 412; 375 | const CUDA_DEVICE_ATTR = 413; 376 | const CUDA_GLOBAL_ATTR = 414; 377 | const CUDA_HOST_ATTR = 415; 378 | const CUDA_SHARED_ATTR = 416; 379 | const LAST_ATTR = DxcCursorKind::CUDA_SHARED_ATTR.bits(); 380 | 381 | const PREPROCESSING_DIRECTIVE = 500; 382 | const MACRO_DEFINITION = 501; 383 | const MACRO_EXPANSION = 502; 384 | const MACRO_INSTANTIATION = DxcCursorKind::MACRO_EXPANSION.bits(); 385 | const INCLUSION_DIRECTIVE = 503; 386 | const FIRST_PREPROCESSING = DxcCursorKind::PREPROCESSING_DIRECTIVE.bits(); 387 | const LAST_PREPROCESSING = DxcCursorKind::INCLUSION_DIRECTIVE.bits(); 388 | 389 | const MODULE_IMPORT_DECL = 600; 390 | const FIRST_EXTRA_DECL = DxcCursorKind::MODULE_IMPORT_DECL.bits(); 391 | const LAST_EXTRA_DECL = DxcCursorKind::MODULE_IMPORT_DECL.bits(); 392 | } 393 | } 394 | 395 | interfaces! { 396 | #[uuid("4f76b234-3659-4d33-99b0-3b0db994b564")] 397 | pub(crate) unsafe interface IDxcDiagnostic: IUnknown { 398 | pub(crate) unsafe fn format_diagnostic( 399 | &self, 400 | options: DxcDiagnosticDisplayOptions, 401 | result: *mut LPSTR, 402 | ) -> HRESULT; 403 | pub(crate) unsafe fn get_severity(&self, result: *mut DxcDiagnosticSeverity) -> HRESULT; 404 | pub(crate) unsafe fn get_location( 405 | &self, 406 | result: *mut Option, 407 | ) -> HRESULT; 408 | pub(crate) unsafe fn get_spelling(&self, result: *mut LPSTR) -> HRESULT; 409 | pub(crate) unsafe fn get_category_text(&self, result: *mut LPSTR) -> HRESULT; 410 | pub(crate) unsafe fn get_num_ranges(&self, result: *mut u32) -> HRESULT; 411 | pub(crate) unsafe fn get_range_at( 412 | &self, 413 | index: u32, 414 | result: *mut Option, 415 | ) -> HRESULT; 416 | pub(crate) unsafe fn get_num_fix_its(&self, result: *mut u32) -> HRESULT; 417 | pub(crate) unsafe fn get_fix_it_at( 418 | &self, 419 | index: u32, 420 | replacement_range: *mut Option, 421 | text: *mut LPSTR, 422 | ) -> HRESULT; 423 | } 424 | 425 | #[uuid("0c364d65-df44-4412-888e-4e552fc5e3d6")] 426 | pub(crate) unsafe interface IDxcInclusion: IUnknown { 427 | pub(crate) unsafe fn get_included_file(&self, result: *mut Option) -> HRESULT; 428 | pub(crate) unsafe fn get_stack_length(&self, result: *mut u32) -> HRESULT; 429 | pub(crate) unsafe fn get_stack_item( 430 | &self, 431 | index: u32, 432 | result: *mut Option, 433 | ) -> HRESULT; 434 | } 435 | 436 | #[uuid("7f90b9ff-a275-4932-97d8-3cfd234482a2")] 437 | pub(crate) unsafe interface IDxcToken: IUnknown { 438 | pub(crate) unsafe fn get_kind(&self, value: *mut DxcTokenKind) -> HRESULT; 439 | pub(crate) unsafe fn get_location(&self, value: *mut Option) 440 | -> HRESULT; 441 | pub(crate) unsafe fn get_extent(&self, value: *mut Option) -> HRESULT; 442 | pub(crate) unsafe fn get_spelling(&self, value: *mut LPSTR) -> HRESULT; 443 | } 444 | 445 | #[uuid("2ec912fd-b144-4a15-ad0d-1c5439c81e46")] 446 | pub(crate) unsafe interface IDxcType: IUnknown { 447 | pub(crate) unsafe fn get_spelling(&self, result: *mut LPSTR) -> HRESULT; 448 | pub(crate) unsafe fn is_equal_to(&self, other: IDxcType, result: *mut bool) -> HRESULT; 449 | pub(crate) unsafe fn get_kind(&self, result: *mut DxcCursorKind) -> HRESULT; 450 | } 451 | 452 | #[uuid("8e7ddf1c-d7d3-4d69-b286-85fccba1e0cf")] 453 | pub(crate) unsafe interface IDxcSourceLocation: IUnknown { 454 | pub(crate) unsafe fn is_equal_to( 455 | &self, 456 | other: IDxcSourceLocation, 457 | result: *mut bool, 458 | ) -> HRESULT; 459 | pub(crate) unsafe fn get_spelling_location( 460 | &self, 461 | file: *mut Option, 462 | line: *mut u32, 463 | col: *mut u32, 464 | offset: *mut u32, 465 | ) -> HRESULT; 466 | pub(crate) unsafe fn is_null(&self, result: *mut bool) -> HRESULT; 467 | } 468 | 469 | #[uuid("f1359b36-a53f-4e81-b514-b6b84122a13f")] 470 | pub(crate) unsafe interface IDxcSourceRange: IUnknown { 471 | pub(crate) unsafe fn is_null(&self, value: *mut bool) -> HRESULT; 472 | pub(crate) unsafe fn get_start(&self, value: *mut Option) -> HRESULT; 473 | pub(crate) unsafe fn get_end(&self, value: *mut Option) -> HRESULT; 474 | pub(crate) unsafe fn get_offsets( 475 | &self, 476 | start_offset: *mut u32, 477 | end_offset: *mut u32, 478 | ) -> HRESULT; 479 | } 480 | 481 | #[uuid("1467b985-288d-4d2a-80c1-ef89c42c40bc")] 482 | pub(crate) unsafe interface IDxcCursor: IUnknown { 483 | pub(crate) unsafe fn get_extent(&self, range: *mut Option) -> HRESULT; 484 | pub(crate) unsafe fn get_location( 485 | &self, 486 | result: *mut Option, 487 | ) -> HRESULT; 488 | pub(crate) unsafe fn get_kind(&self, result: *mut DxcCursorKind) -> HRESULT; 489 | pub(crate) unsafe fn get_kind_flags(&self, result: *mut DxcCursorKindFlags) -> HRESULT; 490 | pub(crate) unsafe fn get_semantic_parent(&self, result: *mut Option) 491 | -> HRESULT; 492 | pub(crate) unsafe fn get_lexical_parent(&self, result: *mut Option) -> HRESULT; 493 | pub(crate) unsafe fn get_cursor_type(&self, result: *mut Option) -> HRESULT; 494 | pub(crate) unsafe fn get_num_arguments(&self, result: *mut i32) -> HRESULT; 495 | pub(crate) unsafe fn get_argument_at( 496 | &self, 497 | index: i32, 498 | result: *mut Option, 499 | ) -> HRESULT; 500 | pub(crate) unsafe fn get_referenced_cursor( 501 | &self, 502 | result: *mut Option, 503 | ) -> HRESULT; 504 | pub(crate) unsafe fn get_definition_cursor( 505 | &self, 506 | result: *mut Option, 507 | ) -> HRESULT; 508 | pub(crate) unsafe fn find_references_in_file( 509 | &self, 510 | file: IDxcFile, 511 | skip: u32, 512 | top: u32, 513 | result_length: *mut u32, 514 | result: *mut *mut IDxcCursor, 515 | ) -> HRESULT; 516 | pub(crate) unsafe fn get_spelling(&self, result: *mut LPSTR) -> HRESULT; 517 | pub(crate) unsafe fn is_equal_to(&self, other: IDxcCursor, result: *mut bool) -> HRESULT; 518 | pub(crate) unsafe fn is_null(&self, result: *mut bool) -> HRESULT; 519 | pub(crate) unsafe fn is_definition(&self, result: *mut bool) -> HRESULT; 520 | pub(crate) unsafe fn get_display_name(&self, result: *mut BSTR) -> HRESULT; 521 | pub(crate) unsafe fn get_qualified_name( 522 | &self, 523 | include_template_args: bool, 524 | result: *mut BSTR, 525 | ) -> HRESULT; 526 | pub(crate) unsafe fn get_formatted_name( 527 | &self, 528 | formatting: DxcCursorFormatting, 529 | result: *mut BSTR, 530 | ) -> HRESULT; 531 | pub(crate) unsafe fn get_children( 532 | &self, 533 | skip: u32, 534 | top: u32, 535 | result_length: *mut u32, 536 | result: *mut *mut IDxcCursor, 537 | ) -> HRESULT; 538 | pub(crate) unsafe fn get_snapped_child( 539 | &self, 540 | location: IDxcSourceLocation, 541 | result: *mut Option, 542 | ) -> HRESULT; 543 | } 544 | 545 | #[uuid("8ec00f98-07d0-4e60-9d7c-5a50b5b0017f")] 546 | pub(crate) unsafe interface IDxcUnsavedFile: IUnknown { 547 | pub(crate) unsafe fn get_file_name(&self, file_name: *mut LPSTR) -> HRESULT; 548 | pub(crate) unsafe fn get_contents(&self, contents: *mut LPSTR) -> HRESULT; 549 | pub(crate) unsafe fn get_length(&self, length: *mut u32) -> HRESULT; 550 | } 551 | 552 | #[uuid("bb2fca9e-1478-47ba-b08c-2c502ada4895")] 553 | pub(crate) unsafe interface IDxcFile: IUnknown { 554 | pub(crate) unsafe fn get_name(&self, result: *mut LPSTR) -> HRESULT; 555 | pub(crate) unsafe fn is_equal_to(&self, other: IDxcFile, result: *mut bool) -> HRESULT; 556 | } 557 | 558 | #[uuid("9677dee0-c0e5-46a1-8b40-3db3168be63d")] 559 | pub(crate) unsafe interface IDxcTranslationUnit: IUnknown { 560 | pub(crate) unsafe fn get_cursor(&self, cursor: *mut Option) -> HRESULT; 561 | pub(crate) unsafe fn tokenize( 562 | &self, 563 | range: IDxcSourceRange, 564 | tokens: *mut *mut IDxcToken, 565 | token_count: *mut u32, 566 | ) -> HRESULT; 567 | pub(crate) unsafe fn get_location( 568 | &self, 569 | file: IDxcFile, 570 | line: u32, 571 | column: u32, 572 | result: *mut Option, 573 | ) -> HRESULT; 574 | pub(crate) unsafe fn get_num_diagnostics(&self, value: *mut u32) -> HRESULT; 575 | pub(crate) unsafe fn get_diagnostic( 576 | &self, 577 | index: u32, 578 | value: *mut Option, 579 | ) -> HRESULT; 580 | pub(crate) unsafe fn get_file( 581 | &self, 582 | name: *const u8, 583 | result: *mut Option, 584 | ) -> HRESULT; 585 | pub(crate) unsafe fn get_file_name(&self, result: *mut LPSTR) -> HRESULT; 586 | pub(crate) unsafe fn reparse( 587 | &self, 588 | unsaved_files: *mut Option, 589 | num_unsaved_files: u32, 590 | ) -> HRESULT; 591 | pub(crate) unsafe fn get_cursor_for_location( 592 | &self, 593 | location: IDxcSourceLocation, 594 | result: *mut Option, 595 | ) -> HRESULT; 596 | pub(crate) unsafe fn get_location_for_offset( 597 | &self, 598 | file: IDxcFile, 599 | offset: u32, 600 | result: *mut Option, 601 | ) -> HRESULT; 602 | pub(crate) unsafe fn get_skipped_ranges( 603 | &self, 604 | file: IDxcFile, 605 | result_count: *mut u32, 606 | result: *mut *mut IDxcSourceRange, 607 | ) -> HRESULT; 608 | pub(crate) unsafe fn get_diagnostic_details( 609 | &self, 610 | index: u32, 611 | options: DxcDiagnosticDisplayOptions, 612 | error_code: *mut u32, 613 | error_line: *mut u32, 614 | error_column: *mut u32, 615 | error_file: *mut BSTR, 616 | error_offset: *mut u32, 617 | error_length: *mut u32, 618 | error_message: *mut BSTR, 619 | ) -> HRESULT; 620 | pub(crate) unsafe fn get_inclusion_list( 621 | &self, 622 | result_count: *mut u32, 623 | result: *mut *mut IDxcInclusion, 624 | ) -> HRESULT; 625 | } 626 | 627 | #[uuid("937824a0-7f5a-4815-9b0a-7cc0424f4173")] 628 | pub(crate) unsafe interface IDxcIndex: IUnknown { 629 | pub(crate) unsafe fn set_global_options(&self, options: DxcGlobalOptions) -> HRESULT; 630 | pub(crate) unsafe fn get_global_options(&self, options: *mut DxcGlobalOptions) -> HRESULT; 631 | pub(crate) unsafe fn parse_translation_unit( 632 | &self, 633 | source_filename: *const u8, 634 | command_line_args: *const *const u8, 635 | num_command_line_args: i32, 636 | // unsaved_files: *const *const dyn IDxcUnsavedFile, 637 | unsaved_files: *const IDxcUnsavedFile, 638 | num_unsaved_files: u32, 639 | options: DxcTranslationUnitFlags, 640 | translation_unit: *mut Option, 641 | ) -> HRESULT; 642 | } 643 | 644 | #[uuid("b1f99513-46d6-4112-8169-dd0d6053f17d")] 645 | pub(crate) unsafe interface IDxcIntelliSense: IUnknown { 646 | pub(crate) unsafe fn create_index(&self, index: *mut Option) -> HRESULT; 647 | pub(crate) unsafe fn get_null_location( 648 | &self, 649 | location: *mut Option, 650 | ) -> HRESULT; 651 | pub(crate) unsafe fn get_null_range( 652 | &self, 653 | location: *mut Option, 654 | ) -> HRESULT; 655 | pub(crate) unsafe fn get_range( 656 | &self, 657 | start: IDxcSourceLocation, 658 | end: IDxcSourceLocation, 659 | location: *mut Option, 660 | ) -> HRESULT; 661 | pub(crate) unsafe fn get_default_diagnostic_display_options( 662 | &self, 663 | value: *mut DxcDiagnosticDisplayOptions, 664 | ) -> HRESULT; 665 | pub(crate) unsafe fn get_default_editing_tu_options( 666 | &self, 667 | value: *mut DxcTranslationUnitFlags, 668 | ) -> HRESULT; 669 | pub(crate) unsafe fn create_unsaved_file( 670 | &self, 671 | file_name: LPCSTR, 672 | contents: LPCSTR, 673 | content_length: u32, 674 | result: *mut Option, 675 | ) -> HRESULT; 676 | } 677 | } 678 | 679 | pub const CLSID_DxcIntelliSense: IID = IID { 680 | data1: 0x3047833c, 681 | data2: 0xd1c0, 682 | data3: 0x4b8e, 683 | data4: [0x9d, 0x40, 0x10, 0x28, 0x78, 0x60, 0x59, 0x85], 684 | }; 685 | -------------------------------------------------------------------------------- /src/intellisense/mod.rs: -------------------------------------------------------------------------------- 1 | mod ffi; 2 | mod wrapper; 3 | 4 | pub use ffi::*; 5 | pub use wrapper::*; 6 | -------------------------------------------------------------------------------- /src/intellisense/wrapper.rs: -------------------------------------------------------------------------------- 1 | use com::Interface; 2 | 3 | use crate::intellisense::ffi::*; 4 | use crate::os::{CoTaskMemFree, BSTR, LPSTR}; 5 | use crate::utils::Result; 6 | use crate::wrapper::Dxc; 7 | use std::ffi::CString; 8 | use std::mem::ManuallyDrop; 9 | 10 | pub struct DxcIntellisense { 11 | inner: IDxcIntelliSense, 12 | } 13 | 14 | impl DxcIntellisense { 15 | fn new(inner: IDxcIntelliSense) -> Self { 16 | Self { inner } 17 | } 18 | 19 | pub fn get_default_editing_tu_options(&self) -> Result { 20 | let mut options: DxcTranslationUnitFlags = DxcTranslationUnitFlags::NONE; 21 | unsafe { self.inner.get_default_editing_tu_options(&mut options) } 22 | .result_with_success(options) 23 | } 24 | 25 | pub fn create_index(&self) -> Result { 26 | let mut index = None; 27 | unsafe { self.inner.create_index(&mut index) }.result()?; 28 | Ok(DxcIndex::new(index.unwrap())) 29 | } 30 | 31 | pub fn create_unsaved_file(&self, file_name: &str, contents: &str) -> Result { 32 | let c_file_name = CString::new(file_name).expect("Failed to convert `file_name`"); 33 | let c_contents = CString::new(contents).expect("Failed to convert `contents`"); 34 | 35 | let mut file = None; 36 | unsafe { 37 | self.inner.create_unsaved_file( 38 | c_file_name.as_ptr(), 39 | c_contents.as_ptr(), 40 | contents.len() as u32, 41 | &mut file, 42 | ) 43 | } 44 | .result()?; 45 | Ok(DxcUnsavedFile::new(file.unwrap())) 46 | } 47 | } 48 | 49 | pub struct DxcIndex { 50 | inner: IDxcIndex, 51 | } 52 | 53 | impl DxcIndex { 54 | fn new(inner: IDxcIndex) -> Self { 55 | Self { inner } 56 | } 57 | } 58 | 59 | impl DxcIndex { 60 | pub fn parse_translation_unit( 61 | &self, 62 | source_filename: &str, 63 | args: &[&str], 64 | unsaved_files: &[&DxcUnsavedFile], 65 | options: DxcTranslationUnitFlags, 66 | ) -> Result { 67 | let c_source_filename = 68 | CString::new(source_filename).expect("Failed to convert `source_filename`"); 69 | 70 | let uf = unsaved_files 71 | .iter() 72 | .map(|unsaved_file| unsaved_file.inner.clone()) 73 | .collect::>(); 74 | 75 | let mut c_args: Vec = vec![]; 76 | let mut cliargs = vec![]; 77 | 78 | for arg in args.iter() { 79 | let c_arg = CString::new(*arg).expect("Failed to convert `arg`"); 80 | cliargs.push(c_arg.as_ptr().cast()); 81 | c_args.push(c_arg); 82 | } 83 | 84 | let mut tu = None; 85 | 86 | unsafe { 87 | self.inner.parse_translation_unit( 88 | c_source_filename.as_ptr().cast(), 89 | cliargs.as_ptr(), 90 | cliargs.len() as i32, 91 | uf.as_ptr(), 92 | uf.len() as u32, 93 | options, 94 | &mut tu, 95 | ) 96 | } 97 | .result()?; 98 | Ok(DxcTranslationUnit::new(tu.unwrap())) 99 | } 100 | } 101 | 102 | pub struct DxcUnsavedFile { 103 | inner: IDxcUnsavedFile, 104 | } 105 | 106 | impl DxcUnsavedFile { 107 | pub fn get_length(&self) -> Result { 108 | let mut length: u32 = 0; 109 | unsafe { self.inner.get_length(&mut length) }.result_with_success(length) 110 | } 111 | 112 | fn new(inner: IDxcUnsavedFile) -> Self { 113 | DxcUnsavedFile { inner } 114 | } 115 | } 116 | 117 | pub struct DxcTranslationUnit { 118 | inner: IDxcTranslationUnit, 119 | } 120 | 121 | impl DxcTranslationUnit { 122 | fn new(inner: IDxcTranslationUnit) -> Self { 123 | DxcTranslationUnit { inner } 124 | } 125 | 126 | pub fn get_file(&self, name: &[u8]) -> Result { 127 | let mut file = None; 128 | unsafe { self.inner.get_file(name.as_ptr(), &mut file) }.result()?; 129 | Ok(DxcFile::new(file.unwrap())) 130 | } 131 | 132 | pub fn get_cursor(&self) -> Result { 133 | let mut cursor = None; 134 | unsafe { self.inner.get_cursor(&mut cursor) }.result()?; 135 | Ok(DxcCursor::new(cursor.unwrap())) 136 | } 137 | } 138 | 139 | pub struct DxcCursor { 140 | inner: IDxcCursor, 141 | } 142 | 143 | impl DxcCursor { 144 | fn new(inner: IDxcCursor) -> Self { 145 | DxcCursor { inner } 146 | } 147 | 148 | pub fn get_children(&self, skip: u32, max_count: u32) -> Result> { 149 | let mut result: *mut IDxcCursor = std::ptr::null_mut(); 150 | let mut result_length: u32 = 0; 151 | 152 | unsafe { 153 | self.inner 154 | .get_children(skip, max_count, &mut result_length, &mut result) 155 | } 156 | .result()?; 157 | 158 | // get_children allocates a buffer to pass the result in. 159 | // Create a vector so that we get ownership of the `IDxcCursor(s) (received from get_children), instead of 160 | // having to clone (copy is intentionally not implemented) them and leaving unowned COM references alive. 161 | // It is wrapped in ManuallyDrop to free the underlying pointer by hand using CoTaskMemFree. 162 | // TODO: switch to Vec::from_raw_parts_in with custom deallocator when this is stabilized 163 | let child_cursors = ManuallyDrop::new(unsafe { 164 | Vec::from_raw_parts(result, result_length as usize, result_length as usize) 165 | }) 166 | .drain(..) 167 | .map(DxcCursor::new) 168 | .collect::>(); 169 | 170 | unsafe { CoTaskMemFree(result.cast()) }; 171 | Ok(child_cursors) 172 | } 173 | 174 | pub fn get_all_children(&self) -> Result> { 175 | const MAX_CHILDREN_PER_CHUNK: u32 = 10; 176 | let mut children = vec![]; 177 | 178 | loop { 179 | let res = self.get_children(children.len() as u32, MAX_CHILDREN_PER_CHUNK)?; 180 | let res_len = res.len(); 181 | children.extend(res); 182 | if res_len < MAX_CHILDREN_PER_CHUNK as usize { 183 | break Ok(children); 184 | } 185 | } 186 | } 187 | 188 | pub fn get_extent(&self) -> Result { 189 | let mut range = None; 190 | unsafe { self.inner.get_extent(&mut range) }.result()?; 191 | Ok(DxcSourceRange::new(range.unwrap())) 192 | } 193 | 194 | pub fn get_location(&self) -> Result { 195 | let mut location = None; 196 | unsafe { self.inner.get_location(&mut location) }.result()?; 197 | Ok(DxcSourceLocation::new(location.unwrap())) 198 | } 199 | 200 | pub fn get_display_name(&self) -> Result { 201 | let mut name: BSTR = std::ptr::null_mut(); 202 | unsafe { self.inner.get_display_name(&mut name) }.result()?; 203 | Ok(crate::utils::from_bstr(name)) 204 | } 205 | 206 | pub fn get_formatted_name(&self, formatting: DxcCursorFormatting) -> Result { 207 | let mut name: BSTR = std::ptr::null_mut(); 208 | unsafe { self.inner.get_formatted_name(formatting, &mut name) }.result()?; 209 | Ok(crate::utils::from_bstr(name)) 210 | } 211 | 212 | pub fn get_qualified_name(&self, include_template_args: bool) -> Result { 213 | let mut name: BSTR = std::ptr::null_mut(); 214 | unsafe { 215 | self.inner 216 | .get_qualified_name(include_template_args, &mut name) 217 | } 218 | .result()?; 219 | Ok(crate::utils::from_bstr(name)) 220 | } 221 | 222 | pub fn get_kind(&self) -> Result { 223 | let mut cursor_kind: DxcCursorKind = DxcCursorKind::UNEXPOSED_DECL; 224 | unsafe { self.inner.get_kind(&mut cursor_kind) }.result_with_success(cursor_kind) 225 | } 226 | 227 | pub fn get_kind_flags(&self) -> Result { 228 | let mut cursor_kind_flags: DxcCursorKindFlags = DxcCursorKindFlags::NONE; 229 | unsafe { self.inner.get_kind_flags(&mut cursor_kind_flags) } 230 | .result_with_success(cursor_kind_flags) 231 | } 232 | 233 | pub fn get_semantic_parent(&self) -> Result { 234 | let mut inner = None; 235 | unsafe { self.inner.get_semantic_parent(&mut inner) }.result()?; 236 | Ok(DxcCursor::new(inner.unwrap())) 237 | } 238 | 239 | pub fn get_lexical_parent(&self) -> Result { 240 | let mut inner = None; 241 | unsafe { self.inner.get_lexical_parent(&mut inner) }.result()?; 242 | Ok(DxcCursor::new(inner.unwrap())) 243 | } 244 | 245 | pub fn get_cursor_type(&self) -> Result { 246 | let mut inner = None; 247 | unsafe { self.inner.get_cursor_type(&mut inner) }.result()?; 248 | Ok(DxcType::new(inner.unwrap())) 249 | } 250 | 251 | pub fn get_num_arguments(&self) -> Result { 252 | let mut result: i32 = 0; 253 | 254 | unsafe { self.inner.get_num_arguments(&mut result) }.result_with_success(result) 255 | } 256 | 257 | pub fn get_argument_at(&self, index: i32) -> Result { 258 | let mut inner = None; 259 | unsafe { self.inner.get_argument_at(index, &mut inner) }.result()?; 260 | Ok(DxcCursor::new(inner.unwrap())) 261 | } 262 | 263 | pub fn get_referenced_cursor(&self) -> Result { 264 | let mut inner = None; 265 | unsafe { self.inner.get_referenced_cursor(&mut inner) }.result()?; 266 | Ok(DxcCursor::new(inner.unwrap())) 267 | } 268 | 269 | pub fn get_definition_cursor(&self) -> Result { 270 | let mut inner = None; 271 | unsafe { self.inner.get_definition_cursor(&mut inner) }.result()?; 272 | Ok(DxcCursor::new(inner.unwrap())) 273 | } 274 | 275 | pub fn find_references_in_file( 276 | &self, 277 | file: &DxcFile, 278 | skip: u32, 279 | top: u32, 280 | ) -> Result> { 281 | let mut result: *mut IDxcCursor = std::ptr::null_mut(); 282 | let mut result_length: u32 = 0; 283 | 284 | unsafe { 285 | self.inner.find_references_in_file( 286 | &file.inner, 287 | skip, 288 | top, 289 | &mut result_length, 290 | &mut result, 291 | ) 292 | } 293 | .result()?; 294 | 295 | // find_references_in_file allocates a buffer to pass the result in. 296 | // Create a vector so that we get ownership of the `IDxcCursor(s) (received from find_references_in_file), instead 297 | // of having to clone (copy is intentionally not implemented) them and leaving unowned COM references alive. 298 | // It is wrapped in ManuallyDrop to free the underlying pointer by hand using CoTaskMemFree. 299 | // TODO: switch to Vec::from_raw_parts_in with custom deallocator when this is stabilized 300 | let child_cursors = ManuallyDrop::new(unsafe { 301 | Vec::from_raw_parts(result, result_length as usize, result_length as usize) 302 | }) 303 | .drain(..) 304 | .map(DxcCursor::new) 305 | .collect::>(); 306 | 307 | unsafe { CoTaskMemFree(result.cast()) }; 308 | Ok(child_cursors) 309 | } 310 | 311 | pub fn get_spelling(&self) -> Result { 312 | let mut spelling: LPSTR = std::ptr::null_mut(); 313 | unsafe { self.inner.get_spelling(&mut spelling) }.result()?; 314 | Ok(crate::utils::from_lpstr(spelling)) 315 | } 316 | 317 | pub fn is_equal_to(&self, other: &DxcCursor) -> Result { 318 | let mut result: bool = false; 319 | unsafe { self.inner.is_equal_to(&other.inner, &mut result) }.result_with_success(result) 320 | } 321 | 322 | pub fn is_null(&mut self) -> Result { 323 | let mut result: bool = false; 324 | unsafe { IDxcCursor::is_null(&self.inner, &mut result) }.result_with_success(result) 325 | } 326 | 327 | pub fn is_definition(&self) -> Result { 328 | let mut result: bool = false; 329 | unsafe { self.inner.is_definition(&mut result) }.result_with_success(result) 330 | } 331 | 332 | pub fn get_snapped_child(&self, location: &DxcSourceLocation) -> Result { 333 | let mut inner = None; 334 | unsafe { self.inner.get_snapped_child(&location.inner, &mut inner) }.result()?; 335 | Ok(DxcCursor::new(inner.unwrap())) 336 | } 337 | 338 | pub fn get_source<'a>(&self, source: &'a str) -> Result<&'a str> { 339 | let range = self.get_extent()?; 340 | 341 | let DxcSourceOffsets { 342 | start_offset, 343 | end_offset, 344 | } = range.get_offsets()?; 345 | 346 | let source_range = (start_offset as usize)..(end_offset as usize); 347 | 348 | Ok(&source[source_range]) 349 | } 350 | } 351 | 352 | pub struct DxcType { 353 | inner: IDxcType, 354 | } 355 | 356 | impl DxcType { 357 | fn new(inner: IDxcType) -> Self { 358 | DxcType { inner } 359 | } 360 | 361 | pub fn get_spelling(&self) -> Result { 362 | let mut spelling: LPSTR = std::ptr::null_mut(); 363 | unsafe { self.inner.get_spelling(&mut spelling) } 364 | .result_with_success(crate::utils::from_lpstr(spelling)) 365 | } 366 | } 367 | 368 | pub struct DxcSourceLocation { 369 | inner: IDxcSourceLocation, 370 | } 371 | 372 | impl std::fmt::Debug for DxcSourceLocation { 373 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 374 | f.debug_struct("DxcSourceLocation") 375 | .field("inner", &self.inner) 376 | .finish() 377 | } 378 | } 379 | 380 | impl DxcSourceLocation { 381 | fn new(inner: IDxcSourceLocation) -> Self { 382 | DxcSourceLocation { inner } 383 | } 384 | } 385 | 386 | #[derive(Debug)] 387 | pub struct DxcSourceOffsets { 388 | pub start_offset: u32, 389 | pub end_offset: u32, 390 | } 391 | 392 | pub struct DxcSourceRange { 393 | inner: IDxcSourceRange, 394 | } 395 | 396 | impl std::fmt::Debug for DxcSourceRange { 397 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 398 | f.debug_struct("DxcSourceRange") 399 | .field("inner", &self.inner) 400 | .finish() 401 | } 402 | } 403 | 404 | impl DxcSourceRange { 405 | pub fn get_offsets(&self) -> Result { 406 | let mut start_offset: u32 = 0; 407 | let mut end_offset: u32 = 0; 408 | unsafe { self.inner.get_offsets(&mut start_offset, &mut end_offset) }.result_with_success( 409 | DxcSourceOffsets { 410 | start_offset, 411 | end_offset, 412 | }, 413 | ) 414 | } 415 | } 416 | 417 | impl DxcSourceRange { 418 | fn new(inner: IDxcSourceRange) -> Self { 419 | DxcSourceRange { inner } 420 | } 421 | } 422 | 423 | pub struct DxcFile { 424 | inner: IDxcFile, 425 | } 426 | 427 | impl DxcFile { 428 | fn new(inner: IDxcFile) -> Self { 429 | DxcFile { inner } 430 | } 431 | } 432 | 433 | impl Dxc { 434 | pub fn create_intellisense(&self) -> Result { 435 | let mut intellisense = None; 436 | 437 | self.get_dxc_create_instance()?( 438 | &CLSID_DxcIntelliSense, 439 | &IDxcIntelliSense::IID, 440 | &mut intellisense, 441 | ) 442 | .result()?; 443 | Ok(DxcIntellisense::new(intellisense.unwrap())) 444 | } 445 | } 446 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | #![allow(non_upper_case_globals)] 3 | #![allow( 4 | clippy::transmute_ptr_to_ptr, // Introduced by com-rs 5 | clippy::too_many_arguments, // We're wrapping an API outside of our control 6 | clippy::uninlined_format_args, // Unfavourable format; implies unneeded MSRV bump 7 | )] 8 | 9 | //! # Hassle 10 | //! 11 | //! This crate provides an FFI layer and idiomatic rust wrappers for the new [DirectXShaderCompiler](https://github.com/Microsoft/DirectXShaderCompiler) library. 12 | //! 13 | //! ## Simple example 14 | //! 15 | //! ```rust 16 | //! use hassle_rs::compile_hlsl; 17 | //! 18 | //! let code = " 19 | //! Texture2D g_input : register(t0, space0); 20 | //! RWTexture2D g_output : register(u0, space0); 21 | //! 22 | //! [numthreads(8, 8, 1)] 23 | //! void copyCs(uint3 dispatchThreadId : SV_DispatchThreadID) 24 | //! { 25 | //! g_output[dispatchThreadId.xy] = g_input[dispatchThreadId.xy]; 26 | //! }"; 27 | //! 28 | //! let ir = compile_hlsl( 29 | //! "shader_filename.hlsl", 30 | //! code, 31 | //! "copyCs", 32 | //! "cs_6_1", 33 | //! &vec!["-spirv"], 34 | //! &vec![ 35 | //! ("MY_DEFINE", Some("Value")), 36 | //! ("OTHER_DEFINE", None) 37 | //! ], 38 | //! ); 39 | //! ``` 40 | 41 | pub mod fake_sign; 42 | pub mod ffi; 43 | pub mod os; 44 | pub mod utils; 45 | pub mod wrapper; 46 | 47 | pub mod intellisense; 48 | 49 | pub use crate::ffi::*; 50 | pub use crate::utils::{compile_hlsl, fake_sign_dxil_in_place, validate_dxil, HassleError, Result}; 51 | pub use crate::wrapper::*; 52 | -------------------------------------------------------------------------------- /src/os.rs: -------------------------------------------------------------------------------- 1 | // Allow uppercase names to match Windows API: 2 | #![allow(clippy::upper_case_acronyms)] 3 | 4 | #[cfg(windows)] 5 | mod os_defs { 6 | pub use winapi::shared::{ 7 | ntdef::{HRESULT, LPCSTR, LPCWSTR, LPSTR, LPWSTR, WCHAR}, 8 | wtypes::BSTR, 9 | }; 10 | 11 | pub use winapi::um::combaseapi::CoTaskMemFree; 12 | pub use winapi::um::oleauto::{SysFreeString, SysStringLen}; 13 | } 14 | 15 | #[cfg(not(windows))] 16 | mod os_defs { 17 | pub type CHAR = std::os::raw::c_char; 18 | pub type UINT = u32; 19 | pub type WCHAR = widestring::WideChar; 20 | pub type OLECHAR = WCHAR; 21 | pub type LPSTR = *mut CHAR; 22 | pub type LPWSTR = *mut WCHAR; 23 | pub type LPCSTR = *const CHAR; 24 | pub type LPCWSTR = *const WCHAR; 25 | pub type BSTR = *mut OLECHAR; 26 | pub type LPBSTR = *mut BSTR; 27 | pub type HRESULT = i32; 28 | 29 | /// Returns a mutable pointer to the length prefix of the string 30 | fn len_ptr(p: BSTR) -> *mut UINT { 31 | // The first four bytes before the pointer contain the length prefix: 32 | // https://docs.microsoft.com/en-us/previous-versions/windows/desktop/automat/bstr#remarks 33 | unsafe { p.cast::().offset(-1) } 34 | } 35 | 36 | #[allow(non_snake_case)] 37 | /// # Safety 38 | /// `p` must be a valid pointer to an allocation made with `malloc`, or null. 39 | pub unsafe fn CoTaskMemFree(p: *mut libc::c_void) { 40 | // https://github.com/microsoft/DirectXShaderCompiler/blob/56e22b30c5e43632f56a1f97865f37108ec35463/include/dxc/Support/WinAdapter.h#L46 41 | if !p.is_null() { 42 | libc::free(p) 43 | } 44 | } 45 | 46 | #[allow(non_snake_case)] 47 | /// # Safety 48 | /// `p` must be a valid pointer to an allocation made with `malloc`, or null. 49 | pub unsafe fn SysFreeString(p: BSTR) { 50 | // https://github.com/microsoft/DirectXShaderCompiler/blob/56e22b30c5e43632f56a1f97865f37108ec35463/lib/DxcSupport/WinAdapter.cpp#L50-L53 51 | if !p.is_null() { 52 | libc::free(len_ptr(p).cast::<_>()) 53 | } 54 | } 55 | 56 | #[allow(non_snake_case)] 57 | /// Returns the size of `p` in bytes, excluding terminating NULL character 58 | /// 59 | /// # Safety 60 | /// `p` must be a valid pointer to a [`BSTR`] with size-prefix in the `4` leading bytes, or null. 61 | /// 62 | /// https://docs.microsoft.com/en-us/previous-versions/windows/desktop/automat/bstr#remarks 63 | pub unsafe fn SysStringByteLen(p: BSTR) -> UINT { 64 | if p.is_null() { 65 | 0 66 | } else { 67 | *len_ptr(p) 68 | } 69 | } 70 | 71 | #[allow(non_snake_case)] 72 | /// Returns the size of `p` in characters, excluding terminating NULL character 73 | /// 74 | /// # Safety 75 | /// `p` must be a valid pointer to a [`BSTR`] with size-prefix in the `4` leading bytes, or null. 76 | /// 77 | /// https://docs.microsoft.com/en-us/previous-versions/windows/desktop/automat/bstr#remarks 78 | pub unsafe fn SysStringLen(p: BSTR) -> UINT { 79 | SysStringByteLen(p) / std::mem::size_of::() as UINT 80 | } 81 | } 82 | 83 | pub use os_defs::*; 84 | 85 | #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] 86 | #[repr(transparent)] 87 | #[must_use] 88 | pub struct HRESULT(pub os_defs::HRESULT); 89 | impl HRESULT { 90 | pub fn is_err(&self) -> bool { 91 | self.0 < 0 92 | } 93 | } 94 | 95 | impl From for HRESULT { 96 | fn from(v: i32) -> Self { 97 | Self(v) 98 | } 99 | } 100 | 101 | impl std::fmt::Debug for HRESULT { 102 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 103 | ::fmt(self, f) 104 | } 105 | } 106 | 107 | impl std::fmt::Display for HRESULT { 108 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 109 | f.write_fmt(format_args!("{:#x}", self)) 110 | } 111 | } 112 | 113 | impl std::fmt::LowerHex for HRESULT { 114 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 115 | let prefix = if f.alternate() { "0x" } else { "" }; 116 | let bare_hex = format!("{:x}", self.0.abs()); 117 | // https://stackoverflow.com/a/44712309 118 | f.pad_integral(self.0 >= 0, prefix, &bare_hex) 119 | // ::fmt(&self.0, f) 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/utils.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::CStr; 2 | use std::path::PathBuf; 3 | 4 | use crate::os::{SysFreeString, SysStringLen, BSTR, HRESULT, LPCSTR, LPCWSTR, WCHAR}; 5 | use crate::wrapper::*; 6 | use thiserror::Error; 7 | 8 | pub(crate) fn to_wide(msg: &str) -> Vec { 9 | widestring::WideCString::from_str(msg) 10 | .unwrap() 11 | .into_vec_with_nul() 12 | } 13 | 14 | pub(crate) fn from_wide(wide: LPCWSTR) -> String { 15 | unsafe { widestring::WideCStr::from_ptr_str(wide) } 16 | .to_string() 17 | .expect("widestring decode failed") 18 | } 19 | 20 | pub(crate) fn from_bstr(string: BSTR) -> String { 21 | let len = unsafe { SysStringLen(string) } as usize; 22 | 23 | let result = unsafe { widestring::WideStr::from_ptr(string, len) } 24 | .to_string() 25 | .expect("widestring decode failed"); 26 | 27 | unsafe { SysFreeString(string) }; 28 | result 29 | } 30 | 31 | pub(crate) fn from_lpstr(string: LPCSTR) -> String { 32 | unsafe { CStr::from_ptr(string) } 33 | .to_str() 34 | .unwrap() 35 | .to_owned() 36 | } 37 | 38 | struct DefaultIncludeHandler {} 39 | 40 | impl DxcIncludeHandler for DefaultIncludeHandler { 41 | fn load_source(&mut self, filename: String) -> Option { 42 | use std::io::Read; 43 | match std::fs::File::open(filename) { 44 | Ok(mut f) => { 45 | let mut content = String::new(); 46 | f.read_to_string(&mut content).ok()?; 47 | Some(content) 48 | } 49 | Err(_) => None, 50 | } 51 | } 52 | } 53 | 54 | #[derive(Error, Debug)] 55 | pub enum HassleError { 56 | #[error("Win32 error: {0:x}")] 57 | Win32Error(HRESULT), 58 | #[error("{0}")] 59 | CompileError(String), 60 | #[error("Validation error: {0}")] 61 | ValidationError(String), 62 | #[error("Failed to load library {filename:?}: {inner:?}")] 63 | LoadLibraryError { 64 | filename: PathBuf, 65 | #[source] 66 | inner: libloading::Error, 67 | }, 68 | #[error("LibLoading error: {0:?}")] 69 | LibLoadingError(#[from] libloading::Error), 70 | } 71 | 72 | pub type Result = std::result::Result; 73 | 74 | impl HRESULT { 75 | /// Turns an [`HRESULT`] from the COM [`crate::ffi`] API declaration 76 | /// into a [`Result`] containing [`HassleError`]. 77 | pub fn result(self) -> Result<()> { 78 | self.result_with_success(()) 79 | } 80 | 81 | /// Turns an [`HRESULT`] from the COM [`crate::ffi`] API declaration 82 | /// into a [`Result`] containing [`HassleError`], with the desired value. 83 | /// 84 | /// Note that `v` is passed by value and is not a closure that is executed 85 | /// lazily. Use the short-circuiting `?` operator for such cases: 86 | /// ```no_run 87 | /// let mut blob: ComPtr = ComPtr::new(); 88 | /// unsafe { self.inner.get_result(blob.as_mut_ptr()) }.result()?; 89 | /// Ok(DxcBlob::new(blob)) 90 | /// ``` 91 | pub fn result_with_success(self, v: T) -> Result { 92 | if self.is_err() { 93 | Err(HassleError::Win32Error(self)) 94 | } else { 95 | Ok(v) 96 | } 97 | } 98 | } 99 | 100 | /// Helper function to directly compile a HLSL shader to an intermediate language, 101 | /// this function expects `dxcompiler.dll` to be available in the current 102 | /// executable environment. 103 | /// 104 | /// Specify -spirv as one of the `args` to compile to SPIR-V 105 | /// `dxc_path` can point to a library directly or the directory containing the library, 106 | /// in which case the appended filename depends on the platform. 107 | pub fn compile_hlsl( 108 | source_name: &str, 109 | shader_text: &str, 110 | entry_point: &str, 111 | target_profile: &str, 112 | args: &[&str], 113 | defines: &[(&str, Option<&str>)], 114 | ) -> Result> { 115 | let dxc = Dxc::new(None)?; 116 | 117 | let compiler = dxc.create_compiler()?; 118 | let library = dxc.create_library()?; 119 | 120 | let blob = library.create_blob_with_encoding_from_str(shader_text)?; 121 | 122 | let result = compiler.compile( 123 | &blob, 124 | source_name, 125 | entry_point, 126 | target_profile, 127 | args, 128 | Some(&mut DefaultIncludeHandler {}), 129 | defines, 130 | ); 131 | 132 | match result { 133 | Err(result) => { 134 | let error_blob = result.0.get_error_buffer()?; 135 | Err(HassleError::CompileError( 136 | library.get_blob_as_string(&error_blob.into())?, 137 | )) 138 | } 139 | Ok(result) => { 140 | let result_blob = result.get_result()?; 141 | 142 | Ok(result_blob.to_vec()) 143 | } 144 | } 145 | } 146 | 147 | /// Helper function to validate a DXIL binary independent from the compilation process, 148 | /// this function expects `dxcompiler.dll` and `dxil.dll` to be available in the current 149 | /// execution environment. 150 | /// 151 | /// `dxil.dll` is only available on Windows. 152 | pub fn validate_dxil(data: &[u8]) -> Result, HassleError> { 153 | let dxc = Dxc::new(None)?; 154 | let dxil = Dxil::new(None)?; 155 | 156 | let validator = dxil.create_validator()?; 157 | let library = dxc.create_library()?; 158 | 159 | let blob_encoding = library.create_blob_with_encoding(data)?; 160 | 161 | match validator.validate(blob_encoding.into()) { 162 | Ok(blob) => Ok(blob.to_vec()), 163 | Err(result) => { 164 | let error_blob = result.0.get_error_buffer()?; 165 | Err(HassleError::ValidationError( 166 | library.get_blob_as_string(&error_blob.into())?, 167 | )) 168 | } 169 | } 170 | } 171 | 172 | pub use crate::fake_sign::fake_sign_dxil_in_place; 173 | -------------------------------------------------------------------------------- /src/wrapper.rs: -------------------------------------------------------------------------------- 1 | #![allow( 2 | clippy::too_many_arguments, 3 | clippy::new_without_default, 4 | clippy::type_complexity 5 | )] 6 | 7 | use crate::ffi::*; 8 | use crate::os::{HRESULT, LPCWSTR, LPWSTR, WCHAR}; 9 | use crate::utils::{from_wide, to_wide, HassleError, Result}; 10 | use com::{class, interfaces::IUnknown, production::Class, production::ClassAllocation, Interface}; 11 | use libloading::{library_filename, Library, Symbol}; 12 | use std::cell::RefCell; 13 | use std::ops::Deref; 14 | use std::path::PathBuf; 15 | use std::pin::Pin; 16 | 17 | pub struct DxcBlob { 18 | inner: IDxcBlob, 19 | } 20 | 21 | impl DxcBlob { 22 | fn new(inner: IDxcBlob) -> Self { 23 | Self { inner } 24 | } 25 | 26 | pub fn as_slice(&self) -> &[T] { 27 | unsafe { 28 | std::slice::from_raw_parts( 29 | self.inner.get_buffer_pointer().cast(), 30 | self.inner.get_buffer_size() / std::mem::size_of::(), 31 | ) 32 | } 33 | } 34 | 35 | pub fn as_mut_slice(&mut self) -> &mut [T] { 36 | unsafe { 37 | std::slice::from_raw_parts_mut( 38 | self.inner.get_buffer_pointer().cast(), 39 | self.inner.get_buffer_size() / std::mem::size_of::(), 40 | ) 41 | } 42 | } 43 | 44 | pub fn to_vec(&self) -> Vec 45 | where 46 | T: Clone, 47 | { 48 | self.as_slice().to_vec() 49 | } 50 | } 51 | 52 | impl AsRef<[u8]> for DxcBlob { 53 | fn as_ref(&self) -> &[u8] { 54 | self.as_slice() 55 | } 56 | } 57 | 58 | impl AsMut<[u8]> for DxcBlob { 59 | fn as_mut(&mut self) -> &mut [u8] { 60 | self.as_mut_slice() 61 | } 62 | } 63 | 64 | pub struct DxcBlobEncoding { 65 | inner: IDxcBlobEncoding, 66 | } 67 | 68 | impl DxcBlobEncoding { 69 | fn new(inner: IDxcBlobEncoding) -> Self { 70 | Self { inner } 71 | } 72 | } 73 | 74 | impl From for DxcBlob { 75 | fn from(encoded_blob: DxcBlobEncoding) -> Self { 76 | DxcBlob::new(encoded_blob.inner.query_interface::().unwrap()) 77 | } 78 | } 79 | 80 | pub struct DxcOperationResult { 81 | inner: IDxcOperationResult, 82 | } 83 | 84 | impl DxcOperationResult { 85 | fn new(inner: IDxcOperationResult) -> Self { 86 | Self { inner } 87 | } 88 | 89 | pub fn get_status(&self) -> Result { 90 | let mut status: u32 = 0; 91 | unsafe { self.inner.get_status(&mut status) }.result_with_success(status) 92 | } 93 | 94 | pub fn get_result(&self) -> Result { 95 | let mut blob = None; 96 | unsafe { self.inner.get_result(&mut blob) }.result()?; 97 | Ok(DxcBlob::new(blob.unwrap())) 98 | } 99 | 100 | pub fn get_error_buffer(&self) -> Result { 101 | let mut blob = None; 102 | 103 | unsafe { self.inner.get_error_buffer(&mut blob) }.result()?; 104 | Ok(DxcBlobEncoding::new(blob.unwrap())) 105 | } 106 | } 107 | 108 | pub trait DxcIncludeHandler { 109 | fn load_source(&mut self, filename: String) -> Option; 110 | } 111 | 112 | class! { 113 | #[no_class_factory] 114 | class DxcIncludeHandlerWrapper: IDxcIncludeHandler { 115 | // Com-rs intentionally does not support lifetimes in its class structs 116 | // since they live on the heap and their lifetime can be prolonged for 117 | // as long as someone keeps a reference through `add_ref()`. 118 | // The only way for us to access the library and handler implementation, 119 | // which are now intentionally behind a borrow to signify our promise 120 | // regarding lifetime, is by transmuting them away and "ensuring" the 121 | // class object is discarded at the end of our function call. 122 | 123 | library: &'static DxcLibrary, 124 | handler: RefCell<&'static mut dyn DxcIncludeHandler>, 125 | 126 | pinned: RefCell>>, 127 | } 128 | 129 | impl IDxcIncludeHandler for DxcIncludeHandlerWrapper { 130 | fn load_source(&self, filename: LPCWSTR, include_source: *mut Option) -> HRESULT { 131 | let filename = crate::utils::from_wide(filename); 132 | 133 | let mut handler = self.handler.borrow_mut(); 134 | let source = handler.load_source(filename); 135 | 136 | if let Some(source) = source { 137 | let source = Pin::new(source); 138 | let blob = self.library 139 | .create_blob_with_encoding_from_str(&source) 140 | .unwrap(); 141 | 142 | unsafe { *include_source = Some(DxcBlob::from(blob).inner) }; 143 | self.pinned.borrow_mut().push(source); 144 | 145 | // NOERROR 146 | 0 147 | } else { 148 | -2_147_024_894 // ERROR_FILE_NOT_FOUND / 0x80070002 149 | } 150 | .into() 151 | } 152 | } 153 | } 154 | 155 | /// Represents a reference to a COM object that should only live as long as itself 156 | /// 157 | /// In other words, on [`drop()`] we assert that the refcount is decremented to zero, 158 | /// rather than allowing it to be referenced externally (i.e. [`Class::dec_ref_count()`] 159 | /// returning `> 0`). 160 | /// This object functions a lot like [`ClassAllocation`]: see its similar [`drop()`] 161 | /// implementation for details. 162 | /// 163 | /// Note that COM objects live on the heap by design, because of this refcount system. 164 | struct LocalClassAllocation(core::pin::Pin>); 165 | 166 | impl LocalClassAllocation { 167 | fn new(allocation: ClassAllocation) -> Self { 168 | // TODO: There is no way to take the internal, owned box out of com-rs's 169 | // allocation wrapper. 170 | // https://github.com/microsoft/com-rs/issues/236 covers this issue as a whole, 171 | // including lifetime support and this `LocalClassAllocation` upstream. 172 | let inner: core::mem::ManuallyDrop>> = 173 | unsafe { std::mem::transmute(allocation) }; 174 | 175 | Self(core::mem::ManuallyDrop::into_inner(inner)) 176 | } 177 | 178 | // TODO: Return a borrow of this interface? 179 | // query_interface() is not behind one of the traits 180 | // fn query_interface(&self) -> Option { 181 | // self.0.query_interface::().unwrap() 182 | // } 183 | } 184 | 185 | impl Deref for LocalClassAllocation { 186 | type Target = core::pin::Pin>; 187 | 188 | fn deref(&self) -> &Self::Target { 189 | &self.0 190 | } 191 | } 192 | 193 | impl Drop for LocalClassAllocation { 194 | fn drop(&mut self) { 195 | // Check if we are the only remaining reference to this object 196 | assert_eq!( 197 | unsafe { self.0.dec_ref_count() }, 198 | 0, 199 | "COM object is still referenced" 200 | ); 201 | // Now that we're the last one to give up our refcount, it is safe 202 | // for the internal object to get dropped. 203 | } 204 | } 205 | 206 | impl DxcIncludeHandlerWrapper { 207 | /// SAFETY: Make sure the returned object does _not_ outlive the lifetime 208 | /// of either `library` nor `include_handler` 209 | unsafe fn create_include_handler( 210 | library: &'_ DxcLibrary, 211 | include_handler: &'_ mut dyn DxcIncludeHandler, 212 | ) -> LocalClassAllocation { 213 | #[allow(clippy::missing_transmute_annotations)] 214 | LocalClassAllocation::new(Self::allocate( 215 | std::mem::transmute(library), 216 | RefCell::new(std::mem::transmute(include_handler)), 217 | RefCell::new(vec![]), 218 | )) 219 | } 220 | } 221 | 222 | pub struct DxcCompiler { 223 | inner: IDxcCompiler2, 224 | library: DxcLibrary, 225 | } 226 | 227 | impl DxcCompiler { 228 | fn new(inner: IDxcCompiler2, library: DxcLibrary) -> Self { 229 | Self { inner, library } 230 | } 231 | 232 | fn prep_defines( 233 | defines: &[(&str, Option<&str>)], 234 | wide_defines: &mut Vec<(Vec, Vec)>, 235 | dxc_defines: &mut Vec, 236 | ) { 237 | for (name, value) in defines { 238 | if value.is_none() { 239 | wide_defines.push((to_wide(name), to_wide("1"))); 240 | } else { 241 | wide_defines.push((to_wide(name), to_wide(value.unwrap()))); 242 | } 243 | } 244 | 245 | for (ref name, ref value) in wide_defines { 246 | dxc_defines.push(DxcDefine { 247 | name: name.as_ptr(), 248 | value: value.as_ptr(), 249 | }); 250 | } 251 | } 252 | 253 | fn prep_args(args: &[&str], wide_args: &mut Vec>, dxc_args: &mut Vec) { 254 | for a in args { 255 | wide_args.push(to_wide(a)); 256 | } 257 | 258 | for a in wide_args { 259 | dxc_args.push(a.as_ptr()); 260 | } 261 | } 262 | 263 | pub fn compile( 264 | &self, 265 | blob: &DxcBlobEncoding, 266 | source_name: &str, 267 | entry_point: &str, 268 | target_profile: &str, 269 | args: &[&str], 270 | include_handler: Option<&mut dyn DxcIncludeHandler>, 271 | defines: &[(&str, Option<&str>)], 272 | ) -> Result { 273 | let mut wide_args = vec![]; 274 | let mut dxc_args = vec![]; 275 | Self::prep_args(args, &mut wide_args, &mut dxc_args); 276 | 277 | let mut wide_defines = vec![]; 278 | let mut dxc_defines = vec![]; 279 | Self::prep_defines(defines, &mut wide_defines, &mut dxc_defines); 280 | 281 | // Keep alive on the stack 282 | let include_handler = include_handler.map(|include_handler| unsafe { 283 | DxcIncludeHandlerWrapper::create_include_handler(&self.library, include_handler) 284 | }); 285 | // TODO: query_interface() should have a borrow on LocalClassAllocation to prevent things going kaboom 286 | let include_handler = include_handler 287 | .as_ref() 288 | .map(|i| i.query_interface().unwrap()); 289 | 290 | let mut result = None; 291 | let result_hr = unsafe { 292 | self.inner.compile( 293 | &blob.inner, 294 | to_wide(source_name).as_ptr(), 295 | to_wide(entry_point).as_ptr(), 296 | to_wide(target_profile).as_ptr(), 297 | dxc_args.as_ptr(), 298 | dxc_args.len() as u32, 299 | dxc_defines.as_ptr(), 300 | dxc_defines.len() as u32, 301 | &include_handler, 302 | &mut result, 303 | ) 304 | }; 305 | 306 | let result = result.unwrap(); 307 | 308 | let mut compile_error = 0u32; 309 | let status_hr = unsafe { result.get_status(&mut compile_error) }; 310 | 311 | if !result_hr.is_err() && !status_hr.is_err() && compile_error == 0 { 312 | Ok(DxcOperationResult::new(result)) 313 | } else { 314 | Err((DxcOperationResult::new(result), result_hr)) 315 | } 316 | } 317 | 318 | pub fn compile_with_debug( 319 | &self, 320 | blob: &DxcBlobEncoding, 321 | source_name: &str, 322 | entry_point: &str, 323 | target_profile: &str, 324 | args: &[&str], 325 | include_handler: Option<&mut dyn DxcIncludeHandler>, 326 | defines: &[(&str, Option<&str>)], 327 | ) -> Result<(DxcOperationResult, String, DxcBlob), (DxcOperationResult, HRESULT)> { 328 | let mut wide_args = vec![]; 329 | let mut dxc_args = vec![]; 330 | Self::prep_args(args, &mut wide_args, &mut dxc_args); 331 | 332 | let mut wide_defines = vec![]; 333 | let mut dxc_defines = vec![]; 334 | Self::prep_defines(defines, &mut wide_defines, &mut dxc_defines); 335 | 336 | // Keep alive on the stack 337 | let include_handler = include_handler.map(|include_handler| unsafe { 338 | DxcIncludeHandlerWrapper::create_include_handler(&self.library, include_handler) 339 | }); 340 | let include_handler = include_handler 341 | .as_ref() 342 | .map(|i| i.query_interface().unwrap()); 343 | 344 | let mut result = None; 345 | let mut debug_blob = None; 346 | let mut debug_filename: LPWSTR = std::ptr::null_mut(); 347 | 348 | let result_hr = unsafe { 349 | self.inner.compile_with_debug( 350 | &blob.inner, 351 | to_wide(source_name).as_ptr(), 352 | to_wide(entry_point).as_ptr(), 353 | to_wide(target_profile).as_ptr(), 354 | dxc_args.as_ptr(), 355 | dxc_args.len() as u32, 356 | dxc_defines.as_ptr(), 357 | dxc_defines.len() as u32, 358 | include_handler, 359 | &mut result, 360 | &mut debug_filename, 361 | &mut debug_blob, 362 | ) 363 | }; 364 | let result = result.unwrap(); 365 | let debug_blob = debug_blob.unwrap(); 366 | 367 | let mut compile_error = 0u32; 368 | let status_hr = unsafe { result.get_status(&mut compile_error) }; 369 | 370 | if !result_hr.is_err() && !status_hr.is_err() && compile_error == 0 { 371 | Ok(( 372 | DxcOperationResult::new(result), 373 | from_wide(debug_filename), 374 | DxcBlob::new(debug_blob), 375 | )) 376 | } else { 377 | Err((DxcOperationResult::new(result), result_hr)) 378 | } 379 | } 380 | 381 | pub fn preprocess( 382 | &self, 383 | blob: &DxcBlobEncoding, 384 | source_name: &str, 385 | args: &[&str], 386 | include_handler: Option<&mut dyn DxcIncludeHandler>, 387 | defines: &[(&str, Option<&str>)], 388 | ) -> Result { 389 | let mut wide_args = vec![]; 390 | let mut dxc_args = vec![]; 391 | Self::prep_args(args, &mut wide_args, &mut dxc_args); 392 | 393 | let mut wide_defines = vec![]; 394 | let mut dxc_defines = vec![]; 395 | Self::prep_defines(defines, &mut wide_defines, &mut dxc_defines); 396 | 397 | // Keep alive on the stack 398 | let include_handler = include_handler.map(|include_handler| unsafe { 399 | DxcIncludeHandlerWrapper::create_include_handler(&self.library, include_handler) 400 | }); 401 | let include_handler = include_handler 402 | .as_ref() 403 | .map(|i| i.query_interface().unwrap()); 404 | 405 | let mut result = None; 406 | let result_hr = unsafe { 407 | self.inner.preprocess( 408 | &blob.inner, 409 | to_wide(source_name).as_ptr(), 410 | dxc_args.as_ptr(), 411 | dxc_args.len() as u32, 412 | dxc_defines.as_ptr(), 413 | dxc_defines.len() as u32, 414 | include_handler, 415 | &mut result, 416 | ) 417 | }; 418 | 419 | let result = result.unwrap(); 420 | 421 | let mut compile_error = 0u32; 422 | let status_hr = unsafe { result.get_status(&mut compile_error) }; 423 | 424 | if !result_hr.is_err() && !status_hr.is_err() && compile_error == 0 { 425 | Ok(DxcOperationResult::new(result)) 426 | } else { 427 | Err((DxcOperationResult::new(result), result_hr)) 428 | } 429 | } 430 | 431 | pub fn disassemble(&self, blob: &DxcBlob) -> Result { 432 | let mut result_blob = None; 433 | unsafe { self.inner.disassemble(&blob.inner, &mut result_blob) }.result()?; 434 | Ok(DxcBlobEncoding::new(result_blob.unwrap())) 435 | } 436 | } 437 | 438 | #[derive(Clone)] 439 | pub struct DxcLibrary { 440 | inner: IDxcLibrary, 441 | } 442 | 443 | impl DxcLibrary { 444 | fn new(inner: IDxcLibrary) -> Self { 445 | Self { inner } 446 | } 447 | 448 | pub fn create_blob_with_encoding(&self, data: &[u8]) -> Result { 449 | let mut blob = None; 450 | 451 | unsafe { 452 | self.inner.create_blob_with_encoding_from_pinned( 453 | data.as_ptr().cast(), 454 | data.len() as u32, 455 | 0, // Binary; no code page 456 | &mut blob, 457 | ) 458 | } 459 | .result()?; 460 | Ok(DxcBlobEncoding::new(blob.unwrap())) 461 | } 462 | 463 | pub fn create_blob_with_encoding_from_str(&self, text: &str) -> Result { 464 | let mut blob = None; 465 | const CP_UTF8: u32 = 65001; // UTF-8 translation 466 | 467 | unsafe { 468 | self.inner.create_blob_with_encoding_from_pinned( 469 | text.as_ptr().cast(), 470 | text.len() as u32, 471 | CP_UTF8, 472 | &mut blob, 473 | ) 474 | } 475 | .result()?; 476 | Ok(DxcBlobEncoding::new(blob.unwrap())) 477 | } 478 | 479 | pub fn get_blob_as_string(&self, blob: &DxcBlob) -> Result { 480 | let mut blob_utf8 = None; 481 | 482 | unsafe { self.inner.get_blob_as_utf8(&blob.inner, &mut blob_utf8) }.result()?; 483 | 484 | let blob_utf8 = blob_utf8.unwrap(); 485 | 486 | Ok(String::from_utf8(DxcBlob::new(blob_utf8.query_interface().unwrap()).to_vec()).unwrap()) 487 | } 488 | } 489 | 490 | #[derive(Debug)] 491 | pub struct Dxc { 492 | dxc_lib: Library, 493 | } 494 | 495 | impl Dxc { 496 | /// `lib_path` is an optional path to the library. Otherwise 497 | /// [`libloading::library_filename("dxcompiler")`] is used. 498 | pub fn new(lib_path: Option) -> Result { 499 | let lib_path = lib_path.unwrap_or_else(|| PathBuf::from(library_filename("dxcompiler"))); 500 | 501 | let dxc_lib = 502 | unsafe { Library::new(&lib_path) }.map_err(|e| HassleError::LoadLibraryError { 503 | filename: lib_path, 504 | inner: e, 505 | })?; 506 | 507 | Ok(Self { dxc_lib }) 508 | } 509 | 510 | pub(crate) fn get_dxc_create_instance(&self) -> Result>> { 511 | Ok(unsafe { self.dxc_lib.get(b"DxcCreateInstance\0")? }) 512 | } 513 | 514 | pub fn create_compiler(&self) -> Result { 515 | let mut compiler = None; 516 | 517 | self.get_dxc_create_instance()?(&CLSID_DxcCompiler, &IDxcCompiler2::IID, &mut compiler) 518 | .result()?; 519 | Ok(DxcCompiler::new( 520 | compiler.unwrap(), 521 | self.create_library().unwrap(), 522 | )) 523 | } 524 | 525 | pub fn create_library(&self) -> Result { 526 | let mut library = None; 527 | self.get_dxc_create_instance()?(&CLSID_DxcLibrary, &IDxcLibrary::IID, &mut library) 528 | .result()?; 529 | Ok(DxcLibrary::new(library.unwrap())) 530 | } 531 | 532 | pub fn create_reflector(&self) -> Result { 533 | let mut reflector = None; 534 | 535 | self.get_dxc_create_instance()?( 536 | &CLSID_DxcContainerReflection, 537 | &IDxcContainerReflection::IID, 538 | &mut reflector, 539 | ) 540 | .result()?; 541 | Ok(DxcReflector::new(reflector.unwrap())) 542 | } 543 | } 544 | 545 | pub struct DxcValidator { 546 | inner: IDxcValidator, 547 | } 548 | 549 | pub type DxcValidatorVersion = (u32, u32); 550 | 551 | impl DxcValidator { 552 | fn new(inner: IDxcValidator) -> Self { 553 | Self { inner } 554 | } 555 | 556 | pub fn version(&self) -> Result { 557 | let version = self 558 | .inner 559 | .query_interface::() 560 | .ok_or(HassleError::Win32Error(HRESULT(com::sys::E_NOINTERFACE)))?; 561 | 562 | let mut major = 0; 563 | let mut minor = 0; 564 | 565 | unsafe { version.get_version(&mut major, &mut minor) }.result_with_success((major, minor)) 566 | } 567 | 568 | pub fn validate(&self, blob: DxcBlob) -> Result { 569 | let mut result = None; 570 | let result_hr = unsafe { 571 | self.inner 572 | .validate(&blob.inner, DXC_VALIDATOR_FLAGS_IN_PLACE_EDIT, &mut result) 573 | }; 574 | 575 | let result = result.unwrap(); 576 | 577 | let mut validate_status = 0u32; 578 | let status_hr = unsafe { result.get_status(&mut validate_status) }; 579 | 580 | if !result_hr.is_err() && !status_hr.is_err() && validate_status == 0 { 581 | Ok(blob) 582 | } else { 583 | Err(( 584 | DxcOperationResult::new(result), 585 | HassleError::Win32Error(result_hr), 586 | )) 587 | } 588 | } 589 | } 590 | 591 | pub struct Reflection { 592 | inner: ID3D12ShaderReflection, 593 | } 594 | impl Reflection { 595 | fn new(inner: ID3D12ShaderReflection) -> Self { 596 | Self { inner } 597 | } 598 | 599 | pub fn thread_group_size(&self) -> [u32; 3] { 600 | let (mut size_x, mut size_y, mut size_z) = (0u32, 0u32, 0u32); 601 | unsafe { 602 | self.inner 603 | .get_thread_group_size(&mut size_x, &mut size_y, &mut size_z) 604 | }; 605 | [size_x, size_y, size_z] 606 | } 607 | } 608 | 609 | pub struct DxcReflector { 610 | inner: IDxcContainerReflection, 611 | } 612 | impl DxcReflector { 613 | fn new(inner: IDxcContainerReflection) -> Self { 614 | Self { inner } 615 | } 616 | 617 | pub fn reflect(&self, blob: DxcBlob) -> Result { 618 | let result_hr = unsafe { self.inner.load(blob.inner) }; 619 | if result_hr.is_err() { 620 | return Err(HassleError::Win32Error(result_hr)); 621 | } 622 | 623 | let mut shader_idx = 0; 624 | let result_hr = unsafe { self.inner.find_first_part_kind(DFCC_DXIL, &mut shader_idx) }; 625 | if result_hr.is_err() { 626 | return Err(HassleError::Win32Error(result_hr)); 627 | } 628 | 629 | let mut reflection = None::; 630 | let result_hr = unsafe { 631 | self.inner.get_part_reflection( 632 | shader_idx, 633 | &ID3D12ShaderReflection::IID, 634 | &mut reflection, 635 | ) 636 | }; 637 | if result_hr.is_err() { 638 | return Err(HassleError::Win32Error(result_hr)); 639 | } 640 | 641 | Ok(Reflection::new( 642 | reflection.unwrap().query_interface().unwrap(), 643 | )) 644 | } 645 | } 646 | 647 | #[derive(Debug)] 648 | pub struct Dxil { 649 | dxil_lib: Library, 650 | } 651 | 652 | impl Dxil { 653 | /// `lib_path` is an optional path to the library. Otherwise 654 | /// [`libloading::library_filename("dxil")`] is used. 655 | pub fn new(lib_path: Option) -> Result { 656 | let lib_path = lib_path.unwrap_or_else(|| PathBuf::from(library_filename("dxil"))); 657 | 658 | let dxil_lib = 659 | unsafe { Library::new(&lib_path) }.map_err(|e| HassleError::LoadLibraryError { 660 | filename: lib_path.to_owned(), 661 | inner: e, 662 | })?; 663 | 664 | Ok(Self { dxil_lib }) 665 | } 666 | 667 | fn get_dxc_create_instance(&self) -> Result>> { 668 | Ok(unsafe { self.dxil_lib.get(b"DxcCreateInstance\0")? }) 669 | } 670 | 671 | pub fn create_validator(&self) -> Result { 672 | let mut validator = None; 673 | self.get_dxc_create_instance()?(&CLSID_DxcValidator, &IDxcValidator::IID, &mut validator) 674 | .result()?; 675 | Ok(DxcValidator::new(validator.unwrap())) 676 | } 677 | } 678 | --------------------------------------------------------------------------------