mirror of
https://github.com/italicsjenga/vello.git
synced 2025-01-09 12:21:31 +11:00
Add hot reloading of shaders to the winit example (#252)
This commit is contained in:
parent
db4fc4e449
commit
ed60031185
11
Cargo.lock
generated
11
Cargo.lock
generated
|
@ -2452,6 +2452,16 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "notify-debouncer-mini"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e23e9fa24f094b143c1eb61f90ac6457de87be6987bc70746e0179f7dbc9007b"
|
||||
dependencies = [
|
||||
"crossbeam-channel",
|
||||
"notify",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nu-ansi-term"
|
||||
version = "0.46.0"
|
||||
|
@ -4186,6 +4196,7 @@ dependencies = [
|
|||
"clap",
|
||||
"console_error_panic_hook",
|
||||
"console_log",
|
||||
"notify-debouncer-mini",
|
||||
"pollster",
|
||||
"roxmltree",
|
||||
"vello",
|
||||
|
|
|
@ -29,4 +29,5 @@ moscato = { git = "https://github.com/dfrg/pinot" }
|
|||
peniko = { git = "https://github.com/linebender/peniko" }
|
||||
|
||||
[features]
|
||||
hot_reload = []
|
||||
buffer_labels = []
|
||||
|
|
|
@ -16,6 +16,10 @@ pollster = "0.2.5"
|
|||
roxmltree = "0.13"
|
||||
clap = { version = "4.1.0", features = ["derive"] }
|
||||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||
vello = { path = "../../", features = ["hot_reload"] }
|
||||
notify-debouncer-mini = "0.2.1"
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||
console_error_panic_hook = "0.1.7"
|
||||
console_log = "0.2"
|
||||
|
|
28
examples/with_winit/src/hot_reload.rs
Normal file
28
examples/with_winit/src/hot_reload.rs
Normal file
|
@ -0,0 +1,28 @@
|
|||
use std::{path::Path, time::Duration};
|
||||
|
||||
use notify_debouncer_mini::{new_debouncer, notify::*, DebounceEventResult};
|
||||
|
||||
pub(crate) fn hot_reload(mut f: impl FnMut() -> Option<()> + Send + 'static) -> impl Sized {
|
||||
let mut debouncer = new_debouncer(
|
||||
Duration::from_millis(500),
|
||||
None,
|
||||
move |res: DebounceEventResult| match res {
|
||||
Ok(_) => f().unwrap(),
|
||||
Err(errors) => errors.iter().for_each(|e| println!("Error {:?}", e)),
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
debouncer
|
||||
.watcher()
|
||||
.watch(
|
||||
dbg!(&Path::new(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("../../shader")
|
||||
.canonicalize()
|
||||
.unwrap()),
|
||||
// We currently don't support hot reloading the imports, so don't recurse into there
|
||||
RecursiveMode::NonRecursive,
|
||||
)
|
||||
.expect("Could watch shaders directory");
|
||||
debouncer
|
||||
}
|
|
@ -18,7 +18,7 @@ mod pico_svg;
|
|||
mod simple_text;
|
||||
mod test_scene;
|
||||
|
||||
use std::{borrow::Cow, path::PathBuf};
|
||||
use std::{borrow::Cow, path::PathBuf, time::Instant};
|
||||
|
||||
use clap::Parser;
|
||||
use vello::{
|
||||
|
@ -26,7 +26,13 @@ use vello::{
|
|||
util::RenderContext,
|
||||
Renderer, Scene, SceneBuilder,
|
||||
};
|
||||
use winit::{event_loop::EventLoop, window::Window};
|
||||
use winit::{
|
||||
event_loop::{EventLoop, EventLoopBuilder},
|
||||
window::Window,
|
||||
};
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
mod hot_reload;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(about, long_about = None)]
|
||||
|
@ -46,7 +52,7 @@ struct Args {
|
|||
|
||||
const TIGER: &'static str = include_str!("../../assets/Ghostscript_Tiger.svg");
|
||||
|
||||
async fn run(event_loop: EventLoop<()>, window: Window, args: Args) {
|
||||
async fn run(event_loop: EventLoop<UserEvent>, window: Window, args: Args) {
|
||||
use winit::{event::*, event_loop::ControlFlow};
|
||||
let mut render_cx = RenderContext::new().unwrap();
|
||||
let size = window.inner_size();
|
||||
|
@ -194,16 +200,40 @@ async fn run(event_loop: EventLoop<()>, window: Window, args: Args) {
|
|||
surface_texture.present();
|
||||
device_handle.device.poll(wgpu::Maintain::Wait);
|
||||
}
|
||||
Event::UserEvent(event) => match event {
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
UserEvent::HotReload => {
|
||||
let device_handle = &render_cx.devices[surface.dev_id];
|
||||
eprintln!("==============\nReloading shaders");
|
||||
let start = Instant::now();
|
||||
let result = renderer.reload_shaders(&device_handle.device);
|
||||
// We know that the only async here is actually sync, so we just block
|
||||
match pollster::block_on(result) {
|
||||
Ok(_) => eprintln!("Reloading took {:?}", start.elapsed()),
|
||||
Err(e) => eprintln!("Failed to reload shaders because of {e}"),
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {}
|
||||
});
|
||||
}
|
||||
|
||||
enum UserEvent {
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
HotReload,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args = Args::parse();
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
{
|
||||
use winit::{dpi::LogicalSize, window::WindowBuilder};
|
||||
let event_loop = EventLoop::new();
|
||||
let event_loop = EventLoopBuilder::<UserEvent>::with_user_event().build();
|
||||
|
||||
let proxy = event_loop.create_proxy();
|
||||
let _keep =
|
||||
hot_reload::hot_reload(move || proxy.send_event(UserEvent::HotReload).ok().map(drop));
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
.with_inner_size(LogicalSize::new(1044, 800))
|
||||
.with_resizable(true)
|
||||
|
@ -214,7 +244,7 @@ fn main() {
|
|||
}
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
{
|
||||
let event_loop = EventLoop::new();
|
||||
let event_loop = EventLoopBuilder::<UserEvent>::with_user_event().build();
|
||||
let window = winit::window::Window::new(&event_loop).unwrap();
|
||||
|
||||
std::panic::set_hook(Box::new(console_error_panic_hook::hook));
|
||||
|
|
15
src/lib.rs
15
src/lib.rs
|
@ -149,6 +149,21 @@ impl Renderer {
|
|||
self.target = Some(target);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Reload the shaders. This should only be used during `vello` development
|
||||
#[cfg(feature = "hot_reload")]
|
||||
pub async fn reload_shaders(&mut self, device: &Device) -> Result<()> {
|
||||
device.push_error_scope(wgpu::ErrorFilter::Validation);
|
||||
let mut engine = Engine::new();
|
||||
let shaders = shaders::full_shaders(device, &mut engine)?;
|
||||
let error = device.pop_error_scope().await;
|
||||
if let Some(error) = error {
|
||||
return Err(error.into());
|
||||
}
|
||||
self.engine = engine;
|
||||
self.shaders = shaders;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct TargetTexture {
|
||||
|
|
|
@ -31,9 +31,29 @@ pub const PATH_DRAWOBJ_WG: u32 = 256;
|
|||
pub const CLIP_REDUCE_WG: u32 = 256;
|
||||
|
||||
macro_rules! shader {
|
||||
($name:expr) => {
|
||||
include_str!(concat!("../shader/", $name, ".wgsl"))
|
||||
};
|
||||
($name:expr) => {&{
|
||||
let shader = include_str!(concat!(
|
||||
env!("CARGO_MANIFEST_DIR"),
|
||||
"/shader/",
|
||||
$name,
|
||||
".wgsl"
|
||||
));
|
||||
#[cfg(feature = "hot_reload")]
|
||||
let shader = std::fs::read_to_string(concat!(
|
||||
env!("CARGO_MANIFEST_DIR"),
|
||||
"/shader/",
|
||||
$name,
|
||||
".wgsl"
|
||||
))
|
||||
.unwrap_or_else(|e| {
|
||||
eprintln!(
|
||||
"Failed to read shader {name}, error falling back to version at compilation time. Error: {e:?}",
|
||||
name = $name
|
||||
);
|
||||
shader.to_string()
|
||||
});
|
||||
shader
|
||||
}};
|
||||
}
|
||||
|
||||
pub struct Shaders {
|
||||
|
|
Loading…
Reference in a new issue