diff options
author | 2023-03-07 03:47:49 +0100 | |
---|---|---|
committer | 2023-03-07 03:47:49 +0100 | |
commit | 3a26baa564524b0f25c5cb180b592c8b004b68a9 (patch) | |
tree | c6a51e9116e7f29552130778fe071efa9b1d1262 /wgpu | |
parent | 9b4bcd287a7f4822314e158990d1dc023d5aab51 (diff) | |
download | iced-3a26baa564524b0f25c5cb180b592c8b004b68a9.tar.gz iced-3a26baa564524b0f25c5cb180b592c8b004b68a9.tar.bz2 iced-3a26baa564524b0f25c5cb180b592c8b004b68a9.zip |
Remove `image` abstractions in `iced_graphics`
Diffstat (limited to '')
-rw-r--r-- | wgpu/Cargo.toml | 20 | ||||
-rw-r--r-- | wgpu/src/backend.rs | 2 | ||||
-rw-r--r-- | wgpu/src/image.rs | 39 | ||||
-rw-r--r-- | wgpu/src/image/atlas.rs | 190 | ||||
-rw-r--r-- | wgpu/src/image/atlas/entry.rs | 6 | ||||
-rw-r--r-- | wgpu/src/image/raster.rs | 121 | ||||
-rw-r--r-- | wgpu/src/image/vector.rs (renamed from graphics/src/image/vector.rs) | 43 |
7 files changed, 256 insertions, 165 deletions
diff --git a/wgpu/Cargo.toml b/wgpu/Cargo.toml index 5601c0de..6a313d4f 100644 --- a/wgpu/Cargo.toml +++ b/wgpu/Cargo.toml @@ -8,21 +8,9 @@ license = "MIT AND OFL-1.1" repository = "https://github.com/iced-rs/iced" [features] -svg = ["iced_graphics/svg"] -image = ["iced_graphics/image"] -png = ["iced_graphics/png"] -jpeg = ["iced_graphics/jpeg"] -jpeg_rayon = ["iced_graphics/jpeg_rayon"] -gif = ["iced_graphics/gif"] -webp = ["iced_graphics/webp"] -pnm = ["iced_graphics/pnm"] -ico = ["iced_graphics/ico"] -bmp = ["iced_graphics/bmp"] -hdr = ["iced_graphics/hdr"] -dds = ["iced_graphics/dds"] -farbfeld = ["iced_graphics/farbfeld"] geometry = ["iced_graphics/geometry", "lyon"] -spirv = ["wgpu/spirv"] +image = ["iced_graphics/image"] +svg = ["resvg"] [dependencies] wgpu = "0.14" @@ -70,6 +58,10 @@ version = "0.21.3" version = "1.0" optional = true +[dependencies.resvg] +version = "0.29" +optional = true + [dependencies.tracing] version = "0.1.6" optional = true diff --git a/wgpu/src/backend.rs b/wgpu/src/backend.rs index 9c9a1b76..88c58554 100644 --- a/wgpu/src/backend.rs +++ b/wgpu/src/backend.rs @@ -119,7 +119,7 @@ impl Backend { self.triangle_pipeline.end_frame(); #[cfg(any(feature = "image", feature = "svg"))] - self.image_pipeline.end_frame(device, queue, encoder); + self.image_pipeline.end_frame(); } fn prepare_text( diff --git a/wgpu/src/image.rs b/wgpu/src/image.rs index 5eaa9a86..4163e338 100644 --- a/wgpu/src/image.rs +++ b/wgpu/src/image.rs @@ -1,5 +1,11 @@ mod atlas; +#[cfg(feature = "image")] +mod raster; + +#[cfg(feature = "svg")] +mod vector; + use atlas::Atlas; use crate::core::{Rectangle, Size}; @@ -7,12 +13,6 @@ use crate::graphics::Transformation; use crate::layer; use crate::Buffer; -#[cfg(feature = "image")] -use crate::graphics::image::raster; - -#[cfg(feature = "svg")] -use crate::graphics::image::vector; - use std::cell::RefCell; use std::mem; @@ -30,9 +30,9 @@ use tracing::info_span; #[derive(Debug)] pub struct Pipeline { #[cfg(feature = "image")] - raster_cache: RefCell<raster::Cache<Atlas>>, + raster_cache: RefCell<raster::Cache>, #[cfg(feature = "svg")] - vector_cache: RefCell<vector::Cache<Atlas>>, + vector_cache: RefCell<vector::Cache>, pipeline: wgpu::RenderPipeline, vertices: wgpu::Buffer, @@ -368,8 +368,10 @@ impl Pipeline { #[cfg(feature = "image")] layer::Image::Raster { handle, bounds } => { if let Some(atlas_entry) = raster_cache.upload( + device, + queue, + encoder, handle, - &mut (device, queue, encoder), &mut self.texture_atlas, ) { add_instances( @@ -392,11 +394,13 @@ impl Pipeline { let size = [bounds.width, bounds.height]; if let Some(atlas_entry) = vector_cache.upload( + device, + queue, + encoder, handle, *color, size, _scale, - &mut (device, queue, encoder), &mut self.texture_atlas, ) { add_instances( @@ -477,21 +481,12 @@ impl Pipeline { } } - pub fn end_frame( - &mut self, - device: &wgpu::Device, - queue: &wgpu::Queue, - encoder: &mut wgpu::CommandEncoder, - ) { + pub fn end_frame(&mut self) { #[cfg(feature = "image")] - self.raster_cache - .borrow_mut() - .trim(&mut self.texture_atlas, &mut (device, queue, encoder)); + self.raster_cache.borrow_mut().trim(&mut self.texture_atlas); #[cfg(feature = "svg")] - self.vector_cache - .borrow_mut() - .trim(&mut self.texture_atlas, &mut (device, queue, encoder)); + self.vector_cache.borrow_mut().trim(&mut self.texture_atlas); self.prepare_layer = 0; } diff --git a/wgpu/src/image/atlas.rs b/wgpu/src/image/atlas.rs index 0a17ca33..c00b8cef 100644 --- a/wgpu/src/image/atlas.rs +++ b/wgpu/src/image/atlas.rs @@ -13,7 +13,6 @@ use allocator::Allocator; pub const SIZE: u32 = 2048; use crate::core::Size; -use crate::graphics::image; use std::num::NonZeroU32; @@ -64,6 +63,97 @@ impl Atlas { self.layers.len() } + pub fn upload( + &mut self, + device: &wgpu::Device, + queue: &wgpu::Queue, + encoder: &mut wgpu::CommandEncoder, + width: u32, + height: u32, + data: &[u8], + ) -> Option<Entry> { + let entry = { + let current_size = self.layers.len(); + let entry = self.allocate(width, height)?; + + // We grow the internal texture after allocating if necessary + let new_layers = self.layers.len() - current_size; + self.grow(new_layers, device, encoder); + + entry + }; + + log::info!("Allocated atlas entry: {:?}", entry); + + // It is a webgpu requirement that: + // BufferCopyView.layout.bytes_per_row % wgpu::COPY_BYTES_PER_ROW_ALIGNMENT == 0 + // So we calculate padded_width by rounding width up to the next + // multiple of wgpu::COPY_BYTES_PER_ROW_ALIGNMENT. + let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT; + let padding = (align - (4 * width) % align) % align; + let padded_width = (4 * width + padding) as usize; + let padded_data_size = padded_width * height as usize; + + let mut padded_data = vec![0; padded_data_size]; + + for row in 0..height as usize { + let offset = row * padded_width; + + padded_data[offset..offset + 4 * width as usize].copy_from_slice( + &data[row * 4 * width as usize..(row + 1) * 4 * width as usize], + ) + } + + match &entry { + Entry::Contiguous(allocation) => { + self.upload_allocation( + &padded_data, + width, + height, + padding, + 0, + allocation, + queue, + ); + } + Entry::Fragmented { fragments, .. } => { + for fragment in fragments { + let (x, y) = fragment.position; + let offset = (y * padded_width as u32 + 4 * x) as usize; + + self.upload_allocation( + &padded_data, + width, + height, + padding, + offset, + &fragment.allocation, + queue, + ); + } + } + } + + log::info!("Current atlas: {:?}", self); + + Some(entry) + } + + pub fn remove(&mut self, entry: &Entry) { + log::info!("Removing atlas entry: {:?}", entry); + + match entry { + Entry::Contiguous(allocation) => { + self.deallocate(allocation); + } + Entry::Fragmented { fragments, .. } => { + for fragment in fragments { + self.deallocate(&fragment.allocation); + } + } + } + } + fn allocate(&mut self, width: u32, height: u32) -> Option<Entry> { // Allocate one layer if texture fits perfectly if width == SIZE && height == SIZE { @@ -296,101 +386,3 @@ impl Atlas { }); } } - -impl image::Storage for Atlas { - type Entry = Entry; - type State<'a> = ( - &'a wgpu::Device, - &'a wgpu::Queue, - &'a mut wgpu::CommandEncoder, - ); - - fn upload( - &mut self, - width: u32, - height: u32, - data: &[u8], - (device, queue, encoder): &mut Self::State<'_>, - ) -> Option<Self::Entry> { - let entry = { - let current_size = self.layers.len(); - let entry = self.allocate(width, height)?; - - // We grow the internal texture after allocating if necessary - let new_layers = self.layers.len() - current_size; - self.grow(new_layers, device, encoder); - - entry - }; - - log::info!("Allocated atlas entry: {:?}", entry); - - // It is a webgpu requirement that: - // BufferCopyView.layout.bytes_per_row % wgpu::COPY_BYTES_PER_ROW_ALIGNMENT == 0 - // So we calculate padded_width by rounding width up to the next - // multiple of wgpu::COPY_BYTES_PER_ROW_ALIGNMENT. - let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT; - let padding = (align - (4 * width) % align) % align; - let padded_width = (4 * width + padding) as usize; - let padded_data_size = padded_width * height as usize; - - let mut padded_data = vec![0; padded_data_size]; - - for row in 0..height as usize { - let offset = row * padded_width; - - padded_data[offset..offset + 4 * width as usize].copy_from_slice( - &data[row * 4 * width as usize..(row + 1) * 4 * width as usize], - ) - } - - match &entry { - Entry::Contiguous(allocation) => { - self.upload_allocation( - &padded_data, - width, - height, - padding, - 0, - allocation, - queue, - ); - } - Entry::Fragmented { fragments, .. } => { - for fragment in fragments { - let (x, y) = fragment.position; - let offset = (y * padded_width as u32 + 4 * x) as usize; - - self.upload_allocation( - &padded_data, - width, - height, - padding, - offset, - &fragment.allocation, - queue, - ); - } - } - } - - log::info!("Current atlas: {:?}", self); - - Some(entry) - } - - fn remove(&mut self, entry: &Entry, _: &mut Self::State<'_>) { - log::info!("Removing atlas entry: {:?}", entry); - - match entry { - Entry::Contiguous(allocation) => { - self.deallocate(allocation); - } - Entry::Fragmented { fragments, .. } => { - for fragment in fragments { - self.deallocate(&fragment.allocation); - } - } - } - } -} diff --git a/wgpu/src/image/atlas/entry.rs b/wgpu/src/image/atlas/entry.rs index 4b06bd95..7e4c92a2 100644 --- a/wgpu/src/image/atlas/entry.rs +++ b/wgpu/src/image/atlas/entry.rs @@ -1,5 +1,4 @@ use crate::core::Size; -use crate::graphics::image; use crate::image::atlas; #[derive(Debug)] @@ -11,8 +10,9 @@ pub enum Entry { }, } -impl image::storage::Entry for Entry { - fn size(&self) -> Size<u32> { +impl Entry { + #[cfg(feature = "image")] + pub fn size(&self) -> Size<u32> { match self { Entry::Contiguous(allocation) => allocation.size(), Entry::Fragmented { size, .. } => *size, diff --git a/wgpu/src/image/raster.rs b/wgpu/src/image/raster.rs new file mode 100644 index 00000000..9b38dce4 --- /dev/null +++ b/wgpu/src/image/raster.rs @@ -0,0 +1,121 @@ +use crate::core::image; +use crate::core::Size; +use crate::graphics; +use crate::graphics::image::image_rs; +use crate::image::atlas::{self, Atlas}; + +use std::collections::{HashMap, HashSet}; + +/// Entry in cache corresponding to an image handle +#[derive(Debug)] +pub enum Memory { + /// Image data on host + Host(image_rs::ImageBuffer<image_rs::Rgba<u8>, Vec<u8>>), + /// Storage entry + Device(atlas::Entry), + /// Image not found + NotFound, + /// Invalid image data + Invalid, +} + +impl Memory { + /// Width and height of image + pub fn dimensions(&self) -> Size<u32> { + match self { + Memory::Host(image) => { + let (width, height) = image.dimensions(); + + Size::new(width, height) + } + Memory::Device(entry) => entry.size(), + Memory::NotFound => Size::new(1, 1), + Memory::Invalid => Size::new(1, 1), + } + } +} + +/// Caches image raster data +#[derive(Debug, Default)] +pub struct Cache { + map: HashMap<u64, Memory>, + hits: HashSet<u64>, +} + +impl Cache { + /// Load image + pub fn load(&mut self, handle: &image::Handle) -> &mut Memory { + if self.contains(handle) { + return self.get(handle).unwrap(); + } + + let memory = match graphics::image::load(handle) { + Ok(image) => Memory::Host(image.to_rgba8()), + Err(image_rs::error::ImageError::IoError(_)) => Memory::NotFound, + Err(_) => Memory::Invalid, + }; + + self.insert(handle, memory); + self.get(handle).unwrap() + } + + /// Load image and upload raster data + pub fn upload( + &mut self, + device: &wgpu::Device, + queue: &wgpu::Queue, + encoder: &mut wgpu::CommandEncoder, + handle: &image::Handle, + atlas: &mut Atlas, + ) -> Option<&atlas::Entry> { + let memory = self.load(handle); + + if let Memory::Host(image) = memory { + let (width, height) = image.dimensions(); + + let entry = + atlas.upload(device, queue, encoder, width, height, image)?; + + *memory = Memory::Device(entry); + } + + if let Memory::Device(allocation) = memory { + Some(allocation) + } else { + None + } + } + + /// Trim cache misses from cache + pub fn trim(&mut self, atlas: &mut Atlas) { + let hits = &self.hits; + + self.map.retain(|k, memory| { + let retain = hits.contains(k); + + if !retain { + if let Memory::Device(entry) = memory { + atlas.remove(entry); + } + } + + retain + }); + + self.hits.clear(); + } + + fn get(&mut self, handle: &image::Handle) -> Option<&mut Memory> { + let _ = self.hits.insert(handle.id()); + + self.map.get_mut(&handle.id()) + } + + fn insert(&mut self, handle: &image::Handle, memory: Memory) { + let _ = self.map.insert(handle.id(), memory); + } + + fn contains(&self, handle: &image::Handle) -> bool { + self.map.contains_key(&handle.id()) + } +} diff --git a/graphics/src/image/vector.rs b/wgpu/src/image/vector.rs index 32729acd..3624e46b 100644 --- a/graphics/src/image/vector.rs +++ b/wgpu/src/image/vector.rs @@ -1,8 +1,6 @@ -//! Vector image loading and caching -use crate::image::Storage; - -use iced_core::svg; -use iced_core::{Color, Size}; +use crate::core::svg; +use crate::core::{Color, Size}; +use crate::image::atlas::{self, Atlas}; use resvg::tiny_skia; use resvg::usvg; @@ -32,17 +30,17 @@ impl Svg { } /// Caches svg vector and raster data -#[derive(Debug)] -pub struct Cache<T: Storage> { +#[derive(Debug, Default)] +pub struct Cache { svgs: HashMap<u64, Svg>, - rasterized: HashMap<(u64, u32, u32, ColorFilter), T::Entry>, + rasterized: HashMap<(u64, u32, u32, ColorFilter), atlas::Entry>, svg_hits: HashSet<u64>, rasterized_hits: HashSet<(u64, u32, u32, ColorFilter)>, } type ColorFilter = Option<[u8; 4]>; -impl<T: Storage> Cache<T> { +impl Cache { /// Load svg pub fn load(&mut self, handle: &svg::Handle) -> &Svg { if self.svgs.contains_key(&handle.id()) { @@ -73,13 +71,15 @@ impl<T: Storage> Cache<T> { /// Load svg and upload raster data pub fn upload( &mut self, + device: &wgpu::Device, + queue: &wgpu::Queue, + encoder: &mut wgpu::CommandEncoder, handle: &svg::Handle, color: Option<Color>, [width, height]: [f32; 2], scale: f32, - state: &mut T::State<'_>, - storage: &mut T, - ) -> Option<&T::Entry> { + atlas: &mut Atlas, + ) -> Option<&atlas::Entry> { let id = handle.id(); let (width, height) = ( @@ -136,7 +136,9 @@ impl<T: Storage> Cache<T> { }); } - let allocation = storage.upload(width, height, &rgba, state)?; + let allocation = atlas + .upload(device, queue, encoder, width, height, &rgba)?; + log::debug!("allocating {} {}x{}", id, width, height); let _ = self.svg_hits.insert(id); @@ -150,7 +152,7 @@ impl<T: Storage> Cache<T> { } /// Load svg and upload raster data - pub fn trim(&mut self, storage: &mut T, state: &mut T::State<'_>) { + pub fn trim(&mut self, atlas: &mut Atlas) { let svg_hits = &self.svg_hits; let rasterized_hits = &self.rasterized_hits; @@ -159,7 +161,7 @@ impl<T: Storage> Cache<T> { let retain = rasterized_hits.contains(k); if !retain { - storage.remove(entry, state); + atlas.remove(entry); } retain @@ -169,17 +171,6 @@ impl<T: Storage> Cache<T> { } } -impl<T: Storage> Default for Cache<T> { - fn default() -> Self { - Self { - svgs: HashMap::new(), - rasterized: HashMap::new(), - svg_hits: HashSet::new(), - rasterized_hits: HashSet::new(), - } - } -} - impl std::fmt::Debug for Svg { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { |