forked from 1b-agent/opencadstudio-upstream
fix(render): chunk Face3D fill buffers to the device limit (#358)
Face3DGpu batched every visible 3DFACE quad and PolyfaceMesh / PolygonMesh fill triangle into a single vertex buffer. A mesh-heavy DWG (e.g. a Navisworks import) blows past wgpu's default 256 MB max_buffer_size once enough layers are thawed; the resulting uncaptured validation error panics inside iced's main-thread redraw and aborts the whole process. Split the 3D and 2D fill streams into per-limit chunks on whole-triangle boundaries — the same scheme build_mesh_batch already uses (#203) — and register an on_uncaptured_error handler so any remaining GPU validation error degrades the frame instead of killing the session. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
5bd683cf4e
commit
a204237268
2 files changed
+64
-28
No files matched your search
@@ -73,18 +73,28 @@ impl Face3DVertex {
|
||||
|
||||
// ── GPU handle ─────────────────────────────────────────────────────────────
|
||||
|
||||
/// One vertex buffer + draw count. Fill data is split into as many chunks
|
||||
/// as the device's `max_buffer_size` requires: a mesh-heavy DWG (e.g. a
|
||||
/// Navisworks import) can hold tens of millions of fill triangles, and at
|
||||
/// 44 B/vertex a single batched buffer blows past the default 256 MB limit
|
||||
/// once enough layers are thawed — wgpu then raises an uncaptured
|
||||
/// validation error and aborts the process (#358). Same scheme as the
|
||||
/// solid-mesh batch chunking in `mesh_gpu.rs` (#203).
|
||||
pub struct Face3DChunk {
|
||||
pub vertex_buffer: wgpu::Buffer,
|
||||
pub vertex_count: u32,
|
||||
}
|
||||
|
||||
pub struct Face3DGpu {
|
||||
/// 3DFACE quads + PolyfaceMesh / PolygonMesh face triangles.
|
||||
/// HiddenLine routes this through the depth-only pipeline so the
|
||||
/// fragments occlude wires behind them without drawing visible
|
||||
/// pixels.
|
||||
pub vertex_buffer_3d: wgpu::Buffer,
|
||||
pub vertex_count_3d: u32,
|
||||
pub chunks_3d: Vec<Face3DChunk>,
|
||||
/// Text-LOD greek dim, MultiLeader background, etc. — fills whose
|
||||
/// source wire has an empty `points` list. Always rendered with the
|
||||
/// normal face3d pipeline (visible in every mode).
|
||||
pub vertex_buffer_2d: wgpu::Buffer,
|
||||
pub vertex_count_2d: u32,
|
||||
pub chunks_2d: Vec<Face3DChunk>,
|
||||
}
|
||||
|
||||
impl Face3DGpu {
|
||||
@@ -212,22 +222,35 @@ impl Face3DGpu {
|
||||
}
|
||||
}
|
||||
|
||||
let vertex_buffer_3d = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||
label: Some("face3d.vbuf.3d"),
|
||||
contents: bytemuck::cast_slice(&verts_3d),
|
||||
usage: wgpu::BufferUsages::VERTEX,
|
||||
});
|
||||
let vertex_buffer_2d = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||
label: Some("face3d.vbuf.2d"),
|
||||
contents: bytemuck::cast_slice(&verts_2d),
|
||||
usage: wgpu::BufferUsages::VERTEX,
|
||||
});
|
||||
|
||||
Self {
|
||||
vertex_buffer_3d,
|
||||
vertex_count_3d: verts_3d.len() as u32,
|
||||
vertex_buffer_2d,
|
||||
vertex_count_2d: verts_2d.len() as u32,
|
||||
chunks_3d: upload_chunks(device, &verts_3d, "face3d.vbuf.3d"),
|
||||
chunks_2d: upload_chunks(device, &verts_2d, "face3d.vbuf.2d"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Upload `verts` as one or more VERTEX buffers, each under 90% of the
|
||||
/// device's `max_buffer_size`, split on whole-triangle boundaries. Also
|
||||
/// keeps every chunk's vertex count well below `u32::MAX` so the draw
|
||||
/// range never truncates.
|
||||
fn upload_chunks(
|
||||
device: &wgpu::Device,
|
||||
verts: &[Face3DVertex],
|
||||
label: &'static str,
|
||||
) -> Vec<Face3DChunk> {
|
||||
let budget = (device.limits().max_buffer_size as usize / 10) * 9; // 10% headroom
|
||||
let vsize = std::mem::size_of::<Face3DVertex>();
|
||||
// Round down to a multiple of 3 so triangles never straddle chunks.
|
||||
let max_verts = ((budget / vsize).max(3) / 3) * 3;
|
||||
verts
|
||||
.chunks(max_verts)
|
||||
.map(|c| Face3DChunk {
|
||||
vertex_buffer: device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||
label: Some(label),
|
||||
contents: bytemuck::cast_slice(c),
|
||||
usage: wgpu::BufferUsages::VERTEX,
|
||||
}),
|
||||
vertex_count: c.len() as u32,
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
Reference in new issue
Block a user