diff --git a/example/Cargo.toml b/example/Cargo.toml index fa2dd0e..478d393 100644 --- a/example/Cargo.toml +++ b/example/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "example" -version = "0.7.0" +version = "0.7.4" edition = "2021" [dependencies] diff --git a/example/build.rs b/example/build.rs index 2d2063e..f4b76d1 100644 --- a/example/build.rs +++ b/example/build.rs @@ -13,7 +13,7 @@ fn main() -> Result<()> { .derive_serde(false) .output_file("src/shader.rs") .short_constructor(2) - .shader_source_type(WgslShaderSourceType::UseComposerWithIncludeStr) + .shader_source_type(WgslShaderSourceType::UseBothComposerWithPathAndIncludeStr) .build()? .generate() .into_diagnostic() diff --git a/example/src/shader.rs b/example/src/shader.rs index 18bd742..bfcebf6 100644 --- a/example/src/shader.rs +++ b/example/src/shader.rs @@ -1,8 +1,8 @@ // File automatically generated by wgsl_bindgen^ // -// ^ wgsl_bindgen version 0.7.0 +// ^ wgsl_bindgen version 0.7.4 // Changes made to this file will not be saved. -// SourceHash: 6f469152513ffb30656f1e8a34dc974a59e090971e235acd89be6ecbd333d8f4 +// SourceHash: 7db63b2890a69cf5dc81933a5710b1c63b8ab9863314b37ebd28c9dce576cfe1 #![allow(unused, non_snake_case, non_camel_case_types)] mod _root { @@ -856,38 +856,171 @@ pub mod testbed { }, ) } - pub fn init_composer() -> naga_oil::compose::Composer { + pub fn create_shader_module(device: &wgpu::Device) -> wgpu::ShaderModule { + let source = std::borrow::Cow::Borrowed(SHADER_STRING); + device + .create_shader_module(wgpu::ShaderModuleDescriptor { + label: None, + source: wgpu::ShaderSource::Wgsl(source), + }) + } + const SHADER_STRING: &'static str = r#" +struct rtsStructX_naga_oil_mod_XEIXC4L3NN5ZGKLLTNBQWIZLSFVTGS3DFOMXXEZLBMNUG2ZJCX { + other_data: i32, + the_array: array, +} + +struct ScalarsX_naga_oil_mod_XOR4XAZLTX { + a: u32, + b: i32, + c: f32, +} + +struct VectorsU32X_naga_oil_mod_XOR4XAZLTX { + a: vec2, + b: vec3, + c: vec4, +} + +struct VectorsI32X_naga_oil_mod_XOR4XAZLTX { + a: vec2, + b: vec3, + c: vec4, +} + +struct VectorsF32X_naga_oil_mod_XOR4XAZLTX { + a: vec2, + b: vec3, + c: vec4, +} + +struct MatricesF32X_naga_oil_mod_XOR4XAZLTX { + a: mat4x4, + b: mat4x3, + c: mat4x2, + d: mat3x4, + e: mat3x3, + f: mat3x2, + g: mat2x4, + h: mat2x3, + i: mat2x2, +} + +struct StaticArraysX_naga_oil_mod_XOR4XAZLTX { + a: array, + b: array, + c: array, 512>, + d: array, 4>, +} + +struct NestedX_naga_oil_mod_XOR4XAZLTX { + a: MatricesF32X_naga_oil_mod_XOR4XAZLTX, + b: VectorsF32X_naga_oil_mod_XOR4XAZLTX, +} + +struct Uniforms { + color_rgb: vec4, + scalars: ScalarsX_naga_oil_mod_XOR4XAZLTX, +} + +struct VertexInput { + @location(0) position: vec3, +} + +struct VertexOutput { + @builtin(position) clip_position: vec4, + @location(0) tex_coords: vec2, +} + +@group(1) @binding(1) +var ONEX_naga_oil_mod_XEIXC4L3NN5ZGKLLTNBQWIZLSFVTGS3DFOMXXEZLBMNUG2ZJCX: f32; +@group(2) @binding(1) +var rts: rtsStructX_naga_oil_mod_XEIXC4L3NN5ZGKLLTNBQWIZLSFVTGS3DFOMXXEZLBMNUG2ZJCX; +@group(2) @binding(2) +var a: ScalarsX_naga_oil_mod_XOR4XAZLTX; +@group(2) @binding(3) +var b: VectorsU32X_naga_oil_mod_XOR4XAZLTX; +@group(2) @binding(4) +var c: VectorsI32X_naga_oil_mod_XOR4XAZLTX; +@group(2) @binding(5) +var d: VectorsF32X_naga_oil_mod_XOR4XAZLTX; +@group(2) @binding(6) +var f: MatricesF32X_naga_oil_mod_XOR4XAZLTX; +@group(2) @binding(8) +var h: StaticArraysX_naga_oil_mod_XOR4XAZLTX; +@group(2) @binding(9) +var i: NestedX_naga_oil_mod_XOR4XAZLTX; +@group(0) @binding(0) +var color_texture: texture_2d; +@group(0) @binding(1) +var color_sampler: sampler; +@group(1) @binding(0) +var uniforms: Uniforms; + +@vertex +fn vs_main(in: VertexInput) -> VertexOutput { + var out: VertexOutput; + + out.clip_position = vec4(in.position.xyz, 1f); + let _e13 = ONEX_naga_oil_mod_XEIXC4L3NN5ZGKLLTNBQWIZLSFVTGS3DFOMXXEZLBMNUG2ZJCX; + out.tex_coords = ((in.position.xy * 0.5f) + vec2((0.5f * _e13))); + let _e18 = out; + return _e18; +} + +@fragment +fn fs_main(in_1: VertexOutput) -> @location(0) vec4 { + let _e4 = textureSample(color_texture, color_sampler, in_1.tex_coords); + let color = _e4.xyz; + let _e8 = uniforms.color_rgb; + return vec4((color * _e8.xyz), 1f); +} +"#; + pub fn init_composer( + entry_dir_path: &std::path::Path, + ) -> Result { let mut composer = naga_oil::compose::Composer::default(); + let mut source_path = entry_dir_path.to_path_buf(); + source_path.push("../more-shader-files/reachme.wgsl"); + let source = std::fs::read_to_string(&source_path)?; composer .add_composable_module(naga_oil::compose::ComposableModuleDescriptor { - source: include_str!("shader/../more-shader-files/reachme.wgsl"), - file_path: "shader/../more-shader-files/reachme.wgsl", + source: &source, + file_path: source_path.to_str().unwrap(), language: naga_oil::compose::ShaderLanguage::Wgsl, as_name: Some("\"../more-shader-files/reachme\"".into()), ..Default::default() }) .expect("failed to add composer module"); + let mut source_path = entry_dir_path.to_path_buf(); + source_path.push("types.wgsl"); + let source = std::fs::read_to_string(&source_path)?; composer .add_composable_module(naga_oil::compose::ComposableModuleDescriptor { - source: include_str!("shader/types.wgsl"), - file_path: "shader/types.wgsl", + source: &source, + file_path: source_path.to_str().unwrap(), language: naga_oil::compose::ShaderLanguage::Wgsl, as_name: Some("types".into()), ..Default::default() }) .expect("failed to add composer module"); - composer + Ok(composer) } pub fn make_naga_module( + entry_dir_path: &std::path::Path, composer: &mut naga_oil::compose::Composer, - ) -> wgpu::naga::Module { - composer + ) -> Result { + let mut source_path = entry_dir_path.to_path_buf(); + source_path.push("testbed.wgsl"); + let source = std::fs::read_to_string(&source_path)?; + let module = composer .make_naga_module(naga_oil::compose::NagaModuleDescriptor { - source: include_str!("shader/testbed.wgsl"), - file_path: "shader/testbed.wgsl", + source: &source, + file_path: source_path.to_str().unwrap(), ..Default::default() }) - .expect("failed to build naga module") + .expect("failed to build naga module"); + Ok(module) } pub fn naga_module_to_string(module: &wgpu::naga::Module) -> String { let info = wgpu::naga::valid::Validator::new( @@ -903,16 +1036,20 @@ pub mod testbed { ) .expect("failed to convert naga module to source") } - pub fn create_shader_module(device: &wgpu::Device) -> wgpu::ShaderModule { - let mut composer = init_composer(); - let module = make_naga_module(&mut composer); + pub fn create_shader_module_from_dir( + entry_dir_path: &std::path::Path, + device: &wgpu::Device, + ) -> Result { + let mut composer = init_composer(entry_dir_path)?; + let module = make_naga_module(entry_dir_path, &mut composer)?; let source = naga_module_to_string(&module); let source = std::borrow::Cow::Owned(source); - device + let module = device .create_shader_module(wgpu::ShaderModuleDescriptor { label: None, source: wgpu::ShaderSource::Wgsl(source), - }) + }); + Ok(module) } } pub mod triangle { @@ -1138,20 +1275,74 @@ pub mod triangle { }, ) } - pub fn init_composer() -> naga_oil::compose::Composer { + pub fn create_shader_module(device: &wgpu::Device) -> wgpu::ShaderModule { + let source = std::borrow::Cow::Borrowed(SHADER_STRING); + device + .create_shader_module(wgpu::ShaderModuleDescriptor { + label: None, + source: wgpu::ShaderSource::Wgsl(source), + }) + } + const SHADER_STRING: &'static str = r#" +struct Uniforms { + color_rgb: vec4, +} + +struct VertexInput { + @location(0) position: vec3, +} + +struct VertexOutput { + @builtin(position) clip_position: vec4, + @location(0) tex_coords: vec2, +} + +@group(0) @binding(0) +var color_texture: texture_2d; +@group(0) @binding(1) +var color_sampler: sampler; +@group(1) @binding(0) +var uniforms: Uniforms; + +@vertex +fn vs_main(in: VertexInput) -> VertexOutput { + var out: VertexOutput; + + out.clip_position = vec4(in.position.xyz, 1f); + out.tex_coords = ((in.position.xy * 0.5f) + vec2(0.5f)); + let _e15 = out; + return _e15; +} + +@fragment +fn fs_main(in_1: VertexOutput) -> @location(0) vec4 { + let _e4 = textureSample(color_texture, color_sampler, in_1.tex_coords); + let color = _e4.xyz; + let _e8 = uniforms.color_rgb; + return vec4((color * _e8.xyz), 1f); +} +"#; + pub fn init_composer( + entry_dir_path: &std::path::Path, + ) -> Result { let mut composer = naga_oil::compose::Composer::default(); - composer + Ok(composer) } pub fn make_naga_module( + entry_dir_path: &std::path::Path, composer: &mut naga_oil::compose::Composer, - ) -> wgpu::naga::Module { - composer + ) -> Result { + let mut source_path = entry_dir_path.to_path_buf(); + source_path.push("triangle.wgsl"); + let source = std::fs::read_to_string(&source_path)?; + let module = composer .make_naga_module(naga_oil::compose::NagaModuleDescriptor { - source: include_str!("shader/triangle.wgsl"), - file_path: "shader/triangle.wgsl", + source: &source, + file_path: source_path.to_str().unwrap(), ..Default::default() }) - .expect("failed to build naga module") + .expect("failed to build naga module"); + Ok(module) } pub fn naga_module_to_string(module: &wgpu::naga::Module) -> String { let info = wgpu::naga::valid::Validator::new( @@ -1167,15 +1358,19 @@ pub mod triangle { ) .expect("failed to convert naga module to source") } - pub fn create_shader_module(device: &wgpu::Device) -> wgpu::ShaderModule { - let mut composer = init_composer(); - let module = make_naga_module(&mut composer); + pub fn create_shader_module_from_dir( + entry_dir_path: &std::path::Path, + device: &wgpu::Device, + ) -> Result { + let mut composer = init_composer(entry_dir_path)?; + let module = make_naga_module(entry_dir_path, &mut composer)?; let source = naga_module_to_string(&module); let source = std::borrow::Cow::Owned(source); - device + let module = device .create_shader_module(wgpu::ShaderModuleDescriptor { label: None, source: wgpu::ShaderSource::Wgsl(source), - }) + }); + Ok(module) } } diff --git a/wgsl_bindgen/Cargo.toml b/wgsl_bindgen/Cargo.toml index acd1044..a2c77f7 100644 --- a/wgsl_bindgen/Cargo.toml +++ b/wgsl_bindgen/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wgsl_bindgen" -version = "0.7.0" +version = "0.7.4" authors = ["Swoorup", "ScanMountGoat(Original)"] description = "Generate typesafe Rust bindings for wgsl shaders in wgpu" license = "MIT" diff --git a/wgsl_bindgen/src/bindgen/options.rs b/wgsl_bindgen/src/bindgen/options.rs index e722df8..11641f7 100644 --- a/wgsl_bindgen/src/bindgen/options.rs +++ b/wgsl_bindgen/src/bindgen/options.rs @@ -19,6 +19,9 @@ pub enum WgslShaderSourceType { /// Use Composer with shader path relative to executing directory, including helper functions which will be executed on runtime /// This is useful for hot-reloading UseComposerWithPath, + + /// Use both `UseSingleString` and `UseComposerWithPath` option. + UseBothComposerWithPathAndIncludeStr, } /// A struct representing a directory to scan for additional source files. @@ -258,6 +261,7 @@ impl WgslBindgenOptionBuilder { self } + /// Adds custom struct mappings to the type map. pub fn custom_struct_mapping( &mut self, mappings: impl IntoIterator>, diff --git a/wgsl_bindgen/src/lib.rs b/wgsl_bindgen/src/lib.rs index 96d37f2..fb17519 100644 --- a/wgsl_bindgen/src/lib.rs +++ b/wgsl_bindgen/src/lib.rs @@ -127,7 +127,7 @@ fn create_rust_bindings( mod_builder.add(mod_name, bind_groups_module(&bind_group_data, shader_stages)); mod_builder.add(mod_name, vertex_struct_methods(naga_module)); - mod_builder.add(mod_name, compute_module(naga_module)); + mod_builder.add(mod_name, compute_module(naga_module, options.shader_source_type)); mod_builder.add(mod_name, entry_point_constants(naga_module)); mod_builder.add(mod_name, vertex_states(naga_module)); @@ -381,8 +381,18 @@ fn shader_module_using_composer( } }; - let create_shader_module = quote! { - pub fn create_shader_module(#create_shader_module_params) -> #create_shader_module_ret_ty { + let create_shader_module_fn_name = if use_composer_with_path { + quote!(create_shader_module_from_dir) + } else { + quote!(create_shader_module) + }; + + quote! { + #init_composer + #make_naga_module + #naga_module_to_string + + pub fn #create_shader_module_fn_name(#create_shader_module_params) -> #create_shader_module_ret_ty { let mut composer = #init_composer_stmt; let module = #make_naga_module_stmt; @@ -391,13 +401,6 @@ fn shader_module_using_composer( #create_shader_module_ret_stmt } - }; - - quote! { - #init_composer - #make_naga_module - #naga_module_to_string - #create_shader_module } } @@ -412,21 +415,48 @@ fn shader_module(entry: &WgslEntryResult, options: &WgslBindgenOption) -> TokenS WgslShaderSourceType::UseComposerWithPath => { shader_module_using_composer(entry, options, true) } + WgslShaderSourceType::UseBothComposerWithPathAndIncludeStr => { + let shader_module_with_include_str = shader_module_using_final_shader_string(entry); + let shader_module_with_path = shader_module_using_composer(entry, options, true); + + quote! { + #shader_module_with_include_str + #shader_module_with_path + } + } } } -fn compute_module(module: &naga::Module) -> TokenStream { +fn compute_module( + module: &naga::Module, + source_type: WgslShaderSourceType, +) -> TokenStream { let entry_points: Vec<_> = module .entry_points .iter() .filter_map(|e| { if e.stage == naga::ShaderStage::Compute { let workgroup_size_constant = workgroup_size(e); - let create_pipeline = create_compute_pipeline(e); + let create_pipeline_fns = match source_type { + WgslShaderSourceType::UseSingleString + | WgslShaderSourceType::UseComposerWithIncludeStr => { + create_compute_pipeline(e, false) + } + WgslShaderSourceType::UseComposerWithPath => create_compute_pipeline(e, true), + WgslShaderSourceType::UseBothComposerWithPathAndIncludeStr => { + let inner_with_include_str = create_compute_pipeline(e, false); + let inner_with_path = create_compute_pipeline(e, true); + + quote! { + #inner_with_include_str + #inner_with_path + } + } + }; Some(quote! { #workgroup_size_constant - #create_pipeline + #create_pipeline_fns }) } else { None @@ -446,23 +476,54 @@ fn compute_module(module: &naga::Module) -> TokenStream { } } -fn create_compute_pipeline(e: &naga::EntryPoint) -> TokenStream { +fn create_compute_pipeline(e: &naga::EntryPoint, uses_shader_path: bool) -> TokenStream { // Compute pipeline creation has few parameters and can be generated. - let pipeline_name = - Ident::new(&format!("create_{}_pipeline", e.name), Span::call_site()); + let pipeline_name_str = if uses_shader_path { + format!("create_{}_pipeline_from_dir", e.name) + } else { + format!("create_{}_pipeline", e.name) + }; + + let pipeline_name = Ident::new(&pipeline_name_str, Span::call_site()); let entry_point = &e.name; // TODO: Include a user supplied module name in the label? let label = format!("Compute Pipeline {}", e.name); + + let invoke_create_shader_module_fn = if uses_shader_path { + quote!(super::create_shader_module_from_dir(entry_dir_path, device)?) + } else { + quote!(super::create_shader_module(device)) + }; + + let params = if uses_shader_path { + quote!(entry_dir_path: &std::path::Path, device: &wgpu::Device) + } else { + quote!(device: &wgpu::Device) + }; + + let ret_type = if uses_shader_path { + quote!(Result) + } else { + quote!(wgpu::ComputePipeline) + }; + + let ret_stmt = if uses_shader_path { + quote!(Ok(pipeline)) + } else { + quote!(pipeline) + }; + quote! { - pub fn #pipeline_name(device: &wgpu::Device) -> wgpu::ComputePipeline { - let module = super::create_shader_module(device); + pub fn #pipeline_name(#params) -> #ret_type { + let module = #invoke_create_shader_module_fn; let layout = super::create_pipeline_layout(device); - device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor { + let pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor { label: Some(#label), layout: Some(&layout), module: &module, entry_point: #entry_point, - }) + }); + #ret_stmt } } } @@ -1009,7 +1070,7 @@ mod test { "#}; let module = naga::front::wgsl::parse_str(source).unwrap(); - let actual = compute_module(&module); + let actual = compute_module(&module, WgslShaderSourceType::UseSingleString); assert_tokens_eq!(quote!(), actual); } @@ -1028,7 +1089,7 @@ mod test { }; let module = naga::front::wgsl::parse_str(source).unwrap(); - let actual = compute_module(&module); + let actual = compute_module(&module, WgslShaderSourceType::UseSingleString); assert_tokens_eq!( quote! { @@ -1037,7 +1098,7 @@ mod test { pub fn create_main1_pipeline(device: &wgpu::Device) -> wgpu::ComputePipeline { let module = super::create_shader_module(device); let layout = super::create_pipeline_layout(device); - device + let pipeline = device .create_compute_pipeline( &wgpu::ComputePipelineDescriptor { label: Some("Compute Pipeline main1"), @@ -1045,13 +1106,14 @@ mod test { module: &module, entry_point: "main1", }, - ) + ); + pipeline } pub const MAIN2_WORKGROUP_SIZE: [u32; 3] = [256, 1, 1]; pub fn create_main2_pipeline(device: &wgpu::Device) -> wgpu::ComputePipeline { let module = super::create_shader_module(device); let layout = super::create_pipeline_layout(device); - device + let pipeline = device .create_compute_pipeline( &wgpu::ComputePipelineDescriptor { label: Some("Compute Pipeline main2"), @@ -1059,7 +1121,8 @@ mod test { module: &module, entry_point: "main2", }, - ) + ); + pipeline } } }, diff --git a/wgsl_bindgen/tests/bindgen_tests.rs b/wgsl_bindgen/tests/bindgen_tests.rs index f4eae78..a694c15 100644 --- a/wgsl_bindgen/tests/bindgen_tests.rs +++ b/wgsl_bindgen/tests/bindgen_tests.rs @@ -35,7 +35,7 @@ fn test_main_bindgen() -> Result<()> { .emit_rerun_if_change(false) .skip_header_comments(true) .ir_capabilities(WgslShaderIRCapabilities::PUSH_CONSTANT) - .shader_source_type(WgslShaderSourceType::UseComposerWithPath) + .shader_source_type(WgslShaderSourceType::UseBothComposerWithPathAndIncludeStr) .output_file("tests/output/bindgen_main.actual.rs") .build()? .generate() diff --git a/wgsl_bindgen/tests/output/bindgen_main.expected.rs b/wgsl_bindgen/tests/output/bindgen_main.expected.rs index e7ca36b..477d900 100644 --- a/wgsl_bindgen/tests/output/bindgen_main.expected.rs +++ b/wgsl_bindgen/tests/output/bindgen_main.expected.rs @@ -191,7 +191,7 @@ pub mod main { pub fn create_main_pipeline(device: &wgpu::Device) -> wgpu::ComputePipeline { let module = super::create_shader_module(device); let layout = super::create_pipeline_layout(device); - device + let pipeline = device .create_compute_pipeline( &wgpu::ComputePipelineDescriptor { label: Some("Compute Pipeline main"), @@ -199,7 +199,25 @@ pub mod main { module: &module, entry_point: "main", }, - ) + ); + pipeline + } + pub fn create_main_pipeline_from_dir( + entry_dir_path: &std::path::Path, + device: &wgpu::Device, + ) -> Result { + let module = super::create_shader_module_from_dir(entry_dir_path, device)?; + let layout = super::create_pipeline_layout(device); + let pipeline = device + .create_compute_pipeline( + &wgpu::ComputePipelineDescriptor { + label: Some("Compute Pipeline main"), + layout: Some(&layout), + module: &module, + entry_point: "main", + }, + ); + Ok(pipeline) } } pub const ENTRY_MAIN: &str = "main"; @@ -216,6 +234,36 @@ pub mod main { }, ) } + pub fn create_shader_module(device: &wgpu::Device) -> wgpu::ShaderModule { + let source = std::borrow::Cow::Borrowed(SHADER_STRING); + device + .create_shader_module(wgpu::ShaderModuleDescriptor { + label: None, + source: wgpu::ShaderSource::Wgsl(source), + }) + } + const SHADER_STRING: &'static str = r#" +struct Style { + color: vec4, + width: f32, +} + +@group(1) @binding(11) +var ONEX_naga_oil_mod_XMJUW4ZDJNZTXGX: f32; +@group(0) @binding(0) +var buffer: array; +var const_style: Style; + +@compute @workgroup_size(1, 1, 1) +fn main(@builtin(global_invocation_id) id: vec3) { + let _e5 = ONEX_naga_oil_mod_XMJUW4ZDJNZTXGX; + let _e11 = const_style.color.w; + let _e15 = const_style.width; + let _e17 = buffer[id.x]; + buffer[id.x] = (_e17 * (((2f * _e5) * _e11) * _e15)); + return; +} +"#; pub fn init_composer( entry_dir_path: &std::path::Path, ) -> Result { @@ -276,7 +324,7 @@ pub mod main { ) .expect("failed to convert naga module to source") } - pub fn create_shader_module( + pub fn create_shader_module_from_dir( entry_dir_path: &std::path::Path, device: &wgpu::Device, ) -> Result {