From b05e61f5c8ae61c9f3c7cc08cded53901ebbccfd Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 3 Apr 2024 21:07:54 +0200 Subject: Redesign `iced_wgpu` layering architecture --- wgpu/src/image/mod.rs | 571 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 571 insertions(+) create mode 100644 wgpu/src/image/mod.rs (limited to 'wgpu/src/image/mod.rs') diff --git a/wgpu/src/image/mod.rs b/wgpu/src/image/mod.rs new file mode 100644 index 00000000..88e6bdb9 --- /dev/null +++ b/wgpu/src/image/mod.rs @@ -0,0 +1,571 @@ +pub(crate) mod cache; +pub(crate) use cache::Cache; + +mod atlas; + +#[cfg(feature = "image")] +mod raster; + +#[cfg(feature = "svg")] +mod vector; + +use crate::core::image; +use crate::core::{Rectangle, Size, Transformation}; +use crate::Buffer; + +use bytemuck::{Pod, Zeroable}; +use std::mem; + +pub use crate::graphics::Image; + +pub type Batch = Vec; + +#[derive(Debug)] +pub struct Pipeline { + pipeline: wgpu::RenderPipeline, + nearest_sampler: wgpu::Sampler, + linear_sampler: wgpu::Sampler, + texture: wgpu::BindGroup, + texture_version: usize, + texture_layout: wgpu::BindGroupLayout, + constant_layout: wgpu::BindGroupLayout, + cache: cache::Shared, + layers: Vec, + prepare_layer: usize, +} + +impl Pipeline { + pub fn new( + device: &wgpu::Device, + format: wgpu::TextureFormat, + backend: wgpu::Backend, + ) -> Self { + let nearest_sampler = device.create_sampler(&wgpu::SamplerDescriptor { + address_mode_u: wgpu::AddressMode::ClampToEdge, + address_mode_v: wgpu::AddressMode::ClampToEdge, + address_mode_w: wgpu::AddressMode::ClampToEdge, + min_filter: wgpu::FilterMode::Nearest, + mag_filter: wgpu::FilterMode::Nearest, + mipmap_filter: wgpu::FilterMode::Nearest, + ..Default::default() + }); + + let linear_sampler = device.create_sampler(&wgpu::SamplerDescriptor { + address_mode_u: wgpu::AddressMode::ClampToEdge, + address_mode_v: wgpu::AddressMode::ClampToEdge, + address_mode_w: wgpu::AddressMode::ClampToEdge, + min_filter: wgpu::FilterMode::Linear, + mag_filter: wgpu::FilterMode::Linear, + mipmap_filter: wgpu::FilterMode::Linear, + ..Default::default() + }); + + let constant_layout = + device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + label: Some("iced_wgpu::image constants layout"), + entries: &[ + wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::VERTEX, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Uniform, + has_dynamic_offset: false, + min_binding_size: wgpu::BufferSize::new( + mem::size_of::() as u64, + ), + }, + count: None, + }, + wgpu::BindGroupLayoutEntry { + binding: 1, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Sampler( + wgpu::SamplerBindingType::Filtering, + ), + count: None, + }, + ], + }); + + let texture_layout = + device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + label: Some("iced_wgpu::image texture atlas layout"), + entries: &[wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Texture { + sample_type: wgpu::TextureSampleType::Float { + filterable: true, + }, + view_dimension: wgpu::TextureViewDimension::D2Array, + multisampled: false, + }, + count: None, + }], + }); + + let layout = + device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: Some("iced_wgpu::image pipeline layout"), + push_constant_ranges: &[], + bind_group_layouts: &[&constant_layout, &texture_layout], + }); + + let shader = + device.create_shader_module(wgpu::ShaderModuleDescriptor { + label: Some("iced_wgpu image shader"), + source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed( + concat!( + include_str!("../shader/vertex.wgsl"), + "\n", + include_str!("../shader/image.wgsl"), + ), + )), + }); + + let pipeline = + device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: Some("iced_wgpu::image pipeline"), + layout: Some(&layout), + vertex: wgpu::VertexState { + module: &shader, + entry_point: "vs_main", + buffers: &[wgpu::VertexBufferLayout { + array_stride: mem::size_of::() as u64, + step_mode: wgpu::VertexStepMode::Instance, + attributes: &wgpu::vertex_attr_array!( + // Position + 0 => Float32x2, + // Scale + 1 => Float32x2, + // Atlas position + 2 => Float32x2, + // Atlas scale + 3 => Float32x2, + // Layer + 4 => Sint32, + ), + }], + }, + fragment: Some(wgpu::FragmentState { + module: &shader, + entry_point: "fs_main", + targets: &[Some(wgpu::ColorTargetState { + format, + blend: Some(wgpu::BlendState { + color: wgpu::BlendComponent { + src_factor: wgpu::BlendFactor::SrcAlpha, + dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha, + operation: wgpu::BlendOperation::Add, + }, + alpha: wgpu::BlendComponent { + src_factor: wgpu::BlendFactor::One, + dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha, + operation: wgpu::BlendOperation::Add, + }, + }), + write_mask: wgpu::ColorWrites::ALL, + })], + }), + primitive: wgpu::PrimitiveState { + topology: wgpu::PrimitiveTopology::TriangleList, + front_face: wgpu::FrontFace::Cw, + ..Default::default() + }, + depth_stencil: None, + multisample: wgpu::MultisampleState { + count: 1, + mask: !0, + alpha_to_coverage_enabled: false, + }, + multiview: None, + }); + + let cache = Cache::new(device, backend); + let texture = cache.create_bind_group(device, &texture_layout); + + Pipeline { + pipeline, + nearest_sampler, + linear_sampler, + texture, + texture_version: cache.layer_count(), + texture_layout, + constant_layout, + cache: cache::Shared::new(cache), + layers: Vec::new(), + prepare_layer: 0, + } + } + + pub fn cache(&self) -> &cache::Shared { + &self.cache + } + + pub fn prepare( + &mut self, + device: &wgpu::Device, + encoder: &mut wgpu::CommandEncoder, + belt: &mut wgpu::util::StagingBelt, + images: &Batch, + transformation: Transformation, + scale: f32, + ) { + let transformation = transformation * Transformation::scale(scale); + + let nearest_instances: &mut Vec = &mut Vec::new(); + let linear_instances: &mut Vec = &mut Vec::new(); + + let mut cache = self.cache.lock(); + + for image in images { + match &image { + #[cfg(feature = "image")] + Image::Raster { + handle, + filter_method, + bounds, + } => { + if let Some(atlas_entry) = + cache.upload_raster(device, encoder, handle) + { + add_instances( + [bounds.x, bounds.y], + [bounds.width, bounds.height], + atlas_entry, + match filter_method { + image::FilterMethod::Nearest => { + nearest_instances + } + image::FilterMethod::Linear => linear_instances, + }, + ); + } + } + #[cfg(not(feature = "image"))] + Image::Raster { .. } => {} + + #[cfg(feature = "svg")] + Image::Vector { + handle, + color, + bounds, + } => { + let size = [bounds.width, bounds.height]; + + if let Some(atlas_entry) = cache.upload_vector( + device, encoder, handle, *color, size, scale, + ) { + add_instances( + [bounds.x, bounds.y], + size, + atlas_entry, + nearest_instances, + ); + } + } + #[cfg(not(feature = "svg"))] + Image::Vector { .. } => {} + } + } + + if nearest_instances.is_empty() && linear_instances.is_empty() { + return; + } + + let texture_version = cache.layer_count(); + + if self.texture_version != texture_version { + log::info!("Atlas has grown. Recreating bind group..."); + + self.texture = + cache.create_bind_group(device, &self.texture_layout); + self.texture_version = texture_version; + } + + if self.layers.len() <= self.prepare_layer { + self.layers.push(Layer::new( + device, + &self.constant_layout, + &self.nearest_sampler, + &self.linear_sampler, + )); + } + + let layer = &mut self.layers[self.prepare_layer]; + + layer.prepare( + device, + encoder, + belt, + nearest_instances, + linear_instances, + transformation, + ); + + self.prepare_layer += 1; + } + + pub fn render<'a>( + &'a self, + layer: usize, + bounds: Rectangle, + render_pass: &mut wgpu::RenderPass<'a>, + ) { + if let Some(layer) = self.layers.get(layer) { + render_pass.set_pipeline(&self.pipeline); + + render_pass.set_scissor_rect( + bounds.x, + bounds.y, + bounds.width, + bounds.height, + ); + + render_pass.set_bind_group(1, &self.texture, &[]); + + layer.render(render_pass); + } + } + + pub fn end_frame(&mut self) { + self.cache.lock().trim(); + self.prepare_layer = 0; + } +} + +#[derive(Debug)] +struct Layer { + uniforms: wgpu::Buffer, + nearest: Data, + linear: Data, +} + +impl Layer { + fn new( + device: &wgpu::Device, + constant_layout: &wgpu::BindGroupLayout, + nearest_sampler: &wgpu::Sampler, + linear_sampler: &wgpu::Sampler, + ) -> Self { + let uniforms = device.create_buffer(&wgpu::BufferDescriptor { + label: Some("iced_wgpu::image uniforms buffer"), + size: mem::size_of::() as u64, + usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, + mapped_at_creation: false, + }); + + let nearest = + Data::new(device, constant_layout, nearest_sampler, &uniforms); + + let linear = + Data::new(device, constant_layout, linear_sampler, &uniforms); + + Self { + uniforms, + nearest, + linear, + } + } + + fn prepare( + &mut self, + device: &wgpu::Device, + encoder: &mut wgpu::CommandEncoder, + belt: &mut wgpu::util::StagingBelt, + nearest_instances: &[Instance], + linear_instances: &[Instance], + transformation: Transformation, + ) { + let uniforms = Uniforms { + transform: transformation.into(), + }; + + let bytes = bytemuck::bytes_of(&uniforms); + + belt.write_buffer( + encoder, + &self.uniforms, + 0, + (bytes.len() as u64).try_into().expect("Sized uniforms"), + device, + ) + .copy_from_slice(bytes); + + self.nearest + .upload(device, encoder, belt, nearest_instances); + + self.linear.upload(device, encoder, belt, linear_instances); + } + + fn render<'a>(&'a self, render_pass: &mut wgpu::RenderPass<'a>) { + self.nearest.render(render_pass); + self.linear.render(render_pass); + } +} + +#[derive(Debug)] +struct Data { + constants: wgpu::BindGroup, + instances: Buffer, + instance_count: usize, +} + +impl Data { + pub fn new( + device: &wgpu::Device, + constant_layout: &wgpu::BindGroupLayout, + sampler: &wgpu::Sampler, + uniforms: &wgpu::Buffer, + ) -> Self { + let constants = device.create_bind_group(&wgpu::BindGroupDescriptor { + label: Some("iced_wgpu::image constants bind group"), + layout: constant_layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: wgpu::BindingResource::Buffer( + wgpu::BufferBinding { + buffer: uniforms, + offset: 0, + size: None, + }, + ), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: wgpu::BindingResource::Sampler(sampler), + }, + ], + }); + + let instances = Buffer::new( + device, + "iced_wgpu::image instance buffer", + Instance::INITIAL, + wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, + ); + + Self { + constants, + instances, + instance_count: 0, + } + } + + fn upload( + &mut self, + device: &wgpu::Device, + encoder: &mut wgpu::CommandEncoder, + belt: &mut wgpu::util::StagingBelt, + instances: &[Instance], + ) { + self.instance_count = instances.len(); + + if self.instance_count == 0 { + return; + } + + let _ = self.instances.resize(device, instances.len()); + let _ = self.instances.write(device, encoder, belt, 0, instances); + } + + fn render<'a>(&'a self, render_pass: &mut wgpu::RenderPass<'a>) { + if self.instance_count == 0 { + return; + } + + render_pass.set_bind_group(0, &self.constants, &[]); + render_pass.set_vertex_buffer(0, self.instances.slice(..)); + + render_pass.draw(0..6, 0..self.instance_count as u32); + } +} + +#[repr(C)] +#[derive(Debug, Clone, Copy, Zeroable, Pod)] +struct Instance { + _position: [f32; 2], + _size: [f32; 2], + _position_in_atlas: [f32; 2], + _size_in_atlas: [f32; 2], + _layer: u32, +} + +impl Instance { + pub const INITIAL: usize = 20; +} + +#[repr(C)] +#[derive(Debug, Clone, Copy, Zeroable, Pod)] +struct Uniforms { + transform: [f32; 16], +} + +fn add_instances( + image_position: [f32; 2], + image_size: [f32; 2], + entry: &atlas::Entry, + instances: &mut Vec, +) { + match entry { + atlas::Entry::Contiguous(allocation) => { + add_instance(image_position, image_size, allocation, instances); + } + atlas::Entry::Fragmented { fragments, size } => { + let scaling_x = image_size[0] / size.width as f32; + let scaling_y = image_size[1] / size.height as f32; + + for fragment in fragments { + let allocation = &fragment.allocation; + + let [x, y] = image_position; + let (fragment_x, fragment_y) = fragment.position; + let Size { + width: fragment_width, + height: fragment_height, + } = allocation.size(); + + let position = [ + x + fragment_x as f32 * scaling_x, + y + fragment_y as f32 * scaling_y, + ]; + + let size = [ + fragment_width as f32 * scaling_x, + fragment_height as f32 * scaling_y, + ]; + + add_instance(position, size, allocation, instances); + } + } + } +} + +#[inline] +fn add_instance( + position: [f32; 2], + size: [f32; 2], + allocation: &atlas::Allocation, + instances: &mut Vec, +) { + let (x, y) = allocation.position(); + let Size { width, height } = allocation.size(); + let layer = allocation.layer(); + + let instance = Instance { + _position: position, + _size: size, + _position_in_atlas: [ + (x as f32 + 0.5) / atlas::SIZE as f32, + (y as f32 + 0.5) / atlas::SIZE as f32, + ], + _size_in_atlas: [ + (width as f32 - 1.0) / atlas::SIZE as f32, + (height as f32 - 1.0) / atlas::SIZE as f32, + ], + _layer: layer as u32, + }; + + instances.push(instance); +} -- cgit From 6d3e1d835e1688fbc58622a03a784ed25ed3f0e1 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 5 Apr 2024 23:59:21 +0200 Subject: Decouple caching from layering and simplify everything --- wgpu/src/image/mod.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'wgpu/src/image/mod.rs') diff --git a/wgpu/src/image/mod.rs b/wgpu/src/image/mod.rs index 88e6bdb9..86731cbf 100644 --- a/wgpu/src/image/mod.rs +++ b/wgpu/src/image/mod.rs @@ -9,7 +9,6 @@ mod raster; #[cfg(feature = "svg")] mod vector; -use crate::core::image; use crate::core::{Rectangle, Size, Transformation}; use crate::Buffer; @@ -234,10 +233,12 @@ impl Pipeline { [bounds.width, bounds.height], atlas_entry, match filter_method { - image::FilterMethod::Nearest => { + crate::core::image::FilterMethod::Nearest => { nearest_instances } - image::FilterMethod::Linear => linear_instances, + crate::core::image::FilterMethod::Linear => { + linear_instances + } }, ); } -- cgit From 493c36ac712ef04523065b94988a88cc4db16b1a Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 24 Apr 2024 21:29:30 +0200 Subject: Make image `Cache` eviction strategy less aggressive in `iced_wgpu` Instead of trimming unconditionally at the end of a frame, we now trim the cache only when there is a cache miss. This way, images that are not visible but still a part of the layout will stay cached. Eviction will only happen when the images are not a part of the UI for two consectuive frames. --- wgpu/src/image/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'wgpu/src/image/mod.rs') diff --git a/wgpu/src/image/mod.rs b/wgpu/src/image/mod.rs index 86731cbf..8b831a3c 100644 --- a/wgpu/src/image/mod.rs +++ b/wgpu/src/image/mod.rs @@ -277,7 +277,7 @@ impl Pipeline { let texture_version = cache.layer_count(); if self.texture_version != texture_version { - log::info!("Atlas has grown. Recreating bind group..."); + log::debug!("Atlas has grown. Recreating bind group..."); self.texture = cache.create_bind_group(device, &self.texture_layout); -- cgit From 09a6bcfffc24f5abdc8709403bab7ae1e01563f1 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 2 May 2024 13:15:17 +0200 Subject: Add `Image` rotation support Co-authored-by: DKolter <68352124+DKolter@users.noreply.github.com> --- wgpu/src/image/mod.rs | 65 ++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 56 insertions(+), 9 deletions(-) (limited to 'wgpu/src/image/mod.rs') diff --git a/wgpu/src/image/mod.rs b/wgpu/src/image/mod.rs index 8b831a3c..69f8a8ca 100644 --- a/wgpu/src/image/mod.rs +++ b/wgpu/src/image/mod.rs @@ -135,14 +135,20 @@ impl Pipeline { attributes: &wgpu::vertex_attr_array!( // Position 0 => Float32x2, - // Scale + // Center 1 => Float32x2, - // Atlas position + // Image size 2 => Float32x2, + // Rotation + 3 => Float32, + // Scale + 4 => Float32x2, + // Atlas position + 5 => Float32x2, // Atlas scale - 3 => Float32x2, + 6 => Float32x2, // Layer - 4 => Sint32, + 7 => Sint32, ), }], }, @@ -208,9 +214,10 @@ impl Pipeline { belt: &mut wgpu::util::StagingBelt, images: &Batch, transformation: Transformation, - scale: f32, + global_scale: f32, ) { - let transformation = transformation * Transformation::scale(scale); + let transformation = + transformation * Transformation::scale(global_scale); let nearest_instances: &mut Vec = &mut Vec::new(); let linear_instances: &mut Vec = &mut Vec::new(); @@ -224,6 +231,8 @@ impl Pipeline { handle, filter_method, bounds, + rotation, + scale, } => { if let Some(atlas_entry) = cache.upload_raster(device, encoder, handle) @@ -231,6 +240,8 @@ impl Pipeline { add_instances( [bounds.x, bounds.y], [bounds.width, bounds.height], + *rotation, + [scale.width, scale.height], atlas_entry, match filter_method { crate::core::image::FilterMethod::Nearest => { @@ -251,15 +262,24 @@ impl Pipeline { handle, color, bounds, + rotation, + scale, } => { let size = [bounds.width, bounds.height]; if let Some(atlas_entry) = cache.upload_vector( - device, encoder, handle, *color, size, scale, + device, + encoder, + handle, + *color, + size, + global_scale, ) { add_instances( [bounds.x, bounds.y], size, + *rotation, + [scale.width, scale.height], atlas_entry, nearest_instances, ); @@ -487,7 +507,10 @@ impl Data { #[derive(Debug, Clone, Copy, Zeroable, Pod)] struct Instance { _position: [f32; 2], + _center: [f32; 2], _size: [f32; 2], + _rotation: f32, + _scale: [f32; 2], _position_in_atlas: [f32; 2], _size_in_atlas: [f32; 2], _layer: u32, @@ -506,12 +529,27 @@ struct Uniforms { fn add_instances( image_position: [f32; 2], image_size: [f32; 2], + rotation: f32, + scale: [f32; 2], entry: &atlas::Entry, instances: &mut Vec, ) { + let center = [ + image_position[0] + image_size[0] / 2.0, + image_position[1] + image_size[1] / 2.0, + ]; + match entry { atlas::Entry::Contiguous(allocation) => { - add_instance(image_position, image_size, allocation, instances); + add_instance( + image_position, + center, + image_size, + rotation, + scale, + allocation, + instances, + ); } atlas::Entry::Fragmented { fragments, size } => { let scaling_x = image_size[0] / size.width as f32; @@ -537,7 +575,10 @@ fn add_instances( fragment_height as f32 * scaling_y, ]; - add_instance(position, size, allocation, instances); + add_instance( + position, center, size, rotation, scale, allocation, + instances, + ); } } } @@ -546,7 +587,10 @@ fn add_instances( #[inline] fn add_instance( position: [f32; 2], + center: [f32; 2], size: [f32; 2], + rotation: f32, + scale: [f32; 2], allocation: &atlas::Allocation, instances: &mut Vec, ) { @@ -556,7 +600,10 @@ fn add_instance( let instance = Instance { _position: position, + _center: center, _size: size, + _rotation: rotation, + _scale: scale, _position_in_atlas: [ (x as f32 + 0.5) / atlas::SIZE as f32, (y as f32 + 0.5) / atlas::SIZE as f32, -- cgit From a57313b23ecb9843856ca0ea08635b6121fcb2cb Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 2 May 2024 15:21:22 +0200 Subject: Simplify image rotation API and its internals --- wgpu/src/image/mod.rs | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) (limited to 'wgpu/src/image/mod.rs') diff --git a/wgpu/src/image/mod.rs b/wgpu/src/image/mod.rs index 69f8a8ca..3ec341fc 100644 --- a/wgpu/src/image/mod.rs +++ b/wgpu/src/image/mod.rs @@ -141,14 +141,12 @@ impl Pipeline { 2 => Float32x2, // Rotation 3 => Float32, - // Scale - 4 => Float32x2, // Atlas position - 5 => Float32x2, + 4 => Float32x2, // Atlas scale - 6 => Float32x2, + 5 => Float32x2, // Layer - 7 => Sint32, + 6 => Sint32, ), }], }, @@ -232,7 +230,6 @@ impl Pipeline { filter_method, bounds, rotation, - scale, } => { if let Some(atlas_entry) = cache.upload_raster(device, encoder, handle) @@ -240,8 +237,7 @@ impl Pipeline { add_instances( [bounds.x, bounds.y], [bounds.width, bounds.height], - *rotation, - [scale.width, scale.height], + f32::from(*rotation), atlas_entry, match filter_method { crate::core::image::FilterMethod::Nearest => { @@ -263,7 +259,6 @@ impl Pipeline { color, bounds, rotation, - scale, } => { let size = [bounds.width, bounds.height]; @@ -278,8 +273,7 @@ impl Pipeline { add_instances( [bounds.x, bounds.y], size, - *rotation, - [scale.width, scale.height], + f32::from(*rotation), atlas_entry, nearest_instances, ); @@ -510,7 +504,6 @@ struct Instance { _center: [f32; 2], _size: [f32; 2], _rotation: f32, - _scale: [f32; 2], _position_in_atlas: [f32; 2], _size_in_atlas: [f32; 2], _layer: u32, @@ -530,7 +523,6 @@ fn add_instances( image_position: [f32; 2], image_size: [f32; 2], rotation: f32, - scale: [f32; 2], entry: &atlas::Entry, instances: &mut Vec, ) { @@ -546,7 +538,6 @@ fn add_instances( center, image_size, rotation, - scale, allocation, instances, ); @@ -576,8 +567,7 @@ fn add_instances( ]; add_instance( - position, center, size, rotation, scale, allocation, - instances, + position, center, size, rotation, allocation, instances, ); } } @@ -590,7 +580,6 @@ fn add_instance( center: [f32; 2], size: [f32; 2], rotation: f32, - scale: [f32; 2], allocation: &atlas::Allocation, instances: &mut Vec, ) { @@ -603,7 +592,6 @@ fn add_instance( _center: center, _size: size, _rotation: rotation, - _scale: scale, _position_in_atlas: [ (x as f32 + 0.5) / atlas::SIZE as f32, (y as f32 + 0.5) / atlas::SIZE as f32, -- cgit From 610394b6957d9424aec1c50d927e34a0fb3fe5fd Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 2 May 2024 15:28:46 +0200 Subject: Rename `global_scale` to `scale` in `wgpu::image` --- wgpu/src/image/mod.rs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) (limited to 'wgpu/src/image/mod.rs') diff --git a/wgpu/src/image/mod.rs b/wgpu/src/image/mod.rs index 3ec341fc..285eb2f6 100644 --- a/wgpu/src/image/mod.rs +++ b/wgpu/src/image/mod.rs @@ -212,10 +212,9 @@ impl Pipeline { belt: &mut wgpu::util::StagingBelt, images: &Batch, transformation: Transformation, - global_scale: f32, + scale: f32, ) { - let transformation = - transformation * Transformation::scale(global_scale); + let transformation = transformation * Transformation::scale(scale); let nearest_instances: &mut Vec = &mut Vec::new(); let linear_instances: &mut Vec = &mut Vec::new(); @@ -263,12 +262,7 @@ impl Pipeline { let size = [bounds.width, bounds.height]; if let Some(atlas_entry) = cache.upload_vector( - device, - encoder, - handle, - *color, - size, - global_scale, + device, encoder, handle, *color, size, scale, ) { add_instances( [bounds.x, bounds.y], -- cgit From fa9e1d96ea1924b51749b775ea0e67e69bc8a305 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 3 May 2024 13:25:58 +0200 Subject: Introduce dynamic `opacity` support for `Image` and `Svg` --- wgpu/src/image/mod.rs | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) (limited to 'wgpu/src/image/mod.rs') diff --git a/wgpu/src/image/mod.rs b/wgpu/src/image/mod.rs index 285eb2f6..063822aa 100644 --- a/wgpu/src/image/mod.rs +++ b/wgpu/src/image/mod.rs @@ -137,16 +137,18 @@ impl Pipeline { 0 => Float32x2, // Center 1 => Float32x2, - // Image size + // Scale 2 => Float32x2, // Rotation 3 => Float32, + // Opacity + 4 => Float32, // Atlas position - 4 => Float32x2, - // Atlas scale 5 => Float32x2, + // Atlas scale + 6 => Float32x2, // Layer - 6 => Sint32, + 7 => Sint32, ), }], }, @@ -229,6 +231,7 @@ impl Pipeline { filter_method, bounds, rotation, + opacity, } => { if let Some(atlas_entry) = cache.upload_raster(device, encoder, handle) @@ -237,6 +240,7 @@ impl Pipeline { [bounds.x, bounds.y], [bounds.width, bounds.height], f32::from(*rotation), + *opacity, atlas_entry, match filter_method { crate::core::image::FilterMethod::Nearest => { @@ -258,6 +262,7 @@ impl Pipeline { color, bounds, rotation, + opacity, } => { let size = [bounds.width, bounds.height]; @@ -268,6 +273,7 @@ impl Pipeline { [bounds.x, bounds.y], size, f32::from(*rotation), + *opacity, atlas_entry, nearest_instances, ); @@ -498,6 +504,7 @@ struct Instance { _center: [f32; 2], _size: [f32; 2], _rotation: f32, + _opacity: f32, _position_in_atlas: [f32; 2], _size_in_atlas: [f32; 2], _layer: u32, @@ -517,6 +524,7 @@ fn add_instances( image_position: [f32; 2], image_size: [f32; 2], rotation: f32, + opacity: f32, entry: &atlas::Entry, instances: &mut Vec, ) { @@ -532,6 +540,7 @@ fn add_instances( center, image_size, rotation, + opacity, allocation, instances, ); @@ -561,7 +570,8 @@ fn add_instances( ]; add_instance( - position, center, size, rotation, allocation, instances, + position, center, size, rotation, opacity, allocation, + instances, ); } } @@ -574,6 +584,7 @@ fn add_instance( center: [f32; 2], size: [f32; 2], rotation: f32, + opacity: f32, allocation: &atlas::Allocation, instances: &mut Vec, ) { @@ -586,6 +597,7 @@ fn add_instance( _center: center, _size: size, _rotation: rotation, + _opacity: opacity, _position_in_atlas: [ (x as f32 + 0.5) / atlas::SIZE as f32, (y as f32 + 0.5) / atlas::SIZE as f32, -- cgit From 547446f0de076149a4c61e6a4179308b266fd9fd Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 6 May 2024 12:14:42 +0200 Subject: Fix windows fighting over shared `image::Cache` Image caches are local to each window now. --- wgpu/src/image/mod.rs | 38 +++++++++++--------------------------- 1 file changed, 11 insertions(+), 27 deletions(-) (limited to 'wgpu/src/image/mod.rs') diff --git a/wgpu/src/image/mod.rs b/wgpu/src/image/mod.rs index 063822aa..daa2fe16 100644 --- a/wgpu/src/image/mod.rs +++ b/wgpu/src/image/mod.rs @@ -13,7 +13,9 @@ use crate::core::{Rectangle, Size, Transformation}; use crate::Buffer; use bytemuck::{Pod, Zeroable}; + use std::mem; +use std::sync::Arc; pub use crate::graphics::Image; @@ -22,13 +24,11 @@ pub type Batch = Vec; #[derive(Debug)] pub struct Pipeline { pipeline: wgpu::RenderPipeline, + backend: wgpu::Backend, nearest_sampler: wgpu::Sampler, linear_sampler: wgpu::Sampler, - texture: wgpu::BindGroup, - texture_version: usize, - texture_layout: wgpu::BindGroupLayout, + texture_layout: Arc, constant_layout: wgpu::BindGroupLayout, - cache: cache::Shared, layers: Vec, prepare_layer: usize, } @@ -186,25 +186,20 @@ impl Pipeline { multiview: None, }); - let cache = Cache::new(device, backend); - let texture = cache.create_bind_group(device, &texture_layout); - Pipeline { pipeline, + backend, nearest_sampler, linear_sampler, - texture, - texture_version: cache.layer_count(), - texture_layout, + texture_layout: Arc::new(texture_layout), constant_layout, - cache: cache::Shared::new(cache), layers: Vec::new(), prepare_layer: 0, } } - pub fn cache(&self) -> &cache::Shared { - &self.cache + pub fn create_cache(&self, device: &wgpu::Device) -> Cache { + Cache::new(device, self.backend, self.texture_layout.clone()) } pub fn prepare( @@ -212,6 +207,7 @@ impl Pipeline { device: &wgpu::Device, encoder: &mut wgpu::CommandEncoder, belt: &mut wgpu::util::StagingBelt, + cache: &mut Cache, images: &Batch, transformation: Transformation, scale: f32, @@ -221,8 +217,6 @@ impl Pipeline { let nearest_instances: &mut Vec = &mut Vec::new(); let linear_instances: &mut Vec = &mut Vec::new(); - let mut cache = self.cache.lock(); - for image in images { match &image { #[cfg(feature = "image")] @@ -288,16 +282,6 @@ impl Pipeline { return; } - let texture_version = cache.layer_count(); - - if self.texture_version != texture_version { - log::debug!("Atlas has grown. Recreating bind group..."); - - self.texture = - cache.create_bind_group(device, &self.texture_layout); - self.texture_version = texture_version; - } - if self.layers.len() <= self.prepare_layer { self.layers.push(Layer::new( device, @@ -323,6 +307,7 @@ impl Pipeline { pub fn render<'a>( &'a self, + cache: &'a Cache, layer: usize, bounds: Rectangle, render_pass: &mut wgpu::RenderPass<'a>, @@ -337,14 +322,13 @@ impl Pipeline { bounds.height, ); - render_pass.set_bind_group(1, &self.texture, &[]); + render_pass.set_bind_group(1, cache.bind_group(), &[]); layer.render(render_pass); } } pub fn end_frame(&mut self) { - self.cache.lock().trim(); self.prepare_layer = 0; } } -- cgit