fix: share renderer resources across example scenes
diff --git a/sparse_strips/vello_cpu/examples/wasm_cpu/src/lib.rs b/sparse_strips/vello_cpu/examples/wasm_cpu/src/lib.rs index 5a38f8f..6fa9643 100644 --- a/sparse_strips/vello_cpu/examples/wasm_cpu/src/lib.rs +++ b/sparse_strips/vello_cpu/examples/wasm_cpu/src/lib.rs
@@ -28,6 +28,7 @@ width: u32, height: u32, renderer: RenderContext, + resources: vello_cpu::Resources, pixmap: vello_common::pixmap::Pixmap, need_render: bool, canvas: HtmlCanvasElement, @@ -58,6 +59,7 @@ width, height, renderer, + resources: vello_cpu::Resources::new(), pixmap, need_render: true, canvas, @@ -69,13 +71,14 @@ return; } self.renderer.reset(); - self.scenes[self.current_scene].render(&mut self.renderer, self.transform); + self.scenes[self.current_scene].render( + &mut self.renderer, + &mut self.resources, + self.transform, + ); // Render the current scene with transform - self.renderer.render( - &mut self.pixmap, - self.scenes[self.current_scene].resources_mut(), - ); + self.renderer.render(&mut self.pixmap, &mut self.resources); let rgba_bytes = self.pixmap.data_as_u8_slice(); let image_data = web_sys::ImageData::new_with_u8_clamped_array_and_sh( wasm_bindgen::Clamped(rgba_bytes),
diff --git a/sparse_strips/vello_cpu/examples/winit/src/main.rs b/sparse_strips/vello_cpu/examples/winit/src/main.rs index cef0c18..eb30e63 100644 --- a/sparse_strips/vello_cpu/examples/winit/src/main.rs +++ b/sparse_strips/vello_cpu/examples/winit/src/main.rs
@@ -36,6 +36,7 @@ current_scene: usize, render_state: RenderState, renderer: RenderContext, + resources: vello_cpu::Resources, pixmap: Pixmap, transform: Affine, mouse_down: bool, @@ -120,6 +121,7 @@ ..Default::default() }, ), + resources: vello_cpu::Resources::new(), pixmap: Pixmap::new(width, height), transform: Affine::IDENTITY, mouse_down: false, @@ -448,12 +450,13 @@ // Render the scene self.renderer.reset(); - self.scenes[self.current_scene].render(&mut self.renderer, self.transform); - self.renderer.flush(); - self.renderer.render( - &mut self.pixmap, - self.scenes[self.current_scene].resources_mut(), + self.scenes[self.current_scene].render( + &mut self.renderer, + &mut self.resources, + self.transform, ); + self.renderer.flush(); + self.renderer.render(&mut self.pixmap, &mut self.resources); // Copy pixmap to window surface let mut buffer = surface.buffer_mut().unwrap();
diff --git a/sparse_strips/vello_example_scenes/src/lib.rs b/sparse_strips/vello_example_scenes/src/lib.rs index dcd2312..4167d57 100644 --- a/sparse_strips/vello_example_scenes/src/lib.rs +++ b/sparse_strips/vello_example_scenes/src/lib.rs
@@ -354,7 +354,6 @@ pub struct AnyScene<T: RenderingContext> { /// The render function that calls the wrapped scene's render method. render_fn: RenderFn<T>, - resources: T::Resources, /// The key handler function. key_handler_fn: KeyHandlerFn, /// The status query function. @@ -383,7 +382,6 @@ impl<T> AnyScene<T> where T: RenderingContext, - T::Resources: Default, { /// Create a new `AnyScene` from any type that implements `ExampleScene`. pub fn new<S: ExampleScene + 'static>(scene: S) -> Self { @@ -395,7 +393,6 @@ render_fn: Box::new(move |s, resources, transform| { scene.borrow_mut().render(s, resources, transform); }), - resources: T::Resources::default(), key_handler_fn: Box::new(move |key| scene_clone.borrow_mut().handle_key(key)), status_fn: Box::new(move || scene_status.borrow().status()), show_widetile_columns: false, @@ -403,9 +400,9 @@ } /// Render the scene. - pub fn render(&mut self, ctx: &mut T, root_transform: Affine) { + pub fn render(&mut self, ctx: &mut T, resources: &mut T::Resources, root_transform: Affine) { // Render the actual scene content - (self.render_fn)(ctx, &mut self.resources, root_transform); + (self.render_fn)(ctx, resources, root_transform); // Draw tile grid overlay if enabled if self.show_widetile_columns { @@ -434,11 +431,6 @@ (self.status_fn)() } - /// Access the scene-owned resources. - pub fn resources_mut(&mut self) -> &mut T::Resources { - &mut self.resources - } - /// Toggle the tile grid overlay. pub fn toggle_tile_grid(&mut self) { self.show_widetile_columns = !self.show_widetile_columns; @@ -470,10 +462,7 @@ capabilities: Capabilities, svg_paths: Option<Vec<&str>>, img_sources: Vec<ImageSource>, -) -> Box<[AnyScene<T>]> -where - T::Resources: Default, -{ +) -> Box<[AnyScene<T>]> { let mut scenes = Vec::new(); // Create SVG scenes for each provided path. @@ -528,10 +517,7 @@ pub fn get_example_scenes<T: RenderingContext + 'static>( capabilities: Capabilities, img_sources: Vec<ImageSource>, -) -> Box<[AnyScene<T>]> -where - T::Resources: Default, -{ +) -> Box<[AnyScene<T>]> { let mut scenes = vec![ AnyScene::new(svg::SvgScene::tiger()), AnyScene::new(text::TextScene::new("Hello, Vello!")),
diff --git a/sparse_strips/vello_hybrid/examples/native_webgl/Cargo.toml b/sparse_strips/vello_hybrid/examples/native_webgl/Cargo.toml index 3747465..f8227de 100644 --- a/sparse_strips/vello_hybrid/examples/native_webgl/Cargo.toml +++ b/sparse_strips/vello_hybrid/examples/native_webgl/Cargo.toml
@@ -26,6 +26,9 @@ web-sys = { workspace = true, features = [ "AddEventListenerOptions", "Window", + "Location", + "History", + "UrlSearchParams", "Document", "DomRect", "Element",
diff --git a/sparse_strips/vello_hybrid/examples/native_webgl/src/lib.rs b/sparse_strips/vello_hybrid/examples/native_webgl/src/lib.rs index cdf848e..6caef6e 100644 --- a/sparse_strips/vello_hybrid/examples/native_webgl/src/lib.rs +++ b/sparse_strips/vello_hybrid/examples/native_webgl/src/lib.rs
@@ -18,19 +18,26 @@ }; use vello_example_scenes::AnyScene; use vello_example_scenes::image::ImageScene; -use vello_hybrid::Scene; +use vello_hybrid::{RenderSettings, Scene}; use wasm_bindgen::prelude::*; use web_sys::{Event, HtmlCanvasElement, KeyboardEvent, MouseEvent, WheelEvent}; struct RendererWrapper { renderer: vello_hybrid::WebGlRenderer, + resources: vello_hybrid::Resources, } impl RendererWrapper { fn new(canvas: HtmlCanvasElement) -> Self { - let renderer = vello_hybrid::WebGlRenderer::new(&canvas); + let settings = RenderSettings::default(); + let resources = + vello_hybrid::Resources::new_with_config(settings.memory_settings.image_atlas_config); + let renderer = vello_hybrid::WebGlRenderer::new_with(&canvas, settings); - Self { renderer } + Self { + renderer, + resources, + } } } @@ -53,7 +60,6 @@ /// State that handles scene rendering and interactions struct AppState { scenes: Box<[AnyScene<Scene>]>, - uploaded_scene_images: Box<[bool]>, current_scene: usize, scene: Scene, transform: Affine, @@ -71,14 +77,13 @@ fn new(canvas: HtmlCanvasElement, scenes: Box<[AnyScene<Scene>]>) -> Self { let width = canvas.width(); let height = canvas.height(); - let uploaded_scene_images = vec![false; scenes.len()].into_boxed_slice(); + let current_scene = initial_scene_index(scenes.len()); let renderer_wrapper = RendererWrapper::new(canvas.clone()); let mut app_state = Self { scenes, - uploaded_scene_images, - current_scene: 0, + current_scene, scene: Scene::new(width as u16, height as u16), transform: Affine::IDENTITY, mouse_down: false, @@ -91,6 +96,8 @@ canvas, }; + update_page_url(app_state.current_scene); + app_state.update_title(); app_state.upload_images_to_atlas(); app_state @@ -104,7 +111,11 @@ self.scene.reset(); // Render the current scene with transform - self.scenes[self.current_scene].render(&mut self.scene, self.transform); + self.scenes[self.current_scene].render( + &mut self.scene, + &mut self.renderer_wrapper.resources, + self.transform, + ); let render_size = vello_hybrid::RenderSize { width: self.width, @@ -115,7 +126,7 @@ .renderer .render( &self.scene, - self.scenes[self.current_scene].resources_mut(), + &mut self.renderer_wrapper.resources, &render_size, ) .unwrap(); @@ -135,7 +146,8 @@ fn next_scene(&mut self) { self.current_scene = (self.current_scene + 1) % self.scenes.len(); - self.upload_images_to_atlas(); + update_page_url(self.current_scene); + self.update_title(); self.transform = Affine::IDENTITY; self.need_render = true; } @@ -146,11 +158,24 @@ } else { self.current_scene - 1 }; - self.upload_images_to_atlas(); + update_page_url(self.current_scene); + self.update_title(); self.transform = Affine::IDENTITY; self.need_render = true; } + fn update_title(&self) { + web_sys::window() + .unwrap() + .document() + .unwrap() + .set_title(&format!( + "Vello Hybrid WebGL - Page {}/{}", + self.current_scene + 1, + self.scenes.len() + )); + } + fn reset_transform(&mut self) { self.transform = Affine::IDENTITY; self.need_render = true; @@ -225,24 +250,18 @@ /// Upload images to the WebGL atlas texture /// This is the WebGL analogue of the winit example's `upload_images_to_atlas` function fn upload_images_to_atlas(&mut self) { - if self.uploaded_scene_images[self.current_scene] { - return; - } - // 1st example — uploading pixmap directly to WebGL atlas let pixmap1 = ImageScene::read_flower_image(); self.renderer_wrapper .renderer - .upload_image(self.scenes[self.current_scene].resources_mut(), &pixmap1); + .upload_image(&mut self.renderer_wrapper.resources, &pixmap1); // 2nd example — uploading from a WebGL texture let pixmap2 = ImageScene::read_cowboy_image(); let texture2 = self.pixmap_to_webgl_texture(&pixmap2); self.renderer_wrapper .renderer - .upload_image(self.scenes[self.current_scene].resources_mut(), &texture2); - - self.uploaded_scene_images[self.current_scene] = true; + .upload_image(&mut self.renderer_wrapper.resources, &texture2); } /// Convert a pixmap to WebGL texture @@ -519,15 +538,39 @@ .append_child(&canvas) .unwrap(); - let mut renderer = vello_hybrid::WebGlRenderer::new(&canvas); + let RendererWrapper { + mut renderer, + mut resources, + } = RendererWrapper::new(canvas); let render_size = vello_hybrid::RenderSize { width: width as u32, height: height as u32, }; - let mut resources = vello_hybrid::Resources::new(); - renderer .render(&scene, &mut resources, &render_size) .unwrap(); } + +fn initial_scene_index(scene_count: usize) -> usize { + web_sys::window() + .and_then(|window| window.location().search().ok()) + .and_then(|search| web_sys::UrlSearchParams::new_with_str(&search).ok()) + .and_then(|params| params.get("page")) + .and_then(|page| page.parse::<usize>().ok()) + .filter(|page| (1..=scene_count).contains(page)) + .map_or(0, |page| page - 1) +} + +fn update_page_url(scene_index: usize) { + web_sys::window() + .unwrap() + .history() + .unwrap() + .replace_state_with_url( + &JsValue::NULL, + "", + Some(&format!("?page={}", scene_index + 1)), + ) + .unwrap(); +}
diff --git a/sparse_strips/vello_hybrid/examples/wgpu_webgl/Cargo.toml b/sparse_strips/vello_hybrid/examples/wgpu_webgl/Cargo.toml index 30be052..2662e5b 100644 --- a/sparse_strips/vello_hybrid/examples/wgpu_webgl/Cargo.toml +++ b/sparse_strips/vello_hybrid/examples/wgpu_webgl/Cargo.toml
@@ -25,6 +25,9 @@ wasm-bindgen-futures = { workspace = true } web-sys = { workspace = true, features = [ "Window", + "Location", + "History", + "UrlSearchParams", "Document", "Element", "HtmlElement",
diff --git a/sparse_strips/vello_hybrid/examples/wgpu_webgl/src/lib.rs b/sparse_strips/vello_hybrid/examples/wgpu_webgl/src/lib.rs index 5fba993..b9f2dd1 100644 --- a/sparse_strips/vello_hybrid/examples/wgpu_webgl/src/lib.rs +++ b/sparse_strips/vello_hybrid/examples/wgpu_webgl/src/lib.rs
@@ -35,6 +35,7 @@ struct RendererWrapper { renderer: Renderer, + resources: vello_hybrid::Resources, device: wgpu::Device, queue: wgpu::Queue, surface: wgpu::Surface<'static>, @@ -88,6 +89,12 @@ }; surface.configure(&device, &surface_config); + let settings = RenderSettings { + level: Level::try_detect().unwrap_or(Level::baseline()), + ..Default::default() + }; + let resources = + vello_hybrid::Resources::new_with_config(settings.memory_settings.image_atlas_config); let renderer = Renderer::new_with( &device, &RenderTargetConfig { @@ -95,14 +102,12 @@ width, height, }, - RenderSettings { - level: Level::try_detect().unwrap_or(Level::baseline()), - ..Default::default() - }, + settings, ); Self { renderer, + resources, device, queue, surface, @@ -127,7 +132,6 @@ /// State that handles scene rendering and interactions struct AppState { scenes: Box<[AnyScene<Scene>]>, - uploaded_scene_images: Box<[bool]>, current_scene: usize, scene: Scene, transform: Affine, @@ -144,14 +148,13 @@ async fn new(canvas: HtmlCanvasElement, scenes: Box<[AnyScene<Scene>]>) -> Self { let width = canvas.width(); let height = canvas.height(); - let uploaded_scene_images = vec![false; scenes.len()].into_boxed_slice(); + let current_scene = initial_scene_index(scenes.len()); let renderer_wrapper = RendererWrapper::new(canvas.clone()).await; let mut app_state = Self { scenes, - uploaded_scene_images, - current_scene: 0, + current_scene, scene: Scene::new(width as u16, height as u16), transform: Affine::IDENTITY, mouse_down: false, @@ -163,6 +166,8 @@ canvas, }; + update_page_url(app_state.current_scene); + app_state.update_title(); // Upload images to the WebGL atlas app_state.upload_images_to_atlas(); @@ -177,7 +182,11 @@ self.scene.reset(); // Render the current scene with transform - self.scenes[self.current_scene].render(&mut self.scene, self.transform); + self.scenes[self.current_scene].render( + &mut self.scene, + &mut self.renderer_wrapper.resources, + self.transform, + ); let render_size = vello_hybrid::RenderSize { width: self.width, @@ -210,7 +219,7 @@ .renderer .render( &self.scene, - self.scenes[self.current_scene].resources_mut(), + &mut self.renderer_wrapper.resources, &self.renderer_wrapper.device, &self.renderer_wrapper.queue, &mut encoder, @@ -240,7 +249,8 @@ fn next_scene(&mut self) { self.current_scene = (self.current_scene + 1) % self.scenes.len(); - self.upload_images_to_atlas(); + update_page_url(self.current_scene); + self.update_title(); self.transform = Affine::IDENTITY; self.need_render = true; } @@ -251,11 +261,24 @@ } else { self.current_scene - 1 }; - self.upload_images_to_atlas(); + update_page_url(self.current_scene); + self.update_title(); self.transform = Affine::IDENTITY; self.need_render = true; } + fn update_title(&self) { + web_sys::window() + .unwrap() + .document() + .unwrap() + .set_title(&format!( + "Vello Hybrid WGPU WebGL - Page {}/{}", + self.current_scene + 1, + self.scenes.len() + )); + } + fn reset_transform(&mut self) { self.transform = Affine::IDENTITY; self.need_render = true; @@ -309,10 +332,6 @@ } fn upload_images_to_atlas(&mut self) { - if self.uploaded_scene_images[self.current_scene] { - return; - } - let mut encoder = self.renderer_wrapper .device @@ -323,7 +342,7 @@ // 1st example — uploading pixmap directly to WebGL atlas let pixmap1 = ImageScene::read_flower_image(); self.renderer_wrapper.renderer.upload_image( - self.scenes[self.current_scene].resources_mut(), + &mut self.renderer_wrapper.resources, &self.renderer_wrapper.device, &self.renderer_wrapper.queue, &mut encoder, @@ -338,7 +357,7 @@ &pixmap2, ); self.renderer_wrapper.renderer.upload_image( - self.scenes[self.current_scene].resources_mut(), + &mut self.renderer_wrapper.resources, &self.renderer_wrapper.device, &self.renderer_wrapper.queue, &mut encoder, @@ -346,7 +365,6 @@ ); self.renderer_wrapper.queue.submit([encoder.finish()]); - self.uploaded_scene_images[self.current_scene] = true; } fn upload_image_to_texture( @@ -608,6 +626,7 @@ let RendererWrapper { mut renderer, + mut resources, device, queue, surface, @@ -627,7 +646,6 @@ let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); - let mut resources = vello_hybrid::Resources::new(); renderer .render( @@ -645,3 +663,26 @@ queue.submit([encoder.finish()]); surface_texture.present(); } + +fn initial_scene_index(scene_count: usize) -> usize { + web_sys::window() + .and_then(|window| window.location().search().ok()) + .and_then(|search| web_sys::UrlSearchParams::new_with_str(&search).ok()) + .and_then(|params| params.get("page")) + .and_then(|page| page.parse::<usize>().ok()) + .filter(|page| (1..=scene_count).contains(page)) + .map_or(0, |page| page - 1) +} + +fn update_page_url(scene_index: usize) { + web_sys::window() + .unwrap() + .history() + .unwrap() + .replace_state_with_url( + &JsValue::NULL, + "", + Some(&format!("?page={}", scene_index + 1)), + ) + .unwrap(); +}
diff --git a/sparse_strips/vello_hybrid/examples/winit/src/main.rs b/sparse_strips/vello_hybrid/examples/winit/src/main.rs index d59b556..70179c8 100644 --- a/sparse_strips/vello_hybrid/examples/winit/src/main.rs +++ b/sparse_strips/vello_hybrid/examples/winit/src/main.rs
@@ -15,7 +15,7 @@ use vello_example_scenes::image::ImageScene; use vello_example_scenes::spritesheet::{SPRITESHEET_TEXTURE_ID, SpritesheetScene}; use vello_example_scenes::{AnyScene, Capabilities, get_example_scenes}; -use vello_hybrid::{Pixmap, RenderSize, Renderer, Scene, TextureBindings}; +use vello_hybrid::{Pixmap, RenderSize, Renderer, Resources, Scene, TextureBindings}; use wgpu::CurrentSurfaceTexture; use winit::{ application::ApplicationHandler, @@ -32,7 +32,8 @@ scenes: Box<[AnyScene<Scene>]>, current_scene: usize, renderers: Vec<Option<Renderer>>, - uploaded_scene_images: Vec<Vec<bool>>, + resources: Vec<Option<Resources>>, + uploaded_images: Vec<bool>, spritesheet_textures: Vec<Option<wgpu::Texture>>, render_state: RenderState<'s>, scene: Scene, @@ -98,7 +99,8 @@ let mut app = App { context: RenderContext::new(), renderers: vec![], - uploaded_scene_images: vec![], + resources: vec![], + uploaded_images: vec![], spritesheet_textures: vec![], scenes, current_scene: start_scene_index, @@ -161,14 +163,15 @@ self.renderers .resize_with(self.context.devices.len(), || None); - self.uploaded_scene_images - .resize_with(self.context.devices.len(), || { - vec![false; self.scenes.len()] - }); + self.resources + .resize_with(self.context.devices.len(), || None); + self.uploaded_images + .resize(self.context.devices.len(), false); self.spritesheet_textures .resize_with(self.context.devices.len(), || None); self.renderers[surface.dev_id] .get_or_insert_with(|| create_vello_renderer(&self.context, &surface)); + self.resources[surface.dev_id].get_or_insert_with(Resources::new); self.upload_images_to_atlas(surface.dev_id); self.ensure_spritesheet_uploaded(surface.dev_id); @@ -193,8 +196,6 @@ return; } - let dev_id = surface.dev_id; - match event { WindowEvent::CloseRequested => event_loop.exit(), WindowEvent::Resized(size) => { @@ -214,13 +215,10 @@ }, .. } => { - let mut upload_images = false; - match logical_key { Key::Named(NamedKey::ArrowRight) => { self.current_scene = (self.current_scene + 1) % self.scenes.len(); self.transform = Affine::IDENTITY; - upload_images = true; window.request_redraw(); } Key::Named(NamedKey::ArrowLeft) => { @@ -230,7 +228,6 @@ self.current_scene - 1 }; self.transform = Affine::IDENTITY; - upload_images = true; window.request_redraw(); } Key::Named(NamedKey::Space) => { @@ -250,12 +247,6 @@ } _ => {} } - - // Each scene has it's own resources struct, so in case the images - // haven't been uploaded previously, we need to do it now. - if upload_images { - self.upload_images_to_atlas(dev_id); - } } WindowEvent::MouseInput { state, @@ -355,7 +346,11 @@ let render_start = Instant::now(); self.scene.set_transform(self.transform); - self.scenes[self.current_scene].render(&mut self.scene, self.transform); + self.scenes[self.current_scene].render( + &mut self.scene, + self.resources[surface.dev_id].as_mut().unwrap(), + self.transform, + ); let device_handle = &self.context.devices[surface.dev_id]; let render_size = RenderSize { @@ -401,7 +396,7 @@ .unwrap() .render( &self.scene, - self.scenes[self.current_scene].resources_mut(), + self.resources[surface.dev_id].as_mut().unwrap(), &device_handle.device, &device_handle.queue, &mut encoder, @@ -428,7 +423,7 @@ impl App<'_> { fn upload_images_to_atlas(&mut self, device_id: usize) { - if self.uploaded_scene_images[device_id][self.current_scene] { + if self.uploaded_images[device_id] { return; } @@ -443,7 +438,7 @@ // 1st example — uploading pixmap directly let pixmap1 = ImageScene::read_flower_image(); self.renderers[device_id].as_mut().unwrap().upload_image( - self.scenes[self.current_scene].resources_mut(), + self.resources[device_id].as_mut().unwrap(), &device_handle.device, &device_handle.queue, &mut encoder, @@ -455,7 +450,7 @@ let texture2 = self.upload_image_to_texture(&device_handle.device, &device_handle.queue, &pixmap2); self.renderers[device_id].as_mut().unwrap().upload_image( - self.scenes[self.current_scene].resources_mut(), + self.resources[device_id].as_mut().unwrap(), &device_handle.device, &device_handle.queue, &mut encoder, @@ -463,7 +458,7 @@ ); device_handle.queue.submit([encoder.finish()]); - self.uploaded_scene_images[device_id][self.current_scene] = true; + self.uploaded_images[device_id] = true; } fn ensure_spritesheet_uploaded(&mut self, device_id: usize) {