nightshade-renderer 0.57.0

GPU-driven wgpu renderer with a built-in frame graph.
use super::super::types::InstancedTransformComputeUniforms;
use super::MeshPass;

impl MeshPass {
    pub(in super::super) fn dispatch_instanced_transform_compute(
        &mut self,
        device: &wgpu::Device,
        encoder: &mut wgpu::CommandEncoder,
        queue: &wgpu::Queue,
        configs: &crate::wgpu::render_configs::RenderInputs,
    ) {
        let ranges: Vec<_> = self.world_states[self.current_world_id as usize]
            .as_ref()
            .unwrap()
            .instanced_transform_ranges
            .iter()
            .map(|(&entity, &(start, count))| (entity, start, count))
            .collect();

        if ranges.is_empty() {
            return;
        }

        let struct_size = std::mem::size_of::<InstancedTransformComputeUniforms>() as u64;
        let alignment = device.limits().min_uniform_buffer_offset_alignment as u64;
        let slot_size = struct_size.div_ceil(alignment) * alignment;

        if ranges.len() > self.instanced_compute_uniforms_slots {
            self.instanced_compute_uniforms_buffer =
                device.create_buffer(&wgpu::BufferDescriptor {
                    label: Some("Instanced Transform Compute Uniforms Buffer (Resized)"),
                    size: slot_size * ranges.len() as u64,
                    usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
                    mapped_at_creation: false,
                });
            self.instanced_compute_uniforms_slots = ranges.len();
            for world_state in self.world_states.iter_mut().flatten() {
                if let Some(gpu) = world_state.gpu_buffers.as_mut() {
                    gpu.instanced_compute_bind_group = None;
                }
            }
        }

        if self.world_states[self.current_world_id as usize]
            .as_ref()
            .unwrap()
            .gpu_buffers
            .as_ref()
            .unwrap()
            .instanced_compute_bind_group
            .is_none()
        {
            self.rebuild_instanced_compute_bind_group(device);
        }

        for (slot_index, &(entity, start, count)) in ranges.iter().enumerate() {
            let parent_transform = configs
                .scene_world
                .get::<crate::config::InstancedObjectData>(entity)
                .map(|instanced| instanced.parent_transform)
                .unwrap_or_else(nalgebra_glm::Mat4::identity);
            let uniforms = InstancedTransformComputeUniforms {
                parent_transform: parent_transform.into(),
                instance_count: count,
                output_offset: start,
                _pad: [0; 2],
            };
            queue.write_buffer(
                &self.instanced_compute_uniforms_buffer,
                slot_size * slot_index as u64,
                bytemuck::bytes_of(&uniforms),
            );
        }

        let bind_group = self.world_states[self.current_world_id as usize]
            .as_ref()
            .unwrap()
            .gpu_buffers
            .as_ref()
            .unwrap()
            .instanced_compute_bind_group
            .as_ref()
            .unwrap();

        let mut compute_pass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {
            label: Some("Instanced Transform Compute Pass"),
            timestamp_writes: None,
        });
        compute_pass.set_pipeline(&self.instanced_compute_pipeline);
        for (slot_index, &(_, _, count)) in ranges.iter().enumerate() {
            let dynamic_offset = (slot_size * slot_index as u64) as u32;
            compute_pass.set_bind_group(0, bind_group, &[dynamic_offset]);
            compute_pass.dispatch_workgroups(count.div_ceil(256), 1, 1);
        }
    }

    pub(in super::super) fn upload_instanced_local_matrices(
        &mut self,
        device: &wgpu::Device,
        queue: &wgpu::Queue,
        local_matrices: &[[[f32; 4]; 4]],
    ) {
        if local_matrices.is_empty() {
            return;
        }

        let current_size = self.world_states[self.current_world_id as usize]
            .as_ref()
            .unwrap()
            .gpu_buffers
            .as_ref()
            .unwrap()
            .instanced_local_matrix_buffer_size;

        if local_matrices.len() > current_size {
            let new_size = std::cmp::min(
                (local_matrices.len() as f32 * super::super::types::BUFFER_GROWTH_FACTOR).ceil()
                    as usize,
                super::super::types::MAX_INSTANCES,
            );

            let new_buffer = device.create_buffer(&wgpu::BufferDescriptor {
                label: Some("Instanced Local Matrix Buffer (Resized)"),
                size: (std::mem::size_of::<[[f32; 4]; 4]>() * new_size) as u64,
                usage: wgpu::BufferUsages::STORAGE
                    | wgpu::BufferUsages::COPY_DST
                    | wgpu::BufferUsages::COPY_SRC,
                mapped_at_creation: false,
            });
            {
                let world_state = self.world_states[self.current_world_id as usize]
                    .as_mut()
                    .unwrap();
                let gpu = world_state.gpu_buffers.as_mut().unwrap();
                gpu.instanced_local_matrix_buffer = new_buffer;
                gpu.instanced_local_matrix_buffer_size = new_size;
            }
        }

        let buffer = &self.world_states[self.current_world_id as usize]
            .as_ref()
            .unwrap()
            .gpu_buffers
            .as_ref()
            .unwrap()
            .instanced_local_matrix_buffer;
        queue.write_buffer(buffer, 0, bytemuck::cast_slice(local_matrices));

        self.rebuild_instanced_compute_bind_group(device);
    }
}