summaryrefslogtreecommitdiffstats
path: root/wgpu/src
diff options
context:
space:
mode:
authorLibravatar Héctor Ramón Jiménez <hector0193@gmail.com>2023-03-07 03:47:49 +0100
committerLibravatar Héctor Ramón Jiménez <hector0193@gmail.com>2023-03-07 03:47:49 +0100
commit3a26baa564524b0f25c5cb180b592c8b004b68a9 (patch)
treec6a51e9116e7f29552130778fe071efa9b1d1262 /wgpu/src
parent9b4bcd287a7f4822314e158990d1dc023d5aab51 (diff)
downloadiced-3a26baa564524b0f25c5cb180b592c8b004b68a9.tar.gz
iced-3a26baa564524b0f25c5cb180b592c8b004b68a9.tar.bz2
iced-3a26baa564524b0f25c5cb180b592c8b004b68a9.zip
Remove `image` abstractions in `iced_graphics`
Diffstat (limited to '')
-rw-r--r--wgpu/src/backend.rs2
-rw-r--r--wgpu/src/image.rs39
-rw-r--r--wgpu/src/image/atlas.rs190
-rw-r--r--wgpu/src/image/atlas/entry.rs6
-rw-r--r--wgpu/src/image/raster.rs121
-rw-r--r--wgpu/src/image/vector.rs (renamed from graphics/src/image/vector.rs)43
6 files changed, 250 insertions, 151 deletions
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 {