dear_imgui_wgpu/
render_resources.rs

1//! Render resources management for the WGPU renderer
2//!
3//! This module handles shared render resources like samplers, uniforms, and bind groups,
4//! corresponding to the RenderResources struct in imgui_impl_wgpu.cpp
5
6use crate::{RendererError, RendererResult, UniformBuffer};
7use std::collections::HashMap;
8use wgpu::*;
9
10/// Shared render resources
11///
12/// This corresponds to the RenderResources struct in the C++ implementation.
13/// Contains samplers, uniform buffers, and bind group layouts that are shared
14/// across all frames.
15pub struct RenderResources {
16    /// Texture sampler
17    pub sampler: Option<Sampler>,
18    /// Uniform buffer manager (also owns the common bind group layout)
19    pub uniform_buffer: Option<UniformBuffer>,
20    /// Image bind groups cache (texture_id -> bind_group)
21    pub image_bind_groups: HashMap<u64, BindGroup>,
22    /// Image bind group layout (cached for efficiency)
23    pub image_bind_group_layout: Option<BindGroupLayout>,
24}
25
26impl RenderResources {
27    /// Create new empty render resources
28    pub fn new() -> Self {
29        Self {
30            sampler: None,
31            uniform_buffer: None,
32            image_bind_groups: HashMap::new(),
33            image_bind_group_layout: None,
34        }
35    }
36
37    /// Initialize render resources
38    pub fn initialize(&mut self, device: &Device) -> RendererResult<()> {
39        // Create texture sampler (matches imgui_impl_wgpu.cpp sampler setup)
40        // Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines'
41        // or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling
42        let sampler = device.create_sampler(&SamplerDescriptor {
43            label: Some("Dear ImGui Texture Sampler"),
44            address_mode_u: AddressMode::ClampToEdge, // matches WGPUAddressMode_ClampToEdge
45            address_mode_v: AddressMode::ClampToEdge, // matches WGPUAddressMode_ClampToEdge
46            address_mode_w: AddressMode::ClampToEdge, // matches WGPUAddressMode_ClampToEdge
47            mag_filter: FilterMode::Linear,           // matches WGPUFilterMode_Linear
48            min_filter: FilterMode::Linear,           // matches WGPUFilterMode_Linear
49            mipmap_filter: FilterMode::Linear,        // matches WGPUMipmapFilterMode_Linear
50            anisotropy_clamp: 1,                      // matches maxAnisotropy = 1
51            ..Default::default()
52        });
53
54        // Create uniform buffer + common bind group layout
55        let uniform_buffer = UniformBuffer::new(device, &sampler);
56
57        // Create image bind group layout (for texture views)
58        let image_bind_group_layout = device.create_bind_group_layout(&BindGroupLayoutDescriptor {
59            label: Some("Dear ImGui Image Bind Group Layout"),
60            entries: &[BindGroupLayoutEntry {
61                binding: 0,
62                visibility: ShaderStages::FRAGMENT,
63                ty: BindingType::Texture {
64                    multisampled: false,
65                    sample_type: TextureSampleType::Float { filterable: true },
66                    view_dimension: TextureViewDimension::D2,
67                },
68                count: None,
69            }],
70        });
71
72        self.sampler = Some(sampler);
73        self.uniform_buffer = Some(uniform_buffer);
74        self.image_bind_group_layout = Some(image_bind_group_layout);
75
76        Ok(())
77    }
78
79    /// Create an image bind group for a texture
80    pub fn create_image_bind_group(
81        &self,
82        device: &Device,
83        texture_view: &TextureView,
84    ) -> RendererResult<BindGroup> {
85        let layout = self.image_bind_group_layout.as_ref().ok_or_else(|| {
86            RendererError::InvalidRenderState("Image bind group layout not initialized".to_string())
87        })?;
88
89        let bind_group = device.create_bind_group(&BindGroupDescriptor {
90            label: Some("Dear ImGui Image Bind Group"),
91            layout,
92            entries: &[BindGroupEntry {
93                binding: 0,
94                resource: BindingResource::TextureView(texture_view),
95            }],
96        });
97
98        Ok(bind_group)
99    }
100
101    /// Get or create an image bind group for a texture
102    pub fn get_or_create_image_bind_group(
103        &mut self,
104        device: &Device,
105        texture_id: u64,
106        texture_view: &TextureView,
107    ) -> RendererResult<&BindGroup> {
108        if !self.image_bind_groups.contains_key(&texture_id) {
109            let bind_group = self.create_image_bind_group(device, texture_view)?;
110            self.image_bind_groups.insert(texture_id, bind_group);
111        }
112
113        Ok(self.image_bind_groups.get(&texture_id).unwrap())
114    }
115
116    /// Remove an image bind group
117    pub fn remove_image_bind_group(&mut self, texture_id: u64) {
118        self.image_bind_groups.remove(&texture_id);
119    }
120
121    /// Clear all image bind groups
122    pub fn clear_image_bind_groups(&mut self) {
123        self.image_bind_groups.clear();
124    }
125
126    /// Get the texture sampler
127    pub fn sampler(&self) -> Option<&Sampler> {
128        self.sampler.as_ref()
129    }
130
131    /// Get the uniform buffer
132    pub fn uniform_buffer(&self) -> Option<&UniformBuffer> {
133        self.uniform_buffer.as_ref()
134    }
135
136    /// Get the common bind group
137    pub fn common_bind_group(&self) -> Option<&BindGroup> {
138        self.uniform_buffer.as_ref().map(|ub| ub.bind_group())
139    }
140
141    /// Get the image bind group layout
142    pub fn image_bind_group_layout(&self) -> Option<&BindGroupLayout> {
143        self.image_bind_group_layout.as_ref()
144    }
145
146    /// Check if resources are initialized
147    pub fn is_initialized(&self) -> bool {
148        self.sampler.is_some()
149            && self.uniform_buffer.is_some()
150            && self.image_bind_group_layout.is_some()
151    }
152
153    /// Get statistics for debugging
154    pub fn stats(&self) -> RenderResourcesStats {
155        RenderResourcesStats {
156            image_bind_groups_count: self.image_bind_groups.len(),
157            is_initialized: self.is_initialized(),
158        }
159    }
160}
161
162impl Default for RenderResources {
163    fn default() -> Self {
164        Self::new()
165    }
166}
167
168/// Statistics for render resources
169#[derive(Debug, Clone)]
170pub struct RenderResourcesStats {
171    pub image_bind_groups_count: usize,
172    pub is_initialized: bool,
173}