vello_gpu: Split the wgpu resources from the pipelines
diff --git a/vello_gpu/src/render/wgpu/mod.rs b/vello_gpu/src/render/wgpu/mod.rs index 22213d3..d2e9f85 100644 --- a/vello_gpu/src/render/wgpu/mod.rs +++ b/vello_gpu/src/render/wgpu/mod.rs
@@ -33,6 +33,7 @@ use glifo::PendingClearRect; use hashbrown::HashMap; use passes::{Programs, RendererContext}; +use resources::GpuResources; use vello_common::geometry::{RectU16, SizeU16}; use vello_common::image_cache::ImageCache; use vello_common::multi_atlas::{AtlasConfig, AtlasId}; @@ -112,8 +113,10 @@ device: Device, /// The queue of `device`. queue: Queue, - /// Programs for rendering. + /// Pipelines and bind group layouts of the render passes. programs: Programs, + /// GPU resources for rendering. + resources: GpuResources, /// Encodes scenes and schedules their rendering. scene_preparer: ScenePreparer, /// Format of the render target that the pipelines are created for. @@ -159,17 +162,24 @@ let (scene_preparer, resources) = ScenePreparer::new(&mut settings, &device_limits); let resource_texture_dimension_2d = device_limits.resource_texture_dimension_2d(); + let programs = Programs::new( + device, + render_target_config, + settings.pipeline_cache.as_ref(), + ); + let gpu_resources = GpuResources::new( + device, + &programs, + &resources.image_cache, + render_target_config, + scene_preparer.layers_config, + resource_texture_dimension_2d, + ); let renderer = Self { device: device.clone(), queue: queue.clone(), - programs: Programs::new( - device, - &resources.image_cache, - render_target_config, - scene_preparer.layers_config, - resource_texture_dimension_2d, - settings.pipeline_cache.as_ref(), - ), + programs, + resources: gpu_resources, scene_preparer, target_format: render_target_config.format, #[cfg(feature = "text")] @@ -305,11 +315,8 @@ label: Some("Render to Atlas Encoder"), }); - Programs::maybe_create_atlas_textures( - &self.device, - &mut self.programs.resources, - atlas_count, - ); + self.resources + .maybe_create_atlas_textures(&self.device, atlas_count); let (atlas_width, atlas_height) = atlas_config.atlas_size; let atlas_render_size = RenderSize { @@ -317,8 +324,7 @@ height: atlas_height, }; - let layer_view = - self.programs.resources.atlas_texture_views[atlas_id.as_u32() as usize].clone(); + let layer_view = self.resources.atlas_texture_views[atlas_id.as_u32() as usize].clone(); let dummy_image_cache = self .scene_preparer @@ -348,13 +354,8 @@ fn current_allocations(&self) -> IntermediateTextureAllocations { IntermediateTextureAllocations { - layer_pages: self - .programs - .resources - .layer_textures - .each_ref() - .map(Vec::len), - scratch: self.programs.resources.scratch_texture.is_some(), + layer_pages: self.resources.layer_textures.each_ref().map(Vec::len), + scratch: self.resources.scratch_texture.is_some(), } } @@ -373,7 +374,7 @@ texture_bindings: &TextureBindings, scene_offset: (i32, i32), ) -> Result<(), RenderError> { - self.programs.depth_cleared_this_frame = false; + self.resources.depth_cleared_this_frame = false; let current_allocations = self.current_allocations(); let schedule = self.scene_preparer.prepare( scene, @@ -385,20 +386,21 @@ scene_offset, ), depth_view.is_some(), - self.programs.resources.texture_size, + self.resources.texture_size, current_allocations, |id, image, region| texture_bindings.encode_paint(id, image, region, view.texture()), )?; - self.programs - .prepare_intermediate_textures(&self.device, &schedule); + self.resources + .prepare_intermediate_textures(&self.device, &self.programs, &schedule); // TODO: For the time being, we upload the entire alpha buffer as one big chunk. As a future // refinement, we could have a bounded alpha buffer, and break draws when the alpha // buffer fills. let draw_buffers = &self.scene_preparer.schedule_storage.buffers.draw_buffers; let strip_count = draw_buffers.opaque.strips().len() + draw_buffers.strips.len(); let strip_bytes = (strip_count * size_of::<GpuStrip>()) as u64; - self.programs.prepare( + self.resources.prepare( &self.device, + &self.programs, encoder, &mut self.scene_preparer.gradient_cache, &self.scene_preparer.encoded_paints, @@ -409,12 +411,13 @@ &self.scene_preparer.schedule_storage.filter_context, ); - self.programs + self.resources .strips_arena .begin_frame(&self.device, strip_bytes); let mut ctx = RendererContext { - programs: &mut self.programs, + programs: &self.programs, + resources: &mut self.resources, device: &self.device, encoder, view, @@ -439,7 +442,7 @@ self.scene_preparer.gradient_cache.maintain(); - self.programs + self.resources .staging_belt .belt .finish_and_recall_on_submit(encoder); @@ -503,14 +506,11 @@ ) { let image_resource = image_cache.get(image_id).expect("Image resource not found"); - Programs::maybe_create_atlas_textures( - &self.device, - &mut self.programs.resources, - image_cache.atlas_count() as u32, - ); + self.resources + .maybe_create_atlas_textures(&self.device, image_cache.atlas_count() as u32); let offset = offset_override.unwrap_or(image_resource.offset); let atlas_texture = - &self.programs.resources.atlas_textures[image_resource.atlas_id.as_u32() as usize]; + &self.resources.atlas_textures[image_resource.atlas_id.as_u32() as usize]; writer.write_to_atlas( &self.device, &self.queue, @@ -547,8 +547,7 @@ /// Returns an individual image atlas texture. pub fn atlas_texture(&self, atlas_id: AtlasId) -> &Texture { - self.programs - .resources + self.resources .atlas_textures .get(atlas_id.as_u32() as usize) .unwrap() @@ -563,8 +562,7 @@ width: u16, height: u16, ) { - let layer_view = - self.programs.resources.atlas_texture_views[atlas_id.as_u32() as usize].clone(); + let layer_view = self.resources.atlas_texture_views[atlas_id.as_u32() as usize].clone(); let mut render_pass = encoder.begin_render_pass(&RenderPassDescriptor { label: Some("Clear Atlas Region"),
diff --git a/vello_gpu/src/render/wgpu/passes/blend.rs b/vello_gpu/src/render/wgpu/passes/blend.rs index e2597f5..0bc0e31 100644 --- a/vello_gpu/src/render/wgpu/passes/blend.rs +++ b/vello_gpu/src/render/wgpu/passes/blend.rs
@@ -12,7 +12,6 @@ schedule::round::BlendOp, target::{BlendPassBindings, TextureParity}, }; -use hashbrown::hash_map::Entry; use wgpu::{ BindGroup, BindGroupLayout, Device, RenderPassColorAttachment, RenderPassDescriptor, RenderPipeline, TextureView, TextureViewDescriptor, util::DeviceExt, @@ -141,24 +140,29 @@ bindings: BlendPassBindings, ) { let texture_size = self.texture_size(); - let resources = &self.programs.resources; - let blend_layer_bind_group = match self.programs.blend_layer_bind_groups.entry(bindings) { - Entry::Occupied(entry) => entry.into_mut(), - Entry::Vacant(entry) => { - let alphas_texture_view = resources - .alphas_texture - .create_view(&TextureViewDescriptor::default()); - entry.insert(Programs::create_blend_layer_bind_group( - self.device, - &self.programs.blend.bind_group_layout, - resources.layer_views([ - bindings.layer_id(TextureParity::Even), - bindings.layer_id(TextureParity::Odd), - ]), - &alphas_texture_view, - )) - } - }; + if !self + .resources + .blend_layer_bind_groups + .contains_key(&bindings) + { + let alphas_texture_view = self + .resources + .alphas_texture + .create_view(&TextureViewDescriptor::default()); + let bind_group = Programs::create_blend_layer_bind_group( + self.device, + &self.programs.blend.bind_group_layout, + self.resources.layer_views([ + bindings.layer_id(TextureParity::Even), + bindings.layer_id(TextureParity::Odd), + ]), + &alphas_texture_view, + ); + self.resources + .blend_layer_bind_groups + .insert(bindings, bind_group); + } + let blend_layer_bind_group = &self.resources.blend_layer_bind_groups[&bindings]; self.scratch_buffers.blend_instances.clear(); @@ -191,7 +195,7 @@ let mut render_pass = self.encoder.begin_render_pass(&RenderPassDescriptor { label: Some("Blend To Scratch"), color_attachments: &[Some(RenderPassColorAttachment { - view: self.programs.resources.scratch_view(), + view: self.resources.scratch_view(), depth_slice: None, resolve_target: None, ops: wgpu::Operations { @@ -205,7 +209,7 @@ multiview_mask: None, }); render_pass.set_pipeline(&self.programs.blend.pipeline); - render_pass.set_bind_group(0, &*blend_layer_bind_group, &[]); + render_pass.set_bind_group(0, blend_layer_bind_group, &[]); render_pass.set_vertex_buffer(0, instance_buffer.slice(..)); render_pass.draw(0..4, 0..instance_count); } @@ -223,8 +227,8 @@ self.encoder, &self.programs.copy.pipeline, &self.scratch_buffers.copy_instances, - &self.programs.resources.scratch_copy_bind_group, - self.programs.resources.layer_view(bindings.target()), + &self.resources.scratch_copy_bind_group, + self.resources.layer_view(bindings.target()), "Copy Blend Scratch To Layer", ); }
diff --git a/vello_gpu/src/render/wgpu/passes/clear.rs b/vello_gpu/src/render/wgpu/passes/clear.rs index 0999fa3..0824b56 100644 --- a/vello_gpu/src/render/wgpu/passes/clear.rs +++ b/vello_gpu/src/render/wgpu/passes/clear.rs
@@ -162,7 +162,7 @@ let view = match target { DrawPassTarget::Root(_) => self.view, - DrawPassTarget::Layer(target) => self.programs.resources.layer_view(target), + DrawPassTarget::Layer(target) => self.resources.layer_view(target), }; let rects = match settings { ClearSettings::Viewport { .. } => { @@ -176,8 +176,8 @@ let (target_size, pipeline) = match target { DrawPassTarget::Root(_) => ( [ - self.programs.render_size.width, - self.programs.render_size.height, + self.resources.render_size.width, + self.resources.render_size.height, ], &self.programs.clear.root, ),
diff --git a/vello_gpu/src/render/wgpu/passes/filter.rs b/vello_gpu/src/render/wgpu/passes/filter.rs index dbfe112..f2670ae 100644 --- a/vello_gpu/src/render/wgpu/passes/filter.rs +++ b/vello_gpu/src/render/wgpu/passes/filter.rs
@@ -155,7 +155,7 @@ copy_source: BindGroup, } -impl Programs { +impl GpuResources { pub(crate) fn create_filter_data_texture(device: &Device, width: u32, height: u32) -> Texture { device.create_texture(&wgpu::TextureDescriptor { label: Some("Filter Data Texture"), @@ -191,6 +191,7 @@ pub(crate) fn maybe_resize_filter_tex( &mut self, device: &Device, + programs: &Programs, resource_texture_dimension_2d: u32, filter_context: &FilterContext, ) { @@ -200,10 +201,10 @@ return; }; debug_assert!( - self.resources.filter_data_texture.width() == resource_texture_dimension_2d, + self.filter_data_texture.width() == resource_texture_dimension_2d, "Filter texture width must match resource texture dimensions" ); - let current_filter_height = self.resources.filter_data_texture.height(); + let current_filter_height = self.filter_data_texture.height(); if required_filter_height > current_filter_height { let required_filter_size = (resource_texture_dimension_2d * required_filter_height) << 4; @@ -214,12 +215,11 @@ resource_texture_dimension_2d, required_filter_height, ); - self.resources.filter_data_texture = filter_texture; - self.resources.filter_base_bind_group = Self::create_filter_base_bind_group( + self.filter_data_texture = filter_texture; + self.filter_base_bind_group = Self::create_filter_base_bind_group( device, - &self.filter.bind_group_layout, + &programs.filter.bind_group_layout, &self - .resources .filter_data_texture .create_view(&TextureViewDescriptor::default()), ); @@ -234,7 +234,7 @@ if filter_context.is_empty() { return; } - let texture = &self.resources.filter_data_texture; + let texture = &self.filter_data_texture; let rows = filter_context.total_texels().div_ceil(texture.width()); let data = &mut self.filter_data[..(rows * texture.width() * 16) as usize]; @@ -250,9 +250,8 @@ plan: &FilterPassPlan, bindings: FilterPassBindings, ) { - let resources = &self.programs.resources; if !self - .programs + .resources .filter_pair_bind_groups .contains_key(&bindings) { @@ -262,7 +261,7 @@ TextureParity::Even => [target, temporary], TextureParity::Odd => [temporary, target], }; - let layer_views = resources.layer_views(layer_ids); + let layer_views = self.resources.layer_views(layer_ids); let inputs = layer_views.map(|view| { create_filter_input_bind_group( self.device, @@ -274,9 +273,9 @@ let copy_source = Programs::create_copy_bind_group( self.device, &self.programs.copy.bind_group_layout, - resources.layer_view(target), + self.resources.layer_view(target), ); - self.programs.filter_pair_bind_groups.insert( + self.resources.filter_pair_bind_groups.insert( bindings, FilterPairBindGroups { inputs, @@ -284,7 +283,8 @@ }, ); } - let filter_pair_bind_groups = &self.programs.filter_pair_bind_groups[&bindings]; + let resources = &*self.resources; + let filter_pair_bind_groups = &resources.filter_pair_bind_groups[&bindings]; if let Some(copy_pass) = plan.copy_pass() { encode_copy_pass(
diff --git a/vello_gpu/src/render/wgpu/passes/mod.rs b/vello_gpu/src/render/wgpu/passes/mod.rs index 08e1e55..d1c351f 100644 --- a/vello_gpu/src/render/wgpu/passes/mod.rs +++ b/vello_gpu/src/render/wgpu/passes/mod.rs
@@ -9,23 +9,20 @@ pub(crate) mod filter; pub(crate) mod strip; -use crate::draw::{EXTERNAL_TEXTURE_SLOT_COUNT, ExternalTextureBindings, ExternalTextureRun}; +use crate::draw::{ExternalTextureBindings, ExternalTextureRun}; use crate::render::wgpu::RenderTargetConfig; use crate::render::wgpu::bindings::TextureBindings; use crate::render::wgpu::passes::blend::BlendPipeline; use crate::render::wgpu::passes::clear::{ClearPipelines, clear_color}; use crate::render::wgpu::passes::copy::CopyPipeline; -use crate::render::wgpu::passes::filter::{ - FilterPairBindGroups, FilterPipeline, create_filter_original_texture_bind_group, -}; -use crate::render::wgpu::passes::strip::{StripPipelines, StripTargetKind}; -use crate::render::wgpu::resources::{GpuResources, StripBufferArena, UploadStagingBelt}; +use crate::render::wgpu::passes::filter::FilterPipeline; +use crate::render::wgpu::passes::strip::StripPipelines; +use crate::render::wgpu::resources::GpuResources; use crate::util::RangedSlice; use crate::{ - ClearSettings, GpuStrip, LayersConfig, RenderSize, TargetInit, + ClearSettings, GpuStrip, TargetInit, blend::BlendStrip, filter::FilterPassPlan, - paint::ExternalSampler, render::common::ScratchBuffers, schedule::{Backend, round::BlendOp}, target::{ @@ -33,20 +30,16 @@ RootTarget, }, }; -use alloc::vec; -use alloc::vec::Vec; use core::fmt::Debug; use hashbrown::HashMap; use vello_common::color::AlphaColor; use vello_common::geometry::{RectU16, SizeU16}; -use vello_common::image_cache::ImageCache; -use vello_common::multi_atlas::AtlasConfig; use wgpu::{ BindGroup, BindGroupLayout, ColorTargetState, ColorWrites, CommandEncoder, Device, - PipelineCompilationOptions, RenderPipeline, Sampler, TextureView, TextureViewDescriptor, + PipelineCompilationOptions, RenderPipeline, TextureView, }; -/// Defines the GPU resources and pipelines for rendering. +/// The pipelines and bind group layouts of the render passes. #[derive(Debug)] pub(crate) struct Programs { /// Bind group layouts and pipelines of the strip pass. @@ -59,44 +52,16 @@ pub(crate) blend: BlendPipeline, /// Bind group layout and pipeline of the copy pass. pub(crate) copy: CopyPipeline, - /// Whether the depth buffer has been cleared this frame. - pub(crate) depth_cleared_this_frame: bool, /// Bind group layout for encoded paints pub(crate) encoded_paints_bind_group_layout: BindGroupLayout, /// Bind group layout for gradient texture pub(crate) gradient_bind_group_layout: BindGroupLayout, - /// Native image samplers shared by external texture slots. - external_samplers: HashMap<ExternalSampler, Sampler>, - /// Strip bind group cache. - pub(crate) strip_layer_bind_groups: - HashMap<(StripTargetKind, Option<LayerTextureId>), BindGroup>, - /// Blend bind group cache. - pub(crate) blend_layer_bind_groups: HashMap<BlendPassBindings, BindGroup>, - /// Filter bind group cache. - pub(crate) filter_pair_bind_groups: HashMap<FilterPassBindings, FilterPairBindGroups>, - /// GPU resources for rendering (created during prepare) - pub(crate) resources: GpuResources, - /// Arena holding all [`GpuStrip`] data. - pub(crate) strips_arena: StripBufferArena, - /// Shared staging memory for buffer and texture uploads. - pub(crate) staging_belt: UploadStagingBelt, - /// Dimensions of the rendering target - pub(crate) render_size: RenderSize, - /// Offset of the scene within the rendering target. - pub(crate) scene_offset: (i32, i32), - /// Scratch buffer for staging encoded paints texture data. - pub(crate) encoded_paints_data: Vec<u8>, - /// Scratch buffer for staging filter data texture data. - filter_data: Vec<u8>, } impl Programs { pub(crate) fn new( device: &Device, - image_cache: &ImageCache, render_target_config: &RenderTargetConfig, - layer_config: LayersConfig, - resource_texture_dimension_2d: u32, pipeline_cache: Option<&wgpu::PipelineCache>, ) -> Self { let encoded_paints_bind_group_layout = @@ -109,173 +74,49 @@ &gradient_bind_group_layout, pipeline_cache, ); - let clear = ClearPipelines::new(device, render_target_config, pipeline_cache); - let filter = FilterPipeline::new(device, pipeline_cache); - let blend = BlendPipeline::new(device, pipeline_cache); - let copy = CopyPipeline::new(device, pipeline_cache); - - let texture_size = layer_config.min_texture_size; - - // The empty bind group needs a sampler even before any external image is drawn. - let mut external_samplers = HashMap::new(); - external_samplers.insert( - ExternalSampler::DEFAULT, - Self::create_external_sampler(device, ExternalSampler::DEFAULT), - ); - - let layer_textures: [Vec<TextureView>; 2] = core::array::from_fn(|_| Vec::new()); - let scratch_texture = None; - let placeholder_external_texture_view = Self::create_placeholder_external_texture(device); - let filter_original_bind_group = create_filter_original_texture_bind_group( - device, - &filter.input_bind_group_layouts[1], - &placeholder_external_texture_view, - ); - let layer_config_buffer = Self::create_config_buffer( - device, - u32::from(texture_size.width()), - u32::from(texture_size.height()), - resource_texture_dimension_2d, - ); - - const INITIAL_ALPHA_TEXTURE_HEIGHT: u32 = 1; - let alphas_texture = Self::create_alphas_texture( - device, - resource_texture_dimension_2d, - INITIAL_ALPHA_TEXTURE_HEIGHT, - ); - let view_config_buffer = Self::create_config_buffer( - device, - u32::from(render_target_config.width), - u32::from(render_target_config.height), - resource_texture_dimension_2d, - ); - - let AtlasConfig { - atlas_size: (atlas_width, atlas_height), - initial_atlas_count, - .. - } = image_cache.atlas_manager().config(); - let atlas_size = (*atlas_width, *atlas_height); - let atlas_textures: Vec<_> = (0..*initial_atlas_count) - .map(|_| Self::create_atlas_texture(device, *atlas_width, *atlas_height)) - .collect(); - let atlas_texture_views = atlas_textures - .iter() - .map(|texture| texture.create_view(&TextureViewDescriptor::default())) - .collect(); - let empty_external_texture_bind_group = Self::create_external_texture_bind_group( - device, - &strip.external_texture_bind_group_layout, - [&placeholder_external_texture_view; EXTERNAL_TEXTURE_SLOT_COUNT], - [&external_samplers[&ExternalSampler::DEFAULT]; EXTERNAL_TEXTURE_SLOT_COUNT], - strip - .wgpu_external_textures - .then_some(wgpu::BindingResource::TextureView( - &placeholder_external_texture_view, - )), - ); - - const INITIAL_ENCODED_PAINTS_TEXTURE_HEIGHT: u32 = 1; - let encoded_paints_data = vec![ - 0; - ((resource_texture_dimension_2d * INITIAL_ENCODED_PAINTS_TEXTURE_HEIGHT) << 4) - as usize - ]; - let encoded_paints_texture = Self::create_encoded_paints_texture( - device, - resource_texture_dimension_2d, - INITIAL_ENCODED_PAINTS_TEXTURE_HEIGHT, - ); - let encoded_paints_bind_group = Self::create_encoded_paints_bind_group( - device, - &encoded_paints_bind_group_layout, - &encoded_paints_texture.create_view(&TextureViewDescriptor::default()), - ); - - const INITIAL_GRADIENT_TEXTURE_HEIGHT: u32 = 1; - let gradient_texture = Self::create_gradient_texture( - device, - resource_texture_dimension_2d, - INITIAL_GRADIENT_TEXTURE_HEIGHT, - ); - let gradient_bind_group = Self::create_gradient_bind_group( - device, - &gradient_bind_group_layout, - &gradient_texture.create_view(&TextureViewDescriptor::default()), - ); - - // TODO: We really should deduplicate handling of this this with encoded paints texture. - const INITIAL_FILTER_TEXTURE_HEIGHT: u32 = 1; - let filter_data = vec![ - 0_u8; - ((resource_texture_dimension_2d * INITIAL_FILTER_TEXTURE_HEIGHT) << 4) - as usize - ]; - let filter_data_texture = Self::create_filter_data_texture( - device, - resource_texture_dimension_2d, - INITIAL_FILTER_TEXTURE_HEIGHT, - ); - let filter_base_bind_group = Self::create_filter_base_bind_group( - device, - &filter.bind_group_layout, - &filter_data_texture.create_view(&TextureViewDescriptor::default()), - ); - let scratch_copy_bind_group = Self::create_copy_bind_group( - device, - ©.bind_group_layout, - &placeholder_external_texture_view, - ); - - let resources = GpuResources { - layer_textures, - scratch_texture, - filter_original_bind_group, - scratch_copy_bind_group, - layer_config_buffer, - alphas_texture, - resource_texture_dimension_2d, - atlas_textures, - atlas_texture_views, - atlas_size, - placeholder_external_texture_view, - empty_external_texture_bind_group, - encoded_paints_texture, - encoded_paints_bind_group, - gradient_texture, - gradient_bind_group, - filter_data_texture, - filter_base_bind_group, - texture_size, - view_config_buffer, - }; Self { strip, - clear, - filter, - blend, - copy, - depth_cleared_this_frame: false, + clear: ClearPipelines::new(device, render_target_config, pipeline_cache), + filter: FilterPipeline::new(device, pipeline_cache), + blend: BlendPipeline::new(device, pipeline_cache), + copy: CopyPipeline::new(device, pipeline_cache), encoded_paints_bind_group_layout, gradient_bind_group_layout, - resources, - strips_arena: StripBufferArena::new(device), - staging_belt: UploadStagingBelt::new(device), - encoded_paints_data, - filter_data, - render_size: RenderSize { - width: render_target_config.width, - height: render_target_config.height, - }, - scene_offset: (0, 0), - external_samplers, - strip_layer_bind_groups: HashMap::new(), - blend_layer_bind_groups: HashMap::new(), - filter_pair_bind_groups: HashMap::new(), } } + + pub(crate) fn create_encoded_paints_bind_group_layout(device: &Device) -> BindGroupLayout { + device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + label: Some("Encoded Paints Bind Group Layout"), + entries: &[wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Texture { + sample_type: wgpu::TextureSampleType::Uint, + view_dimension: wgpu::TextureViewDimension::D2, + multisampled: false, + }, + count: None, + }], + }) + } + + pub(crate) fn create_gradient_bind_group_layout(device: &Device) -> BindGroupLayout { + device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + label: Some("Gradient Bind Group 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::D2, + multisampled: false, + }, + count: None, + }], + }) + } } /// Create a pipeline that draws instanced quads into an intermediate texture without blending. @@ -320,7 +161,8 @@ /// A struct containing references to the many objects needed to get work /// scheduled onto the GPU. pub(crate) struct RendererContext<'a> { - pub(crate) programs: &'a mut Programs, + pub(crate) programs: &'a Programs, + pub(crate) resources: &'a mut GpuResources, pub(crate) device: &'a Device, pub(crate) encoder: &'a mut CommandEncoder, pub(crate) view: &'a TextureView, @@ -359,7 +201,7 @@ } fn texture_size(&self) -> SizeU16 { - self.programs.resources.texture_size + self.resources.texture_size } }
diff --git a/vello_gpu/src/render/wgpu/passes/strip.rs b/vello_gpu/src/render/wgpu/passes/strip.rs index 9e34af9..c79d614 100644 --- a/vello_gpu/src/render/wgpu/passes/strip.rs +++ b/vello_gpu/src/render/wgpu/passes/strip.rs
@@ -7,6 +7,7 @@ use crate::render::wgpu::RenderTargetConfig; use crate::render::wgpu::bindings::TextureBinding; use crate::render::wgpu::passes::{Programs, RendererContext}; +use crate::render::wgpu::resources::GpuResources; use crate::util::RangedSlice; use crate::{ GpuStrip, @@ -322,10 +323,10 @@ fn create_run_external_texture_bind_group( &self, device: &Device, + placeholder: &TextureView, bindings: &[TextureBinding; EXTERNAL_TEXTURE_SLOT_COUNT], samplers: [&Sampler; EXTERNAL_TEXTURE_SLOT_COUNT], ) -> BindGroup { - let placeholder = &self.resources.placeholder_external_texture_view; let [binding] = bindings; let (texture_view, wgpu_external_texture) = match binding { TextureBinding::TextureView(view) => ( @@ -348,7 +349,9 @@ wgpu_external_texture, ) } +} +impl GpuResources { /// Uploads two strip slices (opaque then alpha) into the frame arena and /// returns the byte range of the uploaded data. fn upload_strip_pair( @@ -384,13 +387,14 @@ offset..offset + total_len } } + impl RendererContext<'_> { fn external_texture_bind_group_for_textures( &mut self, bindings: ExternalTextureBindings, ) -> &BindGroup { - let atlas_texture_views = &self.programs.resources.atlas_texture_views; - let placeholder = &self.programs.resources.placeholder_external_texture_view; + let atlas_texture_views = &self.resources.atlas_texture_views; + let placeholder = &self.resources.placeholder_external_texture_view; let texture_bindings = self.texture_bindings; match self.external_texture_bind_groups.entry(bindings) { Entry::Occupied(entry) => entry.into_mut(), @@ -411,14 +415,15 @@ _ => ExternalSampler::DEFAULT, }); let samplers = sampler_keys.map(|key| { - self.programs + self.resources .external_samplers .entry(key) - .or_insert_with(|| Programs::create_external_sampler(self.device, key)) + .or_insert_with(|| GpuResources::create_external_sampler(self.device, key)) .clone() }); let bind_group = self.programs.create_run_external_texture_bind_group( self.device, + placeholder, &slot_bindings, samplers.each_ref(), ); @@ -448,7 +453,7 @@ } let strips_range = - self.programs + self.resources .upload_strip_pair(self.encoder, opaque_strips, alpha_strips); let opaque_count = opaque_count as u32; let alpha_count = alpha_count as u32; @@ -459,54 +464,13 @@ // For layer targets we always want to load. wgpu::LoadOp::Load }; - let (view, config_buffer, bind_group_target) = match target { - DrawPassTarget::Root(_) => ( - self.view, - &self.programs.resources.view_config_buffer, - StripTargetKind::Root, - ), - DrawPassTarget::Layer(id) => ( - self.programs.resources.layer_view(id), - &self.programs.resources.layer_config_buffer, - StripTargetKind::Layer, - ), - }; - let bind_group_key = (bind_group_target, child_layer_texture); - if !self - .programs - .strip_layer_bind_groups - .contains_key(&bind_group_key) - { - let child_layer_view = child_layer_texture.map_or( - &self.programs.resources.placeholder_external_texture_view, - |id| self.programs.resources.layer_view(id), - ); - let alphas_texture_view = self - .programs - .resources - .alphas_texture - .create_view(&TextureViewDescriptor::default()); - let bind_group = Programs::create_strip_bind_group( - self.device, - &self.programs.strip.bind_group_layout, - &alphas_texture_view, - config_buffer, - child_layer_view, - ); - self.programs - .strip_layer_bind_groups - .insert(bind_group_key, bind_group); - } - - let bind_group = &self.programs.strip_layer_bind_groups[&bind_group_key]; - let enable_opaque = self.depth_view.is_some() && target.enable_opaque(); let depth_stencil_attachment = if enable_opaque { - let depth_load = if self.programs.depth_cleared_this_frame { + let depth_load = if self.resources.depth_cleared_this_frame { wgpu::LoadOp::Load } else { - self.programs.depth_cleared_this_frame = true; + self.resources.depth_cleared_this_frame = true; wgpu::LoadOp::Clear(1.0) }; Some(wgpu::RenderPassDepthStencilAttachment { @@ -523,6 +487,47 @@ None }; + let bind_group_target = match target { + DrawPassTarget::Root(_) => StripTargetKind::Root, + DrawPassTarget::Layer(_) => StripTargetKind::Layer, + }; + let bind_group_key = (bind_group_target, child_layer_texture); + if !self + .resources + .strip_layer_bind_groups + .contains_key(&bind_group_key) + { + let resources = &*self.resources; + let config_buffer = match target { + DrawPassTarget::Root(_) => &resources.view_config_buffer, + DrawPassTarget::Layer(_) => &resources.layer_config_buffer, + }; + let child_layer_view = child_layer_texture + .map_or(&resources.placeholder_external_texture_view, |id| { + resources.layer_view(id) + }); + let alphas_texture_view = resources + .alphas_texture + .create_view(&TextureViewDescriptor::default()); + let bind_group = Programs::create_strip_bind_group( + self.device, + &self.programs.strip.bind_group_layout, + &alphas_texture_view, + config_buffer, + child_layer_view, + ); + self.resources + .strip_layer_bind_groups + .insert(bind_group_key, bind_group); + } + + let resources = &*self.resources; + let view = match target { + DrawPassTarget::Root(_) => self.view, + DrawPassTarget::Layer(id) => resources.layer_view(id), + }; + let bind_group = &resources.strip_layer_bind_groups[&bind_group_key]; + let mut render_pass = self.encoder.begin_render_pass(&RenderPassDescriptor { label: Some("Render to Texture Pass"), color_attachments: &[Some(RenderPassColorAttachment { @@ -540,23 +545,13 @@ multiview_mask: None, }); render_pass.set_bind_group(0, bind_group, &[]); - render_pass.set_bind_group(2, &self.programs.resources.encoded_paints_bind_group, &[]); - render_pass.set_bind_group(3, &self.programs.resources.gradient_bind_group, &[]); - render_pass.set_vertex_buffer( - 0, - self.programs - .strips_arena - .buffer - .slice(strips_range.clone()), - ); + render_pass.set_bind_group(2, &resources.encoded_paints_bind_group, &[]); + render_pass.set_bind_group(3, &resources.gradient_bind_group, &[]); + render_pass.set_vertex_buffer(0, resources.strips_arena.buffer.slice(strips_range.clone())); let draw_strip_runs = |render_pass: &mut wgpu::RenderPass<'_>, first_instance, count| { if external_texture_runs.is_empty() { - render_pass.set_bind_group( - 1, - &self.programs.resources.empty_external_texture_bind_group, - &[], - ); + render_pass.set_bind_group(1, &resources.empty_external_texture_bind_group, &[]); render_pass.draw(0..4, first_instance..first_instance + count); return;
diff --git a/vello_gpu/src/render/wgpu/resources.rs b/vello_gpu/src/render/wgpu/resources.rs index 37b8e35..631a56f 100644 --- a/vello_gpu/src/render/wgpu/resources.rs +++ b/vello_gpu/src/render/wgpu/resources.rs
@@ -3,10 +3,15 @@ //! Creation and growth of the renderer's GPU resources. +use crate::draw::EXTERNAL_TEXTURE_SLOT_COUNT; +use crate::render::wgpu::RenderTargetConfig; use crate::render::wgpu::passes::Programs; -use crate::render::wgpu::passes::filter::create_filter_original_texture_bind_group; +use crate::render::wgpu::passes::filter::{ + FilterPairBindGroups, create_filter_original_texture_bind_group, +}; +use crate::render::wgpu::passes::strip::StripTargetKind; use crate::{ - RenderSize, + LayersConfig, RenderSize, filter::FilterContext, gradient_cache::GradientRampCache, paint::{ExternalSampler, SamplerAddress, SamplerFilter}, @@ -15,10 +20,14 @@ common::{GpuEncodedPaint, ScratchTexture}, }, schedule::Schedule, - target::LayerTextureId, + target::{BlendPassBindings, FilterPassBindings, LayerTextureId}, }; +use alloc::vec; use alloc::vec::Vec; use core::{fmt::Debug, num::NonZeroU64}; +use hashbrown::HashMap; +use vello_common::image_cache::ImageCache; +use vello_common::multi_atlas::AtlasConfig; use vello_common::{geometry::SizeU16, tile::Tile}; use wgpu::{ BindGroup, BindGroupLayout, Buffer, BufferDescriptor, BufferViewMut, COPY_BUFFER_ALIGNMENT, @@ -196,9 +205,191 @@ pub(crate) filter_original_bind_group: BindGroup, /// Copy bind group. pub(crate) scratch_copy_bind_group: BindGroup, + /// Native image samplers shared by external texture slots. + pub(crate) external_samplers: HashMap<ExternalSampler, Sampler>, + /// Strip bind group cache. + pub(crate) strip_layer_bind_groups: + HashMap<(StripTargetKind, Option<LayerTextureId>), BindGroup>, + /// Blend bind group cache. + pub(crate) blend_layer_bind_groups: HashMap<BlendPassBindings, BindGroup>, + /// Filter bind group cache. + pub(crate) filter_pair_bind_groups: HashMap<FilterPassBindings, FilterPairBindGroups>, + /// Arena holding all [`GpuStrip`](crate::GpuStrip) data. + pub(crate) strips_arena: StripBufferArena, + /// Shared staging memory for buffer and texture uploads. + pub(crate) staging_belt: UploadStagingBelt, + /// Dimensions of the rendering target + pub(crate) render_size: RenderSize, + /// Offset of the scene within the rendering target. + pub(crate) scene_offset: (i32, i32), + /// Scratch buffer for staging encoded paints texture data. + pub(crate) encoded_paints_data: Vec<u8>, + /// Scratch buffer for staging filter data texture data. + pub(crate) filter_data: Vec<u8>, + /// Whether the depth buffer has been cleared this frame. + pub(crate) depth_cleared_this_frame: bool, } impl GpuResources { + pub(crate) fn new( + device: &Device, + programs: &Programs, + image_cache: &ImageCache, + render_target_config: &RenderTargetConfig, + layer_config: LayersConfig, + resource_texture_dimension_2d: u32, + ) -> Self { + let texture_size = layer_config.min_texture_size; + + // The empty bind group needs a sampler even before any external image is drawn. + let mut external_samplers = HashMap::new(); + external_samplers.insert( + ExternalSampler::DEFAULT, + Self::create_external_sampler(device, ExternalSampler::DEFAULT), + ); + + let layer_textures: [Vec<TextureView>; 2] = core::array::from_fn(|_| Vec::new()); + let scratch_texture = None; + let placeholder_external_texture_view = Self::create_placeholder_external_texture(device); + let filter_original_bind_group = create_filter_original_texture_bind_group( + device, + &programs.filter.input_bind_group_layouts[1], + &placeholder_external_texture_view, + ); + let layer_config_buffer = Self::create_config_buffer( + device, + u32::from(texture_size.width()), + u32::from(texture_size.height()), + resource_texture_dimension_2d, + ); + + const INITIAL_ALPHA_TEXTURE_HEIGHT: u32 = 1; + let alphas_texture = Self::create_alphas_texture( + device, + resource_texture_dimension_2d, + INITIAL_ALPHA_TEXTURE_HEIGHT, + ); + let view_config_buffer = Self::create_config_buffer( + device, + u32::from(render_target_config.width), + u32::from(render_target_config.height), + resource_texture_dimension_2d, + ); + + let AtlasConfig { + atlas_size: (atlas_width, atlas_height), + initial_atlas_count, + .. + } = image_cache.atlas_manager().config(); + let atlas_size = (*atlas_width, *atlas_height); + let atlas_textures: Vec<_> = (0..*initial_atlas_count) + .map(|_| Self::create_atlas_texture(device, *atlas_width, *atlas_height)) + .collect(); + let atlas_texture_views = atlas_textures + .iter() + .map(|texture| texture.create_view(&TextureViewDescriptor::default())) + .collect(); + let empty_external_texture_bind_group = + Programs::create_external_texture_bind_group( + device, + &programs.strip.external_texture_bind_group_layout, + [&placeholder_external_texture_view; EXTERNAL_TEXTURE_SLOT_COUNT], + [&external_samplers[&ExternalSampler::DEFAULT]; EXTERNAL_TEXTURE_SLOT_COUNT], + programs.strip.wgpu_external_textures.then_some( + wgpu::BindingResource::TextureView(&placeholder_external_texture_view), + ), + ); + + const INITIAL_ENCODED_PAINTS_TEXTURE_HEIGHT: u32 = 1; + let encoded_paints_data = vec![ + 0; + ((resource_texture_dimension_2d * INITIAL_ENCODED_PAINTS_TEXTURE_HEIGHT) << 4) + as usize + ]; + let encoded_paints_texture = Self::create_encoded_paints_texture( + device, + resource_texture_dimension_2d, + INITIAL_ENCODED_PAINTS_TEXTURE_HEIGHT, + ); + let encoded_paints_bind_group = Self::create_encoded_paints_bind_group( + device, + &programs.encoded_paints_bind_group_layout, + &encoded_paints_texture.create_view(&TextureViewDescriptor::default()), + ); + + const INITIAL_GRADIENT_TEXTURE_HEIGHT: u32 = 1; + let gradient_texture = Self::create_gradient_texture( + device, + resource_texture_dimension_2d, + INITIAL_GRADIENT_TEXTURE_HEIGHT, + ); + let gradient_bind_group = Self::create_gradient_bind_group( + device, + &programs.gradient_bind_group_layout, + &gradient_texture.create_view(&TextureViewDescriptor::default()), + ); + + // TODO: We really should deduplicate handling of this this with encoded paints texture. + const INITIAL_FILTER_TEXTURE_HEIGHT: u32 = 1; + let filter_data = vec![ + 0_u8; + ((resource_texture_dimension_2d * INITIAL_FILTER_TEXTURE_HEIGHT) << 4) + as usize + ]; + let filter_data_texture = Self::create_filter_data_texture( + device, + resource_texture_dimension_2d, + INITIAL_FILTER_TEXTURE_HEIGHT, + ); + let filter_base_bind_group = Self::create_filter_base_bind_group( + device, + &programs.filter.bind_group_layout, + &filter_data_texture.create_view(&TextureViewDescriptor::default()), + ); + let scratch_copy_bind_group = Programs::create_copy_bind_group( + device, + &programs.copy.bind_group_layout, + &placeholder_external_texture_view, + ); + + Self { + layer_textures, + scratch_texture, + filter_original_bind_group, + scratch_copy_bind_group, + layer_config_buffer, + alphas_texture, + resource_texture_dimension_2d, + atlas_textures, + atlas_texture_views, + atlas_size, + placeholder_external_texture_view, + empty_external_texture_bind_group, + encoded_paints_texture, + encoded_paints_bind_group, + gradient_texture, + gradient_bind_group, + filter_data_texture, + filter_base_bind_group, + texture_size, + view_config_buffer, + external_samplers, + strip_layer_bind_groups: HashMap::new(), + blend_layer_bind_groups: HashMap::new(), + filter_pair_bind_groups: HashMap::new(), + strips_arena: StripBufferArena::new(device), + staging_belt: UploadStagingBelt::new(device), + render_size: RenderSize { + width: render_target_config.width, + height: render_target_config.height, + }, + scene_offset: (0, 0), + encoded_paints_data, + filter_data, + depth_cleared_this_frame: false, + } + } + pub(crate) fn layer_views(&self, layer_ids: [LayerTextureId; 2]) -> [&TextureView; 2] { layer_ids.map(|id| self.layer_view(id)) } @@ -216,7 +407,7 @@ pub(crate) const SIZE_OF_CONFIG: NonZeroU64 = NonZeroU64::new(size_of::<Config>() as u64).unwrap(); -impl Programs { +impl GpuResources { fn create_intermediate_texture( device: &Device, size: SizeU16, @@ -240,25 +431,30 @@ texture.create_view(&TextureViewDescriptor::default()) } - pub(crate) fn prepare_intermediate_textures(&mut self, device: &Device, schedule: &Schedule) { + pub(crate) fn prepare_intermediate_textures( + &mut self, + device: &Device, + programs: &Programs, + schedule: &Schedule, + ) { let requirements = schedule.intermediate_texture_requirements(); let texture_size = requirements.size; - let current_size = self.resources.texture_size; + let current_size = self.texture_size; let allocations = requirements.allocations; let layer_pages = allocations.layer_pages; let scratch_required = allocations.scratch; let size_changed = current_size != texture_size; if size_changed { - self.resources.layer_config_buffer = Self::create_config_buffer( + self.layer_config_buffer = Self::create_config_buffer( device, u32::from(texture_size.width()), u32::from(texture_size.height()), - self.resources.resource_texture_dimension_2d, + self.resource_texture_dimension_2d, ); } - for (index, textures) in self.resources.layer_textures.iter_mut().enumerate() { + for (index, textures) in self.layer_textures.iter_mut().enumerate() { let required_page_count = layer_pages[index]; // Note: Currently, `texture_size` only grows across frames, so if this condition // is true it means that the new required size is larger than what we currently have. @@ -280,22 +476,24 @@ ) })); } - let scratch_changed = (self.resources.scratch_texture.is_some() && size_changed) - || (self.resources.scratch_texture.is_none() && scratch_required); + let scratch_changed = (self.scratch_texture.is_some() && size_changed) + || (self.scratch_texture.is_none() && scratch_required); if scratch_changed { - self.resources.scratch_texture = Some(ScratchTexture::new( - Self::create_intermediate_texture(device, texture_size, "Scratch Texture"), - )); + self.scratch_texture = Some(ScratchTexture::new(Self::create_intermediate_texture( + device, + texture_size, + "Scratch Texture", + ))); } - self.resources.texture_size = texture_size; + self.texture_size = texture_size; if size_changed { self.clear_layer_bind_group_caches(); } if scratch_changed { - self.update_scratch_bind_groups(device); + self.update_scratch_bind_groups(device, programs); } } @@ -305,18 +503,21 @@ self.filter_pair_bind_groups.clear(); } - fn update_scratch_bind_groups(&mut self, device: &Device) { - let scratch_view = self.resources.scratch_view(); + fn update_scratch_bind_groups(&mut self, device: &Device, programs: &Programs) { + let scratch_view = self.scratch_view(); let filter_original_bind_group = create_filter_original_texture_bind_group( device, - &self.filter.input_bind_group_layouts[1], + &programs.filter.input_bind_group_layouts[1], scratch_view, ); - let scratch_copy_bind_group = - Self::create_copy_bind_group(device, &self.copy.bind_group_layout, scratch_view); + let scratch_copy_bind_group = Programs::create_copy_bind_group( + device, + &programs.copy.bind_group_layout, + scratch_view, + ); - self.resources.filter_original_bind_group = filter_original_bind_group; - self.resources.scratch_copy_bind_group = scratch_copy_bind_group; + self.filter_original_bind_group = filter_original_bind_group; + self.scratch_copy_bind_group = scratch_copy_bind_group; } pub(crate) fn create_config_buffer( @@ -418,38 +619,6 @@ }) } - pub(crate) fn create_encoded_paints_bind_group_layout(device: &Device) -> BindGroupLayout { - device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - label: Some("Encoded Paints Bind Group Layout"), - entries: &[wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Texture { - sample_type: wgpu::TextureSampleType::Uint, - view_dimension: wgpu::TextureViewDimension::D2, - multisampled: false, - }, - count: None, - }], - }) - } - - pub(crate) fn create_gradient_bind_group_layout(device: &Device) -> BindGroupLayout { - device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - label: Some("Gradient Bind Group 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::D2, - multisampled: false, - }, - count: None, - }], - }) - } - pub(crate) fn create_encoded_paints_texture( device: &Device, width: u32, @@ -525,6 +694,7 @@ pub(crate) fn prepare( &mut self, device: &Device, + programs: &Programs, encoder: &mut CommandEncoder, gradient_cache: &mut GradientRampCache, encoded_paints: &[GpuEncodedPaint], @@ -534,11 +704,21 @@ paint_idxs: &[u32], filter_context: &FilterContext, ) { - let resource_texture_dimension_2d = self.resources.resource_texture_dimension_2d; + let resource_texture_dimension_2d = self.resource_texture_dimension_2d; self.staging_belt.prepare(device); self.maybe_resize_alphas_tex(device, resource_texture_dimension_2d, alphas.len()); - self.maybe_resize_encoded_paints_tex(device, resource_texture_dimension_2d, paint_idxs); - self.maybe_resize_filter_tex(device, resource_texture_dimension_2d, filter_context); + self.maybe_resize_encoded_paints_tex( + device, + programs, + resource_texture_dimension_2d, + paint_idxs, + ); + self.maybe_resize_filter_tex( + device, + programs, + resource_texture_dimension_2d, + filter_context, + ); self.maybe_update_config_buffer( encoder, resource_texture_dimension_2d, @@ -551,7 +731,12 @@ self.upload_filter_texture(encoder, filter_context); if gradient_cache.has_changed() { - self.maybe_resize_gradient_tex(device, resource_texture_dimension_2d, gradient_cache); + self.maybe_resize_gradient_tex( + device, + programs, + resource_texture_dimension_2d, + gradient_cache, + ); self.upload_gradient_texture(encoder, gradient_cache); gradient_cache.mark_synced(); } @@ -569,10 +754,10 @@ // There are 16 1-byte alpha values per texel. .div_ceil(resource_texture_dimension_2d << 4); debug_assert!( - self.resources.alphas_texture.width() == resource_texture_dimension_2d, + self.alphas_texture.width() == resource_texture_dimension_2d, "Alpha texture width must match resource texture dimensions" ); - let current_alpha_height = self.resources.alphas_texture.height(); + let current_alpha_height = self.alphas_texture.height(); if required_alpha_height > current_alpha_height { // We need to resize the alpha texture to fit the new alpha data. assert!( @@ -586,7 +771,7 @@ resource_texture_dimension_2d, required_alpha_height, ); - self.resources.alphas_texture = alphas_texture; + self.alphas_texture = alphas_texture; self.strip_layer_bind_groups.clear(); self.blend_layer_bind_groups.clear(); } @@ -596,6 +781,7 @@ fn maybe_resize_encoded_paints_tex( &mut self, device: &Device, + programs: &Programs, resource_texture_dimension_2d: u32, paint_idxs: &[u32], ) { @@ -603,10 +789,10 @@ let required_encoded_paints_height = required_texels.div_ceil(resource_texture_dimension_2d); debug_assert!( - self.resources.encoded_paints_texture.width() == resource_texture_dimension_2d, + self.encoded_paints_texture.width() == resource_texture_dimension_2d, "Encoded paints texture width must match resource texture dimensions" ); - let current_encoded_paints_height = self.resources.encoded_paints_texture.height(); + let current_encoded_paints_height = self.encoded_paints_texture.height(); if required_encoded_paints_height > current_encoded_paints_height { assert!( required_encoded_paints_height <= resource_texture_dimension_2d, @@ -621,14 +807,13 @@ resource_texture_dimension_2d, required_encoded_paints_height, ); - self.resources.encoded_paints_texture = encoded_paints_texture; + self.encoded_paints_texture = encoded_paints_texture; // Since the encoded paints texture has changed, we need to update the strip bind groups. - self.resources.encoded_paints_bind_group = Self::create_encoded_paints_bind_group( + self.encoded_paints_bind_group = Self::create_encoded_paints_bind_group( device, - &self.encoded_paints_bind_group_layout, + &programs.encoded_paints_bind_group_layout, &self - .resources .encoded_paints_texture .create_view(&TextureViewDescriptor::default()), ); @@ -639,16 +824,17 @@ fn maybe_resize_gradient_tex( &mut self, device: &Device, + programs: &Programs, resource_texture_dimension_2d: u32, gradient_cache: &GradientRampCache, ) { let gradient_pixels = (gradient_cache.luts_size() / 4) as u32; // 4 bytes per RGBA8 pixel let required_gradient_height = gradient_pixels.div_ceil(resource_texture_dimension_2d); debug_assert!( - self.resources.gradient_texture.width() == resource_texture_dimension_2d, + self.gradient_texture.width() == resource_texture_dimension_2d, "Gradient texture width must match resource texture dimensions" ); - let current_gradient_height = self.resources.gradient_texture.height(); + let current_gradient_height = self.gradient_texture.height(); if required_gradient_height > current_gradient_height { assert!( required_gradient_height <= resource_texture_dimension_2d, @@ -659,14 +845,13 @@ resource_texture_dimension_2d, required_gradient_height, ); - self.resources.gradient_texture = gradient_texture; + self.gradient_texture = gradient_texture; // Since the gradient texture has changed, we need to update the gradient bind group. - self.resources.gradient_bind_group = Self::create_gradient_bind_group( + self.gradient_bind_group = Self::create_gradient_bind_group( device, - &self.gradient_bind_group_layout, + &programs.gradient_bind_group_layout, &self - .resources .gradient_texture .create_view(&TextureViewDescriptor::default()), ); @@ -694,7 +879,7 @@ }; let mut buffer = self.staging_belt.write_buffer( encoder, - &self.resources.view_config_buffer, + &self.view_config_buffer, 0, SIZE_OF_CONFIG, ); @@ -707,16 +892,16 @@ /// Create any newly allocated atlas textures. pub(crate) fn maybe_create_atlas_textures( + &mut self, device: &Device, - resources: &mut GpuResources, required_atlas_count: u32, ) { - let (width, height) = resources.atlas_size; - while resources.atlas_textures.len() < required_atlas_count as usize { + let (width, height) = self.atlas_size; + while self.atlas_textures.len() < required_atlas_count as usize { let texture = Self::create_atlas_texture(device, width, height); let texture_view = texture.create_view(&TextureViewDescriptor::default()); - resources.atlas_textures.push(texture); - resources.atlas_texture_views.push(texture_view); + self.atlas_textures.push(texture); + self.atlas_texture_views.push(texture_view); } } @@ -726,7 +911,7 @@ return; } - let texture = &self.resources.alphas_texture; + let texture = &self.alphas_texture; self.staging_belt .upload_texture(encoder, texture, alphas, 16); } @@ -742,7 +927,7 @@ return; } - let texture = &self.resources.encoded_paints_texture; + let texture = &self.encoded_paints_texture; let rows = paint_idxs.last().unwrap().div_ceil(texture.width()); let data = &mut self.encoded_paints_data[..(rows * texture.width() * 16) as usize]; @@ -761,11 +946,7 @@ return; } - self.staging_belt.upload_texture( - encoder, - &self.resources.gradient_texture, - gradient_cache.luts(), - 4, - ); + self.staging_belt + .upload_texture(encoder, &self.gradient_texture, gradient_cache.luts(), 4); } }