summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authors-ol <s+removethis@s-ol.nu>2026-04-23 07:59:15 +0000
committers-ol <s+removethis@s-ol.nu>2026-05-14 14:46:13 +0000
commit083d289d2d7e807b28bf33d5e269d47e249d156a (patch)
tree9caceace1c5101a04bb90d2e198be2ee0aa54b79 /src
parentcargo fmt (diff)
downloadwgsl-view-083d289d2d7e807b28bf33d5e269d47e249d156a.tar.gz
wgsl-view-083d289d2d7e807b28bf33d5e269d47e249d156a.zip
resource pools
Diffstat (limited to 'src')
-rw-r--r--src/bin/wgsl_render.rs20
-rw-r--r--src/gpu.rs10
-rw-r--r--src/renderer.rs75
-rw-r--r--src/uniform.rs224
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}'"))
diff --git a/src/gpu.rs b/src/gpu.rs
index edcc073..ab15356 100644
--- a/src/gpu.rs
+++ b/src/gpu.rs
@@ -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(