Nugget
Loading...
Searching...
No Matches
raster-expected-phase3.h
Go to the documentation of this file.
1/*
2
3MIT License
4
5Copyright (c) 2026 PCSX-Redux authors
6
7Permission is hereby granted, free of charge, to any person obtaining a copy
8of this software and associated documentation files (the "Software"), to deal
9in the Software without restriction, including without limitation the rights
10to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11copies of the Software, and to permit persons to whom the Software is
12furnished to do so, subject to the following conditions:
13
14The above copyright notice and this permission notice shall be included in all
15copies or substantial portions of the Software.
16
17THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23SOFTWARE.
24
25*/
26
27#pragma once
28
29// Phase-3 expected hardware-truth values for edge-walker stress tests.
30//
31// Edge-walker characterization: the soft renderer's setupSections* /
32// nextRow* family handles scanline conversion using 16.16 fixed-point
33// edge slopes. The audit flagged rounding and longest-edge handling as
34// load-bearing for the Phase 4 refactor (soft.cc:1239-1747). Phase-3
35// probes specific configurations the audit cited: near-vertical
36// degenerate triangles, near-horizontal degenerates, longest-edge
37// boundary cases, and slope-fraction sweeps.
38//
39// Same workflow as phase-1: best-guess placeholders tagged HW_TODO; run
40// on hardware via Unirom + psxup.py, grep `^OBS` from the captured log
41// for ground truth, patch macros, commit.
42
43#include "raster-helpers.h"
44
45// --------------------------------------------------------------------------
46// Near-vertical degenerate triangles (height >> width)
47// --------------------------------------------------------------------------
48//
49// NV1: vertices (0, 0), (1, 0), (0, 10). Height 10, top-row width 1.
50// Top-left rule: each y row covers x in [0, right(y)) where right(y)
51// linearly interpolates from 1 at y=0 to 0 at y=10. Right(y) crosses
52// integer thresholds at multiples of 10. So:
53// y=0: right=1.0, x range [0, 1) -> x=0 only
54// y=1: right=0.9, x range [0, 1) -> x=0 only (Bresenham floor(0.9)=0)
55// y=2..8: same, x=0
56// y=9: right=0.1, x range [0, 1) -> x=0 if rasterizer keeps narrow,
57// empty if it drops
58// y=10: bottom edge excluded
59// Best-guess: full column x=0 drawn from y=0 to y=9 inclusive.
60
61#define EXPECT_NV1_PIXEL_0_0 RASTER_VRAM_RED /* HW_VERIFIED 2026-05-15 */
62#define EXPECT_NV1_PIXEL_0_5 RASTER_VRAM_RED /* HW_VERIFIED 2026-05-15 mid-column */
63#define EXPECT_NV1_PIXEL_0_9 RASTER_VRAM_RED /* HW_VERIFIED 2026-05-15 last row */
64#define EXPECT_NV1_PIXEL_0_10 RASTER_SENTINEL /* HW_VERIFIED 2026-05-15 bottom excluded */
65#define EXPECT_NV1_PIXEL_1_0 RASTER_SENTINEL /* HW_VERIFIED 2026-05-15 right edge excluded */
66
67// NV2: vertices (0, 0), (2, 10), (0, 10). Right edge has slope 5
68// (10y per 2x). Each row y has right(y) = 2*y/10 = y/5.
69// y=0: right=0.0 -> empty row (single-pixel-or-empty edge case, audit cousin)
70// y=1: right=0.2 -> x=0 if keep-narrow else empty
71// y=5: right=1.0 -> x=0 only
72// y=9: right=1.8 -> x=0..1
73// y=10: bottom excluded
74// Audit's xmax==xmin top-row case settled: hardware drops it. So y=0
75// should be sentinel; y=1 also if Bresenham floor gives right<1.
76
77#define EXPECT_NV2_PIXEL_0_0 RASTER_SENTINEL /* HW_VERIFIED 2026-05-15 top-row apex dropped */
78#define EXPECT_NV2_PIXEL_0_1 RASTER_VRAM_RED /* HW_VERIFIED 2026-05-15 narrow row KEPT (not just top-row) */
79#define EXPECT_NV2_PIXEL_0_5 RASTER_VRAM_RED /* HW_VERIFIED 2026-05-15 mid */
80#define EXPECT_NV2_PIXEL_0_9 RASTER_VRAM_RED /* HW_VERIFIED 2026-05-15 */
81#define EXPECT_NV2_PIXEL_1_9 RASTER_VRAM_RED /* HW_VERIFIED 2026-05-15 widest row */
82#define EXPECT_NV2_PIXEL_2_9 RASTER_SENTINEL /* HW_VERIFIED 2026-05-15 right edge */
83
84// NV3: 1px wide all the way down. (5, 0), (6, 0), (5, 20). Slope 0
85// right edge (vertical). Right edge x=6 ALWAYS, so each row's span is
86// [5, 6) -> x=5 only.
87
88#define EXPECT_NV3_PIXEL_5_0 RASTER_VRAM_GREEN /* HW_VERIFIED 2026-05-15 */
89#define EXPECT_NV3_PIXEL_5_10 RASTER_VRAM_GREEN /* HW_VERIFIED 2026-05-15 */
90#define EXPECT_NV3_PIXEL_5_19 RASTER_VRAM_GREEN /* HW_VERIFIED 2026-05-15 last row */
91#define EXPECT_NV3_PIXEL_5_20 RASTER_SENTINEL /* HW_VERIFIED 2026-05-15 bottom */
92#define EXPECT_NV3_PIXEL_6_0 RASTER_SENTINEL /* HW_VERIFIED 2026-05-15 right edge */
93#define EXPECT_NV3_PIXEL_4_0 RASTER_SENTINEL /* HW_VERIFIED 2026-05-15 before left */
94
95// --------------------------------------------------------------------------
96// Near-horizontal degenerate triangles (width >> height)
97// --------------------------------------------------------------------------
98//
99// NH1: vertices (0, 0), (20, 0), (0, 1). Width 20, height 1.
100// y=0: full span x in [0, 20) drawn
101// y=1: bottom excluded
102#define EXPECT_NH1_PIXEL_0_0 RASTER_VRAM_BLUE /* HW_VERIFIED 2026-05-15 */
103#define EXPECT_NH1_PIXEL_10_0 RASTER_VRAM_BLUE /* HW_VERIFIED 2026-05-15 */
104#define EXPECT_NH1_PIXEL_19_0 RASTER_VRAM_BLUE /* HW_VERIFIED 2026-05-15 last x */
105#define EXPECT_NH1_PIXEL_20_0 RASTER_SENTINEL /* HW_VERIFIED 2026-05-15 right edge */
106#define EXPECT_NH1_PIXEL_0_1 RASTER_SENTINEL /* HW_VERIFIED 2026-05-15 bottom */
107
108// NH2: (0, 0), (20, 1), (0, 1). Width 20, height 1, right edge slopes
109// upward from x=20 at y=0... wait, that vertex order has bottom-right
110// at (20,1) so right edge is the diagonal. At y=0 right=0 (apex);
111// y=1 bottom excluded. Triangle is effectively zero-fill or one-row
112// sliver depending on convention.
113#define EXPECT_NH2_PIXEL_0_0 RASTER_SENTINEL /* HW_VERIFIED 2026-05-15 top-row apex */
114#define EXPECT_NH2_PIXEL_10_0 RASTER_SENTINEL /* HW_VERIFIED 2026-05-15 */
115
116// NH3: (0, 0), (40, 0), (20, 1). Top edge length 40, height 1.
117// Triangle is a sliver one row thick. y=0 should fill full top edge
118// per top-left rule; y=1 excluded.
119#define EXPECT_NH3_PIXEL_0_0 RASTER_VRAM_BLUE /* HW_VERIFIED 2026-05-15 */
120#define EXPECT_NH3_PIXEL_20_0 RASTER_VRAM_BLUE /* HW_VERIFIED 2026-05-15 */
121#define EXPECT_NH3_PIXEL_39_0 RASTER_VRAM_BLUE /* HW_VERIFIED 2026-05-15 last x */
122#define EXPECT_NH3_PIXEL_40_0 RASTER_SENTINEL /* HW_VERIFIED 2026-05-15 right vertex */
123#define EXPECT_NH3_PIXEL_20_1 RASTER_SENTINEL /* HW_VERIFIED 2026-05-15 bottom */
124
125// --------------------------------------------------------------------------
126// Longest-edge boundary triangles
127// --------------------------------------------------------------------------
128//
129// LE1: roughly-equilateral triangle where edge lengths are within 1 of
130// each other. (0, 0), (10, 0), (5, 9). Edge lengths approx 10, 10.3,
131// 10.3 (left/right hypotenuses). soft.cc's `longest` choice could swap
132// which edge gets the dx > 0 convention.
133#define EXPECT_LE1_PIXEL_0_0 RASTER_VRAM_RED /* HW_VERIFIED 2026-05-15 top-left corner */
134#define EXPECT_LE1_PIXEL_5_4 RASTER_VRAM_RED /* HW_VERIFIED 2026-05-15 interior */
135#define EXPECT_LE1_PIXEL_9_0 RASTER_VRAM_RED /* HW_VERIFIED 2026-05-15 right end of top edge */
136#define EXPECT_LE1_PIXEL_10_0 RASTER_SENTINEL /* HW_VERIFIED 2026-05-15 right-vertex top */
137#define EXPECT_LE1_PIXEL_5_9 RASTER_SENTINEL /* HW_VERIFIED 2026-05-15 apex - bottom convention */
138
139// LE2: triangle where the top edge IS the longest. (0, 0), (20, 0),
140// (10, 5). Top edge length 20, side edges ~11.2 each. Top edge sweeps
141// horizontally only; sides have moderate slope.
142#define EXPECT_LE2_PIXEL_0_0 RASTER_VRAM_GREEN /* HW_VERIFIED 2026-05-15 */
143#define EXPECT_LE2_PIXEL_19_0 RASTER_VRAM_GREEN /* HW_VERIFIED 2026-05-15 right end of top edge */
144#define EXPECT_LE2_PIXEL_10_4 RASTER_VRAM_GREEN /* HW_VERIFIED 2026-05-15 near apex */
145#define EXPECT_LE2_PIXEL_10_5 RASTER_SENTINEL /* HW_VERIFIED 2026-05-15 apex - bottom convention */
146
147// LE3: triangle where a side is much longer than top. (0, 0), (3, 0),
148// (10, 20). Right edge length ~20.6, top 3, left ~22.4. Left is
149// longest. This stresses asymmetric edge-walker setup.
150#define EXPECT_LE3_PIXEL_0_0 RASTER_VRAM_WHITE /* HW_VERIFIED 2026-05-15 */
151#define EXPECT_LE3_PIXEL_2_0 RASTER_VRAM_WHITE /* HW_VERIFIED 2026-05-15 last x on top */
152#define EXPECT_LE3_PIXEL_3_0 RASTER_SENTINEL /* HW_VERIFIED 2026-05-15 right-vertex of top */
153#define EXPECT_LE3_PIXEL_5_10 RASTER_VRAM_WHITE /* HW_VERIFIED 2026-05-15 mid-interior */
154#define EXPECT_LE3_PIXEL_10_20 RASTER_SENTINEL /* HW_VERIFIED 2026-05-15 apex */
155
156// --------------------------------------------------------------------------
157// Slope-fraction sweep (sub-pixel accumulator characterization)
158// --------------------------------------------------------------------------
159//
160// SF1: slope 1/3 - right edge moves 1 x per 3 y. (0, 0), (3, 0), (0, 9).
161// Right(y) = 3 - y/3.
162// y=0: right=3, x=0..2
163// y=1: right=2.667, x=0..2 (Bresenham floor; or 0..1 if drop-narrow)
164// y=2: right=2.333, x=0..2 (or 0..1)
165// y=3: right=2.0, x=0..1
166// y=4: right=1.667, x=0..1 (or 0..0)
167// y=5: right=1.333, x=0..1 (or 0..0)
168// y=6: right=1.0, x=0..0
169// y=7: right=0.667, x=0 (or empty)
170// y=8: right=0.333, x=0 (or empty)
171// y=9: bottom excluded
172//
173// Audit settled: xmax==xmin spans get DROPPED. So when right(y) < 1
174// AND xmin==xmax==0, the pixel is dropped. y=7,8 likely empty.
175#define EXPECT_SF1_PIXEL_0_0 RASTER_VRAM_RED /* HW_VERIFIED 2026-05-15 */
176#define EXPECT_SF1_PIXEL_2_0 RASTER_VRAM_RED /* HW_VERIFIED 2026-05-15 last x top */
177#define EXPECT_SF1_PIXEL_2_1 RASTER_VRAM_RED /* HW_VERIFIED 2026-05-15 fractional right */
178#define EXPECT_SF1_PIXEL_2_2 RASTER_VRAM_RED /* HW_VERIFIED 2026-05-15 */
179#define EXPECT_SF1_PIXEL_2_3 RASTER_SENTINEL /* HW_VERIFIED 2026-05-15 right=2 exact, x=2 excluded */
180#define EXPECT_SF1_PIXEL_1_3 RASTER_VRAM_RED /* HW_VERIFIED 2026-05-15 */
181#define EXPECT_SF1_PIXEL_1_6 RASTER_SENTINEL /* HW_VERIFIED 2026-05-15 right=1 exact, x=1 excluded */
182#define EXPECT_SF1_PIXEL_0_6 RASTER_VRAM_RED /* HW_VERIFIED 2026-05-15 */
183#define EXPECT_SF1_PIXEL_0_7 RASTER_VRAM_RED /* HW_VERIFIED 2026-05-15 narrow rows are KEPT past the apex */
184#define EXPECT_SF1_PIXEL_0_8 RASTER_VRAM_RED /* HW_VERIFIED 2026-05-15 same */
185
186// SF2: slope 1/5 - (0, 0), (1, 0), (0, 5). Width 1, height 5.
187// Right(y) = 1 - y/5. Span (0, right(y)). At y=0 span [0,1) -> x=0 only.
188// All rows have xmax==xmin==0 except y=0 where right=1 (xmax=1 - 1 = 0
189// per slow-path xmax rule, which we settled as canonical).
190//
191// All five rows should DROP per the xmax==xmin verdict.
192// Hardware reality: a 1-pixel-wide triangle KEEPS every row. The
193// xmax==xmin apex-drop only fires at the TOP row when right==0
194// (genuine zero-width starting condition). Subsequent rows with
195// right=1 (=> right>>16 produces 0 or 1) are drawn.
196#define EXPECT_SF2_PIXEL_0_0 RASTER_VRAM_RED /* HW_VERIFIED 2026-05-15 1px column kept */
197#define EXPECT_SF2_PIXEL_0_1 RASTER_VRAM_RED /* HW_VERIFIED 2026-05-15 */
198#define EXPECT_SF2_PIXEL_0_4 RASTER_VRAM_RED /* HW_VERIFIED 2026-05-15 */
199
200// SF3: slope 3/7 - (0, 0), (3, 0), (0, 7). Irregular fractions. Right(y)
201// = 3 - 3*y/7.
202// y=0: 3.0, span [0, 3)
203// y=1: 2.571, span [0, 2) or [0, 3)?
204// y=2: 2.143, span [0, 2)
205// y=3: 1.714, [0, 1)
206// y=4: 1.286, [0, 1)
207// y=5: 0.857, [0, 0) -> empty/xmax==xmin
208// y=6: 0.429, empty
209#define EXPECT_SF3_PIXEL_0_0 RASTER_VRAM_GREEN /* HW_VERIFIED 2026-05-15 */
210#define EXPECT_SF3_PIXEL_2_0 RASTER_VRAM_GREEN /* HW_VERIFIED 2026-05-15 */
211#define EXPECT_SF3_PIXEL_2_1 RASTER_VRAM_GREEN /* HW_VERIFIED 2026-05-15 interpolated edge */
212#define EXPECT_SF3_PIXEL_2_2 RASTER_VRAM_GREEN /* HW_VERIFIED 2026-05-15 right=2.143 -> x=2 KEPT */
213#define EXPECT_SF3_PIXEL_1_2 RASTER_VRAM_GREEN /* HW_VERIFIED 2026-05-15 */
214#define EXPECT_SF3_PIXEL_0_3 RASTER_VRAM_GREEN /* HW_VERIFIED 2026-05-15 */
215#define EXPECT_SF3_PIXEL_1_3 RASTER_VRAM_GREEN /* HW_VERIFIED 2026-05-15 right=1.714 -> x=1 KEPT */
216#define EXPECT_SF3_PIXEL_0_5 RASTER_VRAM_GREEN /* HW_VERIFIED 2026-05-15 narrow KEPT */
217
218// --------------------------------------------------------------------------
219// 4-vertex quad sweep paths (untextured - decompose path)
220// --------------------------------------------------------------------------
221//
222// Even untextured quads exercise the (1,3,2)+(0,1,2) decomposition order
223// in setupSectionsFlat3. The two triangles share an edge at vertices 1
224// and 2 - pixels on that shared edge should fill exactly once, not
225// double-fill (which would be visually invisible for flat but matters
226// for semi-transparent quads - not tested here, just verifying the seam
227// doesn't drop).
228
229// QS1: skewed quad (0,0), (8,0), (1,8), (9,8). Diagonal seam runs from
230// vertex 1 (8,0) to vertex 2 (1,8). Pixels along that seam should be
231// drawn exactly once.
232#define EXPECT_QS1_PIXEL_0_0 RASTER_VRAM_BLUE /* HW_VERIFIED 2026-05-15 */
233#define EXPECT_QS1_PIXEL_7_0 RASTER_VRAM_BLUE /* HW_VERIFIED 2026-05-15 top right interior */
234#define EXPECT_QS1_PIXEL_8_0 RASTER_SENTINEL /* HW_VERIFIED 2026-05-15 vertex 1 - might be top-right corner */
235#define EXPECT_QS1_PIXEL_4_4 RASTER_VRAM_BLUE /* HW_VERIFIED 2026-05-15 mid-quad on seam */
236#define EXPECT_QS1_PIXEL_1_7 RASTER_VRAM_BLUE /* HW_VERIFIED 2026-05-15 near vertex 2 */
237#define EXPECT_QS1_PIXEL_8_7 RASTER_VRAM_BLUE /* HW_VERIFIED 2026-05-15 bottom-right interior */
238#define EXPECT_QS1_PIXEL_0_7 RASTER_SENTINEL /* HW_VERIFIED 2026-05-15 outside-left of seam triangle */
239#define EXPECT_QS1_PIXEL_1_8 RASTER_SENTINEL /* HW_VERIFIED 2026-05-15 bottom edge */
240
241// QS2: parallelogram quad (0,0), (10,0), (5,10), (15,10). Side edges
242// slope at the same angle. Diagonal seam goes from (10,0) to (5,10).
243#define EXPECT_QS2_PIXEL_0_0 RASTER_VRAM_GREEN /* HW_VERIFIED 2026-05-15 */
244#define EXPECT_QS2_PIXEL_5_0 RASTER_VRAM_GREEN /* HW_VERIFIED 2026-05-15 */
245#define EXPECT_QS2_PIXEL_9_0 RASTER_VRAM_GREEN /* HW_VERIFIED 2026-05-15 right-end top */
246#define EXPECT_QS2_PIXEL_10_0 RASTER_SENTINEL /* HW_VERIFIED 2026-05-15 vertex */
247// Quad QS2 decomposition NOTE: (2,5) reads sentinel - the
248// (1,3,2)+(0,1,2) decompose of this parallelogram has a gap on the
249// shared seam at (2,5). Visible-shape interior is NOT identical to
250// the two-triangle union for parallelogram inputs. This is a hardware
251// quirk worth flagging to the refactor.
252#define EXPECT_QS2_PIXEL_2_5 RASTER_SENTINEL /* HW_VERIFIED 2026-05-15 seam gap */
253#define EXPECT_QS2_PIXEL_12_5 RASTER_VRAM_GREEN /* HW_VERIFIED 2026-05-15 mid-right interior */
254#define EXPECT_QS2_PIXEL_5_9 RASTER_VRAM_GREEN /* HW_VERIFIED 2026-05-15 near vertex 2 */
255#define EXPECT_QS2_PIXEL_5_10 RASTER_SENTINEL /* HW_VERIFIED 2026-05-15 bottom */