Fix block details hidden by wipeout masks (#1304)

This commit is contained in:
Hakan Seven committed 2026-09-16 10:24:55 +03:00
1 parent d10a0cf798
commit 457f4b3dbb
4 files changed
+89 -12

No files matched your search

+1 -1
View File
@@ -145,7 +145,7 @@ pub fn create_pipelines(
},
depth_stencil: Some(wgpu::DepthStencilState {
format: wgpu::TextureFormat::Depth24PlusStencil8,
depth_write_enabled: Some(false),
depth_write_enabled: Some(true),
depth_compare: Some(wgpu::CompareFunction::LessEqual),
stencil: content_stencil.clone(),
bias: wgpu::DepthBiasState::default(),
+84
View File
@@ -104,6 +104,90 @@ fn color_at(bytes: &[u8], ndc_x: f32) -> [u32; 3] {
color
}
#[test]
#[ignore = "requires a GPU adapter"]
fn wipeout_respects_close_block_draw_order() {
use acadrust::{entities::Wipeout, types::Vector2, EntityType};
use crate::scene::{model::wire_model::TangentGeom, Scene};
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor::new_without_display_handle());
let adapter = block_on(instance.request_adapter(&Default::default())).expect("GPU adapter");
for packed in [false, true] {
let mut limits = adapter.limits();
if packed {
limits.max_storage_buffers_per_shader_stage = 0;
}
let (device, queue) = block_on(adapter.request_device(&wgpu::DeviceDescriptor {
required_limits: limits,
..Default::default()
}))
.expect("GPU device");
let validation = device.push_error_scope(wgpu::ErrorFilter::Validation);
let mut pipeline = Pipeline::new(&device, &queue, wgpu::TextureFormat::Rgba8Unorm);
let bounds = Rectangle::with_size(Size::new(SIZE as f32, SIZE as f32));
let mut uniforms = Uniforms::new(&Camera::default(), bounds, false);
uniforms.view_rot = glam::Mat4::IDENTITY;
uniforms.eye_high = [0.; 3];
uniforms.eye_low = [0.; 3];
let mut scene = Scene::new();
scene.add_entity(EntityType::Wipeout(Wipeout::polygonal(
&[
Vector2::new(-0.7, -0.7),
Vector2::new(0.7, -0.7),
Vector2::new(0.7, 0.7),
Vector2::new(-0.7, 0.7),
],
0.5,
)));
let mut masks = scene.wipeout_models_arc().as_ref().clone();
assert_eq!(masks.len(), 1);
masks[0].color = [0., 0., 0., 1.];
masks[0].draw_depth = 0.1;
pipeline.upload_wipeouts(&device, &queue, &masks);
// Block children share a narrow sub-range of their insert's order.
// The mask must hide earlier geometry without erasing later geometry.
let depths = [(1, [0.0998, 0.]), (2, [0.1002, 0.])].into_iter().collect();
for kind in ["line", "circle", "arc", "ellipse"] {
let wires: Vec<_> = [(-0.3, 1), (0.3, 2)]
.into_iter()
.map(|(x, id)| {
let mut wire = WireModel::solid(
id.to_string(),
Vec::new(),
[1.; 4],
false,
);
let center = [x as f64 - 0.1, 0., 0.5];
let axis_x = [1., 0., 0.];
let axis_y = [0., 1., 0.];
match kind {
"circle" => wire.tangent_geoms.push(TangentGeom::PlanarCircle {
center, axis_x, axis_y, radius: 0.1,
}),
"arc" => wire.tangent_geoms.push(TangentGeom::Arc {
center, axis_x, axis_y, radius: 0.1,
start_angle: -std::f64::consts::FRAC_PI_2,
end_angle: std::f64::consts::FRAC_PI_2,
}),
"ellipse" => wire.tangent_geoms.push(TangentGeom::PlanarEllipse {
center, major_axis: [0.1, 0., 0.], normal: [0., 0., 1.],
minor_axis_ratio: 0.6, start_param: 0., end_param: std::f64::consts::TAU,
}),
_ => wire.points = vec![[x, -0.65, 0.5], [x, 0.65, 0.5]],
}
wire
})
.collect();
pipeline.gpu_circles = std::sync::Arc::new(pipeline.upload_circles(&device, &queue, &wires, &depths));
pipeline.gpu_ellipses = std::sync::Arc::new(pipeline.upload_ellipses(&device, &queue, &wires, &depths));
let bytes = pixels(&device, &queue, &mut pipeline, &uniforms, &wires, &depths);
assert_eq!(color_at(&bytes, -0.3), [0; 3], "mask must cover earlier {kind}");
assert_ne!(color_at(&bytes, 0.3), [0; 3], "mask erased later {kind}, packed={packed}");
}
assert!(block_on(validation.pop()).is_none(), "GPU validation failed");
}
}
#[test]
#[ignore = "requires a GPU adapter"]
fn perspective_draw_order_keeps_flat_lines_visible() {
+1 -1
View File
@@ -145,7 +145,7 @@ pub fn create_pipelines(
},
depth_stencil: Some(wgpu::DepthStencilState {
format: wgpu::TextureFormat::Depth24PlusStencil8,
depth_write_enabled: Some(false),
depth_write_enabled: Some(true),
depth_compare: Some(wgpu::CompareFunction::LessEqual),
stencil: content_stencil.clone(),
bias: wgpu::DepthBiasState::default(),
+3 -10
View File
@@ -1055,16 +1055,9 @@ impl Pipeline {
depth_write_enabled: Some(true),
depth_compare: Some(wgpu::CompareFunction::LessEqual),
stencil: content_stencil.clone(),
// Bias TOWARD the camera: a wipeout must win against geometry at
// its own depth (a block's wipeout + shapes are coincident at
// Z=0). A positive bias pushed the mask behind, so LessEqual
// rejected it and the geometry showed through. Tiny enough that
// meaningfully-nearer geometry still occludes the mask.
bias: wgpu::DepthBiasState {
constant: -8,
slope_scale: -1.0,
clamp: 0.0,
},
// The shader already applies draw order. A raster bias can
// jump across a block's narrow range and erase its foreground.
bias: wgpu::DepthBiasState::default(),
}),
multisample: wgpu::MultisampleState {
count: MSAA_SAMPLES,