Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 37 additions & 15 deletions crates/renderide/src/passes/clustered_light/froxel_cpu/bounds.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use glam::{Mat4, Vec2, Vec3, Vec4};
use rayon::prelude::*;

use crate::gpu::GpuLight;
use crate::world_mesh::cluster::{ClusterFrameParams, TILE_SIZE, sanitize_cluster_clip_planes};
Expand Down Expand Up @@ -190,6 +191,7 @@ pub(super) fn build_eye_froxel_spheres(
return Some(Vec::new());
}

profiling::scope!("clustered_light::build_eye_froxel_spheres");
let mut all_spheres = Vec::with_capacity(eye_params.len());
for (params, &layout) in eye_params.iter().zip(layouts.iter()) {
all_spheres.push(froxel_bounding_spheres(*params, layout)?);
Expand Down Expand Up @@ -218,16 +220,37 @@ fn froxel_bounding_spheres(
return None;
}

let cluster_count = layout.cluster_count()?;
let mut spheres = Vec::with_capacity(cluster_count);
for z in 0..layout.cluster_count_z {
for y in 0..layout.cluster_count_y {
for x in 0..layout.cluster_count_x {
spheres.push(cluster_aabb(params, inv_proj, layout, x, y, z)?.bounding_sphere());
(0..layout.cluster_count_z)
.into_par_iter()
.map(|z| {
let depth_bounds = cluster_z_depth_bounds(
z,
layout.cluster_count_z,
params.near_clip,
params.far_clip,
);
ClusterDepthParams {
cluster_z: z,
near_depth: depth_bounds.0,
far_depth: depth_bounds.1,
}
}
}
Some(spheres)
})
.flat_map_iter(|depth_params| (0..layout.cluster_count_y).map(move |y| (y, depth_params)))
.flat_map_iter(|(y, depth_params)| {
(0..layout.cluster_count_x).map(move |x| (x, y, depth_params))
})
.map(|(x, y, depth_params)| {
cluster_aabb(params, inv_proj, layout, x, y, depth_params).map(|c| c.bounding_sphere())
})
.collect()
}

/// Cached logarithmic clustered-depth bounds for one Z slice.
#[derive(Clone, Copy, Debug)]
struct ClusterDepthParams {
cluster_z: u32,
near_depth: f32,
far_depth: f32,
}

/// Computes the view-space AABB for one froxel.
Expand All @@ -237,14 +260,13 @@ fn cluster_aabb(
layout: FroxelLayout,
cluster_x: u32,
cluster_y: u32,
cluster_z: u32,
depth_params: ClusterDepthParams,
) -> Option<FroxelAabb> {
let (near_depth, far_depth) = cluster_z_depth_bounds(
let ClusterDepthParams {
cluster_z,
layout.cluster_count_z,
params.near_clip,
params.far_clip,
);
near_depth,
far_depth,
} = depth_params;
let tile_near = -near_depth;
let tile_far = -far_depth;

Expand Down
58 changes: 21 additions & 37 deletions crates/renderide/src/passes/clustered_light/froxel_cpu/parallel.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::num::Saturating;
use std::sync::atomic::AtomicU32;

use rayon::prelude::*;
Expand Down Expand Up @@ -38,7 +39,7 @@ pub(super) fn build_parallel(
let chunks = count_parallel_light_chunks(&inputs);
let (counts, stats) = merge_parallel_chunk_counts(&chunks, total_clusters);
let (ranges, total_indices) = prefix_counts_to_ranges(&counts)?;
let chunk_offsets = build_parallel_chunk_offsets(&chunks, &ranges, total_clusters);
let chunk_offsets = build_chunk_offsets(&chunks, &ranges, total_clusters);
let indices = write_parallel_light_chunks(&inputs, &chunk_offsets, total_indices);

Some(CpuClusterAssignments {
Expand Down Expand Up @@ -103,14 +104,17 @@ fn merge_parallel_chunk_counts(
chunks: &[CpuFroxelCountChunk],
total_clusters: usize,
) -> (Vec<u32>, CpuFroxelStats) {
profiling::scope!("clustered_light::merge_parallel_chunk_counts");
let counts = if should_parallelize_cpu_froxel_prefix(total_clusters) {
(0..total_clusters)
.into_par_iter()
.with_min_len(CPU_FROXEL_PREFIX_CHUNK_SIZE)
.map(|cluster_id| {
chunks.iter().fold(0u32, |total, chunk| {
total.saturating_add(chunk.counts[cluster_id])
})
chunks
.iter()
.map(|chunk| Saturating(chunk.counts[cluster_id]))
.sum::<Saturating<u32>>()
.0
})
.collect()
} else {
Expand All @@ -130,48 +134,28 @@ fn merge_parallel_chunk_counts(
(counts, stats)
}

fn build_parallel_chunk_offsets(
fn build_chunk_offsets(
chunks: &[CpuFroxelCountChunk],
ranges: &[[u32; 2]],
total_clusters: usize,
) -> Vec<Vec<u32>> {
profiling::scope!("clustered_light::build_chunk_offsets");
let chunk_count = chunks.len();
if should_parallelize_cpu_froxel_prefix(total_clusters) && chunk_count >= 2 {
let per_cluster_offsets = (0..total_clusters)
.into_par_iter()
.with_min_len(CPU_FROXEL_PREFIX_CHUNK_SIZE)
.map(|cluster_id| {
let mut next = ranges[cluster_id][0];
chunks
.iter()
.map(|chunk| {
let offset = next;
next = next.saturating_add(chunk.counts[cluster_id]);
offset
})
.collect::<Vec<_>>()
})
.collect::<Vec<_>>();
let mut chunk_offsets = (0..chunk_count)
.map(|_| vec![0u32; total_clusters])
.collect::<Vec<_>>();
for (cluster_id, offsets) in per_cluster_offsets.into_iter().enumerate() {
for (chunk_idx, offset) in offsets.into_iter().enumerate() {
chunk_offsets[chunk_idx][cluster_id] = offset;
}
}
return chunk_offsets;
}

// The amount of memory allocations/deallocations necessary
// to properly parallelize computations and then transpose the Vec<Vec<_>>
// is not worth the amount of time spent iterating here
let mut chunk_offsets = (0..chunk_count)
.map(|_| vec![0u32; total_clusters])
.collect::<Vec<_>>();
for cluster_id in 0..total_clusters {
let mut next = ranges[cluster_id][0];
for (chunk_idx, chunk) in chunks.iter().enumerate() {
chunk_offsets[chunk_idx][cluster_id] = next;
next = next.saturating_add(chunk.counts[cluster_id]);
}
chunks
.iter()
.map(|chunk| chunk.counts[cluster_id])
.enumerate()
.fold(ranges[cluster_id][0], |offset, (chunk_idx, chunk)| {
chunk_offsets[chunk_idx][cluster_id] = offset;
offset.saturating_add(chunk)
});
}
chunk_offsets
}
Expand Down
Loading