[iterate-4A] intro-video: complete RectangleList quad (fix diagonal seam)

A Xenos RectangleList gives 3 corners of a rectangle; the GPU synthesizes
the 4th (v3 = v0 + v2 - v1) and tessellates the quad as two triangles
(v0,v1,v2) + (v0,v2,v3). Our expansion emitted only the front triangle, so
the movie's fullscreen YUV rect filled half the screen and left a diagonal
seam down the frame (absent in canary).

expand_rectangles now emits the full 6-index quad. Since v3 has no backing
vertex in the guest window, the translated VS synthesizes its attributes:
the RectangleList arm maps 6 host verts through [0,1,2, 0,2,3], flags the
4th corner, and emit_vfetch computes that corner's attributes (position and
every interpolator) as v0 + v2 - v1 by reading the three real source
vertices. Non-rect draws are unaffected (the synth flag is false).

Verified: the movie background now fills the whole frame uniformly, no
diagonal, matching canary.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-07-02 20:53:37 +02:00
parent 3559c8f6ef
commit deb9292395
2 changed files with 64 additions and 31 deletions

View File

@@ -143,18 +143,17 @@ fn expand_quads(indices: Option<&[u32]>, vertex_count: u32) -> ProcessedPrimitiv
/// `primitive_processor.cc:389-456`): a 4-vertex triangle strip per rect with
/// the 4th corner synthesized *in the VS* from the host-vertex index.
///
/// Our replay pipeline has no host-VS corner synthesis (and the procedural
/// `vs_main` does not consume `rewritten_indices` yet), so we mirror the
/// `expand_quads`/`expand_fan` CPU idiom and emit the 3 real vertices of each
/// rect as one triangle list `(v0,v1,v2)` — the visible lower half of the
/// rect. This un-rejects the draw and gives a faithful `host_vertex_count`.
///
/// TODO: once `vs_main` does real vertex fetch + interpolation, upgrade to the
/// full quad — 6 indices `[v0,v1,v2, v2,v1,v3]` with a synthesized `v3` corner
/// — mirroring canary's `kRectangleListAsTriangleStrip`.
/// We draw the full quad as a 6-vertex triangle list `[v0,v1,v2, v0,v2,v3]`
/// per rect (canary's tessellation). The synthesized `v3` (index `base+3`) has no backing vertex in
/// the guest window, so the translated `vs_main` computes its attributes as
/// `v0 + v2 - v1` when the host-vertex index maps to that corner (see the
/// RectangleList handling + `emit_vfetch` corner synthesis in `translator.rs`).
/// Emitting only the front triangle `(v0,v1,v2)` — as we used to — fills just
/// half the rect and leaves a diagonal seam (the movie's fullscreen YUV rect
/// showed exactly that).
fn expand_rectangles(indices: Option<&[u32]>, vertex_count: u32) -> ProcessedPrimitive {
let rect_count = vertex_count / 3;
let mut out = Vec::with_capacity(3 * rect_count as usize);
let mut out = Vec::with_capacity(6 * rect_count as usize);
let get = |i: u32| -> u32 {
match indices {
Some(buf) => buf[i as usize],
@@ -163,9 +162,11 @@ fn expand_rectangles(indices: Option<&[u32]>, vertex_count: u32) -> ProcessedPri
};
for r in 0..rect_count {
let base = r * 3;
out.push(get(base));
out.push(get(base + 1));
out.push(get(base + 2));
let (v0, v1, v2) = (get(base), get(base + 1), get(base + 2));
// Front triangle (v0,v1,v2), then the completing triangle (v0,v2,v3)
// with the synthesized 4th corner (index `base + 3`; the VS
// extrapolates its attributes as v0+v2-v1).
out.extend_from_slice(&[v0, v1, v2, v0, v2, base + 3]);
}
let host_vertex_count = out.len() as u32;
metrics::counter!("gpu.primitive.expanded", "shape" => "rectangle_list").increment(1);
@@ -239,12 +240,14 @@ mod tests {
#[test]
fn rectangle_list_expansion() {
// 2 rects (6 verts) → one triangle (v0,v1,v2) per rect, not rejected.
// 2 rects (6 verts) → full quad per rect: [v0,v1,v2, v2,v1,v3] where
// v3 is the synthesized 4th corner (index base+3, extrapolated in the
// VS). rect0 → base 0, rect1 → base 3.
let p = process(PrimitiveType::RectangleList, 6, None);
let idx = p.rewritten_indices.unwrap();
assert_eq!(idx, vec![0, 1, 2, 3, 4, 5]);
assert_eq!(idx, vec![0, 1, 2, 0, 2, 3, 3, 4, 5, 3, 5, 6]);
assert_eq!(p.topology, HostTopology::TriangleList);
assert_eq!(p.host_vertex_count, 6);
assert_eq!(p.host_vertex_count, 12);
assert!(!p.rejected);
}