Awesome
Rust WGPU hot-reload
<details> <summary>Demo Boids</summary> https://github.com/Azkellas/rust_wgpu_hot_reload/assets/29731210/b3befb4d-6ef5-437e-a737-b99d32dbaa65 </details> <details> <summary>Demo Raymarching</summary> https://github.com/Azkellas/rust_wgpu_hot_reload/assets/29731210/592edece-d19e-48a1-9bab-e0e711511992 </details>Hot reload both shaders and rust code while developing a WGPU application with egui integration.
Package a single binary/wasm for native and web targets.
Features
- build to native and wasm
- hot reload shader (instant), embed shader files in release and wasm builds
- hot reload rust (~4sec to rebuild and relink library)
- hot reload ui with egui integration
- shader preprocessor
#import "file.wgsl"
Running the project
To have both shader and rust hot reload, run:
cargo run --features reload
in one terminalcargo watch -w lib -x "rustc --crate-type=dylib -p lib"
in another terminal. (requires cargo-watch)
Alternatively use cargo runcc -c runcc.yml
to run both commands at the same time. (requires cargo-runcc)
You can also run cargo run
if you only care about shader hot reloading.
cargo run --release
as usual to build a single executable for your native target. For wasm
builds, see below.
Building for the web
This project targets webgpu first, and defaults to webgl when not available.
# In charge of building and hosting a webserver. Should expose to localhost:8000
cargo run-wasm [--release] [--features ...]
Wgsl preprocessing
This project contains a small homemade preprocessor for wgsl files.
It currently allows to include other files by using #import "path/to/file.wgsl"
in your shader files.
This syntax follows the bevy preprocessor syntax, which is roughly supported by wgsl-analyzer.
Using the template
The project comes with a PipelineFuncs
trait. Hopefully it should be enough for your needs. You just need to replace the current implementation with yours in lib/lib.rs
: demo_pipelines::demo::Pipeline as CurrentPipeline;
.
Project architecture
The project is divided in two crates: lib
and src
.
src
should only contain the minimal code necessary to start the application and the windowing system,
allowing a maximum of code to be hot-reloaded in lib
which is built as a dynamic library and reloaded at runtime whenever changes are saved.
Entry point functions should be written in lib/src/lib.rs
, be public and have the #[no_mangle]
attribute to be hot-reloadable.
Note that they cannot be generic. Example:
#[no_mangle]
pub fn get_pipeline_name(pipeline: &CurrentPipeline) -> String {
pipeline.get_name().to_owned()
}
Then each use of the lib
in src
should be done via the module hot_lib::library_bridge
that makes the bridge between the binary and the dynamic library.
See hot-lib-reloader-rs for more information about hot reloading Rust code and its limitations.
Troubleshooting
-
Since the wasm and native targets use different flags, switching from one target to the other takes time as many dependencies need to be rebuilt. Be careful to set rust-analyzer to the same target you're building to, otherwise they will compete against each other and create a lot of unnecessary recompiling.
-
Rust requires the dylib to be present at compile time to link, so starting the hot-reload mode with runcc can crash if the bin finishes compiling when the no dll/so is present yet. In which case you just have to let the library finish building in dynamic mode and restart runcc.
-
wgpu does not use the idiomatic rust way
Error
to handle errors. See here for more info, or have a look atshader_build.rs::ShaderBuilder::create_module
for an example.
References:
- hot-lib-reloader-rs
- wgpu and its boids example
- learn-wgpu
- egui