[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:
@@ -235,11 +235,17 @@ impl EmitCtx {
|
||||
// vertices here (mirrors `primitive.rs` index rewrite, but in the
|
||||
// VS since the replay path is non-indexed):
|
||||
// QuadList(13): 6 host verts → guest [0,1,2, 0,2,3]
|
||||
// RectangleList(8): drawn as one triangle [0,1,2] (the 4th
|
||||
// corner needs cross-vertex synthesis — TODO), so host
|
||||
// indices >=3 fold onto the existing triangle.
|
||||
// RectangleList(8): 6 host verts → [0,1,2, 2,1,3]; corner 3
|
||||
// has no backing vertex, so `rect_synth` flags it and
|
||||
// `emit_vfetch` extrapolates its attributes as v0+v2-v1
|
||||
// (parallelogram completion, matching Xenos rect semantics).
|
||||
// Drawing only the front triangle left a diagonal seam.
|
||||
// Other prims pass through unchanged.
|
||||
self.push("var gvidx: u32 = vidx;");
|
||||
// Rectangle-list 4th-corner synthesis state, consumed by
|
||||
// `emit_vfetch`. `rect_synth` is false for every other prim.
|
||||
self.push("var rect_base: u32 = 0u;");
|
||||
self.push("var rect_synth: bool = false;");
|
||||
self.push("if (draw_ctx.prim_kind == 13u) {");
|
||||
self.indent += 1;
|
||||
self.push("let q = vidx % 6u; let qbase = (vidx / 6u) * 4u;");
|
||||
@@ -248,8 +254,13 @@ impl EmitCtx {
|
||||
self.indent -= 1;
|
||||
self.push("} else if (draw_ctx.prim_kind == 8u) {");
|
||||
self.indent += 1;
|
||||
self.push("let t = vidx % 3u; let rbase = (vidx / 3u) * 3u;");
|
||||
self.push("gvidx = rbase + t;");
|
||||
self.push("let local = vidx % 6u; rect_base = (vidx / 6u) * 3u;");
|
||||
// Triangles (v0,v1,v2) + (v0,v2,v3) — canary's rect tessellation
|
||||
// (an explicit list, not a strip). v3 (corner 3) is synthesized.
|
||||
self.push("var rlut = array<u32, 6>(0u, 1u, 2u, 0u, 2u, 3u);");
|
||||
self.push("let corner = rlut[local];");
|
||||
self.push("gvidx = rect_base + corner;");
|
||||
self.push("rect_synth = corner == 3u;");
|
||||
self.indent -= 1;
|
||||
self.push("}");
|
||||
// Seed r0 with vertex index for simple shaders that read it.
|
||||
@@ -681,24 +692,43 @@ impl EmitCtx {
|
||||
} else {
|
||||
""
|
||||
};
|
||||
let l0 = lane(0);
|
||||
let l1 = lane(1);
|
||||
let l2 = lane(2);
|
||||
let l3 = lane(3);
|
||||
// One decode of `addr` → the attribute vec4. Reused verbatim for the
|
||||
// normal single-vertex read and for each of the three source vertices
|
||||
// of a synthesized rectangle corner (the lane exprs reference `addr`
|
||||
// and `w16` by name, so each `{ let addr = …; }` sub-scope re-decodes).
|
||||
let read_into = |dst: &str, idx_expr: &str| -> String {
|
||||
format!(
|
||||
"{{ let addr = base + ({idx_expr}) * {stride}u + {attr_off}u; \
|
||||
if (addr + {read_bound}u < n) {{ {w16_decl}{dst} = vec4<f32>({l0}, {l1}, {l2}, {l3}); }} }}"
|
||||
)
|
||||
};
|
||||
// RectangleList 4th corner: no backing vertex, so extrapolate the
|
||||
// attribute as v0 + v2 - v1 (parallelogram completion, matching canary).
|
||||
// With the (v0,v1,v2)+(v0,v2,v3) tessellation the synthesized corner is
|
||||
// diagonal to v1 (e.g. TL,TR,BR given → BL = TL+BR-TR). Applies
|
||||
// uniformly to position and every interpolator. `rect_synth`/`rect_base`
|
||||
// come from the VS preamble; false/0 for every non-rect draw.
|
||||
let t0 = read_into("t0", "rect_base + 0u");
|
||||
let t1 = read_into("t1", "rect_base + 1u");
|
||||
let t2 = read_into("t2", "rect_base + 2u");
|
||||
let normal = read_into(&format!("r[{dst_reg}u]"), &format!("u32(r[{src_reg}u].x)"));
|
||||
self.push(&format!(
|
||||
"{{ let endian = {endian_term}; \
|
||||
let vidx = u32(r[{src_reg}u].x); \
|
||||
var base = 0u; \
|
||||
if (draw_ctx.vertex_base_dwords == 0u) {{ \
|
||||
base = (xenos_consts.fetch[{fc0_idx}u] & 0xFFFFFFFCu) >> 2u; \
|
||||
}} \
|
||||
let addr = base + vidx * {stride}u + {attr_off}u; \
|
||||
let n = arrayLength(&vertex_buffer); \
|
||||
if (addr + {read_bound}u < n) {{ \
|
||||
{w16_decl}\
|
||||
r[{dst_reg}u] = vec4<f32>({l0}, {l1}, {l2}, {l3}); \
|
||||
}} }}",
|
||||
if (rect_synth) {{ \
|
||||
var t0 = vec4<f32>(0.0, 0.0, 0.0, 1.0); var t1 = t0; var t2 = t0; \
|
||||
{t0} {t1} {t2} \
|
||||
r[{dst_reg}u] = t0 + t2 - t1; \
|
||||
}} else {{ {normal} }} }}",
|
||||
fc0_idx = const_off,
|
||||
l0 = lane(0),
|
||||
l1 = lane(1),
|
||||
l2 = lane(2),
|
||||
l3 = lane(3),
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user