Skip to main content

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