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: MipmapFilterMode::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        self.image_bind_groups.get(&texture_id).ok_or_else(|| {
114            RendererError::InvalidRenderState("Image bind group missing after creation".to_string())
115        })
116    }
117
118    /// Remove an image bind group
119    pub fn remove_image_bind_group(&mut self, texture_id: u64) {
120        self.image_bind_groups.remove(&texture_id);
121    }
122
123    /// Clear all image bind groups
124    pub fn clear_image_bind_groups(&mut self) {
125        self.image_bind_groups.clear();
126    }
127
128    /// Get the texture sampler
129    pub fn sampler(&self) -> Option<&Sampler> {
130        self.sampler.as_ref()
131    }
132
133    /// Get the uniform buffer
134    pub fn uniform_buffer(&self) -> Option<&UniformBuffer> {
135        self.uniform_buffer.as_ref()
136    }
137
138    /// Get the common bind group
139    pub fn common_bind_group(&self) -> Option<&BindGroup> {
140        self.uniform_buffer.as_ref().map(|ub| ub.bind_group())
141    }
142
143    /// Get the image bind group layout
144    pub fn image_bind_group_layout(&self) -> Option<&BindGroupLayout> {
145        self.image_bind_group_layout.as_ref()
146    }
147
148    /// Check if resources are initialized
149    pub fn is_initialized(&self) -> bool {
150        self.sampler.is_some()
151            && self.uniform_buffer.is_some()
152            && self.image_bind_group_layout.is_some()
153    }
154
155    /// Get statistics for debugging
156    pub fn stats(&self) -> RenderResourcesStats {
157        RenderResourcesStats {
158            image_bind_groups_count: self.image_bind_groups.len(),
159            is_initialized: self.is_initialized(),
160        }
161    }
162}
163
164impl Default for RenderResources {
165    fn default() -> Self {
166        Self::new()
167    }
168}
169
170/// Statistics for render resources
171#[derive(Debug, Clone)]
172pub struct RenderResourcesStats {
173    pub image_bind_groups_count: usize,
174    pub is_initialized: bool,
175}