Skip to content
Draft
Show file tree
Hide file tree
Changes from 45 commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
ce14ac3
Initial work for macro
inner-daemons Sep 12, 2025
d974548
More work for macro that wasn't commited?
inner-daemons Sep 12, 2025
c1f9549
Worked on macro a lot
inner-daemons Sep 12, 2025
95491ce
Removed shader stage parameter from the thing
inner-daemons Sep 12, 2025
9d52aca
Updated macro slightly
inner-daemons Sep 12, 2025
80509c6
Updated some stuff
inner-daemons Sep 12, 2025
59db4c7
Made more stuff build properly
inner-daemons Sep 12, 2025
1dfc2b2
Refactored slightly
inner-daemons Sep 12, 2025
f7ecc4c
Tried to fix a compile fail
inner-daemons Sep 12, 2025
930b35b
Merge branch 'trunk' into precompiled-shaders-macro
inner-daemons Sep 12, 2025
309919a
Added precompile test
inner-daemons Sep 12, 2025
bac4a48
Added more related macros
inner-daemons Sep 12, 2025
12d5e26
Worked more on the testing and such. Currently glsl include is broken…
inner-daemons Sep 12, 2025
a28790d
Added comment explaining why compiled spv is in tree
inner-daemons Sep 12, 2025
1a1d563
Made entry point specific to each backend in passthrough
inner-daemons Sep 12, 2025
2f0293d
Enhanced macro slightly
inner-daemons Sep 12, 2025
2229f05
Worked a little more
inner-daemons Sep 12, 2025
11c9bca
Fixed vulkan backend y flip
inner-daemons Sep 12, 2025
662c76c
Allowed precompiling dxil (will break everything again)
inner-daemons Sep 12, 2025
9d45fd9
Added a comment explaining why entry point name differs by shader lan…
inner-daemons Sep 12, 2025
78250d3
Tried to fix various compile errors/typos
inner-daemons Sep 12, 2025
9c8988e
Added changelog entry
inner-daemons Sep 15, 2025
f12f60f
Updated changelog entry again for the spirv passthrough change
inner-daemons Sep 15, 2025
d1f1675
Refactored to allow compilation guards
inner-daemons Sep 15, 2025
ed9934b
Reformatted cargo.toml
inner-daemons Sep 15, 2025
be1504d
Tried updating syn and quote to see if it fixes anything
inner-daemons Sep 15, 2025
038fb11
Merge branch 'trunk' into precompiled-shaders-macro
inner-daemons Sep 15, 2025
b66e991
Attempted to fix windows compilation
inner-daemons Sep 15, 2025
5c03032
This time I think DXC compilation should work, as I actually tested it
inner-daemons Sep 15, 2025
6b34506
Fixed stupid typo that would've broken it
inner-daemons Sep 15, 2025
0915522
Added typo to ignore list
inner-daemons Sep 15, 2025
fdfa221
Updated Fo typo thing
inner-daemons Sep 15, 2025
cf98e44
Actually fixed typos this time
inner-daemons Sep 15, 2025
507f3d4
Undid bump to syn/quote that didn't fix MSRV test
inner-daemons Sep 15, 2025
d954714
Tried to fix MSRV test
inner-daemons Sep 15, 2025
f586710
Removed dxil compilation from example so that clippy won't complain w…
inner-daemons Sep 15, 2025
10f31e0
Tried to make clippy happy
inner-daemons Sep 15, 2025
9281184
Fixed dependency tests
inner-daemons Sep 15, 2025
9e41b07
Updated deps to reflect #8246
inner-daemons Sep 21, 2025
f2a3620
Merge branch 'trunk' into precompiled-shaders-macro
inner-daemons Sep 22, 2025
113a75e
Update wgpu-precompile-macro/src/lib.rs
inner-daemons Sep 24, 2025
c860d50
Merge branch 'trunk' into precompiled-shaders-macro
inner-daemons Sep 24, 2025
5b9616e
Merge branch 'trunk' into precompiled-shaders-macro
inner-daemons Sep 24, 2025
6ea7ea7
I don't even understand what happened here
inner-daemons Sep 25, 2025
a0f613b
Now removes temporary folder regardless of crashing/etc
inner-daemons Sep 25, 2025
c01e505
Update wgpu-precompile-macro/src/lib.rs
inner-daemons Sep 25, 2025
fc4e1c7
Merge branch 'trunk' into precompiled-shaders-macro
inner-daemons Sep 25, 2025
bedc327
Updated to latest dx12 mesh shader changes
inner-daemons Sep 25, 2025
dff0697
Tried to improve error messages for dxc precompiling
inner-daemons Sep 25, 2025
8344a81
Made dependency tests also check build deps
inner-daemons Sep 25, 2025
537b32e
Fixed tests sorta (broke mesh shader test most likely due to requirin…
inner-daemons Sep 25, 2025
b06ce5e
Other part to last commit
inner-daemons Sep 25, 2025
1675170
Initial work on new shaders crate that will be used by wgpu-hal, the …
inner-daemons Sep 25, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,14 +109,30 @@ 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()
})
```
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
Expand Down
13 changes: 13 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ members = [
"wgpu-macros",
"wgpu-types",
"wgpu",
"wgpu-precompile-macro",
"xtask",
]
exclude = []
Expand All @@ -47,6 +48,7 @@ default-members = [
"wgpu-macros",
"wgpu-types",
"wgpu",
"wgpu-precompile-macro",
"xtask",
]

Expand Down Expand Up @@ -80,6 +82,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 }

Expand Down Expand Up @@ -166,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 }
Expand Down
3 changes: 2 additions & 1 deletion examples/features/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 = [
Expand Down
37 changes: 25 additions & 12 deletions examples/features/src/hello_triangle/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use std::borrow::Cow;
use winit::{
event::{Event, WindowEvent},
event_loop::EventLoop,
Expand Down Expand Up @@ -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: &[],
Expand All @@ -52,18 +45,32 @@ 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: &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())],
Expand All @@ -86,7 +93,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: _,
Expand Down
5 changes: 3 additions & 2 deletions examples/features/src/mesh_shader/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
})
}
Expand Down
40 changes: 28 additions & 12 deletions player/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ impl GlobalPlay for wgc::global::Global {
Action::CreateShaderModulePassthrough {
id,
data,
entry_point,
entry_points,
label,
num_workgroups,
runtime_checks,
Expand All @@ -326,53 +326,69 @@ 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
}
});
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,
Expand Down
2 changes: 1 addition & 1 deletion tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Loading
Loading