1use super::CachedTexture;
2use crate::render_resource::{TextureFormat, TextureView};
3use alloc::sync::Arc;
4use bevy_color::LinearRgba;
5use core::sync::atomic::{AtomicBool, Ordering};
6use wgpu::{
7 Color as WgpuColor, LoadOp, Operations, RenderPassColorAttachment,
8 RenderPassDepthStencilAttachment, StoreOp, TextureViewDescriptor,
9};
10
11#[derive(Clone)]
13pub struct ColorAttachment {
14 pub texture: CachedTexture,
15 pub resolve_target: Option<CachedTexture>,
16 pub previous_frame_texture: Option<CachedTexture>,
17 clear_color: Option<WgpuColor>,
18 is_first_call: Arc<AtomicBool>,
19}
20
21impl ColorAttachment {
22 pub fn new(
23 texture: CachedTexture,
24 resolve_target: Option<CachedTexture>,
25 previous_frame_texture: Option<CachedTexture>,
26 clear_color: Option<WgpuColor>,
27 ) -> Self {
28 Self {
29 texture,
30 resolve_target,
31 previous_frame_texture,
32 clear_color,
33 is_first_call: Arc::new(AtomicBool::new(true)),
34 }
35 }
36
37 pub fn get_attachment(&self) -> RenderPassColorAttachment<'_> {
42 if let Some(resolve_target) = self.resolve_target.as_ref() {
43 let first_call = self.is_first_call.fetch_and(false, Ordering::SeqCst);
44
45 RenderPassColorAttachment {
46 view: &resolve_target.default_view,
47 depth_slice: None,
48 resolve_target: Some(&self.texture.default_view),
49 ops: Operations {
50 load: match (self.clear_color, first_call) {
51 (Some(clear_color), true) => LoadOp::Clear(clear_color),
52 (None, _) | (Some(_), false) => LoadOp::Load,
53 },
54 store: StoreOp::Store,
55 },
56 }
57 } else {
58 self.get_unsampled_attachment()
59 }
60 }
61
62 pub fn get_unsampled_attachment(&self) -> RenderPassColorAttachment<'_> {
67 let first_call = self.is_first_call.fetch_and(false, Ordering::SeqCst);
68
69 RenderPassColorAttachment {
70 view: &self.texture.default_view,
71 depth_slice: None,
72 resolve_target: None,
73 ops: Operations {
74 load: match (self.clear_color, first_call) {
75 (Some(clear_color), true) => LoadOp::Clear(clear_color),
76 (None, _) | (Some(_), false) => LoadOp::Load,
77 },
78 store: StoreOp::Store,
79 },
80 }
81 }
82
83 pub(crate) fn mark_as_cleared(&self) {
84 self.is_first_call.store(false, Ordering::SeqCst);
85 }
86}
87
88#[derive(Clone)]
89enum DepthStencilData {
90 DepthOnly {
91 depth_clear_value: Option<f32>,
92 depth_is_first_call: Arc<AtomicBool>,
93 },
94 StencilOnly {
95 stencil_clear_value: Option<u32>,
96 stencil_is_first_call: Arc<AtomicBool>,
97 },
98 DepthStencil {
99 depth_clear_value: Option<f32>,
100 stencil_clear_value: Option<u32>,
101 depth_is_first_call: Arc<AtomicBool>,
102 stencil_is_first_call: Arc<AtomicBool>,
103 },
104}
105
106#[derive(Clone)]
113pub enum DepthStencilViews {
114 DepthOnly { depth_view: TextureView },
116 StencilOnly { stencil_view: TextureView },
118 DepthStencil {
120 combined_view: TextureView,
122 depth_view: TextureView,
124 stencil_view: TextureView,
126 },
127}
128
129impl DepthStencilViews {
130 pub fn depth_only_view(&self) -> Option<&TextureView> {
132 match self {
133 DepthStencilViews::DepthStencil { depth_view, .. }
134 | DepthStencilViews::DepthOnly { depth_view } => Some(depth_view),
135 DepthStencilViews::StencilOnly { .. } => None,
136 }
137 }
138
139 pub fn stencil_only_view(&self) -> Option<&TextureView> {
141 match self {
142 DepthStencilViews::DepthOnly { .. } => None,
143 DepthStencilViews::DepthStencil { stencil_view, .. }
144 | DepthStencilViews::StencilOnly { stencil_view } => Some(stencil_view),
145 }
146 }
147
148 pub fn attachment_view(&self) -> &TextureView {
150 match self {
151 DepthStencilViews::DepthOnly { depth_view } => depth_view,
152 DepthStencilViews::StencilOnly { stencil_view } => stencil_view,
153 DepthStencilViews::DepthStencil { combined_view, .. } => combined_view,
154 }
155 }
156
157 fn from_texture(texture: &CachedTexture) -> Self {
159 let depth_view = || {
160 texture.texture.create_view(&TextureViewDescriptor {
161 aspect: wgpu::TextureAspect::DepthOnly,
162 ..Default::default()
163 })
164 };
165
166 let stencil_view = || {
167 texture.texture.create_view(&TextureViewDescriptor {
168 aspect: wgpu::TextureAspect::StencilOnly,
169 ..Default::default()
170 })
171 };
172
173 match texture.texture.format().channels() {
174 wgpu_types::TextureChannel::DEPTH_STENCIL => DepthStencilViews::DepthStencil {
175 combined_view: texture
176 .texture
177 .create_view(&TextureViewDescriptor::default()),
178 depth_view: depth_view(),
179 stencil_view: stencil_view(),
180 },
181 wgpu_types::TextureChannel::DEPTH => DepthStencilViews::DepthOnly {
182 depth_view: depth_view(),
183 },
184 wgpu_types::TextureChannel::STENCIL => DepthStencilViews::StencilOnly {
185 stencil_view: stencil_view(),
186 },
187 _ => {
188 panic!(
189 "Can't create depth attachment. Texture format is not a depth-stencil format."
190 )
191 }
192 }
193 }
194}
195
196#[derive(Clone)]
198pub struct DepthStencilAttachment {
199 pub texture: CachedTexture,
200 pub previous_frame_texture: Option<CachedTexture>,
201 depth_stencil_view_attachment: DepthStencilViewAttachment,
202 pub previous_frame_depth_stencil_views: Option<DepthStencilViews>,
203}
204
205impl DepthStencilAttachment {
206 pub fn depth_stencil_views(&self) -> &DepthStencilViews {
207 &self.depth_stencil_view_attachment.depth_stencil_views
208 }
209
210 pub fn new(
211 texture: CachedTexture,
212 previous_frame_texture: Option<CachedTexture>,
213 depth_clear_value: Option<f32>,
214 stencil_clear_value: Option<u32>,
215 ) -> Self {
216 let depth_stencil_views = DepthStencilViews::from_texture(&texture);
217
218 let previous_frame_depth_stencil_views = previous_frame_texture
219 .as_ref()
220 .map(DepthStencilViews::from_texture);
221
222 Self {
223 texture,
224 previous_frame_texture,
225 depth_stencil_view_attachment: DepthStencilViewAttachment::new(
226 depth_stencil_views,
227 depth_clear_value,
228 stencil_clear_value,
229 ),
230 previous_frame_depth_stencil_views,
231 }
232 }
233
234 pub fn get_attachment(&self, store: StoreOp) -> RenderPassDepthStencilAttachment<'_> {
238 self.depth_stencil_view_attachment.get_attachment(store)
239 }
240
241 pub fn prepare_for_new_frame(&self) {
244 self.depth_stencil_view_attachment.prepare_for_new_frame();
245 }
246}
247
248#[derive(Clone)]
250pub struct DepthStencilViewAttachment {
251 pub depth_stencil_views: DepthStencilViews,
252 depth_stencil_data: DepthStencilData,
253}
254
255impl DepthStencilViewAttachment {
256 pub fn new(
257 depth_stencil_views: DepthStencilViews,
258 depth_clear_value: Option<f32>,
259 stencil_clear_value: Option<u32>,
260 ) -> Self {
261 let depth_stencil_data = match &depth_stencil_views {
262 DepthStencilViews::DepthStencil { .. } => DepthStencilData::DepthStencil {
263 depth_clear_value,
264 stencil_clear_value,
265 depth_is_first_call: Arc::new(AtomicBool::new(depth_clear_value.is_some())),
266 stencil_is_first_call: Arc::new(AtomicBool::new(stencil_clear_value.is_some())),
267 },
268 DepthStencilViews::DepthOnly { .. } => DepthStencilData::DepthOnly {
269 depth_clear_value,
270 depth_is_first_call: Arc::new(AtomicBool::new(depth_clear_value.is_some())),
271 },
272 DepthStencilViews::StencilOnly { .. } => DepthStencilData::StencilOnly {
273 stencil_clear_value,
274 stencil_is_first_call: Arc::new(AtomicBool::new(stencil_clear_value.is_some())),
275 },
276 };
277 Self {
278 depth_stencil_views,
279 depth_stencil_data,
280 }
281 }
282
283 pub fn get_attachment(&self, store: StoreOp) -> RenderPassDepthStencilAttachment<'_> {
287 match (&self.depth_stencil_data, &self.depth_stencil_views) {
288 (
289 DepthStencilData::DepthStencil {
290 depth_clear_value,
291 stencil_clear_value,
292 depth_is_first_call,
293 stencil_is_first_call,
294 },
295 DepthStencilViews::DepthStencil { combined_view, .. },
296 ) => {
297 let depth_is_first_call =
298 depth_is_first_call.fetch_and(store != StoreOp::Store, Ordering::Relaxed);
299 let stencil_is_first_call =
300 stencil_is_first_call.fetch_and(store != StoreOp::Store, Ordering::Relaxed);
301 RenderPassDepthStencilAttachment {
302 view: combined_view,
303 depth_ops: Some(Operations {
304 load: if depth_is_first_call {
305 LoadOp::Clear(depth_clear_value.unwrap())
307 } else {
308 LoadOp::Load
309 },
310 store,
311 }),
312 stencil_ops: Some(Operations {
313 load: if stencil_is_first_call {
314 LoadOp::Clear(stencil_clear_value.unwrap())
316 } else {
317 LoadOp::Load
318 },
319 store,
320 }),
321 }
322 }
323 (
324 DepthStencilData::DepthOnly {
325 depth_clear_value,
326 depth_is_first_call,
327 },
328 DepthStencilViews::DepthOnly { depth_view },
329 ) => {
330 let depth_is_first_call =
331 depth_is_first_call.fetch_and(store != StoreOp::Store, Ordering::Relaxed);
332 RenderPassDepthStencilAttachment {
333 view: depth_view,
334 depth_ops: Some(Operations {
335 load: if depth_is_first_call {
336 LoadOp::Clear(depth_clear_value.unwrap())
338 } else {
339 LoadOp::Load
340 },
341 store,
342 }),
343 stencil_ops: None,
344 }
345 }
346 (
347 DepthStencilData::StencilOnly {
348 stencil_clear_value,
349 stencil_is_first_call,
350 },
351 DepthStencilViews::StencilOnly { stencil_view },
352 ) => {
353 let stencil_is_first_call =
354 stencil_is_first_call.fetch_and(store != StoreOp::Store, Ordering::Relaxed);
355 RenderPassDepthStencilAttachment {
356 view: stencil_view,
357 depth_ops: None,
358 stencil_ops: Some(Operations {
359 load: if stencil_is_first_call {
360 LoadOp::Clear(stencil_clear_value.unwrap())
362 } else {
363 LoadOp::Load
364 },
365 store,
366 }),
367 }
368 }
369
370 _ => unreachable!("Mismatched depth stencil data and views"),
371 }
372 }
373
374 pub fn prepare_for_new_frame(&self) {
377 match &self.depth_stencil_data {
378 DepthStencilData::DepthStencil {
379 depth_is_first_call,
380 stencil_is_first_call,
381 ..
382 } => {
383 depth_is_first_call.store(true, Ordering::Relaxed);
384 stencil_is_first_call.store(true, Ordering::Relaxed);
385 }
386 DepthStencilData::DepthOnly {
387 depth_is_first_call,
388 ..
389 } => {
390 depth_is_first_call.store(true, Ordering::Relaxed);
391 }
392 DepthStencilData::StencilOnly {
393 stencil_is_first_call,
394 ..
395 } => {
396 stencil_is_first_call.store(true, Ordering::Relaxed);
397 }
398 }
399 }
400}
401
402#[derive(Clone)]
405pub struct OutputColorAttachment {
406 pub view: TextureView,
407 pub view_format: TextureFormat,
408 is_first_call: Arc<AtomicBool>,
409}
410
411impl OutputColorAttachment {
412 pub fn new(view: TextureView, view_format: TextureFormat) -> Self {
413 Self {
414 view,
415 view_format,
416 is_first_call: Arc::new(AtomicBool::new(true)),
417 }
418 }
419
420 pub fn get_attachment(&self, clear_color: Option<LinearRgba>) -> RenderPassColorAttachment<'_> {
424 let first_call = self.is_first_call.fetch_and(false, Ordering::SeqCst);
425
426 RenderPassColorAttachment {
427 view: &self.view,
428 depth_slice: None,
429 resolve_target: None,
430 ops: Operations {
431 load: match (clear_color, first_call) {
432 (Some(clear_color), true) => LoadOp::Clear(clear_color.into()),
433 (None, _) | (Some(_), false) => LoadOp::Load,
434 },
435 store: StoreOp::Store,
436 },
437 }
438 }
439
440 pub fn needs_present(&self) -> bool {
444 !self.is_first_call.load(Ordering::SeqCst)
445 }
446}