perf(wire): upload vertex buffers via mapped_at_creation

Step 1 of the wire pipeline cold-open optimization plan. Replaces
`create_buffer_init` (intermediate staging-buffer memcpy) with a
`mapped_at_creation` buffer the build path writes into directly.
Cuts one alloc + one memcpy per wire buffer; on cold open with hundreds
of MB of wire data this shows up immediately as lower peak memory and
faster upload.

Guarded against size-0 buffers by allocating a single-vertex placeholder
when the wire is empty (the renderer already skips draws on vertex_count
below the per-segment minimum).

Co-Authored-By: Claude Opus 4.7 <[email protected]>
This commit is contained in:
HakanandClaude Opus 4.7 committed 2026-05-23 09:47:07 +03:00
1 parent d3986e9cba
commit 522b123d82
1 file changed
+32 -11
+32 -11
View File
@@ -25,7 +25,34 @@
use crate::scene::wire_model::WireModel;
use iced::wgpu;
use iced::wgpu::util::DeviceExt;
/// Allocate a VERTEX buffer with `mapped_at_creation` and write `data` directly
/// into the mapped slice. Skips the intermediate staging copy that
/// `create_buffer_init` performs and avoids holding a second `Vec` worth of
/// memory during upload — meaningful on cold open where wire buffers can run
/// into the hundreds of MB.
fn vertex_buffer_mapped(
device: &wgpu::Device,
label: &str,
data: &[WireVertex],
) -> wgpu::Buffer {
let bytes: &[u8] = bytemuck::cast_slice(data);
// wgpu rejects size-0 buffers; the renderer already guards `vertex_count`
// before issuing a draw, so a placeholder allocation is fine here.
let size = bytes.len().max(std::mem::size_of::<WireVertex>()) as u64;
let buf = device.create_buffer(&wgpu::BufferDescriptor {
label: Some(label),
size,
usage: wgpu::BufferUsages::VERTEX,
mapped_at_creation: true,
});
{
let mut view = buf.slice(..).get_mapped_range_mut();
view[..bytes.len()].copy_from_slice(bytes);
}
buf.unmap();
buf
}
// ── Vertex layout ─────────────────────────────────────────────────────────
@@ -222,11 +249,8 @@ impl WireGpu {
.chunks(MAX_VERTS)
.enumerate()
.map(|(i, chunk)| {
let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some(&format!("wire.batch.vbuf.{i}")),
contents: bytemuck::cast_slice(chunk),
usage: wgpu::BufferUsages::VERTEX,
});
let label = format!("wire.batch.vbuf.{i}");
let vertex_buffer = vertex_buffer_mapped(device, &label, chunk);
Self {
vertex_buffer,
vertex_count: chunk.len() as u32,
@@ -323,11 +347,8 @@ impl WireGpu {
vertices.push(make(0.0, 1.0));
}
let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some(&format!("wire.vbuf.{}", wire.name)),
contents: bytemuck::cast_slice(&vertices),
usage: wgpu::BufferUsages::VERTEX,
});
let label = format!("wire.vbuf.{}", wire.name);
let vertex_buffer = vertex_buffer_mapped(device, &label, &vertices);
Self {
vertex_buffer,