1use std::{collections::HashMap, sync::Arc};
8
9use parking_lot::{RwLock, RwLockWriteGuard};
10
11use crate::{
12 PxSize,
13 render_graph::{ExternalTextureDesc, RenderTextureDesc},
14};
15
16#[derive(Clone)]
18pub struct ExternalTextureRegistry {
19 inner: Arc<RwLock<ExternalTextureRegistryInner>>,
20}
21
22impl ExternalTextureRegistry {
23 pub(crate) fn new() -> Self {
24 Self {
25 inner: Arc::new(RwLock::new(ExternalTextureRegistryInner::default())),
26 }
27 }
28
29 pub fn allocate(
31 &self,
32 device: &wgpu::Device,
33 desc: RenderTextureDesc,
34 sample_count: u32,
35 ) -> ExternalTextureHandle {
36 let mut inner = self.inner.write();
37 let id = inner.next_id;
38 inner.next_id = inner.next_id.wrapping_add(1);
39 let entry = ExternalTextureEntry::new(device, desc.clone(), sample_count);
40 inner.entries.insert(id, entry);
41 ExternalTextureHandle {
42 id,
43 desc,
44 sample_count,
45 registry: self.clone(),
46 }
47 }
48
49 pub fn ensure(
51 &self,
52 device: &wgpu::Device,
53 handle: &mut ExternalTextureHandle,
54 desc: RenderTextureDesc,
55 sample_count: u32,
56 ) {
57 if handle.desc == desc && handle.sample_count == sample_count {
58 return;
59 }
60 let mut inner = self.inner.write();
61 if let Some(entry) = inner.entries.get_mut(&handle.id) {
62 entry.rebuild(device, desc.clone(), sample_count);
63 } else {
64 let entry = ExternalTextureEntry::new(device, desc.clone(), sample_count);
65 inner.entries.insert(handle.id, entry);
66 }
67 handle.desc = desc;
68 handle.sample_count = sample_count;
69 }
70
71 pub fn mark_used(&self, id: u32, frame_index: u64) {
73 if let Some(entry) = self.inner.write().entries.get_mut(&id) {
74 entry.last_used_frame = frame_index;
75 }
76 }
77
78 pub fn collect_garbage(&self, frame_index: u64, delay_frames: u64) {
80 let mut inner = self.inner.write();
81 inner.entries.retain(|_, entry| {
82 if entry.ref_count > 0 {
83 return true;
84 }
85 frame_index <= entry.last_used_frame.saturating_add(delay_frames)
86 });
87 }
88
89 pub(crate) fn slot(&self, id: u32) -> Option<ExternalTextureSlotGuard<'_>> {
90 let guard = self.inner.write();
91 guard
92 .entries
93 .contains_key(&id)
94 .then_some(ExternalTextureSlotGuard { guard, id })
95 }
96
97 fn add_ref(&self, id: u32) {
98 self.inner.write().add_ref(id);
99 }
100
101 fn release(&self, id: u32) {
102 self.inner.write().release(id);
103 }
104}
105
106pub struct ExternalTextureHandle {
108 id: u32,
109 desc: RenderTextureDesc,
110 sample_count: u32,
111 registry: ExternalTextureRegistry,
112}
113
114impl ExternalTextureHandle {
115 pub fn id(&self) -> u32 {
117 self.id
118 }
119
120 pub fn desc(&self, clear_on_first_use: bool) -> ExternalTextureDesc {
122 ExternalTextureDesc {
123 handle_id: self.id,
124 size: self.desc.size,
125 format: self.desc.format,
126 sample_count: self.sample_count,
127 clear_on_first_use,
128 }
129 }
130
131 pub fn ensure(
133 &mut self,
134 registry: &ExternalTextureRegistry,
135 device: &wgpu::Device,
136 desc: RenderTextureDesc,
137 sample_count: u32,
138 ) {
139 registry.ensure(device, self, desc, sample_count);
140 }
141}
142
143impl Clone for ExternalTextureHandle {
144 fn clone(&self) -> Self {
145 self.registry.add_ref(self.id);
146 Self {
147 id: self.id,
148 desc: self.desc.clone(),
149 sample_count: self.sample_count,
150 registry: self.registry.clone(),
151 }
152 }
153}
154
155impl Drop for ExternalTextureHandle {
156 fn drop(&mut self) {
157 self.registry.release(self.id);
158 }
159}
160
161pub(crate) struct ExternalTextureSlotGuard<'a> {
162 guard: RwLockWriteGuard<'a, ExternalTextureRegistryInner>,
163 id: u32,
164}
165
166impl ExternalTextureSlotGuard<'_> {
167 pub fn size(&self) -> PxSize {
168 self.entry().desc.size
169 }
170
171 pub fn front_view(&self) -> wgpu::TextureView {
172 self.entry().front.clone()
173 }
174
175 pub fn back_view(&self) -> wgpu::TextureView {
176 self.entry().back.clone()
177 }
178
179 pub fn msaa_view(&self) -> Option<wgpu::TextureView> {
180 self.entry().msaa_view.clone()
181 }
182
183 pub fn swap_front_back(&mut self) {
184 let entry = self.entry_mut();
185 std::mem::swap(&mut entry.front, &mut entry.back);
186 }
187
188 fn entry(&self) -> &ExternalTextureEntry {
189 self.guard
190 .entries
191 .get(&self.id)
192 .expect("missing external texture entry")
193 }
194
195 fn entry_mut(&mut self) -> &mut ExternalTextureEntry {
196 self.guard
197 .entries
198 .get_mut(&self.id)
199 .expect("missing external texture entry")
200 }
201}
202
203#[derive(Default)]
204struct ExternalTextureRegistryInner {
205 next_id: u32,
206 entries: HashMap<u32, ExternalTextureEntry>,
207}
208
209impl ExternalTextureRegistryInner {
210 fn add_ref(&mut self, id: u32) {
211 if let Some(entry) = self.entries.get_mut(&id) {
212 entry.ref_count = entry.ref_count.saturating_add(1);
213 }
214 }
215
216 fn release(&mut self, id: u32) {
217 if let Some(entry) = self.entries.get_mut(&id) {
218 entry.ref_count = entry.ref_count.saturating_sub(1);
219 }
220 }
221}
222
223struct ExternalTextureEntry {
224 desc: RenderTextureDesc,
225 sample_count: u32,
226 front: wgpu::TextureView,
227 back: wgpu::TextureView,
228 msaa_view: Option<wgpu::TextureView>,
229 last_used_frame: u64,
230 ref_count: u32,
231}
232
233impl ExternalTextureEntry {
234 fn new(device: &wgpu::Device, desc: RenderTextureDesc, sample_count: u32) -> Self {
235 let (front, back, msaa_view) = create_views(device, &desc, sample_count);
236 Self {
237 desc,
238 sample_count,
239 front,
240 back,
241 msaa_view,
242 last_used_frame: 0,
243 ref_count: 1,
244 }
245 }
246
247 fn rebuild(&mut self, device: &wgpu::Device, desc: RenderTextureDesc, sample_count: u32) {
248 let (front, back, msaa_view) = create_views(device, &desc, sample_count);
249 self.desc = desc;
250 self.sample_count = sample_count;
251 self.front = front;
252 self.back = back;
253 self.msaa_view = msaa_view;
254 }
255}
256
257fn create_views(
258 device: &wgpu::Device,
259 desc: &RenderTextureDesc,
260 sample_count: u32,
261) -> (
262 wgpu::TextureView,
263 wgpu::TextureView,
264 Option<wgpu::TextureView>,
265) {
266 let front = create_texture_view(device, desc, "External Front");
267 let back = create_texture_view(device, desc, "External Back");
268 let msaa_view = if sample_count > 1 {
269 Some(create_msaa_view(device, desc, sample_count))
270 } else {
271 None
272 };
273 (front, back, msaa_view)
274}
275
276fn create_texture_view(
277 device: &wgpu::Device,
278 desc: &RenderTextureDesc,
279 label: &str,
280) -> wgpu::TextureView {
281 let width = desc.size.width.positive().max(1);
282 let height = desc.size.height.positive().max(1);
283 let texture = device.create_texture(&wgpu::TextureDescriptor {
284 label: Some(label),
285 size: wgpu::Extent3d {
286 width,
287 height,
288 depth_or_array_layers: 1,
289 },
290 mip_level_count: 1,
291 sample_count: 1,
292 dimension: wgpu::TextureDimension::D2,
293 format: desc.format,
294 usage: wgpu::TextureUsages::RENDER_ATTACHMENT
295 | wgpu::TextureUsages::TEXTURE_BINDING
296 | wgpu::TextureUsages::STORAGE_BINDING
297 | wgpu::TextureUsages::COPY_SRC
298 | wgpu::TextureUsages::COPY_DST,
299 view_formats: &[],
300 });
301 texture.create_view(&wgpu::TextureViewDescriptor::default())
302}
303
304fn create_msaa_view(
305 device: &wgpu::Device,
306 desc: &RenderTextureDesc,
307 sample_count: u32,
308) -> wgpu::TextureView {
309 let width = desc.size.width.positive().max(1);
310 let height = desc.size.height.positive().max(1);
311 let texture = device.create_texture(&wgpu::TextureDescriptor {
312 label: Some("External MSAA"),
313 size: wgpu::Extent3d {
314 width,
315 height,
316 depth_or_array_layers: 1,
317 },
318 mip_level_count: 1,
319 sample_count,
320 dimension: wgpu::TextureDimension::D2,
321 format: desc.format,
322 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
323 view_formats: &[],
324 });
325 texture.create_view(&wgpu::TextureViewDescriptor::default())
326}