Skip to main content

astrelis_render/
depth.rs

1//! Depth texture abstraction for render systems.
2//!
3//! Provides a first-class depth texture resource with Arc-wrapped views
4//! for cheap, lifetime-free sharing across render passes and contexts.
5
6use std::sync::Arc;
7
8/// Default depth format used for depth textures.
9pub const DEFAULT_DEPTH_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Depth32Float;
10
11/// A depth texture with Arc-wrapped view for cheap, lifetime-free sharing.
12///
13/// The depth view is wrapped in an `Arc` to eliminate lifetime coupling when
14/// passing depth textures to render passes. This is safe because `wgpu::TextureView`
15/// is internally just an ID, so the Arc overhead is minimal.
16///
17/// # Example
18///
19/// ```ignore
20/// let mut depth = DepthTexture::new(device, 800, 600, DEFAULT_DEPTH_FORMAT);
21///
22/// // Cheap clone of the Arc
23/// let depth_view = depth.view();
24///
25/// // Later, if window resizes:
26/// if depth.needs_resize(new_width, new_height) {
27///     depth.resize(device, new_width, new_height);
28/// }
29/// ```
30pub struct DepthTexture {
31    texture: wgpu::Texture,
32    view: Arc<wgpu::TextureView>,
33    size: (u32, u32),
34    format: wgpu::TextureFormat,
35}
36
37impl DepthTexture {
38    /// Create a new depth texture with the given dimensions and format.
39    pub fn new(
40        device: &wgpu::Device,
41        width: u32,
42        height: u32,
43        format: wgpu::TextureFormat,
44    ) -> Self {
45        let (texture, view) = create_depth_texture(device, width, height, format, None);
46        Self {
47            texture,
48            view: Arc::new(view),
49            size: (width, height),
50            format,
51        }
52    }
53
54    /// Create a new depth texture with a debug label.
55    pub fn with_label(
56        device: &wgpu::Device,
57        width: u32,
58        height: u32,
59        format: wgpu::TextureFormat,
60        label: &str,
61    ) -> Self {
62        let (texture, view) = create_depth_texture(device, width, height, format, Some(label));
63        Self {
64            texture,
65            view: Arc::new(view),
66            size: (width, height),
67            format,
68        }
69    }
70
71    /// Resize the depth texture if dimensions have changed.
72    ///
73    /// This recreates the texture and view. The old `Arc<TextureView>` remains
74    /// valid until all references are dropped, but any render passes using it
75    /// should be completed before resize.
76    pub fn resize(&mut self, device: &wgpu::Device, width: u32, height: u32) {
77        if self.size == (width, height) {
78            return;
79        }
80
81        let (texture, view) = create_depth_texture(device, width, height, self.format, None);
82        self.texture = texture;
83        self.view = Arc::new(view);
84        self.size = (width, height);
85    }
86
87    /// Get a cheap clone of the depth view.
88    ///
89    /// The Arc wrapper allows the view to be shared without lifetime constraints,
90    /// making it easy to pass to closures and render passes.
91    pub fn view(&self) -> Arc<wgpu::TextureView> {
92        self.view.clone()
93    }
94
95    /// Get a reference to the depth view (for cases where Arc is not needed).
96    pub fn view_ref(&self) -> &wgpu::TextureView {
97        &self.view
98    }
99
100    /// Get the current size as (width, height).
101    pub fn size(&self) -> (u32, u32) {
102        self.size
103    }
104
105    /// Get the width in pixels.
106    pub fn width(&self) -> u32 {
107        self.size.0
108    }
109
110    /// Get the height in pixels.
111    pub fn height(&self) -> u32 {
112        self.size.1
113    }
114
115    /// Check if the depth texture needs to be resized for the given dimensions.
116    pub fn needs_resize(&self, width: u32, height: u32) -> bool {
117        self.size != (width, height)
118    }
119
120    /// Get the depth format.
121    pub fn format(&self) -> wgpu::TextureFormat {
122        self.format
123    }
124
125    /// Get the underlying wgpu texture.
126    pub fn texture(&self) -> &wgpu::Texture {
127        &self.texture
128    }
129}
130
131impl std::fmt::Debug for DepthTexture {
132    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
133        f.debug_struct("DepthTexture")
134            .field("size", &self.size)
135            .field("format", &self.format)
136            .finish()
137    }
138}
139
140/// Create a depth texture and its view.
141fn create_depth_texture(
142    device: &wgpu::Device,
143    width: u32,
144    height: u32,
145    format: wgpu::TextureFormat,
146    label: Option<&str>,
147) -> (wgpu::Texture, wgpu::TextureView) {
148    let texture = device.create_texture(&wgpu::TextureDescriptor {
149        label: label.or(Some("Depth Texture")),
150        size: wgpu::Extent3d {
151            width: width.max(1),
152            height: height.max(1),
153            depth_or_array_layers: 1,
154        },
155        mip_level_count: 1,
156        sample_count: 1,
157        dimension: wgpu::TextureDimension::D2,
158        format,
159        usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING,
160        view_formats: &[],
161    });
162
163    let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
164
165    (texture, view)
166}
167
168#[cfg(test)]
169mod tests {
170    // Note: These tests require a GPU device, so they're typically run
171    // as integration tests or with a test harness that provides a device.
172}