mirror of
https://github.com/horizon-rust/horizon.git
synced 2026-11-07 11:06:11 -10:00
Make encoded material GPU controls detect filtering and mip regressions
Strengthen only the original synthetic oracle: fractional contrasting data, hostile unused mip contents and deliberately invalid filtering sampler negative control. Production/API code is byte-identical to the already completed full-check head ec6054c.
Co-Authored-By: OpenAI Codex <[email protected]>
This commit is contained in:
1 parent
ec6054c5c0
commit
3a0132d80e
2 files changed
+77
-16
No files matched your search
@@ -30,8 +30,8 @@ uniform sampler2D specular;
|
||||
void main() {
|
||||
int x = int(floor(gl_FragCoord.x));
|
||||
int layer = x - (x / 5) * 5;
|
||||
// Interior of source texel (1,1), away from a nearest-sampling boundary.
|
||||
vec2 uv = (vec2(float(layer - (layer / 3) * 3), float(layer / 3)) + 0.375) / vec2(3.0, 2.0);
|
||||
// Source coordinate (1.25,1.25): inside a texel, away from both its centre and boundary.
|
||||
vec2 uv = (vec2(float(layer - (layer / 3) * 3), float(layer / 3)) + 0.3125) / vec2(3.0, 2.0);
|
||||
if (x < 5) gl_FragData[0] = texture2DLod(specular, uv, 12.0);
|
||||
else if (x < 10) gl_FragData[0] = texture2DLod(normals, uv, 12.0);
|
||||
else gl_FragData[0] = texture2DLod(gtexture, uv, 0.0);
|
||||
@@ -44,23 +44,34 @@ void main() {
|
||||
let atlases: Vec<_> = (0..3)
|
||||
.map(|kind| {
|
||||
let side = if kind == 0 { 4 } else { 2 };
|
||||
let levels = if kind == 0 { 3 } else { 1 };
|
||||
let arrays: Vec<_> = (0..layers.div_ceil(split))
|
||||
.map(|array| {
|
||||
let count = (layers - array * split).min(split);
|
||||
let pixels: Vec<u8> = (0..count)
|
||||
.flat_map(|l| {
|
||||
let layer = (array * split + l) as u8;
|
||||
(0..side * side).flat_map(move |pixel| match kind {
|
||||
0 if layer == 3 => crate::sprites::NO_SPECULAR,
|
||||
0 => [
|
||||
17 + layer,
|
||||
if pixel % 2 == 0 { 229 } else { 230 },
|
||||
65,
|
||||
if pixel % 2 == 0 { 254 } else { 255 },
|
||||
],
|
||||
1 if layer == 2 => crate::sprites::FLAT_NORMAL,
|
||||
1 => [128 + layer, 126, 255, 19 + layer],
|
||||
_ => [40 + layer, 50, 60, 255],
|
||||
(0..levels).flat_map(move |level| {
|
||||
let edge = side >> level;
|
||||
(0..edge * edge).flat_map(move |pixel| {
|
||||
if level != 0 {
|
||||
// Sampling unused source mip levels must fail the oracle.
|
||||
[211, 212, 213, 214]
|
||||
} else {
|
||||
match kind {
|
||||
0 if layer == 3 => crate::sprites::NO_SPECULAR,
|
||||
0 => [
|
||||
17 + layer,
|
||||
if pixel % 2 == 0 { 0 } else { 240 },
|
||||
65,
|
||||
if pixel % 2 == 0 { 0 } else { 255 },
|
||||
],
|
||||
1 if layer == 2 => crate::sprites::FLAT_NORMAL,
|
||||
1 => [128 + layer, 126, 255, 19 + layer],
|
||||
_ => [40 + layer, 50, 60, 255],
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
@@ -73,7 +84,7 @@ void main() {
|
||||
height: side,
|
||||
depth_or_array_layers: count,
|
||||
},
|
||||
mip_level_count: 1,
|
||||
mip_level_count: levels,
|
||||
sample_count: 1,
|
||||
dimension: wgpu::TextureDimension::D2,
|
||||
format: wgpu::TextureFormat::Rgba8Unorm,
|
||||
@@ -219,11 +230,28 @@ void main() {
|
||||
} else if !maps_expected || layer == 3 {
|
||||
crate::sprites::NO_SPECULAR
|
||||
} else {
|
||||
[17 + layer, 230, 65, 255]
|
||||
[17 + layer, 240, 65, 255]
|
||||
};
|
||||
assert_eq!(&data[i * 4..i * 4 + 4], &expected, "case={case}, pixel={i}");
|
||||
}
|
||||
eprintln!("Material oracle case={case}: 15 exact RGBA pixels pass");
|
||||
}
|
||||
// Fault injection: the same production binding must refuse a filtering sampler.
|
||||
// If a regression changed both the binding and sampler, the fractional positive
|
||||
// probes above would instead mix 0.75*240=180 and 0.75*255=191.25, not 240/255.
|
||||
pass.material_maps = true;
|
||||
post.material_maps = Some([atlases[1].view.clone(), atlases[0].view.clone()]);
|
||||
post.nearest_repeat = sampler(&device, wgpu::FilterMode::Linear, wgpu::AddressMode::Repeat);
|
||||
pass.groups.clear();
|
||||
let validation = device.push_error_scope(wgpu::ErrorFilter::Validation);
|
||||
let mut encoder = device.create_command_encoder(&Default::default());
|
||||
let _invalid_group = post.bind(&device, &queue, &mut encoder, &mut pass, [15, 1]);
|
||||
let error = pollster::block_on(validation.pop())
|
||||
.expect("production material binding must reject a filtering sampler");
|
||||
assert!(
|
||||
error.to_string().to_ascii_lowercase().contains("filtering"),
|
||||
"{error}"
|
||||
);
|
||||
eprintln!("Material oracle negative filtering control: rejected by production binding");
|
||||
std::fs::remove_dir_all(root).unwrap();
|
||||
}
|
||||
@@ -511,7 +511,7 @@
|
||||
{
|
||||
"path": "crates/horizon-client-render/src/gfx/pack_post.rs",
|
||||
"sha256_at_public_main": "079dd496cd203fd42656d67c4eb9d71de54870bf2d1fecddd43da8db53ab52ea",
|
||||
"scope": "build/contribution instructions or targeted API/type construction/diff only; existing fixture algorithms preserved"
|
||||
"scope": "Targeted main API/type construction, sampler/binding and error-scope fixture instrumentation only; existing algorithms preserved."
|
||||
},
|
||||
{
|
||||
"path": "crates/horizon-client-render/src/gfx/pack_post/world_target_tests.rs",
|
||||
@@ -557,5 +557,38 @@
|
||||
"Ambiguous grids and unverified generated/tiled origins retain missing-map defaults.",
|
||||
"Final-main qualification is reported separately in the PR."
|
||||
]
|
||||
},
|
||||
"post_integration_fixture_strengthening": {
|
||||
"reason": "Texel-centre/adjacent-byte input could miss an unintended filtering regression. Independently strengthen original synthetic fixture before publication; no production/API byte changes.",
|
||||
"prior_full_self_check_head": "ec6054c5c0c4438a692ddd93f159cd188ac13ff9",
|
||||
"only_changed_paths": [
|
||||
"crates/horizon-client-render/src/gfx/pack_material_tests.rs",
|
||||
"docs/research/terrain-material-inputs-20261103.json"
|
||||
],
|
||||
"fixture_sha256": "cb0ed419d87449fca478b422a68cc4c5d7145ac7d285ae4bad3a3755c1352d3d",
|
||||
"fractional_source_coordinate": [
|
||||
1.25,
|
||||
1.25
|
||||
],
|
||||
"encoded_green_contrast": [
|
||||
0,
|
||||
240
|
||||
],
|
||||
"encoded_alpha_contrast": [
|
||||
0,
|
||||
255
|
||||
],
|
||||
"unused_specular_source_mips": {
|
||||
"levels": 3,
|
||||
"higher_level_bytes": [
|
||||
211,
|
||||
212,
|
||||
213,
|
||||
214
|
||||
]
|
||||
},
|
||||
"positive_controls": "Three15-pixel exact RGBA controls; nearest expected240/255, no source mip averaging at requestedLOD12.",
|
||||
"negative_control": "Substituting a filtering sampler must be rejected by the same production non-filtering binding; if both sampler and binding regressed, positive fractional samples would mix to180/191 rather than240/255.",
|
||||
"qualification": "Final all-target renderer Clippy, compiled-fixture GPU results and exact final hashes reported separately in PR; prior full product gate remains nonzero on2 independently reproduced main framegen assertions."
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user