renderling/stage/
cpu.rs

1//! GPU staging area.
2//!
3//! The `Stage` object contains a slab buffer and a render pipeline.
4//! It is used to stage [`Renderlet`]s for rendering.
5use core::sync::atomic::{AtomicU32, Ordering};
6use crabslab::{Array, Id, Slab, SlabItem};
7use rustc_hash::FxHashMap;
8use snafu::Snafu;
9use std::{
10    ops::{Deref, DerefMut},
11    sync::{atomic::AtomicBool, Arc, Mutex, RwLock},
12};
13
14use crate::{
15    atlas::{Atlas, AtlasError, AtlasImage, AtlasImageError, AtlasTexture},
16    bloom::Bloom,
17    camera::Camera,
18    pbr::{debug::DebugMode, light::Light, PbrConfig},
19    skybox::{Skybox, SkyboxRenderPipeline},
20    slab::*,
21    stage::Renderlet,
22    texture::{DepthTexture, Texture},
23    tonemapping::Tonemapping,
24    transform::Transform,
25};
26
27use super::*;
28
29#[derive(Debug, Snafu)]
30pub enum StageError {
31    #[snafu(display("{source}"))]
32    Atlas { source: AtlasError },
33}
34
35impl From<AtlasError> for StageError {
36    fn from(source: AtlasError) -> Self {
37        Self::Atlas { source }
38    }
39}
40
41/// The count at which a `Renderlet` will be automatically removed from the
42/// stage and recycled.
43///
44/// This constant is exposed for downstream authors to write renderers that
45/// may need to store the renderlet in a system.
46///
47/// * +1 for the clone in [`Stage`]'s renderlets (added via
48///   [`Stage::add_renderlet`]), required to draw
49/// * +1 for the clone in [`Stage`]'s [`SlabAllocator`]'s update sources, needed
50///   to update the GPU
51pub const RENDERLET_STRONG_COUNT_LOWER_BOUND: usize = 2;
52
53struct IndirectDraws {
54    slab: SlabAllocator<wgpu::Buffer>,
55    draws: Vec<Gpu<DrawIndirectArgs>>,
56}
57
58impl From<&Hybrid<Renderlet>> for DrawIndirectArgs {
59    fn from(h: &Hybrid<Renderlet>) -> Self {
60        let r = h.get();
61        DrawIndirectArgs {
62            vertex_count: if r.indices_array.is_null() {
63                r.vertices_array.len() as u32
64            } else {
65                r.indices_array.len() as u32
66            },
67            instance_count: 1,
68            first_vertex: 0,
69            first_instance: h.id().into(),
70        }
71    }
72}
73
74/// Provides a way to communicate with the stage about how you'd like your
75/// objects drawn.
76#[derive(Default)]
77pub(crate) struct StageDraws {
78    hybrids: Vec<Hybrid<Renderlet>>,
79    indirect: Option<IndirectDraws>,
80}
81
82fn create_msaa_textureview(
83    device: &wgpu::Device,
84    width: u32,
85    height: u32,
86    format: wgpu::TextureFormat,
87    sample_count: u32,
88) -> wgpu::TextureView {
89    device
90        .create_texture(&wgpu::TextureDescriptor {
91            label: Some("stage msaa render target"),
92            size: wgpu::Extent3d {
93                width,
94                height,
95                depth_or_array_layers: 1,
96            },
97            mip_level_count: 1,
98            sample_count,
99            dimension: wgpu::TextureDimension::D2,
100            format,
101            usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
102            view_formats: &[],
103        })
104        .create_view(&wgpu::TextureViewDescriptor::default())
105}
106
107fn create_stage_render_pipeline(
108    device: &wgpu::Device,
109    multisample_count: u32,
110) -> wgpu::RenderPipeline {
111    log::trace!("creating stage render pipeline");
112    let label = Some("stage render");
113    let vertex_linkage = crate::linkage::renderlet_vertex::linkage(device);
114    let fragment_linkage = crate::linkage::renderlet_fragment::linkage(device);
115    let stage_slab_buffers_layout = crate::linkage::slab_bindgroup_layout(device);
116    let atlas_and_skybox_layout = crate::linkage::atlas_and_skybox_bindgroup_layout(device);
117    let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
118        label,
119        bind_group_layouts: &[&stage_slab_buffers_layout, &atlas_and_skybox_layout],
120        push_constant_ranges: &[],
121    });
122
123    device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
124        label,
125        layout: Some(&layout),
126        vertex: wgpu::VertexState {
127            module: &vertex_linkage.module,
128            entry_point: vertex_linkage.entry_point,
129            buffers: &[],
130            compilation_options: Default::default(),
131        },
132        primitive: wgpu::PrimitiveState {
133            topology: wgpu::PrimitiveTopology::TriangleList,
134            strip_index_format: None,
135            front_face: wgpu::FrontFace::Ccw,
136            cull_mode: None,
137            unclipped_depth: false,
138            polygon_mode: wgpu::PolygonMode::Fill,
139            conservative: false,
140        },
141        depth_stencil: Some(wgpu::DepthStencilState {
142            format: wgpu::TextureFormat::Depth32Float,
143            depth_write_enabled: true,
144            depth_compare: wgpu::CompareFunction::Less,
145            stencil: wgpu::StencilState::default(),
146            bias: wgpu::DepthBiasState::default(),
147        }),
148        multisample: wgpu::MultisampleState {
149            mask: !0,
150            alpha_to_coverage_enabled: false,
151            count: multisample_count,
152        },
153        fragment: Some(wgpu::FragmentState {
154            module: &fragment_linkage.module,
155            entry_point: fragment_linkage.entry_point,
156            targets: &[Some(wgpu::ColorTargetState {
157                format: wgpu::TextureFormat::Rgba16Float,
158                blend: Some(wgpu::BlendState::ALPHA_BLENDING),
159                write_mask: wgpu::ColorWrites::ALL,
160            })],
161            compilation_options: Default::default(),
162        }),
163        multiview: None,
164        cache: None,
165    })
166}
167
168/// Represents an entire scene worth of rendering data.
169///
170/// A clone of a stage is a reference to the same stage.
171///
172/// ## Note
173/// Only available on the CPU. Not available in shaders.
174#[derive(Clone)]
175pub struct Stage {
176    pub(crate) device: Arc<wgpu::Device>,
177    pub(crate) queue: Arc<wgpu::Queue>,
178
179    pub(crate) mngr: SlabAllocator<wgpu::Buffer>,
180
181    pub(crate) pbr_config: Hybrid<PbrConfig>,
182    pub(crate) lights: Arc<RwLock<HybridArray<Id<Light>>>>,
183
184    pub(crate) stage_pipeline: Arc<RwLock<wgpu::RenderPipeline>>,
185    pub(crate) skybox_pipeline: Arc<RwLock<Option<Arc<SkyboxRenderPipeline>>>>,
186
187    pub(crate) hdr_texture: Arc<RwLock<Texture>>,
188    pub(crate) depth_texture: Arc<RwLock<Texture>>,
189    pub(crate) msaa_render_target: Arc<RwLock<Option<wgpu::TextureView>>>,
190    pub(crate) msaa_sample_count: Arc<AtomicU32>,
191    pub(crate) clear_color_attachments: Arc<AtomicBool>,
192    pub(crate) clear_depth_attachments: Arc<AtomicBool>,
193
194    pub(crate) atlas: Atlas,
195    pub(crate) bloom: Bloom,
196    pub(crate) skybox: Arc<RwLock<Skybox>>,
197    pub(crate) tonemapping: Tonemapping,
198    pub(crate) background_color: Arc<RwLock<wgpu::Color>>,
199
200    pub(crate) has_skybox: Arc<AtomicBool>,
201    pub(crate) has_bloom: Arc<AtomicBool>,
202
203    pub(crate) skybox_bindgroup: Arc<Mutex<Option<Arc<wgpu::BindGroup>>>>,
204    pub(crate) buffers_bindgroup: Arc<Mutex<Option<Arc<wgpu::BindGroup>>>>,
205    pub(crate) textures_bindgroup: Arc<Mutex<Option<Arc<wgpu::BindGroup>>>>,
206
207    pub(crate) draws: Arc<RwLock<StageDraws>>,
208}
209
210impl Deref for Stage {
211    type Target = SlabAllocator<wgpu::Buffer>;
212
213    fn deref(&self) -> &Self::Target {
214        &self.mngr
215    }
216}
217
218impl Stage {
219    /// Create a new stage.
220    pub fn new(ctx: &crate::Context) -> Self {
221        let (device, queue) = ctx.get_device_and_queue_owned();
222        let resolution @ UVec2 { x: w, y: h } = ctx.get_size();
223        let atlas_size = *ctx.atlas_size.read().unwrap();
224        let atlas = Atlas::new(&device, &queue, atlas_size).unwrap();
225        let mngr = SlabAllocator::default();
226        let pbr_config = mngr.new_value(PbrConfig {
227            atlas_size: UVec2::new(atlas_size.width, atlas_size.height),
228            resolution,
229            ..Default::default()
230        });
231        let multisample_count = 1;
232        let lights = mngr.new_array(vec![Id::<Light>::NONE; 16]);
233        let hdr_texture = Arc::new(RwLock::new(Texture::create_hdr_texture(
234            &device,
235            w,
236            h,
237            multisample_count,
238        )));
239        let depth_texture = Arc::new(RwLock::new(Texture::create_depth_texture(
240            &device,
241            w,
242            h,
243            multisample_count,
244        )));
245        let msaa_render_target = Default::default();
246        // UNWRAP: safe because no other references at this point (created above^)
247        let bloom = Bloom::new(&device, &queue, &hdr_texture.read().unwrap());
248        let tonemapping = Tonemapping::new(
249            &device,
250            &queue,
251            ctx.get_render_target().format().add_srgb_suffix(),
252            &bloom.get_mix_texture(),
253        );
254        let draws = StageDraws {
255            hybrids: vec![],
256            indirect: {
257                if ctx.get_adapter().features().contains(
258                    wgpu::Features::INDIRECT_FIRST_INSTANCE | wgpu::Features::MULTI_DRAW_INDIRECT,
259                ) {
260                    log::info!("creating stage to use Renderpass::multi_draw_indirect");
261                    Some(IndirectDraws {
262                        slab: SlabAllocator::default(),
263                        draws: vec![],
264                    })
265                } else {
266                    None
267                }
268            },
269        };
270
271        Self {
272            mngr,
273            pbr_config,
274            lights: Arc::new(RwLock::new(lights)),
275
276            stage_pipeline: Arc::new(RwLock::new(create_stage_render_pipeline(
277                &device,
278                multisample_count,
279            ))),
280            atlas,
281            skybox: Arc::new(RwLock::new(Skybox::empty(&device, &queue))),
282            skybox_bindgroup: Default::default(),
283            skybox_pipeline: Default::default(),
284            has_skybox: Arc::new(AtomicBool::new(false)),
285            bloom,
286            tonemapping,
287            has_bloom: AtomicBool::from(true).into(),
288            buffers_bindgroup: Default::default(),
289            textures_bindgroup: Default::default(),
290            draws: Arc::new(RwLock::new(draws)),
291            hdr_texture,
292            depth_texture,
293            msaa_render_target,
294            msaa_sample_count: Arc::new(multisample_count.into()),
295            clear_color_attachments: Arc::new(true.into()),
296            clear_depth_attachments: Arc::new(true.into()),
297            background_color: Arc::new(RwLock::new(wgpu::Color::TRANSPARENT)),
298            device,
299            queue,
300        }
301    }
302
303    pub fn get_device_and_queue_owned(&self) -> (Arc<wgpu::Device>, Arc<wgpu::Queue>) {
304        (self.device.clone(), self.queue.clone())
305    }
306
307    pub fn set_background_color(&self, color: impl Into<Vec4>) {
308        let color = color.into();
309        *self.background_color.write().unwrap() = wgpu::Color {
310            r: color.x as f64,
311            g: color.y as f64,
312            b: color.z as f64,
313            a: color.w as f64,
314        };
315    }
316
317    pub fn with_background_color(self, color: impl Into<Vec4>) -> Self {
318        self.set_background_color(color);
319        self
320    }
321
322    /// Set the MSAA multisample count.
323    ///
324    /// Set to `1` to disable MSAA. Setting to `0` will be treated the same as
325    /// setting to `1`.
326    pub fn set_msaa_sample_count(&self, multisample_count: u32) {
327        let multisample_count = multisample_count.max(1);
328        let prev_multisample_count = self
329            .msaa_sample_count
330            .swap(multisample_count, Ordering::Relaxed);
331        if prev_multisample_count == multisample_count {
332            log::warn!("set_multisample_count: multisample count is unchanged, noop");
333            return;
334        }
335
336        log::debug!("setting multisample count to {multisample_count}");
337        // UNWRAP: POP
338        *self.stage_pipeline.write().unwrap() =
339            create_stage_render_pipeline(&self.device, multisample_count);
340        let size = self.get_size();
341        // UNWRAP: POP
342        *self.depth_texture.write().unwrap() =
343            Texture::create_depth_texture(&self.device, size.x, size.y, multisample_count);
344        // UNWRAP: POP
345        let format = self.hdr_texture.read().unwrap().texture.format();
346        *self.msaa_render_target.write().unwrap() = if multisample_count == 1 {
347            None
348        } else {
349            Some(create_msaa_textureview(
350                &self.device,
351                size.x,
352                size.y,
353                format,
354                multisample_count,
355            ))
356        };
357
358        // Invalidate the textures bindgroup - it must be recreated
359        let _ = self.textures_bindgroup.lock().unwrap().take();
360    }
361
362    /// Set the MSAA multisample count.
363    ///
364    /// Set to `1` to disable MSAA. Setting to `0` will be treated the same as
365    /// setting to `1`.
366    pub fn with_msaa_sample_count(self, multisample_count: u32) -> Self {
367        self.set_msaa_sample_count(multisample_count);
368        self
369    }
370
371    /// Set whether color attachments are cleared before rendering.
372    pub fn set_clear_color_attachments(&self, should_clear: bool) {
373        self.clear_color_attachments
374            .store(should_clear, Ordering::Relaxed);
375    }
376
377    /// Set whether color attachments are cleared before rendering.
378    pub fn with_clear_color_attachments(self, should_clear: bool) -> Self {
379        self.set_clear_color_attachments(should_clear);
380        self
381    }
382
383    /// Set whether color attachments are cleared before rendering.
384    pub fn set_clear_depth_attachments(&self, should_clear: bool) {
385        self.clear_depth_attachments
386            .store(should_clear, Ordering::Relaxed);
387    }
388
389    /// Set whether color attachments are cleared before rendering.
390    pub fn with_clear_depth_attachments(self, should_clear: bool) -> Self {
391        self.set_clear_depth_attachments(should_clear);
392        self
393    }
394
395    /// Set the debug mode.
396    pub fn set_debug_mode(&self, debug_mode: DebugMode) {
397        self.pbr_config.modify(|cfg| cfg.debug_mode = debug_mode);
398    }
399
400    /// Set the debug mode.
401    pub fn with_debug_mode(self, debug_mode: DebugMode) -> Self {
402        self.set_debug_mode(debug_mode);
403        self
404    }
405
406    /// Set whether the stage uses lighting.
407    pub fn set_has_lighting(&self, use_lighting: bool) {
408        self.pbr_config
409            .modify(|cfg| cfg.has_lighting = use_lighting);
410    }
411
412    /// Set whether the stage uses lighting.
413    pub fn with_lighting(self, use_lighting: bool) -> Self {
414        self.set_has_lighting(use_lighting);
415        self
416    }
417
418    /// Set whether to use vertex skinning.
419    pub fn set_has_vertex_skinning(&self, use_skinning: bool) {
420        self.pbr_config
421            .modify(|cfg| cfg.has_skinning = use_skinning);
422    }
423
424    /// Set whether to use vertex skinning.
425    pub fn with_vertex_skinning(self, use_skinning: bool) -> Self {
426        self.set_has_vertex_skinning(use_skinning);
427        self
428    }
429
430    /// Set the lights to use for shading.
431    pub fn set_lights(&self, lights: impl IntoIterator<Item = Id<Light>>) {
432        log::trace!("setting lights");
433        let lights = self.mngr.new_array(lights);
434        self.pbr_config.modify(|cfg| {
435            cfg.light_array = lights.array();
436        });
437        // UNWRAP: POP
438        *self.lights.write().unwrap() = lights;
439    }
440
441    pub fn get_size(&self) -> UVec2 {
442        // UNWRAP: panic on purpose
443        let hdr = self.hdr_texture.read().unwrap();
444        let w = hdr.width();
445        let h = hdr.height();
446        UVec2::new(w, h)
447    }
448
449    pub fn set_size(&self, size: UVec2) {
450        if size == self.get_size() {
451            return;
452        }
453
454        self.pbr_config.modify(|cfg| cfg.resolution = size);
455        let hdr_texture = Texture::create_hdr_texture(&self.device, size.x, size.y, 1);
456        let sample_count = self.msaa_sample_count.load(Ordering::Relaxed);
457        if let Some(msaa_view) = self.msaa_render_target.write().unwrap().as_mut() {
458            *msaa_view = create_msaa_textureview(
459                &self.device,
460                size.x,
461                size.y,
462                hdr_texture.texture.format(),
463                sample_count,
464            );
465        }
466
467        // UNWRAP: panic on purpose
468        *self.depth_texture.write().unwrap() =
469            Texture::create_depth_texture(&self.device, size.x, size.y, sample_count);
470        self.bloom
471            .set_hdr_texture(&self.device, &self.queue, &hdr_texture);
472        self.tonemapping.set_hdr_texture(&self.device, &hdr_texture);
473        *self.hdr_texture.write().unwrap() = hdr_texture;
474
475        let _ = self.skybox_bindgroup.lock().unwrap().take();
476        let _ = self.textures_bindgroup.lock().unwrap().take();
477    }
478
479    pub fn with_size(self, size: UVec2) -> Self {
480        self.set_size(size);
481        self
482    }
483
484    /// Set the size of the atlas.
485    ///
486    /// This will cause a repacking.
487    pub fn set_atlas_size(&self, size: wgpu::Extent3d) -> Result<(), StageError> {
488        self.atlas.resize(&self.device, &self.queue, size)?;
489        Ok(())
490    }
491
492    /// Add images to the set of atlas images.
493    ///
494    /// Adding an image can be quite expensive, as it requires creating a new texture
495    /// array for the atlas and repacking all previous images. For that reason it is
496    /// good to batch images to reduce the number of calls.
497    ///
498    /// This returns a vector of [`Hybrid<AtlasTexture>`](e), which
499    /// represent each image in the atlas maintained on the GPU. Dropping these entries
500    /// will invalidate those images and cause the atlas to be repacked, and any GPU
501    /// references to the underlying [`AtlasFrame`](f) and [`AtlasTexture`](t) will also
502    /// be invalidated.
503    ///
504    /// [e]: crate::atlas::Hybrid<AtlasTexture>
505    /// [f]: crate::atlas::AtlasFrame
506    /// [t]: crate::atlas::AtlasTexture
507    pub fn add_images(
508        &self,
509        images: impl IntoIterator<Item = impl Into<AtlasImage>>,
510    ) -> Result<Vec<Hybrid<AtlasTexture>>, StageError> {
511        let frames = self
512            .atlas
513            .add_images(&self.device, &self.queue, self, images)?;
514
515        // The textures bindgroup will have to be remade
516        let _ = self.textures_bindgroup.lock().unwrap().take();
517
518        Ok(frames)
519    }
520
521    /// Clear all images from the atlas.
522    ///
523    /// ## WARNING
524    /// This invalidates any previously staged `AtlasFrame`s.
525    pub fn clear_images(&self) -> Result<(), StageError> {
526        let none = Option::<AtlasImage>::None;
527        let _ = self.set_images(none)?;
528        Ok(())
529    }
530
531    /// Set the images to use for the atlas.
532    ///
533    /// Resets the atlas, packing it with the given images and returning a
534    /// vector of the frames already staged.
535    ///
536    /// ## WARNING
537    /// This invalidates any previously staged `Hybrid<AtlasTexture>`s.
538    pub fn set_images(
539        &self,
540        images: impl IntoIterator<Item = impl Into<AtlasImage>>,
541    ) -> Result<Vec<Hybrid<AtlasTexture>>, StageError> {
542        let frames = self
543            .atlas
544            .set_images(&self.device, &self.queue, self, images)?;
545
546        // The textures bindgroup will have to be remade
547        let _ = self.textures_bindgroup.lock().unwrap().take();
548
549        Ok(frames)
550    }
551
552    /// Set the skybox.
553    pub fn set_skybox(&self, skybox: Skybox) {
554        // UNWRAP: if we can't acquire the lock we want to panic.
555        let mut guard = self.skybox.write().unwrap();
556        *guard = skybox;
557        self.has_skybox
558            .store(true, std::sync::atomic::Ordering::Relaxed);
559        *self.skybox_bindgroup.lock().unwrap() = None;
560        *self.textures_bindgroup.lock().unwrap() = None;
561    }
562
563    /// Turn the bloom effect on or off.
564    pub fn set_has_bloom(&self, has_bloom: bool) {
565        self.has_bloom
566            .store(has_bloom, std::sync::atomic::Ordering::Relaxed);
567    }
568
569    /// Turn the bloom effect on or off.
570    pub fn with_bloom(self, has_bloom: bool) -> Self {
571        self.set_has_bloom(has_bloom);
572        self
573    }
574
575    /// Set the amount of bloom that is mixed in with the input image.
576    ///
577    /// Defaults to `0.04`.
578    pub fn set_bloom_mix_strength(&self, strength: f32) {
579        self.bloom.set_mix_strength(strength);
580    }
581
582    pub fn with_bloom_mix_strength(self, strength: f32) -> Self {
583        self.set_bloom_mix_strength(strength);
584        self
585    }
586
587    /// Sets the bloom filter radius, in pixels.
588    ///
589    /// Default is `1.0`.
590    pub fn set_bloom_filter_radius(&self, filter_radius: f32) {
591        self.bloom.set_filter_radius(filter_radius);
592    }
593
594    /// Sets the bloom filter radius, in pixels.
595    ///
596    /// Default is `1.0`.
597    pub fn with_bloom_filter_radius(self, filter_radius: f32) -> Self {
598        self.set_bloom_filter_radius(filter_radius);
599        self
600    }
601
602    /// Return the skybox render pipeline, creating it if necessary.
603    fn get_skybox_pipeline_and_bindgroup(
604        &self,
605        slab_buffer: &wgpu::Buffer,
606    ) -> (Arc<SkyboxRenderPipeline>, Arc<wgpu::BindGroup>) {
607        let msaa_sample_count = self.msaa_sample_count.load(Ordering::Relaxed);
608        // UNWRAP: safe because we're only ever called from the render thread.
609        let mut pipeline_guard = self.skybox_pipeline.write().unwrap();
610        let pipeline = if let Some(pipeline) = pipeline_guard.as_mut() {
611            if pipeline.msaa_sample_count() != msaa_sample_count {
612                *pipeline = Arc::new(crate::skybox::create_skybox_render_pipeline(
613                    &self.device,
614                    Texture::HDR_TEXTURE_FORMAT,
615                    Some(msaa_sample_count),
616                ));
617            }
618            pipeline.clone()
619        } else {
620            let pipeline = Arc::new(crate::skybox::create_skybox_render_pipeline(
621                &self.device,
622                Texture::HDR_TEXTURE_FORMAT,
623                Some(msaa_sample_count),
624            ));
625            *pipeline_guard = Some(pipeline.clone());
626            pipeline
627        };
628        // UNWRAP: safe because we're only ever called from the render thread.
629        let mut bindgroup = self.skybox_bindgroup.lock().unwrap();
630        let bindgroup = if let Some(bindgroup) = bindgroup.as_ref() {
631            bindgroup.clone()
632        } else {
633            let bg = Arc::new(crate::skybox::create_skybox_bindgroup(
634                &self.device,
635                slab_buffer,
636                &self.skybox.read().unwrap().environment_cubemap,
637            ));
638            *bindgroup = Some(bg.clone());
639            bg
640        };
641        (pipeline, bindgroup)
642    }
643
644    fn get_slab_buffers_bindgroup(&self, slab_buffer: &wgpu::Buffer) -> Arc<wgpu::BindGroup> {
645        // UNWRAP: safe because we're only ever called from the render thread.
646        let mut bindgroup = self.buffers_bindgroup.lock().unwrap();
647        if let Some(bindgroup) = bindgroup.as_ref() {
648            bindgroup.clone()
649        } else {
650            let b = Arc::new({
651                let device: &wgpu::Device = &self.device;
652                crate::linkage::slab_bindgroup(
653                    device,
654                    slab_buffer,
655                    // UNWRAP: POP
656                    &self.stage_pipeline.read().unwrap().get_bind_group_layout(0),
657                )
658            });
659            *bindgroup = Some(b.clone());
660            b
661        }
662    }
663
664    fn get_textures_bindgroup(&self) -> Arc<wgpu::BindGroup> {
665        // UNWRAP: safe because we're only ever called from the render thread.
666        let mut bindgroup = self.textures_bindgroup.lock().unwrap();
667        if let Some(bindgroup) = bindgroup.as_ref() {
668            bindgroup.clone()
669        } else {
670            let b = Arc::new(crate::linkage::atlas_and_skybox_bindgroup(
671                &self.device,
672                &{
673                    let this = &self;
674                    this.stage_pipeline.clone()
675                }
676                .read()
677                // UNWRAP: POP
678                .unwrap()
679                .get_bind_group_layout(1),
680                // UNWRAP: POP
681                &self.atlas,
682                &self.skybox.read().unwrap(),
683            ));
684            *bindgroup = Some(b.clone());
685            b
686        }
687    }
688
689    /// Adds a renderlet to the internal list of renderlets to be drawn each
690    /// frame.
691    ///
692    /// This makes an internal clone of the renderlet.
693    ///
694    /// If you drop the renderlet and no other references are kept, it will be
695    /// removed automatically from the internal list and will cease to be
696    /// drawn each frame.
697    pub fn add_renderlet(&self, renderlet: &Hybrid<Renderlet>) {
698        // UNWRAP: if we can't acquire the lock we want to panic.
699        let mut draws = self.draws.write().unwrap();
700        let StageDraws { hybrids, indirect } = draws.deref_mut();
701        hybrids.push(renderlet.clone());
702        if let Some(IndirectDraws { slab, draws }) = indirect.as_mut() {
703            draws.push(
704                slab.new_value(DrawIndirectArgs::from(renderlet))
705                    .into_gpu_only(),
706            );
707        }
708    }
709
710    /// Erase the given renderlet from the internal list of renderlets to be
711    /// drawn each frame.
712    pub fn remove_renderlet(&self, renderlet: &Hybrid<Renderlet>) {
713        let id = renderlet.id();
714        let mut draws = self.draws.write().unwrap();
715        let StageDraws { hybrids, indirect } = draws.deref_mut();
716        hybrids.retain(|hybrid| hybrid.id() != id);
717        if let Some(IndirectDraws { slab: _, draws }) = indirect.as_mut() {
718            *draws = vec![];
719        }
720    }
721
722    /// Returns a clone of all the staged [`Renderlet`]s.
723    pub fn get_renderlets(&self) -> Vec<Hybrid<Renderlet>> {
724        // UNWRAP: if we can't acquire the lock we want to panic.
725        let draws = self.draws.read().unwrap();
726        let StageDraws {
727            hybrids,
728            indirect: _,
729        } = draws.deref();
730        hybrids.clone()
731    }
732
733    /// Re-order the renderlets.
734    ///
735    /// This determines the order in which they are drawn each frame.
736    ///
737    /// If the `order` iterator is missing any renderlet ids, those missing
738    /// renderlets will be drawn _before_ the ordered ones, in no particular
739    /// order.
740    pub fn reorder_renderlets(&self, order: impl IntoIterator<Item = Id<Renderlet>>) {
741        log::trace!("reordering renderlets");
742        // UNWRAP: panic on purpose
743        let mut guard = self.draws.write().unwrap();
744        let StageDraws {
745            ref mut hybrids,
746            indirect,
747        } = guard.deref_mut();
748        let mut ordered = vec![];
749        let mut m = FxHashMap::from_iter(std::mem::take(hybrids).into_iter().map(|r| (r.id(), r)));
750        for id in order.into_iter() {
751            if let Some(renderlet) = m.remove(&id) {
752                ordered.push(renderlet);
753            }
754        }
755        hybrids.extend(m.into_values());
756        hybrids.extend(ordered);
757        if let Some(indirect) = indirect.as_mut() {
758            indirect.draws.drain(..);
759            // for (hybrid, draw) in hybrids.iter().zip(indirect.draws.iter_mut()) {
760            //     draw.set(hybrid.into());
761            // }
762        }
763    }
764
765    /// Returns a clone of the current depth texture.
766    pub fn get_depth_texture(&self) -> DepthTexture {
767        DepthTexture {
768            device: self.device.clone(),
769            queue: self.queue.clone(),
770            texture: self.depth_texture.read().unwrap().clone(),
771        }
772    }
773
774    pub fn new_skybox_from_path(
775        &self,
776        path: impl AsRef<std::path::Path>,
777        camera_id: Id<Camera>,
778    ) -> Result<Skybox, AtlasImageError> {
779        let hdr = AtlasImage::from_hdr_path(path)?;
780        Ok(Skybox::new(&self.device, &self.queue, hdr, camera_id))
781    }
782
783    pub fn new_nested_transform(&self) -> NestedTransform {
784        NestedTransform::new(&self.mngr)
785    }
786
787    fn tick_internal(&self) -> (Arc<wgpu::Buffer>, Option<Arc<wgpu::Buffer>>) {
788        let maybe_indirect_draw_buffer = {
789            let mut redraw_args = false;
790            let mut draw_guard = self.draws.write().unwrap();
791            let StageDraws { hybrids, indirect } = draw_guard.deref_mut();
792            hybrids.retain(|d| {
793                if d.strong_count() > RENDERLET_STRONG_COUNT_LOWER_BOUND {
794                    true
795                } else {
796                    redraw_args = true;
797                    false
798                }
799            });
800            if let Some(IndirectDraws { slab, draws }) = indirect.as_mut() {
801                if redraw_args || draws.len() != hybrids.len() {
802                    // Pre-upkeep to reclaim resources - this is necessary because
803                    // the draw buffer has to be contiguous (it can't start with a bunch of trash)
804                    let _ = slab.upkeep((
805                        &self.device,
806                        &self.queue,
807                        Some("indirect draw pre upkeep"),
808                        wgpu::BufferUsages::INDIRECT,
809                    ));
810                    *draws = hybrids
811                        .iter()
812                        .map(|h: &Hybrid<Renderlet>| {
813                            slab.new_value(DrawIndirectArgs::from(h)).into_gpu_only()
814                        })
815                        .collect();
816                }
817                Some(slab.get_updated_buffer((
818                    &self.device,
819                    &self.queue,
820                    Some("indirect draw upkeep"),
821                    wgpu::BufferUsages::INDIRECT,
822                )))
823            } else {
824                None
825            }
826        };
827        if let Some(new_slab_buffer) = self.mngr.upkeep((
828            &self.device,
829            &self.queue,
830            Some("stage render upkeep"),
831            wgpu::BufferUsages::empty(),
832        )) {
833            // invalidate our bindgroups, etc
834            let _ = self.skybox_bindgroup.lock().unwrap().take();
835            let _ = self.buffers_bindgroup.lock().unwrap().take();
836            (new_slab_buffer, maybe_indirect_draw_buffer)
837        } else {
838            // UNWRAP: safe because we called `SlabManager::upkeep` above^, which ensures
839            // the buffer exists
840            (self.mngr.get_buffer().unwrap(), maybe_indirect_draw_buffer)
841        }
842    }
843
844    /// Ticks the stage, synchronizing changes with the GPU.
845    ///
846    /// It's good to call this after dropping assets to free up space on the
847    /// slab.
848    pub fn tick(&self) {
849        self.atlas.upkeep(&self.device, &self.queue);
850        let _ = self.tick_internal();
851    }
852
853    pub fn render(&self, view: &wgpu::TextureView) {
854        {
855            log::trace!("rendering the stage");
856            let label = Some("stage render");
857            // UNWRAP: POP
858            let background_color = *self.background_color.read().unwrap();
859            let (slab_buffer, maybe_indirect_draw_buffer) = self.tick_internal();
860            // UNWRAP: POP
861            let pipeline = self.stage_pipeline.read().unwrap();
862            // UNWRAP: POP
863            let msaa_target = self.msaa_render_target.read().unwrap();
864            let slab_buffers_bindgroup = self.get_slab_buffers_bindgroup(&slab_buffer);
865            let textures_bindgroup = self.get_textures_bindgroup();
866            let has_skybox = self.has_skybox.load(Ordering::Relaxed);
867            let may_skybox_pipeline_and_bindgroup = if has_skybox {
868                Some(self.get_skybox_pipeline_and_bindgroup(&slab_buffer))
869            } else {
870                None
871            };
872            let clear_colors = self.clear_color_attachments.load(Ordering::Relaxed);
873            let clear_depth = self.clear_depth_attachments.load(Ordering::Relaxed);
874
875            // UNWRAP: if we can't read we want to panic.
876            let draws = self.draws.read().unwrap();
877
878            let mut encoder = self
879                .device
880                .create_command_encoder(&wgpu::CommandEncoderDescriptor { label });
881            {
882                let hdr_texture = self.hdr_texture.read().unwrap();
883                let depth_texture = self.depth_texture.read().unwrap();
884                let ops = wgpu::Operations {
885                    load: if clear_colors {
886                        wgpu::LoadOp::Clear(background_color)
887                    } else {
888                        wgpu::LoadOp::Load
889                    },
890                    store: wgpu::StoreOp::Store,
891                };
892                let color_attachment = if let Some(msaa_view) = msaa_target.as_ref() {
893                    wgpu::RenderPassColorAttachment {
894                        ops,
895                        view: msaa_view,
896                        resolve_target: Some(&hdr_texture.view),
897                    }
898                } else {
899                    wgpu::RenderPassColorAttachment {
900                        ops,
901                        view: &hdr_texture.view,
902                        resolve_target: None,
903                    }
904                };
905                let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
906                    label,
907                    color_attachments: &[Some(color_attachment)],
908                    depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
909                        view: &depth_texture.view,
910                        depth_ops: Some(wgpu::Operations {
911                            load: if clear_depth {
912                                wgpu::LoadOp::Clear(1.0)
913                            } else {
914                                wgpu::LoadOp::Load
915                            },
916                            store: wgpu::StoreOp::Store,
917                        }),
918                        stencil_ops: None,
919                    }),
920                    ..Default::default()
921                });
922
923                render_pass.set_pipeline(&pipeline);
924                render_pass.set_bind_group(0, &slab_buffers_bindgroup, &[]);
925                render_pass.set_bind_group(1, &textures_bindgroup, &[]);
926                let num_draw_calls = draws.hybrids.len();
927                if num_draw_calls > 0 {
928                    if let Some(indirect_draw_buffer) = maybe_indirect_draw_buffer {
929                        render_pass.multi_draw_indirect(
930                            &indirect_draw_buffer,
931                            0,
932                            draws.hybrids.len() as u32,
933                        );
934                    } else {
935                        for hybrid in draws.hybrids.iter() {
936                            let rlet = hybrid.get();
937                            if rlet.visible {
938                                let vertex_range = if rlet.indices_array.is_null() {
939                                    0..rlet.vertices_array.len() as u32
940                                } else {
941                                    0..rlet.indices_array.len() as u32
942                                };
943                                let id = hybrid.id();
944                                let instance_range = id.inner()..id.inner() + 1;
945                                log::trace!(
946                                    "drawing vertices {vertex_range:?} and instances \
947                                             {instance_range:?}"
948                                );
949                                render_pass.draw(vertex_range, instance_range);
950                            }
951                        }
952                    }
953                }
954
955                if let Some((pipeline, bindgroup)) = may_skybox_pipeline_and_bindgroup.as_ref() {
956                    log::trace!("rendering skybox");
957                    // UNWRAP: if we can't acquire the lock we want to panic.
958                    let skybox = self.skybox.read().unwrap();
959                    render_pass.set_pipeline(&pipeline.pipeline);
960                    render_pass.set_bind_group(0, bindgroup, &[]);
961                    render_pass.draw(0..36, skybox.camera.inner()..skybox.camera.inner() + 1);
962                }
963            }
964            self.queue.submit(std::iter::once(encoder.finish()));
965        }
966
967        // then render bloom
968        if self.has_bloom.load(Ordering::Relaxed) {
969            log::trace!("stage bloom");
970            self.bloom.bloom(&self.device, &self.queue);
971        } else {
972            // copy the input hdr texture to the bloom mix texture
973            let mut encoder = self
974                .device
975                .create_command_encoder(&wgpu::CommandEncoderDescriptor {
976                    label: Some("no bloom copy"),
977                });
978            let bloom_mix_texture = self.bloom.get_mix_texture();
979            encoder.copy_texture_to_texture(
980                wgpu::ImageCopyTexture {
981                    texture: &self.hdr_texture.read().unwrap().texture,
982                    mip_level: 0,
983                    origin: wgpu::Origin3d { x: 0, y: 0, z: 0 },
984                    aspect: wgpu::TextureAspect::All,
985                },
986                wgpu::ImageCopyTexture {
987                    texture: &bloom_mix_texture.texture,
988                    mip_level: 0,
989                    origin: wgpu::Origin3d { x: 0, y: 0, z: 0 },
990                    aspect: wgpu::TextureAspect::All,
991                },
992                wgpu::Extent3d {
993                    width: bloom_mix_texture.width(),
994                    height: bloom_mix_texture.height(),
995                    depth_or_array_layers: 1,
996                },
997            );
998            self.queue.submit(std::iter::once(encoder.finish()));
999        }
1000
1001        // then render tonemapping
1002        log::trace!("stage tonemapping");
1003        self.tonemapping.render(&self.device, &self.queue, view);
1004    }
1005}
1006
1007/// Manages scene heirarchy on the [`Stage`].
1008///
1009/// Clones all reference the same nested transform.
1010#[derive(Clone)]
1011pub struct NestedTransform {
1012    global_transform_id: Id<Transform>,
1013    local_transform: Arc<RwLock<Transform>>,
1014
1015    notifier_index: usize,
1016    notify: async_channel::Sender<usize>,
1017
1018    children: Arc<RwLock<Vec<NestedTransform>>>,
1019    parent: Arc<RwLock<Option<NestedTransform>>>,
1020}
1021
1022impl Drop for NestedTransform {
1023    fn drop(&mut self) {
1024        let _ = self.notify.try_send(self.notifier_index);
1025    }
1026}
1027
1028impl core::fmt::Debug for NestedTransform {
1029    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1030        let children = self
1031            .children
1032            .read()
1033            .unwrap()
1034            .iter()
1035            .map(|nt| nt.global_transform_id)
1036            .collect::<Vec<_>>();
1037        let parent = self
1038            .parent
1039            .read()
1040            .unwrap()
1041            .as_ref()
1042            .map(|nt| nt.global_transform_id);
1043        f.debug_struct("NestedTransform")
1044            .field("local_transform", &self.local_transform)
1045            .field("children", &children)
1046            .field("parent", &parent)
1047            .finish()
1048    }
1049}
1050
1051impl UpdatesSlab for NestedTransform {
1052    fn id(&self) -> usize {
1053        self.notifier_index
1054    }
1055
1056    fn u32_array(&self) -> Array<u32> {
1057        Array::new(
1058            self.global_transform_id.inner(),
1059            Transform::SLAB_SIZE as u32,
1060        )
1061    }
1062
1063    fn strong_count(&self) -> usize {
1064        Arc::strong_count(&self.local_transform)
1065    }
1066
1067    fn get_update(&self) -> Vec<SlabUpdate> {
1068        let transform = self.get_global_transform();
1069        let array = self.u32_array();
1070        let mut elements = vec![0u32; Transform::SLAB_SIZE];
1071        elements.write_indexed(&transform, 0);
1072        vec![SlabUpdate { array, elements }]
1073    }
1074}
1075
1076impl NestedTransform {
1077    pub fn new(mngr: &SlabAllocator<impl IsBuffer>) -> Self {
1078        let id = mngr.allocate::<Transform>();
1079        let notifier_index = mngr.next_update_k();
1080
1081        let nested = NestedTransform {
1082            global_transform_id: id,
1083            local_transform: Arc::new(RwLock::new(Transform::default())),
1084
1085            notifier_index,
1086            notify: mngr.notifier.0.clone(),
1087
1088            children: Default::default(),
1089            parent: Default::default(),
1090        };
1091
1092        mngr.insert_update_source(notifier_index, nested.clone());
1093
1094        nested.mark_dirty();
1095        nested
1096    }
1097
1098    #[cfg(test)]
1099    pub(crate) fn get_notifier_index(&self) -> usize {
1100        self.notifier_index
1101    }
1102
1103    fn mark_dirty(&self) {
1104        // UNWRAP: safe because it's unbounded
1105        self.notify.try_send(self.notifier_index).unwrap();
1106        for child in self.children.read().unwrap().iter() {
1107            child.mark_dirty();
1108        }
1109    }
1110
1111    /// Modify the local transform.
1112    ///
1113    /// This automatically applies the local transform to the global transform.
1114    pub fn modify(&self, f: impl Fn(&mut Transform)) {
1115        // UNWRAP: panic on purpose
1116        let mut local_guard = self.local_transform.write().unwrap();
1117        f(&mut local_guard);
1118        self.mark_dirty();
1119    }
1120
1121    /// Set the local transform.
1122    ///
1123    /// This automatically applies the local transform to the global transform.
1124    pub fn set(&self, transform: Transform) {
1125        self.modify(move |t| {
1126            *t = transform;
1127        });
1128    }
1129
1130    pub fn get(&self) -> Transform {
1131        *self.local_transform.read().unwrap()
1132    }
1133
1134    pub fn get_global_transform(&self) -> Transform {
1135        let maybe_parent_guard = self.parent.read().unwrap();
1136        let transform = self.get();
1137        let parent_transform = maybe_parent_guard
1138            .as_ref()
1139            .map(|parent| parent.get_global_transform())
1140            .unwrap_or_default();
1141        Transform::from(Mat4::from(parent_transform) * Mat4::from(transform))
1142    }
1143
1144    pub fn global_transform_id(&self) -> Id<Transform> {
1145        self.global_transform_id
1146    }
1147
1148    pub fn add_child(&self, node: &NestedTransform) {
1149        *node.parent.write().unwrap() = Some(self.clone());
1150        node.mark_dirty();
1151        self.children.write().unwrap().push(node.clone());
1152    }
1153
1154    pub fn remove_child(&self, node: &NestedTransform) {
1155        self.children.write().unwrap().retain_mut(|child| {
1156            if child.global_transform_id == node.global_transform_id {
1157                node.mark_dirty();
1158                let _ = node.parent.write().unwrap().take();
1159                false
1160            } else {
1161                true
1162            }
1163        });
1164    }
1165
1166    pub fn parent(&self) -> Option<NestedTransform> {
1167        self.parent.read().unwrap().clone()
1168    }
1169}
1170
1171#[cfg(test)]
1172mod test {
1173    use std::sync::Mutex;
1174
1175    use crabslab::{Array, Slab};
1176    use glam::{Mat4, Vec2, Vec3};
1177
1178    use crate::{
1179        camera::Camera,
1180        stage::{
1181            cpu::{SlabAllocator, UpdatesSlab},
1182            NestedTransform, Renderlet, Vertex,
1183        },
1184        transform::Transform,
1185        Context,
1186    };
1187
1188    #[test]
1189    fn vertex_slab_roundtrip() {
1190        let initial_vertices = {
1191            let tl = Vertex::default()
1192                .with_position(Vec3::ZERO)
1193                .with_uv0(Vec2::ZERO);
1194            let tr = Vertex::default()
1195                .with_position(Vec3::new(1.0, 0.0, 0.0))
1196                .with_uv0(Vec2::new(1.0, 0.0));
1197            let bl = Vertex::default()
1198                .with_position(Vec3::new(0.0, 1.0, 0.0))
1199                .with_uv0(Vec2::new(0.0, 1.0));
1200            let br = Vertex::default()
1201                .with_position(Vec3::new(1.0, 1.0, 0.0))
1202                .with_uv0(Vec2::splat(1.0));
1203            vec![tl, bl, br, tl, br, tr]
1204        };
1205        let mut slab = [0u32; 256];
1206        slab.write_indexed_slice(&initial_vertices, 0);
1207        let vertices = slab.read_vec(Array::<Vertex>::new(0, initial_vertices.len() as u32));
1208        pretty_assertions::assert_eq!(initial_vertices, vertices);
1209    }
1210
1211    #[test]
1212    fn matrix_subtraction_sanity() {
1213        let m = Mat4::IDENTITY - Mat4::IDENTITY;
1214        assert_eq!(Mat4::ZERO, m);
1215    }
1216
1217    #[test]
1218    fn can_global_transform_calculation() {
1219        let slab = SlabAllocator::<Mutex<Vec<u32>>>::default();
1220        // Setup a hierarchy of transforms
1221        let root = NestedTransform::new(&slab);
1222        let child = NestedTransform::new(&slab);
1223        child.set(Transform {
1224            translation: Vec3::new(1.0, 0.0, 0.0),
1225            ..Default::default()
1226        });
1227        let grandchild = NestedTransform::new(&slab);
1228        grandchild.set(Transform {
1229            translation: Vec3::new(1.0, 0.0, 0.0),
1230            ..Default::default()
1231        });
1232
1233        // Build the hierarchy
1234        root.add_child(&child);
1235        child.add_child(&grandchild);
1236
1237        // Calculate global transforms
1238        let grandchild_global_transform = grandchild.get_global_transform();
1239
1240        // Assert that the global transform is as expected
1241        assert_eq!(
1242            grandchild_global_transform.translation.x, 2.0,
1243            "Grandchild's global translation should   2.0 along the x-axis"
1244        );
1245    }
1246
1247    #[test]
1248    fn can_msaa() {
1249        let ctx = Context::headless(100, 100);
1250        let stage = ctx
1251            .new_stage()
1252            .with_background_color([1.0, 1.0, 1.0, 1.0])
1253            .with_lighting(false);
1254        let camera = stage.new_value(Camera::default_ortho2d(100.0, 100.0));
1255        let vertices = stage.new_array([
1256            Vertex::default()
1257                .with_position([10.0, 10.0, 0.0])
1258                .with_color([0.0, 1.0, 1.0, 1.0]),
1259            Vertex::default()
1260                .with_position([10.0, 90.0, 0.0])
1261                .with_color([1.0, 1.0, 0.0, 1.0]),
1262            Vertex::default()
1263                .with_position([90.0, 10.0, 0.0])
1264                .with_color([1.0, 0.0, 1.0, 1.0]),
1265        ]);
1266        let triangle = stage.new_value(Renderlet {
1267            camera_id: camera.id(),
1268            vertices_array: vertices.array(),
1269            ..Default::default()
1270        });
1271        stage.add_renderlet(&triangle);
1272
1273        log::debug!("rendering without msaa");
1274        let frame = ctx.get_next_frame().unwrap();
1275        stage.render(&frame.view());
1276        let img = frame.read_image().unwrap();
1277        img_diff::assert_img_eq_cfg(
1278            "msaa/without.png",
1279            img,
1280            img_diff::DiffCfg {
1281                pixel_threshold: img_diff::LOW_PIXEL_THRESHOLD,
1282                ..Default::default()
1283            },
1284        );
1285        frame.present();
1286        log::debug!("  all good!");
1287
1288        stage.set_msaa_sample_count(4);
1289        log::debug!("rendering with msaa");
1290        let frame = ctx.get_next_frame().unwrap();
1291        stage.render(&frame.view());
1292        let img = frame.read_image().unwrap();
1293        img_diff::assert_img_eq_cfg(
1294            "msaa/with.png",
1295            img,
1296            img_diff::DiffCfg {
1297                pixel_threshold: img_diff::LOW_PIXEL_THRESHOLD,
1298                ..Default::default()
1299            },
1300        );
1301        frame.present();
1302    }
1303
1304    #[test]
1305    fn has_consistent_stage_renderlet_strong_count() {
1306        let ctx = Context::headless(100, 100);
1307        let stage = ctx.new_stage();
1308        let r = stage.new_value(Renderlet::default());
1309        stage.add_renderlet(&r);
1310        let expected_strong_count = 1 // for `r` here
1311            + super::RENDERLET_STRONG_COUNT_LOWER_BOUND;
1312        assert_eq!(expected_strong_count, r.strong_count());
1313    }
1314}