viewport_lib/plugin_api/cull.rs
1//! GPU-driven culling service published to plugins.
2//!
3//! A plugin that submits its own instanced geometry can run the lib's
4//! frustum-cull compute against its instance AABB buffer to get a compacted
5//! visibility list and a `DrawIndexedIndirect` entry suitable for
6//! `draw_indexed_indirect`.
7//!
8//! [`CullSubmission`] carries the buffers for a multi-batch cull: one big
9//! AABB list, one [`BatchMeta`] entry per batch, one atomic counter slot per
10//! batch, and one indirect-draw entry per batch. Plugins with many meshes
11//! pack them all into one submission so the renderer dispatches the compute
12//! pass once for the whole group.
13//!
14//! For the common one-mesh-N-instances case, see
15//! [`crate::renderer::ViewportRenderer::submit_cull_single_mesh`]. It takes
16//! the per-mesh draw parameters directly and fills the lib's scratch meta +
17//! counter buffers for you.
18//!
19//! Submit via [`crate::renderer::ViewportRenderer::submit_cull`] or
20//! [`crate::renderer::ViewportRenderer::submit_cull_shadow`].
21
22pub use crate::resources::BatchMeta;
23
24/// Per-instance world-space bounding box for GPU culling.
25///
26/// Plugin code builds an `array<InstanceAabb>` storage buffer, one entry per
27/// drawable instance, and passes it via [`CullSubmission::instance_aabbs`].
28///
29/// Layout matches the cull shader's contract (32 bytes per entry).
30/// `batch_index` selects which [`BatchMeta`] entry the instance belongs to;
31/// for a single-batch submission set it to 0. `cast_shadows` is honoured
32/// when [`CullSubmission::shadow_pass`] is `true` or the submission goes
33/// through `submit_cull_shadow`.
34#[repr(C)]
35#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable, Debug)]
36pub struct InstanceAabb {
37 /// World-space minimum corner.
38 pub min: [f32; 3],
39 /// Index into the batch_meta buffer that this instance belongs to.
40 pub batch_index: u32,
41 /// World-space maximum corner.
42 pub max: [f32; 3],
43 /// `1` if the instance participates in shadow casting, `0` to skip it
44 /// during shadow cull dispatches. Ignored on non-shadow submissions.
45 pub cast_shadows: u32,
46}
47
48const _: () = assert!(std::mem::size_of::<InstanceAabb>() == 32);
49
50/// Inputs to one cull dispatch.
51///
52/// Buffer requirements:
53///
54/// - `instance_aabbs`: `STORAGE`. `instance_count` x [`InstanceAabb`].
55/// - `batch_meta`: `STORAGE`. `batch_count` x [`BatchMeta`].
56/// - `counter`: `STORAGE`. `batch_count` x `u32`, used as atomic counters.
57/// The renderer zeroes this slot before the dispatch.
58/// - `visible_out`: `STORAGE`. Holds up to `instance_count` x `u32`, the
59/// compacted list of visible instance indices written by the cull pass.
60/// - `indirect_out`: `STORAGE | INDIRECT`. `batch_count` x
61/// `DrawIndexedIndirect` (20 bytes per entry). The static fields
62/// (`index_count`, `first_index`, `base_vertex`, `first_instance`) come
63/// from the matching [`BatchMeta`] entry; `instance_count` is overwritten
64/// by the cull pass.
65///
66/// After the encoder runs, draw each batch with
67/// `pass.draw_indexed_indirect(indirect_out, batch_idx * 20)` using
68/// `visible_out` as the per-instance lookup buffer.
69pub struct CullSubmission<'a> {
70 /// Storage buffer of [`InstanceAabb`] entries.
71 pub instance_aabbs: &'a wgpu::Buffer,
72 /// Number of valid entries in `instance_aabbs` (not the buffer capacity).
73 pub instance_count: u32,
74 /// Storage buffer of [`BatchMeta`] entries, one per batch.
75 pub batch_meta: &'a wgpu::Buffer,
76 /// Number of valid entries in `batch_meta`.
77 pub batch_count: u32,
78 /// Storage buffer used for per-batch atomic counters. Must hold at
79 /// least `batch_count` u32 slots. The cull pass zeroes the counters at
80 /// the end of its dispatch so the same buffer can be reused across
81 /// cascades or frames.
82 pub counter: &'a wgpu::Buffer,
83 /// Storage buffer that receives the compacted list of visible instance
84 /// indices. Sized for at least `instance_count` u32 slots.
85 pub visible_out: &'a wgpu::Buffer,
86 /// Storage / indirect buffer that receives one `DrawIndexedIndirect`
87 /// entry per batch (20 bytes each).
88 pub indirect_out: &'a wgpu::Buffer,
89 /// `true` to run the shadow-cull variant on the main camera pass: the
90 /// cull shader honours `InstanceAabb::cast_shadows` and skips entries
91 /// with the flag clear. `submit_cull_shadow` forces this on regardless
92 /// of the value here.
93 pub shadow_pass: bool,
94}
95
96/// Draw parameters for a one-mesh-N-instances submission.
97///
98/// Passed to [`crate::renderer::ViewportRenderer::submit_cull_single_mesh`]
99/// when a plugin only has one mesh to cull. The renderer fills its scratch
100/// `BatchMeta` and counter slots from these fields, so the plugin does not
101/// have to allocate either buffer.
102#[derive(Copy, Clone, Debug)]
103pub struct SingleMeshDraw {
104 /// Mesh index count for one instance.
105 pub index_count: u32,
106 /// First index offset (typically 0).
107 pub first_index: u32,
108 /// Vertex offset (typically 0).
109 pub base_vertex: i32,
110 /// First-instance value written into the indirect entry (typically 0).
111 pub first_instance: u32,
112}