diff options
| author | s-ol <s+removethis@s-ol.nu> | 2026-04-23 07:59:15 +0000 |
|---|---|---|
| committer | s-ol <s+removethis@s-ol.nu> | 2026-05-14 14:46:13 +0000 |
| commit | 083d289d2d7e807b28bf33d5e269d47e249d156a (patch) | |
| tree | 9caceace1c5101a04bb90d2e198be2ee0aa54b79 /src | |
| parent | cargo fmt (diff) | |
| download | wgsl-view-083d289d2d7e807b28bf33d5e269d47e249d156a.tar.gz wgsl-view-083d289d2d7e807b28bf33d5e269d47e249d156a.zip | |
resource pools
Diffstat (limited to 'src')
| -rw-r--r-- | src/bin/wgsl_render.rs | 20 | ||||
| -rw-r--r-- | src/gpu.rs | 10 | ||||
| -rw-r--r-- | src/renderer.rs | 75 | ||||
| -rw-r--r-- | src/uniform.rs | 224 |
4 files changed, 115 insertions, 214 deletions
diff --git a/src/bin/wgsl_render.rs b/src/bin/wgsl_render.rs index 70f6904..2288366 100644 --- a/src/bin/wgsl_render.rs +++ b/src/bin/wgsl_render.rs @@ -77,24 +77,26 @@ fn main() { set_uniform(renderer.uniforms(), rest, args).map_err(|e| e.to_string()) } (["texture", id], [OscType::String(tsv_name)]) => { - renderer.create_texture_resource(&device, id, tsv_name); + renderer.uniforms().create_texture(&device, id, tsv_name); Ok(()) } (["sampler", id], [OscType::String(filter), OscType::String(clamp)]) => { let (filter_mode, address_mode) = parse_sampler_modes(filter, clamp)?; - renderer.create_sampler_resource(&device, id, filter_mode, address_mode); + renderer + .uniforms() + .create_sampler(&device, id, filter_mode, address_mode); Ok(()) } (["texture", id, "destroy"], []) => { - renderer.destroy_texture_resource(&device, id); + renderer.uniforms().destroy_texture(&device, id); Ok(()) } (["sampler", id, "destroy"], []) => { - renderer.destroy_sampler_resource(&device, id); + renderer.uniforms().destroy_sampler(&device, id); Ok(()) } (["binding", name], [OscType::String(target)]) => { - bind_resource(&mut renderer, &device, name, target) + bind_resource(renderer.uniforms(), &device, name, target) } _ => Err(format!("unhandled OSC message {} {:?}", msg.addr, msg.args)), } @@ -108,7 +110,7 @@ fn main() { }; if dirty { - renderer.refresh_textures(&device, &mut client, fence); + gpu::refresh_textures(renderer.uniforms(), &device, &mut client, fence); renderer.render(&device, &queue); device.poll(wgpu::PollType::wait_indefinitely()).unwrap(); @@ -213,16 +215,16 @@ fn parse_sampler_modes( } fn bind_resource( - renderer: &mut Renderer, + uniforms: &mut wgsl_view::uniform::UniformCache, device: &wgpu::Device, name: &str, target: &str, ) -> Result<(), String> { if let Some(id) = target.strip_prefix("/texture/") { - renderer.bind_texture_resource(device, name, id)?; + uniforms.bind_texture(device, name, id)?; Ok(()) } else if let Some(id) = target.strip_prefix("/sampler/") { - renderer.bind_sampler_resource(device, name, id)?; + uniforms.bind_sampler(device, name, id)?; Ok(()) } else { Err(format!("invalid binding target '{target}'")) @@ -176,16 +176,16 @@ pub fn destroy_fence(client: &VkClient, fence: vk::Fence) { } /// Refresh all texture inputs from TSV shared images. -/// Handles format detection and texture recreation internally. +/// Rebuilds the bind group if any texture was resized. Returns true if rebind occurred. pub fn refresh_textures( - pools: &mut crate::uniform::ResourcePools, + cache: &mut crate::uniform::UniformCache, device: &wgpu::Device, client: &mut VkClient, fence: vk::Fence, ) -> bool { let mut needs_rebind = false; - for texture in pools.textures_mut() { + for texture in cache.textures_mut() { let tsv_name = match texture.tsv_name() { Some(n) => n.to_string(), None => continue, @@ -233,6 +233,10 @@ pub fn refresh_textures( } } + if needs_rebind { + cache.rebuild_bind_group(device); + } + needs_rebind } diff --git a/src/renderer.rs b/src/renderer.rs index 3717945..155c318 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -1,5 +1,4 @@ -use crate::gpu; -use crate::uniform::{ResourcePools, UniformCache}; +use crate::uniform::UniformCache; const CANVAS_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Rgba8UnormSrgb; @@ -38,7 +37,6 @@ pub struct Renderer { vertex_bgl: wgpu::BindGroupLayout, vertex_bg: wgpu::BindGroup, render_pipeline: wgpu::RenderPipeline, - resources: ResourcePools, uniforms: UniformCache, } @@ -103,14 +101,12 @@ impl Renderer { bytemuck::cast_slice(&[width as f32, height as f32]), ); - let resources = ResourcePools::new(); let mut uniforms = UniformCache::new(); let render_pipeline = build_pipeline( device, &vertex_module, &vertex_bgl, DEFAULT_FRAGMENT, - &resources, &mut uniforms, ) .expect("default shader"); @@ -122,7 +118,6 @@ impl Renderer { vertex_bgl, vertex_bg, render_pipeline, - resources, uniforms, } } @@ -145,7 +140,6 @@ impl Renderer { &self.vertex_module, &self.vertex_bgl, source, - &self.resources, &mut self.uniforms, )?; self.render_pipeline = pipeline; @@ -153,70 +147,6 @@ impl Renderer { Ok(()) } - pub fn create_texture_resource(&mut self, device: &wgpu::Device, id: &str, tsv_name: &str) { - self.resources.create_texture(device, id, tsv_name); - self.uniforms.rebuild_bind_group(device, &self.resources); - } - - pub fn destroy_texture_resource(&mut self, device: &wgpu::Device, id: &str) { - let removed = self.resources.destroy_texture(id); - let unbound = self.uniforms.unbind_texture_resource(id); - if removed || unbound { - self.uniforms.rebuild_bind_group(device, &self.resources); - } - } - - pub fn create_sampler_resource( - &mut self, - device: &wgpu::Device, - id: &str, - filter: wgpu::FilterMode, - address_mode: wgpu::AddressMode, - ) { - self.resources - .create_sampler(device, id, filter, address_mode); - self.uniforms.rebuild_bind_group(device, &self.resources); - } - - pub fn destroy_sampler_resource(&mut self, device: &wgpu::Device, id: &str) { - let removed = self.resources.destroy_sampler(id); - let unbound = self.uniforms.unbind_sampler_resource(id); - if removed || unbound { - self.uniforms.rebuild_bind_group(device, &self.resources); - } - } - - pub fn bind_texture_resource( - &mut self, - device: &wgpu::Device, - name: &str, - id: &str, - ) -> Result<(), String> { - self.uniforms - .bind_texture_resource(device, &self.resources, name, id) - } - - pub fn bind_sampler_resource( - &mut self, - device: &wgpu::Device, - name: &str, - id: &str, - ) -> Result<(), String> { - self.uniforms - .bind_sampler_resource(device, &self.resources, name, id) - } - - pub fn refresh_textures( - &mut self, - device: &wgpu::Device, - client: &mut texture_share_vk_client::VkClient, - fence: ash::vk::Fence, - ) { - if gpu::refresh_textures(&mut self.resources, device, client, fence) { - self.uniforms.rebuild_bind_group(device, &self.resources); - } - } - pub fn render(&mut self, device: &wgpu::Device, queue: &wgpu::Queue) { self.uniforms.flush(queue); @@ -258,7 +188,6 @@ fn build_pipeline( vertex_module: &wgpu::ShaderModule, vertex_bgl: &wgpu::BindGroupLayout, fragment_source: &str, - resources: &ResourcePools, cache: &mut UniformCache, ) -> Result<wgpu::RenderPipeline, String> { let module = naga::front::wgsl::parse_str(fragment_source) @@ -272,7 +201,7 @@ fn build_pipeline( .map_err(|e| format!("WGSL validation error: {e}"))?; cache.refresh(module, device); - cache.rebuild_bind_group(device, resources); + cache.rebuild_bind_group(device); let fragment_module = device.create_shader_module(wgpu::ShaderModuleDescriptor { label: Some("fragment_shader"), diff --git a/src/uniform.rs b/src/uniform.rs index 8f8b172..142c70c 100644 --- a/src/uniform.rs +++ b/src/uniform.rs @@ -409,16 +409,12 @@ pub struct TextureResource { height: u32, depth_or_array_layers: u32, view_dimension: wgpu::TextureViewDimension, - sample_type: wgpu::TextureSampleType, - multisampled: bool, } impl TextureResource { pub fn new(device: &wgpu::Device, id: String, tsv_name: String) -> Self { let format = wgpu::TextureFormat::Rgba8UnormSrgb; let view_dimension = wgpu::TextureViewDimension::D2; - let sample_type = wgpu::TextureSampleType::Float { filterable: true }; - let multisampled = false; let dimension = view_dimension_to_texture_dimension(view_dimension); let (texture, view) = create_input_texture(device, &id, 1, 1, 1, dimension, view_dimension, format); @@ -433,8 +429,6 @@ impl TextureResource { height: 1, depth_or_array_layers: 1, view_dimension, - sample_type, - multisampled, } } @@ -473,14 +467,6 @@ impl TextureResource { self.view_dimension } - pub fn sample_type(&self) -> wgpu::TextureSampleType { - self.sample_type - } - - pub fn multisampled(&self) -> bool { - self.multisampled - } - pub fn width(&self) -> u32 { self.width } @@ -525,8 +511,6 @@ impl TextureResource { self.height = height; self.depth_or_array_layers = depth_or_array_layers; self.view_dimension = view_dimension; - self.sample_type = wgpu::TextureSampleType::Float { filterable: true }; - self.multisampled = false; true } } @@ -585,12 +569,10 @@ impl SamplerResource { filter: wgpu::FilterMode, address_mode: wgpu::AddressMode, ) -> Self { - let mut this = Self { + Self { sampler: create_sampler(device, &id, filter, address_mode), id, - }; - this.configure(device, filter, address_mode); - this + } } pub fn id(&self) -> &str { @@ -633,70 +615,6 @@ fn create_sampler( }) } -pub struct ResourcePools { - textures: HashMap<String, TextureResource>, - samplers: HashMap<String, SamplerResource>, -} - -impl Default for ResourcePools { - fn default() -> Self { - Self::new() - } -} - -impl ResourcePools { - pub fn new() -> Self { - Self { - textures: HashMap::new(), - samplers: HashMap::new(), - } - } - - pub fn create_texture(&mut self, device: &wgpu::Device, id: &str, tsv_name: &str) { - self.textures.insert( - id.to_string(), - TextureResource::new(device, id.to_string(), tsv_name.to_string()), - ); - } - - pub fn destroy_texture(&mut self, id: &str) -> bool { - self.textures.remove(id).is_some() - } - - pub fn texture(&self, id: &str) -> Option<&TextureResource> { - self.textures.get(id) - } - - pub fn texture_mut(&mut self, id: &str) -> Option<&mut TextureResource> { - self.textures.get_mut(id) - } - - pub fn textures_mut(&mut self) -> impl Iterator<Item = &mut TextureResource> { - self.textures.values_mut() - } - - pub fn create_sampler( - &mut self, - device: &wgpu::Device, - id: &str, - filter: wgpu::FilterMode, - address_mode: wgpu::AddressMode, - ) { - self.samplers.insert( - id.to_string(), - SamplerResource::new(device, id.to_string(), filter, address_mode), - ); - } - - pub fn destroy_sampler(&mut self, id: &str) -> bool { - self.samplers.remove(id).is_some() - } - - pub fn sampler(&self, id: &str) -> Option<&SamplerResource> { - self.samplers.get(id) - } -} - struct TextureBindingSlot { name: String, binding: u32, @@ -748,13 +666,16 @@ fn image_multisampled(class: naga::ImageClass) -> bool { } /// Manages uniform buffer data and texture/sampler bindings for all globals in a shader. +/// Also owns the pooled texture and sampler resources. pub struct UniformCache { pub module: naga::Module, pub layouter: Layouter, uniforms: HashMap<String, UniformMember>, buffers: Vec<BufferState>, - textures: Vec<TextureBindingSlot>, - samplers: Vec<SamplerBindingSlot>, + texture_pool: HashMap<String, TextureResource>, + sampler_pool: HashMap<String, SamplerResource>, + texture_slots: Vec<TextureBindingSlot>, + sampler_slots: Vec<SamplerBindingSlot>, pub bind_group_layout: Option<wgpu::BindGroupLayout>, pub bind_group: Option<wgpu::BindGroup>, dirty: bool, @@ -786,8 +707,10 @@ impl UniformCache { layouter: Layouter::default(), uniforms: HashMap::new(), buffers: Vec::new(), - textures: Vec::new(), - samplers: Vec::new(), + texture_pool: HashMap::new(), + sampler_pool: HashMap::new(), + texture_slots: Vec::new(), + sampler_slots: Vec::new(), bind_group_layout: None, bind_group: None, dirty: false, @@ -804,8 +727,8 @@ impl UniformCache { let old_module = std::mem::take(&mut self.module); let old_uniforms = std::mem::take(&mut self.uniforms); let old_buffers = std::mem::take(&mut self.buffers); - let old_textures = std::mem::take(&mut self.textures); - let old_samplers = std::mem::take(&mut self.samplers); + let old_textures = std::mem::take(&mut self.texture_slots); + let old_samplers = std::mem::take(&mut self.sampler_slots); let mut new_uniforms: HashMap<String, UniformMember> = HashMap::new(); let mut new_buffers: Vec<BufferState> = Vec::new(); @@ -932,54 +855,96 @@ impl UniformCache { self.layouter = new_layouter; self.uniforms = new_uniforms; self.buffers = new_buffers; - self.textures = new_textures; - self.samplers = new_samplers; + self.texture_slots = new_textures; + self.sampler_slots = new_samplers; self.dirty = true; } - pub fn bind_texture_resource( + // --- Resource pool operations --- + + pub fn create_texture(&mut self, device: &wgpu::Device, id: &str, tsv_name: &str) { + self.texture_pool.insert( + id.to_string(), + TextureResource::new(device, id.to_string(), tsv_name.to_string()), + ); + self.build_bind_group(device); + } + + pub fn destroy_texture(&mut self, device: &wgpu::Device, id: &str) { + let removed = self.texture_pool.remove(id).is_some(); + let unbound = self.unbind_texture_resource(id); + if removed || unbound { + self.build_bind_group(device); + } + } + + pub fn textures_mut(&mut self) -> impl Iterator<Item = &mut TextureResource> { + self.texture_pool.values_mut() + } + + pub fn create_sampler( + &mut self, + device: &wgpu::Device, + id: &str, + filter: wgpu::FilterMode, + address_mode: wgpu::AddressMode, + ) { + self.sampler_pool.insert( + id.to_string(), + SamplerResource::new(device, id.to_string(), filter, address_mode), + ); + self.build_bind_group(device); + } + + pub fn destroy_sampler(&mut self, device: &wgpu::Device, id: &str) { + let removed = self.sampler_pool.remove(id).is_some(); + let unbound = self.unbind_sampler_resource(id); + if removed || unbound { + self.build_bind_group(device); + } + } + + pub fn bind_texture( &mut self, device: &wgpu::Device, - pools: &ResourcePools, - name: &str, + slot_name: &str, resource_id: &str, ) -> Result<(), String> { - if pools.texture(resource_id).is_none() { + if !self.texture_pool.contains_key(resource_id) { return Err(format!("texture '{resource_id}' not found")); } let slot = self - .textures + .texture_slots .iter_mut() - .find(|t| t.name == name) - .ok_or_else(|| format!("texture binding '{name}' not found"))?; + .find(|t| t.name == slot_name) + .ok_or_else(|| format!("texture binding '{slot_name}' not found in shader"))?; slot.resource_id = Some(resource_id.to_string()); - self.build_bind_group(device, pools); + self.build_bind_group(device); Ok(()) } - pub fn bind_sampler_resource( + pub fn bind_sampler( &mut self, device: &wgpu::Device, - pools: &ResourcePools, - name: &str, + slot_name: &str, resource_id: &str, ) -> Result<(), String> { - if pools.sampler(resource_id).is_none() { + if !self.sampler_pool.contains_key(resource_id) { return Err(format!("sampler '{resource_id}' not found")); } let slot = self - .samplers + .sampler_slots .iter_mut() - .find(|s| s.name == name) - .ok_or_else(|| format!("sampler binding '{name}' not found"))?; + .find(|s| s.name == slot_name) + .ok_or_else(|| format!("sampler binding '{slot_name}' not found in shader"))?; slot.resource_id = Some(resource_id.to_string()); - self.build_bind_group(device, pools); + self.build_bind_group(device); Ok(()) } - pub fn unbind_texture_resource(&mut self, id: &str) -> bool { + fn unbind_texture_resource(&mut self, id: &str) -> bool { let mut changed = false; - for slot in &mut self.textures { + for slot in &mut self.texture_slots { if slot.resource_id.as_deref() == Some(id) { slot.resource_id = None; changed = true; @@ -988,9 +953,9 @@ impl UniformCache { changed } - pub fn unbind_sampler_resource(&mut self, id: &str) -> bool { + fn unbind_sampler_resource(&mut self, id: &str) -> bool { let mut changed = false; - for slot in &mut self.samplers { + for slot in &mut self.sampler_slots { if slot.resource_id.as_deref() == Some(id) { slot.resource_id = None; changed = true; @@ -999,14 +964,15 @@ impl UniformCache { changed } - /// Rebuild the bind group (e.g. after a texture resize). - pub fn rebuild_bind_group(&mut self, device: &wgpu::Device, pools: &ResourcePools) { - self.build_bind_group(device, pools); + /// Rebuild the bind group after external changes (e.g. texture resize from TSV). + pub fn rebuild_bind_group(&mut self, device: &wgpu::Device) { + self.build_bind_group(device); } - fn build_bind_group(&mut self, device: &wgpu::Device, pools: &ResourcePools) { - let has_bindings = - !self.buffers.is_empty() || !self.textures.is_empty() || !self.samplers.is_empty(); + fn build_bind_group(&mut self, device: &wgpu::Device) { + let has_bindings = !self.buffers.is_empty() + || !self.texture_slots.is_empty() + || !self.sampler_slots.is_empty(); if !has_bindings { self.bind_group_layout = None; @@ -1029,7 +995,7 @@ impl UniformCache { }); } - for tex in &self.textures { + for tex in &self.texture_slots { layout_entries.push(wgpu::BindGroupLayoutEntry { binding: tex.binding, visibility: wgpu::ShaderStages::FRAGMENT, @@ -1042,7 +1008,7 @@ impl UniformCache { }); } - for smp in &self.samplers { + for smp in &self.sampler_slots { layout_entries.push(wgpu::BindGroupLayoutEntry { binding: smp.binding, visibility: wgpu::ShaderStages::FRAGMENT, @@ -1058,12 +1024,12 @@ impl UniformCache { let mut bg_entries: Vec<wgpu::BindGroupEntry> = Vec::new(); let fallback_textures_and_views: Vec<_> = self - .textures + .texture_slots .iter() .map(|tex| create_placeholder_texture(device, tex)) .collect(); let fallback_samplers: Vec<_> = self - .samplers + .sampler_slots .iter() .map(|smp| { if smp.binding_type == wgpu::SamplerBindingType::Comparison { @@ -1088,11 +1054,11 @@ impl UniformCache { }); } - for (idx, tex) in self.textures.iter().enumerate() { + for (idx, tex) in self.texture_slots.iter().enumerate() { let resource = tex .resource_id .as_deref() - .and_then(|id| pools.texture(id)) + .and_then(|id| self.texture_pool.get(id)) .filter(|res| texture_resource_matches(tex, res)); let texture_view = if let Some(resource) = resource { resource.view() @@ -1105,11 +1071,13 @@ impl UniformCache { }); } - for (idx, smp) in self.samplers.iter().enumerate() { + for (idx, smp) in self.sampler_slots.iter().enumerate() { let sampler = if smp.binding_type == wgpu::SamplerBindingType::Comparison { &fallback_samplers[idx] - } else if let Some(resource) = - smp.resource_id.as_deref().and_then(|id| pools.sampler(id)) + } else if let Some(resource) = smp + .resource_id + .as_deref() + .and_then(|id| self.sampler_pool.get(id)) { resource.sampler() } else { @@ -1164,8 +1132,6 @@ impl UniformCache { fn texture_resource_matches(slot: &TextureBindingSlot, resource: &TextureResource) -> bool { slot.view_dimension == resource.view_dimension() - && slot.sample_type == resource.sample_type() - && slot.multisampled == resource.multisampled() } fn create_placeholder_texture( |
