mirror of
https://github.com/italicsjenga/vello.git
synced 2025-01-09 20:31:29 +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",
|
"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]]
|
[[package]]
|
||||||
name = "nu-ansi-term"
|
name = "nu-ansi-term"
|
||||||
version = "0.46.0"
|
version = "0.46.0"
|
||||||
|
@ -4186,6 +4196,7 @@ dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"console_error_panic_hook",
|
"console_error_panic_hook",
|
||||||
"console_log",
|
"console_log",
|
||||||
|
"notify-debouncer-mini",
|
||||||
"pollster",
|
"pollster",
|
||||||
"roxmltree",
|
"roxmltree",
|
||||||
"vello",
|
"vello",
|
||||||
|
|
|
@ -29,4 +29,5 @@ moscato = { git = "https://github.com/dfrg/pinot" }
|
||||||
peniko = { git = "https://github.com/linebender/peniko" }
|
peniko = { git = "https://github.com/linebender/peniko" }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
hot_reload = []
|
||||||
buffer_labels = []
|
buffer_labels = []
|
||||||
|
|
|
@ -16,6 +16,10 @@ pollster = "0.2.5"
|
||||||
roxmltree = "0.13"
|
roxmltree = "0.13"
|
||||||
clap = { version = "4.1.0", features = ["derive"] }
|
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]
|
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||||
console_error_panic_hook = "0.1.7"
|
console_error_panic_hook = "0.1.7"
|
||||||
console_log = "0.2"
|
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 simple_text;
|
||||||
mod test_scene;
|
mod test_scene;
|
||||||
|
|
||||||
use std::{borrow::Cow, path::PathBuf};
|
use std::{borrow::Cow, path::PathBuf, time::Instant};
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use vello::{
|
use vello::{
|
||||||
|
@ -26,7 +26,13 @@ use vello::{
|
||||||
util::RenderContext,
|
util::RenderContext,
|
||||||
Renderer, Scene, SceneBuilder,
|
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)]
|
#[derive(Parser, Debug)]
|
||||||
#[command(about, long_about = None)]
|
#[command(about, long_about = None)]
|
||||||
|
@ -46,7 +52,7 @@ struct Args {
|
||||||
|
|
||||||
const TIGER: &'static str = include_str!("../../assets/Ghostscript_Tiger.svg");
|
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};
|
use winit::{event::*, event_loop::ControlFlow};
|
||||||
let mut render_cx = RenderContext::new().unwrap();
|
let mut render_cx = RenderContext::new().unwrap();
|
||||||
let size = window.inner_size();
|
let size = window.inner_size();
|
||||||
|
@ -194,16 +200,40 @@ async fn run(event_loop: EventLoop<()>, window: Window, args: Args) {
|
||||||
surface_texture.present();
|
surface_texture.present();
|
||||||
device_handle.device.poll(wgpu::Maintain::Wait);
|
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() {
|
fn main() {
|
||||||
let args = Args::parse();
|
let args = Args::parse();
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
{
|
{
|
||||||
use winit::{dpi::LogicalSize, window::WindowBuilder};
|
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()
|
let window = WindowBuilder::new()
|
||||||
.with_inner_size(LogicalSize::new(1044, 800))
|
.with_inner_size(LogicalSize::new(1044, 800))
|
||||||
.with_resizable(true)
|
.with_resizable(true)
|
||||||
|
@ -214,7 +244,7 @@ fn main() {
|
||||||
}
|
}
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[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();
|
let window = winit::window::Window::new(&event_loop).unwrap();
|
||||||
|
|
||||||
std::panic::set_hook(Box::new(console_error_panic_hook::hook));
|
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);
|
self.target = Some(target);
|
||||||
Ok(())
|
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 {
|
struct TargetTexture {
|
||||||
|
|
|
@ -31,9 +31,29 @@ pub const PATH_DRAWOBJ_WG: u32 = 256;
|
||||||
pub const CLIP_REDUCE_WG: u32 = 256;
|
pub const CLIP_REDUCE_WG: u32 = 256;
|
||||||
|
|
||||||
macro_rules! shader {
|
macro_rules! shader {
|
||||||
($name:expr) => {
|
($name:expr) => {&{
|
||||||
include_str!(concat!("../shader/", $name, ".wgsl"))
|
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 {
|
pub struct Shaders {
|
||||||
|
|
Loading…
Reference in a new issue