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