Skip to main content

arcane_core/renderer/
rendertarget.rs

1/// Render target store: off-screen textures that TS can draw into,
2/// then use as sprite inputs.
3///
4/// Each render target owns a wgpu::Texture + TextureView created with
5/// RENDER_ATTACHMENT | TEXTURE_BINDING usage. The format matches the
6/// surface format so the same SpritePipeline can render into it.
7///
8/// Lifecycle:
9/// 1. `create()` — allocates GPU texture + view
10/// 2. `get_view()` — returns the view for render pass target
11/// 3. `destroy()` — drops GPU resources
12
13use std::collections::HashMap;
14
15/// A single off-screen render target.
16pub struct RenderTargetEntry {
17    pub texture: wgpu::Texture,
18    pub view: wgpu::TextureView,
19    pub width: u32,
20    pub height: u32,
21}
22
23/// Stores all live render targets, keyed by their ID (which doubles as TextureId).
24pub struct RenderTargetStore {
25    pub targets: HashMap<u32, RenderTargetEntry>,
26}
27
28impl RenderTargetStore {
29    pub fn new() -> Self {
30        Self {
31            targets: HashMap::new(),
32        }
33    }
34
35    /// Allocate a new off-screen render target.
36    ///
37    /// The texture format matches the surface format (`surface_format`) so the
38    /// sprite pipeline can render into it without a format mismatch.
39    pub fn create(
40        &mut self,
41        device: &wgpu::Device,
42        id: u32,
43        width: u32,
44        height: u32,
45        surface_format: wgpu::TextureFormat,
46    ) {
47        let texture = device.create_texture(&wgpu::TextureDescriptor {
48            label: Some(&format!("render_target_{id}")),
49            size: wgpu::Extent3d {
50                width,
51                height,
52                depth_or_array_layers: 1,
53            },
54            mip_level_count: 1,
55            sample_count: 1,
56            dimension: wgpu::TextureDimension::D2,
57            // Must match surface format so SpritePipeline (compiled for surface_format)
58            // can render into this target without a pipeline/attachment format mismatch.
59            format: surface_format,
60            usage: wgpu::TextureUsages::RENDER_ATTACHMENT
61                | wgpu::TextureUsages::TEXTURE_BINDING,
62            view_formats: &[],
63        });
64
65        let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
66        self.targets.insert(
67            id,
68            RenderTargetEntry {
69                texture,
70                view,
71                width,
72                height,
73            },
74        );
75    }
76
77    /// Get the TextureView for rendering INTO this target.
78    pub fn get_view(&self, id: u32) -> Option<&wgpu::TextureView> {
79        self.targets.get(&id).map(|e| &e.view)
80    }
81
82    /// Get dimensions of a render target.
83    pub fn get_dims(&self, id: u32) -> Option<(u32, u32)> {
84        self.targets.get(&id).map(|e| (e.width, e.height))
85    }
86
87    /// Drop GPU resources for a render target.
88    pub fn destroy(&mut self, id: u32) {
89        self.targets.remove(&id);
90    }
91}