forked from 1b-agent/opencadstudio-upstream
178 lines
6.4 KiB
Rust
178 lines
6.4 KiB
Rust
use super::*;
|
|
use crate::scene::model::{mesh_model::MeshLodSet, mesh_model::MeshModel, wire_model::WireModel};
|
|
use acadrust::{types::Transform, Handle};
|
|
use iced::futures::executor::block_on;
|
|
|
|
// 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"]
|
|
fn bounded_gpu_uploads_preserve_geometry_and_instancing() {
|
|
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor::new_without_display_handle());
|
|
let adapter = block_on(instance.request_adapter(&wgpu::RequestAdapterOptions::default()))
|
|
.expect("GPU adapter");
|
|
eprintln!("GPU regression adapter: {:?}", adapter.get_info());
|
|
let (device, queue) = block_on(adapter.request_device(&wgpu::DeviceDescriptor {
|
|
required_limits: adapter.limits(),
|
|
..Default::default()
|
|
}))
|
|
.expect("GPU device");
|
|
let budget = gpu_budget::buffer_budget(&device) as u64;
|
|
assert_eq!(budget, 1024 * 1024, "run with OCS_GPU_CHUNK_MIB=1");
|
|
let validation = device.push_error_scope(wgpu::ErrorFilter::Validation);
|
|
let pipeline = Pipeline::new(&device, &queue, wgpu::TextureFormat::Bgra8UnormSrgb);
|
|
|
|
let line = WireModel {
|
|
points: vec![[0.0, 0.0, 0.0], [1.0, 0.0, 0.0]],
|
|
..Default::default()
|
|
};
|
|
let count = gpu_budget::max_storage_elements::<wire_gpu::WireConst>(&device) + 2;
|
|
let wires = vec![line; count];
|
|
for bgl in [pipeline.wire_const_bgl.as_ref(), None] {
|
|
let chunks =
|
|
wire_gpu::WireGpu::from_run(&device, &queue, &wires, &Default::default(), false, bgl);
|
|
assert!(chunks.len() > 1);
|
|
assert_eq!(
|
|
chunks
|
|
.iter()
|
|
.map(|chunk| chunk.instance_count as usize)
|
|
.sum::<usize>(),
|
|
count
|
|
);
|
|
assert!(chunks
|
|
.iter()
|
|
.all(|chunk| chunk.instance_buffer.size() <= budget));
|
|
}
|
|
let segments = gpu_budget::max_elements::<wire_gpu::WireInstance>(&device) + 1;
|
|
let dense = WireModel {
|
|
points: (0..=segments).map(|i| [i as f32, 0.0, 0.0]).collect(),
|
|
..Default::default()
|
|
};
|
|
let chunks = wire_gpu::WireGpu::from_run(
|
|
&device,
|
|
&queue,
|
|
&[dense],
|
|
&Default::default(),
|
|
false,
|
|
pipeline.wire_const_bgl.as_ref(),
|
|
);
|
|
assert!(chunks.len() > 1);
|
|
assert_eq!(
|
|
chunks
|
|
.iter()
|
|
.map(|chunk| chunk.instance_count as usize)
|
|
.sum::<usize>(),
|
|
segments
|
|
);
|
|
assert!(chunks
|
|
.iter()
|
|
.all(|chunk| chunk.instance_buffer.size() <= budget));
|
|
|
|
let vertices = vec![
|
|
bytemuck::Zeroable::zeroed();
|
|
gpu_budget::max_elements_grouped::<text_gpu::TextVertex>(&device, 6) + 6
|
|
];
|
|
let text = text_gpu::upload_vertices(&device, &queue, &vertices);
|
|
assert_eq!(text.len(), 2);
|
|
assert_eq!(
|
|
text.iter()
|
|
.map(|chunk| chunk.vertex_count as usize)
|
|
.sum::<usize>(),
|
|
vertices.len()
|
|
);
|
|
assert!(text
|
|
.iter()
|
|
.all(|chunk| chunk.vertex_count % 6 == 0 && chunk.vertex_buffer.size() <= budget));
|
|
|
|
let source = |triangles: usize| {
|
|
let mut set = MeshLodSet::from_single(MeshModel {
|
|
name: "7".to_owned(),
|
|
verts: (0..triangles)
|
|
.flat_map(|_| [[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]])
|
|
.collect(),
|
|
verts_low: Vec::new(),
|
|
normals: Vec::new(),
|
|
indices: (0..triangles as u32 * 3).collect(),
|
|
triangle_material_handles: Vec::new(),
|
|
triangle_colors: Vec::new(),
|
|
color: [1.0; 4],
|
|
selected: false,
|
|
});
|
|
set.prepare_instance_source(Handle::new(7));
|
|
set.lods.clear();
|
|
set.instance_transform = Some(Transform::identity());
|
|
set.instance_handle = Some(Handle::new(101));
|
|
set
|
|
};
|
|
let triangles = gpu_budget::max_elements::<mesh_gpu::MeshVertex>(&device) / 3 + 1;
|
|
let first = source(triangles);
|
|
let mut second = first.clone();
|
|
second.instance_handle = Some(Handle::new(102));
|
|
let (mesh, total) = mesh_gpu::build_mesh_batch(&device, &queue, &[first, second]);
|
|
assert!(mesh.len() > 1);
|
|
assert_eq!(total, (triangles * 2) as u64);
|
|
assert_eq!(
|
|
mesh.iter()
|
|
.map(|chunk| chunk.index_count as usize / 3)
|
|
.sum::<usize>(),
|
|
triangles
|
|
);
|
|
for chunk in &mesh {
|
|
assert_eq!(
|
|
chunk.instance_count, 2,
|
|
"large geometry must remain instanced"
|
|
);
|
|
assert_eq!(chunk.handles.len(), 2);
|
|
assert_eq!(chunk.highlight_ranges.len(), 2);
|
|
assert!(chunk
|
|
.highlight_ranges
|
|
.iter()
|
|
.all(|range| range.index_count == chunk.index_count && range.instance_start < 2));
|
|
for buffer in [
|
|
&chunk.vertex_buffer,
|
|
&chunk.index_buffer,
|
|
&chunk.transp_index_buffer,
|
|
&chunk.edge_vertex_buffer,
|
|
&chunk.wire_vertex_buffer,
|
|
&chunk.instance_buffer,
|
|
] {
|
|
assert!(
|
|
buffer.size() <= budget,
|
|
"buffer size {} exceeds {budget}",
|
|
buffer.size()
|
|
);
|
|
}
|
|
}
|
|
|
|
let instance_count = gpu_budget::max_elements::<mesh_gpu::MeshInstanceGpu>(&device) + 1;
|
|
let mut repeated = vec![source(1); instance_count];
|
|
for (index, set) in repeated.iter_mut().enumerate() {
|
|
set.instance_handle = Some(Handle::new(1000 + index as u64));
|
|
}
|
|
let (mesh, total) = mesh_gpu::build_mesh_batch(&device, &queue, &repeated);
|
|
assert_eq!(mesh.len(), 2);
|
|
assert_eq!(total, instance_count as u64);
|
|
assert_eq!(mesh[0].vertex_buffer, mesh[1].vertex_buffer);
|
|
assert_eq!(mesh[0].index_buffer, mesh[1].index_buffer);
|
|
assert_eq!(
|
|
mesh.iter()
|
|
.map(|chunk| chunk.instance_count as usize)
|
|
.sum::<usize>(),
|
|
instance_count
|
|
);
|
|
for chunk in &mesh {
|
|
assert!(chunk.instance_buffer.size() <= budget);
|
|
assert_eq!(chunk.highlight_ranges.len(), chunk.instance_count as usize);
|
|
assert!(chunk
|
|
.highlight_ranges
|
|
.iter()
|
|
.all(|range| range.instance_start < chunk.instance_count));
|
|
}
|
|
|
|
queue.submit([]);
|
|
device.poll(wgpu::PollType::wait_indefinitely()).unwrap();
|
|
assert!(
|
|
block_on(validation.pop()).is_none(),
|
|
"GPU validation failed"
|
|
);
|
|
}
|