From 09707f29fcf7fbd71570a43db214921043427c3f Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 15 Dec 2019 06:19:07 +0100 Subject: Rerasterize SVGs when resized and refactor a bit --- wgpu/src/image/vector.rs | 187 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 187 insertions(+) create mode 100644 wgpu/src/image/vector.rs (limited to 'wgpu/src/image/vector.rs') diff --git a/wgpu/src/image/vector.rs b/wgpu/src/image/vector.rs new file mode 100644 index 00000000..0894c6bc --- /dev/null +++ b/wgpu/src/image/vector.rs @@ -0,0 +1,187 @@ +use iced_native::svg; +use std::{ + collections::{HashMap, HashSet}, + rc::Rc, +}; + +pub struct Svg { + tree: resvg::usvg::Tree, +} + +impl Svg { + pub fn viewport_dimensions(&self) -> (u32, u32) { + let size = self.tree.svg_node().size; + + (size.width() as u32, size.height() as u32) + } +} + +impl std::fmt::Debug for Svg { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Svg") + } +} + +#[derive(Debug)] +pub struct Cache { + svgs: HashMap, + rasterized: HashMap<(u64, u32, u32), Rc>, + svg_hits: HashSet, + rasterized_hits: HashSet<(u64, u32, u32)>, +} + +impl Cache { + pub fn new() -> Self { + Self { + svgs: HashMap::new(), + rasterized: HashMap::new(), + svg_hits: HashSet::new(), + rasterized_hits: HashSet::new(), + } + } + + pub fn load(&mut self, handle: &svg::Handle) -> Option<&Svg> { + if self.svgs.contains_key(&handle.id()) { + return self.svgs.get(&handle.id()); + } + + let opt = resvg::Options::default(); + + match resvg::usvg::Tree::from_file(handle.path(), &opt.usvg) { + Ok(tree) => { + let _ = self.svgs.insert(handle.id(), Svg { tree }); + } + Err(_) => {} + }; + + self.svgs.get(&handle.id()) + } + + pub fn upload( + &mut self, + handle: &svg::Handle, + [width, height]: [f32; 2], + scale: f32, + device: &wgpu::Device, + encoder: &mut wgpu::CommandEncoder, + texture_layout: &wgpu::BindGroupLayout, + ) -> Option> { + let id = handle.id(); + + let (width, height) = ( + (scale * width).round() as u32, + (scale * height).round() as u32, + ); + + // TODO: Optimize! + // We currently rerasterize the SVG when its size changes. This is slow + // as heck. A GPU rasterizer like `pathfinder` may perform better. + // It would be cool to be able to smooth resize the `tiger` example. + if let Some(bind_group) = self.rasterized.get(&(id, width, height)) { + let _ = self.svg_hits.insert(id); + let _ = self.rasterized_hits.insert((id, width, height)); + + return Some(bind_group.clone()); + } + + match self.load(handle) { + Some(svg) => { + let extent = wgpu::Extent3d { + width, + height, + depth: 1, + }; + + let texture = device.create_texture(&wgpu::TextureDescriptor { + size: extent, + array_layer_count: 1, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: wgpu::TextureFormat::Bgra8UnormSrgb, + usage: wgpu::TextureUsage::COPY_DST + | wgpu::TextureUsage::SAMPLED, + }); + + let temp_buf = { + let screen_size = + resvg::ScreenSize::new(width, height).unwrap(); + + let mut canvas = resvg::raqote::DrawTarget::new( + width as i32, + height as i32, + ); + + resvg::backend_raqote::render_to_canvas( + &svg.tree, + &resvg::Options::default(), + screen_size, + &mut canvas, + ); + + let slice = canvas.get_data(); + + device + .create_buffer_mapped( + slice.len(), + wgpu::BufferUsage::COPY_SRC, + ) + .fill_from_slice(slice) + }; + + encoder.copy_buffer_to_texture( + wgpu::BufferCopyView { + buffer: &temp_buf, + offset: 0, + row_pitch: 4 * width as u32, + image_height: height as u32, + }, + wgpu::TextureCopyView { + texture: &texture, + array_layer: 0, + mip_level: 0, + origin: wgpu::Origin3d { + x: 0.0, + y: 0.0, + z: 0.0, + }, + }, + extent, + ); + + let bind_group = + device.create_bind_group(&wgpu::BindGroupDescriptor { + layout: texture_layout, + bindings: &[wgpu::Binding { + binding: 0, + resource: wgpu::BindingResource::TextureView( + &texture.create_default_view(), + ), + }], + }); + + let bind_group = Rc::new(bind_group); + + let _ = self + .rasterized + .insert((id, width, height), bind_group.clone()); + + let _ = self.svg_hits.insert(id); + let _ = self.rasterized_hits.insert((id, width, height)); + + Some(bind_group) + } + None => None, + } + } + + pub fn trim(&mut self) { + let svg_hits = &self.svg_hits; + let rasterized_hits = &self.rasterized_hits; + + self.svgs.retain(|k, _| svg_hits.contains(k)); + self.rasterized.retain(|k, _| rasterized_hits.contains(k)); + self.svg_hits.clear(); + self.rasterized_hits.clear(); + } +} -- cgit