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}