Nugget
Loading...
Searching...
No Matches
cross-primitive-mask.c
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// Mask-bit set + check behavior across PRIMITIVE TYPES. Phase-2 only
28// tested triangles; this suite extends to rectangles, lines, textured
29// rectangles, and textured triangles at all three depths. Each
30// primitive type goes through its own writer code path in the soft
31// renderer, and the audit (writer-policy refactor track) treats mask
32// behavior as embedded in writers rather than templated. Cross-
33// primitive coverage matters for the refactor.
34//
35// Pattern: pass 1 draws with set-mask (E6=0x01), expecting pixels to
36// land with bit 15 forced. Pass 2 with check-mask (E6=0x02) overlays
37// a second primitive; pixels with bit 15 set should be preserved,
38// pixels with bit 15 clear (or sentinel) should be overwritten.
39//
40// Local sentinel 0x5555 (bit 15 clear) - same workaround as phase-2.
41
42#define MASK_SUITE_SENTINEL 0x5555u
43
45
46static void fillSuiteRegion(int16_t x, int16_t y, int16_t w, int16_t h) {
47 rasterFillRect(x, y, w, h, MASK_SUITE_SENTINEL);
48}
49
50// -- Rectangle (GP0 0x60) --
51
52static void drawRectSet(void) {
53 rasterReset();
54 fillSuiteRegion(0, 0, 16, 8);
55 sendGPUData(0xe6000001u);
56 /* 4x4 RED rect at (0,0). */
57 rasterFlatRect(RASTER_CMD_RED, 0, 0, 4, 4);
58 rasterFlushPrimitive();
59 sendGPUData(0xe6000000u);
60}
61
62static void drawRectSetThenCheck(void) {
63 rasterReset();
64 fillSuiteRegion(0, 0, 16, 8);
65 /* Pass 1: RED rect (0,0,4,4) with set-mask. */
66 sendGPUData(0xe6000001u);
67 rasterFlatRect(RASTER_CMD_RED, 0, 0, 4, 4);
68 rasterFlushPrimitive();
69 /* Pass 2: GREEN rect (2,0,6,4) with check-mask. Overlap region
70 (2..3, 0..3) should be preserved RED-with-mask. Non-overlap
71 (4..7, 0..3) should fill GREEN. */
72 sendGPUData(0xe6000002u);
73 rasterFlatRect(RASTER_CMD_GREEN, 2, 0, 6, 4);
74 rasterFlushPrimitive();
75 sendGPUData(0xe6000000u);
76}
77
78// -- Line (GP0 0x40) --
79
80static void drawLineSet(void) {
81 rasterReset();
82 fillSuiteRegion(0, 0, 16, 4);
83 sendGPUData(0xe6000001u);
84 /* Horizontal RED line from (0,0) to (5,0). */
85 rasterFlatLine(RASTER_CMD_RED, 0, 0, 5, 0);
86 rasterFlushPrimitive();
87 sendGPUData(0xe6000000u);
88}
89
90static void drawLineSetThenCheck(void) {
91 rasterReset();
92 fillSuiteRegion(0, 0, 16, 4);
93 sendGPUData(0xe6000001u);
94 rasterFlatLine(RASTER_CMD_RED, 0, 0, 5, 0);
95 rasterFlushPrimitive();
96 sendGPUData(0xe6000002u);
97 /* Overlay GREEN line (3,0)-(8,0). Overlap pixels (3..5) preserved,
98 new pixels (6..8) filled GREEN. */
99 rasterFlatLine(RASTER_CMD_GREEN, 3, 0, 8, 0);
100 rasterFlushPrimitive();
101 sendGPUData(0xe6000000u);
102}
103
104// -- Textured rectangle (GP0 0x64) - 4-bit CLUT --
105
106// GP0(0x64) variable-size textured rectangle. Word layout:
107// word 0: 0x64xxxxxx (24-bit modulation color)
108// word 1: y << 16 | x (top-left corner)
109// word 2: clut << 16 | (v0 << 8) | u0 (UV at top-left + CLUT)
110// word 3: h << 16 | w (size)
111//
112// Texpage state comes from the LAST GP0(E1), not embedded in the
113// command. So setTexpage() must precede the draw.
114static inline void rasterFlatTexRect(uint32_t cmdColor, int16_t x, int16_t y,
115 int16_t w, int16_t h, uint8_t u0,
116 uint8_t v0, uint16_t clut_field) {
117 waitGPU();
118 GPU_DATA = 0x64000000u | (cmdColor & 0x00ffffffu);
119 GPU_DATA = ((uint32_t)(uint16_t)y << 16) | (uint32_t)(uint16_t)x;
120 GPU_DATA = ((uint32_t)clut_field << 16) |
121 ((uint32_t)v0 << 8) | (uint32_t)u0;
122 GPU_DATA = ((uint32_t)(uint16_t)h << 16) | (uint32_t)(uint16_t)w;
123}
124
125static void drawTexRect4Set(void) {
126 rasterReset();
127 fillSuiteRegion(0, 0, 16, 8);
128 setTexpage(TEX4_TX, TEX4_TY, 0);
129 setTextureWindow(0, 0, 0, 0);
130 sendGPUData(0xe6000001u);
131 /* 4x4 textured rect at (0,0) with UV (1,1)-(4,4) at 4-bit. */
132 rasterFlatTexRect(TEX_MOD_NEUTRAL, 0, 0, 4, 4, 1, 1, CLUT4_FIELD);
133 rasterFlushPrimitive();
134 sendGPUData(0xe6000000u);
135}
136
137// -- Textured triangle (GP0 0x24) - 4-bit CLUT --
138
139static void drawTexTri4Set(void) {
140 rasterReset();
141 fillSuiteRegion(0, 0, 16, 8);
142 setTexpage(TEX4_TX, TEX4_TY, 0);
143 setTextureWindow(0, 0, 0, 0);
144 sendGPUData(0xe6000001u);
145 /* 4-pixel triangle at (0,0)(4,0)(0,4) with UV matching. */
146 rasterTexTri(TEX_MOD_NEUTRAL,
147 0, 0, 1, 1,
148 4, 0, 5, 1,
149 0, 4, 1, 5,
151 rasterFlushPrimitive();
152 sendGPUData(0xe6000000u);
153}
154
155// -- Textured triangle (GP0 0x24) - 8-bit CLUT --
156
157static void drawTexTri8Set(void) {
158 rasterReset();
159 fillSuiteRegion(0, 0, 16, 8);
160 setTexpage(TEX8_TX, TEX8_TY, 1);
161 setTextureWindow(0, 0, 0, 0);
162 sendGPUData(0xe6000001u);
163 rasterTexTri(TEX_MOD_NEUTRAL,
164 0, 0, 1, 1,
165 4, 0, 5, 1,
166 0, 4, 1, 5,
168 rasterFlushPrimitive();
169 sendGPUData(0xe6000000u);
170}
171
172// -- Textured triangle (GP0 0x24) - 15-bit direct --
173
174static void drawTexTri15Set(void) {
175 rasterReset();
176 fillSuiteRegion(0, 0, 16, 8);
177 setTexpage(TEX15_TX, TEX15_TY, 2);
178 setTextureWindow(0, 0, 0, 0);
179 sendGPUData(0xe6000001u);
180 rasterTexTri(TEX_MOD_NEUTRAL,
181 0, 0, 1, 1,
182 4, 0, 5, 1,
183 0, 4, 1, 5,
185 rasterFlushPrimitive();
186 sendGPUData(0xe6000000u);
187}
188
189) // CESTER_BODY
190
191// --------------------------------------------------------------------------
192// Rectangle mask-bit
193// --------------------------------------------------------------------------
194
195CESTER_TEST(rect_mask_set_origin_has_bit15, gpu_raster_phase5,
196 drawRectSet();
197 /* (0,0) drawn RED with mask -> 0x801f. */
198 ASSERT_PIXEL_EQ(0x801fu, 0, 0);
199)
200
201CESTER_TEST(rect_mask_set_interior_has_bit15, gpu_raster_phase5,
202 drawRectSet();
203 ASSERT_PIXEL_EQ(0x801fu, 2, 2);
204)
205
206CESTER_TEST(rect_mask_set_right_edge_excluded, gpu_raster_phase5,
207 drawRectSet();
208 ASSERT_PIXEL_EQ((unsigned)MASK_SUITE_SENTINEL, 4, 0);
209)
210
211CESTER_TEST(rect_mask_check_preserves_red_in_overlap, gpu_raster_phase5,
212 drawRectSetThenCheck();
213 /* (2, 0) in both rects. RED mask-set written first, GREEN
214 check-mask should skip. Expect 0x801f preserved. */
215 ASSERT_PIXEL_EQ(0x801fu, 2, 0);
216)
217
218CESTER_TEST(rect_mask_check_fills_green_in_non_overlap, gpu_raster_phase5,
219 drawRectSetThenCheck();
220 /* (6, 0) only in GREEN rect, sentinel before, mask bit clear,
221 should fill GREEN. */
223)
224
225CESTER_TEST(rect_mask_check_red_untouched_left, gpu_raster_phase5,
226 drawRectSetThenCheck();
227 /* (0, 0) RED-mask only (not in GREEN overlap). Stays 0x801f. */
228 ASSERT_PIXEL_EQ(0x801fu, 0, 0);
229)
230
231// --------------------------------------------------------------------------
232// Line mask-bit
233// --------------------------------------------------------------------------
234
235CESTER_TEST(line_mask_set_start_has_bit15, gpu_raster_phase5,
236 drawLineSet();
237 ASSERT_PIXEL_EQ(0x801fu, 0, 0);
238)
239
240CESTER_TEST(line_mask_set_mid_has_bit15, gpu_raster_phase5,
241 drawLineSet();
242 ASSERT_PIXEL_EQ(0x801fu, 3, 0);
243)
244
245CESTER_TEST(line_mask_set_end_inclusive, gpu_raster_phase5,
246 drawLineSet();
247 /* PSX lines are endpoint-inclusive (verified phase-2). */
248 ASSERT_PIXEL_EQ(0x801fu, 5, 0);
249)
250
251CESTER_TEST(line_mask_check_preserves_overlap, gpu_raster_phase5,
252 drawLineSetThenCheck();
253 ASSERT_PIXEL_EQ(0x801fu, 4, 0);
254)
255
256CESTER_TEST(line_mask_check_fills_new_pixel, gpu_raster_phase5,
257 drawLineSetThenCheck();
259)
260
261// --------------------------------------------------------------------------
262// Textured rectangle mask-bit (4-bit CLUT)
263// --------------------------------------------------------------------------
264
265CESTER_TEST(texrect4_mask_set_origin_has_bit15, gpu_raster_phase5,
266 drawTexRect4Set();
267 /* (0,0) of textured rect samples UV (1,1) -> CLUT[1] from 4-bit
268 fixture = vram555(1, 30, 0) = 0x03C1, with mask bit forced:
269 0x83C1. */
270 uint16_t expected = (uint16_t)(expectedClut4Color(1) | 0x8000);
271 ASSERT_PIXEL_EQ(expected, 0, 0);
272)
273
274CESTER_TEST(texrect4_mask_set_interior_has_bit15, gpu_raster_phase5,
275 drawTexRect4Set();
276 /* (2,2) of rect samples UV (3,3) -> CLUT[3] | 0x8000. */
277 uint16_t expected = (uint16_t)(expectedClut4Color(3) | 0x8000);
278 ASSERT_PIXEL_EQ(expected, 2, 2);
279)
280
281CESTER_TEST(texrect4_mask_set_right_edge_excluded, gpu_raster_phase5,
282 drawTexRect4Set();
283 ASSERT_PIXEL_EQ((unsigned)MASK_SUITE_SENTINEL, 4, 0);
284)
285
286// --------------------------------------------------------------------------
287// Textured triangle mask-bit at all three depths
288// --------------------------------------------------------------------------
289
290CESTER_TEST(textri4_mask_set_origin, gpu_raster_phase5,
291 drawTexTri4Set();
292 /* At screen (0,0), UV (1,1), CLUT[1] | 0x8000. */
293 uint16_t expected = (uint16_t)(expectedClut4Color(1) | 0x8000);
294 ASSERT_PIXEL_EQ(expected, 0, 0);
295)
296
297CESTER_TEST(textri4_mask_set_interior, gpu_raster_phase5,
298 drawTexTri4Set();
299 uint16_t expected = (uint16_t)(expectedClut4Color(2) | 0x8000);
300 ASSERT_PIXEL_EQ(expected, 1, 1);
301)
302
303CESTER_TEST(textri4_mask_set_right_edge_excluded, gpu_raster_phase5,
304 drawTexTri4Set();
305 ASSERT_PIXEL_EQ((unsigned)MASK_SUITE_SENTINEL, 4, 0);
306)
307
308CESTER_TEST(textri8_mask_set_origin, gpu_raster_phase5,
309 drawTexTri8Set();
310 uint16_t expected = (uint16_t)(expectedClut8Color(1) | 0x8000);
311 ASSERT_PIXEL_EQ(expected, 0, 0);
312)
313
314CESTER_TEST(textri8_mask_set_interior, gpu_raster_phase5,
315 drawTexTri8Set();
316 uint16_t expected = (uint16_t)(expectedClut8Color(2) | 0x8000);
317 ASSERT_PIXEL_EQ(expected, 1, 1);
318)
319
320CESTER_TEST(textri15_mask_set_origin, gpu_raster_phase5,
321 drawTexTri15Set();
322 /* 15-bit direct: texel at UV (1,1) is vram555(1, 1, 2) | 0x8000. */
323 uint16_t expected = (uint16_t)(expectedTex15Color(1, 1) | 0x8000);
324 ASSERT_PIXEL_EQ(expected, 0, 0);
325)
326
327CESTER_TEST(textri15_mask_set_interior, gpu_raster_phase5,
328 drawTexTri15Set();
329 uint16_t expected = (uint16_t)(expectedTex15Color(2, 2) | 0x8000);
330 ASSERT_PIXEL_EQ(expected, 1, 1);
331)
332
333CESTER_TEST(textri15_mask_set_right_edge_excluded, gpu_raster_phase5,
334 drawTexTri15Set();
335 ASSERT_PIXEL_EQ((unsigned)MASK_SUITE_SENTINEL, 4, 0);
336)
CESTER_BODY(static int s_got40;static int s_got80;static uint32_t s_cause;static uint32_t s_epc;static uint32_t s_from;static uint32_t *s_resume;static uint32_t *s_regs;static uint32_t(*s_customhandler)()=NULL;static uint32_t s_oldIMASK;static uint32_t s_oldDPCR;static uint32_t s_oldDICR;uint32_t handler(uint32_t *regs, uint32_t from) { if(from==0x40) s_got40=1;if(from==0x80) s_got80=1;uint32_t cause;uint32_t epc;s_from=from;asm("mfc0 %0, $13\nnop\nmfc0 %1, $14\nnop" :"=r"(cause), "=r"(epc));s_cause=cause;s_epc=epc;if(s_customhandler) { return s_customhandler();} else { return s_resume ?((uint32_t) s_resume) :(epc+4);} } void installExceptionHandlers(uint32_t(*handler)(uint32_t *regs, uint32_t from));void uninstallExceptionHandlers();uint32_t branchbranch1();uint32_t branchbranch2();uint32_t jumpjump1();uint32_t jumpjump2();uint32_t cpu_LWR_LWL_half(uint32_t buff[], uint32_t initial);uint32_t cpu_LWR_LWL_nodelay(uint32_t buff[], uint32_t initial);uint32_t cpu_LWR_LWL_delayed(uint32_t buff[], uint32_t initial);uint32_t cpu_LWR_LWL_load_different(uint32_t buff[], uint32_t initial);uint32_t cpu_LW_LWR(uint32_t buff[], uint32_t initial);uint32_t cpu_delayed_load(uint32_t buff[], uint32_t override);uint32_t cpu_delayed_load_cancelled(uint32_t buff[], uint32_t override);uint64_t cpu_delayed_load_load(uint32_t buff[], uint32_t override);uint32_t linkandload();uint32_t lwandlink();uint32_t nolink();static int s_interruptsWereEnabled;) CESTER_BEFORE_EACH(cpu_tests
CESTER_TEST(cpu_cop0_basic_write_bp, cpu_tests, uint32_t expectedEPC;uint32_t t;volatile uint32_t *ptr=(volatile uint32_t *) 0x58; *ptr=1;__asm__ volatile("" " lui %0, 0b1100101010000000\n" " mtc0 %0, $7\n" " li %0, 0x58\n" " mtc0 %0, $5\n" " li %0, 0xfffffff0\n" " mtc0 %0, $9\n" :"=r"(t));cester_assert_uint_eq(1, *ptr);__asm__ volatile("la %0, 1f\n1:\nsw $0, 0x58($0)" :"=r"(expectedEPC));__asm__ volatile("mtc0 $0, $7\n");cester_assert_uint_eq(0, *ptr);cester_assert_uint_eq(1, s_got40);cester_assert_uint_eq(0, s_got80);cester_assert_uint_eq(0x40, s_from);cester_assert_uint_eq(expectedEPC, s_epc);) CESTER_TEST(cpu_cop0_kseg_write_bp
#define MASK_SUITE_SENTINEL
Definition cross-primitive-mask.c:42
sendGPUData(0xe1000000)
#define GPU_DATA
Definition hwregs.h:52
#define RASTER_VRAM_GREEN
Definition raster-helpers.h:126
#define RASTER_CMD_RED
Definition raster-helpers.h:123
#define RASTER_CMD_GREEN
Definition raster-helpers.h:125
#define ASSERT_PIXEL_EQ(expected, x_, y_)
Definition raster-helpers.h:472
void uint32_t(classId, spec)
#define CLUT8_FIELD
Definition texture-fixtures.h:84
#define TEX15_TY
Definition texture-fixtures.h:66
#define TEX8_TY
Definition texture-fixtures.h:61
#define CLUT4_FIELD
Definition texture-fixtures.h:83
#define TEX15_TPAGE
Definition texture-fixtures.h:104
#define TEX8_TPAGE
Definition texture-fixtures.h:103
#define CLUT15_FIELD
Definition texture-fixtures.h:85
#define TEX15_TX
Definition texture-fixtures.h:65
#define TEX8_TX
Definition texture-fixtures.h:60
#define TEX_MOD_NEUTRAL
Definition texture-fixtures.h:328
#define TEX4_TY
Definition texture-fixtures.h:56
#define TEX4_TX
Definition texture-fixtures.h:55
#define TEX4_TPAGE
Definition texture-fixtures.h:102