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 <noreply@anthropic.com>
This commit is contained in:
parent
d3986e9cba
commit
522b123d82
1 changed files with 32 additions and 11 deletions
|
|
@ -25,7 +25,34 @@
|
||||||
|
|
||||||
use crate::scene::wire_model::WireModel;
|
use crate::scene::wire_model::WireModel;
|
||||||
use iced::wgpu;
|
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 ─────────────────────────────────────────────────────────
|
// ── Vertex layout ─────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|
@ -222,11 +249,8 @@ impl WireGpu {
|
||||||
.chunks(MAX_VERTS)
|
.chunks(MAX_VERTS)
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, chunk)| {
|
.map(|(i, chunk)| {
|
||||||
let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
let label = format!("wire.batch.vbuf.{i}");
|
||||||
label: Some(&format!("wire.batch.vbuf.{i}")),
|
let vertex_buffer = vertex_buffer_mapped(device, &label, chunk);
|
||||||
contents: bytemuck::cast_slice(chunk),
|
|
||||||
usage: wgpu::BufferUsages::VERTEX,
|
|
||||||
});
|
|
||||||
Self {
|
Self {
|
||||||
vertex_buffer,
|
vertex_buffer,
|
||||||
vertex_count: chunk.len() as u32,
|
vertex_count: chunk.len() as u32,
|
||||||
|
|
@ -323,11 +347,8 @@ impl WireGpu {
|
||||||
vertices.push(make(0.0, 1.0));
|
vertices.push(make(0.0, 1.0));
|
||||||
}
|
}
|
||||||
|
|
||||||
let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
let label = format!("wire.vbuf.{}", wire.name);
|
||||||
label: Some(&format!("wire.vbuf.{}", wire.name)),
|
let vertex_buffer = vertex_buffer_mapped(device, &label, &vertices);
|
||||||
contents: bytemuck::cast_slice(&vertices),
|
|
||||||
usage: wgpu::BufferUsages::VERTEX,
|
|
||||||
});
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
vertex_buffer,
|
vertex_buffer,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue