re: monotone assignment refuted again -- box identity needs descriptor data

XBG7_MONOTONE=1 enforces descriptor order per (stride, vtx, idx) signature: 89
minority decodes, unchanged. Monotonicity constrains order WITHIN a container
while the disagreement is BETWEEN containers, which hold different numbers of
these boxes in different arrangements. The remaining 89 are bounding-box identity
ambiguities needing descriptor-level information, not another anchoring
heuristic. Knob kept, default off, measurement recorded; suites green.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-12 18:30:58 +00:00
parent 1d8b783d26
commit e8434541f2
2 changed files with 53 additions and 4 deletions

View File

@@ -583,6 +583,7 @@ impl Xbg7Model {
&r.decl, &r.decl,
min_consistency, min_consistency,
&empty_taken, &empty_taken,
0,
) )
.into_iter() .into_iter()
.collect() .collect()
@@ -607,6 +608,7 @@ impl Xbg7Model {
&r.decl, &r.decl,
min_consistency, min_consistency,
&empty_taken, &empty_taken,
0,
) )
.into_iter() .into_iter()
.collect() .collect()
@@ -650,10 +652,39 @@ impl Xbg7Model {
// (the adjacency-anchor path) take part; grouped-pool models are left // (the adjacency-anchor path) take part; grouped-pool models are left
// exactly as they were. // exactly as they were.
let mut taken: std::collections::HashSet<usize> = std::collections::HashSet::new(); let mut taken: std::collections::HashSet<usize> = std::collections::HashSet::new();
// Opt-in monotone pass: the last offset handed to each (stride, vtx, idx)
// signature. Resources that share a signature are interchangeable to the
// validator — many containers hold dozens of identical 24-vertex bound
// boxes — so file order is the only thing that can pin which is which.
let monotone = std::env::var("XBG7_MONOTONE").is_ok();
let mut last_by_sig: std::collections::HashMap<(usize, usize, usize), usize> =
std::collections::HashMap::new();
let mut models: Vec<Xbg7Model> = Vec::with_capacity(decoded.len()); let mut models: Vec<Xbg7Model> = Vec::with_capacity(decoded.len());
for (i, mut m) in decoded { for (i, mut m) in decoded {
let r = &resources[i]; let r = &resources[i];
let starts = &starts_by_stride[&r.decl.stride]; let starts = &starts_by_stride[&r.decl.stride];
if monotone && m.meshes.len() == 1 && r.markers.len() == 1 {
let (vc, ic) = r.markers[0];
let sig = (r.decl.stride, vc, ic);
let floor = last_by_sig.get(&sig).map_or(0, |o| o + 1);
if m.meshes[0].vbuf_offset.map_or(false, |o| o < floor) {
if let Some(alt) = anchor_pool_mesh(
bytes,
starts,
ic,
vc,
&r.decl,
min_consistency,
&taken,
floor,
) {
m.meshes[0] = alt;
}
}
if let Some(o) = m.meshes[0].vbuf_offset {
last_by_sig.insert(sig, o);
}
}
if let Some(vb) = m.meshes[0].vbuf_offset { if let Some(vb) = m.meshes[0].vbuf_offset {
if taken.contains(&vb) { if taken.contains(&vb) {
if m.meshes.len() == 1 && r.markers.len() == 1 { if m.meshes.len() == 1 && r.markers.len() == 1 {
@@ -666,6 +697,7 @@ impl Xbg7Model {
&r.decl, &r.decl,
min_consistency, min_consistency,
&taken, &taken,
0,
) { ) {
m.meshes[0] = alt; m.meshes[0] = alt;
} }
@@ -1169,9 +1201,15 @@ fn anchor_pool_mesh(
decl: &VertexDecl, decl: &VertexDecl,
min_consistency: f32, min_consistency: f32,
taken: &std::collections::HashSet<usize>, taken: &std::collections::HashSet<usize>,
min_vb: usize,
) -> Option<GameMesh> { ) -> Option<GameMesh> {
let idx_bytes = index_count * 2; let idx_bytes = index_count * 2;
for &vb in starts { for &vb in starts {
// Monotone assignment (opt-in): resources of one signature are laid out
// in descriptor order, so a later one may not take an earlier block.
if vb < min_vb {
continue;
}
// A buffer another resource already claimed is not a candidate: the // A buffer another resource already claimed is not a candidate: the
// engine draws each part from its own buffer (proved for the mirrored // engine draws each part from its own buffer (proved for the mirrored
// `e106_bdy_0{1,2}_l` twins by a runtime capture), so two resources // `e106_bdy_0{1,2}_l` twins by a runtime capture), so two resources

View File

@@ -1128,10 +1128,21 @@ rather than being the "one real disagreement" first written here.
different containers.** What remains is 24-vertex boxes swapping identities: many different containers.** What remains is 24-vertex boxes swapping identities: many
of them exist, they are structurally identical, and distinct assignment gives each of them exist, they are structurally identical, and distinct assignment gives each
a *distinct* block without pinning *which* block belongs to which name. Fixing a *distinct* block without pinning *which* block belongs to which name. Fixing
that needs an ordering rule (descriptor order ↔ ascending offset). Monotone that needs an ordering rule (descriptor order ↔ ascending offset).
anchoring was refuted once before, but that was measured under a decoder with
several other defects, so it is worth re-testing — and it is now cheap to judge, **Re-tested, and refuted again.** `XBG7_MONOTONE=1` adds exactly that rule: per
because the target is a single number: 89 → 0. `(stride, vtx_count, idx_count)` signature, a later resource may not take an
earlier block than the previous one of the same signature. Result: **89 minority
decodes — unchanged.** The reason is structural. Monotonicity constrains the
order *within* a container, but the disagreement is *between* containers, which
hold different numbers of these boxes in different arrangements; a consistent
within-container order does not force a consistent name↔box mapping across them.
So the remaining 89 are **bounding-box identity ambiguities**, and pinning them
needs information from the descriptor itself (a composite's own bounds or node
data), not another anchoring heuristic. The knob stays, default off, with this
measurement recorded so the idea is not tried a third time. Defaults verified
unchanged: all ten test suites green.
(The boxes are harmless in themselves — nothing draws them. But no consistency (The boxes are harmless in themselves — nothing draws them. But no consistency
figure should be quoted without saying whether it counts them; the ignored figure should be quoted without saying whether it counts them; the ignored