A bevy plugin supporting the use of rust-gpu shader crates. Part of the Bevy Rust-GPU suite.
Features include hot-reloading with metadata-based entrypoint validation, and hot-recompiling via runtime export of active entrypoints.
bevy-rust-gpu relies on rust-gpu, which is in active development.
As such, its use implies all the caveats of the above, plus the use of a custom bevy fork.
Beyond that, bevy-rust-gpu is also in active development, but has a relatively small user-facing API footprint. Major changes will be driven by development in the upstream bevy and rust-gpu crates.
In practical terms, its current state is able to support the hot-rebuild workflow depicted above, and allows for relatively complex shader implementations, such as a Rust reimplementation of bevy_pbr.
Currently, none of the Bevy Rust-GPU crates are published on crates.io; this may change as and when the major caveats are solved, but in the meantime will be hosted on github and versioned by tag.
First, add bevy-rust-gpu to your Cargo.toml:
[dependencies] bevy-rust-gpu = { git = "https://github.com/Bevy-Rust-GPU/bevy-rust-gpu", tag = "v0.4.0" }Next, implement a Material type to describe your material's bind group layout and pipeline specialization:
#[derive(Debug, Default, Copy, Clone, AsBindGroup, TypeUuid)] #[uuid = "786779ff-e3ac-4b36-ae96-f4844f8e3064"] struct MyRustGpuMaterial { #[uniform(0)] color: Vec4, } // The vertex and fragment shaders specified here can be used // as a fallback when entrypoints are unavailable // (see the documentation of bevy_rust_gpu::prelude::RustGpuSettings), // but are otherwise deferred to ShaderRef::Default, so can be left unimplemented. impl Material for MyRustGpuMaterial {}Then, implement RustGpuMaterial for your Material type.
// First, implement some marker structs to represent our shader entry points pub enum MyVertex {} impl EntryPoint for MyVertex { const NAME: EntryPointName = "vertex"; const PARAMETERS: EntryPointParameters = &[]; const CONSTANTS: EntryPointConstants = &[]; } pub enum MyFragment {} impl EntryPoint for MyFragment { const NAME: EntryPointName = "fragment"; const PARAMETERS: EntryPointParameters = &[]; const CONSTANTS: EntryPointConstants = &[]; } // Then, impl RustGpuMaterial for our material to tie them together impl RustGpuMaterial for MyRustGpuMaterial { type Vertex = MyVertex; type Fragment = MyFragment; }(See bevy_pbr_rust.rs for the bevy-pbr-rust-backed StandardMaterial reference implementation.)
Next, add RustGpuPlugin to your bevy app to configure the backend.
let mut app = App::default(); app.add_plugin(RustGpuPlugin);For each RustGpuMaterial implementor, add a RustGpuMaterialPlugin::<M> to your app to setup backend rendering machinery. This will also configure hot-reloading and hot-rebuilding if the corresponding features are enabled.
app.add_plugin(RustGpuMaterialPlugin::<MyRustGpuMaterial>::default());If using hot-rebuilding, tell the material where to export its entry points:
RustGpu::<ExampleMaterial>::export_to(ENTRY_POINTS_PATH);Rust-GPU shader assets are represented by .rust-gpu.json files. This is the combined SPIR-V binary and entry point metadata output by rust-gpu-builder, and can be hot reloaded on change via AssetServer in the same way as regular Shader assets.
Given one of these files, the last steps are to load it via AssetServer, and add it to a RustGpu material:
fn setup(materials: ResMut<Assets<RustGpu<MyRustGpuMaterial>>>) { let shader = asset_server.load::<RustGpuBuilderOutput>(SHADER_PATH); // Add it to a RustGpu material, which can be used with bevy's MaterialMeshBundle let material = materials.add(RustGpu { vertex_shader = Some(shader), fragment_shader = Some(shader), ..default() }); // Create cube mesh let mesh = meshes.add(Cube { size: 1.0 }.into()); // Spawn a mesh with our rust-gpu material commands.spawn(MaterialMeshBundle { mesh, material, ..default() }); }Enables hot-rebuilding support.
RustGpu gains a new export_to function, which will register it for entry point aggregation, and export to the provided path alongside any other materials pointing there.
This can be used in concert with the hot-reload feature, rust-gpu-builder's file watching functionality, and permutate-macro's static permutation generation to drive a hot-rebuild workflow on par with bevy's WGSL user experience:
- The bevy app loads a
RustGpumaterial, tries to specialize it, and exports the set of required entry points toentry_points.json rust-gpu-builderpicks up the change toentry_points.jsonand triggers a recompilepermutate-macroattributes in the target shader crates readentry_points.json, and conditionally generate the required entry pointsrust-gpucompiles the generated code, outputtingshader.spvandshader.spv.json- The bevy app picks up the changes to
shader.spvandshader.spv.json, hot-reloads them, and respecializes the material with the now-available entry points - Repeat as new
RustGpumaterials are loaded by the bevy app
Implements RustGpu for StandardMaterial via the MeshVertex and PbrFragment markers, which corresponding to entry points defined in bevy-pbr-rust.
