Display tessellation plans (4/4): one GPU dispatch for the scene, intersection edges by Newton (#806)
Packs the scene's planned bodies into one GPU upload and dispatch, and evaluates intersection edges by seeded Newton refinement from a fitted B-spline through exactly solved leader points, falling back to the previous evaluation near tangency. Intersection nodes carry their faces' parameters. This changes speed, not what the app draws. Part 4 of 4 of #303. Co-authored-by: jyviko <2501648+jyviko@users.noreply.github.com>
This commit is contained in:
1 parent
c1c813e572
commit
587cf865b8
9 files changed
+612
-106
No files matched your search
+44
-21
@@ -184,7 +184,7 @@ struct Batch {
|
||||
count: u32,
|
||||
}
|
||||
|
||||
/// A planned body on the GPU: its evaluated mesh and a colour per vertex.
|
||||
/// A chunk of planned bodies on the GPU: their evaluated mesh and a colour per vertex.
|
||||
struct PlannedDraw {
|
||||
mesh: Arc<solidcraft_gpu::GpuMesh>,
|
||||
colors: wgpu::Buffer,
|
||||
@@ -206,9 +206,9 @@ struct Batches {
|
||||
xray: Option<Batch>,
|
||||
under: Option<Batch>,
|
||||
planned: Vec<PlannedDraw>,
|
||||
/// Evaluated plans by the plan's address, holding the plan so the address stays its own:
|
||||
/// a body whose plan is unchanged is not evaluated again when the scene is rebuilt.
|
||||
resident: std::collections::HashMap<usize, (Arc<solidcraft_engine::geom::tess::TessPlan>, Arc<solidcraft_gpu::GpuMesh>)>,
|
||||
/// Evaluated chunks by their plans' addresses, holding the plans so the addresses stay
|
||||
/// theirs: a chunk whose plans are unchanged is not evaluated again when the scene is rebuilt.
|
||||
resident: std::collections::HashMap<Vec<usize>, (Vec<Arc<solidcraft_engine::geom::tess::TessPlan>>, Arc<solidcraft_gpu::GpuMesh>)>,
|
||||
}
|
||||
|
||||
impl Batches {
|
||||
@@ -238,6 +238,9 @@ impl Batches {
|
||||
}
|
||||
}
|
||||
|
||||
/// The planned bodies on the GPU: in chunks of at most `MAX_BATCH` vertices (one for any
|
||||
/// ordinary scene), each uploaded and evaluated in one dispatch and drawn in one call. A
|
||||
/// chunk whose plans are unchanged is not evaluated again; colours are rebuilt each time.
|
||||
fn planned(
|
||||
&mut self,
|
||||
device: &wgpu::Device,
|
||||
@@ -245,27 +248,47 @@ impl Batches {
|
||||
encoder: &mut wgpu::CommandEncoder,
|
||||
bodies: &[PlannedBody],
|
||||
) -> Vec<PlannedDraw> {
|
||||
let ptr = |b: &PlannedBody| Arc::as_ptr(&b.plan) as usize;
|
||||
self.resident.retain(|k, _| bodies.iter().any(|b| ptr(b) == *k));
|
||||
let mut out = Vec::with_capacity(bodies.len());
|
||||
let mut chunks: Vec<Vec<&PlannedBody>> = Vec::new();
|
||||
let mut filled = 0usize;
|
||||
for b in bodies {
|
||||
let mesh = match self.resident.get(&ptr(b)) {
|
||||
let n = b.plan.vertex_count();
|
||||
match chunks.last_mut() {
|
||||
Some(c) if filled + n <= solidcraft_gpu::MAX_BATCH => c.push(b),
|
||||
_ => {
|
||||
chunks.push(vec![b]);
|
||||
filled = 0;
|
||||
}
|
||||
}
|
||||
filled += n;
|
||||
}
|
||||
let key = |c: &[&PlannedBody]| c.iter().map(|b| Arc::as_ptr(&b.plan) as usize).collect::<Vec<usize>>();
|
||||
self.resident.retain(|k, _| chunks.iter().any(|c| key(c) == *k));
|
||||
let mut out = Vec::with_capacity(chunks.len());
|
||||
for chunk in &chunks {
|
||||
let mesh = match self.resident.get(&key(chunk)) {
|
||||
Some((_, m)) => m.clone(),
|
||||
None => match tess.prepare(&b.plan) {
|
||||
Ok(m) => {
|
||||
tess.record(encoder, &m);
|
||||
let m = Arc::new(m);
|
||||
self.resident.insert(ptr(b), (b.plan.clone(), m.clone()));
|
||||
m
|
||||
None => {
|
||||
let plans: Vec<&solidcraft_engine::geom::tess::TessPlan> = chunk.iter().map(|b| &*b.plan).collect();
|
||||
match tess.prepare_many(&plans) {
|
||||
Ok(m) => {
|
||||
tess.record(encoder, &m);
|
||||
let m = Arc::new(m);
|
||||
self.resident.insert(key(chunk), (chunk.iter().map(|b| b.plan.clone()).collect(), m.clone()));
|
||||
m
|
||||
}
|
||||
Err(e) => {
|
||||
log::warn!("bodies are not drawn on the GPU: {e}");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
log::warn!("a body is not drawn on the GPU: {e}");
|
||||
continue;
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
let last = b.face_colors.last().copied().unwrap_or([200, 200, 200, 255]);
|
||||
let bytes: Vec<u8> = mesh.vertex_face.iter().flat_map(|f| b.face_colors.get(*f as usize).copied().unwrap_or(last)).collect();
|
||||
let mut bytes = Vec::with_capacity(mesh.vertex_face.len() * 4);
|
||||
for (part, b) in mesh.parts.iter().zip(chunk) {
|
||||
let last = b.face_colors.last().copied().unwrap_or([200, 200, 200, 255]);
|
||||
let faces = mesh.vertex_face.get(part.vertices.start as usize..part.vertices.end as usize).unwrap_or_default();
|
||||
bytes.extend(faces.iter().flat_map(|f| b.face_colors.get(*f as usize).copied().unwrap_or(last)));
|
||||
}
|
||||
if let Some(colors) = upload(device, "sc_planned_colors", &bytes, 4) {
|
||||
out.push(PlannedDraw { mesh, colors: colors.buffer });
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user