From 4c0c7dd67ea9d45c1f7ae87aba7a6151f1429a74 Mon Sep 17 00:00:00 2001 From: CrazyRoka Date: Sat, 11 Apr 2026 16:38:41 +0100 Subject: [PATCH 1/3] Introduce buffer_vec_benches benchmark --- benches/Cargo.toml | 4 ++ benches/benches/bevy_render/buffer_vec.rs | 49 +++++++++++++++++++++++ benches/benches/bevy_render/main.rs | 4 +- 3 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 benches/benches/bevy_render/buffer_vec.rs diff --git a/benches/Cargo.toml b/benches/Cargo.toml index e03c699e24861..6f3ad7b9e241b 100644 --- a/benches/Cargo.toml +++ b/benches/Cargo.toml @@ -110,3 +110,7 @@ harness = false name = "tasks" path = "benches/bevy_tasks/main.rs" harness = false + +[features] +# Forces the wgpu instance to be initialized using the raw Vulkan HAL, enabling additional configuration +raw_vulkan_init = ["bevy_render/raw_vulkan_init"] diff --git a/benches/benches/bevy_render/buffer_vec.rs b/benches/benches/bevy_render/buffer_vec.rs new file mode 100644 index 0000000000000..6100386d598c8 --- /dev/null +++ b/benches/benches/bevy_render/buffer_vec.rs @@ -0,0 +1,49 @@ +use bevy_render::render_resource::{BufferUsages, PollType, RawBufferVec}; +use bevy_render::renderer::initialize_renderer; +use bevy_render::settings::{RenderResources, WgpuSettings}; +use bevy_tasks::block_on; +use core::hint::black_box; +use criterion::{criterion_group, Criterion}; +use std::time::{Duration, Instant}; + +pub fn buffer_vec_benches(c: &mut Criterion) { + let settings = WgpuSettings::default(); + let backends = settings.backends.expect("No backends found"); + + let RenderResources(device, queue, ..) = block_on(initialize_renderer( + backends, + None, // No window needed for buffer tests + &settings, + #[cfg(feature = "raw_vulkan_init")] + Default::default(), + )); + + c.bench_function("raw_buffer_vec_incremental_write", |b| { + b.iter_custom(|iters| { + let mut total = Duration::default(); + + for _ in 0..iters { + let start = Instant::now(); + + // Simulate incremental writes to the buffer vec, writing after each push + let mut vec = RawBufferVec::::new(BufferUsages::STORAGE); + for i in 0..5000 { + vec.push(i); + vec.write_buffer(&device, &queue); + } + total += Instant::now().duration_since(start); + + black_box(&vec); + // Cleanup allocated buffers to prevent memory bloat during the benchmark + queue.submit(None); + device + .poll(PollType::wait_indefinitely()) + .expect("Failed to poll device"); + } + + total + }); + }); +} + +criterion_group!(benches, buffer_vec_benches); diff --git a/benches/benches/bevy_render/main.rs b/benches/benches/bevy_render/main.rs index e335670222641..a75620fb5ddec 100644 --- a/benches/benches/bevy_render/main.rs +++ b/benches/benches/bevy_render/main.rs @@ -1,5 +1,6 @@ use criterion::criterion_main; +mod buffer_vec; mod compute_normals; mod render_layers; mod torus; @@ -7,5 +8,6 @@ mod torus; criterion_main!( render_layers::benches, compute_normals::benches, - torus::benches + torus::benches, + buffer_vec::benches ); From 2ee0acd0554a3ef6211733fb1e9b2eea7a18da7d Mon Sep 17 00:00:00 2001 From: CrazyRoka Date: Sat, 11 Apr 2026 17:59:59 +0100 Subject: [PATCH 2/3] Optimize buffer growth strategy This commit changes the buffer growth strategy to use a doubling strategy when increasing capacity. This reduces the number of reallocations and buffer creations, leading to better performance, especially when dealing with frequently resizing buffers. --- .../src/render_resource/buffer_vec.rs | 31 +++++++++++-------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/crates/bevy_render/src/render_resource/buffer_vec.rs b/crates/bevy_render/src/render_resource/buffer_vec.rs index ed9e1a39db3a3..318453a925bec 100644 --- a/crates/bevy_render/src/render_resource/buffer_vec.rs +++ b/crates/bevy_render/src/render_resource/buffer_vec.rs @@ -154,9 +154,11 @@ impl RawBufferVec { /// the `RawBufferVec` was created, the buffer on the [`RenderDevice`] /// is marked as [`BufferUsages::COPY_DST`](BufferUsages). pub fn reserve(&mut self, capacity: usize, device: &RenderDevice) { - let size = self.item_size * capacity; - if capacity > self.capacity || (self.changed && size > 0) { - self.capacity = capacity; + if capacity > self.capacity || (self.changed && capacity > 0) { + if capacity > self.capacity { + self.capacity = capacity.max(self.capacity * 2); + } + let size = self.item_size * self.capacity; self.buffer = Some(device.create_buffer(&wgpu::BufferDescriptor { label: make_buffer_label::(&self.label), size: size as BufferAddress, @@ -383,9 +385,11 @@ where /// the `AtomicRawBufferVec` was created, the buffer on the [`RenderDevice`] /// is marked as [`BufferUsages::COPY_DST`](BufferUsages). pub fn reserve(&mut self, capacity: usize, device: &RenderDevice) { - let size = size_of::() * capacity; - if capacity > self.capacity || (self.changed && size > 0) { - self.capacity = capacity; + if capacity > self.capacity || (self.changed && capacity > 0) { + if capacity > self.capacity { + self.capacity = capacity.max(self.capacity * 2); + } + let size = size_of::() * self.capacity; self.buffer = Some(device.create_buffer(&wgpu::BufferDescriptor { label: make_buffer_label::(&self.label), size: size as BufferAddress, @@ -609,8 +613,9 @@ where if capacity <= self.capacity && !self.label_changed { return; } - - self.capacity = capacity; + if capacity > self.capacity { + self.capacity = capacity.max(self.capacity * 2); + } let size = u64::from(T::min_size()) as usize * capacity; self.buffer = Some(device.create_buffer(&wgpu::BufferDescriptor { label: make_buffer_label::(&self.label), @@ -793,8 +798,9 @@ where if capacity <= self.capacity && !self.label_changed { return; } - - self.capacity = capacity; + if capacity > self.capacity { + self.capacity = capacity.max(self.capacity * 2); + } let size = self.item_size * capacity; self.buffer = Some(device.create_buffer(&wgpu::BufferDescriptor { label: make_buffer_label::(&self.label), @@ -883,9 +889,8 @@ where if capacity <= self.capacity { return; } - - let size = size_of::() * capacity; - self.capacity = capacity; + self.capacity = capacity.max(self.capacity * 2); + let size = size_of::() * self.capacity; self.buffer = Some(render_device.create_buffer(&wgpu::BufferDescriptor { label: Some(&self.label), size: size as u64, From 8b4cb26aeb68917c63bf714768bf65b954a4f0ea Mon Sep 17 00:00:00 2001 From: CrazyRoka Date: Sun, 12 Apr 2026 11:40:26 +0100 Subject: [PATCH 3/3] Add raw_vulkan_init feature to CI test --bench command --- tools/ci/src/commands/test.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/ci/src/commands/test.rs b/tools/ci/src/commands/test.rs index 46533012f8d29..b85a73353ebbb 100644 --- a/tools/ci/src/commands/test.rs +++ b/tools/ci/src/commands/test.rs @@ -32,7 +32,7 @@ impl Prepare for TestCommand { sh, // `--benches` runs each benchmark once in order to verify that they behave // correctly and do not panic. - "cargo test --workspace --benches {no_fail_fast...} {jobs...}" + "cargo test --workspace --benches --features raw_vulkan_init {no_fail_fast...} {jobs...}" ), "Please fix failing tests in output above.", )