//! Vello (GPU) render backend for guiduck scenes. //! //! This is the only guiduck crate that touches wgpu. The window/surface //! plumbing (swapchain, blit, present) belongs to the platform shell; this //! crate's job is fragment tree → `vello::Scene` → texture. pub mod encode; pub use encode::encode_fragment_tree; pub use vello; use guiduck_scene::geom::Affine; use guiduck_scene::paint::Color; use guiduck_scene::{FragmentId, FragmentStore, RenderBackend, RenderError}; /// GPU render backend: encodes fragment trees with vello and rasterizes them /// into a configured target texture view. pub struct VelloBackend { renderer: vello::Renderer, device: wgpu::Device, queue: wgpu::Queue, scene: vello::Scene, /// What `scene` currently holds: (store id, mutation counter, root, /// scale bits). While unchanged, the whole encode is skipped and the /// scene re-rendered as is. encoded: Option<(u64, u64, FragmentId, u64)>, target: Option, base_color: Color, } impl VelloBackend { pub fn new(device: wgpu::Device, queue: wgpu::Queue) -> Result { let renderer = vello::Renderer::new(&device, vello::RendererOptions::default()) .map_err(|e| RenderError::Backend(Box::new(e)))?; Ok(Self { renderer, device, queue, scene: vello::Scene::new(), encoded: None, target: None, base_color: Color::WHITE, }) } /// Set the texture view rendered into by [`RenderBackend::render`]. The /// shell calls this whenever the surface's target texture is (re)created. pub fn set_target(&mut self, view: wgpu::TextureView) { self.target = Some(view); } /// Set the background color the target is cleared to before drawing. pub fn set_base_color(&mut self, color: Color) { self.base_color = color; } } impl RenderBackend for VelloBackend { fn render( &mut self, root: FragmentId, store: &FragmentStore, width_px: u32, height_px: u32, scale: f64, ) -> Result<(), RenderError> { let target = self .target .as_ref() .ok_or_else(|| RenderError::Backend("no target texture view configured".into()))?; let signature = ( store.store_id(), store.mutation_counter(), root, scale.to_bits(), ); if self.encoded != Some(signature) { self.encoded = None; self.scene.reset(); encode_fragment_tree(&mut self.scene, root, store, Affine::scale(scale))?; self.encoded = Some(signature); } self.renderer .render_to_texture( &self.device, &self.queue, &self.scene, target, &vello::RenderParams { base_color: self.base_color, width: width_px, height: height_px, antialiasing_method: vello::AaConfig::Area, }, ) .map_err(|e| RenderError::Backend(Box::new(e))) } }