Merge pull request #1006 with block rendering fixes

Invalidate cached world-space block vertices when the base translation changes. Share the regular/mesh partition between full uploads and patches so instanced blocks are uploaded only once. Cover both paths with CPU and GPU regression checks.
This commit is contained in:
Hakan Seven committed 2026-09-05 12:30:43 +03:00
commit af0cc68c5a
5 files changed
+317 -106

No files matched your search

+89
View File
@@ -3,6 +3,95 @@ use crate::scene::model::{mesh_model::MeshLodSet, mesh_model::MeshModel, wire_mo
use acadrust::{types::Transform, Handle};
use iced::futures::executor::block_on;
#[test]
#[ignore = "requires a GPU adapter"]
fn block_edits_preserve_cache_coordinates_and_arena_partition() {
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor::new_without_display_handle());
let adapter = block_on(instance.request_adapter(&wgpu::RequestAdapterOptions::default()))
.expect("GPU adapter");
let (device, queue) = block_on(adapter.request_device(&wgpu::DeviceDescriptor {
required_limits: adapter.limits(),
..Default::default()
}))
.expect("GPU device");
let validation = device.push_error_scope(wgpu::ErrorFilter::Validation);
let mut pipeline = Pipeline::new(&device, &queue, wgpu::TextureFormat::Bgra8UnormSrgb);
let block = |handle: u64, x: f64| WireModel {
name: handle.to_string(),
points: vec![[x as f32, 0.0, 0.0], [x as f32 + 1.0, 0.0, 0.0]],
render_instance: Some(crate::scene::model::instance_model::RenderInstance {
source_id: 1,
translation: [x, 0.0, 0.0],
}),
..Default::default()
};
let first = block(1, 10.0);
let second = block(2, 20.0);
let depth = rustc_hash::FxHashMap::from_iter([(1, [0.1, 0.01]), (2, [0.2, 0.01])]);
let original = pipeline.upload_block_wires(&device, &queue, &[&first, &second], &depth);
let unchanged = pipeline.upload_block_wires(&device, &queue, &[&first, &second], &depth);
assert_eq!(original[0].vertex_buffer, unchanged[0].vertex_buffer);
assert_eq!(original[0].instance_count, 2);
let removed = pipeline.upload_block_wires(&device, &queue, &[&second], &depth);
assert_ne!(
original[0].vertex_buffer, removed[0].vertex_buffer,
"removing the base instance must replace vertices baked at its old position"
);
let moved = block(2, 30.0);
let moved_gpu = pipeline.upload_block_wires(&device, &queue, &[&moved], &depth);
assert_ne!(removed[0].vertex_buffer, moved_gpu[0].vertex_buffer);
let restored = pipeline.upload_block_wires(&device, &queue, &[&first, &second], &depth);
assert_ne!(moved_gpu[0].vertex_buffer, restored[0].vertex_buffer);
assert_eq!(restored[0].instance_count, 2);
assert_eq!(
pipeline.block_geometry.len(),
1,
"discard obsolete geometry"
);
let line = WireModel {
render_instance: None,
..first.clone()
};
let changed = [second];
let (regular, _) = wire_arena::split_wires(&changed);
let runs = rustc_hash::FxHashMap::from_iter([(Handle::new(2), regular)]);
for layout in [pipeline.wire_const_bgl.as_ref(), None] {
let mut arena = wire_arena::PersistentWireArena::build(
&device,
&queue,
&[&line],
&depth,
layout,
false,
)
.unwrap();
assert!(arena.patch(
&queue,
&[(Handle::new(2), crate::scene::ChangeKind::Added)],
&runs,
true,
&depth
));
assert_eq!(
arena
.wire_gpus()
.iter()
.map(|gpu| gpu.instance_count)
.sum::<u32>(),
1,
"the block must not also enter the regular arena"
);
}
queue.submit([]);
device.poll(wgpu::PollType::wait_indefinitely()).unwrap();
assert!(
block_on(validation.pop()).is_none(),
"GPU validation failed"
);
}
// OCS_GPU_CHUNK_MIB=1 cargo test --lib bounded_gpu_uploads -- --ignored --nocapture
#[test]
#[ignore = "requires a GPU adapter and OCS_GPU_CHUNK_MIB=1"]