wde_pbr/deferred/batches/
ssbo_batches.rs

1use bevy::{ecs::system::SystemParamItem, prelude::*};
2use wde_renderer::prelude::*;
3
4use crate::prelude::BatchList;
5
6/// The maximum number of batches in the ssbo.
7pub const SSBO_MAX_BATCHES: usize = 100_000;
8
9pub(crate) struct SsboInstancesToTransformPlugin;
10impl Plugin for SsboInstancesToTransformPlugin {
11    fn build(&self, app: &mut App) {
12        app.add_plugins(RenderDataRegisterPlugin::<PbrSsboInstanceToTransform>::default());
13    }
14}
15
16/// Contains pointers to the transforms of the entities in the SSBO, as well as the offset in the "instance to transform" buffer of the first entity of this batch. This is used to bind the correct transforms to the instances of each batch when rendering.
17#[derive(Asset, Clone, TypePath, Default)]
18pub struct PbrSsboInstanceToTransform;
19impl PbrSsboInstanceToTransform {
20    pub const INSTANCE_TO_TRANSFORM_IDX: u32 = 0;
21    pub const INSTANCE_TO_TRANSFORM_STAGING_IDX: u32 = 1;
22}
23impl RenderData for PbrSsboInstanceToTransform {
24    type Params = ();
25
26    fn describe(_params: &mut SystemParamItem<Self::Params>, builder: &mut RenderDataBuilder) {
27        builder
28            .add_buffer(
29                Self::INSTANCE_TO_TRANSFORM_IDX,
30                Buffer {
31                    label: "pbr-instance-to-transform-buffer-gpu".to_string(),
32                    size: std::mem::size_of::<u32>() * SSBO_MAX_BATCHES,
33                    usage: BufferUsage::STORAGE | BufferUsage::COPY_DST,
34                    content: None,
35                },
36            )
37            .add_buffer(
38                Self::INSTANCE_TO_TRANSFORM_STAGING_IDX,
39                Buffer {
40                    label: "pbr-instance-to-transform-staging".to_string(),
41                    size: std::mem::size_of::<u32>() * SSBO_MAX_BATCHES,
42                    usage: BufferUsage::COPY_SRC | BufferUsage::COPY_DST,
43                    content: None,
44                },
45            );
46    }
47}
48
49pub(crate) fn set_instances_to_transform(
50    instance_to_transform: ResRenderData<PbrSsboInstanceToTransform>,
51    mut batches: ResMut<BatchList>,
52    buffers: Res<RenderAssets<GpuBuffer>>,
53    render_instance: Res<RenderInstance>,
54) {
55    // Check if there are any dirty batches that need to be updated
56    if batches.dirty_batches.is_empty() {
57        return;
58    }
59
60    // Get buffers
61    let instance_to_transform = match instance_to_transform.iter().next() {
62        Some((_, data)) => data,
63        None => return
64    };
65    let (staging_buffer, gpu_buffer) = match (
66        buffers.get(&instance_to_transform.get_buffer(PbrSsboInstanceToTransform::INSTANCE_TO_TRANSFORM_STAGING_IDX).unwrap()),
67        buffers.get(&instance_to_transform.get_buffer(PbrSsboInstanceToTransform::INSTANCE_TO_TRANSFORM_IDX).unwrap()),
68    ) {
69        (Some(staging), Some(gpu)) => (staging, gpu),
70        _ => return
71    };
72
73    // Update the staging buffer with the new instance to transform mappings for each batch
74    let mut staging_content = vec![0u8; staging_buffer.buffer.buffer.size() as usize];
75    let mut offset = 0;
76    for batch_key in batches.dirty_batches.clone().iter() {
77        let batch = batches.batches.get_mut(batch_key).unwrap();
78        batch.instances_offset = offset;
79        for transform_id in &batch.transform_ssbo_ids {
80            let bytes = transform_id.to_ne_bytes();
81            staging_content[(offset as usize * 4)..((offset as usize + 1) * 4)].copy_from_slice(&bytes);
82            offset += 1;
83        }
84    }
85    let render_instance = render_instance.0.read().unwrap();
86    staging_buffer.buffer.write(&render_instance, &staging_content, 0);
87
88    // Copy the staging buffer to the GPU buffer
89    gpu_buffer.buffer.copy_from_buffer(&render_instance, &staging_buffer.buffer);
90
91    // Clear dirty batches
92    batches.dirty_batches.clear();
93}