From ce14ac3974f7f879c531c96a071252a6f892c3e9 Mon Sep 17 00:00:00 2001 From: SupaMaggie70Incorporated Date: Thu, 11 Sep 2025 23:04:58 -0500 Subject: [PATCH 01/47] Initial work for macro --- Cargo.lock | 11 ++++++ Cargo.toml | 3 ++ wgpu-hal/src/auxil/dxgi/result.rs | 56 +++++++++++++++---------------- wgpu/Cargo.toml | 4 +++ wgpu/src/macros.rs | 9 +++++ 5 files changed, 55 insertions(+), 28 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a518e7ef7e9..6899bbc46a6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4935,6 +4935,7 @@ dependencies = [ "web-sys", "wgpu-core", "wgpu-hal", + "wgpu-precompile-macro", "wgpu-types", ] @@ -5152,6 +5153,16 @@ dependencies = [ "syn", ] +[[package]] +name = "wgpu-precompile-macro" +version = "26.0.0" +dependencies = [ + "hashbrown", + "naga", + "quote", + "syn", +] + [[package]] name = "wgpu-test" version = "26.0.0" diff --git a/Cargo.toml b/Cargo.toml index 1fba257724c..fda6aa9cd0c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,7 @@ members = [ "wgpu-macros", "wgpu-types", "wgpu", + "wgpu/precompile-macro", "xtask", ] exclude = [] @@ -45,6 +46,7 @@ default-members = [ "wgpu-macros", "wgpu-types", "wgpu", + "wgpu/precompile-macro", "xtask", ] @@ -77,6 +79,7 @@ wgpu = { version = "26.0.0", path = "./wgpu", default-features = false, features wgpu-core = { version = "26.0.0", path = "./wgpu-core" } wgpu-hal = { version = "26.0.0", path = "./wgpu-hal" } wgpu-macros = { version = "26.0.0", path = "./wgpu-macros" } +wgpu-precompile-macro = { version = "26.0.0", path = "./wgpu/precompile-macro" } wgpu-test = { version = "26.0.0", path = "./tests" } wgpu-types = { version = "26.0.0", path = "./wgpu-types", default-features = false } diff --git a/wgpu-hal/src/auxil/dxgi/result.rs b/wgpu-hal/src/auxil/dxgi/result.rs index 256e0827879..f3d77e27be1 100644 --- a/wgpu-hal/src/auxil/dxgi/result.rs +++ b/wgpu-hal/src/auxil/dxgi/result.rs @@ -1,28 +1,28 @@ -use windows::Win32::{Foundation, Graphics::Dxgi}; - -pub(crate) trait HResult { - fn into_device_result(self, description: &str) -> Result; -} -impl HResult for windows::core::Result { - fn into_device_result(self, description: &str) -> Result { - #![allow(unreachable_code)] - - self.map_err(|err| { - log::error!("{description} failed: {err}"); - - match err.code() { - Foundation::E_OUTOFMEMORY => crate::DeviceError::OutOfMemory, - Dxgi::DXGI_ERROR_DEVICE_RESET | Dxgi::DXGI_ERROR_DEVICE_REMOVED => { - #[cfg(feature = "device_lost_panic")] - panic!("{description} failed: Device lost ({err})"); - crate::DeviceError::Lost - } - _ => { - #[cfg(feature = "internal_error_panic")] - panic!("{description} failed: {err}"); - crate::DeviceError::Unexpected - } - } - }) - } -} +use windows::Win32::{Foundation, Graphics::Dxgi}; + +pub(crate) trait HResult { + fn into_device_result(self, description: &str) -> Result; +} +impl HResult for windows::core::Result { + fn into_device_result(self, description: &str) -> Result { + #![allow(unreachable_code)] + + self.map_err(|err| { + log::error!("{description} failed: {err}"); + + match err.code() { + Foundation::E_OUTOFMEMORY => crate::DeviceError::OutOfMemory, + Dxgi::DXGI_ERROR_DEVICE_RESET | Dxgi::DXGI_ERROR_DEVICE_REMOVED => { + #[cfg(feature = "device_lost_panic")] + panic!("{description} failed: Device lost ({err})"); + crate::DeviceError::Lost + } + _ => { + #[cfg(feature = "internal_error_panic")] + panic!("{description} failed: {err}"); + crate::DeviceError::Unexpected + } + } + }) + } +} diff --git a/wgpu/Cargo.toml b/wgpu/Cargo.toml index a6a37da6cb4..92fb1fbc556 100644 --- a/wgpu/Cargo.toml +++ b/wgpu/Cargo.toml @@ -178,6 +178,8 @@ std = ["raw-window-handle/std", "wgpu-types/std", "wgpu-core?/std"] ## based on whether `std` is enabled or not. parking_lot = ["dep:parking_lot"] +precompile = ["dep:wgpu-precompile-macro"] + ######################### # Standard Dependencies # ######################### @@ -185,6 +187,7 @@ parking_lot = ["dep:parking_lot"] [dependencies] naga = { workspace = true, optional = true, features = ["termcolor"] } wgpu-core = { workspace = true, optional = true } +wgpu-precompile-macro = { workspace = true, optional = true } wgpu-types.workspace = true # Needed for both wgpu-core and webgpu @@ -199,6 +202,7 @@ profiling.workspace = true raw-window-handle = { workspace = true, features = ["alloc"] } static_assertions.workspace = true + ######################################## # Target Specific Feature Dependencies # ######################################## diff --git a/wgpu/src/macros.rs b/wgpu/src/macros.rs index 537756adb92..8b0a7235825 100644 --- a/wgpu/src/macros.rs +++ b/wgpu/src/macros.rs @@ -230,9 +230,18 @@ macro_rules! hal_type_gles { }; } +#[cfg(feature = "precompile")] +macro_rules! precompile_wgsl { + () => { + $crate::__macro_helpers::precompile($crate) + }; +} + #[doc(hidden)] pub mod helpers { pub use alloc::borrow::Cow; pub use core::{include_bytes, include_str}; + #[cfg(feature = "precompile")] + pub use wgpu_precompile_macro::precompile; pub use Some; } From d9745486e7673aefc5f19a9ec4beda3b96b61ea0 Mon Sep 17 00:00:00 2001 From: SupaMaggie70Incorporated Date: Thu, 11 Sep 2025 23:05:09 -0500 Subject: [PATCH 02/47] More work for macro that wasn't commited? --- wgpu/precompile-macro/Cargo.toml | 31 +++++++ wgpu/precompile-macro/src/lib.rs | 147 +++++++++++++++++++++++++++++++ 2 files changed, 178 insertions(+) create mode 100644 wgpu/precompile-macro/Cargo.toml create mode 100644 wgpu/precompile-macro/src/lib.rs diff --git a/wgpu/precompile-macro/Cargo.toml b/wgpu/precompile-macro/Cargo.toml new file mode 100644 index 00000000000..d3910a2ba0d --- /dev/null +++ b/wgpu/precompile-macro/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "wgpu-precompile-macro" +version.workspace = true +authors.workspace = true +edition.workspace = true +description = "Macros for wgpu that enables precompiling shaders" +homepage.workspace = true +repository.workspace = true +keywords.workspace = true +license.workspace = true + +[lib] +proc-macro = true + +[features] + +[dependencies] +naga = { workspace = true, features = [ + "spv-in", + "wgsl-in", + "glsl-in", + "msl-out", + "wgsl-out", + "spv-out", + "hlsl-out", + "glsl-out", +] } + +quote.workspace = true +syn = { workspace = true, features = ["full"] } +hashbrown.workspace = true diff --git a/wgpu/precompile-macro/src/lib.rs b/wgpu/precompile-macro/src/lib.rs new file mode 100644 index 00000000000..ad316078961 --- /dev/null +++ b/wgpu/precompile-macro/src/lib.rs @@ -0,0 +1,147 @@ +use std::path::PathBuf; + +use hashbrown::HashSet; + +use proc_macro::TokenStream; +use syn::{parse::Parse, parse_macro_input, Ident, Path}; + +enum SourceType { + Wgsl, + Glsl, + Spirv, +} +impl SourceType { + fn parse(str: &str) -> Option { + Some(match str { + "wgsl" => Self::Wgsl, + "glsl" => Self::Glsl, + "spirv" => Self::Spirv, + _ => return None, + }) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +enum SourceTarget { + Wgsl, + Msl, + Hlsl, + Spirv, + Glsl, + Dxil, +} +impl SourceTarget { + fn parse(str: &str) -> Option { + Some(match str { + "wgsl" => Self::Wgsl, + "msl" => Self::Msl, + "hlsl" => Self::Hlsl, + "spirv" => Self::Spirv, + "glsl" => Self::Glsl, + "dxil" => Self::Dxil, + _ => return None, + }) + } +} + +enum ShaderSource { + String(String), + File(Vec), +} +impl ShaderSource { + fn expect_bytes(self) -> Vec { + match self { + Self::File(f) => f, + Self::String(_) => panic!("Spirv input must be a file path"), + } + } + fn expect_string(self) -> String { + match self { + Self::String(s) => s, + Self::File(file) => String::from_utf8(file) + .expect("Expected string input, but file couldn't be parsed as UTF-8"), + } + } +} + +struct MacroArgs { + wgpu_crate: Path, + source_type: SourceType, + source: ShaderSource, + targets: HashSet, +} +impl Parse for MacroArgs { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let wgpu_crate: Path = input.parse()?; + let source_type = + SourceType::parse(&input.parse::()?.to_string()).expect("Invalid source type"); + let is_file_path = input.parse::()?.value; + let source_literal = input.parse::()?.value(); + + let mut targets = HashSet::new(); + while !input.is_empty() { + let target = SourceTarget::parse(&input.parse::()?.to_string()) + .expect("Invalid target type"); + targets.insert(target); + } + + let source = if is_file_path { + let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap(); + let relative_path = PathBuf::from(source_literal); + let path_to_read = if relative_path.is_relative() { + PathBuf::from(manifest_dir).join(relative_path) + } else { + relative_path + }; + let bytes = std::fs::read(path_to_read).expect("Failed to read input file"); + ShaderSource::File(bytes) + } else { + ShaderSource::String(source_literal) + }; + Ok(Self { + wgpu_crate, + source_type, + source, + targets, + }) + } +} + +/// This is to be re-exported by wgpu in a certain way, so that it can refer to items in the `wgpu` crate +/// +/// Input format: +/// precompile!(wgpu_crate_name, source_type, is_file_path, source_string, targets...) +#[proc_macro] +pub fn precompile(input: TokenStream) -> TokenStream { + let args = parse_macro_input!(input as MacroArgs); + let module = match args.source_type { + SourceType::Spirv => { + let source = args.source.expect_bytes(); + let options = naga::front::spv::Options { + adjust_coordinate_space: false, // we require NDC_Y_UP feature + strict_capabilities: true, + block_ctx_dump_prefix: None, + }; + naga::front::spv::parse_u8_slice(&source, &options) + .expect("Naga failed to parse SPIR-V input") + } + SourceType::Wgsl => { + let source = args.source.expect_string(); + naga::front::wgsl::parse_str(&source).expect("Nagaဠfailed to parse WGSL input") + } + SourceType::Glsl => { + todo!() + } + }; + + let module_info: naga::valid::ModuleInfo = naga::valid::Validator::new( + naga::valid::ValidationFlags::all(), + naga::valid::Capabilities::all(), + ) + .subgroup_stages(naga::valid::ShaderStages::all()) + .subgroup_operations(naga::valid::SubgroupOperationSet::all()) + .validate(&module) + .expect("Naga failed to validate module"); + + todo!() +} From c1f9549bbf9516a65744694d358155a6af2481d7 Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Fri, 12 Sep 2025 00:19:51 -0500 Subject: [PATCH 03/47] Worked on macro a lot --- examples/features/src/hello_triangle/mod.rs | 30 +-- wgpu/precompile-macro/src/lib.rs | 192 +++++++++++++++++--- wgpu/src/macros.rs | 2 + 3 files changed, 190 insertions(+), 34 deletions(-) diff --git a/examples/features/src/hello_triangle/mod.rs b/examples/features/src/hello_triangle/mod.rs index eca99f6bb37..895da43893e 100644 --- a/examples/features/src/hello_triangle/mod.rs +++ b/examples/features/src/hello_triangle/mod.rs @@ -1,4 +1,3 @@ -use std::borrow::Cow; use winit::{ event::{Event, WindowEvent}, event_loop::EventLoop, @@ -27,23 +26,17 @@ async fn run(event_loop: EventLoop<()>, window: Window) { let (device, queue) = adapter .request_device(&wgpu::DeviceDescriptor { label: None, - required_features: wgpu::Features::empty(), + required_features: wgpu::Features::EXPERIMENTAL_PASSTHROUGH_SHADERS, // Make sure we use the texture resolution limits from the adapter, so we can support images the size of the swapchain. required_limits: wgpu::Limits::downlevel_webgl2_defaults() .using_resolution(adapter.limits()), - experimental_features: wgpu::ExperimentalFeatures::disabled(), + experimental_features: unsafe { wgpu::ExperimentalFeatures::enabled() }, memory_hints: wgpu::MemoryHints::MemoryUsage, trace: wgpu::Trace::Off, }) .await .expect("Failed to create device"); - // Load the shaders from disk - let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { - label: None, - source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("shader.wgsl"))), - }); - let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: None, bind_group_layouts: &[], @@ -53,17 +46,24 @@ async fn run(event_loop: EventLoop<()>, window: Window) { let swapchain_capabilities = surface.get_capabilities(&adapter); let swapchain_format = swapchain_capabilities.formats[0]; + let vs_shader = unsafe { + device.create_shader_module_passthrough(wgpu::__macro_helpers::precompile!(wgpu wgsl true "src/hello_triangle/shader.wgsl" "vs_main" vertex spirv msl hlsl wgsl glsl dxil)) + }; + let fs_shader = unsafe { + device.create_shader_module_passthrough(wgpu::__macro_helpers::precompile!(wgpu wgsl true "src/hello_triangle/shader.wgsl" "fs_main" fragment spirv msl hlsl wgsl glsl dxil)) + }; + let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { label: None, layout: Some(&pipeline_layout), vertex: wgpu::VertexState { - module: &shader, + module: &vs_shader, entry_point: Some("vs_main"), buffers: &[], compilation_options: Default::default(), }, fragment: Some(wgpu::FragmentState { - module: &shader, + module: &fs_shader, entry_point: Some("fs_main"), compilation_options: Default::default(), targets: &[Some(swapchain_format.into())], @@ -86,7 +86,13 @@ async fn run(event_loop: EventLoop<()>, window: Window) { // Have the closure take ownership of the resources. // `event_loop.run` never returns, therefore we must do this to ensure // the resources are properly cleaned up. - let _ = (&instance, &adapter, &shader, &pipeline_layout); + let _ = ( + &instance, + &adapter, + &vs_shader, + &fs_shader, + &pipeline_layout, + ); if let Event::WindowEvent { window_id: _, diff --git a/wgpu/precompile-macro/src/lib.rs b/wgpu/precompile-macro/src/lib.rs index ad316078961..a453bc5b50d 100644 --- a/wgpu/precompile-macro/src/lib.rs +++ b/wgpu/precompile-macro/src/lib.rs @@ -1,8 +1,7 @@ -use std::path::PathBuf; - use hashbrown::HashSet; - use proc_macro::TokenStream; +use quote::quote; +use std::path::PathBuf; use syn::{parse::Parse, parse_macro_input, Ident, Path}; enum SourceType { @@ -11,18 +10,18 @@ enum SourceType { Spirv, } impl SourceType { - fn parse(str: &str) -> Option { - Some(match str { + fn parse(str: &str) -> Self { + match str { "wgsl" => Self::Wgsl, "glsl" => Self::Glsl, "spirv" => Self::Spirv, - _ => return None, - }) + other => panic!("Unrecognized source type: {other}"), + } } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] -enum SourceTarget { +enum CompileTarget { Wgsl, Msl, Hlsl, @@ -30,17 +29,28 @@ enum SourceTarget { Glsl, Dxil, } -impl SourceTarget { - fn parse(str: &str) -> Option { - Some(match str { +impl CompileTarget { + fn parse(str: &str) -> Self { + match str { "wgsl" => Self::Wgsl, "msl" => Self::Msl, "hlsl" => Self::Hlsl, "spirv" => Self::Spirv, "glsl" => Self::Glsl, "dxil" => Self::Dxil, - _ => return None, - }) + other => panic!("Unrecognized compile target: {other}"), + } + } +} + +fn parse_shader_stage(str: &str) -> naga::ShaderStage { + match str { + "vertex" => naga::ShaderStage::Vertex, + "fragment" => naga::ShaderStage::Fragment, + "compute" => naga::ShaderStage::Compute, + "Task" => naga::ShaderStage::Task, + "Mesh" => naga::ShaderStage::Mesh, + other => panic!("Unrecognized shader stage: {other}"), } } @@ -68,20 +78,22 @@ struct MacroArgs { wgpu_crate: Path, source_type: SourceType, source: ShaderSource, - targets: HashSet, + targets: HashSet, + entry_point: String, + shader_stage: naga::ShaderStage, } impl Parse for MacroArgs { fn parse(input: syn::parse::ParseStream) -> syn::Result { let wgpu_crate: Path = input.parse()?; - let source_type = - SourceType::parse(&input.parse::()?.to_string()).expect("Invalid source type"); + let source_type = SourceType::parse(&input.parse::()?.to_string()); let is_file_path = input.parse::()?.value; let source_literal = input.parse::()?.value(); + let entry_point = input.parse::()?.value(); + let shader_stage = parse_shader_stage(&input.parse::()?.to_string()); let mut targets = HashSet::new(); while !input.is_empty() { - let target = SourceTarget::parse(&input.parse::()?.to_string()) - .expect("Invalid target type"); + let target = CompileTarget::parse(&input.parse::()?.to_string()); targets.insert(target); } @@ -102,6 +114,8 @@ impl Parse for MacroArgs { wgpu_crate, source_type, source, + entry_point, + shader_stage, targets, }) } @@ -110,7 +124,7 @@ impl Parse for MacroArgs { /// This is to be re-exported by wgpu in a certain way, so that it can refer to items in the `wgpu` crate /// /// Input format: -/// precompile!(wgpu_crate_name, source_type, is_file_path, source_string, targets...) +/// precompile!(wgpu_crate_name source_type is_file_path source_string entry_point shader_stage targets...) #[proc_macro] pub fn precompile(input: TokenStream) -> TokenStream { let args = parse_macro_input!(input as MacroArgs); @@ -127,10 +141,10 @@ pub fn precompile(input: TokenStream) -> TokenStream { } SourceType::Wgsl => { let source = args.source.expect_string(); - naga::front::wgsl::parse_str(&source).expect("Nagaဠfailed to parse WGSL input") + naga::front::wgsl::parse_str(&source).expect("Naga failed to parse WGSL input") } SourceType::Glsl => { - todo!() + panic!("GLSL parsing support is not yet available") } }; @@ -143,5 +157,139 @@ pub fn precompile(input: TokenStream) -> TokenStream { .validate(&module) .expect("Naga failed to validate module"); - todo!() + let entry_point_idx = module + .entry_points + .iter() + .position(|a| a.name == args.entry_point) + .expect("Requested entry point not present in module"); + + if args.shader_stage != module.entry_points[entry_point_idx].stage { + panic!( + "Incorrect shader stage: given {:?} but entry point has stage {:?}", + args.shader_stage, module.entry_points[entry_point_idx].stage + ) + } + + let wgpu_path = &args.wgpu_crate; + + let spirv_tokens = if args.targets.contains(&CompileTarget::Spirv) { + let spirv_data = naga::back::spv::write_vec( + &module, + &module_info, + &naga::back::spv::Options::default(), + Some(&naga::back::spv::PipelineOptions { + shader_stage: args.shader_stage, + entry_point: args.entry_point.clone(), + }), + ) + .expect("Naga failed to write SPIR-V code"); + quote! { + #wgpu_path::__macro_helpers::Some(#wgpu_path::__macro_helpers::Cow::Borrowed(&[#(#spirv_data),*])) + } + } else { + quote! { + #wgpu_path::__macro_helpers::None + } + }; + + let msl_tokens = if args.targets.contains(&CompileTarget::Msl) { + let msl_str = naga::back::msl::write_string( + &module, + &module_info, + &naga::back::msl::Options::default(), + &naga::back::msl::PipelineOptions { + entry_point: Some((args.shader_stage, args.entry_point.clone())), + ..Default::default() + }, + ) + .expect("Naga failed to write MSL code") + .0; + quote! { + #wgpu_path::__macro_helpers::Some(#wgpu_path::__macro_helpers::Cow::Borrowed(#msl_str)) + } + } else { + quote! { + #wgpu_path::__macro_helpers::None + } + }; + + let hlsl_tokens = if args.targets.contains(&CompileTarget::Hlsl) { + let mut hlsl_str = String::new(); + naga::back::hlsl::Writer::new( + &mut hlsl_str, + &naga::back::hlsl::Options::default(), + &naga::back::hlsl::PipelineOptions { + entry_point: Some((args.shader_stage, args.entry_point.clone())), + }, + ) + .write(&module, &module_info, None) + .expect("Naga failed to write HLSL code"); + quote! { + #wgpu_path::__macro_helpers::Some(#wgpu_path::__macro_helpers::Cow::Borrowed(#hlsl_str)) + } + } else { + quote! { + #wgpu_path::__macro_helpers::None + } + }; + + let wgsl_tokens = if args.targets.contains(&CompileTarget::Wgsl) { + let mut wgsl_str = String::new(); + naga::back::wgsl::Writer::new(&mut wgsl_str, naga::back::wgsl::WriterFlags::empty()) + .write(&module, &module_info) + .expect("Naga failed to write WGSL code"); + quote! { + #wgpu_path::__macro_helpers::Some(#wgpu_path::__macro_helpers::Cow::Borrowed(#wgsl_str)) + } + } else { + quote! { + #wgpu_path::__macro_helpers::None + } + }; + + let glsl_tokens = if args.targets.contains(&CompileTarget::Glsl) { + let mut glsl_str = String::new(); + naga::back::glsl::Writer::new( + &mut glsl_str, + &module, + &module_info, + &naga::back::glsl::Options::default(), + &naga::back::glsl::PipelineOptions { + shader_stage: args.shader_stage, + entry_point: args.entry_point.clone(), + multiview: None, + }, + naga::proc::BoundsCheckPolicies::default(), + ) + .expect("Naga failed to create GLSL writer") + .write() + .expect("Naga failed write GLSL code"); + quote! { + #wgpu_path::__macro_helpers::Some(#wgpu_path::__macro_helpers::Cow::Borrowed(#glsl_str)) + } + } else { + quote! { + #wgpu_path::__macro_helpers::None + } + }; + + let [x, y, z] = module.entry_points[entry_point_idx].workgroup_size; + let entry_point = &args.entry_point; + + quote! { + #wgpu_path::ShaderModuleDescriptorPassthrough { + // TODO: make this something else when file name is provided + label: #wgpu_path::__macro_helpers::None, + entry_point: #wgpu_path::__macro_helpers::String::from(#entry_point), + num_workgroups: (#x, #y, #z), + runtime_checks: #wgpu_path::ShaderRuntimeChecks::unchecked(), + spirv: #spirv_tokens, + dxil: #wgpu_path::__macro_helpers::None, + msl: #msl_tokens, + hlsl: #hlsl_tokens, + glsl: #glsl_tokens, + wgsl: #wgsl_tokens, + } + } + .into() } diff --git a/wgpu/src/macros.rs b/wgpu/src/macros.rs index 8b0a7235825..9f6fefb6bc4 100644 --- a/wgpu/src/macros.rs +++ b/wgpu/src/macros.rs @@ -240,8 +240,10 @@ macro_rules! precompile_wgsl { #[doc(hidden)] pub mod helpers { pub use alloc::borrow::Cow; + pub use alloc::string::String; pub use core::{include_bytes, include_str}; #[cfg(feature = "precompile")] pub use wgpu_precompile_macro::precompile; + pub use None; pub use Some; } From 95491ce7c0cf42f4fb8e7108d8cb397315d0377e Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Fri, 12 Sep 2025 00:22:55 -0500 Subject: [PATCH 04/47] Removed shader stage parameter from the thing --- examples/features/src/hello_triangle/mod.rs | 4 +-- wgpu/precompile-macro/src/lib.rs | 36 +++++---------------- 2 files changed, 10 insertions(+), 30 deletions(-) diff --git a/examples/features/src/hello_triangle/mod.rs b/examples/features/src/hello_triangle/mod.rs index 895da43893e..55d6cc90b27 100644 --- a/examples/features/src/hello_triangle/mod.rs +++ b/examples/features/src/hello_triangle/mod.rs @@ -47,10 +47,10 @@ async fn run(event_loop: EventLoop<()>, window: Window) { let swapchain_format = swapchain_capabilities.formats[0]; let vs_shader = unsafe { - device.create_shader_module_passthrough(wgpu::__macro_helpers::precompile!(wgpu wgsl true "src/hello_triangle/shader.wgsl" "vs_main" vertex spirv msl hlsl wgsl glsl dxil)) + device.create_shader_module_passthrough(wgpu::__macro_helpers::precompile!(wgpu wgsl true "src/hello_triangle/shader.wgsl" "vs_main" spirv msl hlsl wgsl glsl dxil)) }; let fs_shader = unsafe { - device.create_shader_module_passthrough(wgpu::__macro_helpers::precompile!(wgpu wgsl true "src/hello_triangle/shader.wgsl" "fs_main" fragment spirv msl hlsl wgsl glsl dxil)) + device.create_shader_module_passthrough(wgpu::__macro_helpers::precompile!(wgpu wgsl true "src/hello_triangle/shader.wgsl" "fs_main" spirv msl hlsl wgsl glsl dxil)) }; let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { diff --git a/wgpu/precompile-macro/src/lib.rs b/wgpu/precompile-macro/src/lib.rs index a453bc5b50d..8e33576e5c9 100644 --- a/wgpu/precompile-macro/src/lib.rs +++ b/wgpu/precompile-macro/src/lib.rs @@ -43,17 +43,6 @@ impl CompileTarget { } } -fn parse_shader_stage(str: &str) -> naga::ShaderStage { - match str { - "vertex" => naga::ShaderStage::Vertex, - "fragment" => naga::ShaderStage::Fragment, - "compute" => naga::ShaderStage::Compute, - "Task" => naga::ShaderStage::Task, - "Mesh" => naga::ShaderStage::Mesh, - other => panic!("Unrecognized shader stage: {other}"), - } -} - enum ShaderSource { String(String), File(Vec), @@ -80,7 +69,6 @@ struct MacroArgs { source: ShaderSource, targets: HashSet, entry_point: String, - shader_stage: naga::ShaderStage, } impl Parse for MacroArgs { fn parse(input: syn::parse::ParseStream) -> syn::Result { @@ -89,7 +77,6 @@ impl Parse for MacroArgs { let is_file_path = input.parse::()?.value; let source_literal = input.parse::()?.value(); let entry_point = input.parse::()?.value(); - let shader_stage = parse_shader_stage(&input.parse::()?.to_string()); let mut targets = HashSet::new(); while !input.is_empty() { @@ -115,7 +102,6 @@ impl Parse for MacroArgs { source_type, source, entry_point, - shader_stage, targets, }) } @@ -157,18 +143,13 @@ pub fn precompile(input: TokenStream) -> TokenStream { .validate(&module) .expect("Naga failed to validate module"); - let entry_point_idx = module + let entry_point = module .entry_points .iter() - .position(|a| a.name == args.entry_point) + .find(|a| a.name == args.entry_point) .expect("Requested entry point not present in module"); - - if args.shader_stage != module.entry_points[entry_point_idx].stage { - panic!( - "Incorrect shader stage: given {:?} but entry point has stage {:?}", - args.shader_stage, module.entry_points[entry_point_idx].stage - ) - } + let shader_stage = entry_point.stage; + let [x, y, z] = entry_point.workgroup_size; let wgpu_path = &args.wgpu_crate; @@ -178,7 +159,7 @@ pub fn precompile(input: TokenStream) -> TokenStream { &module_info, &naga::back::spv::Options::default(), Some(&naga::back::spv::PipelineOptions { - shader_stage: args.shader_stage, + shader_stage, entry_point: args.entry_point.clone(), }), ) @@ -198,7 +179,7 @@ pub fn precompile(input: TokenStream) -> TokenStream { &module_info, &naga::back::msl::Options::default(), &naga::back::msl::PipelineOptions { - entry_point: Some((args.shader_stage, args.entry_point.clone())), + entry_point: Some((shader_stage, args.entry_point.clone())), ..Default::default() }, ) @@ -219,7 +200,7 @@ pub fn precompile(input: TokenStream) -> TokenStream { &mut hlsl_str, &naga::back::hlsl::Options::default(), &naga::back::hlsl::PipelineOptions { - entry_point: Some((args.shader_stage, args.entry_point.clone())), + entry_point: Some((shader_stage, args.entry_point.clone())), }, ) .write(&module, &module_info, None) @@ -255,7 +236,7 @@ pub fn precompile(input: TokenStream) -> TokenStream { &module_info, &naga::back::glsl::Options::default(), &naga::back::glsl::PipelineOptions { - shader_stage: args.shader_stage, + shader_stage, entry_point: args.entry_point.clone(), multiview: None, }, @@ -273,7 +254,6 @@ pub fn precompile(input: TokenStream) -> TokenStream { } }; - let [x, y, z] = module.entry_points[entry_point_idx].workgroup_size; let entry_point = &args.entry_point; quote! { From 9d52aca3626370803caf6782e09f3d473506ea42 Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Fri, 12 Sep 2025 00:46:18 -0500 Subject: [PATCH 05/47] Updated macro slightly --- examples/features/src/hello_triangle/mod.rs | 11 +++- wgpu/Cargo.toml | 9 +-- wgpu/precompile-macro/Cargo.toml | 17 +++--- wgpu/precompile-macro/src/lib.rs | 67 +++++++++++++++++---- wgpu/src/macros.rs | 5 +- 5 files changed, 79 insertions(+), 30 deletions(-) diff --git a/examples/features/src/hello_triangle/mod.rs b/examples/features/src/hello_triangle/mod.rs index 55d6cc90b27..ffa91fc702a 100644 --- a/examples/features/src/hello_triangle/mod.rs +++ b/examples/features/src/hello_triangle/mod.rs @@ -45,12 +45,17 @@ async fn run(event_loop: EventLoop<()>, window: Window) { let swapchain_capabilities = surface.get_capabilities(&adapter); let swapchain_format = swapchain_capabilities.formats[0]; - let vs_shader = unsafe { - device.create_shader_module_passthrough(wgpu::__macro_helpers::precompile!(wgpu wgsl true "src/hello_triangle/shader.wgsl" "vs_main" spirv msl hlsl wgsl glsl dxil)) + device.create_shader_module_passthrough(wgpu::precompile_wgsl!( + "src/hello_triangle/shader.wgsl", + "vs_main" + )) }; let fs_shader = unsafe { - device.create_shader_module_passthrough(wgpu::__macro_helpers::precompile!(wgpu wgsl true "src/hello_triangle/shader.wgsl" "fs_main" spirv msl hlsl wgsl glsl dxil)) + device.create_shader_module_passthrough(wgpu::precompile_wgsl!( + "src/hello_triangle/shader.wgsl", + "fs_main" + )) }; let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { diff --git a/wgpu/Cargo.toml b/wgpu/Cargo.toml index 92fb1fbc556..b6601849ec3 100644 --- a/wgpu/Cargo.toml +++ b/wgpu/Cargo.toml @@ -44,19 +44,20 @@ default = [ # -------------------------------------------------------------------- ## Enables the DX12 backend on Windows. -dx12 = ["wgpu-core?/dx12"] +dx12 = ["wgpu-core?/dx12", "wgpu-precompile-macro/hlsl"] ## Enables the Metal backend on macOS & iOS. -metal = ["wgpu-core?/metal"] +metal = ["wgpu-core?/metal", "wgpu-precompile-macro/msl"] ## Enables the Vulkan backend on Windows, Linux, and Android. -vulkan = ["wgpu-core?/vulkan"] +vulkan = ["wgpu-core?/vulkan", "wgpu-precompile-macro/spv"] ## Enables the OpenGL/GLES backend on Windows, Linux, Android, and Emscripten. -gles = ["wgpu-core?/gles"] +gles = ["wgpu-core?/gles", "wgpu-precompile-macro/glsl"] ## Enables the WebGPU backend on WebAssembly. webgpu = [ + "wgpu-precompile-macro/wgsl", "web", "naga?/wgsl-out", "dep:wasm-bindgen-futures", diff --git a/wgpu/precompile-macro/Cargo.toml b/wgpu/precompile-macro/Cargo.toml index d3910a2ba0d..b05d6d222e5 100644 --- a/wgpu/precompile-macro/Cargo.toml +++ b/wgpu/precompile-macro/Cargo.toml @@ -13,18 +13,15 @@ license.workspace = true proc-macro = true [features] +msl = ["naga/msl-out"] +wgsl = ["naga/wgsl-out"] +spv = ["naga/spv-out"] +glsl = ["naga/glsl-out"] +hlsl = ["naga/hlsl-out"] +dxil = ["hlsl"] [dependencies] -naga = { workspace = true, features = [ - "spv-in", - "wgsl-in", - "glsl-in", - "msl-out", - "wgsl-out", - "spv-out", - "hlsl-out", - "glsl-out", -] } +naga = { workspace = true, features = ["spv-in", "wgsl-in", "glsl-in"] } quote.workspace = true syn = { workspace = true, features = ["full"] } diff --git a/wgpu/precompile-macro/src/lib.rs b/wgpu/precompile-macro/src/lib.rs index 8e33576e5c9..75e65093c34 100644 --- a/wgpu/precompile-macro/src/lib.rs +++ b/wgpu/precompile-macro/src/lib.rs @@ -28,6 +28,7 @@ enum CompileTarget { Spirv, Glsl, Dxil, + AllSupported, } impl CompileTarget { fn parse(str: &str) -> Self { @@ -38,11 +39,13 @@ impl CompileTarget { "spirv" => Self::Spirv, "glsl" => Self::Glsl, "dxil" => Self::Dxil, + "all" => Self::AllSupported, other => panic!("Unrecognized compile target: {other}"), } } } +#[derive(Clone)] enum ShaderSource { String(String), File(Vec), @@ -69,6 +72,7 @@ struct MacroArgs { source: ShaderSource, targets: HashSet, entry_point: String, + file_name: Option, } impl Parse for MacroArgs { fn parse(input: syn::parse::ParseStream) -> syn::Result { @@ -84,18 +88,24 @@ impl Parse for MacroArgs { targets.insert(target); } - let source = if is_file_path { + let (source, file_name) = if is_file_path { let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap(); let relative_path = PathBuf::from(source_literal); + let file_name = relative_path + .file_name() + .unwrap() + .to_str() + .unwrap() + .to_owned(); let path_to_read = if relative_path.is_relative() { PathBuf::from(manifest_dir).join(relative_path) } else { relative_path }; let bytes = std::fs::read(path_to_read).expect("Failed to read input file"); - ShaderSource::File(bytes) + (ShaderSource::File(bytes), Some(file_name)) } else { - ShaderSource::String(source_literal) + (ShaderSource::String(source_literal), None) }; Ok(Self { wgpu_crate, @@ -103,9 +113,39 @@ impl Parse for MacroArgs { source, entry_point, targets, + file_name, }) } } +impl MacroArgs { + fn target_enabled(&self, target: CompileTarget) -> bool { + if self.targets.contains(&target) { + return true; + } else if self.targets.contains(&CompileTarget::AllSupported) { + #[cfg(feature = "dxil")] + if target == CompileTarget::Dxil { + return true; + } + #[cfg(feature = "glsl")] + if target == CompileTarget::Glsl { + return true; + } + #[cfg(feature = "hlsl")] + if target == CompileTarget::Hlsl { + return true; + } + #[cfg(feature = "spv")] + if target == CompileTarget::Spirv { + return true; + } + #[cfg(feature = "msl")] + if target == CompileTarget::Msl { + return true; + } + } + false + } +} /// This is to be re-exported by wgpu in a certain way, so that it can refer to items in the `wgpu` crate /// @@ -116,7 +156,7 @@ pub fn precompile(input: TokenStream) -> TokenStream { let args = parse_macro_input!(input as MacroArgs); let module = match args.source_type { SourceType::Spirv => { - let source = args.source.expect_bytes(); + let source = args.source.clone().expect_bytes(); let options = naga::front::spv::Options { adjust_coordinate_space: false, // we require NDC_Y_UP feature strict_capabilities: true, @@ -126,7 +166,7 @@ pub fn precompile(input: TokenStream) -> TokenStream { .expect("Naga failed to parse SPIR-V input") } SourceType::Wgsl => { - let source = args.source.expect_string(); + let source = args.source.clone().expect_string(); naga::front::wgsl::parse_str(&source).expect("Naga failed to parse WGSL input") } SourceType::Glsl => { @@ -153,7 +193,7 @@ pub fn precompile(input: TokenStream) -> TokenStream { let wgpu_path = &args.wgpu_crate; - let spirv_tokens = if args.targets.contains(&CompileTarget::Spirv) { + let spirv_tokens = if args.target_enabled(CompileTarget::Spirv) { let spirv_data = naga::back::spv::write_vec( &module, &module_info, @@ -173,7 +213,7 @@ pub fn precompile(input: TokenStream) -> TokenStream { } }; - let msl_tokens = if args.targets.contains(&CompileTarget::Msl) { + let msl_tokens = if args.target_enabled(CompileTarget::Msl) { let msl_str = naga::back::msl::write_string( &module, &module_info, @@ -194,7 +234,7 @@ pub fn precompile(input: TokenStream) -> TokenStream { } }; - let hlsl_tokens = if args.targets.contains(&CompileTarget::Hlsl) { + let hlsl_tokens = if args.target_enabled(CompileTarget::Hlsl) { let mut hlsl_str = String::new(); naga::back::hlsl::Writer::new( &mut hlsl_str, @@ -214,7 +254,7 @@ pub fn precompile(input: TokenStream) -> TokenStream { } }; - let wgsl_tokens = if args.targets.contains(&CompileTarget::Wgsl) { + let wgsl_tokens = if args.target_enabled(CompileTarget::Wgsl) { let mut wgsl_str = String::new(); naga::back::wgsl::Writer::new(&mut wgsl_str, naga::back::wgsl::WriterFlags::empty()) .write(&module, &module_info) @@ -228,7 +268,7 @@ pub fn precompile(input: TokenStream) -> TokenStream { } }; - let glsl_tokens = if args.targets.contains(&CompileTarget::Glsl) { + let glsl_tokens = if args.target_enabled(CompileTarget::Glsl) { let mut glsl_str = String::new(); naga::back::glsl::Writer::new( &mut glsl_str, @@ -254,12 +294,17 @@ pub fn precompile(input: TokenStream) -> TokenStream { } }; + let label_tokens = match args.file_name { + Some(f) => quote! {#wgpu_path::__macro_helpers::Some(#f)}, + None => quote! {#wgpu_path::__macro_helpers::None}, + }; + let entry_point = &args.entry_point; quote! { #wgpu_path::ShaderModuleDescriptorPassthrough { // TODO: make this something else when file name is provided - label: #wgpu_path::__macro_helpers::None, + label: #label_tokens, entry_point: #wgpu_path::__macro_helpers::String::from(#entry_point), num_workgroups: (#x, #y, #z), runtime_checks: #wgpu_path::ShaderRuntimeChecks::unchecked(), diff --git a/wgpu/src/macros.rs b/wgpu/src/macros.rs index 9f6fefb6bc4..55b9d39b52b 100644 --- a/wgpu/src/macros.rs +++ b/wgpu/src/macros.rs @@ -230,10 +230,11 @@ macro_rules! hal_type_gles { }; } +#[macro_export] #[cfg(feature = "precompile")] macro_rules! precompile_wgsl { - () => { - $crate::__macro_helpers::precompile($crate) + ($path: literal, $entry: literal) => { + $crate::__macro_helpers::precompile!($crate wgsl true $path $entry all) }; } From 80509c6d69b5e51912da7905590c026c47b3e5ae Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Fri, 12 Sep 2025 00:53:13 -0500 Subject: [PATCH 06/47] Updated some stuff --- wgpu/precompile-macro/src/lib.rs | 3 ++- wgpu/src/macros.rs | 10 ++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/wgpu/precompile-macro/src/lib.rs b/wgpu/precompile-macro/src/lib.rs index 75e65093c34..42c78979bbd 100644 --- a/wgpu/precompile-macro/src/lib.rs +++ b/wgpu/precompile-macro/src/lib.rs @@ -157,6 +157,7 @@ pub fn precompile(input: TokenStream) -> TokenStream { let module = match args.source_type { SourceType::Spirv => { let source = args.source.clone().expect_bytes(); + // This is yanked from wgpu-hal. Maybe at some point this kinda logic should be unified somewhere let options = naga::front::spv::Options { adjust_coordinate_space: false, // we require NDC_Y_UP feature strict_capabilities: true, @@ -307,7 +308,7 @@ pub fn precompile(input: TokenStream) -> TokenStream { label: #label_tokens, entry_point: #wgpu_path::__macro_helpers::String::from(#entry_point), num_workgroups: (#x, #y, #z), - runtime_checks: #wgpu_path::ShaderRuntimeChecks::unchecked(), + runtime_checks: #wgpu_path::ShaderRuntimeChecks::default(), spirv: #spirv_tokens, dxil: #wgpu_path::__macro_helpers::None, msl: #msl_tokens, diff --git a/wgpu/src/macros.rs b/wgpu/src/macros.rs index 55b9d39b52b..ac3ea1ad7d8 100644 --- a/wgpu/src/macros.rs +++ b/wgpu/src/macros.rs @@ -230,6 +230,16 @@ macro_rules! hal_type_gles { }; } +/// Precompile WGSL shader for all supported backends. Will fail at compiletime +/// in case of error. Note that file paths are relative to the `MANIFEST_DIR`, i.e. not the same +/// folder as the invoking file as in `include_bytes` or the like. +/// +/// This can always take in wgsl, spirv, and glsl input, even if the corresponding features aren't +/// enabled on the wgpu crate. +/// +/// ```ignore +/// device.create_shader_module_passthrough(precompile_wgsl!("path/to/file", "entry_point_name")) +/// ``` #[macro_export] #[cfg(feature = "precompile")] macro_rules! precompile_wgsl { From 59db4c7789c4fb63851430c158d3ba70a8bd1887 Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Fri, 12 Sep 2025 01:05:51 -0500 Subject: [PATCH 07/47] Made more stuff build properly --- examples/features/Cargo.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/features/Cargo.toml b/examples/features/Cargo.toml index 85c777f48b7..ab43e2eca16 100644 --- a/examples/features/Cargo.toml +++ b/examples/features/Cargo.toml @@ -56,7 +56,7 @@ wgpu-test.workspace = true [target.'cfg(not(target_arch = "wasm32"))'.dependencies] env_logger.workspace = true -wgpu.workspace = true +wgpu = { workspace = true, features = ["precompile"] } [target.'cfg(target_arch = "wasm32")'.dependencies] console_error_panic_hook.workspace = true @@ -67,6 +67,7 @@ wasm-bindgen-futures.workspace = true wgpu = { path = "../../wgpu", default-features = false, features = [ "wgsl", "std", + "precompile", ] } # We need these features in the framework examples and tests web-sys = { workspace = true, features = [ From 1dfc2b2f70ad6d9377684bc259e8edf107a27a26 Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Fri, 12 Sep 2025 01:26:41 -0500 Subject: [PATCH 08/47] Refactored slightly --- Cargo.toml | 6 +++--- .../precompile-macro => wgpu-precompile-macro}/Cargo.toml | 6 +++++- .../precompile-macro => wgpu-precompile-macro}/src/lib.rs | 7 +------ 3 files changed, 9 insertions(+), 10 deletions(-) rename {wgpu/precompile-macro => wgpu-precompile-macro}/Cargo.toml (68%) rename {wgpu/precompile-macro => wgpu-precompile-macro}/src/lib.rs (98%) diff --git a/Cargo.toml b/Cargo.toml index fda6aa9cd0c..3a8f99ffc19 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ members = [ "wgpu-macros", "wgpu-types", "wgpu", - "wgpu/precompile-macro", + "wgpu-precompile-macro", "xtask", ] exclude = [] @@ -46,7 +46,7 @@ default-members = [ "wgpu-macros", "wgpu-types", "wgpu", - "wgpu/precompile-macro", + "wgpu-precompile-macro", "xtask", ] @@ -79,7 +79,7 @@ wgpu = { version = "26.0.0", path = "./wgpu", default-features = false, features wgpu-core = { version = "26.0.0", path = "./wgpu-core" } wgpu-hal = { version = "26.0.0", path = "./wgpu-hal" } wgpu-macros = { version = "26.0.0", path = "./wgpu-macros" } -wgpu-precompile-macro = { version = "26.0.0", path = "./wgpu/precompile-macro" } +wgpu-precompile-macro = { version = "26.0.0", path = "./wgpu-precompile-macro" } wgpu-test = { version = "26.0.0", path = "./tests" } wgpu-types = { version = "26.0.0", path = "./wgpu-types", default-features = false } diff --git a/wgpu/precompile-macro/Cargo.toml b/wgpu-precompile-macro/Cargo.toml similarity index 68% rename from wgpu/precompile-macro/Cargo.toml rename to wgpu-precompile-macro/Cargo.toml index b05d6d222e5..ba6de9beeca 100644 --- a/wgpu/precompile-macro/Cargo.toml +++ b/wgpu-precompile-macro/Cargo.toml @@ -13,12 +13,16 @@ license.workspace = true proc-macro = true [features] + +# Wgpu enables these features if the corresponding backend's feature is enabled. +# That means even if we are compiling to Windows, the MSL feature will be enabled +# by default. So eventually to not include unnecessary shader files we should add +# extra logic to detect target platforms and such. msl = ["naga/msl-out"] wgsl = ["naga/wgsl-out"] spv = ["naga/spv-out"] glsl = ["naga/glsl-out"] hlsl = ["naga/hlsl-out"] -dxil = ["hlsl"] [dependencies] naga = { workspace = true, features = ["spv-in", "wgsl-in", "glsl-in"] } diff --git a/wgpu/precompile-macro/src/lib.rs b/wgpu-precompile-macro/src/lib.rs similarity index 98% rename from wgpu/precompile-macro/src/lib.rs rename to wgpu-precompile-macro/src/lib.rs index 42c78979bbd..c60f4f216cf 100644 --- a/wgpu/precompile-macro/src/lib.rs +++ b/wgpu-precompile-macro/src/lib.rs @@ -27,7 +27,6 @@ enum CompileTarget { Hlsl, Spirv, Glsl, - Dxil, AllSupported, } impl CompileTarget { @@ -38,7 +37,6 @@ impl CompileTarget { "hlsl" => Self::Hlsl, "spirv" => Self::Spirv, "glsl" => Self::Glsl, - "dxil" => Self::Dxil, "all" => Self::AllSupported, other => panic!("Unrecognized compile target: {other}"), } @@ -122,10 +120,6 @@ impl MacroArgs { if self.targets.contains(&target) { return true; } else if self.targets.contains(&CompileTarget::AllSupported) { - #[cfg(feature = "dxil")] - if target == CompileTarget::Dxil { - return true; - } #[cfg(feature = "glsl")] if target == CompileTarget::Glsl { return true; @@ -235,6 +229,7 @@ pub fn precompile(input: TokenStream) -> TokenStream { } }; + // TODO: compile DXIL if DXC can be detected let hlsl_tokens = if args.target_enabled(CompileTarget::Hlsl) { let mut hlsl_str = String::new(); naga::back::hlsl::Writer::new( From f7ecc4c55997ee4e16b7270c2753369a341415bd Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Fri, 12 Sep 2025 01:39:35 -0500 Subject: [PATCH 09/47] Tried to fix a compile fail --- wgpu-precompile-macro/src/lib.rs | 49 ++++++++++++++------------------ 1 file changed, 22 insertions(+), 27 deletions(-) diff --git a/wgpu-precompile-macro/src/lib.rs b/wgpu-precompile-macro/src/lib.rs index c60f4f216cf..9f1e5ad9e9b 100644 --- a/wgpu-precompile-macro/src/lib.rs +++ b/wgpu-precompile-macro/src/lib.rs @@ -117,27 +117,7 @@ impl Parse for MacroArgs { } impl MacroArgs { fn target_enabled(&self, target: CompileTarget) -> bool { - if self.targets.contains(&target) { - return true; - } else if self.targets.contains(&CompileTarget::AllSupported) { - #[cfg(feature = "glsl")] - if target == CompileTarget::Glsl { - return true; - } - #[cfg(feature = "hlsl")] - if target == CompileTarget::Hlsl { - return true; - } - #[cfg(feature = "spv")] - if target == CompileTarget::Spirv { - return true; - } - #[cfg(feature = "msl")] - if target == CompileTarget::Msl { - return true; - } - } - false + self.targets.contains(&target) || self.targets.contains(&CompileTarget::AllSupported) } } @@ -188,6 +168,11 @@ pub fn precompile(input: TokenStream) -> TokenStream { let wgpu_path = &args.wgpu_crate; + let none_tokens = quote! { + #wgpu_path::__macro_helpers::None + }; + + #[cfg(feature = "spv")] let spirv_tokens = if args.target_enabled(CompileTarget::Spirv) { let spirv_data = naga::back::spv::write_vec( &module, @@ -203,11 +188,12 @@ pub fn precompile(input: TokenStream) -> TokenStream { #wgpu_path::__macro_helpers::Some(#wgpu_path::__macro_helpers::Cow::Borrowed(&[#(#spirv_data),*])) } } else { - quote! { - #wgpu_path::__macro_helpers::None - } + none_tokens.clone() }; + #[cfg(not(feature = "spv"))] + let spirv_tokens = none_tokens.clone(); + #[cfg(feature = "msl")] let msl_tokens = if args.target_enabled(CompileTarget::Msl) { let msl_str = naga::back::msl::write_string( &module, @@ -224,12 +210,13 @@ pub fn precompile(input: TokenStream) -> TokenStream { #wgpu_path::__macro_helpers::Some(#wgpu_path::__macro_helpers::Cow::Borrowed(#msl_str)) } } else { - quote! { - #wgpu_path::__macro_helpers::None - } + none_tokens.clone() }; + #[cfg(not(feature = "msl"))] + let msl_tokens = none_tokens.clone(); // TODO: compile DXIL if DXC can be detected + #[cfg(feature = "hlsl")] let hlsl_tokens = if args.target_enabled(CompileTarget::Hlsl) { let mut hlsl_str = String::new(); naga::back::hlsl::Writer::new( @@ -249,7 +236,10 @@ pub fn precompile(input: TokenStream) -> TokenStream { #wgpu_path::__macro_helpers::None } }; + #[cfg(not(feature = "hlsl"))] + let hlsl_tokens = none_tokens.clone(); + #[cfg(feature = "wgsl")] let wgsl_tokens = if args.target_enabled(CompileTarget::Wgsl) { let mut wgsl_str = String::new(); naga::back::wgsl::Writer::new(&mut wgsl_str, naga::back::wgsl::WriterFlags::empty()) @@ -263,7 +253,10 @@ pub fn precompile(input: TokenStream) -> TokenStream { #wgpu_path::__macro_helpers::None } }; + #[cfg(not(feature = "wgsl"))] + let wgsl_tokens = none_tokens.clone(); + #[cfg(feature = "glsl")] let glsl_tokens = if args.target_enabled(CompileTarget::Glsl) { let mut glsl_str = String::new(); naga::back::glsl::Writer::new( @@ -289,6 +282,8 @@ pub fn precompile(input: TokenStream) -> TokenStream { #wgpu_path::__macro_helpers::None } }; + #[cfg(not(feature = "glsl"))] + let glsl_tokens = none_tokens.clone(); let label_tokens = match args.file_name { Some(f) => quote! {#wgpu_path::__macro_helpers::Some(#f)}, From 309919aa2eb4b09acdceb8f6f5ab992b3f468041 Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Fri, 12 Sep 2025 02:03:51 -0500 Subject: [PATCH 10/47] Added precompile test --- tests/Cargo.toml | 2 +- tests/tests/wgpu-gpu/main.rs | 2 ++ tests/tests/wgpu-gpu/precompile/mod.rs | 31 +++++++++++++++++++++ tests/tests/wgpu-gpu/precompile/shader.wgsl | 13 +++++++++ wgpu-precompile-macro/src/lib.rs | 3 ++ 5 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 tests/tests/wgpu-gpu/precompile/mod.rs create mode 100644 tests/tests/wgpu-gpu/precompile/shader.wgsl diff --git a/tests/Cargo.toml b/tests/Cargo.toml index 95301df9488..cd65f790ce8 100644 --- a/tests/Cargo.toml +++ b/tests/Cargo.toml @@ -34,7 +34,7 @@ webgl = ["wgpu/webgl"] test-build-with-profiling = ["profiling/type-check"] [dependencies] -wgpu = { workspace = true, features = ["noop"] } +wgpu = { workspace = true, features = ["noop", "precompile"] } wgpu-hal = { workspace = true, features = ["validation_canary"] } wgpu-macros.workspace = true diff --git a/tests/tests/wgpu-gpu/main.rs b/tests/tests/wgpu-gpu/main.rs index d461845e92b..eab57485237 100644 --- a/tests/tests/wgpu-gpu/main.rs +++ b/tests/tests/wgpu-gpu/main.rs @@ -43,6 +43,7 @@ mod pipeline; mod pipeline_cache; mod planar_texture; mod poll; +mod precompile; mod push_constants; mod query_set; mod queue_transfer; @@ -104,6 +105,7 @@ fn all_tests() -> Vec { planar_texture::all_tests(&mut tests); poll::all_tests(&mut tests); push_constants::all_tests(&mut tests); + precompile::all_tests(&mut tests); query_set::all_tests(&mut tests); queue_transfer::all_tests(&mut tests); ray_tracing::all_tests(&mut tests); diff --git a/tests/tests/wgpu-gpu/precompile/mod.rs b/tests/tests/wgpu-gpu/precompile/mod.rs new file mode 100644 index 00000000000..1dfb44e0513 --- /dev/null +++ b/tests/tests/wgpu-gpu/precompile/mod.rs @@ -0,0 +1,31 @@ +use wgpu_test::{gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters}; + +pub fn all_tests(vec: &mut Vec) { + vec.push(PRECOMPILE_ALL_STAGES_TEST); +} + +#[gpu_test] +static PRECOMPILE_ALL_STAGES_TEST: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters( + TestParameters::default().features(wgpu::Features::EXPERIMENTAL_PASSTHROUGH_SHADERS), + ) + .run_async(async |ctx| unsafe { + let _ = ctx + .device + .create_shader_module_passthrough(wgpu::precompile_wgsl!( + "tests/wgpu-gpu/precompile/shader.wgsl", + "vs_main" + )); + let _ = ctx + .device + .create_shader_module_passthrough(wgpu::precompile_wgsl!( + "tests/wgpu-gpu/precompile/shader.wgsl", + "fs_main" + )); + let _ = ctx + .device + .create_shader_module_passthrough(wgpu::precompile_wgsl!( + "tests/wgpu-gpu/precompile/shader.wgsl", + "cs_main" + )); + }); diff --git a/tests/tests/wgpu-gpu/precompile/shader.wgsl b/tests/tests/wgpu-gpu/precompile/shader.wgsl new file mode 100644 index 00000000000..dbead7756c2 --- /dev/null +++ b/tests/tests/wgpu-gpu/precompile/shader.wgsl @@ -0,0 +1,13 @@ +@vertex +fn vs_main(@builtin(vertex_index) in_vertex_index: u32) -> @builtin(position) vec4 { + return vec4(0.0); +} + +@fragment +fn fs_main() -> @location(0) vec4 { + return vec4(0.0); +} + +@compute +@workgroup_size(1) +fn cs_main() {} \ No newline at end of file diff --git a/wgpu-precompile-macro/src/lib.rs b/wgpu-precompile-macro/src/lib.rs index 9f1e5ad9e9b..3208ea7f07c 100644 --- a/wgpu-precompile-macro/src/lib.rs +++ b/wgpu-precompile-macro/src/lib.rs @@ -100,6 +100,9 @@ impl Parse for MacroArgs { } else { relative_path }; + if !path_to_read.is_file() { + panic!("Path does not exist or is not a file: {path_to_read:?}") + } let bytes = std::fs::read(path_to_read).expect("Failed to read input file"); (ShaderSource::File(bytes), Some(file_name)) } else { From bac4a48779b03f22aab2e77c4b2a3ddbb6c4f2ce Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Fri, 12 Sep 2025 10:27:25 -0500 Subject: [PATCH 11/47] Added more related macros --- examples/features/src/hello_triangle/mod.rs | 4 +- tests/tests/wgpu-gpu/precompile/mod.rs | 6 +- wgpu-precompile-macro/src/lib.rs | 44 ++++++- wgpu/src/macros.rs | 135 +++++++++++++++++++- 4 files changed, 174 insertions(+), 15 deletions(-) diff --git a/examples/features/src/hello_triangle/mod.rs b/examples/features/src/hello_triangle/mod.rs index ffa91fc702a..01d39f9e85a 100644 --- a/examples/features/src/hello_triangle/mod.rs +++ b/examples/features/src/hello_triangle/mod.rs @@ -46,13 +46,13 @@ async fn run(event_loop: EventLoop<()>, window: Window) { let swapchain_capabilities = surface.get_capabilities(&adapter); let swapchain_format = swapchain_capabilities.formats[0]; let vs_shader = unsafe { - device.create_shader_module_passthrough(wgpu::precompile_wgsl!( + device.create_shader_module_passthrough(wgpu::include_precompiled_wgsl!( "src/hello_triangle/shader.wgsl", "vs_main" )) }; let fs_shader = unsafe { - device.create_shader_module_passthrough(wgpu::precompile_wgsl!( + device.create_shader_module_passthrough(wgpu::include_precompiled_wgsl!( "src/hello_triangle/shader.wgsl", "fs_main" )) diff --git a/tests/tests/wgpu-gpu/precompile/mod.rs b/tests/tests/wgpu-gpu/precompile/mod.rs index 1dfb44e0513..f0ff61af5e0 100644 --- a/tests/tests/wgpu-gpu/precompile/mod.rs +++ b/tests/tests/wgpu-gpu/precompile/mod.rs @@ -12,19 +12,19 @@ static PRECOMPILE_ALL_STAGES_TEST: GpuTestConfiguration = GpuTestConfiguration:: .run_async(async |ctx| unsafe { let _ = ctx .device - .create_shader_module_passthrough(wgpu::precompile_wgsl!( + .create_shader_module_passthrough(wgpu::include_precompiled_wgsl!( "tests/wgpu-gpu/precompile/shader.wgsl", "vs_main" )); let _ = ctx .device - .create_shader_module_passthrough(wgpu::precompile_wgsl!( + .create_shader_module_passthrough(wgpu::include_precompiled_wgsl!( "tests/wgpu-gpu/precompile/shader.wgsl", "fs_main" )); let _ = ctx .device - .create_shader_module_passthrough(wgpu::precompile_wgsl!( + .create_shader_module_passthrough(wgpu::include_precompiled_wgsl!( "tests/wgpu-gpu/precompile/shader.wgsl", "cs_main" )); diff --git a/wgpu-precompile-macro/src/lib.rs b/wgpu-precompile-macro/src/lib.rs index 3208ea7f07c..14add169ba3 100644 --- a/wgpu-precompile-macro/src/lib.rs +++ b/wgpu-precompile-macro/src/lib.rs @@ -4,6 +4,7 @@ use quote::quote; use std::path::PathBuf; use syn::{parse::Parse, parse_macro_input, Ident, Path}; +#[derive(PartialEq, Eq)] enum SourceType { Wgsl, Glsl, @@ -64,9 +65,21 @@ impl ShaderSource { } } +fn parse_shader_stage(str: &str) -> Option { + match str { + "vertex" => Some(naga::ShaderStage::Vertex), + "fragment" => Some(naga::ShaderStage::Fragment), + "compute" => Some(naga::ShaderStage::Compute), + "task" => Some(naga::ShaderStage::Task), + "mesh" => Some(naga::ShaderStage::Mesh), + _ => None, + } +} + struct MacroArgs { wgpu_crate: Path, source_type: SourceType, + shader_stage: Option, source: ShaderSource, targets: HashSet, entry_point: String, @@ -76,6 +89,16 @@ impl Parse for MacroArgs { fn parse(input: syn::parse::ParseStream) -> syn::Result { let wgpu_crate: Path = input.parse()?; let source_type = SourceType::parse(&input.parse::()?.to_string()); + let shader_stage = if source_type == SourceType::Glsl { + let ident = input.parse::()?.to_string(); + let stage = parse_shader_stage(&ident); + if stage.is_none() { + panic!("Invalid shader stage for GLSL: {ident}"); + } + stage + } else { + None + }; let is_file_path = input.parse::()?.value; let source_literal = input.parse::()?.value(); let entry_point = input.parse::()?.value(); @@ -111,6 +134,7 @@ impl Parse for MacroArgs { Ok(Self { wgpu_crate, source_type, + shader_stage, source, entry_point, targets, @@ -127,7 +151,7 @@ impl MacroArgs { /// This is to be re-exported by wgpu in a certain way, so that it can refer to items in the `wgpu` crate /// /// Input format: -/// precompile!(wgpu_crate_name source_type is_file_path source_string entry_point shader_stage targets...) +/// precompile!(wgpu_crate_name source_type is_file_path source_string entry_point targets...) #[proc_macro] pub fn precompile(input: TokenStream) -> TokenStream { let args = parse_macro_input!(input as MacroArgs); @@ -145,10 +169,24 @@ pub fn precompile(input: TokenStream) -> TokenStream { } SourceType::Wgsl => { let source = args.source.clone().expect_string(); - naga::front::wgsl::parse_str(&source).expect("Naga failed to parse WGSL input") + naga::front::wgsl::Frontend::new_with_options(naga::front::wgsl::Options { + parse_doc_comments: false, + }) + .parse(&source) + .expect("Naga failed to parse WGSL input") } SourceType::Glsl => { - panic!("GLSL parsing support is not yet available") + let src = args.source.clone().expect_string(); + naga::front::glsl::Frontend::default() + .parse( + &naga::front::glsl::Options { + // This is guaranteed to be some + stage: args.shader_stage.unwrap(), + defines: Default::default(), + }, + &src, + ) + .expect("Naga failed to parse GLSL input") } }; diff --git a/wgpu/src/macros.rs b/wgpu/src/macros.rs index ac3ea1ad7d8..10fa81ed49e 100644 --- a/wgpu/src/macros.rs +++ b/wgpu/src/macros.rs @@ -230,22 +230,143 @@ macro_rules! hal_type_gles { }; } -/// Precompile WGSL shader for all supported backends. Will fail at compiletime -/// in case of error. Note that file paths are relative to the `MANIFEST_DIR`, i.e. not the same -/// folder as the invoking file as in `include_bytes` or the like. +/// Include precompiled WGSL shader from file. Will fail at compiletime in case of error. Note that file +/// paths are relative to the `MANIFEST_DIR`, i.e. not the same folder as the invoking file like in +/// the `include_*` family. /// -/// This can always take in wgsl, spirv, and glsl input, even if the corresponding features aren't -/// enabled on the wgpu crate. +/// This macro is always valid, even if the `wgsl` feature isn't enabled. /// /// ```ignore -/// device.create_shader_module_passthrough(precompile_wgsl!("path/to/file", "entry_point_name")) +/// device.create_shader_module_passthrough(include_precompiled_wgsl!("path/to/file", "entry_point_name")) /// ``` +/// or +/// ```ignore +/// device.create_shader_module_passthrough(include_precompiled_wgsl!("path/to/file", "entry_point_name", backends)) +/// ``` +/// If specified, backends is a list of shader languages to be used, without comma separators. For example, +/// `wgsl glsl hlsl spirv msl`. If the `all` backend is specified, then all shader languages supported by the +/// backends in wgpu features will be compiled for. All backends will be compiled for by naga; the input wgsl +/// code won't be the same as the output wgsl. #[macro_export] #[cfg(feature = "precompile")] -macro_rules! precompile_wgsl { +macro_rules! include_precompiled_wgsl { ($path: literal, $entry: literal) => { $crate::__macro_helpers::precompile!($crate wgsl true $path $entry all) }; + ($path: literal, $entry: literal,$($id: ident)+) => { + $crate::__macro_helpers::precompile!($crate wgsl true $path $entry $id) + }; +} + +/// Precompile WGSL shader string. Will fail at compiletime in case of error. +/// +/// +/// This macro is always valid when the `precompile` feature is enabled, even if the `wgsl` feature isn't enabled. +/// +/// ```ignore +/// device.create_shader_module_passthrough(precompile_wgsl!("", "entry_point_name")) +/// ``` +/// or +/// ```ignore +/// device.create_shader_module_passthrough(precompile_wgsl!("", "entry_point_name", backends)) +/// ``` +/// If specified, backends is a list of shader languages to be used, without comma separators. For example, +/// `wgsl glsl hlsl spirv msl`. If the `all` backend is specified, then all shader languages supported by the +/// backends in wgpu features will be compiled for. All backends will be compiled for by naga; the input wgsl +/// code won't be the same as the output wgsl. +#[macro_export] +#[cfg(feature = "precompile")] +macro_rules! precompile_wgsl { + ($shader: literal, $entry: literal) => { + $crate::__macro_helpers::precompile!($crate wgsl false $shader $entry all) + }; + ($shader: literal, $entry: literal,$($id: ident)+) => { + $crate::__macro_helpers::precompile!($crate wgsl false $shader $entry $id) + }; +} + +/// Include precompiled SPIR-V shader from file. Will fail at compiletime in case of error. Note that file +/// paths are relative to the `MANIFEST_DIR`, i.e. not the same folder as the invoking file like in +/// the `include_*` family. +/// +/// This macro is always valid when the `precompile` feature is enabled, even if the `spirv` feature isn't +/// enabled. +/// +/// ```ignore +/// device.create_shader_module_passthrough(include_precompiled_spirv!("path/to/file", "entry_point_name")) +/// ``` +/// or +/// ```ignore +/// device.create_shader_module_passthrough(include_precompiled_spirv!("path/to/file", "entry_point_name", backends)) +/// ``` +/// If specified, backends is a list of shader languages to be used, without comma separators. For example, +/// `wgsl glsl hlsl spirv msl`. If the `all` backend is specified, then all shader languages supported by the +/// backends in wgpu features will be compiled for. All backends will be compiled for by naga; the input spirv +/// code won't be the same as the output spirv. +#[macro_export] +#[cfg(feature = "precompile")] +macro_rules! include_precompiled_spirv { + ($path: literal, $entry: literal) => { + $crate::__macro_helpers::precompile!($crate spirv true $path $entry all) + }; + ($path: literal, $entry: literal,$($id: ident)+) => { + $crate::__macro_helpers::precompile!($crate spirv true $path $entry $id) + }; +} + +/// Include precompiled GLSL shader from file. Will fail at compiletime in case of error. Note that file +/// paths are relative to the `MANIFEST_DIR`, i.e. not the same folder as the invoking file like in +/// the `include_*` family. +/// +/// This macro is always valid, even if the `glsl` feature isn't enabled. +/// +/// ```ignore +/// device.create_shader_module_passthrough(include_precompiled_wgsl!("path/to/file", "entry_point_name")) +/// ``` +/// or +/// ```ignore +/// device.create_shader_module_passthrough(include_precompiled_wgsl!("path/to/file", "entry_point_name", backends)) +/// ``` +/// If specified, backends is a list of shader languages to be used, without comma separators. For example, +/// `wgsl glsl hlsl spirv msl`. If the `all` backend is specified, then all shader languages supported by the +/// backends in wgpu features will be compiled for. All backends will be compiled for by naga; the input glsl +/// code won't be the same as the output glsl. +#[macro_export] +#[cfg(feature = "precompile")] +macro_rules! include_precompiled_glsl { + ($path: literal, $entry: literal) => { + $crate::__macro_helpers::precompile!($crate glsl true $path $entry all) + }; + ($path: literal, $entry: literal,$($id: ident)+) => { + $crate::__macro_helpers::precompile!($crate glsl true $path $entry $id) + }; +} + +/// Precompile GLSL shader string. Will fail at compiletime in case of error. +/// +/// +/// This macro is always valid when the `precompile` feature is enabled, even if the `glsl` feature isn't enabled. +/// +/// ```ignore +/// device.create_shader_module_passthrough(precompile_wgsl!("", "entry_point_name")) +/// ``` +/// or +/// ```ignore +/// device.create_shader_module_passthrough(precompile_wgsl!("", "entry_point_name", backends)) +/// ``` +/// If specified, backends is a list of shader languages to be used, without comma separators. For example, +/// `wgsl glsl hlsl spirv msl`. If the `all` backend is specified, then all shader languages supported by the +/// backends in wgpu features will be compiled for. All backends will be compiled for by naga; the input glsl +/// code won't be the same as the output glsl. +#[macro_export] +#[cfg(feature = "precompile")] +macro_rules! precompile_glsl { + ($shader: literal, $entry: literal) => { + $crate::__macro_helpers::precompile!($crate glsl false $shader $entry all) + }; + ($shader: literal, $entry: literal,$($id: ident)+) => { + $crate::__macro_helpers::precompile!($crate glsl false $shader $entry $id) + }; } #[doc(hidden)] From 12d5e261ae912e345294a324cb6b1d29762bf7c4 Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Fri, 12 Sep 2025 10:57:50 -0500 Subject: [PATCH 12/47] Worked more on the testing and such. Currently glsl include is broken (unsure why), testing if its only on macos/msl --- examples/features/src/hello_triangle/mod.rs | 4 +- tests/tests/wgpu-gpu/precompile/mod.rs | 49 ++++++++++++++++++-- tests/tests/wgpu-gpu/precompile/shader.spv | Bin 0 -> 1004 bytes tests/tests/wgpu-gpu/precompile/shader.vert | 11 +++++ wgpu-precompile-macro/src/lib.rs | 4 +- wgpu/src/macros.rs | 47 +++++++------------ 6 files changed, 78 insertions(+), 37 deletions(-) create mode 100644 tests/tests/wgpu-gpu/precompile/shader.spv create mode 100644 tests/tests/wgpu-gpu/precompile/shader.vert diff --git a/examples/features/src/hello_triangle/mod.rs b/examples/features/src/hello_triangle/mod.rs index 01d39f9e85a..826304caa08 100644 --- a/examples/features/src/hello_triangle/mod.rs +++ b/examples/features/src/hello_triangle/mod.rs @@ -48,13 +48,13 @@ async fn run(event_loop: EventLoop<()>, window: Window) { let vs_shader = unsafe { device.create_shader_module_passthrough(wgpu::include_precompiled_wgsl!( "src/hello_triangle/shader.wgsl", - "vs_main" + "vs_main", )) }; let fs_shader = unsafe { device.create_shader_module_passthrough(wgpu::include_precompiled_wgsl!( "src/hello_triangle/shader.wgsl", - "fs_main" + "fs_main", )) }; diff --git a/tests/tests/wgpu-gpu/precompile/mod.rs b/tests/tests/wgpu-gpu/precompile/mod.rs index f0ff61af5e0..df2cafc7855 100644 --- a/tests/tests/wgpu-gpu/precompile/mod.rs +++ b/tests/tests/wgpu-gpu/precompile/mod.rs @@ -14,18 +14,61 @@ static PRECOMPILE_ALL_STAGES_TEST: GpuTestConfiguration = GpuTestConfiguration:: .device .create_shader_module_passthrough(wgpu::include_precompiled_wgsl!( "tests/wgpu-gpu/precompile/shader.wgsl", - "vs_main" + "vs_main", )); let _ = ctx .device .create_shader_module_passthrough(wgpu::include_precompiled_wgsl!( "tests/wgpu-gpu/precompile/shader.wgsl", - "fs_main" + "fs_main", + all )); let _ = ctx .device .create_shader_module_passthrough(wgpu::include_precompiled_wgsl!( "tests/wgpu-gpu/precompile/shader.wgsl", - "cs_main" + "cs_main", + glsl spirv wgsl hlsl msl + )); + let _ = ctx + .device + .create_shader_module_passthrough(wgpu::precompile_wgsl!( + r#" +@compute +@workgroup_size(1) +fn cs_main() {} + "#, + "cs_main", + )); + let _ = ctx + .device + .create_shader_module_passthrough(wgpu::include_precompiled_spirv!( + "tests/wgpu-gpu/precompile/shader.spv", + "main", + )); + let _ = ctx + .device + .create_shader_module_passthrough(wgpu::include_precompiled_glsl!( + "tests/wgpu-gpu/precompile/shader.vert", + vertex, + )); + + let _ = ctx + .device + .create_shader_module_passthrough(wgpu::precompile_glsl!( + r#" +#version 450 +const float c_scale = 1.2; + +layout(location = 0) in vec2 a_pos; +layout(location = 1) in vec2 a_uv; +layout(location = 0) out vec2 v_uv; + +void main() { + v_uv = a_uv; + gl_Position = vec4(c_scale * a_pos, 0.0, 1.0); +} + "#, + vertex, )); }); diff --git a/tests/tests/wgpu-gpu/precompile/shader.spv b/tests/tests/wgpu-gpu/precompile/shader.spv new file mode 100644 index 0000000000000000000000000000000000000000..b83a210c75e809ddc446465a03bf9b94032de0f5 GIT binary patch literal 1004 zcmYL`PfG$(6vbcu87)gQQ~P5!(lU@16+x6lfh}AZLR*m3pnViX*hWWFiNr*AxoH({Sy-qWX9-GG}M^xlZGA^X1 zV3y-`O8@79KLo(!_p~-Tr;kZL)p~cQF zX$n0Jbx_B;qLriQ%B!l^W5<}BE1XrN7XEyWc@H@9D~f2cd9c;Verv+)kDjvrc|})_ z9nNtB z&0X1g@;h?O*6+sXy~X@}t(k?kQ@tDa#Tha9Z!u}b3$fhIs+@bdRF?Y#+uWAc%>GaZ ywcBzy^xcU=OHB@sS#D2jI5c bool { - self.targets.contains(&target) || self.targets.contains(&CompileTarget::AllSupported) + self.targets.contains(&target) + || self.targets.contains(&CompileTarget::AllSupported) + || self.targets.is_empty() } } diff --git a/wgpu/src/macros.rs b/wgpu/src/macros.rs index 10fa81ed49e..2f9e8e9ec59 100644 --- a/wgpu/src/macros.rs +++ b/wgpu/src/macros.rs @@ -237,7 +237,7 @@ macro_rules! hal_type_gles { /// This macro is always valid, even if the `wgsl` feature isn't enabled. /// /// ```ignore -/// device.create_shader_module_passthrough(include_precompiled_wgsl!("path/to/file", "entry_point_name")) +/// device.create_shader_module_passthrough(include_precompiled_wgsl!("path/to/file", "entry_point_name",)) /// ``` /// or /// ```ignore @@ -250,11 +250,8 @@ macro_rules! hal_type_gles { #[macro_export] #[cfg(feature = "precompile")] macro_rules! include_precompiled_wgsl { - ($path: literal, $entry: literal) => { - $crate::__macro_helpers::precompile!($crate wgsl true $path $entry all) - }; - ($path: literal, $entry: literal,$($id: ident)+) => { - $crate::__macro_helpers::precompile!($crate wgsl true $path $entry $id) + ($path: literal, $entry: literal, $($id: ident)*) => { + $crate::__macro_helpers::precompile!($crate wgsl true $path $entry $($id)*) }; } @@ -264,7 +261,7 @@ macro_rules! include_precompiled_wgsl { /// This macro is always valid when the `precompile` feature is enabled, even if the `wgsl` feature isn't enabled. /// /// ```ignore -/// device.create_shader_module_passthrough(precompile_wgsl!("", "entry_point_name")) +/// device.create_shader_module_passthrough(precompile_wgsl!("", "entry_point_name",)) /// ``` /// or /// ```ignore @@ -277,11 +274,8 @@ macro_rules! include_precompiled_wgsl { #[macro_export] #[cfg(feature = "precompile")] macro_rules! precompile_wgsl { - ($shader: literal, $entry: literal) => { - $crate::__macro_helpers::precompile!($crate wgsl false $shader $entry all) - }; - ($shader: literal, $entry: literal,$($id: ident)+) => { - $crate::__macro_helpers::precompile!($crate wgsl false $shader $entry $id) + ($shader: literal, $entry: literal, $($id: ident)*) => { + $crate::__macro_helpers::precompile!($crate wgsl false $shader $entry $($id)*) }; } @@ -293,7 +287,7 @@ macro_rules! precompile_wgsl { /// enabled. /// /// ```ignore -/// device.create_shader_module_passthrough(include_precompiled_spirv!("path/to/file", "entry_point_name")) +/// device.create_shader_module_passthrough(include_precompiled_spirv!("path/to/file", "entry_point_name",)) /// ``` /// or /// ```ignore @@ -306,11 +300,8 @@ macro_rules! precompile_wgsl { #[macro_export] #[cfg(feature = "precompile")] macro_rules! include_precompiled_spirv { - ($path: literal, $entry: literal) => { - $crate::__macro_helpers::precompile!($crate spirv true $path $entry all) - }; - ($path: literal, $entry: literal,$($id: ident)+) => { - $crate::__macro_helpers::precompile!($crate spirv true $path $entry $id) + ($path: literal, $entry: literal, $($id: ident)*) => { + $crate::__macro_helpers::precompile!($crate spirv true $path $entry $($id)*) }; } @@ -321,11 +312,11 @@ macro_rules! include_precompiled_spirv { /// This macro is always valid, even if the `glsl` feature isn't enabled. /// /// ```ignore -/// device.create_shader_module_passthrough(include_precompiled_wgsl!("path/to/file", "entry_point_name")) +/// device.create_shader_module_passthrough(include_precompiled_wgsl!("path/to/file", shader_stage, "entry_point_name",)) /// ``` /// or /// ```ignore -/// device.create_shader_module_passthrough(include_precompiled_wgsl!("path/to/file", "entry_point_name", backends)) +/// device.create_shader_module_passthrough(include_precompiled_wgsl!("path/to/file", shader_stage, "entry_point_name", backends)) /// ``` /// If specified, backends is a list of shader languages to be used, without comma separators. For example, /// `wgsl glsl hlsl spirv msl`. If the `all` backend is specified, then all shader languages supported by the @@ -334,11 +325,8 @@ macro_rules! include_precompiled_spirv { #[macro_export] #[cfg(feature = "precompile")] macro_rules! include_precompiled_glsl { - ($path: literal, $entry: literal) => { - $crate::__macro_helpers::precompile!($crate glsl true $path $entry all) - }; - ($path: literal, $entry: literal,$($id: ident)+) => { - $crate::__macro_helpers::precompile!($crate glsl true $path $entry $id) + ($path: literal, $stage: ident, $($id: ident)*) => { + $crate::__macro_helpers::precompile!($crate glsl $stage true $path "main" $($id)*) }; } @@ -348,7 +336,7 @@ macro_rules! include_precompiled_glsl { /// This macro is always valid when the `precompile` feature is enabled, even if the `glsl` feature isn't enabled. /// /// ```ignore -/// device.create_shader_module_passthrough(precompile_wgsl!("", "entry_point_name")) +/// device.create_shader_module_passthrough(precompile_wgsl!("", "entry_point_name",)) /// ``` /// or /// ```ignore @@ -361,11 +349,8 @@ macro_rules! include_precompiled_glsl { #[macro_export] #[cfg(feature = "precompile")] macro_rules! precompile_glsl { - ($shader: literal, $entry: literal) => { - $crate::__macro_helpers::precompile!($crate glsl false $shader $entry all) - }; - ($shader: literal, $entry: literal,$($id: ident)+) => { - $crate::__macro_helpers::precompile!($crate glsl false $shader $entry $id) + ($shader: literal, $stage: ident, $($id: ident)*) => { + $crate::__macro_helpers::precompile!($crate glsl $stage false $shader "main" $($id)*) }; } From a28790d0135456cf09033fef6ad71b78dff01da2 Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Fri, 12 Sep 2025 11:02:41 -0500 Subject: [PATCH 13/47] Added comment explaining why compiled spv is in tree --- tests/tests/wgpu-gpu/precompile/mod.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/tests/wgpu-gpu/precompile/mod.rs b/tests/tests/wgpu-gpu/precompile/mod.rs index df2cafc7855..7c3698e4671 100644 --- a/tests/tests/wgpu-gpu/precompile/mod.rs +++ b/tests/tests/wgpu-gpu/precompile/mod.rs @@ -40,6 +40,9 @@ fn cs_main() {} "#, "cs_main", )); + // This is just the GLSL file compiled with glslang -V shader.vert -o shader.spv. + // The spirv file must exist before parsing begins. I didn't want to add it to + // the build script but that is another viable option. let _ = ctx .device .create_shader_module_passthrough(wgpu::include_precompiled_spirv!( From 1a1d5630700189dce096ff034a02b7094fcec7b5 Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Fri, 12 Sep 2025 12:37:32 -0500 Subject: [PATCH 14/47] Made entry point specific to each backend in passthrough --- examples/features/src/mesh_shader/mod.rs | 5 +- player/src/lib.rs | 40 ++++++--- tests/tests/wgpu-gpu/mesh_shader/mod.rs | 5 +- tests/tests/wgpu-gpu/precompile/mod.rs | 102 ++++++++++++----------- wgpu-core/src/device/global.rs | 48 +++++++++-- wgpu-core/src/device/resource.rs | 43 ++++++---- wgpu-core/src/device/trace.rs | 2 +- wgpu-precompile-macro/src/lib.rs | 43 ++++++---- wgpu-types/src/lib.rs | 78 ++++++++++++++--- wgpu/src/lib.rs | 32 +++---- wgpu/src/macros.rs | 2 +- 11 files changed, 264 insertions(+), 136 deletions(-) diff --git a/examples/features/src/mesh_shader/mod.rs b/examples/features/src/mesh_shader/mod.rs index 675150f5106..d616dfb3c1b 100644 --- a/examples/features/src/mesh_shader/mod.rs +++ b/examples/features/src/mesh_shader/mod.rs @@ -25,9 +25,10 @@ fn compile_glsl( assert!(output.status.success()); unsafe { device.create_shader_module_passthrough(wgpu::ShaderModuleDescriptorPassthrough { - entry_point: "main".into(), label: None, - spirv: Some(wgpu::util::make_spirv_raw(&output.stdout)), + spirv: Some(wgpu::SpirvPassthroughDescriptor { + code: wgpu::util::make_spirv_raw(&output.stdout), + }), ..Default::default() }) } diff --git a/player/src/lib.rs b/player/src/lib.rs index 437f51b14bb..66be62754a7 100644 --- a/player/src/lib.rs +++ b/player/src/lib.rs @@ -316,7 +316,7 @@ impl GlobalPlay for wgc::global::Global { Action::CreateShaderModulePassthrough { id, data, - entry_point, + entry_points, label, num_workgroups, runtime_checks, @@ -326,31 +326,43 @@ impl GlobalPlay for wgc::global::Global { let data = fs::read(dir.join(a)).unwrap(); assert!(data.len() % 4 == 0); - Some(Cow::Owned(bytemuck::pod_collect_to_vec(&data))) + Some(wgt::SpirvPassthroughDescriptor { + code: Cow::Owned(bytemuck::pod_collect_to_vec(&data)), + }) } else { None } }); - let dxil = data.iter().find_map(|a| { + let dxil = data.iter().zip(&entry_points).find_map(|(a, entry_point)| { if a.ends_with(".dxil") { let vec = std::fs::read(dir.join(a)).unwrap(); - Some(Cow::Owned(vec)) + Some(wgt::DxilPassthroughDescriptor { + code: Cow::Owned(vec), + entry_point: entry_point.to_owned(), + }) } else { None } }); - let hlsl = data.iter().find_map(|a| { + let hlsl = data.iter().zip(&entry_points).find_map(|(a, entry_point)| { if a.ends_with(".hlsl") { let code = fs::read_to_string(dir.join(a)).unwrap(); - Some(Cow::Owned(code)) + + Some(wgt::HlslPassthroughDescriptor { + code: Cow::Owned(code), + entry_point: entry_point.to_owned(), + }) } else { None } }); - let msl = data.iter().find_map(|a| { + let msl = data.iter().zip(&entry_points).find_map(|(a, entry_point)| { if a.ends_with(".msl") { let code = fs::read_to_string(dir.join(a)).unwrap(); - Some(Cow::Owned(code)) + Some(wgt::MslPassthroughDescriptor { + code: Cow::Owned(code), + entry_point: entry_point.to_owned(), + }) } else { None } @@ -358,21 +370,25 @@ impl GlobalPlay for wgc::global::Global { let glsl = data.iter().find_map(|a| { if a.ends_with(".glsl") { let code = fs::read_to_string(dir.join(a)).unwrap(); - Some(Cow::Owned(code)) + Some(wgt::GlslPassthroughDescriptor { + code: Cow::Owned(code), + }) } else { None } }); - let wgsl = data.iter().find_map(|a| { + let wgsl = data.iter().zip(&entry_points).find_map(|(a, entry_point)| { if a.ends_with(".wgsl") { let code = fs::read_to_string(dir.join(a)).unwrap(); - Some(Cow::Owned(code)) + Some(wgt::WgslPassthroughDescriptor { + code: Cow::Owned(code), + entry_point: entry_point.to_owned(), + }) } else { None } }); let desc = wgt::CreateShaderModuleDescriptorPassthrough { - entry_point, label, num_workgroups, runtime_checks, diff --git a/tests/tests/wgpu-gpu/mesh_shader/mod.rs b/tests/tests/wgpu-gpu/mesh_shader/mod.rs index 23e2c6ccda5..8c87f56d3da 100644 --- a/tests/tests/wgpu-gpu/mesh_shader/mod.rs +++ b/tests/tests/wgpu-gpu/mesh_shader/mod.rs @@ -42,9 +42,10 @@ fn compile_glsl( assert!(output.status.success()); unsafe { device.create_shader_module_passthrough(wgpu::ShaderModuleDescriptorPassthrough { - entry_point: "main".into(), label: None, - spirv: Some(wgpu::util::make_spirv_raw(&output.stdout)), + spirv: Some(wgpu::SpirvPassthroughDescriptor { + code: wgpu::util::make_spirv_raw(&output.stdout), + }), ..Default::default() }) } diff --git a/tests/tests/wgpu-gpu/precompile/mod.rs b/tests/tests/wgpu-gpu/precompile/mod.rs index 7c3698e4671..0a2cbd36bdd 100644 --- a/tests/tests/wgpu-gpu/precompile/mod.rs +++ b/tests/tests/wgpu-gpu/precompile/mod.rs @@ -9,57 +9,60 @@ static PRECOMPILE_ALL_STAGES_TEST: GpuTestConfiguration = GpuTestConfiguration:: .parameters( TestParameters::default().features(wgpu::Features::EXPERIMENTAL_PASSTHROUGH_SHADERS), ) - .run_async(async |ctx| unsafe { - let _ = ctx - .device - .create_shader_module_passthrough(wgpu::include_precompiled_wgsl!( - "tests/wgpu-gpu/precompile/shader.wgsl", - "vs_main", - )); - let _ = ctx - .device - .create_shader_module_passthrough(wgpu::include_precompiled_wgsl!( - "tests/wgpu-gpu/precompile/shader.wgsl", - "fs_main", - all - )); - let _ = ctx - .device - .create_shader_module_passthrough(wgpu::include_precompiled_wgsl!( - "tests/wgpu-gpu/precompile/shader.wgsl", - "cs_main", - glsl spirv wgsl hlsl msl - )); - let _ = ctx - .device - .create_shader_module_passthrough(wgpu::precompile_wgsl!( - r#" + .run_async( + async + | ctx + | unsafe { + let _ = + ctx.device + .create_shader_module_passthrough(wgpu::include_precompiled_wgsl!( + "tests/wgpu-gpu/precompile/shader.wgsl", + "vs_main", + )); + let _ = + ctx.device + .create_shader_module_passthrough(wgpu::include_precompiled_wgsl!( + "tests/wgpu-gpu/precompile/shader.wgsl", + "fs_main", + all + )); + let _ = + ctx.device + .create_shader_module_passthrough(wgpu::include_precompiled_wgsl!( + "tests/wgpu-gpu/precompile/shader.wgsl", + "cs_main", + glsl spirv wgsl hlsl msl + )); + let _ = ctx + .device + .create_shader_module_passthrough(wgpu::precompile_wgsl!( + r#" @compute @workgroup_size(1) fn cs_main() {} "#, - "cs_main", - )); - // This is just the GLSL file compiled with glslang -V shader.vert -o shader.spv. - // The spirv file must exist before parsing begins. I didn't want to add it to - // the build script but that is another viable option. - let _ = ctx - .device - .create_shader_module_passthrough(wgpu::include_precompiled_spirv!( - "tests/wgpu-gpu/precompile/shader.spv", - "main", - )); - let _ = ctx - .device - .create_shader_module_passthrough(wgpu::include_precompiled_glsl!( - "tests/wgpu-gpu/precompile/shader.vert", - vertex, - )); + "cs_main", + )); + // This is just the GLSL file compiled with glslang -V shader.vert -o shader.spv. + // The spirv file must exist before parsing begins. I didn't want to add it to + // the build script but that is another viable option. + let _ = + ctx.device + .create_shader_module_passthrough(wgpu::include_precompiled_spirv!( + "tests/wgpu-gpu/precompile/shader.spv", + "main", + )); + let _ = + ctx.device + .create_shader_module_passthrough(wgpu::include_precompiled_glsl!( + "tests/wgpu-gpu/precompile/shader.vert", + vertex, + )); - let _ = ctx - .device - .create_shader_module_passthrough(wgpu::precompile_glsl!( - r#" + let _ = ctx + .device + .create_shader_module_passthrough(wgpu::precompile_glsl!( + r#" #version 450 const float c_scale = 1.2; @@ -72,6 +75,7 @@ void main() { gl_Position = vec4(c_scale * a_pos, 0.0, 1.0); } "#, - vertex, - )); - }); + vertex, + )); + }, + ); diff --git a/wgpu-core/src/device/global.rs b/wgpu-core/src/device/global.rs index 4b35638c48f..04f2e4f9bad 100644 --- a/wgpu-core/src/device/global.rs +++ b/wgpu-core/src/device/global.rs @@ -1095,23 +1095,53 @@ impl Global { #[cfg(feature = "trace")] if let Some(ref mut trace) = *device.trace.lock() { let mut file_names = Vec::new(); + let mut entry_points = Vec::new(); for (data, ext) in [ - (desc.spirv.as_ref().map(|a| bytemuck::cast_slice(a)), "spv"), - (desc.dxil.as_deref(), "dxil"), - (desc.hlsl.as_ref().map(|a| a.as_bytes()), "hlsl"), - (desc.msl.as_ref().map(|a| a.as_bytes()), "msl"), - (desc.glsl.as_ref().map(|a| a.as_bytes()), "glsl"), - (desc.wgsl.as_ref().map(|a| a.as_bytes()), "wgsl"), + ( + desc.spirv + .as_ref() + .map(|a| (bytemuck::cast_slice(&a.code), "main")), + "spv", + ), + ( + desc.dxil + .as_ref() + .map(|a| (&*a.code, a.entry_point.as_str())), + "dxil", + ), + ( + desc.hlsl + .as_ref() + .map(|a| (a.code.as_bytes(), a.entry_point.as_str())), + "hlsl", + ), + ( + desc.msl + .as_ref() + .map(|a| (a.code.as_bytes(), a.entry_point.as_str())), + "msl", + ), + ( + desc.glsl.as_ref().map(|a| (a.code.as_bytes(), "main")), + "glsl", + ), + ( + desc.wgsl + .as_ref() + .map(|a| (a.code.as_bytes(), a.entry_point.as_str())), + "wgsl", + ), ] { - if let Some(data) = data { + if let Some((data, entry_point)) = data { file_names.push(trace.make_binary(ext, data)); + + entry_points.push(alloc::string::ToString::to_string(entry_point)); } } trace.add(trace::Action::CreateShaderModulePassthrough { id: fid.id(), data: file_names, - - entry_point: desc.entry_point.clone(), + entry_points, label: desc.label.clone(), num_workgroups: desc.num_workgroups, runtime_checks: desc.runtime_checks, diff --git a/wgpu-core/src/device/resource.rs b/wgpu-core/src/device/resource.rs index 7d5898ecc2d..86d7fe6faeb 100644 --- a/wgpu-core/src/device/resource.rs +++ b/wgpu-core/src/device/resource.rs @@ -2136,44 +2136,51 @@ impl Device { log::info!("Backend: {}", self.backend()); let hal_shader = match self.backend() { wgt::Backend::Vulkan => hal::ShaderInput::SpirV( - descriptor + &descriptor .spirv .as_ref() - .ok_or(pipeline::CreateShaderModuleError::NotCompiledForBackend)?, + .ok_or(pipeline::CreateShaderModuleError::NotCompiledForBackend)? + .code, ), wgt::Backend::Dx12 => { if let Some(dxil) = &descriptor.dxil { hal::ShaderInput::Dxil { - shader: dxil, - entry_point: descriptor.entry_point.clone(), + shader: &dxil.code, + entry_point: dxil.entry_point.clone(), num_workgroups: descriptor.num_workgroups, } } else if let Some(hlsl) = &descriptor.hlsl { hal::ShaderInput::Hlsl { - shader: hlsl, - entry_point: descriptor.entry_point.clone(), + shader: &hlsl.code, + entry_point: hlsl.entry_point.clone(), num_workgroups: descriptor.num_workgroups, } } else { return Err(pipeline::CreateShaderModuleError::NotCompiledForBackend); } } - wgt::Backend::Metal => hal::ShaderInput::Msl { - shader: descriptor + wgt::Backend::Metal => { + let msl = descriptor .msl .as_ref() - .ok_or(pipeline::CreateShaderModuleError::NotCompiledForBackend)?, - entry_point: descriptor.entry_point.clone(), - num_workgroups: descriptor.num_workgroups, - }, - wgt::Backend::Gl => hal::ShaderInput::Glsl { - shader: descriptor + .ok_or(pipeline::CreateShaderModuleError::NotCompiledForBackend)?; + hal::ShaderInput::Msl { + shader: &msl.code, + entry_point: msl.entry_point.clone(), + num_workgroups: descriptor.num_workgroups, + } + } + wgt::Backend::Gl => { + let glsl = descriptor .glsl .as_ref() - .ok_or(pipeline::CreateShaderModuleError::NotCompiledForBackend)?, - entry_point: descriptor.entry_point.clone(), - num_workgroups: descriptor.num_workgroups, - }, + .ok_or(pipeline::CreateShaderModuleError::NotCompiledForBackend)?; + hal::ShaderInput::Glsl { + shader: &glsl.code, + entry_point: "main".to_string(), + num_workgroups: descriptor.num_workgroups, + } + } wgt::Backend::Noop => { return Err(pipeline::CreateShaderModuleError::NotCompiledForBackend) } diff --git a/wgpu-core/src/device/trace.rs b/wgpu-core/src/device/trace.rs index fcb3f589154..4a362850492 100644 --- a/wgpu-core/src/device/trace.rs +++ b/wgpu-core/src/device/trace.rs @@ -97,7 +97,7 @@ pub enum Action<'a> { id: id::ShaderModuleId, data: Vec, - entry_point: String, + entry_points: Vec, label: crate::Label<'a>, num_workgroups: (u32, u32, u32), runtime_checks: wgt::ShaderRuntimeChecks, diff --git a/wgpu-precompile-macro/src/lib.rs b/wgpu-precompile-macro/src/lib.rs index ba609d7e9be..c5fcc600507 100644 --- a/wgpu-precompile-macro/src/lib.rs +++ b/wgpu-precompile-macro/src/lib.rs @@ -228,7 +228,9 @@ pub fn precompile(input: TokenStream) -> TokenStream { ) .expect("Naga failed to write SPIR-V code"); quote! { - #wgpu_path::__macro_helpers::Some(#wgpu_path::__macro_helpers::Cow::Borrowed(&[#(#spirv_data),*])) + #wgpu_path::__macro_helpers::Some(#wgpu_path::SpirvPassthroughDescriptor { + code: #wgpu_path::__macro_helpers::Cow::Borrowed(&[#(#spirv_data),*]), + }) } } else { none_tokens.clone() @@ -238,7 +240,7 @@ pub fn precompile(input: TokenStream) -> TokenStream { #[cfg(feature = "msl")] let msl_tokens = if args.target_enabled(CompileTarget::Msl) { - let msl_str = naga::back::msl::write_string( + let (msl_str, translation_info) = naga::back::msl::write_string( &module, &module_info, &naga::back::msl::Options::default(), @@ -247,10 +249,13 @@ pub fn precompile(input: TokenStream) -> TokenStream { ..Default::default() }, ) - .expect("Naga failed to write MSL code") - .0; + .expect("Naga failed to write MSL code"); + let entry_point = translation_info.entry_point_names[0].as_ref().unwrap(); quote! { - #wgpu_path::__macro_helpers::Some(#wgpu_path::__macro_helpers::Cow::Borrowed(#msl_str)) + #wgpu_path::__macro_helpers::Some(#wgpu_path::MslPassthroughDescriptor { + code: #wgpu_path::__macro_helpers::Cow::Borrowed(#msl_str), + entry_point: #wgpu_path::__macro_helpers::ToString::to_string(#entry_point), + }) } } else { none_tokens.clone() @@ -262,7 +267,7 @@ pub fn precompile(input: TokenStream) -> TokenStream { #[cfg(feature = "hlsl")] let hlsl_tokens = if args.target_enabled(CompileTarget::Hlsl) { let mut hlsl_str = String::new(); - naga::back::hlsl::Writer::new( + let reflection = naga::back::hlsl::Writer::new( &mut hlsl_str, &naga::back::hlsl::Options::default(), &naga::back::hlsl::PipelineOptions { @@ -271,8 +276,12 @@ pub fn precompile(input: TokenStream) -> TokenStream { ) .write(&module, &module_info, None) .expect("Naga failed to write HLSL code"); + let entry_point = reflection.entry_point_names[0].as_ref().unwrap(); quote! { - #wgpu_path::__macro_helpers::Some(#wgpu_path::__macro_helpers::Cow::Borrowed(#hlsl_str)) + #wgpu_path::__macro_helpers::Some(#wgpu_path::HlslPassthroughDescriptor { + code: #wgpu_path::__macro_helpers::Cow::Borrowed(#hlsl_str), + entry_point: #wgpu_path::__macro_helpers::ToString::to_string(#entry_point), + }) } } else { quote! { @@ -284,12 +293,19 @@ pub fn precompile(input: TokenStream) -> TokenStream { #[cfg(feature = "wgsl")] let wgsl_tokens = if args.target_enabled(CompileTarget::Wgsl) { - let mut wgsl_str = String::new(); - naga::back::wgsl::Writer::new(&mut wgsl_str, naga::back::wgsl::WriterFlags::empty()) + let mut writer = + naga::back::wgsl::Writer::new(String::new(), naga::back::wgsl::WriterFlags::empty()); + writer .write(&module, &module_info) .expect("Naga failed to write WGSL code"); + let wgsl_str = writer.finish(); + // TODO: ensure that the entry point here is sensible + let entry_point = &args.entry_point; quote! { - #wgpu_path::__macro_helpers::Some(#wgpu_path::__macro_helpers::Cow::Borrowed(#wgsl_str)) + #wgpu_path::__macro_helpers::Some(#wgpu_path::WgslPassthroughDescriptor { + code: #wgpu_path::__macro_helpers::Cow::Borrowed(#wgsl_str), + entry_point: #wgpu_path::__macro_helpers::ToString::to_string(#entry_point), + }) } } else { quote! { @@ -318,7 +334,9 @@ pub fn precompile(input: TokenStream) -> TokenStream { .write() .expect("Naga failed write GLSL code"); quote! { - #wgpu_path::__macro_helpers::Some(#wgpu_path::__macro_helpers::Cow::Borrowed(#glsl_str)) + #wgpu_path::__macro_helpers::Some(#wgpu_path::GlslPassthroughDescriptor { + code: #wgpu_path::__macro_helpers::Cow::Borrowed(#glsl_str), + }) } } else { quote! { @@ -333,13 +351,10 @@ pub fn precompile(input: TokenStream) -> TokenStream { None => quote! {#wgpu_path::__macro_helpers::None}, }; - let entry_point = &args.entry_point; - quote! { #wgpu_path::ShaderModuleDescriptorPassthrough { // TODO: make this something else when file name is provided label: #label_tokens, - entry_point: #wgpu_path::__macro_helpers::String::from(#entry_point), num_workgroups: (#x, #y, #z), runtime_checks: #wgpu_path::ShaderRuntimeChecks::default(), spirv: #spirv_tokens, diff --git a/wgpu-types/src/lib.rs b/wgpu-types/src/lib.rs index 7674c0a95d8..dd6edd9e2c5 100644 --- a/wgpu-types/src/lib.rs +++ b/wgpu-types/src/lib.rs @@ -8117,14 +8117,68 @@ pub enum DeviceLostReason { Destroyed = 1, } +/// Descriptor for MSL shader passthrough. +#[derive(Debug, Clone)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct MslPassthroughDescriptor<'a> { + /// The shader code. + pub code: Cow<'a, str>, + /// The entry point name. + pub entry_point: String, +} + +/// Descriptor for HLSL shader passthrough. +#[derive(Debug, Clone)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct HlslPassthroughDescriptor<'a> { + /// The shader code. + pub code: Cow<'a, str>, + /// The entry point name. + pub entry_point: String, +} + +/// Descriptor for WGSL shader passthrough. +#[derive(Debug, Clone)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct WgslPassthroughDescriptor<'a> { + /// The shader code. + pub code: Cow<'a, str>, + /// The entry point name. + pub entry_point: String, +} + +/// Descriptor for GLSL shader passthrough. +#[derive(Debug, Clone)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct GlslPassthroughDescriptor<'a> { + /// The shader code. + pub code: Cow<'a, str>, +} + +/// Descriptor for SPIR-V shader passthrough. +#[derive(Debug, Clone)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct SpirvPassthroughDescriptor<'a> { + /// The shader code. + pub code: Cow<'a, [u32]>, +} + +/// Descriptor for DXIL shader passthrough. +#[derive(Debug, Clone)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct DxilPassthroughDescriptor<'a> { + /// The shader code. + pub code: Cow<'a, [u8]>, + /// The entry point name. + pub entry_point: String, +} + /// Descriptor for a shader module given by any of several sources. /// These shaders are passed through directly to the underlying api. /// At least one shader type that may be used by the backend must be `Some` or a panic is raised. #[derive(Debug, Clone)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct CreateShaderModuleDescriptorPassthrough<'a, L> { - /// Entrypoint. Unused for Spir-V. - pub entry_point: String, /// Debug label of the shader module. This will show up in graphics debuggers for easy identification. pub label: L, /// Number of workgroups in each dimension x, y and z. Unused for Spir-V. @@ -8133,17 +8187,17 @@ pub struct CreateShaderModuleDescriptorPassthrough<'a, L> { pub runtime_checks: ShaderRuntimeChecks, /// Binary SPIR-V data, in 4-byte words. - pub spirv: Option>, + pub spirv: Option>, /// Shader DXIL source. - pub dxil: Option>, + pub dxil: Option>, /// Shader MSL source. - pub msl: Option>, + pub msl: Option>, /// Shader HLSL source. - pub hlsl: Option>, + pub hlsl: Option>, /// Shader GLSL source (currently unused). - pub glsl: Option>, + pub glsl: Option>, /// Shader WGSL source. - pub wgsl: Option>, + pub wgsl: Option>, } // This is so people don't have to fill in fields they don't use, like num_workgroups, @@ -8151,7 +8205,6 @@ pub struct CreateShaderModuleDescriptorPassthrough<'a, L> { impl<'a, L: Default> Default for CreateShaderModuleDescriptorPassthrough<'a, L> { fn default() -> Self { Self { - entry_point: "".into(), label: Default::default(), num_workgroups: (0, 0, 0), runtime_checks: ShaderRuntimeChecks::unchecked(), @@ -8172,7 +8225,6 @@ impl<'a, L> CreateShaderModuleDescriptorPassthrough<'a, L> { fun: impl FnOnce(&L) -> K, ) -> CreateShaderModuleDescriptorPassthrough<'a, K> { CreateShaderModuleDescriptorPassthrough { - entry_point: self.entry_point.clone(), label: fun(&self.label), num_workgroups: self.num_workgroups, runtime_checks: self.runtime_checks, @@ -8189,11 +8241,11 @@ impl<'a, L> CreateShaderModuleDescriptorPassthrough<'a, L> { /// Returns the source data for tracing purpose. pub fn trace_data(&self) -> &[u8] { if let Some(spirv) = &self.spirv { - bytemuck::cast_slice(spirv) + bytemuck::cast_slice(&spirv.code) } else if let Some(msl) = &self.msl { - msl.as_bytes() + msl.code.as_bytes() } else if let Some(dxil) = &self.dxil { - dxil + &dxil.code } else { panic!("No binary data provided to `ShaderModuleDescriptorGeneric`") } diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index a9f5d898205..aa31bf7c859 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -94,21 +94,23 @@ pub use wgt::{ CommandBufferDescriptor, CompareFunction, CompositeAlphaMode, CopyExternalImageDestInfo, CoreCounters, DepthBiasState, DepthStencilState, DeviceLostReason, DeviceType, DownlevelCapabilities, DownlevelFlags, DownlevelLimits, Dx12BackendOptions, Dx12Compiler, - DxcShaderModel, DynamicOffset, ExperimentalFeatures, Extent3d, ExternalTextureFormat, - ExternalTextureTransferFunction, Face, Features, FeaturesWGPU, FeaturesWebGPU, FilterMode, - FrontFace, GlBackendOptions, GlFenceBehavior, Gles3MinorVersion, HalCounters, - ImageSubresourceRange, IndexFormat, InstanceDescriptor, InstanceFlags, InternalCounters, - Limits, MemoryBudgetThresholds, MemoryHints, MultisampleState, NoopBackendOptions, Origin2d, - Origin3d, PipelineStatisticsTypes, PollError, PollStatus, PolygonMode, PowerPreference, - PredefinedColorSpace, PresentMode, PresentationTimestamp, PrimitiveState, PrimitiveTopology, - PushConstantRange, QueryType, RenderBundleDepthStencil, RequestAdapterError, - SamplerBindingType, SamplerBorderColor, ShaderLocation, ShaderModel, ShaderRuntimeChecks, - ShaderStages, StencilFaceState, StencilOperation, StencilState, StorageTextureAccess, - SurfaceCapabilities, SurfaceStatus, TexelCopyBufferLayout, TextureAspect, TextureDimension, - TextureFormat, TextureFormatFeatureFlags, TextureFormatFeatures, TextureSampleType, - TextureTransition, TextureUsages, TextureUses, TextureViewDimension, Trace, VertexAttribute, - VertexFormat, VertexStepMode, WasmNotSend, WasmNotSendSync, WasmNotSync, COPY_BUFFER_ALIGNMENT, - COPY_BYTES_PER_ROW_ALIGNMENT, MAP_ALIGNMENT, PUSH_CONSTANT_ALIGNMENT, + DxcShaderModel, DxilPassthroughDescriptor, DynamicOffset, ExperimentalFeatures, Extent3d, + ExternalTextureFormat, ExternalTextureTransferFunction, Face, Features, FeaturesWGPU, + FeaturesWebGPU, FilterMode, FrontFace, GlBackendOptions, GlFenceBehavior, Gles3MinorVersion, + GlslPassthroughDescriptor, HalCounters, HlslPassthroughDescriptor, ImageSubresourceRange, + IndexFormat, InstanceDescriptor, InstanceFlags, InternalCounters, Limits, + MemoryBudgetThresholds, MemoryHints, MslPassthroughDescriptor, MultisampleState, + NoopBackendOptions, Origin2d, Origin3d, PipelineStatisticsTypes, PollError, PollStatus, + PolygonMode, PowerPreference, PredefinedColorSpace, PresentMode, PresentationTimestamp, + PrimitiveState, PrimitiveTopology, PushConstantRange, QueryType, RenderBundleDepthStencil, + RequestAdapterError, SamplerBindingType, SamplerBorderColor, ShaderLocation, ShaderModel, + ShaderRuntimeChecks, ShaderStages, SpirvPassthroughDescriptor, StencilFaceState, + StencilOperation, StencilState, StorageTextureAccess, SurfaceCapabilities, SurfaceStatus, + TexelCopyBufferLayout, TextureAspect, TextureDimension, TextureFormat, + TextureFormatFeatureFlags, TextureFormatFeatures, TextureSampleType, TextureTransition, + TextureUsages, TextureUses, TextureViewDimension, Trace, VertexAttribute, VertexFormat, + VertexStepMode, WasmNotSend, WasmNotSendSync, WasmNotSync, WgslPassthroughDescriptor, + COPY_BUFFER_ALIGNMENT, COPY_BYTES_PER_ROW_ALIGNMENT, MAP_ALIGNMENT, PUSH_CONSTANT_ALIGNMENT, QUERY_RESOLVE_BUFFER_ALIGNMENT, QUERY_SET_MAX_QUERIES, QUERY_SIZE, VERTEX_ALIGNMENT, }; diff --git a/wgpu/src/macros.rs b/wgpu/src/macros.rs index 2f9e8e9ec59..ac2451c76bf 100644 --- a/wgpu/src/macros.rs +++ b/wgpu/src/macros.rs @@ -357,7 +357,7 @@ macro_rules! precompile_glsl { #[doc(hidden)] pub mod helpers { pub use alloc::borrow::Cow; - pub use alloc::string::String; + pub use alloc::string::{String, ToString}; pub use core::{include_bytes, include_str}; #[cfg(feature = "precompile")] pub use wgpu_precompile_macro::precompile; From 2f0293d33ac7b9635610a31d556a9ba95614a8b0 Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Fri, 12 Sep 2025 12:49:58 -0500 Subject: [PATCH 15/47] Enhanced macro slightly --- examples/features/src/hello_triangle/mod.rs | 2 + tests/tests/wgpu-gpu/precompile/mod.rs | 107 ++++++++++---------- wgpu-precompile-macro/src/lib.rs | 17 +++- wgpu/src/macros.rs | 12 +-- 4 files changed, 76 insertions(+), 62 deletions(-) diff --git a/examples/features/src/hello_triangle/mod.rs b/examples/features/src/hello_triangle/mod.rs index 826304caa08..a18616c53ab 100644 --- a/examples/features/src/hello_triangle/mod.rs +++ b/examples/features/src/hello_triangle/mod.rs @@ -49,12 +49,14 @@ async fn run(event_loop: EventLoop<()>, window: Window) { device.create_shader_module_passthrough(wgpu::include_precompiled_wgsl!( "src/hello_triangle/shader.wgsl", "vs_main", + all )) }; let fs_shader = unsafe { device.create_shader_module_passthrough(wgpu::include_precompiled_wgsl!( "src/hello_triangle/shader.wgsl", "fs_main", + all )) }; diff --git a/tests/tests/wgpu-gpu/precompile/mod.rs b/tests/tests/wgpu-gpu/precompile/mod.rs index 0a2cbd36bdd..7c2ac29d72d 100644 --- a/tests/tests/wgpu-gpu/precompile/mod.rs +++ b/tests/tests/wgpu-gpu/precompile/mod.rs @@ -9,60 +9,61 @@ static PRECOMPILE_ALL_STAGES_TEST: GpuTestConfiguration = GpuTestConfiguration:: .parameters( TestParameters::default().features(wgpu::Features::EXPERIMENTAL_PASSTHROUGH_SHADERS), ) - .run_async( - async - | ctx - | unsafe { - let _ = - ctx.device - .create_shader_module_passthrough(wgpu::include_precompiled_wgsl!( - "tests/wgpu-gpu/precompile/shader.wgsl", - "vs_main", - )); - let _ = - ctx.device - .create_shader_module_passthrough(wgpu::include_precompiled_wgsl!( - "tests/wgpu-gpu/precompile/shader.wgsl", - "fs_main", - all - )); - let _ = - ctx.device - .create_shader_module_passthrough(wgpu::include_precompiled_wgsl!( - "tests/wgpu-gpu/precompile/shader.wgsl", - "cs_main", - glsl spirv wgsl hlsl msl - )); - let _ = ctx - .device - .create_shader_module_passthrough(wgpu::precompile_wgsl!( - r#" + .run_async(async |ctx| unsafe { + let _ = ctx + .device + .create_shader_module_passthrough(wgpu::include_precompiled_wgsl!( + "tests/wgpu-gpu/precompile/shader.wgsl", + "vs_main", + all + )); + let _ = ctx + .device + .create_shader_module_passthrough(wgpu::include_precompiled_wgsl!( + "tests/wgpu-gpu/precompile/shader.wgsl", + "fs_main", + all + )); + let _ = ctx + .device + .create_shader_module_passthrough(wgpu::include_precompiled_wgsl!( + "tests/wgpu-gpu/precompile/shader.wgsl", + "cs_main", + glsl spirv wgsl hlsl msl + )); + let _ = ctx + .device + .create_shader_module_passthrough(wgpu::precompile_wgsl!( + r#" @compute @workgroup_size(1) fn cs_main() {} "#, - "cs_main", - )); - // This is just the GLSL file compiled with glslang -V shader.vert -o shader.spv. - // The spirv file must exist before parsing begins. I didn't want to add it to - // the build script but that is another viable option. - let _ = - ctx.device - .create_shader_module_passthrough(wgpu::include_precompiled_spirv!( - "tests/wgpu-gpu/precompile/shader.spv", - "main", - )); - let _ = - ctx.device - .create_shader_module_passthrough(wgpu::include_precompiled_glsl!( - "tests/wgpu-gpu/precompile/shader.vert", - vertex, - )); + "cs_main", + all + )); + // This is just the GLSL file compiled with glslang -V shader.vert -o shader.spv. + // The spirv file must exist before parsing begins. I didn't want to add it to + // the build script but that is another viable option. + let _ = ctx + .device + .create_shader_module_passthrough(wgpu::include_precompiled_spirv!( + "tests/wgpu-gpu/precompile/shader.spv", + "main", + all + )); + let _ = ctx + .device + .create_shader_module_passthrough(wgpu::include_precompiled_glsl!( + "tests/wgpu-gpu/precompile/shader.vert", + vertex, + all + )); - let _ = ctx - .device - .create_shader_module_passthrough(wgpu::precompile_glsl!( - r#" + let _ = ctx + .device + .create_shader_module_passthrough(wgpu::precompile_glsl!( + r#" #version 450 const float c_scale = 1.2; @@ -75,7 +76,7 @@ void main() { gl_Position = vec4(c_scale * a_pos, 0.0, 1.0); } "#, - vertex, - )); - }, - ); + vertex, + all + )); + }); diff --git a/wgpu-precompile-macro/src/lib.rs b/wgpu-precompile-macro/src/lib.rs index c5fcc600507..f6dfe2bc521 100644 --- a/wgpu-precompile-macro/src/lib.rs +++ b/wgpu-precompile-macro/src/lib.rs @@ -28,6 +28,7 @@ enum CompileTarget { Hlsl, Spirv, Glsl, + Dxil, AllSupported, } impl CompileTarget { @@ -38,6 +39,7 @@ impl CompileTarget { "hlsl" => Self::Hlsl, "spirv" => Self::Spirv, "glsl" => Self::Glsl, + "dxil" => Self::Dxil, "all" => Self::AllSupported, other => panic!("Unrecognized compile target: {other}"), } @@ -144,9 +146,18 @@ impl Parse for MacroArgs { } impl MacroArgs { fn target_enabled(&self, target: CompileTarget) -> bool { - self.targets.contains(&target) - || self.targets.contains(&CompileTarget::AllSupported) - || self.targets.is_empty() + match target { + CompileTarget::Dxil => self.targets.contains(&CompileTarget::Dxil), + CompileTarget::Hlsl => { + (self.targets.contains(&CompileTarget::Hlsl) + || self.targets.contains(&CompileTarget::AllSupported)) + && !self.targets.contains(&CompileTarget::Dxil) + } + CompileTarget::AllSupported => unreachable!(), + other => { + self.targets.contains(&other) || self.targets.contains(&CompileTarget::AllSupported) + } + } } } diff --git a/wgpu/src/macros.rs b/wgpu/src/macros.rs index ac2451c76bf..d67b337b0e5 100644 --- a/wgpu/src/macros.rs +++ b/wgpu/src/macros.rs @@ -250,7 +250,7 @@ macro_rules! hal_type_gles { #[macro_export] #[cfg(feature = "precompile")] macro_rules! include_precompiled_wgsl { - ($path: literal, $entry: literal, $($id: ident)*) => { + ($path: literal, $entry: literal, $($id: ident)+) => { $crate::__macro_helpers::precompile!($crate wgsl true $path $entry $($id)*) }; } @@ -274,7 +274,7 @@ macro_rules! include_precompiled_wgsl { #[macro_export] #[cfg(feature = "precompile")] macro_rules! precompile_wgsl { - ($shader: literal, $entry: literal, $($id: ident)*) => { + ($shader: literal, $entry: literal, $($id: ident)+) => { $crate::__macro_helpers::precompile!($crate wgsl false $shader $entry $($id)*) }; } @@ -300,7 +300,7 @@ macro_rules! precompile_wgsl { #[macro_export] #[cfg(feature = "precompile")] macro_rules! include_precompiled_spirv { - ($path: literal, $entry: literal, $($id: ident)*) => { + ($path: literal, $entry: literal, $($id: ident)+) => { $crate::__macro_helpers::precompile!($crate spirv true $path $entry $($id)*) }; } @@ -325,7 +325,7 @@ macro_rules! include_precompiled_spirv { #[macro_export] #[cfg(feature = "precompile")] macro_rules! include_precompiled_glsl { - ($path: literal, $stage: ident, $($id: ident)*) => { + ($path: literal, $stage: ident, $($id: ident)+) => { $crate::__macro_helpers::precompile!($crate glsl $stage true $path "main" $($id)*) }; } @@ -349,8 +349,8 @@ macro_rules! include_precompiled_glsl { #[macro_export] #[cfg(feature = "precompile")] macro_rules! precompile_glsl { - ($shader: literal, $stage: ident, $($id: ident)*) => { - $crate::__macro_helpers::precompile!($crate glsl $stage false $shader "main" $($id)*) + ($shader: literal, $stage: ident, $($id: ident)+) => { + $crate::__macro_helpers::precompile!($crate glsl $stage false $shader "main" $($id)+) }; } From 2229f05d0abb683149d85298a2cd59bd70439b8c Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Fri, 12 Sep 2025 13:00:54 -0500 Subject: [PATCH 16/47] Worked a little more --- wgpu-precompile-macro/src/lib.rs | 48 +++++++++++++-------- wgpu/src/macros.rs | 71 +++++++++++++------------------- 2 files changed, 59 insertions(+), 60 deletions(-) diff --git a/wgpu-precompile-macro/src/lib.rs b/wgpu-precompile-macro/src/lib.rs index f6dfe2bc521..0b17c5c2aa1 100644 --- a/wgpu-precompile-macro/src/lib.rs +++ b/wgpu-precompile-macro/src/lib.rs @@ -274,34 +274,48 @@ pub fn precompile(input: TokenStream) -> TokenStream { #[cfg(not(feature = "msl"))] let msl_tokens = none_tokens.clone(); - // TODO: compile DXIL if DXC can be detected + #[cfg(feature = "hlsl")] + let (hlsl_str, hlsl_entry_point) = + if args.target_enabled(CompileTarget::Hlsl) || args.target_enabled(CompileTarget::Dxil) { + let mut hlsl_str = String::new(); + let reflection = naga::back::hlsl::Writer::new( + &mut hlsl_str, + &naga::back::hlsl::Options::default(), + &naga::back::hlsl::PipelineOptions { + entry_point: Some((shader_stage, args.entry_point.clone())), + }, + ) + .write(&module, &module_info, None) + .expect("Naga failed to write HLSL code"); + let entry_point = reflection.entry_point_names[0].as_ref().unwrap(); + (hlsl_str, entry_point.clone()) + } else { + (String::new(), String::new()) + }; + #[cfg(feature = "hlsl")] let hlsl_tokens = if args.target_enabled(CompileTarget::Hlsl) { - let mut hlsl_str = String::new(); - let reflection = naga::back::hlsl::Writer::new( - &mut hlsl_str, - &naga::back::hlsl::Options::default(), - &naga::back::hlsl::PipelineOptions { - entry_point: Some((shader_stage, args.entry_point.clone())), - }, - ) - .write(&module, &module_info, None) - .expect("Naga failed to write HLSL code"); - let entry_point = reflection.entry_point_names[0].as_ref().unwrap(); quote! { #wgpu_path::__macro_helpers::Some(#wgpu_path::HlslPassthroughDescriptor { code: #wgpu_path::__macro_helpers::Cow::Borrowed(#hlsl_str), - entry_point: #wgpu_path::__macro_helpers::ToString::to_string(#entry_point), + entry_point: #wgpu_path::__macro_helpers::ToString::to_string(#hlsl_entry_point), }) } } else { - quote! { - #wgpu_path::__macro_helpers::None - } + none_tokens.clone() }; #[cfg(not(feature = "hlsl"))] let hlsl_tokens = none_tokens.clone(); + #[cfg(feature = "hlsl")] + let dxil_tokens = if args.target_enabled(CompileTarget::Dxil) { + todo!() + } else { + none_tokens.clone() + }; + #[cfg(not(feature = "hlsl"))] + let dxil_tokens = none_tokens.clone(); + #[cfg(feature = "wgsl")] let wgsl_tokens = if args.target_enabled(CompileTarget::Wgsl) { let mut writer = @@ -369,7 +383,7 @@ pub fn precompile(input: TokenStream) -> TokenStream { num_workgroups: (#x, #y, #z), runtime_checks: #wgpu_path::ShaderRuntimeChecks::default(), spirv: #spirv_tokens, - dxil: #wgpu_path::__macro_helpers::None, + dxil: #dxil_tokens, msl: #msl_tokens, hlsl: #hlsl_tokens, glsl: #glsl_tokens, diff --git a/wgpu/src/macros.rs b/wgpu/src/macros.rs index d67b337b0e5..7c22d2f50d3 100644 --- a/wgpu/src/macros.rs +++ b/wgpu/src/macros.rs @@ -237,16 +237,13 @@ macro_rules! hal_type_gles { /// This macro is always valid, even if the `wgsl` feature isn't enabled. /// /// ```ignore -/// device.create_shader_module_passthrough(include_precompiled_wgsl!("path/to/file", "entry_point_name",)) -/// ``` -/// or -/// ```ignore /// device.create_shader_module_passthrough(include_precompiled_wgsl!("path/to/file", "entry_point_name", backends)) /// ``` -/// If specified, backends is a list of shader languages to be used, without comma separators. For example, -/// `wgsl glsl hlsl spirv msl`. If the `all` backend is specified, then all shader languages supported by the -/// backends in wgpu features will be compiled for. All backends will be compiled for by naga; the input wgsl -/// code won't be the same as the output wgsl. +/// Backends is a list of shader languages to be written, without comma separators. For example, +/// `wgsl glsl hlsl spirv msl dxil`. If the `all` backend is specified, then all shader languages supported by the +/// backends in wgpu features will be compiled for (except dxil which must be specified separately). If the dxil +/// backend is enabled, hlsl won't be compiled. All backends will be compiled for by naga; the input wgsl code won't +/// necessarily be the same as the output wgsl. #[macro_export] #[cfg(feature = "precompile")] macro_rules! include_precompiled_wgsl { @@ -261,16 +258,13 @@ macro_rules! include_precompiled_wgsl { /// This macro is always valid when the `precompile` feature is enabled, even if the `wgsl` feature isn't enabled. /// /// ```ignore -/// device.create_shader_module_passthrough(precompile_wgsl!("", "entry_point_name",)) +/// device.create_shader_module_passthrough(precompile_wgsl!("shader_code", "entry_point_name", backends)) /// ``` -/// or -/// ```ignore -/// device.create_shader_module_passthrough(precompile_wgsl!("", "entry_point_name", backends)) -/// ``` -/// If specified, backends is a list of shader languages to be used, without comma separators. For example, -/// `wgsl glsl hlsl spirv msl`. If the `all` backend is specified, then all shader languages supported by the -/// backends in wgpu features will be compiled for. All backends will be compiled for by naga; the input wgsl -/// code won't be the same as the output wgsl. +/// Backends is a list of shader languages to be written, without comma separators. For example, +/// `wgsl glsl hlsl spirv msl dxil`. If the `all` backend is specified, then all shader languages supported by the +/// backends in wgpu features will be compiled for (except dxil which must be specified separately). If the dxil +/// backend is enabled, hlsl won't be compiled. All backends will be compiled for by naga; the input wgsl code won't +/// necessarily be the same as the output wgsl. #[macro_export] #[cfg(feature = "precompile")] macro_rules! precompile_wgsl { @@ -287,16 +281,13 @@ macro_rules! precompile_wgsl { /// enabled. /// /// ```ignore -/// device.create_shader_module_passthrough(include_precompiled_spirv!("path/to/file", "entry_point_name",)) -/// ``` -/// or -/// ```ignore /// device.create_shader_module_passthrough(include_precompiled_spirv!("path/to/file", "entry_point_name", backends)) /// ``` -/// If specified, backends is a list of shader languages to be used, without comma separators. For example, -/// `wgsl glsl hlsl spirv msl`. If the `all` backend is specified, then all shader languages supported by the -/// backends in wgpu features will be compiled for. All backends will be compiled for by naga; the input spirv -/// code won't be the same as the output spirv. +/// Backends is a list of shader languages to be written, without comma separators. For example, +/// `wgsl glsl hlsl spirv msl dxil`. If the `all` backend is specified, then all shader languages supported by the +/// backends in wgpu features will be compiled for (except dxil which must be specified separately). If the dxil +/// backend is enabled, hlsl won't be compiled. All backends will be compiled for by naga; the input spirv code won't +/// necessarily be the same as the output spirv. #[macro_export] #[cfg(feature = "precompile")] macro_rules! include_precompiled_spirv { @@ -312,16 +303,13 @@ macro_rules! include_precompiled_spirv { /// This macro is always valid, even if the `glsl` feature isn't enabled. /// /// ```ignore -/// device.create_shader_module_passthrough(include_precompiled_wgsl!("path/to/file", shader_stage, "entry_point_name",)) -/// ``` -/// or -/// ```ignore -/// device.create_shader_module_passthrough(include_precompiled_wgsl!("path/to/file", shader_stage, "entry_point_name", backends)) +/// device.create_shader_module_passthrough(include_precompiled_glsl!("path/to/file", "entry_point_name", backends)) /// ``` -/// If specified, backends is a list of shader languages to be used, without comma separators. For example, -/// `wgsl glsl hlsl spirv msl`. If the `all` backend is specified, then all shader languages supported by the -/// backends in wgpu features will be compiled for. All backends will be compiled for by naga; the input glsl -/// code won't be the same as the output glsl. +/// Backends is a list of shader languages to be written, without comma separators. For example, +/// `wgsl glsl hlsl spirv msl dxil`. If the `all` backend is specified, then all shader languages supported by the +/// backends in wgpu features will be compiled for (except dxil which must be specified separately). If the dxil +/// backend is enabled, hlsl won't be compiled. All backends will be compiled for by naga; the input glsl code won't +/// necessarily be the same as the output glsl. #[macro_export] #[cfg(feature = "precompile")] macro_rules! include_precompiled_glsl { @@ -336,16 +324,13 @@ macro_rules! include_precompiled_glsl { /// This macro is always valid when the `precompile` feature is enabled, even if the `glsl` feature isn't enabled. /// /// ```ignore -/// device.create_shader_module_passthrough(precompile_wgsl!("", "entry_point_name",)) -/// ``` -/// or -/// ```ignore -/// device.create_shader_module_passthrough(precompile_wgsl!("", "entry_point_name", backends)) +/// device.create_shader_module_passthrough(precompile_glsl!("shader_code", "entry_point_name", backends)) /// ``` -/// If specified, backends is a list of shader languages to be used, without comma separators. For example, -/// `wgsl glsl hlsl spirv msl`. If the `all` backend is specified, then all shader languages supported by the -/// backends in wgpu features will be compiled for. All backends will be compiled for by naga; the input glsl -/// code won't be the same as the output glsl. +/// Backends is a list of shader languages to be written, without comma separators. For example, +/// `wgsl glsl hlsl spirv msl dxil`. If the `all` backend is specified, then all shader languages supported by the +/// backends in wgpu features will be compiled for (except dxil which must be specified separately). If the dxil +/// backend is enabled, hlsl won't be compiled. All backends will be compiled for by naga; the input glsl code won't +/// be the same as the output glsl. #[macro_export] #[cfg(feature = "precompile")] macro_rules! precompile_glsl { From 11c9bcab7a1a2f0a00b48d52ef6a861c003905de Mon Sep 17 00:00:00 2001 From: SupaMaggie70Incorporated Date: Fri, 12 Sep 2025 13:21:08 -0500 Subject: [PATCH 17/47] Fixed vulkan backend y flip --- wgpu-precompile-macro/src/lib.rs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/wgpu-precompile-macro/src/lib.rs b/wgpu-precompile-macro/src/lib.rs index 0b17c5c2aa1..3994d905aa8 100644 --- a/wgpu-precompile-macro/src/lib.rs +++ b/wgpu-precompile-macro/src/lib.rs @@ -228,10 +228,25 @@ pub fn precompile(input: TokenStream) -> TokenStream { #[cfg(feature = "spv")] let spirv_tokens = if args.target_enabled(CompileTarget::Spirv) { - let spirv_data = naga::back::spv::write_vec( + use naga::back::spv; + // Ripped from wgpu-hal backend + let mut flags = spv::WriterFlags::empty(); + flags.set(spv::WriterFlags::DEBUG, false); + flags.set(spv::WriterFlags::LABEL_VARYINGS, false); + flags.set( + spv::WriterFlags::FORCE_POINT_SIZE, + //Note: we could technically disable this when we are compiling separate entry points, + // and we know exactly that the primitive topology is not `PointList`. + // But this requires cloning the `spv::Options` struct, which has heap allocations. + true, // could check `super::Workarounds::SEPARATE_ENTRY_POINTS` + ); + let spirv_data = spv::write_vec( &module, &module_info, - &naga::back::spv::Options::default(), + &naga::back::spv::Options { + flags, + ..Default::default() + }, Some(&naga::back::spv::PipelineOptions { shader_stage, entry_point: args.entry_point.clone(), From 662c76cc79ac27d2d63e21598990b74e37283ba5 Mon Sep 17 00:00:00 2001 From: SupaMaggie70Incorporated Date: Fri, 12 Sep 2025 13:38:48 -0500 Subject: [PATCH 18/47] Allowed precompiling dxil (will break everything again) --- Cargo.lock | 146 ++++++++++++++++++++++--------- wgpu-precompile-macro/Cargo.toml | 5 +- wgpu-precompile-macro/src/lib.rs | 29 +++++- 3 files changed, 137 insertions(+), 43 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c7b0cb2da84..872af1154c9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -224,7 +224,7 @@ dependencies = [ "argh_shared", "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] @@ -274,7 +274,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] @@ -381,7 +381,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn", + "syn 2.0.106", ] [[package]] @@ -499,7 +499,7 @@ checksum = "4f154e572231cb6ba2bd1176980827e3d5dc04cc183a75dea38109fbdd672d29" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] @@ -601,7 +601,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b4a6cae9efc04cc6cbb8faf338d2c497c165c83e74509cf4dbedea948bbf6e5" dependencies = [ "quote", - "syn", + "syn 2.0.106", ] [[package]] @@ -773,7 +773,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] @@ -799,6 +799,37 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" +[[package]] +name = "com" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e17887fd17353b65b1b2ef1c526c83e26cd72e74f598a8dc1bee13a48f3d9f6" +dependencies = [ + "com_macros", +] + +[[package]] +name = "com_macros" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d375883580a668c7481ea6631fc1a8863e33cc335bf56bfad8d7e6d4b04b13a5" +dependencies = [ + "com_macros_support", + "proc-macro2", + "syn 1.0.109", +] + +[[package]] +name = "com_macros_support" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad899a1087a9296d5644792d7cb72b8e34c1bec8e7d4fbc002230169a6e8710c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "combine" version = "4.6.7" @@ -1129,7 +1160,7 @@ checksum = "babccedee31ce7e57c3e6dff2cb3ab8d68c49d0df8222fe0d11d628e65192790" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] @@ -1145,7 +1176,7 @@ dependencies = [ "stringcase", "strum 0.25.0", "strum_macros 0.25.3", - "syn", + "syn 2.0.106", "thiserror 2.0.16", ] @@ -1272,7 +1303,7 @@ checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] @@ -1295,7 +1326,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] @@ -1378,7 +1409,7 @@ checksum = "1796db3d892515842ca2dfb11124c4bb4a9e58d9f2c5c1072e5bca1b2334507b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] @@ -1545,7 +1576,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] @@ -1654,7 +1685,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] @@ -1931,6 +1962,21 @@ dependencies = [ "serde", ] +[[package]] +name = "hassle-rs" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d6af3d4e299d56c5c738c71de7e7c7d2c60051df4bad2cecdb2e05cd679b269" +dependencies = [ + "bitflags 2.9.4", + "com", + "libc", + "libloading", + "thiserror 1.0.69", + "widestring", + "winapi", +] + [[package]] name = "heck" version = "0.4.1" @@ -2196,7 +2242,7 @@ checksum = "03343451ff899767262ec32146f6d559dd759fdadf42ff0e227c7c48f72594b4" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] @@ -2780,7 +2826,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] @@ -3198,7 +3244,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] @@ -3306,7 +3352,7 @@ checksum = "ac5da421106a50887c5b51d20806867db377fbb86bacf478ee0500a912e0c113" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] @@ -3355,7 +3401,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn", + "syn 2.0.106", ] [[package]] @@ -3375,7 +3421,7 @@ checksum = "07c277e4e643ef00c1233393c673f655e3672cf7eb3ba08a00bdd0ea59139b5f" dependencies = [ "proc-macro-rules-macros", "proc-macro2", - "syn", + "syn 2.0.106", ] [[package]] @@ -3387,7 +3433,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] @@ -3771,7 +3817,7 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] @@ -4050,7 +4096,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn", + "syn 2.0.106", ] [[package]] @@ -4062,7 +4108,18 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn", + "syn 2.0.106", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", ] [[package]] @@ -4084,7 +4141,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] @@ -4104,7 +4161,7 @@ checksum = "181f22127402abcf8ee5c83ccd5b408933fec36a6095cf82cda545634692657e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] @@ -4154,7 +4211,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] @@ -4165,7 +4222,7 @@ checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] @@ -4260,7 +4317,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] @@ -4666,7 +4723,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn", + "syn 2.0.106", "wasm-bindgen-shared", ] @@ -4701,7 +4758,7 @@ checksum = "7bb4ce89b08211f923caf51d527662b75bdc9c9c7aab40f86dcb9fb85ac552aa" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -4736,7 +4793,7 @@ checksum = "c5ada2ab788d46d4bda04c9d567702a79c8ced14f51f221646a16ed39d0e6a5d" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] @@ -5167,7 +5224,7 @@ version = "26.0.0" dependencies = [ "heck 0.5.0", "quote", - "syn", + "syn 2.0.106", ] [[package]] @@ -5175,9 +5232,10 @@ name = "wgpu-precompile-macro" version = "26.0.0" dependencies = [ "hashbrown", + "hassle-rs", "naga", "quote", - "syn", + "syn 2.0.106", ] [[package]] @@ -5270,6 +5328,12 @@ dependencies = [ "winsafe", ] +[[package]] +name = "widestring" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd7cf3379ca1aac9eea11fba24fd7e315d621f8dfe35c8d7d2be8b793726e07d" + [[package]] name = "winapi" version = "0.3.9" @@ -5378,7 +5442,7 @@ checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] @@ -5389,7 +5453,7 @@ checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] @@ -5400,7 +5464,7 @@ checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] @@ -5411,7 +5475,7 @@ checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] @@ -6014,7 +6078,7 @@ checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", "synstructure", ] @@ -6035,7 +6099,7 @@ checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] @@ -6055,7 +6119,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", "synstructure", ] @@ -6089,5 +6153,5 @@ checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] diff --git a/wgpu-precompile-macro/Cargo.toml b/wgpu-precompile-macro/Cargo.toml index ba6de9beeca..45e9e6aecea 100644 --- a/wgpu-precompile-macro/Cargo.toml +++ b/wgpu-precompile-macro/Cargo.toml @@ -22,11 +22,14 @@ msl = ["naga/msl-out"] wgsl = ["naga/wgsl-out"] spv = ["naga/spv-out"] glsl = ["naga/glsl-out"] -hlsl = ["naga/hlsl-out"] +hlsl = ["naga/hlsl-out", "dep:hassle-rs"] [dependencies] naga = { workspace = true, features = ["spv-in", "wgsl-in", "glsl-in"] } +# TODO: verify that this dependency should be allowed +hassle-rs = { version = "0.12", optional = true } + quote.workspace = true syn = { workspace = true, features = ["full"] } hashbrown.workspace = true diff --git a/wgpu-precompile-macro/src/lib.rs b/wgpu-precompile-macro/src/lib.rs index 3994d905aa8..3e0b8cdcb77 100644 --- a/wgpu-precompile-macro/src/lib.rs +++ b/wgpu-precompile-macro/src/lib.rs @@ -146,6 +146,9 @@ impl Parse for MacroArgs { } impl MacroArgs { fn target_enabled(&self, target: CompileTarget) -> bool { + // TODO: only enable if we are actually targetting that platform. + // This is especially important for DXIL, which requires dxc. No need + // to fail to compile on MacOS if dxc isn't present! match target { CompileTarget::Dxil => self.targets.contains(&CompileTarget::Dxil), CompileTarget::Hlsl => { @@ -324,7 +327,31 @@ pub fn precompile(input: TokenStream) -> TokenStream { #[cfg(feature = "hlsl")] let dxil_tokens = if args.target_enabled(CompileTarget::Dxil) { - todo!() + let target_profile = match shader_stage { + naga::ShaderStage::Vertex => "vs_5_1", + naga::ShaderStage::Fragment => "ps_5_1", + naga::ShaderStage::Compute => "cs_5_1", + naga::ShaderStage::Task => "as_5_1", + naga::ShaderStage::Mesh => "ms_5_1", + }; + let dxil = hassle_rs::compile_hlsl( + match &args.file_name { + Some(f) => f, + None => "precompile-inline.hlsl", + }, + &hlsl_str, + &hlsl_entry_point, + target_profile, + &["-spirv"], + &[], + ) + .expect("Hassle failed to compile HLSL to DXIL"); + quote! { + #wgpu_path::__macro_helpers::Some(#wgpu_path::DxilPassthroughDescriptor { + code: #wgpu_path::__macro_helpers::Cow::Borrowed(&[#(#dxil),*]), + entry_point: #wgpu_path::__macro_helpers::ToString::to_string(#hlsl_entry_point), + }) + } } else { none_tokens.clone() }; From 9d45fd9e56801a31c37c3f130db894f2a04f70de Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Fri, 12 Sep 2025 14:39:40 -0500 Subject: [PATCH 19/47] Added a comment explaining why entry point name differs by shader language --- wgpu-types/src/lib.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/wgpu-types/src/lib.rs b/wgpu-types/src/lib.rs index dd6edd9e2c5..58ac483dc27 100644 --- a/wgpu-types/src/lib.rs +++ b/wgpu-types/src/lib.rs @@ -8176,6 +8176,9 @@ pub struct DxilPassthroughDescriptor<'a> { /// Descriptor for a shader module given by any of several sources. /// These shaders are passed through directly to the underlying api. /// At least one shader type that may be used by the backend must be `Some` or a panic is raised. +/// +/// The entry point name may differ by format. For example, SPIR-V and GLSL code will always parse +/// this as `main` but MSL treats `main` as a keyword so naga converts it to `_main`. #[derive(Debug, Clone)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct CreateShaderModuleDescriptorPassthrough<'a, L> { From 78250d32e94fc38d413cb40f2ea4aad89d30c4af Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Fri, 12 Sep 2025 15:16:34 -0500 Subject: [PATCH 20/47] Tried to fix various compile errors/typos --- wgpu-precompile-macro/src/lib.rs | 2 +- wgpu/src/backend/webgpu.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/wgpu-precompile-macro/src/lib.rs b/wgpu-precompile-macro/src/lib.rs index 3e0b8cdcb77..bb1e7a40d9e 100644 --- a/wgpu-precompile-macro/src/lib.rs +++ b/wgpu-precompile-macro/src/lib.rs @@ -146,7 +146,7 @@ impl Parse for MacroArgs { } impl MacroArgs { fn target_enabled(&self, target: CompileTarget) -> bool { - // TODO: only enable if we are actually targetting that platform. + // TODO: only enable if we are actually targeting that platform. // This is especially important for DXIL, which requires dxc. No need // to fail to compile on MacOS if dxc isn't present! match target { diff --git a/wgpu/src/backend/webgpu.rs b/wgpu/src/backend/webgpu.rs index 920ad58ba17..2476e5d1536 100644 --- a/wgpu/src/backend/webgpu.rs +++ b/wgpu/src/backend/webgpu.rs @@ -1865,11 +1865,11 @@ impl dispatch::DeviceInterface for WebDevice { desc: &crate::ShaderModuleDescriptorPassthrough<'_>, ) -> dispatch::DispatchShaderModule { let shader_module_result = if let Some(ref code) = desc.wgsl { - let shader_module = webgpu_sys::GpuShaderModuleDescriptor::new(code); + let shader_module = webgpu_sys::GpuShaderModuleDescriptor::new(&code.code); Ok(( shader_module, WebShaderCompilationInfo::Wgsl { - source: code.to_string(), + source: code.code.to_string(), }, )) } else { From 9c8988e016d61f26488711f7a1f5fec469b5b1be Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Mon, 15 Sep 2025 10:49:13 -0500 Subject: [PATCH 21/47] Added changelog entry --- CHANGELOG.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8bac70e359f..ed798ac1c32 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -117,6 +117,21 @@ Difference for SPIR-V passthrough: ``` This allows using precompiled shaders without manually checking which backend's code to pass, for example if you have shaders precompiled for both DXIL and SPIR-V. +This comes along with an optional wgpu feature, `precompiled`, which provides a macro for precompiling shaders. For example, the following code is used in a test to precompile the `vs_main` entry point of `shader.wgsl` for all shader backends, excluding DXIL. +```rust +ctx + .device + .create_shader_module_passthrough(wgpu::include_precompiled_wgsl!( + // Shader source file + "shader.wgsl", + // Shader entry point + "vs_main", + // Target formats: a space separated list of backends, for example "all" or "hlsl glsl spirv" + all + )) +``` +You can also precompile SPIR-V and GLSL, and you can precompile raw shader code (code that lives in the rust file) using `precompile_wgsl` instead of `include_precompiled_wgsl`, and the analogous functions for GLSL. + #### Buffer mapping apis no longer have lifetimes `Buffer::get_mapped_range()`, `Buffer::get_mapped_range_mut()`, and `Queue::write_buffer_with()` now return guard objects without any lifetimes. This From f12f60f618d752899b4a31e165346409cbe6be65 Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Mon, 15 Sep 2025 11:26:24 -0500 Subject: [PATCH 22/47] Updated changelog entry again for the spirv passthrough change --- CHANGELOG.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ed798ac1c32..ac9ea17764b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -109,9 +109,10 @@ Difference for SPIR-V passthrough: - }, - )) + device.create_shader_module_passthrough(wgpu::ShaderModuleDescriptorPassthrough { -+ entry_point: "main".into(), + label: None, -+ spirv: Some(spirv_code), ++ spirv: Some(wgpu::SpirvPassthroughDescriptor { ++ code: spirv_code ++ }), + ..Default::default() }) ``` From d1f1675bb1c3faf893c5003605a3e2345a0b5e8b Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Mon, 15 Sep 2025 13:34:34 -0500 Subject: [PATCH 23/47] Refactored to allow compilation guards --- Cargo.lock | 1 + examples/features/src/hello_triangle/mod.rs | 4 +- tests/tests/wgpu-gpu/precompile/mod.rs | 6 +- wgpu-precompile-macro/Cargo.toml | 12 +- wgpu-precompile-macro/src/lib.rs | 264 ++++++++++++++++---- wgpu/Cargo.toml | 16 +- wgpu/src/macros.rs | 2 +- 7 files changed, 238 insertions(+), 67 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 872af1154c9..616cc0b208b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5234,6 +5234,7 @@ dependencies = [ "hashbrown", "hassle-rs", "naga", + "proc-macro2", "quote", "syn 2.0.106", ] diff --git a/examples/features/src/hello_triangle/mod.rs b/examples/features/src/hello_triangle/mod.rs index a18616c53ab..1558e348608 100644 --- a/examples/features/src/hello_triangle/mod.rs +++ b/examples/features/src/hello_triangle/mod.rs @@ -49,14 +49,14 @@ async fn run(event_loop: EventLoop<()>, window: Window) { device.create_shader_module_passthrough(wgpu::include_precompiled_wgsl!( "src/hello_triangle/shader.wgsl", "vs_main", - all + all dxil )) }; let fs_shader = unsafe { device.create_shader_module_passthrough(wgpu::include_precompiled_wgsl!( "src/hello_triangle/shader.wgsl", "fs_main", - all + all dxil )) }; diff --git a/tests/tests/wgpu-gpu/precompile/mod.rs b/tests/tests/wgpu-gpu/precompile/mod.rs index 7c2ac29d72d..6e63e841a8d 100644 --- a/tests/tests/wgpu-gpu/precompile/mod.rs +++ b/tests/tests/wgpu-gpu/precompile/mod.rs @@ -15,21 +15,21 @@ static PRECOMPILE_ALL_STAGES_TEST: GpuTestConfiguration = GpuTestConfiguration:: .create_shader_module_passthrough(wgpu::include_precompiled_wgsl!( "tests/wgpu-gpu/precompile/shader.wgsl", "vs_main", - all + all dxil )); let _ = ctx .device .create_shader_module_passthrough(wgpu::include_precompiled_wgsl!( "tests/wgpu-gpu/precompile/shader.wgsl", "fs_main", - all + all dxil )); let _ = ctx .device .create_shader_module_passthrough(wgpu::include_precompiled_wgsl!( "tests/wgpu-gpu/precompile/shader.wgsl", "cs_main", - glsl spirv wgsl hlsl msl + glsl spirv wgsl hlsl msl dxil )); let _ = ctx .device diff --git a/wgpu-precompile-macro/Cargo.toml b/wgpu-precompile-macro/Cargo.toml index 45e9e6aecea..152bc4cf4c5 100644 --- a/wgpu-precompile-macro/Cargo.toml +++ b/wgpu-precompile-macro/Cargo.toml @@ -18,9 +18,18 @@ proc-macro = true # That means even if we are compiling to Windows, the MSL feature will be enabled # by default. So eventually to not include unnecessary shader files we should add # extra logic to detect target platforms and such. +metal = ["msl"] +webgpu = ["wgsl"] +vulkan = ["spirv"] +gles = ["glsl"] +dx12 = ["hlsl"] +angle = ["glsl"] +vulkan-portability = ["spirv"] +webgl = ["glsl"] + +spirv = ["naga/spv-out"] msl = ["naga/msl-out"] wgsl = ["naga/wgsl-out"] -spv = ["naga/spv-out"] glsl = ["naga/glsl-out"] hlsl = ["naga/hlsl-out", "dep:hassle-rs"] @@ -32,4 +41,5 @@ hassle-rs = { version = "0.12", optional = true } quote.workspace = true syn = { workspace = true, features = ["full"] } +proc-macro2 = "1.0" hashbrown.workspace = true diff --git a/wgpu-precompile-macro/src/lib.rs b/wgpu-precompile-macro/src/lib.rs index bb1e7a40d9e..ae8a308475d 100644 --- a/wgpu-precompile-macro/src/lib.rs +++ b/wgpu-precompile-macro/src/lib.rs @@ -78,7 +78,17 @@ fn parse_shader_stage(str: &str) -> Option { } } -struct MacroArgs { +fn shader_stage_to_string(stage: naga::ShaderStage) -> &'static str { + match stage { + naga::ShaderStage::Vertex => "vertex", + naga::ShaderStage::Fragment => "fragment", + naga::ShaderStage::Compute => "compute", + naga::ShaderStage::Mesh => "mesh", + naga::ShaderStage::Task => "task", + } +} + +struct PrecompileArgs { wgpu_crate: Path, source_type: SourceType, shader_stage: Option, @@ -87,7 +97,7 @@ struct MacroArgs { entry_point: String, file_name: Option, } -impl Parse for MacroArgs { +impl Parse for PrecompileArgs { fn parse(input: syn::parse::ParseStream) -> syn::Result { let wgpu_crate: Path = input.parse()?; let source_type = SourceType::parse(&input.parse::()?.to_string()); @@ -144,7 +154,7 @@ impl Parse for MacroArgs { }) } } -impl MacroArgs { +impl PrecompileArgs { fn target_enabled(&self, target: CompileTarget) -> bool { // TODO: only enable if we are actually targeting that platform. // This is especially important for DXIL, which requires dxc. No need @@ -164,13 +174,142 @@ impl MacroArgs { } } +/// All fields represented as string literals +struct PrecompileDxilArgs { + hlsl_code: String, + entry_point: String, + shader_stage: naga::ShaderStage, + /// For debug reasons + source_file_name: String, +} +impl Parse for PrecompileDxilArgs { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let hlsl_code = input.parse::()?.value(); + let entry_point = input.parse::()?.value(); + let shader_stage = parse_shader_stage(&input.parse::()?.value()).unwrap(); + let source_file_name = input.parse::()?.value(); + Ok(Self { + hlsl_code, + entry_point, + shader_stage, + source_file_name, + }) + } +} + +/// This is so we can conditionally hook into DXC depending on the target, which must be configured via #\[cfg] within the main program. +/// Proc macros don't directly have access to the target configuration. Invoking naga unnecessarily shouldn't be *too* major, but +/// compiling to macOS shouldn't require dxc be present. +#[proc_macro] +pub fn precompile_hlsl_to_dxil(input: TokenStream) -> TokenStream { + let args = parse_macro_input!(input as PrecompileDxilArgs); + let target_profile = match args.shader_stage { + naga::ShaderStage::Vertex => "vs_5_1", + naga::ShaderStage::Fragment => "ps_5_1", + naga::ShaderStage::Compute => "cs_5_1", + naga::ShaderStage::Task => "as_5_1", + naga::ShaderStage::Mesh => "ms_5_1", + }; + let dxil = hassle_rs::compile_hlsl( + &args.source_file_name, + &args.hlsl_code, + &args.entry_point, + target_profile, + &["-spirv"], + &[], + ) + .expect("Hassle failed to compile HLSL to DXIL"); + quote! { + &[#(#dxil),*] + } + .into() +} + +fn generate_conditional_guard(target: CompileTarget) -> proc_macro2::TokenStream { + // This is for my sanity. The guards should always be used within conditional code anyway. + #[allow(unused_variables)] + let always_false = quote! { + any() + }; + // This basically mirrors the things in wgpu-core/platform-deps + match target { + CompileTarget::Msl => { + // Related features: metal + #[cfg(feature = "metal")] + quote! { + target_vendor = "apple" + } + // Always false + #[cfg(not(feature = "metal"))] + always_false + } + CompileTarget::Glsl => { + // Related features: gles, webgl, angle + let webgl = if cfg!(feature = "webgl") { + quote! { + ,all(target_arch = "wasm32", not(target_os = "emscripten")) + } + } else { + quote! {} + }; + let angle = if cfg!(feature = "angle") { + quote! { + ,target_vendor = "apple" + } + } else { + quote! {} + }; + #[cfg(feature = "gles")] + quote! { + any(target_os = "emscripten", windows, target_os = "linux", target_os = "android" #angle #webgl) + } + #[cfg(not(feature = "gles"))] + always_false + } + CompileTarget::Spirv => { + // Related features: vulkan, vulkan-portability + #[cfg(all(feature = "vulkan", feature = "vulkan-portability"))] + quote! { + any(target_vendor = "apple", windows, target_os = "linux", target_os = "android") + } + #[cfg(all(feature = "vulkan", not(feature = "vulkan-portability")))] + quote! { + any(windows, target_os = "linux", target_os = "android") + } + #[cfg(not(feature = "vulkan"))] + always_false + } + CompileTarget::Wgsl => { + // Related features: webgpu + #[cfg(feature = "webgpu")] + quote! { + all(target_arch = "wasm32", not(target_os = "emscripten")) + } + #[cfg(not(feature = "webgpu"))] + always_false + } + CompileTarget::Hlsl | CompileTarget::Dxil => { + // Related features: dx12 + // Note: this differs slightly from platform-deps which enables dx12 even on linux/android, but just doesn't + // let you use it + #[cfg(feature = "dx12")] + quote! { + windows + } + #[cfg(not(feature = "dx12"))] + always_false + } + CompileTarget::AllSupported => unreachable!(), + } +} + /// This is to be re-exported by wgpu in a certain way, so that it can refer to items in the `wgpu` crate /// /// Input format: /// precompile!(wgpu_crate_name source_type is_file_path source_string entry_point targets...) #[proc_macro] pub fn precompile(input: TokenStream) -> TokenStream { - let args = parse_macro_input!(input as MacroArgs); + let args = parse_macro_input!(input as PrecompileArgs); let module = match args.source_type { SourceType::Spirv => { let source = args.source.clone().expect_bytes(); @@ -229,7 +368,7 @@ pub fn precompile(input: TokenStream) -> TokenStream { #wgpu_path::__macro_helpers::None }; - #[cfg(feature = "spv")] + #[cfg(feature = "spirv")] let spirv_tokens = if args.target_enabled(CompileTarget::Spirv) { use naga::back::spv; // Ripped from wgpu-hal backend @@ -256,15 +395,21 @@ pub fn precompile(input: TokenStream) -> TokenStream { }), ) .expect("Naga failed to write SPIR-V code"); + let guard = generate_conditional_guard(CompileTarget::Spirv); quote! { - #wgpu_path::__macro_helpers::Some(#wgpu_path::SpirvPassthroughDescriptor { + #[cfg(#guard)] + spirv: #wgpu_path::__macro_helpers::Some(#wgpu_path::SpirvPassthroughDescriptor { code: #wgpu_path::__macro_helpers::Cow::Borrowed(&[#(#spirv_data),*]), - }) + }), + #[cfg(not(#guard))] + spirv: #none_tokens, } } else { - none_tokens.clone() + quote! { + spirv: #none_tokens, + } }; - #[cfg(not(feature = "spv"))] + #[cfg(not(feature = "spirv"))] let spirv_tokens = none_tokens.clone(); #[cfg(feature = "msl")] @@ -280,14 +425,20 @@ pub fn precompile(input: TokenStream) -> TokenStream { ) .expect("Naga failed to write MSL code"); let entry_point = translation_info.entry_point_names[0].as_ref().unwrap(); + let guard = generate_conditional_guard(CompileTarget::Msl); quote! { - #wgpu_path::__macro_helpers::Some(#wgpu_path::MslPassthroughDescriptor { + #[cfg(#guard)] + msl: #wgpu_path::__macro_helpers::Some(#wgpu_path::MslPassthroughDescriptor { code: #wgpu_path::__macro_helpers::Cow::Borrowed(#msl_str), entry_point: #wgpu_path::__macro_helpers::ToString::to_string(#entry_point), - }) + }), + #[cfg(not(#guard))] + msl: #none_tokens, } } else { - none_tokens.clone() + quote! { + msl: #none_tokens, + } }; #[cfg(not(feature = "msl"))] let msl_tokens = none_tokens.clone(); @@ -313,47 +464,46 @@ pub fn precompile(input: TokenStream) -> TokenStream { #[cfg(feature = "hlsl")] let hlsl_tokens = if args.target_enabled(CompileTarget::Hlsl) { + let guard = generate_conditional_guard(CompileTarget::Hlsl); quote! { - #wgpu_path::__macro_helpers::Some(#wgpu_path::HlslPassthroughDescriptor { + #[cfg(#guard)] + hlsl: #wgpu_path::__macro_helpers::Some(#wgpu_path::HlslPassthroughDescriptor { code: #wgpu_path::__macro_helpers::Cow::Borrowed(#hlsl_str), entry_point: #wgpu_path::__macro_helpers::ToString::to_string(#hlsl_entry_point), - }) + }), + #[cfg(not(#guard))] + hlsl: #none_tokens, } } else { - none_tokens.clone() + quote! { + hlsl: #none_tokens, + } }; #[cfg(not(feature = "hlsl"))] let hlsl_tokens = none_tokens.clone(); #[cfg(feature = "hlsl")] let dxil_tokens = if args.target_enabled(CompileTarget::Dxil) { - let target_profile = match shader_stage { - naga::ShaderStage::Vertex => "vs_5_1", - naga::ShaderStage::Fragment => "ps_5_1", - naga::ShaderStage::Compute => "cs_5_1", - naga::ShaderStage::Task => "as_5_1", - naga::ShaderStage::Mesh => "ms_5_1", + let source_name = match &args.file_name { + Some(f) => f, + None => "precompile-inline.hlsl", }; - let dxil = hassle_rs::compile_hlsl( - match &args.file_name { - Some(f) => f, - None => "precompile-inline.hlsl", - }, - &hlsl_str, - &hlsl_entry_point, - target_profile, - &["-spirv"], - &[], - ) - .expect("Hassle failed to compile HLSL to DXIL"); + let shader_stage = shader_stage_to_string(shader_stage); + let guard = generate_conditional_guard(CompileTarget::Dxil); quote! { - #wgpu_path::__macro_helpers::Some(#wgpu_path::DxilPassthroughDescriptor { - code: #wgpu_path::__macro_helpers::Cow::Borrowed(&[#(#dxil),*]), + #[cfg(#guard)] + dxil: #wgpu_path::__macro_helpers::Some(#wgpu_path::DxilPassthroughDescriptor { + // HLSL, entry, shader stage, filename + code: #wgpu_path::__macro_helpers::Cow::Borrowed(#wgpu_path::__macro_helpers::precompile_hlsl_to_dxil!(#hlsl_str #hlsl_entry_point #shader_stage #source_name)), entry_point: #wgpu_path::__macro_helpers::ToString::to_string(#hlsl_entry_point), - }) + }), + #[cfg(not(#guard))] + dxil: #none_tokens, } } else { - none_tokens.clone() + quote! { + dxil: #none_tokens, + } }; #[cfg(not(feature = "hlsl"))] let dxil_tokens = none_tokens.clone(); @@ -368,15 +518,20 @@ pub fn precompile(input: TokenStream) -> TokenStream { let wgsl_str = writer.finish(); // TODO: ensure that the entry point here is sensible let entry_point = &args.entry_point; + let guard = generate_conditional_guard(CompileTarget::Wgsl); quote! { - #wgpu_path::__macro_helpers::Some(#wgpu_path::WgslPassthroughDescriptor { + #[cfg(#guard)] + wgsl: #wgpu_path::__macro_helpers::Some(#wgpu_path::WgslPassthroughDescriptor { code: #wgpu_path::__macro_helpers::Cow::Borrowed(#wgsl_str), entry_point: #wgpu_path::__macro_helpers::ToString::to_string(#entry_point), - }) + }), + #[cfg(not(#guard))] + wgsl: #none_tokens, + } } else { quote! { - #wgpu_path::__macro_helpers::None + wgsl: #none_tokens, } }; #[cfg(not(feature = "wgsl"))] @@ -400,14 +555,18 @@ pub fn precompile(input: TokenStream) -> TokenStream { .expect("Naga failed to create GLSL writer") .write() .expect("Naga failed write GLSL code"); + let guard = generate_conditional_guard(CompileTarget::Glsl); quote! { - #wgpu_path::__macro_helpers::Some(#wgpu_path::GlslPassthroughDescriptor { + #[cfg(#guard)] + glsl: #wgpu_path::__macro_helpers::Some(#wgpu_path::GlslPassthroughDescriptor { code: #wgpu_path::__macro_helpers::Cow::Borrowed(#glsl_str), - }) + }), + #[cfg(not(#guard))] + glsl: #none_tokens, } } else { quote! { - #wgpu_path::__macro_helpers::None + glsl: #none_tokens, } }; #[cfg(not(feature = "glsl"))] @@ -418,19 +577,20 @@ pub fn precompile(input: TokenStream) -> TokenStream { None => quote! {#wgpu_path::__macro_helpers::None}, }; - quote! { + let f = quote! { #wgpu_path::ShaderModuleDescriptorPassthrough { // TODO: make this something else when file name is provided label: #label_tokens, num_workgroups: (#x, #y, #z), runtime_checks: #wgpu_path::ShaderRuntimeChecks::default(), - spirv: #spirv_tokens, - dxil: #dxil_tokens, - msl: #msl_tokens, - hlsl: #hlsl_tokens, - glsl: #glsl_tokens, - wgsl: #wgsl_tokens, + #spirv_tokens + #dxil_tokens + #msl_tokens + #hlsl_tokens + #glsl_tokens + #wgsl_tokens } - } - .into() + }; + //panic!("FInal tokenstream: {}", f); + f.into() } diff --git a/wgpu/Cargo.toml b/wgpu/Cargo.toml index b6601849ec3..4ce2ba9a270 100644 --- a/wgpu/Cargo.toml +++ b/wgpu/Cargo.toml @@ -44,20 +44,20 @@ default = [ # -------------------------------------------------------------------- ## Enables the DX12 backend on Windows. -dx12 = ["wgpu-core?/dx12", "wgpu-precompile-macro/hlsl"] +dx12 = ["wgpu-core?/dx12", "wgpu-precompile-macro/dx12"] ## Enables the Metal backend on macOS & iOS. -metal = ["wgpu-core?/metal", "wgpu-precompile-macro/msl"] +metal = ["wgpu-core?/metal", "wgpu-precompile-macro/metal"] ## Enables the Vulkan backend on Windows, Linux, and Android. -vulkan = ["wgpu-core?/vulkan", "wgpu-precompile-macro/spv"] +vulkan = ["wgpu-core?/vulkan", "wgpu-precompile-macro/vulkan"] ## Enables the OpenGL/GLES backend on Windows, Linux, Android, and Emscripten. -gles = ["wgpu-core?/gles", "wgpu-precompile-macro/glsl"] +gles = ["wgpu-core?/gles", "wgpu-precompile-macro/gles"] ## Enables the WebGPU backend on WebAssembly. webgpu = [ - "wgpu-precompile-macro/wgsl", + "wgpu-precompile-macro/webgpu", "web", "naga?/wgsl-out", "dep:wasm-bindgen-futures", @@ -73,13 +73,13 @@ webgpu = [ #! ### Conditional Backends ## Enables the GLES backend on macOS only for use with [ANGLE](https://github.com/google/angle). -angle = ["wgpu-core?/angle"] +angle = ["wgpu-core?/angle", "wgpu-precompile-macro/angle"] ## Enables the Vulkan backend on macOS & iOS only for use with [MoltenVK](https://github.com/KhronosGroup/MoltenVK). -vulkan-portability = ["wgpu-core?/vulkan-portability"] +vulkan-portability = ["wgpu-core?/vulkan-portability", "wgpu-precompile-macro/vulkan-portability"] ## Enables the GLES backend on WebAssembly only. -webgl = ["web", "wgpu-core/webgl", "dep:wgpu-hal", "dep:smallvec"] +webgl = ["web", "wgpu-core/webgl", "dep:wgpu-hal", "dep:smallvec", "wgpu-precompile-macro/webgl"] ## Enables the noop backend for testing. ## diff --git a/wgpu/src/macros.rs b/wgpu/src/macros.rs index 7c22d2f50d3..355b56c2af5 100644 --- a/wgpu/src/macros.rs +++ b/wgpu/src/macros.rs @@ -345,7 +345,7 @@ pub mod helpers { pub use alloc::string::{String, ToString}; pub use core::{include_bytes, include_str}; #[cfg(feature = "precompile")] - pub use wgpu_precompile_macro::precompile; + pub use wgpu_precompile_macro::{precompile, precompile_hlsl_to_dxil}; pub use None; pub use Some; } From ed9934b2f3008be83f533033ab5df443bc0fde3e Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Mon, 15 Sep 2025 13:37:43 -0500 Subject: [PATCH 24/47] Reformatted cargo.toml --- wgpu/Cargo.toml | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/wgpu/Cargo.toml b/wgpu/Cargo.toml index 4ce2ba9a270..32f954b6a47 100644 --- a/wgpu/Cargo.toml +++ b/wgpu/Cargo.toml @@ -76,10 +76,19 @@ webgpu = [ angle = ["wgpu-core?/angle", "wgpu-precompile-macro/angle"] ## Enables the Vulkan backend on macOS & iOS only for use with [MoltenVK](https://github.com/KhronosGroup/MoltenVK). -vulkan-portability = ["wgpu-core?/vulkan-portability", "wgpu-precompile-macro/vulkan-portability"] +vulkan-portability = [ + "wgpu-core?/vulkan-portability", + "wgpu-precompile-macro/vulkan-portability", +] ## Enables the GLES backend on WebAssembly only. -webgl = ["web", "wgpu-core/webgl", "dep:wgpu-hal", "dep:smallvec", "wgpu-precompile-macro/webgl"] +webgl = [ + "web", + "wgpu-core/webgl", + "dep:wgpu-hal", + "dep:smallvec", + "wgpu-precompile-macro/webgl", +] ## Enables the noop backend for testing. ## From be1504d88faada9987665aa297ad0a595802e82b Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Mon, 15 Sep 2025 13:49:42 -0500 Subject: [PATCH 25/47] Tried updating syn and quote to see if it fixes anything --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5a79af7da36..9a0eab7ac45 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -170,7 +170,7 @@ portable-atomic = "1.8" portable-atomic-util = "0.2.4" pp-rs = "0.2.1" profiling = { version = "1.0.1", default-features = false } -quote = "1.0.38" +quote = "1.0.40" raw-window-handle = { version = "0.6.2", default-features = false } rwh_05 = { version = "0.5.2", package = "raw-window-handle" } # temporary compatibility for glutin-winit rayon = "1.3" @@ -189,7 +189,7 @@ smallvec = "1.13.1" spirv = "0.3" static_assertions = "1.1" strum = { version = "0.27", default-features = false, features = ["derive"] } -syn = "2.0.98" +syn = "2.0.106" toml = "0.9.0" trybuild = "1" tracy-client = "0.18" From b66e9917dba38167c291bf9866a071b0688c5a3b Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Mon, 15 Sep 2025 14:21:54 -0500 Subject: [PATCH 26/47] Attempted to fix windows compilation --- Cargo.lock | 147 +++++++++---------------------- wgpu-precompile-macro/Cargo.toml | 6 +- wgpu-precompile-macro/src/lib.rs | 53 +++++++---- 3 files changed, 78 insertions(+), 128 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dc087a886f2..dea545df448 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -224,7 +224,7 @@ dependencies = [ "argh_shared", "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -274,7 +274,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -381,7 +381,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.106", + "syn", ] [[package]] @@ -499,7 +499,7 @@ checksum = "4f154e572231cb6ba2bd1176980827e3d5dc04cc183a75dea38109fbdd672d29" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -601,7 +601,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b4a6cae9efc04cc6cbb8faf338d2c497c165c83e74509cf4dbedea948bbf6e5" dependencies = [ "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -773,7 +773,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -799,37 +799,6 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" -[[package]] -name = "com" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e17887fd17353b65b1b2ef1c526c83e26cd72e74f598a8dc1bee13a48f3d9f6" -dependencies = [ - "com_macros", -] - -[[package]] -name = "com_macros" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d375883580a668c7481ea6631fc1a8863e33cc335bf56bfad8d7e6d4b04b13a5" -dependencies = [ - "com_macros_support", - "proc-macro2", - "syn 1.0.109", -] - -[[package]] -name = "com_macros_support" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad899a1087a9296d5644792d7cb72b8e34c1bec8e7d4fbc002230169a6e8710c" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "combine" version = "4.6.7" @@ -1160,7 +1129,7 @@ checksum = "babccedee31ce7e57c3e6dff2cb3ab8d68c49d0df8222fe0d11d628e65192790" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -1176,7 +1145,7 @@ dependencies = [ "stringcase", "strum 0.25.0", "strum_macros 0.25.3", - "syn 2.0.106", + "syn", "thiserror 2.0.16", ] @@ -1303,7 +1272,7 @@ checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -1326,7 +1295,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -1409,7 +1378,7 @@ checksum = "1796db3d892515842ca2dfb11124c4bb4a9e58d9f2c5c1072e5bca1b2334507b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -1576,7 +1545,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -1685,7 +1654,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -1962,21 +1931,6 @@ dependencies = [ "serde", ] -[[package]] -name = "hassle-rs" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d6af3d4e299d56c5c738c71de7e7c7d2c60051df4bad2cecdb2e05cd679b269" -dependencies = [ - "bitflags 2.9.4", - "com", - "libc", - "libloading", - "thiserror 1.0.69", - "widestring", - "winapi", -] - [[package]] name = "heck" version = "0.4.1" @@ -2242,7 +2196,7 @@ checksum = "03343451ff899767262ec32146f6d559dd759fdadf42ff0e227c7c48f72594b4" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -2826,7 +2780,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -3244,7 +3198,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -3352,7 +3306,7 @@ checksum = "ac5da421106a50887c5b51d20806867db377fbb86bacf478ee0500a912e0c113" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -3401,7 +3355,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn 2.0.106", + "syn", ] [[package]] @@ -3421,7 +3375,7 @@ checksum = "07c277e4e643ef00c1233393c673f655e3672cf7eb3ba08a00bdd0ea59139b5f" dependencies = [ "proc-macro-rules-macros", "proc-macro2", - "syn 2.0.106", + "syn", ] [[package]] @@ -3433,7 +3387,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -3817,7 +3771,7 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -4096,7 +4050,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.106", + "syn", ] [[package]] @@ -4108,18 +4062,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.106", -] - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", + "syn", ] [[package]] @@ -4141,7 +4084,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -4161,7 +4104,7 @@ checksum = "181f22127402abcf8ee5c83ccd5b408933fec36a6095cf82cda545634692657e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -4211,7 +4154,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -4222,7 +4165,7 @@ checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -4317,7 +4260,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -4723,7 +4666,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.106", + "syn", "wasm-bindgen-shared", ] @@ -4758,7 +4701,7 @@ checksum = "7bb4ce89b08211f923caf51d527662b75bdc9c9c7aab40f86dcb9fb85ac552aa" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -4793,7 +4736,7 @@ checksum = "c5ada2ab788d46d4bda04c9d567702a79c8ced14f51f221646a16ed39d0e6a5d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -5225,7 +5168,7 @@ version = "26.0.0" dependencies = [ "heck 0.5.0", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -5233,11 +5176,11 @@ name = "wgpu-precompile-macro" version = "26.0.0" dependencies = [ "hashbrown", - "hassle-rs", "naga", + "nanorand 0.8.0", "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -5330,12 +5273,6 @@ dependencies = [ "winsafe", ] -[[package]] -name = "widestring" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd7cf3379ca1aac9eea11fba24fd7e315d621f8dfe35c8d7d2be8b793726e07d" - [[package]] name = "winapi" version = "0.3.9" @@ -5444,7 +5381,7 @@ checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -5455,7 +5392,7 @@ checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -5466,7 +5403,7 @@ checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -5477,7 +5414,7 @@ checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -6080,7 +6017,7 @@ checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", "synstructure", ] @@ -6101,7 +6038,7 @@ checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -6121,7 +6058,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", "synstructure", ] @@ -6155,5 +6092,5 @@ checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] diff --git a/wgpu-precompile-macro/Cargo.toml b/wgpu-precompile-macro/Cargo.toml index 152bc4cf4c5..4635cfac2ce 100644 --- a/wgpu-precompile-macro/Cargo.toml +++ b/wgpu-precompile-macro/Cargo.toml @@ -31,14 +31,12 @@ spirv = ["naga/spv-out"] msl = ["naga/msl-out"] wgsl = ["naga/wgsl-out"] glsl = ["naga/glsl-out"] -hlsl = ["naga/hlsl-out", "dep:hassle-rs"] +hlsl = ["naga/hlsl-out"] [dependencies] naga = { workspace = true, features = ["spv-in", "wgsl-in", "glsl-in"] } -# TODO: verify that this dependency should be allowed -hassle-rs = { version = "0.12", optional = true } - +nanorand = { workspace = true, features = ["tls"] } quote.workspace = true syn = { workspace = true, features = ["full"] } proc-macro2 = "1.0" diff --git a/wgpu-precompile-macro/src/lib.rs b/wgpu-precompile-macro/src/lib.rs index ae8a308475d..bc88d8d3c56 100644 --- a/wgpu-precompile-macro/src/lib.rs +++ b/wgpu-precompile-macro/src/lib.rs @@ -1,7 +1,8 @@ use hashbrown::HashSet; +use nanorand::Rng; use proc_macro::TokenStream; use quote::quote; -use std::path::PathBuf; +use std::{io::Write, path::PathBuf, process::Stdio}; use syn::{parse::Parse, parse_macro_input, Ident, Path}; #[derive(PartialEq, Eq)] @@ -179,20 +180,16 @@ struct PrecompileDxilArgs { hlsl_code: String, entry_point: String, shader_stage: naga::ShaderStage, - /// For debug reasons - source_file_name: String, } impl Parse for PrecompileDxilArgs { fn parse(input: syn::parse::ParseStream) -> syn::Result { let hlsl_code = input.parse::()?.value(); let entry_point = input.parse::()?.value(); let shader_stage = parse_shader_stage(&input.parse::()?.value()).unwrap(); - let source_file_name = input.parse::()?.value(); Ok(Self { hlsl_code, entry_point, shader_stage, - source_file_name, }) } } @@ -210,15 +207,37 @@ pub fn precompile_hlsl_to_dxil(input: TokenStream) -> TokenStream { naga::ShaderStage::Task => "as_5_1", naga::ShaderStage::Mesh => "ms_5_1", }; - let dxil = hassle_rs::compile_hlsl( - &args.source_file_name, - &args.hlsl_code, - &args.entry_point, - target_profile, - &["-spirv"], - &[], - ) - .expect("Hassle failed to compile HLSL to DXIL"); + + let temporary_folder_location = + std::env::temp_dir().join(nanorand::WyRand::new().generate::().to_string()); + std::fs::create_dir(&temporary_folder_location).expect("Failed to create temporary directory"); + + let temporary_file_location = temporary_folder_location.join("file.dxil"); + let mut cmd = std::process::Command::new("dxc") + .args([ + "-T", + target_profile, + "-E", + &args.entry_point, + "-", + "-Fo", + &temporary_file_location.display().to_string(), + ]) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .spawn() + .expect("Failed to spawn DXC"); + let mut stdin = cmd.stdin.take().unwrap(); + stdin + .write_all(args.hlsl_code.as_bytes()) + .expect("Failed to write to DXC stdin"); + let output = cmd.wait_with_output().expect("DXC failed to wait"); + if !output.status.success() { + panic!("DXC failed:\n{}", String::from_utf8(output.stderr).unwrap()); + } + let dxil = std::fs::read(temporary_file_location).expect("Failed to read DXC output file"); + std::fs::remove_dir_all(temporary_folder_location) + .expect("Failed to remove temporary directory"); quote! { &[#(#dxil),*] } @@ -484,17 +503,13 @@ pub fn precompile(input: TokenStream) -> TokenStream { #[cfg(feature = "hlsl")] let dxil_tokens = if args.target_enabled(CompileTarget::Dxil) { - let source_name = match &args.file_name { - Some(f) => f, - None => "precompile-inline.hlsl", - }; let shader_stage = shader_stage_to_string(shader_stage); let guard = generate_conditional_guard(CompileTarget::Dxil); quote! { #[cfg(#guard)] dxil: #wgpu_path::__macro_helpers::Some(#wgpu_path::DxilPassthroughDescriptor { // HLSL, entry, shader stage, filename - code: #wgpu_path::__macro_helpers::Cow::Borrowed(#wgpu_path::__macro_helpers::precompile_hlsl_to_dxil!(#hlsl_str #hlsl_entry_point #shader_stage #source_name)), + code: #wgpu_path::__macro_helpers::Cow::Borrowed(#wgpu_path::__macro_helpers::precompile_hlsl_to_dxil!(#hlsl_str #hlsl_entry_point #shader_stage)), entry_point: #wgpu_path::__macro_helpers::ToString::to_string(#hlsl_entry_point), }), #[cfg(not(#guard))] From 5c030323b8281e2292420ff21e826e056f6e8619 Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Mon, 15 Sep 2025 14:35:17 -0500 Subject: [PATCH 27/47] This time I think DXC compilation should work, as I actually tested it --- Cargo.lock | 2 +- wgpu-precompile-macro/Cargo.toml | 3 ++- wgpu-precompile-macro/src/lib.rs | 28 +++++++++++++++------------- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dea545df448..c8085d8620d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5175,9 +5175,9 @@ dependencies = [ name = "wgpu-precompile-macro" version = "26.0.0" dependencies = [ + "getrandom 0.3.3", "hashbrown", "naga", - "nanorand 0.8.0", "proc-macro2", "quote", "syn", diff --git a/wgpu-precompile-macro/Cargo.toml b/wgpu-precompile-macro/Cargo.toml index 4635cfac2ce..c6f0886c8af 100644 --- a/wgpu-precompile-macro/Cargo.toml +++ b/wgpu-precompile-macro/Cargo.toml @@ -36,7 +36,8 @@ hlsl = ["naga/hlsl-out"] [dependencies] naga = { workspace = true, features = ["spv-in", "wgsl-in", "glsl-in"] } -nanorand = { workspace = true, features = ["tls"] } +# This is so we can have each DXIL generation path have a unique file/folder +getrandom = "0.3" quote.workspace = true syn = { workspace = true, features = ["full"] } proc-macro2 = "1.0" diff --git a/wgpu-precompile-macro/src/lib.rs b/wgpu-precompile-macro/src/lib.rs index bc88d8d3c56..24a5bdcbe97 100644 --- a/wgpu-precompile-macro/src/lib.rs +++ b/wgpu-precompile-macro/src/lib.rs @@ -1,8 +1,7 @@ use hashbrown::HashSet; -use nanorand::Rng; use proc_macro::TokenStream; use quote::quote; -use std::{io::Write, path::PathBuf, process::Stdio}; +use std::{path::PathBuf, process::Stdio}; use syn::{parse::Parse, parse_macro_input, Ident, Path}; #[derive(PartialEq, Eq)] @@ -208,30 +207,33 @@ pub fn precompile_hlsl_to_dxil(input: TokenStream) -> TokenStream { naga::ShaderStage::Mesh => "ms_5_1", }; - let temporary_folder_location = - std::env::temp_dir().join(nanorand::WyRand::new().generate::().to_string()); + let temporary_folder_location = std::env::temp_dir().join( + getrandom::u64() + .expect("Failed to generate random u64") + .to_string(), + ); std::fs::create_dir(&temporary_folder_location).expect("Failed to create temporary directory"); + // The naming matters for DXIL debug info. We don't want to give it a name that seems like something the + // user might've specified, as that could cause confusion. + let input_file = temporary_folder_location.join("__wgpu_inline.hlsl"); + std::fs::write(&input_file, args.hlsl_code.as_bytes()) + .expect("Failed to write to HLSL input file"); let temporary_file_location = temporary_folder_location.join("file.dxil"); - let mut cmd = std::process::Command::new("dxc") + let output = std::process::Command::new("dxc") .args([ "-T", target_profile, "-E", &args.entry_point, - "-", + &input_file.display().to_string(), "-Fo", &temporary_file_location.display().to_string(), ]) .stdin(Stdio::piped()) .stdout(Stdio::piped()) - .spawn() + .output() .expect("Failed to spawn DXC"); - let mut stdin = cmd.stdin.take().unwrap(); - stdin - .write_all(args.hlsl_code.as_bytes()) - .expect("Failed to write to DXC stdin"); - let output = cmd.wait_with_output().expect("DXC failed to wait"); if !output.status.success() { panic!("DXC failed:\n{}", String::from_utf8(output.stderr).unwrap()); } @@ -316,7 +318,7 @@ fn generate_conditional_guard(target: CompileTarget) -> proc_macro2::TokenStream windows } #[cfg(not(feature = "dx12"))] - always_false + always_fals } CompileTarget::AllSupported => unreachable!(), } From 6b345064ec7ce5ba9ae189801a68dc729fae9540 Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Mon, 15 Sep 2025 14:37:17 -0500 Subject: [PATCH 28/47] Fixed stupid typo that would've broken it --- wgpu-precompile-macro/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wgpu-precompile-macro/src/lib.rs b/wgpu-precompile-macro/src/lib.rs index 24a5bdcbe97..9585f8dc620 100644 --- a/wgpu-precompile-macro/src/lib.rs +++ b/wgpu-precompile-macro/src/lib.rs @@ -318,7 +318,7 @@ fn generate_conditional_guard(target: CompileTarget) -> proc_macro2::TokenStream windows } #[cfg(not(feature = "dx12"))] - always_fals + always_false } CompileTarget::AllSupported => unreachable!(), } From 0915522f64a8438e38ac78511f0e26dc0f023108 Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Mon, 15 Sep 2025 14:40:03 -0500 Subject: [PATCH 29/47] Added typo to ignore list --- typos.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/typos.toml b/typos.toml index d93e1f8ecdc..be7f938e074 100644 --- a/typos.toml +++ b/typos.toml @@ -20,6 +20,9 @@ extend-exclude = [ lod = "lod" metalness = "metalness" +# A DXC command line argument +fo = "Fo" + # Usernames Healthire = "Healthire" REASY = "REASY" From fdfa22191566275c3e9606ecdcf85d7f369d7043 Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Mon, 15 Sep 2025 14:44:48 -0500 Subject: [PATCH 30/47] Updated Fo typo thing --- typos.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/typos.toml b/typos.toml index be7f938e074..e22f007ac66 100644 --- a/typos.toml +++ b/typos.toml @@ -21,7 +21,7 @@ lod = "lod" metalness = "metalness" # A DXC command line argument -fo = "Fo" +-Fo = "-Fo" # Usernames Healthire = "Healthire" From cf98e445ce79dc4ce0b3f2d81644455cddee98a9 Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Mon, 15 Sep 2025 14:47:43 -0500 Subject: [PATCH 31/47] Actually fixed typos this time --- typos.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/typos.toml b/typos.toml index e22f007ac66..f5efa9df29f 100644 --- a/typos.toml +++ b/typos.toml @@ -21,7 +21,7 @@ lod = "lod" metalness = "metalness" # A DXC command line argument --Fo = "-Fo" +Fo = "Fo" # Usernames Healthire = "Healthire" From 507f3d401f055f63a2eebe94a45a136860391689 Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Mon, 15 Sep 2025 14:52:07 -0500 Subject: [PATCH 32/47] Undid bump to syn/quote that didn't fix MSRV test --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9a0eab7ac45..5a79af7da36 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -170,7 +170,7 @@ portable-atomic = "1.8" portable-atomic-util = "0.2.4" pp-rs = "0.2.1" profiling = { version = "1.0.1", default-features = false } -quote = "1.0.40" +quote = "1.0.38" raw-window-handle = { version = "0.6.2", default-features = false } rwh_05 = { version = "0.5.2", package = "raw-window-handle" } # temporary compatibility for glutin-winit rayon = "1.3" @@ -189,7 +189,7 @@ smallvec = "1.13.1" spirv = "0.3" static_assertions = "1.1" strum = { version = "0.27", default-features = false, features = ["derive"] } -syn = "2.0.106" +syn = "2.0.98" toml = "0.9.0" trybuild = "1" tracy-client = "0.18" From d95471491591ef4861aabc1ff2e1286a1596e5af Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Mon, 15 Sep 2025 15:01:48 -0500 Subject: [PATCH 33/47] Tried to fix MSRV test --- Cargo.toml | 1 + wgpu-precompile-macro/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 5a79af7da36..583694d4a84 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -169,6 +169,7 @@ pollster = "0.4" portable-atomic = "1.8" portable-atomic-util = "0.2.4" pp-rs = "0.2.1" +proc-macro2 = "1.0.101" profiling = { version = "1.0.1", default-features = false } quote = "1.0.38" raw-window-handle = { version = "0.6.2", default-features = false } diff --git a/wgpu-precompile-macro/Cargo.toml b/wgpu-precompile-macro/Cargo.toml index c6f0886c8af..5a0a756aedf 100644 --- a/wgpu-precompile-macro/Cargo.toml +++ b/wgpu-precompile-macro/Cargo.toml @@ -40,5 +40,5 @@ naga = { workspace = true, features = ["spv-in", "wgsl-in", "glsl-in"] } getrandom = "0.3" quote.workspace = true syn = { workspace = true, features = ["full"] } -proc-macro2 = "1.0" +proc-macro2.workspace = true hashbrown.workspace = true From f58671069e5b2f852587de5be1da106560ef3616 Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Mon, 15 Sep 2025 15:05:42 -0500 Subject: [PATCH 34/47] Removed dxil compilation from example so that clippy won't complain when DXC isn't present --- examples/features/src/hello_triangle/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/features/src/hello_triangle/mod.rs b/examples/features/src/hello_triangle/mod.rs index 1558e348608..a18616c53ab 100644 --- a/examples/features/src/hello_triangle/mod.rs +++ b/examples/features/src/hello_triangle/mod.rs @@ -49,14 +49,14 @@ async fn run(event_loop: EventLoop<()>, window: Window) { device.create_shader_module_passthrough(wgpu::include_precompiled_wgsl!( "src/hello_triangle/shader.wgsl", "vs_main", - all dxil + all )) }; let fs_shader = unsafe { device.create_shader_module_passthrough(wgpu::include_precompiled_wgsl!( "src/hello_triangle/shader.wgsl", "fs_main", - all dxil + all )) }; From 10f31e09b9d06cccb881a903bea212af48296c5c Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Mon, 15 Sep 2025 15:13:13 -0500 Subject: [PATCH 35/47] Tried to make clippy happy --- tests/tests/wgpu-gpu/precompile/mod.rs | 48 ++++++++++++++------------ 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/tests/tests/wgpu-gpu/precompile/mod.rs b/tests/tests/wgpu-gpu/precompile/mod.rs index 6e63e841a8d..5c3fe17d5dd 100644 --- a/tests/tests/wgpu-gpu/precompile/mod.rs +++ b/tests/tests/wgpu-gpu/precompile/mod.rs @@ -3,34 +3,38 @@ use wgpu_test::{gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParamete pub fn all_tests(vec: &mut Vec) { vec.push(PRECOMPILE_ALL_STAGES_TEST); } - #[gpu_test] static PRECOMPILE_ALL_STAGES_TEST: GpuTestConfiguration = GpuTestConfiguration::new() .parameters( TestParameters::default().features(wgpu::Features::EXPERIMENTAL_PASSTHROUGH_SHADERS), ) .run_async(async |ctx| unsafe { - let _ = ctx - .device - .create_shader_module_passthrough(wgpu::include_precompiled_wgsl!( - "tests/wgpu-gpu/precompile/shader.wgsl", - "vs_main", - all dxil - )); - let _ = ctx - .device - .create_shader_module_passthrough(wgpu::include_precompiled_wgsl!( - "tests/wgpu-gpu/precompile/shader.wgsl", - "fs_main", - all dxil - )); - let _ = ctx - .device - .create_shader_module_passthrough(wgpu::include_precompiled_wgsl!( - "tests/wgpu-gpu/precompile/shader.wgsl", - "cs_main", - glsl spirv wgsl hlsl msl dxil - )); + // Don't let clippy see the ones that compile to DXIL, as the CI might not have access to DXC when not actually running tests + // Preferably, we'd guard this behind running tests, as `cargo check` will still fail. + #[cfg(not(clippy))] + { + let _ = ctx + .device + .create_shader_module_passthrough(wgpu::include_precompiled_wgsl!( + "tests/wgpu-gpu/precompile/shader.wgsl", + "vs_main", + all dxil + )); + let _ = ctx + .device + .create_shader_module_passthrough(wgpu::include_precompiled_wgsl!( + "tests/wgpu-gpu/precompile/shader.wgsl", + "fs_main", + all dxil + )); + let _ = ctx + .device + .create_shader_module_passthrough(wgpu::include_precompiled_wgsl!( + "tests/wgpu-gpu/precompile/shader.wgsl", + "cs_main", + glsl spirv wgsl hlsl msl dxil + )); + } let _ = ctx .device .create_shader_module_passthrough(wgpu::precompile_wgsl!( From 9281184483f9c751f284789ca2942af6113874d5 Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Mon, 15 Sep 2025 17:39:24 -0500 Subject: [PATCH 36/47] Fixed dependency tests --- tests/tests/wgpu-dependency/main.rs | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/tests/tests/wgpu-dependency/main.rs b/tests/tests/wgpu-dependency/main.rs index ac96afdb0e1..a604e55d6ba 100644 --- a/tests/tests/wgpu-dependency/main.rs +++ b/tests/tests/wgpu-dependency/main.rs @@ -18,19 +18,25 @@ struct Requirement<'a> { features: &'a [&'a str], default_features: bool, search_terms: &'a [Search<'a>], + prune: &'a [&'a str], } fn check_feature_dependency(requirement: Requirement) { println!("Checking: {}", requirement.human_readable_name); let mut args = Vec::new(); - args.extend(["tree", "--target", requirement.target]); + args.extend(["tree", "-e", "normal", "--target", requirement.target]); for package in requirement.packages { args.push("--package"); args.push(package); } + for prune in requirement.prune { + args.push("--prune"); + args.push(prune); + } + if !requirement.default_features { args.push("--no-default-features"); } @@ -116,6 +122,7 @@ fn wasm32_without_webgl_or_noop_does_not_depend_on_wgpu_core() { features: &features_no_webgl, default_features: false, search_terms: &[Search::Negative("wgpu-core")], + prune: &[], }); } @@ -128,6 +135,7 @@ fn wasm32_with_webgpu_and_wgsl_does_not_depend_on_naga() { features: &["webgpu", "wgsl"], default_features: false, search_terms: &[Search::Negative("naga")], + prune: &["wgpu-precompile-macro"], }); } @@ -140,6 +148,7 @@ fn wasm32_with_webgl_depends_on_glow() { features: &["webgl"], default_features: false, search_terms: &[Search::Positive("glow")], + prune: &[], }); } @@ -152,6 +161,7 @@ fn wasm32_with_only_custom_backend_does_not_depend_on_web_specifics() { features: &["custom"], default_features: false, search_terms: &[Search::Negative("wasm-bindgen"), Search::Negative("js-sys"), Search::Negative("web-sys")], + prune: &[], }); } @@ -164,6 +174,7 @@ fn wasm32_with_webgpu_backend_does_depend_on_web_specifics() { features: &["webgpu"], default_features: false, search_terms: &[Search::Positive("wasm-bindgen"), Search::Positive("js-sys"), Search::Positive("web-sys")], + prune: &[], }); } @@ -176,6 +187,7 @@ fn wasm32_with_webgl_backend_does_depend_on_web_specifics() { features: &["webgl"], default_features: false, search_terms: &[Search::Positive("wasm-bindgen"), Search::Positive("js-sys"), Search::Positive("web-sys")], + prune: &[], }); } @@ -188,6 +200,7 @@ fn windows_with_webgpu_webgl_backend_does_not_depend_on_web_specifics() { features: &["webgpu", "webgl"], default_features: false, search_terms: &[Search::Negative("wasm-bindgen"), Search::Negative("js-sys"), Search::Negative("web-sys")], + prune: &[], }); } @@ -200,6 +213,7 @@ fn windows_with_webgl_does_not_depend_on_glow() { features: &["webgl"], default_features: false, search_terms: &[Search::Negative("glow")], + prune: &[], }); } @@ -212,6 +226,7 @@ fn apple_with_vulkan_does_not_depend_on_ash() { features: &["vulkan"], default_features: false, search_terms: &[Search::Negative("ash")], + prune: &[], }); } @@ -225,6 +240,7 @@ fn apple_with_vulkan_portability_depends_on_ash_and_renderdoc_sys() { features: &["vulkan-portability"], default_features: false, search_terms: &[Search::Positive("ash"), Search::Positive("renderdoc-sys")], + prune: &[], }); } @@ -237,6 +253,7 @@ fn apple_with_gles_does_not_depend_on_glow() { features: &["gles"], default_features: false, search_terms: &[Search::Negative("glow")], + prune: &[], }); } @@ -249,6 +266,7 @@ fn apple_with_angle_depends_on_glow_and_renderdoc_sys() { features: &["angle"], default_features: false, search_terms: &[Search::Positive("glow"), Search::Positive("renderdoc-sys")], + prune: &[], }); } @@ -261,6 +279,7 @@ fn apple_with_no_features_does_not_depend_on_renderdoc_sys() { features: &[], default_features: false, search_terms: &[Search::Negative("renderdoc-sys")], + prune: &[], }); } @@ -278,6 +297,7 @@ fn windows_with_no_features_does_not_depend_on_glow_windows_or_ash() { Search::Negative("windows"), Search::Negative("ash"), ], + prune: &[], }); } @@ -290,6 +310,7 @@ fn windows_with_no_features_depends_on_renderdoc_sys() { features: &[], default_features: false, search_terms: &[Search::Positive("renderdoc-sys")], + prune: &[], }); } @@ -302,6 +323,7 @@ fn emscripten_with_webgl_does_not_depend_on_glow() { features: &["webgl"], default_features: false, search_terms: &[Search::Negative("glow")], + prune: &[], }); } @@ -314,6 +336,7 @@ fn emscripten_with_gles_depends_on_glow() { features: &["gles"], default_features: false, search_terms: &[Search::Positive("glow")], + prune: &[], }); } @@ -326,6 +349,7 @@ fn x86_64_does_not_depend_on_portable_atomic() { features: &[], default_features: false, search_terms: &[Search::Negative("portable-atomic")], + prune: &[], }); } @@ -338,5 +362,6 @@ fn ppc32_does_depend_on_portable_atomic() { features: &[], default_features: false, search_terms: &[Search::Positive("portable-atomic")], + prune: &[], }); } From 9e41b079c5179f283da7365d7390758927dc72ea Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Sat, 20 Sep 2025 22:38:30 -0500 Subject: [PATCH 37/47] Updated deps to reflect #8246 --- wgpu-precompile-macro/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/wgpu-precompile-macro/src/lib.rs b/wgpu-precompile-macro/src/lib.rs index 9585f8dc620..3c45aca5839 100644 --- a/wgpu-precompile-macro/src/lib.rs +++ b/wgpu-precompile-macro/src/lib.rs @@ -282,7 +282,7 @@ fn generate_conditional_guard(target: CompileTarget) -> proc_macro2::TokenStream }; #[cfg(feature = "gles")] quote! { - any(target_os = "emscripten", windows, target_os = "linux", target_os = "android" #angle #webgl) + any(target_os = "emscripten", windows, target_os = "linux", target_os = "android", target_os = "freebsd" #angle #webgl) } #[cfg(not(feature = "gles"))] always_false @@ -291,11 +291,11 @@ fn generate_conditional_guard(target: CompileTarget) -> proc_macro2::TokenStream // Related features: vulkan, vulkan-portability #[cfg(all(feature = "vulkan", feature = "vulkan-portability"))] quote! { - any(target_vendor = "apple", windows, target_os = "linux", target_os = "android") + any(target_vendor = "apple", windows, target_os = "linux", target_os = "android", target_os = "freebsd") } #[cfg(all(feature = "vulkan", not(feature = "vulkan-portability")))] quote! { - any(windows, target_os = "linux", target_os = "android") + any(windows, target_os = "linux", target_os = "android", target_os = "freebsd") } #[cfg(not(feature = "vulkan"))] always_false From 113a75ee34b4e46d5ece12aeb000b8504bbcc59e Mon Sep 17 00:00:00 2001 From: Magnus <85136135+SupaMaggie70Incorporated@users.noreply.github.com> Date: Tue, 23 Sep 2025 23:44:19 -0500 Subject: [PATCH 38/47] Update wgpu-precompile-macro/src/lib.rs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- wgpu-precompile-macro/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wgpu-precompile-macro/src/lib.rs b/wgpu-precompile-macro/src/lib.rs index 3c45aca5839..3d685668827 100644 --- a/wgpu-precompile-macro/src/lib.rs +++ b/wgpu-precompile-macro/src/lib.rs @@ -608,6 +608,6 @@ pub fn precompile(input: TokenStream) -> TokenStream { #wgsl_tokens } }; - //panic!("FInal tokenstream: {}", f); + //panic!("Final tokenstream: {}", f); f.into() } From 6ea7ea7d747f6193c48839c33265e4b68a8cc225 Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Wed, 24 Sep 2025 22:12:00 -0500 Subject: [PATCH 39/47] I don't even understand what happened here --- wgpu-hal/src/auxil/dxgi/result.rs | 56 +++++++++++++++---------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/wgpu-hal/src/auxil/dxgi/result.rs b/wgpu-hal/src/auxil/dxgi/result.rs index f3d77e27be1..256e0827879 100644 --- a/wgpu-hal/src/auxil/dxgi/result.rs +++ b/wgpu-hal/src/auxil/dxgi/result.rs @@ -1,28 +1,28 @@ -use windows::Win32::{Foundation, Graphics::Dxgi}; - -pub(crate) trait HResult { - fn into_device_result(self, description: &str) -> Result; -} -impl HResult for windows::core::Result { - fn into_device_result(self, description: &str) -> Result { - #![allow(unreachable_code)] - - self.map_err(|err| { - log::error!("{description} failed: {err}"); - - match err.code() { - Foundation::E_OUTOFMEMORY => crate::DeviceError::OutOfMemory, - Dxgi::DXGI_ERROR_DEVICE_RESET | Dxgi::DXGI_ERROR_DEVICE_REMOVED => { - #[cfg(feature = "device_lost_panic")] - panic!("{description} failed: Device lost ({err})"); - crate::DeviceError::Lost - } - _ => { - #[cfg(feature = "internal_error_panic")] - panic!("{description} failed: {err}"); - crate::DeviceError::Unexpected - } - } - }) - } -} +use windows::Win32::{Foundation, Graphics::Dxgi}; + +pub(crate) trait HResult { + fn into_device_result(self, description: &str) -> Result; +} +impl HResult for windows::core::Result { + fn into_device_result(self, description: &str) -> Result { + #![allow(unreachable_code)] + + self.map_err(|err| { + log::error!("{description} failed: {err}"); + + match err.code() { + Foundation::E_OUTOFMEMORY => crate::DeviceError::OutOfMemory, + Dxgi::DXGI_ERROR_DEVICE_RESET | Dxgi::DXGI_ERROR_DEVICE_REMOVED => { + #[cfg(feature = "device_lost_panic")] + panic!("{description} failed: Device lost ({err})"); + crate::DeviceError::Lost + } + _ => { + #[cfg(feature = "internal_error_panic")] + panic!("{description} failed: {err}"); + crate::DeviceError::Unexpected + } + } + }) + } +} From a0f613b220161df066b7d4eda824e677529ad72b Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Wed, 24 Sep 2025 22:47:17 -0500 Subject: [PATCH 40/47] Now removes temporary folder regardless of crashing/etc --- wgpu-precompile-macro/src/lib.rs | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/wgpu-precompile-macro/src/lib.rs b/wgpu-precompile-macro/src/lib.rs index 3d685668827..0a006fd8fc1 100644 --- a/wgpu-precompile-macro/src/lib.rs +++ b/wgpu-precompile-macro/src/lib.rs @@ -193,6 +193,15 @@ impl Parse for PrecompileDxilArgs { } } +struct TempFolder { + path: PathBuf, +} +impl Drop for TempFolder { + fn drop(&mut self) { + std::fs::remove_dir_all(&self.path).expect("Failed to remove temporary folder"); + } +} + /// This is so we can conditionally hook into DXC depending on the target, which must be configured via #\[cfg] within the main program. /// Proc macros don't directly have access to the target configuration. Invoking naga unnecessarily shouldn't be *too* major, but /// compiling to macOS shouldn't require dxc be present. @@ -213,13 +222,17 @@ pub fn precompile_hlsl_to_dxil(input: TokenStream) -> TokenStream { .to_string(), ); std::fs::create_dir(&temporary_folder_location).expect("Failed to create temporary directory"); + // Drop guard essentially + let tempoary_folder = TempFolder { + path: temporary_folder_location, + }; // The naming matters for DXIL debug info. We don't want to give it a name that seems like something the // user might've specified, as that could cause confusion. - let input_file = temporary_folder_location.join("__wgpu_inline.hlsl"); + let input_file = tempoary_folder.path.join("__wgpu_inline.hlsl"); std::fs::write(&input_file, args.hlsl_code.as_bytes()) .expect("Failed to write to HLSL input file"); - let temporary_file_location = temporary_folder_location.join("file.dxil"); + let temporary_file_location = tempoary_folder.path.join("file.dxil"); let output = std::process::Command::new("dxc") .args([ "-T", @@ -238,8 +251,6 @@ pub fn precompile_hlsl_to_dxil(input: TokenStream) -> TokenStream { panic!("DXC failed:\n{}", String::from_utf8(output.stderr).unwrap()); } let dxil = std::fs::read(temporary_file_location).expect("Failed to read DXC output file"); - std::fs::remove_dir_all(temporary_folder_location) - .expect("Failed to remove temporary directory"); quote! { &[#(#dxil),*] } From c01e505d4e69547191ed2bb93590a86bb41f8ce3 Mon Sep 17 00:00:00 2001 From: Magnus <85136135+SupaMaggie70Incorporated@users.noreply.github.com> Date: Wed, 24 Sep 2025 23:01:36 -0500 Subject: [PATCH 41/47] Update wgpu-precompile-macro/src/lib.rs Co-authored-by: Connor Fitzgerald --- wgpu-precompile-macro/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wgpu-precompile-macro/src/lib.rs b/wgpu-precompile-macro/src/lib.rs index 0a006fd8fc1..13d47bb4949 100644 --- a/wgpu-precompile-macro/src/lib.rs +++ b/wgpu-precompile-macro/src/lib.rs @@ -198,7 +198,7 @@ struct TempFolder { } impl Drop for TempFolder { fn drop(&mut self) { - std::fs::remove_dir_all(&self.path).expect("Failed to remove temporary folder"); + let _ = std::fs::remove_dir_all(&self.path); } } From bedc327500c9152f1dd17b13c66f805f42b9e93a Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Wed, 24 Sep 2025 23:28:21 -0500 Subject: [PATCH 42/47] Updated to latest dx12 mesh shader changes --- Cargo.lock | 2 +- examples/features/src/mesh_shader/mod.rs | 6 ++++-- tests/tests/wgpu-gpu/mesh_shader/mod.rs | 6 ++++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d43392e7c71..50782b4fdcd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5202,7 +5202,7 @@ name = "wgpu-precompile-macro" version = "26.0.0" dependencies = [ "getrandom 0.3.3", - "hashbrown", + "hashbrown 0.16.0", "naga", "proc-macro2", "quote", diff --git a/examples/features/src/mesh_shader/mod.rs b/examples/features/src/mesh_shader/mod.rs index 77e80bea6e6..45cfd379b9a 100644 --- a/examples/features/src/mesh_shader/mod.rs +++ b/examples/features/src/mesh_shader/mod.rs @@ -53,10 +53,12 @@ fn compile_hlsl(device: &wgpu::Device, entry: &str, stage_str: &str) -> wgpu::Sh std::fs::remove_file(out_path).unwrap(); unsafe { device.create_shader_module_passthrough(wgpu::ShaderModuleDescriptorPassthrough { - entry_point: entry.to_owned(), label: None, num_workgroups: (1, 1, 1), - dxil: Some(std::borrow::Cow::Owned(file)), + dxil: Some(wgpu::DxilPassthroughDescriptor { + entry_point: entry.to_owned(), + code: std::borrow::Cow::Owned(file), + }), ..Default::default() }) } diff --git a/tests/tests/wgpu-gpu/mesh_shader/mod.rs b/tests/tests/wgpu-gpu/mesh_shader/mod.rs index 0c869e0f940..b4798bdb091 100644 --- a/tests/tests/wgpu-gpu/mesh_shader/mod.rs +++ b/tests/tests/wgpu-gpu/mesh_shader/mod.rs @@ -85,10 +85,12 @@ fn compile_hlsl( std::fs::remove_file(out_path).unwrap(); unsafe { device.create_shader_module_passthrough(wgpu::ShaderModuleDescriptorPassthrough { - entry_point: entry.to_owned(), label: None, num_workgroups: (1, 1, 1), - dxil: Some(std::borrow::Cow::Owned(file)), + dxil: Some(wgpu::DxilPassthroughDescriptor { + entry_point: entry.to_owned(), + code: std::borrow::Cow::Owned(file), + }), ..Default::default() }) } From dff0697955132becb22733e8bc25f4bce1093081 Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Wed, 24 Sep 2025 23:43:52 -0500 Subject: [PATCH 43/47] Tried to improve error messages for dxc precompiling --- wgpu-precompile-macro/src/lib.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/wgpu-precompile-macro/src/lib.rs b/wgpu-precompile-macro/src/lib.rs index 13d47bb4949..94efbf9edd9 100644 --- a/wgpu-precompile-macro/src/lib.rs +++ b/wgpu-precompile-macro/src/lib.rs @@ -231,7 +231,7 @@ pub fn precompile_hlsl_to_dxil(input: TokenStream) -> TokenStream { // user might've specified, as that could cause confusion. let input_file = tempoary_folder.path.join("__wgpu_inline.hlsl"); std::fs::write(&input_file, args.hlsl_code.as_bytes()) - .expect("Failed to write to HLSL input file"); + .expect("Failed to write to HLSL input file for DXC to consume"); let temporary_file_location = tempoary_folder.path.join("file.dxil"); let output = std::process::Command::new("dxc") .args([ @@ -246,11 +246,15 @@ pub fn precompile_hlsl_to_dxil(input: TokenStream) -> TokenStream { .stdin(Stdio::piped()) .stdout(Stdio::piped()) .output() - .expect("Failed to spawn DXC"); + .expect("Failed to spawn DXC. Maybe you don't have it installed or on the path environment variable?"); if !output.status.success() { - panic!("DXC failed:\n{}", String::from_utf8(output.stderr).unwrap()); + panic!( + "DXC failed to precompile HLSL to DXIL. Please report this at https://github.com/gfx-rs/wgpu/issues/ :\n{}", + String::from_utf8(output.stderr).unwrap() + ); } - let dxil = std::fs::read(temporary_file_location).expect("Failed to read DXC output file"); + let dxil = std::fs::read(temporary_file_location) + .expect("DXC exited without error but did not write file as expected. Please report this at https://github.com/gfx-rs/wgpu/issues/."); quote! { &[#(#dxil),*] } From 8344a8113f6bf78f696a2069a32f0919e2f32de8 Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Wed, 24 Sep 2025 23:45:43 -0500 Subject: [PATCH 44/47] Made dependency tests also check build deps --- tests/tests/wgpu-dependency/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/tests/wgpu-dependency/main.rs b/tests/tests/wgpu-dependency/main.rs index a604e55d6ba..6f554df3805 100644 --- a/tests/tests/wgpu-dependency/main.rs +++ b/tests/tests/wgpu-dependency/main.rs @@ -25,7 +25,7 @@ fn check_feature_dependency(requirement: Requirement) { println!("Checking: {}", requirement.human_readable_name); let mut args = Vec::new(); - args.extend(["tree", "-e", "normal", "--target", requirement.target]); + args.extend(["tree", "-e", "normal,build", "--target", requirement.target]); for package in requirement.packages { args.push("--package"); From 537b32e2c6b3802385bfa8903874b6ad792c9470 Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Thu, 25 Sep 2025 00:11:32 -0500 Subject: [PATCH 45/47] Fixed tests sorta (broke mesh shader test most likely due to requiring check against non-uploaded image) --- examples/features/src/hello_triangle/mod.rs | 37 +++++++-------------- 1 file changed, 12 insertions(+), 25 deletions(-) diff --git a/examples/features/src/hello_triangle/mod.rs b/examples/features/src/hello_triangle/mod.rs index a18616c53ab..eca99f6bb37 100644 --- a/examples/features/src/hello_triangle/mod.rs +++ b/examples/features/src/hello_triangle/mod.rs @@ -1,3 +1,4 @@ +use std::borrow::Cow; use winit::{ event::{Event, WindowEvent}, event_loop::EventLoop, @@ -26,17 +27,23 @@ async fn run(event_loop: EventLoop<()>, window: Window) { let (device, queue) = adapter .request_device(&wgpu::DeviceDescriptor { label: None, - required_features: wgpu::Features::EXPERIMENTAL_PASSTHROUGH_SHADERS, + required_features: wgpu::Features::empty(), // Make sure we use the texture resolution limits from the adapter, so we can support images the size of the swapchain. required_limits: wgpu::Limits::downlevel_webgl2_defaults() .using_resolution(adapter.limits()), - experimental_features: unsafe { wgpu::ExperimentalFeatures::enabled() }, + experimental_features: wgpu::ExperimentalFeatures::disabled(), memory_hints: wgpu::MemoryHints::MemoryUsage, trace: wgpu::Trace::Off, }) .await .expect("Failed to create device"); + // Load the shaders from disk + let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { + label: None, + source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("shader.wgsl"))), + }); + let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: None, bind_group_layouts: &[], @@ -45,32 +52,18 @@ async fn run(event_loop: EventLoop<()>, window: Window) { let swapchain_capabilities = surface.get_capabilities(&adapter); let swapchain_format = swapchain_capabilities.formats[0]; - let vs_shader = unsafe { - device.create_shader_module_passthrough(wgpu::include_precompiled_wgsl!( - "src/hello_triangle/shader.wgsl", - "vs_main", - all - )) - }; - let fs_shader = unsafe { - device.create_shader_module_passthrough(wgpu::include_precompiled_wgsl!( - "src/hello_triangle/shader.wgsl", - "fs_main", - all - )) - }; let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { label: None, layout: Some(&pipeline_layout), vertex: wgpu::VertexState { - module: &vs_shader, + module: &shader, entry_point: Some("vs_main"), buffers: &[], compilation_options: Default::default(), }, fragment: Some(wgpu::FragmentState { - module: &fs_shader, + module: &shader, entry_point: Some("fs_main"), compilation_options: Default::default(), targets: &[Some(swapchain_format.into())], @@ -93,13 +86,7 @@ async fn run(event_loop: EventLoop<()>, window: Window) { // Have the closure take ownership of the resources. // `event_loop.run` never returns, therefore we must do this to ensure // the resources are properly cleaned up. - let _ = ( - &instance, - &adapter, - &vs_shader, - &fs_shader, - &pipeline_layout, - ); + let _ = (&instance, &adapter, &shader, &pipeline_layout); if let Event::WindowEvent { window_id: _, From b06ce5edd23bbbc06958a8ac63ae0fdb37be6aaf Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Thu, 25 Sep 2025 00:11:33 -0500 Subject: [PATCH 46/47] Other part to last commit --- examples/README.md | 1 + examples/features/src/lib.rs | 2 + examples/features/src/main.rs | 18 ++- examples/features/src/mesh_shader/mod.rs | 19 +++ .../features/src/mesh_shader/screenshot.png | Bin 0 -> 8139 bytes .../features/src/precompiled_shader/mod.rs | 117 ++++++++++++++++++ 6 files changed, 151 insertions(+), 6 deletions(-) create mode 100644 examples/features/src/mesh_shader/screenshot.png create mode 100644 examples/features/src/precompiled_shader/mod.rs diff --git a/examples/README.md b/examples/README.md index 4cdc532751b..8ed4216788c 100644 --- a/examples/README.md +++ b/examples/README.md @@ -33,6 +33,7 @@ These examples use a common framework to handle wgpu init, window creation, and #### Graphics - `hello_triangle` - Provides an example of a bare-bones wgpu workflow using the Winit crate that simply renders a red triangle on a green background. +- `precompiled_shader` - Essentially `hello_triangle` except that the shaders are compiled at compile-time instead of runtime - `uniform_values` - Demonstrates the basics of enabling shaders and the GPU, in general, to access app state through uniform variables. `uniform_values` also serves as an example of rudimentary app building as the app stores state and takes window-captured keyboard events. The app displays the Mandelbrot Set in grayscale (similar to `storage_texture`) but allows the user to navigate and explore it using their arrow keys and scroll wheel. - `cube` - Introduces the user to slightly more advanced models. The example creates a set of triangles to form a cube on the CPU and then uses a vertex and index buffer to send the generated model to the GPU for usage in rendering. It also uses a texture generated on the CPU to shade the sides of the cube and a uniform variable to apply a transformation matrix to the cube in the shader. - `bunnymark` - Demonstrates many things, but chief among them is performing numerous draw calls with different bind groups in one render pass. The example also uses textures for the icon and uniform buffers to transfer both global and per-particle states. diff --git a/examples/features/src/lib.rs b/examples/features/src/lib.rs index baacf6a6b39..b5c933187f6 100644 --- a/examples/features/src/lib.rs +++ b/examples/features/src/lib.rs @@ -17,6 +17,7 @@ pub mod mesh_shader; pub mod mipmap; pub mod msaa_line; pub mod multiple_render_targets; +pub mod precompiled_shader; pub mod ray_cube_compute; pub mod ray_cube_fragment; pub mod ray_cube_normals; @@ -52,6 +53,7 @@ fn all_tests() -> Vec { mipmap::TEST_QUERY, msaa_line::TEST, multiple_render_targets::TEST, + precompiled_shader::TEST, ray_cube_compute::TEST, ray_cube_fragment::TEST, ray_cube_normals::TEST, diff --git a/examples/features/src/main.rs b/examples/features/src/main.rs index c9fc31259c9..60682b94ce4 100644 --- a/examples/features/src/main.rs +++ b/examples/features/src/main.rs @@ -62,6 +62,12 @@ const EXAMPLES: &[ExampleDesc] = &[ webgl: false, webgpu: true, }, + ExampleDesc { + name: "mesh_shader", + function: wgpu_examples::mesh_shader::main, + webgl: false, + webgpu: false, + }, ExampleDesc { name: "mipmap", function: wgpu_examples::mipmap::main, @@ -80,6 +86,12 @@ const EXAMPLES: &[ExampleDesc] = &[ webgl: false, webgpu: true, }, + ExampleDesc { + name: "precompiled_shader", + function: wgpu_examples::precompiled_shader::main, + webgl: true, + webgpu: true, + }, ExampleDesc { name: "render_to_texture", function: wgpu_examples::render_to_texture::main, @@ -182,12 +194,6 @@ const EXAMPLES: &[ExampleDesc] = &[ webgl: false, // No Ray-tracing extensions webgpu: false, // No Ray-tracing extensions (yet) }, - ExampleDesc { - name: "mesh_shader", - function: wgpu_examples::mesh_shader::main, - webgl: false, - webgpu: false, - }, ]; fn get_example_name() -> Option { diff --git a/examples/features/src/mesh_shader/mod.rs b/examples/features/src/mesh_shader/mod.rs index 45cfd379b9a..eb7d2c78627 100644 --- a/examples/features/src/mesh_shader/mod.rs +++ b/examples/features/src/mesh_shader/mod.rs @@ -181,3 +181,22 @@ impl crate::framework::Example for Example { pub fn main() { crate::framework::run::("mesh_shader"); } + +#[cfg(test)] +#[wgpu_test::gpu_test] +pub static TEST: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams { + name: "mesh_shader", + image_path: "/examples/features/src/mesh_shader/screenshot.png", + width: 1024, + height: 768, + optional_features: wgpu::Features::default(), + base_test_parameters: wgpu_test::TestParameters::default() + .test_features_limits() + .features( + wgpu::Features::EXPERIMENTAL_MESH_SHADER + | wgpu::Features::EXPERIMENTAL_PASSTHROUGH_SHADERS, + ) + .limits(wgpu::Limits::default().using_recommended_minimum_mesh_shader_values()), + comparisons: &[wgpu_test::ComparisonType::Mean(0.02)], + _phantom: std::marker::PhantomData::, +}; diff --git a/examples/features/src/mesh_shader/screenshot.png b/examples/features/src/mesh_shader/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..ec2bb24bc3848a384a5269b1ff96e28b30c223d4 GIT binary patch literal 8139 zcmYjWdq9lo_doBIEjyAfL{qsW={qhfS~BF)R?E|tTq9XYlF6kl zEG?Ug@Fo&cS?z9UHOW?N7^`iwF8$8@8t>~bW6qrO`JC(XoTu&5S%D%QGaUdB1qbO#kGNfJLepBjErr$e}Cb^`P#~Y z^Ll@6Tli>4!O7`Ld)3u1KmPY_GwPFjAG<~aaoYUh?T5_+&k*Ej)mv>4-`vwV&~cRy zoh}sq3aAhO$+iU^ABzCQTk#7{>({+o0J5spw1DS~zpC+FTeBMf+i3_u%Nkw#`~Y(A zvht~xmG?Zf01~tTWQuua-`D`uFU2p=S}rnK)KF2amcJ3AJj=y=qguY0mD|j1Z8^jN z;)U)z)3*Z_;_qA{)$|D111KuT#&^%8K(d(wu)ODXZ8XX+&u8T&mL7j2eZ>U)%Jt{- z!;Co~37xBk7Zaqkyy%*!Y79VkAbyiMJFaRAK-13QYPnE`Ci99DJL1us|MW2w-2LXO zkY2BhrT4_UwNy>-Kcpv({Gm1q;mbaQFW&9(Tn#@S;fXijSHn~L%)dhT=Fi~o8pPM3_w**D+Tu_$5zW&vtoatZ*8#0Y z06n%CE>X7mB2@2L#IoO6N&RaMv@U8_WYzw@nu1GgXq6-5t3KF6={}@3UUBcAM?lB3 zrX%Y`~3%liv!2vG#Ddz7$x-sm=3E^IvtwDt#+8MA=3I#Y87HRJHm?@J!p|6sJ$~7g z0Nwt`Sg_pxLmxC{vW5$u@*lPVcpD&NL)U_jBcSOsSgWVC588O(tuAA(Yu-n9XgbX5 zUQuQ|KyU5U-ddW?*gO_;udy|LM1HJiH6Qk2?v?z^<5w*qWC=@IuT}icoJ0E~<^CLKukEuU>&87*xlV1S0V6;13d=My6~jEgIW z^?3-09cqqT!Ta!n60^<{&v^2BC}ag;*%)P77idBFXI9&5jT?Dz3rp6&sk{9Fh*?j` z>sHkhQwlI~H7j~lr-9zcBG#@wwlkIwn=pdT*#WwZx=_WYMrPqrk(dF>jmRh9UPb$S z4taE1?WP1Urn}l}M>HBi$}9|^(9F$e0mzRq;U#^oO;1d@UvF3)0Hr!Mj zo^otUtRcjFX4uE$bYcLdT#tsQ-;ZpcuSF7A!wz>c9Si1z=N>ei?HZoAfl@}Zp}4&! zZAYvLG>pdHHp(QYito^?6Rz$mlUF)$iY<-E+Dv<&n_qD0 z=}0}Nsg9ob*b?Ucg#Q^VSGl)GuFC9{K8!_vp8IRH8rm0flq*?)Ssk?o4SycyC__+Z zR$*AGTh#){zrlotzSAnb7WH@{G>EZTMwuB_WNZ|Xc`O6Z`b@j%J6{Osv6w!Kqr*~t ztCGRuvkAaO@6H%>L4qjF+Xu9wguEK3|sVJ!mLy4+P^|H>ZrupOrw z<*`e8XrmEP8>pDEgz`SuGZ0!CmL5}e9$Il|D(y3Qy-)wpQo67%ZtCCtQD-?DH=$}jMqRwH(`mNxJ_ieNmA7HqNv& zX{{RD#v@L6BI)aVs~pkBCl(BI0`dJMImH$xq30P9E!;h-A0fAqQFqQY_f?u+F@^I8 zo)KQh4eZ|l^=#-(>O@v5oi=@P5K~j*(3mWM#$f$|8QH8Me)6}iF&R#chF z-SD@F(~aIY?Yd@OOqUB4)=cIlw(+fGddfaX8iq%GVH4l+?^T>`wkw;6xw~Rgn**^z zdN;bOAEWDJMPm)b)^CV^ne`w6ERNj^K@Q(G~eXT9u z?)6=6%M5_h?S3XfPag?N_b{oKaXn;`No^A1i|WP^{ha<0oF2ArDzeNckRvIE)R|FO z)!Y1xl^d^Q!tZS7M$;l=x+}$&I)itB$dE-tRBmn4$v>=nLn`la67yHv(@|JF(~QnR z_nZV%+QfpwndTztiPD!&RUU;LGdNf3y&%{6p z9#VMM&*xfTYbIu2Gn9SVwwQFWF)UElYAbZxmXa<+usYtvE52E7s$9b&2r3qFitl)s zDJP3#+Z>$Z5($Rfnc(|An>R(sN`(14!N zZusyvhx@*P=^@n+j{r#MFXC3I4IJG{9?F|-&T*Bg zl*~9*ziB_7Y8#>SdR5#1rMohRAY zFiA0zRo`K52u1QD>Q_XcWHcQ@I#zNWS@cmAHk8aU)l zgnPare_&|cb!^}l&v)c78@dtk5(xTpC~pG}mID#}kkv3>_*v_Z|Ff10R}$6XtT}v2%GwlfgNK?%f{~4NA9$aG4gvU+(BJ0_3`}j&huwalKKcfRVp!`xt3vzEf(0zZKiQ)Si5e4;6cL_G(^wi;kNyB`-6z1q8@^~JI z(_F0eLE?->vUE+~RCmU^^tD`ogUygN10dDnJ!H)s2L~By`T?pD6gyaQp(9&v0{2Pq%`|DVY>b zd>IH_pfo#*yNpSTB*EYtQ0m0#c8B8f?X`^}V@rG$9*pCzqf!O1L7FD{9|xA#RS$ul z<0DcF80$^Du3JK}WG3f>5ER1%<3PIoAuox^iNw%g11Lti>GawF;ai*r$yag1yV<9U z8^O&K(QS5;9>I(xWO3kd@j?X~b)!*J1mGQBBU#7+hq`KQSXY#jn#X`kcU`xDT**xC zKM1Zb7zNJTAM)c^@L>jPK{nD&w;91X7Q2968fRI|f*%ULP7z+5RSSA1OJK1VZIa_$X57p2lv4$|(lRfFw09S9wz4LsxS^dLT-~?v!Lg(DxKPk2qibI}+TQ z#sh_3nRzR-%#zTcEDHV$tEuind0HA5-;c^HTA4+Q=-Jf*d?EnbpNY*^~eBz8%#oPxw0vuuRk^|VF=bXZ(4`&!|= z`}znV^Ge1X#BuE~8FQRJZPhJTCM~sDO&pNByV>JQh)MNUQy_Di^Fo+sA%*XDCxBBW zUI!&0`Fiqofaqy2g4y?lhuJi$ySX_W)`sP`wj$Go%}3WU`-j4EI}*KA68tO(Bx5K4 z3^2RU{e(F~epL`&HH!l>r;p}9lnPN5j%AMQ>ySg}Q5}%1kDKeQ57IonF#rpT7nZP5 zpKmZ5A6CO^!VluUpMrJMJ$>AaIgD?&_vUi&NhmL82M@k~GF2OBqqqepnZQdk(v5(u zXs(z&5)?e^0Dz+KzrvXljJ&Wr3J~%`=Lrr43*PE;Aj|K?9riix!d`-31&Hcrao~cg zoeSdqQMn75eu$p+L>naDZWF%{LdExbmUu?};NBU>kQ;B?&I9sqb6s>W(4$gXaB+1n zn>3GMSDNpScY~J^E(ux`%y^5nO)qI~IL*|v%v9SRz$Z|27x(@gRXg5omo)!!nt6s* zt}~c{2Ww_t-f$k|-`9VX@S>*?`&14_kfwo5FA7dimGxG`RB$wRsLU}~W zasUOt%&UW^j!Md+7h3hxee^dW)HQ;FSN&3a$Ql}# zJeg%+t&Q^fqqSYQ_kV21`v$m_{PUI>`NSU^JtN}Hn@kYr>mlk=={x2EAhf5DoKq56 z$sy916o9(p#&t~P3+-NWfvimWE`&pzW7c7I%`Z=3HcyGZwl#oitgqh)N+O)#jxRqu zV{DjVD<=MWxDp07W=#R-d}CA0WBTJolRvc$Ei=GiD@~KOJ>bFAZ`M#iYU2; zy9K$PZHlP31C!dFnECuEt0yvhsZ3uRt5I(6TkFIjHdXkdPwOOQdNFz920g{_?(em5 z5klUZk+^KnYfA2A2JP|?jsV9%u--3+Krv>56{bBc=JO|uwU>UW~puIXJ5<9SOEhmT*~nNp0DsJm94Ul1pvhyaglg~jDx z-!fj0&X%G^e&C+<<57%LMd5V$w{KW+RuGC$AM5f~6Bpu3Nx|d4m=&KG2Pu%*nPYV1!y`iJeTYKW-Yl2yV**`8#Z0 zT5pB#;*ddI<+0;2_cY>MjW{+usrI!-`K(>LG*bij(7gzkWNY zE?IAxG*6@6t5LsL{J17ZG^oBVqVj-7eYyr+aq6V+s!EOe#^K2eH0sS7^`he6I&w7V zArU3Ne^S?=%T7A_zUtGEUpGAIe}n3>zclLMJug$sHKF~br)P^MbmKIk3o1J``ijqB z=(6GiSFgm*!@rA0dk=;Vu3Wkpc*SW@PAAXnyRFIWD2=}I!l|UrKL=9-FE1V4cwsQM zIU4WC&C@Rn+b({@3oOvCW0O~C=-txL^ISG3^uup|S{os1`04H+HS{7zL+eMiidVzS zpW04-Ov3_|Ty#lZHK?xqUQ<}5wex-dU9f+%y5N-;T#{C6(8dFH%iRr$h!8z`p} zVxHa5*q^GQFWYm>_wA!O1Jl%Uuid?!8uubJaz#dLa&P&Zfoiopsz$-o`+YlYF^qu<+#xn!@kxogX^T*F9j1@}`;} zA3q+f+(Zve_?Pv!bX@(mPhEwO^semp^1 Self { + let vs_shader = unsafe { + device.create_shader_module_passthrough(wgpu::include_precompiled_wgsl!( + "src/hello_triangle/shader.wgsl", + "vs_main", + all + )) + }; + let fs_shader = unsafe { + device.create_shader_module_passthrough(wgpu::include_precompiled_wgsl!( + "src/hello_triangle/shader.wgsl", + "fs_main", + all + )) + }; + + let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: None, + bind_group_layouts: &[], + push_constant_ranges: &[], + }); + let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: None, + layout: Some(&pipeline_layout), + vertex: wgpu::VertexState { + module: &vs_shader, + entry_point: Some("vs_main"), + buffers: &[], + compilation_options: Default::default(), + }, + fragment: Some(wgpu::FragmentState { + module: &fs_shader, + entry_point: Some("fs_main"), + compilation_options: Default::default(), + targets: &[Some(config.format.into())], + }), + primitive: wgpu::PrimitiveState::default(), + depth_stencil: None, + multisample: wgpu::MultisampleState::default(), + multiview: None, + cache: None, + }); + Self { pipeline } + } + fn render(&mut self, view: &wgpu::TextureView, device: &wgpu::Device, queue: &wgpu::Queue) { + let mut encoder = + device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); + { + let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: None, + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + view, + depth_slice: None, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Clear(wgpu::Color::GREEN), + store: wgpu::StoreOp::Store, + }, + })], + depth_stencil_attachment: None, + timestamp_writes: None, + occlusion_query_set: None, + }); + rpass.set_pipeline(&self.pipeline); + rpass.draw(0..3, 0..1); + } + + queue.submit(Some(encoder.finish())); + } + fn update(&mut self, _event: winit::event::WindowEvent) {} + fn resize( + &mut self, + _config: &wgpu::SurfaceConfiguration, + _device: &wgpu::Device, + _queue: &wgpu::Queue, + ) { + } + fn required_downlevel_capabilities() -> wgpu::DownlevelCapabilities { + wgpu::DownlevelCapabilities::default() + } + fn required_features() -> wgpu::Features { + wgpu::Features::EXPERIMENTAL_PASSTHROUGH_SHADERS + } + fn required_limits() -> wgpu::Limits { + wgpu::Limits::defaults() + } +} + +pub fn main() { + crate::framework::run::("precompiled_shader"); +} + +#[cfg(test)] +#[wgpu_test::gpu_test] +pub static TEST: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams { + name: "precompiled_shader", + image_path: "/examples/features/src/mesh_shader/screenshot.png", + width: 1024, + height: 768, + optional_features: wgpu::Features::default(), + base_test_parameters: wgpu_test::TestParameters::default() + .test_features_limits() + .features(wgpu::Features::EXPERIMENTAL_PASSTHROUGH_SHADERS), + comparisons: &[wgpu_test::ComparisonType::Mean(0.02)], + _phantom: std::marker::PhantomData::, +}; From 167517053b1484c9bb633b0ff6dd9ae04829d827 Mon Sep 17 00:00:00 2001 From: SupaMaggie70 Date: Thu, 25 Sep 2025 16:46:24 -0500 Subject: [PATCH 47/47] Initial work on new shaders crate that will be used by wgpu-hal, the macro crate, and the whole gang --- Cargo.lock | 10 ++ Cargo.toml | 1 + examples/features/Cargo.toml | 4 +- tests/Cargo.toml | 2 +- wgpu-shaders/Cargo.toml | 38 ++++++++ wgpu-shaders/src/lib.rs | 171 +++++++++++++++++++++++++++++++++++ wgpu/Cargo.toml | 2 +- wgpu/src/macros.rs | 12 +-- 8 files changed, 230 insertions(+), 10 deletions(-) create mode 100644 wgpu-shaders/Cargo.toml create mode 100644 wgpu-shaders/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 50782b4fdcd..5945558bb81 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5209,6 +5209,16 @@ dependencies = [ "syn", ] +[[package]] +name = "wgpu-shaders" +version = "26.0.0" +dependencies = [ + "naga", + "serde", + "thiserror 2.0.16", + "wgpu-types", +] + [[package]] name = "wgpu-test" version = "26.0.0" diff --git a/Cargo.toml b/Cargo.toml index 3f657b9f66f..1c827ba6e50 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,7 @@ members = [ "wgpu", "wgpu-precompile-macro", "xtask", + "wgpu-shaders", ] exclude = [] default-members = [ diff --git a/examples/features/Cargo.toml b/examples/features/Cargo.toml index 1a9cd202354..fae13e9b41e 100644 --- a/examples/features/Cargo.toml +++ b/examples/features/Cargo.toml @@ -56,7 +56,7 @@ wgpu-test.workspace = true [target.'cfg(not(target_arch = "wasm32"))'.dependencies] env_logger.workspace = true -wgpu = { workspace = true, features = ["precompile"] } +wgpu = { workspace = true, features = ["precompile-macro"] } [target.'cfg(target_arch = "wasm32")'.dependencies] console_error_panic_hook.workspace = true @@ -67,7 +67,7 @@ wasm-bindgen-futures.workspace = true wgpu = { path = "../../wgpu", default-features = false, features = [ "wgsl", "std", - "precompile", + "precompile-macro", ] } # We need these features in the framework examples and tests web-sys = { workspace = true, features = [ diff --git a/tests/Cargo.toml b/tests/Cargo.toml index cd65f790ce8..b15f49b3b62 100644 --- a/tests/Cargo.toml +++ b/tests/Cargo.toml @@ -34,7 +34,7 @@ webgl = ["wgpu/webgl"] test-build-with-profiling = ["profiling/type-check"] [dependencies] -wgpu = { workspace = true, features = ["noop", "precompile"] } +wgpu = { workspace = true, features = ["noop", "precompile-macro"] } wgpu-hal = { workspace = true, features = ["validation_canary"] } wgpu-macros.workspace = true diff --git a/wgpu-shaders/Cargo.toml b/wgpu-shaders/Cargo.toml new file mode 100644 index 00000000000..12e437ff5d9 --- /dev/null +++ b/wgpu-shaders/Cargo.toml @@ -0,0 +1,38 @@ +[package] +name = "wgpu-shaders" +edition.workspace = true +rust-version.workspace = true +keywords.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +version.workspace = true +authors.workspace = true + +[features] +serde = ["dep:serde", "wgpu-types/serde", "naga/serialize", "naga/deserialize"] + +all-in = ["spv-in", "wgsl-in", "glsl-in"] +spv-in = ["naga/spv-in"] +wgsl-in = ["naga/wgsl-in"] +glsl-in = ["naga/glsl-in"] + +all-out = ["spv-out", "wgsl-out", "glsl-out", "hlsl-out", "msl-out"] +spv-out = ["naga/spv-out"] +wgsl-out = ["naga/wgsl-out"] +glsl-out = ["naga/glsl-out"] +hlsl-out = ["naga/hlsl-out"] +msl-out = ["naga/msl-out"] + +default = ["all-in", "all-out"] + + +[dependencies] +naga = { workspace = true } +wgpu-types = { workspace = true, default-features = false } + +serde = { workspace = true, optional = true } +thiserror.workspace = true + +[lints] +workspace = true diff --git a/wgpu-shaders/src/lib.rs b/wgpu-shaders/src/lib.rs new file mode 100644 index 00000000000..7df2e20645d --- /dev/null +++ b/wgpu-shaders/src/lib.rs @@ -0,0 +1,171 @@ +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; +use thiserror::Error; +use wgpu_types::{ + BindGroupLayoutEntry, CreateShaderModuleDescriptorPassthrough, PushConstantRange, + ShaderRuntimeChecks, +}; + +// Reexport certain configuration options so that not every user has to also depend on naga themselves +#[cfg(feature = "glsl-out")] +pub use naga::back::glsl; +#[cfg(feature = "hlsl-out")] +pub use naga::back::hlsl; +#[cfg(feature = "msl-out")] +pub use naga::back::msl; +#[cfg(feature = "spv-out")] +pub use naga::back::spv; +#[cfg(feature = "wgsl-out")] +pub use naga::back::wgsl; + +/// A range of push constant memory to pass to a shader stage. +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct PipelineLayout< + 'a, + A: IntoIterator, + B: IntoIterator, + C: IntoIterator, +> { + pub bind_groups: B, + pub push_constant_ranges: C, +} + +pub enum PrecompiledShaderInput<'a> { + #[cfg(feature = "wgsl-in")] + Wgsl(&'a str), + #[cfg(feature = "spv-in")] + Spirv(&'a [u32]), + #[cfg(feature = "glsl-in")] + Glsl(&'a str), + Naga(&'a naga::Module, &'a naga::valid::ModuleInfo), +} + +pub enum Dx12OutputMode { + Hlsl, + Dxc, + Fxc, +} + +pub struct CompileShaderDescriptor<'a> { + pub input: PrecompiledShaderInput<'a>, + pub entry_point: String, + pub runtime_checks: Option, +} + +pub struct PipelineCompileInfo< + 'a, + A: IntoIterator, + B: IntoIterator, + C: IntoIterator, +> { + pub layout: &'a PipelineLayout<'a, A, B, C>, + /// Leave as None to exclude spirv output + #[cfg(feature = "spv-out")] + pub spirv_options: Option>, + /// Leave as None to exclude wgsl output + #[cfg(feature = "wgsl-out")] + pub wgsl_options: Option, + /// Leave as None to exclude glsl output + #[cfg(feature = "glsl-out")] + pub glsl_options: Option, + /// Leave as None to exclude hlsl/dxil output + #[cfg(feature = "hlsl-out")] + pub hlsl_options: Option, + /// Leave as None to exclude msl output + #[cfg(feature = "msl-out")] + pub msl_options: Option, + #[cfg(feature = "hlsl-out")] + /// If `hlsl_options` is not None, whether or not to compile the outputted HLSL into DXIL + pub dx12_output_mode: Dx12OutputMode, +} + +pub struct CompileRenderPipelineInfo< + 'a, + A: IntoIterator, + B: IntoIterator, + C: IntoIterator, +> { + pub vertex: CompileShaderDescriptor<'a>, + pub fragment: Option>, + pub pipeline_info: PipelineCompileInfo<'a, A, B, C>, +} +pub struct CompiledRenderPipelineShaders<'a> { + pub vertex: CreateShaderModuleDescriptorPassthrough<'a, ()>, + pub fragment: Option>, +} + +pub struct CompileComputePipelineInfo< + 'a, + A: IntoIterator, + B: IntoIterator, + C: IntoIterator, +> { + pub compute: CompileShaderDescriptor<'a>, + pub pipeline_info: PipelineCompileInfo<'a, A, B, C>, +} +pub struct CompiledComputePipelineShaders<'a> { + pub compute: CreateShaderModuleDescriptorPassthrough<'a, ()>, +} + +pub struct CompileMeshPipelineInfo< + 'a, + A: IntoIterator, + B: IntoIterator, + C: IntoIterator, +> { + pub task: Option>, + pub mesh: CompileShaderDescriptor<'a>, + pub fragment: Option>, + pub pipeline_info: PipelineCompileInfo<'a, A, B, C>, +} +pub struct CompiledMeshPipelineShaders<'a> { + pub task: Option>, + pub mesh: CreateShaderModuleDescriptorPassthrough<'a, ()>, + pub fragment: Option>, +} + +/// Write out the passthrough to literal rust code that can then be parsed by rustc. +pub fn passthrough_descriptor_to_rust_code( + desc: &CreateShaderModuleDescriptorPassthrough<'_, ()>, + out: &mut impl std::fmt::Write, +) { + todo!() +} + +/// Error during compiling of shaders +#[derive(Error, Debug)] +pub enum Error {} + +pub fn compile_render_pipeline_shaders< + 'a, + A: IntoIterator, + B: IntoIterator, + C: IntoIterator, +>( + info: &CompileRenderPipelineInfo<'a, A, B, C>, +) -> Result, Error> { + todo!() +} + +pub fn compile_mesh_pipeline_shaders< + 'a, + A: IntoIterator, + B: IntoIterator, + C: IntoIterator, +>( + info: &CompileMeshPipelineInfo<'a, A, B, C>, +) -> Result, Error> { + todo!() +} + +pub fn compile_compute_pipeline_shaders< + 'a, + A: IntoIterator, + B: IntoIterator, + C: IntoIterator, +>( + info: &CompileComputePipelineInfo<'a, A, B, C>, +) -> Result, Error> { + todo!() +} diff --git a/wgpu/Cargo.toml b/wgpu/Cargo.toml index 32f954b6a47..99608807bce 100644 --- a/wgpu/Cargo.toml +++ b/wgpu/Cargo.toml @@ -188,7 +188,7 @@ std = ["raw-window-handle/std", "wgpu-types/std", "wgpu-core?/std"] ## based on whether `std` is enabled or not. parking_lot = ["dep:parking_lot"] -precompile = ["dep:wgpu-precompile-macro"] +precompile-macro = ["dep:wgpu-precompile-macro"] ######################### # Standard Dependencies # diff --git a/wgpu/src/macros.rs b/wgpu/src/macros.rs index 355b56c2af5..36d85cd3d51 100644 --- a/wgpu/src/macros.rs +++ b/wgpu/src/macros.rs @@ -245,7 +245,7 @@ macro_rules! hal_type_gles { /// backend is enabled, hlsl won't be compiled. All backends will be compiled for by naga; the input wgsl code won't /// necessarily be the same as the output wgsl. #[macro_export] -#[cfg(feature = "precompile")] +#[cfg(feature = "precompile-macro")] macro_rules! include_precompiled_wgsl { ($path: literal, $entry: literal, $($id: ident)+) => { $crate::__macro_helpers::precompile!($crate wgsl true $path $entry $($id)*) @@ -266,7 +266,7 @@ macro_rules! include_precompiled_wgsl { /// backend is enabled, hlsl won't be compiled. All backends will be compiled for by naga; the input wgsl code won't /// necessarily be the same as the output wgsl. #[macro_export] -#[cfg(feature = "precompile")] +#[cfg(feature = "precompile-macro")] macro_rules! precompile_wgsl { ($shader: literal, $entry: literal, $($id: ident)+) => { $crate::__macro_helpers::precompile!($crate wgsl false $shader $entry $($id)*) @@ -289,7 +289,7 @@ macro_rules! precompile_wgsl { /// backend is enabled, hlsl won't be compiled. All backends will be compiled for by naga; the input spirv code won't /// necessarily be the same as the output spirv. #[macro_export] -#[cfg(feature = "precompile")] +#[cfg(feature = "precompile-macro")] macro_rules! include_precompiled_spirv { ($path: literal, $entry: literal, $($id: ident)+) => { $crate::__macro_helpers::precompile!($crate spirv true $path $entry $($id)*) @@ -311,7 +311,7 @@ macro_rules! include_precompiled_spirv { /// backend is enabled, hlsl won't be compiled. All backends will be compiled for by naga; the input glsl code won't /// necessarily be the same as the output glsl. #[macro_export] -#[cfg(feature = "precompile")] +#[cfg(feature = "precompile-macro")] macro_rules! include_precompiled_glsl { ($path: literal, $stage: ident, $($id: ident)+) => { $crate::__macro_helpers::precompile!($crate glsl $stage true $path "main" $($id)*) @@ -332,7 +332,7 @@ macro_rules! include_precompiled_glsl { /// backend is enabled, hlsl won't be compiled. All backends will be compiled for by naga; the input glsl code won't /// be the same as the output glsl. #[macro_export] -#[cfg(feature = "precompile")] +#[cfg(feature = "precompile-macro")] macro_rules! precompile_glsl { ($shader: literal, $stage: ident, $($id: ident)+) => { $crate::__macro_helpers::precompile!($crate glsl $stage false $shader "main" $($id)+) @@ -344,7 +344,7 @@ pub mod helpers { pub use alloc::borrow::Cow; pub use alloc::string::{String, ToString}; pub use core::{include_bytes, include_str}; - #[cfg(feature = "precompile")] + #[cfg(feature = "precompile-macro")] pub use wgpu_precompile_macro::{precompile, precompile_hlsl_to_dxil}; pub use None; pub use Some;