Skip to content

Commit c237dae

Browse files
authored
Bind entire metadata buffer with dynamic indexing (#503)
Remove the individual binding of a single `GpuEffectMetadata` entry per effect, and instead bind the entire buffer. Use dynamic indexing from the `GpuSpawner` to fetch the effect's metadata entry in the buffer. This allows collapsing all bind groups using the same buffer into a single one, and lays some foundations for batching effects.
1 parent 48e7a5b commit c237dae

File tree

5 files changed

+28
-44
lines changed

5 files changed

+28
-44
lines changed

src/lib.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -949,13 +949,12 @@ impl EffectShaderSource {
949949
"@group(3) @binding({binding_index}) var<storage, read_write> event_buffer_{i} : EventBuffer;\n"));
950950
emit_event_buffer_append_funcs_code.push_str(&format!(
951951
r##"/// Append one or more spawn events to the event buffer.
952-
fn append_spawn_events_{0}(particle_index: u32, count: u32) {{
952+
fn append_spawn_events_{0}(base_child_index: u32, particle_index: u32, count: u32) {{
953953
// Optimize this case.
954954
if (count == 0u) {{
955955
return;
956956
}}
957957
958-
let base_child_index = effect_metadata.base_child_index;
959958
let capacity = arrayLength(&event_buffer_{0}.spawn_events);
960959
let base = min(u32(atomicAdd(&child_info_buffer.rows[base_child_index + {0}].event_count, i32(count))), capacity);
961960
let capped_count = min(count, capacity - base);

src/modifier/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -674,11 +674,11 @@ impl EmitSpawnEventModifier {
674674

675675
let cond = match self.condition {
676676
EventEmitCondition::Always => format!(
677-
"if (is_alive) {{ append_spawn_events_{channel_index}(particle_index, {}); }}",
677+
"if (is_alive) {{ append_spawn_events_{channel_index}((*effect_metadata).base_child_index, particle_index, {}); }}",
678678
count_var
679679
),
680680
EventEmitCondition::OnDie => format!(
681-
"if (was_alive && !is_alive) {{ append_spawn_events_{channel_index}(particle_index, {}); }}",
681+
"if (was_alive && !is_alive) {{ append_spawn_events_{channel_index}((*effect_metadata).base_child_index, particle_index, {}); }}",
682682
count_var
683683
),
684684
};

src/render/mod.rs

Lines changed: 10 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2817,11 +2817,6 @@ impl GpuLimits {
28172817
pub fn effect_metadata_offset(&self, buffer_index: u32) -> u64 {
28182818
self.effect_metadata_aligned_size.get() as u64 * buffer_index as u64
28192819
}
2820-
2821-
/// Byte alignment for [`GpuEffectMetadata`].
2822-
pub fn effect_metadata_size(&self) -> NonZeroU64 {
2823-
NonZeroU64::new(self.effect_metadata_aligned_size.get() as u64).unwrap()
2824-
}
28252820
}
28262821

28272822
/// Global render world resource containing the GPU data to draw all the
@@ -4838,15 +4833,13 @@ impl From<&ConsumeEventBuffers<'_>> for ConsumeEventKey {
48384833
struct InitMetadataBindGroupKey {
48394834
pub slab_id: SlabId,
48404835
pub effect_metadata_buffer: BufferId,
4841-
pub effect_metadata_offset: u32,
48424836
pub consume_event_key: Option<ConsumeEventKey>,
48434837
}
48444838

48454839
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
48464840
struct UpdateMetadataBindGroupKey {
48474841
pub slab_id: SlabId,
48484842
pub effect_metadata_buffer: BufferId,
4849-
pub effect_metadata_offset: u32,
48504843
pub child_info_buffer_id: Option<BufferId>,
48514844
pub event_buffers_keys: Vec<BindingKey>,
48524845
}
@@ -4942,33 +4935,29 @@ impl EffectBindGroups {
49424935
pub(self) fn get_or_create_init_metadata(
49434936
&mut self,
49444937
effect_batch: &EffectBatch,
4945-
gpu_limits: &GpuLimits,
49464938
render_device: &RenderDevice,
49474939
layout: &BindGroupLayout,
49484940
effect_metadata_buffer: &Buffer,
49494941
consume_event_buffers: Option<ConsumeEventBuffers>,
49504942
) -> Result<&BindGroup, ()> {
49514943
assert!(effect_batch.metadata_table_id.is_valid());
49524944

4953-
let effect_metadata_offset =
4954-
gpu_limits.effect_metadata_offset(effect_batch.metadata_table_id.0) as u32;
49554945
let key = InitMetadataBindGroupKey {
49564946
slab_id: effect_batch.slab_id,
49574947
effect_metadata_buffer: effect_metadata_buffer.id(),
4958-
effect_metadata_offset,
49594948
consume_event_key: consume_event_buffers.as_ref().map(Into::into),
49604949
};
49614950

49624951
let make_entry = || {
49634952
let mut entries = Vec::with_capacity(3);
49644953
entries.push(
4965-
// @group(3) @binding(0) var<storage, read_write> effect_metadata : EffectMetadata;
4954+
// @group(3) @binding(0) var<storage, read_write> effect_metadatas : array<EffectMetadata>;
49664955
BindGroupEntry {
49674956
binding: 0,
49684957
resource: BindingResource::Buffer(BufferBinding {
49694958
buffer: effect_metadata_buffer,
4970-
offset: key.effect_metadata_offset as u64,
4971-
size: Some(gpu_limits.effect_metadata_size()),
4959+
offset: 0,
4960+
size: None,
49724961
}),
49734962
},
49744963
);
@@ -5038,7 +5027,6 @@ impl EffectBindGroups {
50385027
pub(self) fn get_or_create_update_metadata(
50395028
&mut self,
50405029
effect_batch: &EffectBatch,
5041-
gpu_limits: &GpuLimits,
50425030
render_device: &RenderDevice,
50435031
layout: &BindGroupLayout,
50445032
effect_metadata_buffer: &Buffer,
@@ -5067,23 +5055,20 @@ impl EffectBindGroups {
50675055
let key = UpdateMetadataBindGroupKey {
50685056
slab_id: effect_batch.slab_id,
50695057
effect_metadata_buffer: effect_metadata_buffer.id(),
5070-
effect_metadata_offset: gpu_limits
5071-
.effect_metadata_offset(effect_batch.metadata_table_id.0)
5072-
as u32,
50735058
child_info_buffer_id,
50745059
event_buffers_keys,
50755060
};
50765061

50775062
let make_entry = || {
50785063
let mut entries = Vec::with_capacity(2 + event_buffers.len());
5079-
// @group(3) @binding(0) var<storage, read_write> effect_metadata :
5080-
// EffectMetadata;
5064+
// @group(3) @binding(0) var<storage, read_write> effect_metadatas :
5065+
// array<EffectMetadata>;
50815066
entries.push(BindGroupEntry {
50825067
binding: 0,
50835068
resource: BindingResource::Buffer(BufferBinding {
50845069
buffer: effect_metadata_buffer,
5085-
offset: key.effect_metadata_offset as u64,
5086-
size: Some(gpu_limits.effect_metadata_aligned_size.into()),
5070+
offset: 0,
5071+
size: None,
50875072
}),
50885073
});
50895074
if emits_gpu_spawn_events {
@@ -6372,7 +6357,6 @@ pub(crate) fn prepare_bind_groups(
63726357
if effect_bind_groups
63736358
.get_or_create_init_metadata(
63746359
effect_batch,
6375-
&effects_meta.gpu_limits,
63766360
&render_device,
63776361
init_metadata_layout,
63786362
effects_meta.effect_metadata_buffer.buffer().unwrap(),
@@ -6397,7 +6381,6 @@ pub(crate) fn prepare_bind_groups(
63976381
if effect_bind_groups
63986382
.get_or_create_update_metadata(
63996383
effect_batch,
6400-
&effects_meta.gpu_limits,
64016384
&render_device,
64026385
update_metadata_layout,
64036386
effects_meta.effect_metadata_buffer.buffer().unwrap(),
@@ -7242,16 +7225,16 @@ impl Node for VfxSimulateNode {
72427225
}
72437226

72447227
// Compute dynamic offsets
7245-
let spawner_index = effect_batch.spawner_base;
7228+
let spawner_base = effect_batch.spawner_base;
72467229
let spawner_aligned_size = effects_meta.spawner_buffer.aligned_size();
72477230
assert!(spawner_aligned_size >= GpuSpawnerParams::min_size().get() as usize);
7248-
let spawner_offset = spawner_index * spawner_aligned_size as u32;
7231+
let spawner_offset = spawner_base * spawner_aligned_size as u32;
72497232
let property_offset = effect_batch.property_offset;
72507233

72517234
trace!(
72527235
"record commands for update pipeline of effect {:?} spawner_base={}",
72537236
effect_batch.handle,
7254-
spawner_index,
7237+
spawner_base,
72557238
);
72567239

72577240
// Setup update pass

src/render/vfx_init.wgsl

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ struct ParentParticleBuffer {
4242
{{PROPERTIES_BINDING}}
4343

4444
// "metadata" group @3
45-
@group(3) @binding(0) var<storage, read_write> effect_metadata : EffectMetadata;
45+
@group(3) @binding(0) var<storage, read_write> effect_metadatas : array<EffectMetadata>;
4646
#ifdef CONSUME_GPU_SPAWN_EVENTS
4747
@group(3) @binding(1) var<storage, read> child_info_buffer : ChildInfoBuffer;
4848
@group(3) @binding(2) var<storage, read> event_buffer : EventBuffer;
@@ -56,7 +56,8 @@ fn main(@builtin(global_invocation_id) global_invocation_id: vec3<u32>) {
5656

5757
// Cap to max number of dead particles, copied from (capacity - alive_count) at the end
5858
// of the previous iteration, and constant during this pass (unlike alive_count).
59-
let max_spawn = atomicLoad(&effect_metadata.max_spawn);
59+
let effect_metadata = &effect_metadatas[spawner.effect_metadata_index];
60+
let max_spawn = atomicLoad(&(*effect_metadata).max_spawn);
6061
if (thread_index >= max_spawn) {
6162
return;
6263
}
@@ -65,7 +66,7 @@ fn main(@builtin(global_invocation_id) global_invocation_id: vec3<u32>) {
6566
// in workgroup_size(64) so more threads than needed are launched (rounded up to 64).
6667
#ifdef CONSUME_GPU_SPAWN_EVENTS
6768
let event_index = thread_index;
68-
let global_child_index = effect_metadata.global_child_index;
69+
let global_child_index = (*effect_metadata).global_child_index;
6970
let event_count = child_info_buffer.rows[global_child_index].event_count;
7071
if (event_index >= u32(event_count)) {
7172
return;
@@ -81,14 +82,14 @@ fn main(@builtin(global_invocation_id) global_invocation_id: vec3<u32>) {
8182
#endif
8283

8384
// Count as alive, and recycle a dead particle slot to store the newly spawned particle
84-
let alive_index = atomicAdd(&effect_metadata.alive_count, 1u);
85-
let dead_index = effect_metadata.capacity - alive_index - 1u;
85+
let alive_index = atomicAdd(&(*effect_metadata).alive_count, 1u);
86+
let dead_index = (*effect_metadata).capacity - alive_index - 1u;
8687
let particle_index = indirect_buffer.rows[dead_index].dead_index;
8788

8889
// Bump the particle counter each time we allocate a particle. This generates a unique
8990
// particle ID used for various purposes (but not directly by the simulation). We still
9091
// store it in a variable, because the INIT_CODE might access it.
91-
let particle_counter = atomicAdd(&effect_metadata.particle_counter, 1u);
92+
let particle_counter = atomicAdd(&(*effect_metadata).particle_counter, 1u);
9293

9394
// Initialize the PRNG seed
9495
seed = pcg_hash(particle_index ^ spawner.seed);
@@ -127,7 +128,7 @@ fn main(@builtin(global_invocation_id) global_invocation_id: vec3<u32>) {
127128
#endif
128129

129130
// Append to alive list of indirect buffer.
130-
let write_index = effect_metadata.indirect_write_index;
131+
let write_index = (*effect_metadata).indirect_write_index;
131132
indirect_buffer.rows[alive_index].particle_index[write_index] = particle_index;
132133

133134
// Write back new particle

src/render/vfx_update.wgsl

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ struct ParentParticleBuffer {
4343
{{PROPERTIES_BINDING}}
4444

4545
// "metadata" group @3
46-
@group(3) @binding(0) var<storage, read_write> effect_metadata : EffectMetadata;
46+
@group(3) @binding(0) var<storage, read_write> effect_metadatas : array<EffectMetadata>;
4747
#ifdef EMITS_GPU_SPAWN_EVENTS
4848
{{EMIT_EVENT_BUFFER_BINDINGS}}
4949
#endif
@@ -59,7 +59,8 @@ fn main(@builtin(global_invocation_id) global_invocation_id: vec3<u32>) {
5959
let thread_index = global_invocation_id.x;
6060

6161
// Cap at maximum number of alive particles.
62-
if (thread_index >= effect_metadata.max_update) {
62+
let effect_metadata = &effect_metadatas[spawner.effect_metadata_index];
63+
if (thread_index >= (*effect_metadata).max_update) {
6364
return;
6465
}
6566

@@ -83,18 +84,18 @@ fn main(@builtin(global_invocation_id) global_invocation_id: vec3<u32>) {
8384
// Check if alive
8485
if (!is_alive) {
8586
// Save dead index
86-
let alive_index = atomicSub(&effect_metadata.alive_count, 1u);
87-
let dead_index = effect_metadata.capacity - alive_index;
87+
let alive_index = atomicSub(&((*effect_metadata).alive_count), 1u);
88+
let dead_index = (*effect_metadata).capacity - alive_index;
8889
indirect_buffer.rows[dead_index].dead_index = particle_index;
8990

9091
// Also increment copy of dead count, which was updated in dispatch indirect
9192
// pass just before, and need to remain correct after this pass. We wouldn't have
9293
// to do that here if we had a per-effect pass between update and the next init.
93-
atomicAdd(&effect_metadata.max_spawn, 1u);
94+
atomicAdd(&((*effect_metadata).max_spawn), 1u);
9495
} else {
9596
// Increment visible particle count (in the absence of any GPU culling), and write
9697
// the indirection index for later rendering.
97-
let indirect_index = atomicAdd(&draw_indirect_buffer[effect_metadata.indirect_render_index].instance_count, 1u);
98+
let indirect_index = atomicAdd(&draw_indirect_buffer[(*effect_metadata).indirect_render_index].instance_count, 1u);
9899
indirect_buffer.rows[indirect_index].particle_index[write_index] = particle_index;
99100
}
100101
}

0 commit comments

Comments
 (0)