lib.rs
raw
//! 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<wgpu::TextureView>,
base_color: Color,
}
impl VelloBackend {
pub fn new(device: wgpu::Device, queue: wgpu::Queue) -> Result<Self, RenderError> {
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)))
}
}