perf: speed large drawing workflows

This commit is contained in:
Hakan Seven 2026-07-25 09:05:52 +03:00
commit 39449ec684
10 changed files with 700 additions and 195 deletions

View file

@ -163,6 +163,14 @@ pub struct Pipeline {
/// both patch incrementally. Shares `wire_arena_id`.
#[cfg(not(target_arch = "wasm32"))]
pub(crate) wire_arena_mesh: Option<wire_arena::WireArena>,
/// Chunked resident buffers for whichever arena partition exceeded one
/// GPU buffer. `Some(false)` = regular wires, `Some(true)` = mesh edges.
#[cfg(not(target_arch = "wasm32"))]
pub(crate) wire_arena_fallback: std::sync::Arc<Vec<WireGpu>>,
#[cfg(not(target_arch = "wasm32"))]
pub(crate) wire_arena_fallback_kind: Option<bool>,
#[cfg(not(target_arch = "wasm32"))]
pub(crate) wire_arena_fallback_handles: rustc_hash::FxHashSet<acadrust::Handle>,
/// The Model content id both arenas currently mirror (`u64::MAX` = none).
#[cfg(not(target_arch = "wasm32"))]
pub(crate) wire_arena_id: u64,
@ -1455,6 +1463,12 @@ impl Pipeline {
#[cfg(not(target_arch = "wasm32"))]
wire_arena_mesh: None,
#[cfg(not(target_arch = "wasm32"))]
wire_arena_fallback: std::sync::Arc::new(Vec::new()),
#[cfg(not(target_arch = "wasm32"))]
wire_arena_fallback_kind: None,
#[cfg(not(target_arch = "wasm32"))]
wire_arena_fallback_handles: rustc_hash::FxHashSet::default(),
#[cfg(not(target_arch = "wasm32"))]
wire_arena_id: u64::MAX,
#[cfg(not(target_arch = "wasm32"))]
wire_cull_key: (u64::MAX, u64::MAX, 0, 0),

View file

@ -282,6 +282,7 @@ impl WirePipelineMode {
// ── GPU handle ────────────────────────────────────────────────────────────
#[derive(Clone)]
pub struct WireGpu {
pub instance_buffer: wgpu::Buffer,
/// First instance in a shared arena buffer. Standalone buffers start at 0.
@ -609,6 +610,55 @@ fn build_const_bind_group(
}
impl WireGpu {
/// Native-only equivalent of [`from_run`] for an already partitioned set
/// of borrowed wires. Used when one arena partition exceeds the 256 MB
/// buffer limit: the compatible partition stays patchable while only the
/// oversized side uses chunked resident buffers.
#[cfg(not(target_arch = "wasm32"))]
pub fn from_run_refs(
device: &wgpu::Device,
wires: &[&WireModel],
depth_map: &rustc_hash::FxHashMap<u64, [f32; 2]>,
mesh_edge: bool,
const_bgl: &wgpu::BindGroupLayout,
) -> Vec<Self> {
const MAX_INSTANCES: usize =
268_435_456 / std::mem::size_of::<WireInstance>();
use crate::par::prelude::*;
let per: Vec<(Vec<WireInstance>, WireConst)> = wires
.par_iter()
.enumerate()
.map(|(idx, &wire)| {
let depth = if mesh_edge {
0.0
} else {
wire_draw_depth(wire, depth_map)
};
emit_wire_native(wire, idx as u32, wire.color, depth)
})
.collect();
let mut instances: Vec<WireInstance> =
Vec::with_capacity(per.iter().map(|(items, _)| items.len()).sum());
let mut consts = Vec::with_capacity(per.len());
for (mut items, constant) in per {
instances.append(&mut items);
consts.push(constant);
}
if instances.is_empty() {
return Vec::new();
}
let bind_group = build_const_bind_group(device, const_bgl, &consts);
instances
.chunks(MAX_INSTANCES)
.map(|chunk| Self {
instance_buffer: instance_buffer_mapped(device, "wire.run.hybrid.ibuf", chunk),
first_instance: 0,
instance_count: chunk.len() as u32,
is_3d_mesh_edge: mesh_edge,
const_bind_group: Some(bind_group.clone()),
})
.collect()
}
/// Merge a run of WireModels that share scissor + mesh-edge state into one
/// (or, past the 256 MB GPU limit, a few) instance buffer(s), then stamp