1use alloc::sync::{Arc, Weak};
5use core::{
6 slice,
7 sync::atomic::{AtomicU64, Ordering},
8};
9
10use bevy_app::{App, Plugin};
11use bevy_asset::{embedded_asset, load_embedded_asset, AssetServer, Handle};
12use bevy_derive::{Deref, DerefMut};
13use bevy_ecs::{
14 resource::Resource,
15 schedule::IntoScheduleConfigs as _,
16 system::{Commands, Res, ResMut},
17};
18use bevy_log::{debug, error, info};
19use bevy_material::{
20 bind_group_layout_entries::{
21 binding_types::{storage_buffer, storage_buffer_read_only, uniform_buffer},
22 BindGroupLayoutEntries,
23 },
24 descriptor::{BindGroupLayoutDescriptor, CachedComputePipelineId, ComputePipelineDescriptor},
25};
26use bevy_shader::Shader;
27use bytemuck::{Pod, Zeroable};
28use encase::ShaderType;
29use weak_table::WeakKeyHashMap;
30use wgpu::{BufferDescriptor, BufferUsages, ComputePassDescriptor, ShaderStages};
31
32use crate::{
33 diagnostic::RecordDiagnostics as _,
34 init_gpu_resource,
35 render_resource::{
36 AtomicPod, BindGroup, BindGroupEntries, Buffer, PipelineCache, RawBufferVec,
37 SpecializedComputePipeline, SpecializedComputePipelines, UniformBuffer,
38 },
39 renderer::{RenderContext, RenderDevice, RenderGraph, RenderGraphSystems, RenderQueue},
40 ExtractSchedule, GpuResourceAppExt, RenderApp, RenderStartup,
41};
42
43pub struct SparseBufferPlugin;
46
47impl Plugin for SparseBufferPlugin {
48 fn build(&self, app: &mut App) {
49 embedded_asset!(app, "sparse_buffer_update.wesl");
50 }
51
52 fn finish(&self, app: &mut App) {
53 let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
54 return;
55 };
56
57 render_app
58 .init_gpu_resource::<SparseBufferUpdateJobs>()
59 .init_gpu_resource::<SpecializedComputePipelines<SparseBufferUpdatePipelines>>()
60 .add_systems(ExtractSchedule, clear_sparse_buffer_jobs)
61 .add_systems(
62 RenderStartup,
63 (
64 init_sparse_buffer_update_pipelines,
65 init_sparse_buffer_update_bind_groups.after(init_gpu_resource::<SpecializedComputePipelines<SparseBufferUpdatePipelines>>),
66 )
67 .chain(),
68 )
69 .add_systems(
70 RenderGraph,
71 update_sparse_buffers.in_set(RenderGraphSystems::Begin),
74 );
75 }
76}
77
78#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Deref, DerefMut)]
80pub struct SparseBufferId(pub u64);
81
82pub type SparseBufferHandle = Arc<SparseBufferId>;
90
91static NEXT_SPARSE_BUFFER_ID: AtomicU64 = AtomicU64::new(0);
93
94const SPARSE_BUFFER_UPDATE_WORKGROUP_SIZE: u32 = 256;
96
97const SPARSE_UPLOAD_THRESHOLD: f64 = 0.15;
104
105const MAX_WORKGROUPS: u32 = 65535;
107
108const REALLOCATION_FACTOR: f64 = 1.5;
110const REALLOCATION_SIZE_MULTIPLE: usize = 256;
112
113const BITS_PER_WORD: u32 = 64;
115
116#[derive(Resource)]
120pub struct SparseBufferUpdatePipelines {
121 bind_group_layout: Option<BindGroupLayoutDescriptor>,
126 shader: Option<Handle<Shader>>,
128}
129
130#[derive(Resource)]
133pub struct SparseBufferUpdateBindGroups {
134 bind_groups: WeakKeyHashMap<Weak<SparseBufferId>, SparseBufferUpdateBindGroup>,
139 pipeline_id: Option<CachedComputePipelineId>,
141}
142
143pub struct SparseBufferUpdateBindGroup {
145 bind_group: BindGroup,
147}
148
149#[derive(Resource, Default, Deref, DerefMut)]
152pub struct SparseBufferUpdateJobs(pub Vec<SparseBufferUpdateJob>);
153
154pub struct SparseBufferUpdateJob {
156 sparse_buffer_handle: SparseBufferHandle,
158 updated_element_count: u32,
160 element_word_size: u32,
162}
163
164impl SparseBufferUpdateJob {
165 fn words_to_update(&self) -> u32 {
167 self.updated_element_count * self.element_word_size
168 }
169
170 fn workgroup_count(&self) -> u32 {
172 self.words_to_update()
173 .div_ceil(SPARSE_BUFFER_UPDATE_WORKGROUP_SIZE)
174 }
175}
176
177#[derive(Clone, Copy, Default, ShaderType, Pod, Zeroable)]
179#[repr(C)]
180struct GpuSparseBufferUpdateMetadata {
181 element_size: u32,
183 updated_element_count: u32,
185}
186
187pub fn update_sparse_buffers(
195 sparse_buffer_update_jobs: Res<SparseBufferUpdateJobs>,
196 sparse_buffer_update_bind_groups: Res<SparseBufferUpdateBindGroups>,
197 pipeline_cache: Res<PipelineCache>,
198 mut render_context: RenderContext,
199) {
200 if sparse_buffer_update_jobs.is_empty() {
202 return;
203 }
204
205 let Some(pipeline_id) = sparse_buffer_update_bind_groups.pipeline_id else {
206 return;
207 };
208 let Some(compute_pipeline) = pipeline_cache.get_compute_pipeline(pipeline_id) else {
209 return;
210 };
211
212 let diagnostics = render_context.diagnostic_recorder();
213 let diagnostics = diagnostics.as_deref();
214 let command_encoder = render_context.command_encoder();
215
216 let mut sparse_buffer_update_pass =
217 command_encoder.begin_compute_pass(&ComputePassDescriptor {
218 label: Some("sparse buffer updates"),
219 timestamp_writes: None,
220 });
221 sparse_buffer_update_pass.set_pipeline(compute_pipeline);
222 let time_span = diagnostics.time_span(&mut sparse_buffer_update_pass, "sparse buffer updates");
223
224 for sparse_buffer_update_job in sparse_buffer_update_jobs.iter() {
226 let Some(sparse_buffer_update_bind_group) = sparse_buffer_update_bind_groups
227 .bind_groups
228 .get(&sparse_buffer_update_job.sparse_buffer_handle)
229 else {
230 continue;
231 };
232
233 sparse_buffer_update_pass.set_bind_group(
234 0,
235 &sparse_buffer_update_bind_group.bind_group,
236 &[],
237 );
238 sparse_buffer_update_pass.dispatch_workgroups(
239 sparse_buffer_update_job.workgroup_count(),
240 1,
241 1,
242 );
243 }
244
245 time_span.end(&mut sparse_buffer_update_pass);
246}
247
248fn clear_sparse_buffer_jobs(mut sparse_buffer_update_jobs: ResMut<SparseBufferUpdateJobs>) {
251 sparse_buffer_update_jobs.clear();
252}
253
254pub fn init_sparse_buffer_update_pipelines(
255 mut commands: Commands,
256 asset_server: Res<AssetServer>,
257 render_device: Res<RenderDevice>,
258) {
259 let limit = render_device.limits().max_storage_buffers_per_shader_stage;
260 if limit < 3 {
261 info!(
262 "Sparse buffer updates disabled. RenderDevice lacks support: max_storage_buffers_per_shader_stage ({}) < 3.",
263 limit
264 );
265 commands.insert_resource(SparseBufferUpdatePipelines {
266 bind_group_layout: None,
267 shader: None,
268 });
269 return;
270 }
271
272 let bind_group_layout = BindGroupLayoutDescriptor::new(
273 "sparse buffer update bind group layout",
274 &BindGroupLayoutEntries::sequential(
275 ShaderStages::COMPUTE,
276 (
277 storage_buffer::<u32>(false),
279 storage_buffer_read_only::<u32>(false),
281 storage_buffer_read_only::<u32>(false),
283 uniform_buffer::<GpuSparseBufferUpdateMetadata>(false),
286 ),
287 ),
288 );
289
290 commands.insert_resource(SparseBufferUpdatePipelines {
291 bind_group_layout: Some(bind_group_layout),
292 shader: Some(load_embedded_asset!(
293 asset_server.as_ref(),
294 "sparse_buffer_update.wesl"
295 )),
296 });
297}
298
299impl SpecializedComputePipeline for SparseBufferUpdatePipelines {
300 type Key = Handle<Shader>;
301
302 fn specialize(&self, key: Self::Key) -> ComputePipelineDescriptor {
303 ComputePipelineDescriptor {
304 label: Some("sparse buffer update pipeline".into()),
305 layout: self.bind_group_layout.clone().into_iter().collect(),
306 shader: key,
307 shader_defs: vec![],
308 ..ComputePipelineDescriptor::default()
309 }
310 }
311}
312
313struct SparseBufferStagingBuffers {
317 source_data: RawBufferVec<u32>,
319
320 indices: RawBufferVec<u32>,
325
326 element_word_size: u32,
328}
329
330impl SparseBufferStagingBuffers {
331 fn new(label: &str, element_word_size: u32) -> SparseBufferStagingBuffers {
333 let mut source_data_buffer =
334 RawBufferVec::new(BufferUsages::COPY_DST | BufferUsages::STORAGE);
335 source_data_buffer.set_label(Some(&*format!("{} staging buffer", label)));
336
337 let mut indices_buffer = RawBufferVec::new(BufferUsages::COPY_DST | BufferUsages::STORAGE);
338 indices_buffer.set_label(Some(&*format!("{} index buffer", label)));
339
340 SparseBufferStagingBuffers {
341 source_data: source_data_buffer,
342 indices: indices_buffer,
343 element_word_size,
344 }
345 }
346
347 fn updated_element_count(&self) -> u32 {
349 (self.source_data.len() / self.element_word_size as usize) as u32
350 }
351
352 fn write_buffers(
358 &mut self,
359 metadata_uniform: &mut UniformBuffer<GpuSparseBufferUpdateMetadata>,
360 render_device: &RenderDevice,
361 render_queue: &RenderQueue,
362 ) {
363 metadata_uniform.get_mut().updated_element_count = self.updated_element_count();
364 metadata_uniform.write_buffer(render_device, render_queue);
365
366 self.source_data.write_buffer(render_device, render_queue);
367 self.indices.write_buffer(render_device, render_queue);
368 }
369
370 fn should_perform_full_reupload(
373 &self,
374 changed_element_count: u32,
375 buffer_length: usize,
376 ) -> bool {
377 let total_changed_word_count = changed_element_count * self.element_word_size;
385 if total_changed_word_count > MAX_WORKGROUPS * SPARSE_BUFFER_UPDATE_WORKGROUP_SIZE {
386 return true;
387 }
388
389 let sparse_upload_fraction = changed_element_count as f64 / buffer_length as f64;
392 let should_reupload = sparse_upload_fraction > SPARSE_UPLOAD_THRESHOLD;
393
394 debug!(
395 "Sparse buffer changed {}/{} elements ({:.3}, threshold {:.3}): performing {} upload",
396 changed_element_count,
397 buffer_length,
398 sparse_upload_fraction,
399 SPARSE_UPLOAD_THRESHOLD,
400 if should_reupload { "full" } else { "sparse" }
401 );
402
403 should_reupload
404 }
405}
406
407pub struct AtomicSparseBufferVec<T>
426where
427 T: AtomicPod,
428{
429 handle: SparseBufferHandle,
431 values: Vec<T::Blob>,
436 data_buffer: Option<Buffer>,
438 staging_buffers: SparseBufferStagingBuffers,
441 metadata_uniform: UniformBuffer<GpuSparseBufferUpdateMetadata>,
444 capacity: usize,
446 buffer_usages: BufferUsages,
448 label: Arc<str>,
450 summary: Vec<AtomicU64>,
458 dirty_bits: Vec<AtomicU64>,
467 needs_full_reupload: bool,
471 sparse_update_scheduled: bool,
473}
474
475impl<T> AtomicSparseBufferVec<T>
476where
477 T: AtomicPod,
478{
479 pub fn new(buffer_usages: BufferUsages, label: Arc<str>) -> Self {
486 debug_assert_eq!(size_of::<T>() % 4, 0);
488 let element_word_size = size_of::<T>() / 4;
489
490 let id = Arc::new(SparseBufferId(
492 NEXT_SPARSE_BUFFER_ID.fetch_add(1, Ordering::Relaxed),
493 ));
494
495 Self {
496 handle: id,
497 values: vec![],
498 data_buffer: None,
499 staging_buffers: SparseBufferStagingBuffers::new(&label, element_word_size as u32),
500 metadata_uniform: UniformBuffer::from(GpuSparseBufferUpdateMetadata::new::<T>()),
501 capacity: 0,
502 buffer_usages: buffer_usages | BufferUsages::COPY_DST,
503 label,
504 summary: vec![],
505 dirty_bits: vec![],
506 needs_full_reupload: false,
507 sparse_update_scheduled: false,
508 }
509 }
510
511 pub fn len(&self) -> u32 {
513 self.values.len() as u32
514 }
515
516 pub fn is_empty(&self) -> bool {
518 self.values.is_empty()
519 }
520
521 pub fn buffer(&self) -> Option<&Buffer> {
523 self.data_buffer.as_ref()
524 }
525
526 pub fn clear(&mut self) {
528 self.values.clear();
529 self.summary.clear();
530 self.dirty_bits.clear();
531 }
532
533 pub fn get(&self, index: u32) -> T {
535 T::read_from_blob(&self.values[index as usize])
536 }
537
538 pub fn set(&self, index: u32, value: T) {
550 value.write_to_blob(&self.values[index as usize]);
551 self.note_changed_index(index);
552 }
553
554 pub fn push(&mut self, value: T) -> u32 {
556 let index = self.values.len() as u32;
557 self.values.push(T::Blob::default());
558 value.write_to_blob(&self.values[index as usize]);
559
560 let dirty_word_index = (index / BITS_PER_WORD) as usize;
561 let summary_word_index = dirty_word_index / BITS_PER_WORD as usize;
562 while self.summary.len() < summary_word_index + 1 {
563 self.summary.push(AtomicU64::default());
564 }
565 while self.dirty_bits.len() < dirty_word_index + 1 {
566 self.dirty_bits.push(AtomicU64::default());
567 }
568
569 self.note_changed_index(index);
570 index
571 }
572
573 fn note_changed_index(&self, index: u32) {
576 note_changed_index(index, &self.summary, &self.dirty_bits);
577 }
578
579 pub fn reserve(&mut self, new_capacity: usize, render_device: &RenderDevice) {
582 reserve(
583 new_capacity,
584 &mut self.capacity,
585 &self.label,
586 &mut self.data_buffer,
587 self.buffer_usages,
588 &mut self.needs_full_reupload,
589 size_of::<T::Blob>(),
590 render_device,
591 );
592 }
593
594 pub fn grow(&mut self, new_len: u32) {
601 let old_len = self.values.len() as u32;
602 if old_len >= new_len {
603 return;
604 }
605
606 self.values.reserve(new_len as usize - old_len as usize);
607 self.values.resize_with(new_len as usize, T::Blob::default);
608
609 set_dirty_bits_for_vector_growth(old_len, new_len, &mut self.summary, &mut self.dirty_bits);
610 }
611
612 pub fn write_buffers(&mut self, render_device: &RenderDevice, render_queue: &RenderQueue) {
615 if self.values.is_empty() {
616 return;
617 }
618
619 let good_size = calculate_allocation_size(self.values.len());
622 self.reserve(good_size, render_device);
623
624 if self.should_perform_full_reupload(render_device) {
625 self.write_entire_buffer(render_queue);
626 } else {
627 self.prepare_sparse_upload(render_device, render_queue);
628 }
629 }
630
631 fn should_perform_full_reupload(&self, render_device: &RenderDevice) -> bool {
635 if self.needs_full_reupload
636 || render_device.limits().max_storage_buffers_per_shader_stage < 3
637 {
638 return true;
639 }
640
641 let changed_element_count = count_dirty_elements(&self.summary, &self.dirty_bits);
642 self.staging_buffers
643 .should_perform_full_reupload(changed_element_count, self.values.len())
644 }
645
646 fn write_entire_buffer(&mut self, render_queue: &RenderQueue) {
652 let Some(ref mut data_buffer) = self.data_buffer else {
653 error!("Dirty sparse buffer should have created a data buffer by now");
654 return;
655 };
656
657 unsafe {
661 render_queue.write_buffer(
662 data_buffer,
663 0,
664 slice::from_raw_parts(
665 self.values.as_ptr().cast::<u8>(),
666 self.values.len() * size_of::<T::Blob>(),
667 ),
668 );
669 }
670
671 for atomic_summary_word in self.summary.iter() {
673 atomic_summary_word.store(0, Ordering::Relaxed);
674 }
675 for atomic_dirty_word in self.dirty_bits.iter() {
676 atomic_dirty_word.store(0, Ordering::Relaxed);
677 }
678 self.sparse_update_scheduled = false;
679
680 self.needs_full_reupload = false;
682 }
683
684 fn prepare_sparse_upload(&mut self, render_device: &RenderDevice, render_queue: &RenderQueue) {
686 for (summary_word_index, atomic_summary_word) in self.summary.iter().enumerate() {
689 let summary_word = atomic_summary_word.load(Ordering::Relaxed);
690 for summary_bit_offset in BitIter::new(summary_word) {
691 let dirty_word_index =
692 summary_word_index * BITS_PER_WORD as usize + summary_bit_offset as usize;
693
694 let atomic_dirty_word = &self.dirty_bits[dirty_word_index];
696 let dirty_word = atomic_dirty_word.load(Ordering::Relaxed);
697 for dirty_bit_offset in BitIter::new(dirty_word) {
698 let element_index =
699 dirty_word_index * BITS_PER_WORD as usize + dirty_bit_offset as usize;
700
701 let Some(blob) = self.values.get(element_index) else {
702 continue;
703 };
704
705 self.staging_buffers.indices.push(element_index as u32);
708
709 let value = T::read_from_blob(blob);
711 self.staging_buffers
712 .source_data
713 .extend(bytemuck::cast_slice(&[value]).iter().copied());
714
715 debug_assert_eq!(
717 self.staging_buffers.source_data.len()
718 % self.staging_buffers.element_word_size as usize,
719 0
720 );
721 }
722
723 atomic_dirty_word.store(0, Ordering::Relaxed);
725 }
726
727 atomic_summary_word.store(0, Ordering::Relaxed);
729 }
730
731 self.sparse_update_scheduled = !self.staging_buffers.source_data.is_empty();
733 if self.sparse_update_scheduled {
734 self.staging_buffers.write_buffers(
735 &mut self.metadata_uniform,
736 render_device,
737 render_queue,
738 );
739 }
740 }
741
742 pub fn prepare_to_populate_buffers(
746 &mut self,
747 render_device: &RenderDevice,
748 pipeline_cache: &PipelineCache,
749 sparse_buffer_update_jobs: &mut SparseBufferUpdateJobs,
750 sparse_buffer_update_bind_groups: &mut SparseBufferUpdateBindGroups,
751 sparse_buffer_update_pipelines: &SparseBufferUpdatePipelines,
752 ) {
753 let mut retry_with_full_reupload = false;
757
758 if self.sparse_update_scheduled {
759 let scheduled = match (&self.data_buffer, self.metadata_uniform.buffer()) {
760 (Some(data_buffer), Some(metadata_buffer)) => prepare_to_populate_buffers(
761 self.handle.clone(),
762 &self.label,
763 data_buffer,
764 &mut self.staging_buffers,
765 metadata_buffer,
766 render_device,
767 pipeline_cache,
768 sparse_buffer_update_jobs,
769 sparse_buffer_update_bind_groups,
770 sparse_buffer_update_pipelines,
771 ),
772 _ => {
773 error!("Buffers should have been created by now");
774 false
775 }
776 };
777
778 retry_with_full_reupload = !scheduled;
779 }
780
781 self.staging_buffers.source_data.clear();
784 self.staging_buffers.indices.clear();
785
786 self.needs_full_reupload |= retry_with_full_reupload;
789 self.sparse_update_scheduled = false;
790 }
791}
792
793impl<T> AtomicSparseBufferVec<T>
794where
795 T: AtomicPod + PartialEq,
796{
797 pub fn grow_and_set(&mut self, index: u32, value: T) {
807 if self.len() > index {
808 if self.get(index) == value {
809 return;
810 }
811 } else {
812 self.grow(index + 1);
813 }
814 self.set(index, value);
815 }
816
817 pub fn set_if_changed(&self, index: u32, value: T) {
825 debug_assert!(
826 index < self.len(),
827 "buffer was not grown past index {index}"
828 );
829 if self.get(index) != value {
830 self.set(index, value);
831 }
832 }
833}
834
835pub fn init_sparse_buffer_update_bind_groups(
836 mut commands: Commands,
837 mut specialized_sparse_buffer_update_pipelines: ResMut<
838 SpecializedComputePipelines<SparseBufferUpdatePipelines>,
839 >,
840 pipeline_cache: Res<PipelineCache>,
841 sparse_buffer_update_pipelines: Res<SparseBufferUpdatePipelines>,
842) {
843 let pipeline_id = sparse_buffer_update_pipelines.shader.clone().map(|shader| {
844 specialized_sparse_buffer_update_pipelines.specialize(
845 &pipeline_cache,
846 &sparse_buffer_update_pipelines,
847 shader,
848 )
849 });
850
851 commands.insert_resource(SparseBufferUpdateBindGroups {
852 bind_groups: WeakKeyHashMap::default(),
853 pipeline_id,
854 });
855}
856
857fn set_dirty_bits_for_vector_growth(
866 old_len: u32,
867 new_len: u32,
868 summary: &mut Vec<AtomicU64>,
869 dirty_bits: &mut Vec<AtomicU64>,
870) {
871 debug_assert!(new_len >= old_len);
872 if new_len == old_len {
873 return;
874 }
875
876 if old_len > 0 {
877 let old_final_dirty_word_index = (old_len - 1) / BITS_PER_WORD;
880 let old_final_dirty_bit_offset = (old_len - 1) % BITS_PER_WORD;
881 if old_final_dirty_bit_offset < BITS_PER_WORD - 1
882 && let Some(ref mut old_final_atomic_dirty_word) =
883 dirty_bits.get_mut(old_final_dirty_word_index as usize)
884 {
885 *old_final_atomic_dirty_word.get_mut() |=
888 !((1u64 << (old_final_dirty_bit_offset + 1)).wrapping_sub(1));
889 }
890
891 let old_final_summary_word_index = old_final_dirty_word_index / BITS_PER_WORD;
895 let mut old_final_summary_bit_offset = old_final_dirty_word_index % BITS_PER_WORD;
896 if old_final_dirty_bit_offset == BITS_PER_WORD - 1 {
900 old_final_summary_bit_offset += 1;
901 }
902 if old_final_summary_bit_offset < BITS_PER_WORD
903 && let Some(ref mut old_final_atomic_summary_word) =
904 summary.get_mut(old_final_summary_word_index as usize)
905 {
906 *old_final_atomic_summary_word.get_mut() |=
909 !((1u64 << old_final_summary_bit_offset).wrapping_sub(1));
910 }
911 }
912
913 let new_dirty_word_count = (new_len as usize).div_ceil(BITS_PER_WORD as usize);
915 let new_summary_word_count = new_dirty_word_count.div_ceil(BITS_PER_WORD as usize);
916 summary.resize_with(new_summary_word_count, || AtomicU64::new(u64::MAX));
917 dirty_bits.resize_with(new_dirty_word_count, || AtomicU64::new(u64::MAX));
918
919 let last_dirty_bit_offset = new_len % BITS_PER_WORD;
921 if last_dirty_bit_offset != 0 {
922 let mut final_dirty_word = dirty_bits[new_dirty_word_count - 1].load(Ordering::Relaxed);
923 final_dirty_word &= (1u64 << last_dirty_bit_offset) - 1;
924 dirty_bits[new_dirty_word_count - 1].store(final_dirty_word, Ordering::Relaxed);
925 }
926
927 let last_summary_bit_offset = new_dirty_word_count % BITS_PER_WORD as usize;
929 if last_summary_bit_offset != 0 {
930 let mut final_summary_word = summary[new_summary_word_count - 1].load(Ordering::Relaxed);
931 final_summary_word &= (1u64 << last_summary_bit_offset) - 1;
932 summary[new_summary_word_count - 1].store(final_summary_word, Ordering::Relaxed);
933 }
934}
935
936fn note_changed_index(index: u32, summary: &[AtomicU64], dirty_bits: &[AtomicU64]) {
942 let dirty_word_index = index / BITS_PER_WORD;
943 let (summary_word_index, summary_bit_offset) = (
944 dirty_word_index / BITS_PER_WORD,
945 dirty_word_index % BITS_PER_WORD,
946 );
947 summary[summary_word_index as usize].fetch_or(1 << summary_bit_offset, Ordering::Relaxed);
948 let (element_word, element_in_word) = (index / BITS_PER_WORD, index % BITS_PER_WORD);
949 dirty_bits[element_word as usize].fetch_or(1 << element_in_word, Ordering::Relaxed);
950}
951
952fn count_dirty_elements(summary: &[AtomicU64], dirty_bits: &[AtomicU64]) -> u32 {
955 let mut changed_element_count = 0u32;
956 for (summary_word_index, summary_word) in summary.iter().enumerate() {
957 for summary_bit_offset in BitIter::new(summary_word.load(Ordering::Relaxed)) {
958 let dirty_word_index =
959 summary_word_index * BITS_PER_WORD as usize + summary_bit_offset as usize;
960 let dirty_word = dirty_bits[dirty_word_index].load(Ordering::Relaxed);
961 changed_element_count += dirty_word.count_ones();
962 }
963 }
964
965 changed_element_count
966}
967
968fn prepare_to_populate_buffers(
978 sparse_buffer_handle: SparseBufferHandle,
979 label: &Arc<str>,
980 data_buffer: &Buffer,
981 staging_buffers: &mut SparseBufferStagingBuffers,
982 metadata_buffer: &Buffer,
983 render_device: &RenderDevice,
984 pipeline_cache: &PipelineCache,
985 sparse_buffer_update_jobs: &mut SparseBufferUpdateJobs,
986 sparse_buffer_update_bind_groups: &mut SparseBufferUpdateBindGroups,
987 sparse_buffer_update_pipelines: &SparseBufferUpdatePipelines,
988) -> bool {
989 let (Some(source_data_staging_buffer), Some(indices_staging_buffer)) = (
990 staging_buffers.source_data.buffer(),
991 staging_buffers.indices.buffer(),
992 ) else {
993 error!("Staging buffers should have been created by now");
994 return false;
995 };
996
997 let Some(bind_group_layout) = &sparse_buffer_update_pipelines.bind_group_layout else {
998 return false;
999 };
1000
1001 let Some(pipeline_id) = sparse_buffer_update_bind_groups.pipeline_id else {
1004 return false;
1005 };
1006 if pipeline_cache.get_compute_pipeline(pipeline_id).is_none() {
1007 return false;
1008 }
1009
1010 sparse_buffer_update_jobs.push(SparseBufferUpdateJob {
1012 sparse_buffer_handle: sparse_buffer_handle.clone(),
1013 updated_element_count: staging_buffers.updated_element_count(),
1014 element_word_size: staging_buffers.element_word_size,
1015 });
1016
1017 let bind_group = render_device.create_bind_group(
1019 Some(&*format!("{} bind group", label)),
1020 &pipeline_cache.get_bind_group_layout(bind_group_layout),
1021 &BindGroupEntries::sequential((
1022 data_buffer.as_entire_binding(),
1024 source_data_staging_buffer.as_entire_binding(),
1026 indices_staging_buffer.as_entire_binding(),
1028 metadata_buffer.as_entire_binding(),
1031 )),
1032 );
1033 sparse_buffer_update_bind_groups.bind_groups.insert(
1034 sparse_buffer_handle,
1035 SparseBufferUpdateBindGroup { bind_group },
1036 );
1037
1038 true
1039}
1040
1041fn reserve(
1047 new_capacity: usize,
1048 capacity: &mut usize,
1049 label: &str,
1050 data_buffer: &mut Option<Buffer>,
1051 buffer_usages: BufferUsages,
1052 needs_full_reupload: &mut bool,
1053 element_size: usize,
1054 render_device: &RenderDevice,
1055) {
1056 if new_capacity == 0 || new_capacity <= *capacity {
1058 return;
1059 }
1060
1061 *capacity = new_capacity;
1062 *data_buffer = Some(render_device.create_buffer(&BufferDescriptor {
1063 label: Some(label),
1064 size: element_size as u64 * new_capacity as u64,
1065 usage: buffer_usages,
1066 mapped_at_creation: false,
1067 }));
1068
1069 *needs_full_reupload = true;
1071}
1072
1073impl GpuSparseBufferUpdateMetadata {
1074 fn new<T>() -> GpuSparseBufferUpdateMetadata {
1076 assert_eq!(size_of::<T>() % 4, 0);
1077 GpuSparseBufferUpdateMetadata {
1078 element_size: (size_of::<T>() / 4) as u32,
1079 updated_element_count: 0,
1080 }
1081 }
1082}
1083
1084struct BitIter(u64);
1087
1088impl BitIter {
1089 fn new(bits: u64) -> BitIter {
1090 BitIter(bits)
1091 }
1092}
1093
1094impl Iterator for BitIter {
1095 type Item = u32;
1096
1097 fn next(&mut self) -> Option<Self::Item> {
1098 let trailing_zeros = self.0.trailing_zeros();
1099 if trailing_zeros == 64 {
1100 return None;
1101 }
1102 self.0 &= !(1 << trailing_zeros);
1103 Some(trailing_zeros)
1104 }
1105}
1106
1107fn calculate_allocation_size(length: usize) -> usize {
1110 let exponent = (length as f64).log(REALLOCATION_FACTOR).ceil();
1111 let size = REALLOCATION_FACTOR.powf(exponent) as usize;
1112 size.next_multiple_of(REALLOCATION_SIZE_MULTIPLE)
1113}
1114
1115#[cfg(test)]
1116mod tests {
1117 use super::{BitIter, BITS_PER_WORD};
1118 use core::{
1119 iter,
1120 sync::atomic::{AtomicU64, Ordering},
1121 };
1122 use proptest::prelude::proptest;
1123
1124 proptest! {
1125 #[test]
1127 fn bit_iter(bits: u64) {
1128 let bits_reference: Vec<_> = (0u32..64u32).filter(|bit_pos| {
1129 (bits & (1 << bit_pos)) != 0
1130 }).collect();
1131 let bits_iter_results: Vec<_> = BitIter::new(bits).collect();
1132 assert_eq!(bits_iter_results, bits_reference);
1133 }
1134
1135 #[test]
1137 fn set_dirty_bits_for_vector_growth(
1138 old_len in 0u32..16384u32,
1139 new_element_count in 0u32..16384u32,
1140 start_dirty: bool
1141 ) {
1142 let new_len = old_len + new_element_count;
1144 let mut dirty_bits: Vec<_> = iter::repeat_with(|| {
1145 AtomicU64::new(0)
1146 }).take(old_len.div_ceil(BITS_PER_WORD) as usize).collect();
1147 if start_dirty {
1148 for bit_index in 0..old_len {
1149 let word_index = bit_index as usize / 64;
1150 dirty_bits[word_index].fetch_or(1 << (bit_index % 64), Ordering::Relaxed);
1151 }
1152 }
1153
1154 let mut summary: Vec<_> = iter::repeat_with(|| {
1156 AtomicU64::new(0)
1157 }).take(dirty_bits.len().div_ceil(BITS_PER_WORD as usize)).collect();
1158 for (word_index, word) in dirty_bits.iter().enumerate() {
1159 if word.load(Ordering::Relaxed) != 0 {
1160 summary[word_index / 64].fetch_or(1 << (word_index % 64), Ordering::Relaxed);
1161 }
1162 }
1163
1164 super::set_dirty_bits_for_vector_growth(
1165 old_len,
1166 new_len,
1167 &mut summary,
1168 &mut dirty_bits
1169 );
1170
1171 for element_index in 0..old_len {
1174 check_element_dirty(element_index, &dirty_bits, start_dirty);
1175 }
1176 for element_index in old_len..new_len {
1178 check_element_dirty(element_index, &dirty_bits, true);
1179 }
1180 for element_index in (new_len..).take_while(|element_index| {
1182 element_index % BITS_PER_WORD != 0
1183 }) {
1184 check_element_dirty(element_index, &dirty_bits, false);
1185 }
1186
1187 for (dirty_word_index, atomic_dirty_word) in dirty_bits.iter().enumerate() {
1191 let element_start = dirty_word_index * BITS_PER_WORD as usize;
1193 let element_end =
1194 ((dirty_word_index + 1) * BITS_PER_WORD as usize).min(new_len as usize);
1195 assert!(element_start <= element_end);
1196
1197 let dirty_word = atomic_dirty_word.load(Ordering::Relaxed);
1199 let block_is_dirty = (element_start..element_end).any(|element_index| {
1200 (dirty_word & (1 << (element_index % (BITS_PER_WORD as usize)))) != 0
1201 });
1202
1203 check_block_dirty(dirty_word_index as u32, &summary, block_is_dirty);
1205 }
1206
1207 if !summary.is_empty() {
1210 let last_summary_word_index = summary.len();
1211 let last_padding_block_index = last_summary_word_index * BITS_PER_WORD as usize;
1212 let last_dirty_word_index = (new_len as usize - 1) / BITS_PER_WORD as usize;
1213 for padding_block_index in (last_dirty_word_index + 1)..last_padding_block_index {
1214 check_block_dirty(padding_block_index as u32, &summary, false);
1215 }
1216 }
1217
1218 fn check_element_dirty(
1221 element_index: u32,
1222 dirty_bits: &[AtomicU64],
1223 expect_dirty: bool
1224 ) {
1225 let expected = if expect_dirty { 1 } else { 0 };
1226
1227 let dirty_word_index = element_index / BITS_PER_WORD;
1228 let dirty_bit_offset = element_index % BITS_PER_WORD;
1229 let dirty_word = dirty_bits[dirty_word_index as usize].load(Ordering::Relaxed);
1230 assert_eq!((dirty_word >> dirty_bit_offset) & 1, expected);
1231 }
1232
1233 fn check_block_dirty(block_index: u32, summary: &[AtomicU64], expect_dirty: bool) {
1239 let expected = if expect_dirty { 1 } else { 0 };
1240
1241 let summary_word_index = block_index / BITS_PER_WORD;
1242 let summary_bit_offset = block_index % BITS_PER_WORD;
1243 let summary_word = summary[summary_word_index as usize].load(Ordering::Relaxed);
1244 assert_eq!((summary_word >> summary_bit_offset) & 1, expected);
1245 }
1246 }
1247
1248 #[test]
1255 fn dirty_element_count(dirty_flags: Vec<bool>) {
1256 let dirty_word_count = dirty_flags.len().div_ceil(BITS_PER_WORD as usize);
1257 let summary_word_count = dirty_word_count.div_ceil(BITS_PER_WORD as usize);
1258
1259 let dirty_bits: Vec<_> = (0..dirty_word_count).map(|_| AtomicU64::new(0)).collect();
1260 let summary: Vec<_> = (0..summary_word_count).map(|_| AtomicU64::new(0)).collect();
1261
1262 let mut true_dirty_element_count = 0;
1263 for (element_index, _) in dirty_flags.iter().enumerate().filter(|(_, element)| **element) {
1264 super::note_changed_index(element_index as u32, &summary, &dirty_bits);
1265 true_dirty_element_count += 1;
1266 }
1267
1268 let calculated_dirty_element_count = super::count_dirty_elements(
1269 &summary,
1270 &dirty_bits
1271 );
1272 assert_eq!(calculated_dirty_element_count, true_dirty_element_count);
1273 }
1274 }
1275}