1use std::{
2 marker::PhantomData,
3 num::NonZeroU64,
4 sync::{Mutex, PoisonError},
5};
6
7use bevy::{
8 prelude::*,
9 render::{
10 Extract, ExtractSchedule, Render, RenderApp, RenderStartup,
11 render_resource::{
12 Buffer, BufferInitDescriptor, BufferUsages,
13 encase::{self, ShaderSize, internal::WriteInto},
14 },
15 renderer::{RenderDevice, RenderQueue},
16 },
17};
18
19use crate::{
20 ShaderSystems,
21 bindings::{AutoBufferKind, BindingTable, register_binding},
22};
23
24#[derive(Resource)]
46pub struct ArrayBufferChanges<Tag> {
47 queue: Mutex<ChangeQueue>,
50 len: usize,
51 stride: usize,
52 _marker: PhantomData<Tag>,
53}
54
55impl<Tag> Default for ArrayBufferChanges<Tag> {
56 fn default() -> Self {
57 Self {
58 queue: Mutex::default(),
59 len: 0,
60 stride: 0,
61 _marker: PhantomData,
62 }
63 }
64}
65
66#[derive(Default)]
68struct ChangeQueue {
69 has_fill: bool,
71 fill: Vec<u8>,
72 indices: Vec<usize>,
75 bytes: Vec<u8>,
76}
77
78impl ChangeQueue {
79 fn is_empty(&self) -> bool {
80 !self.has_fill && self.indices.is_empty()
81 }
82
83 fn clear(&mut self) {
85 self.has_fill = false;
86 self.fill.clear();
87 self.indices.clear();
88 self.bytes.clear();
89 }
90
91 fn push<T: ShaderSize + WriteInto>(&mut self, index: usize, value: &T, stride: usize) {
93 let start = self.bytes.len();
94 self.bytes.resize(start + stride, 0);
95 write_element(value, &mut self.bytes[start..]);
96 self.indices.push(index);
97 }
98
99 fn append(&mut self, newer: &mut ChangeQueue) {
101 if self.is_empty() || newer.has_fill {
102 std::mem::swap(self, newer);
104 } else {
105 self.indices.append(&mut newer.indices);
106 self.bytes.append(&mut newer.bytes);
107 }
108 newer.clear();
109 }
110}
111
112fn write_element<T: ShaderSize + WriteInto>(value: &T, out: &mut [u8]) {
114 encase::StorageBuffer::new(out).write(value).unwrap();
115}
116
117pub(crate) fn array_stride<T: ShaderSize>() -> usize {
119 <[T; 1] as ShaderSize>::SHADER_SIZE.get() as usize
121}
122
123pub(crate) fn default_filled_bytes<T: ShaderSize + WriteInto + Default>(len: usize) -> Vec<u8> {
125 let stride = array_stride::<T>();
126 let mut element = vec![0u8; stride];
127 write_element(&T::default(), &mut element);
128 element.repeat(len)
129}
130
131pub(crate) fn register<Tag, T, const N: usize>(
134 app: &mut App,
135 group_index: u32,
136 binding_index: u32,
137 read_write: bool,
138) where
139 Tag: Send + Sync + 'static,
140 T: ShaderSize + WriteInto + Default + Send + Sync + 'static,
141{
142 let stride = array_stride::<T>();
143 app.insert_resource(ArrayBufferChanges::<Tag> {
144 queue: Mutex::default(),
145 len: N,
146 stride,
147 _marker: PhantomData,
148 });
149
150 let render_app = app.sub_app_mut(RenderApp);
151 register_binding(
152 render_app,
153 group_index,
154 binding_index,
155 AutoBufferKind::Storage {
156 read_only: !read_write,
157 },
158 NonZeroU64::new((stride * N) as u64),
159 );
160
161 render_app.add_systems(
163 RenderStartup,
164 move |mut commands: Commands, render_device: Res<RenderDevice>| {
165 let buffer = render_device.create_buffer_with_data(&BufferInitDescriptor {
166 label: Some("array_buffer"),
167 contents: &default_filled_bytes::<T>(N),
168 usage: BufferUsages::STORAGE | BufferUsages::COPY_DST | BufferUsages::COPY_SRC,
170 });
171
172 commands.insert_resource(ArrayBufferState::<Tag> {
173 buffer,
174 stride,
175 len: N,
176 _marker: PhantomData,
177 });
178 },
179 );
180
181 render_app.init_resource::<PendingArrayChanges<Tag>>();
182 render_app.add_systems(ExtractSchedule, extract_array_changes::<Tag>);
183
184 render_app.add_systems(
185 Render,
186 (move |state: Option<Res<ArrayBufferState<Tag>>>,
187 mut pending: ResMut<PendingArrayChanges<Tag>>,
188 render_queue: Res<RenderQueue>,
189 mut table: ResMut<BindingTable>| {
190 let Some(state) = state else {
191 return;
192 };
193 let PendingArrayChanges { queue, scratch, .. } = &mut *pending;
194 plan_writes(queue, state.stride, state.len, scratch, |offset, bytes| {
195 render_queue.write_buffer(&state.buffer, offset, bytes);
196 });
197 queue.clear();
198 table.set_buffer(group_index, binding_index, &state.buffer);
199 })
200 .in_set(ShaderSystems::PrepareBindings),
201 );
202}
203
204impl<Tag> ArrayBufferChanges<Tag> {
205 pub fn set<T: ShaderSize + WriteInto>(&mut self, index: usize, value: T) {
208 self.check_index(index);
209 let stride = self.stride;
210 self.queue_mut().push(index, &value, stride);
211 }
212
213 pub fn set_many<T, I>(&mut self, pairs: I)
223 where
224 T: ShaderSize + WriteInto,
225 I: IntoIterator<Item = (usize, T)>,
226 {
227 for (index, value) in pairs {
228 self.set(index, value);
229 }
230 }
231
232 pub fn set_all<T: ShaderSize + WriteInto>(&mut self, value: T) {
235 let stride = self.stride;
236 let queue = self.queue_mut();
237 queue.clear();
239 queue.has_fill = true;
240 queue.fill.resize(stride, 0);
241 write_element(&value, &mut queue.fill);
242 }
243
244 fn check_index(&self, index: usize) {
245 if index >= self.len {
246 panic!(
247 "ArrayBufferChanges::set index out of bounds: {} >= {}",
248 index, self.len
249 );
250 }
251 }
252
253 fn queue_mut(&mut self) -> &mut ChangeQueue {
254 self.queue.get_mut().unwrap_or_else(PoisonError::into_inner)
255 }
256}
257
258#[derive(Resource)]
260pub struct ArrayBufferState<Tag> {
261 pub(crate) buffer: Buffer,
262 pub(crate) stride: usize,
264 pub(crate) len: usize,
266 pub(crate) _marker: PhantomData<Tag>,
267}
268
269#[derive(Resource)]
271struct PendingArrayChanges<Tag> {
272 queue: ChangeQueue,
273 scratch: WriteScratch,
274 _marker: PhantomData<Tag>,
275}
276
277impl<Tag> Default for PendingArrayChanges<Tag> {
278 fn default() -> Self {
279 Self {
280 queue: ChangeQueue::default(),
281 scratch: WriteScratch::default(),
282 _marker: PhantomData,
283 }
284 }
285}
286
287fn extract_array_changes<Tag: Send + Sync + 'static>(
294 changes: Extract<Option<Res<ArrayBufferChanges<Tag>>>>,
295 mut pending: ResMut<PendingArrayChanges<Tag>>,
296) {
297 let Some(changes) = changes.as_ref() else {
298 return;
299 };
300 let mut queue = changes.queue.lock().unwrap_or_else(PoisonError::into_inner);
301 if !queue.is_empty() {
302 pending.queue.append(&mut queue);
303 }
304}
305
306#[derive(Default)]
308struct WriteScratch {
309 order: Vec<usize>,
311 bytes: Vec<u8>,
313}
314
315fn plan_writes(
318 queue: &ChangeQueue,
319 stride: usize,
320 len: usize,
321 scratch: &mut WriteScratch,
322 mut write: impl FnMut(u64, &[u8]),
323) {
324 let element = |position: usize| &queue.bytes[position * stride..(position + 1) * stride];
325 let WriteScratch { order, bytes } = scratch;
326
327 if queue.has_fill {
328 bytes.clear();
330 for _ in 0..len {
331 bytes.extend_from_slice(&queue.fill);
332 }
333 for (position, &index) in queue.indices.iter().enumerate() {
334 bytes[index * stride..(index + 1) * stride].copy_from_slice(element(position));
335 }
336 write(0, bytes);
337 return;
338 }
339
340 order.clear();
343 order.extend(0..queue.indices.len());
344 order.sort_by_key(|&position| queue.indices[position]);
345
346 let mut run_start = 0;
347 bytes.clear();
348 let mut iter = order.iter().copied().peekable();
349 while let Some(position) = iter.next() {
350 let index = queue.indices[position];
351 if iter
352 .peek()
353 .is_some_and(|&next| queue.indices[next] == index)
354 {
355 continue; }
357 if !bytes.is_empty() && index != run_start + bytes.len() / stride {
358 write((run_start * stride) as u64, bytes);
359 bytes.clear();
360 }
361 if bytes.is_empty() {
362 run_start = index;
363 }
364 bytes.extend_from_slice(element(position));
365 }
366 if !bytes.is_empty() {
367 write((run_start * stride) as u64, bytes);
368 }
369}
370
371#[cfg(test)]
372mod tests {
373 use super::*;
374
375 const STRIDE: usize = 16;
376 const LEN: usize = 32;
377
378 #[derive(Clone, Copy, Debug)]
380 enum Op {
381 Set(usize, u32),
382 SetAll(u32),
383 }
384
385 fn element_bytes(value: u32) -> Vec<u8> {
386 let mut bytes = vec![0u8; STRIDE];
387 write_element(&UVec4::splat(value), &mut bytes);
388 bytes
389 }
390
391 fn queue_from(ops: &[Op]) -> ChangeQueue {
392 let mut queue = ChangeQueue::default();
393 for &op in ops {
394 match op {
395 Op::Set(index, value) => queue.push(index, &UVec4::splat(value), STRIDE),
396 Op::SetAll(value) => {
397 queue.clear();
398 queue.has_fill = true;
399 queue.fill = element_bytes(value);
400 }
401 }
402 }
403 queue
404 }
405
406 fn naive(initial: &[u8], ops: &[Op]) -> Vec<u8> {
407 let mut buffer = initial.to_vec();
408 for &op in ops {
409 match op {
410 Op::Set(index, value) => buffer[index * STRIDE..(index + 1) * STRIDE]
411 .copy_from_slice(&element_bytes(value)),
412 Op::SetAll(value) => buffer = element_bytes(value).repeat(LEN),
413 }
414 }
415 buffer
416 }
417
418 fn apply(initial: &[u8], queue: &ChangeQueue) -> (Vec<u8>, usize) {
421 let mut buffer = initial.to_vec();
422 let mut writes = 0;
423 plan_writes(
424 queue,
425 STRIDE,
426 LEN,
427 &mut WriteScratch::default(),
428 |offset, bytes| {
429 let offset = offset as usize;
430 buffer[offset..offset + bytes.len()].copy_from_slice(bytes);
431 writes += 1;
432 },
433 );
434 (buffer, writes)
435 }
436
437 fn initial() -> Vec<u8> {
438 (0..LEN as u32)
439 .flat_map(|i| element_bytes(1000 + i))
440 .collect()
441 }
442
443 #[test]
444 fn empty_queue_writes_nothing() {
445 assert_eq!(apply(&initial(), &ChangeQueue::default()), (initial(), 0));
446 }
447
448 #[test]
449 fn contiguous_indices_become_one_write() {
450 let ops = [Op::Set(5, 1), Op::Set(3, 2), Op::Set(4, 3)];
451 let (buffer, writes) = apply(&initial(), &queue_from(&ops));
452 assert_eq!(buffer, naive(&initial(), &ops));
453 assert_eq!(writes, 1);
454 }
455
456 #[test]
457 fn gaps_split_writes() {
458 let ops = [Op::Set(0, 1), Op::Set(1, 2), Op::Set(9, 3), Op::Set(31, 4)];
459 let (buffer, writes) = apply(&initial(), &queue_from(&ops));
460 assert_eq!(buffer, naive(&initial(), &ops));
461 assert_eq!(writes, 3);
462 }
463
464 #[test]
465 fn last_write_wins() {
466 let ops = [Op::Set(7, 1), Op::Set(8, 2), Op::Set(7, 3), Op::Set(7, 4)];
467 let (buffer, writes) = apply(&initial(), &queue_from(&ops));
468 assert_eq!(buffer, naive(&initial(), &ops));
469 assert_eq!(writes, 1);
470 }
471
472 #[test]
473 fn set_all_then_set() {
474 let ops = [Op::Set(2, 9), Op::SetAll(5), Op::Set(3, 6)];
475 let (buffer, writes) = apply(&initial(), &queue_from(&ops));
476 assert_eq!(buffer, naive(&initial(), &ops));
477 assert_eq!(writes, 1);
478 }
479
480 #[test]
481 fn matches_naive_on_random_sequences() {
482 let mut seed = 0x2545_f491_u64;
484 let mut next = |bound: u64| {
485 seed = seed
486 .wrapping_mul(6364136223846793005)
487 .wrapping_add(1442695040888963407);
488 ((seed >> 33) % bound) as usize
489 };
490 for _ in 0..500 {
491 let ops: Vec<Op> = (0..next(40))
492 .map(|_| match next(20) {
493 0 => Op::SetAll(next(100) as u32),
494 _ => Op::Set(next(LEN as u64), next(100) as u32),
495 })
496 .collect();
497 assert_eq!(
498 apply(&initial(), &queue_from(&ops)).0,
499 naive(&initial(), &ops),
500 "{ops:?}"
501 );
502 }
503 }
504
505 #[test]
506 fn append_keeps_order_and_fill_semantics() {
507 let older = [Op::Set(1, 1), Op::Set(2, 2)];
509 let newer = [Op::Set(1, 3), Op::Set(4, 4)];
510 let mut pending = queue_from(&older);
511 pending.append(&mut queue_from(&newer));
512 let all: Vec<Op> = older.iter().chain(&newer).copied().collect();
513 assert_eq!(apply(&initial(), &pending).0, naive(&initial(), &all));
514
515 let newer = [Op::SetAll(8), Op::Set(0, 9)];
517 let mut pending = queue_from(&older);
518 let mut incoming = queue_from(&newer);
519 pending.append(&mut incoming);
520 assert!(incoming.is_empty());
521 let all: Vec<Op> = older.iter().chain(&newer).copied().collect();
522 assert_eq!(apply(&initial(), &pending).0, naive(&initial(), &all));
523 }
524}