1use std::{collections::BTreeMap, marker::PhantomData};
2
3use bevy::{
4 prelude::*,
5 render::{
6 ExtractSchedule, MainWorld, Render, RenderApp, RenderStartup,
7 render_resource::{Buffer, BufferUsages},
8 renderer::{RenderDevice, RenderQueue},
9 },
10};
11use encase::ShaderSize;
12use encase::internal::WriteInto;
13use wgpu::util::BufferInitDescriptor;
14
15use crate::{
16 ShaderSystems,
17 bindings::{AutoBufferKind, BindingTable, register_binding},
18};
19
20#[derive(Resource)]
42pub struct ArrayBufferChanges<Tag> {
43 pub(crate) changes: Vec<(usize, Vec<u8>)>,
44 pub(crate) len: usize,
45 pub(crate) _marker: PhantomData<Tag>,
46}
47
48impl<Tag> Default for ArrayBufferChanges<Tag> {
49 fn default() -> Self {
50 Self {
51 changes: Vec::new(),
52 len: 0,
53 _marker: PhantomData,
54 }
55 }
56}
57
58fn serialize_element<T: ShaderSize + WriteInto>(value: &T) -> Vec<u8> {
59 let el_size = T::SHADER_SIZE.get() as usize;
60 let mut bytes = vec![0u8; el_size];
61 encase::StorageBuffer::new(&mut bytes[..])
62 .write(value)
63 .unwrap();
64 bytes
65}
66
67pub(crate) fn array_stride<T: ShaderSize>() -> usize {
69 <[T; 1] as ShaderSize>::SHADER_SIZE.get() as usize
71}
72
73pub(crate) fn default_filled_bytes<T: ShaderSize + WriteInto + Default>(len: usize) -> Vec<u8> {
75 let stride = array_stride::<T>();
76 let el_bytes = serialize_element(&T::default());
77 let mut bytes = vec![0u8; stride * len];
78 for i in 0..len {
79 bytes[i * stride..i * stride + el_bytes.len()].copy_from_slice(&el_bytes);
80 }
81 bytes
82}
83
84pub(crate) fn register<Tag, T, const N: usize>(
87 app: &mut App,
88 group_index: u32,
89 binding_index: u32,
90 read_write: bool,
91) where
92 Tag: Send + Sync + 'static,
93 T: ShaderSize + WriteInto + Default + Send + Sync + 'static,
94{
95 app.insert_resource(ArrayBufferChanges::<Tag> {
96 changes: Vec::new(),
97 len: N,
98 _marker: PhantomData,
99 });
100
101 let render_app = app.sub_app_mut(RenderApp);
102 register_binding(
103 render_app,
104 group_index,
105 binding_index,
106 AutoBufferKind::Storage {
107 read_only: !read_write,
108 },
109 );
110
111 render_app.add_systems(
113 RenderStartup,
114 |mut commands: Commands, render_device: Res<RenderDevice>| {
115 let buffer = render_device.create_buffer_with_data(&BufferInitDescriptor {
116 label: Some("array_buffer"),
117 contents: &default_filled_bytes::<T>(N),
118 usage: BufferUsages::STORAGE | BufferUsages::COPY_DST | BufferUsages::COPY_SRC,
120 });
121
122 commands.insert_resource(ArrayBufferState::<Tag> {
123 buffer,
124 stride: array_stride::<T>(),
125 _marker: PhantomData,
126 });
127 },
128 );
129
130 render_app.add_systems(ExtractSchedule, extract_array_changes::<Tag>);
131
132 render_app.add_systems(
133 Render,
134 (move |state: Option<Res<ArrayBufferState<Tag>>>,
135 changes: Option<Res<ArrayBufferChanges<Tag>>>,
136 render_queue: Res<RenderQueue>,
137 mut table: ResMut<BindingTable>| {
138 let (Some(state), Some(changes)) = (state, changes) else {
139 return;
140 };
141 apply_array_buffer_changes(
142 &state,
143 &changes,
144 &render_queue,
145 &mut table,
146 group_index,
147 binding_index,
148 );
149 })
150 .in_set(ShaderSystems::PrepareBindings),
151 );
152}
153
154impl<Tag> ArrayBufferChanges<Tag> {
155 pub fn set<T: ShaderSize + WriteInto>(&mut self, index: usize, value: T) {
158 if index >= self.len {
159 panic!(
160 "ArrayBufferChanges::set index out of bounds: {} >= {}",
161 index, self.len
162 );
163 }
164 let bytes = serialize_element(&value);
165 self.changes.push((index, bytes));
166 }
167
168 pub fn set_many<T, I>(&mut self, pairs: I)
178 where
179 T: ShaderSize + WriteInto,
180 I: IntoIterator<Item = (usize, T)>,
181 {
182 for (index, value) in pairs {
183 self.set(index, value);
184 }
185 }
186
187 pub fn set_all<T: ShaderSize + WriteInto>(&mut self, value: T) {
190 let bytes = serialize_element(&value);
191 self.changes.reserve(self.len);
192 for i in 0..self.len {
193 self.changes.push((i, bytes.clone()));
194 }
195 }
196}
197
198#[derive(Resource)]
200pub struct ArrayBufferState<Tag> {
201 pub(crate) buffer: Buffer,
202 pub(crate) stride: usize,
204 pub(crate) _marker: PhantomData<Tag>,
205}
206
207pub(crate) fn extract_array_changes<Tag: Send + Sync + 'static>(
213 mut commands: Commands,
214 mut main_world: ResMut<MainWorld>,
215) {
216 let extracted = {
217 let Some(mut changes) = main_world.get_resource_mut::<ArrayBufferChanges<Tag>>() else {
218 return;
219 };
220 (std::mem::take(&mut changes.changes), changes.len)
221 };
222 commands.insert_resource(ArrayBufferChanges::<Tag> {
223 changes: extracted.0,
224 len: extracted.1,
225 _marker: PhantomData,
226 });
227}
228
229fn apply_array_buffer_changes<Tag: Send + Sync + 'static>(
232 state: &ArrayBufferState<Tag>,
233 changes: &ArrayBufferChanges<Tag>,
234 render_queue: &RenderQueue,
235 table: &mut BindingTable,
236 group_index: u32,
237 binding_index: u32,
238) {
239 let stride = state.stride;
240
241 let mut by_index: BTreeMap<usize, &[u8]> = BTreeMap::new();
243 for (i, bytes) in &changes.changes {
244 by_index.insert(*i, bytes);
245 }
246
247 let mut runs: Vec<(u64, Vec<u8>)> = Vec::new();
249 let mut run_start: Option<usize> = None;
250 let mut run_bytes: Vec<u8> = Vec::new();
251 let mut prev: Option<usize> = None;
252
253 for (&index, &el_bytes) in &by_index {
254 let mut padded = vec![0u8; stride];
256 padded[..el_bytes.len()].copy_from_slice(el_bytes);
257
258 match prev {
259 Some(p) if index == p + 1 => {
260 run_bytes.extend_from_slice(&padded);
261 }
262 _ => {
263 if let Some(start) = run_start {
264 runs.push((start as u64 * stride as u64, std::mem::take(&mut run_bytes)));
265 }
266 run_start = Some(index);
267 run_bytes = padded;
268 }
269 }
270 prev = Some(index);
271 }
272 if let Some(start) = run_start {
273 runs.push((start as u64 * stride as u64, run_bytes));
274 }
275
276 for (offset, bytes) in runs {
277 render_queue.write_buffer(&state.buffer, offset, &bytes);
278 }
279
280 table.set_buffer(group_index, binding_index, &state.buffer);
281}