Nugget
Loading...
Searching...
No Matches
texture-fixtures.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// Texture + CLUT fixtures for the gpu-raster phase-4 textured tests.
30//
31// VRAM layout (lower half draw zone left untouched, textures live to the
32// right):
33//
34// x=0..256, y=0..256 test draw area (uses RASTER_DRAW_AREA default)
35// x=512..575, y=0..255 4-bit CLUT texpage cell (tx=8, ty=0)
36// actual VRAM footprint: x=512..527 (16 wide)
37// carries 16 logical texels per word, 64 logical
38// texels per row
39// x=576..639, y=0..255 8-bit CLUT texpage cell (tx=9, ty=0)
40// actual VRAM footprint: x=576..607 (32 wide)
41// carries 32 logical texels per word
42// x=640..703, y=0..255 15-bit direct texpage cell (tx=10, ty=0)
43// actual VRAM footprint: x=640..703 (64 wide)
44// 1 logical texel per pixel
45// x=512..527, y=256 4-bit CLUT (16 entries)
46// x=512..767, y=257 8-bit CLUT (256 entries)
47//
48// Texture data is a U,V coordinate pattern so each texel encodes a
49// deterministic function of its address - lets tests assert "this draw
50// sampled position (U,V)" by reading back specific VRAM pixels.
51
52#include "raster-helpers.h"
53
54// Texpage cell base coords (in actual VRAM pixels, units of 64).
55#define TEX4_TX 8u
56#define TEX4_TY 0u
57#define TEX4_VRAM_BASE_X (TEX4_TX * 64) /* 512 */
58#define TEX4_VRAM_BASE_Y (TEX4_TY * 256) /* 0 */
59
60#define TEX8_TX 9u
61#define TEX8_TY 0u
62#define TEX8_VRAM_BASE_X (TEX8_TX * 64) /* 576 */
63#define TEX8_VRAM_BASE_Y (TEX8_TY * 256) /* 0 */
64
65#define TEX15_TX 10u
66#define TEX15_TY 0u
67#define TEX15_VRAM_BASE_X (TEX15_TX * 64) /* 640 */
68#define TEX15_VRAM_BASE_Y (TEX15_TY * 256) /* 0 */
69
70// CLUT locations. CLUT X must be 16-aligned (the CLUT field stores
71// CLUT_X >> 4 in bits 0-5 of the textured-primitive command word).
72#define CLUT4_VRAM_X 512u
73#define CLUT4_VRAM_Y 256u
74#define CLUT8_VRAM_X 512u
75#define CLUT8_VRAM_Y 257u
76
77// Encode the GP0 CLUT field used in textured-primitive command words.
78// bits 0-5 = clut_x >> 4, bits 6-14 = clut_y.
79static inline uint16_t clutField(uint16_t cx, uint16_t cy) {
80 return (uint16_t)(((cx & 0x3f0) >> 4) | ((cy & 0x1ff) << 6));
81}
82
83#define CLUT4_FIELD clutField(CLUT4_VRAM_X, CLUT4_VRAM_Y)
84#define CLUT8_FIELD clutField(CLUT8_VRAM_X, CLUT8_VRAM_Y)
85#define CLUT15_FIELD 0u /* 15-bit direct ignores CLUT */
86
87// Encode the texpage field used in textured-primitive command words.
88// bits 0-3 = tx, bit 4 = ty, bits 5-6 = ABR (semi-trans mode), bits 7-8
89// = depth (0=4bit, 1=8bit, 2=15bit), bit 9 = (reserved/dither in E1).
90// For the polygon command embedded-texpage field the layout differs
91// slightly - see GP0 polygon command word format for the textured
92// variant.
93//
94// We separately issue an explicit GP0(E1) before each draw to set the
95// texpage so tests don't depend on stale state.
96static inline uint16_t texpageField(uint16_t tx, uint16_t ty,
97 uint16_t abr, uint16_t depth) {
98 return (uint16_t)((tx & 0xf) | ((ty & 1) << 4) |
99 ((abr & 3) << 5) | ((depth & 3) << 7));
100}
101
102#define TEX4_TPAGE texpageField(TEX4_TX, TEX4_TY, 0, 0)
103#define TEX8_TPAGE texpageField(TEX8_TX, TEX8_TY, 0, 1)
104#define TEX15_TPAGE texpageField(TEX15_TX, TEX15_TY, 0, 2)
105
106// Send GP0(E1) to set the current texpage state. abr=0 (opaque), no
107// dither, draw-to-display allowed (bit 10 = 1).
108static inline void setTexpage(uint16_t tx, uint16_t ty, uint16_t depth) {
109 uint32_t cmd = 0xe1000000u |
110 ((tx & 0xf)) |
111 ((ty & 1) << 4) |
112 (0u << 5) | /* abr = 0 */
113 ((depth & 3) << 7) |
114 (0u << 9) | /* dither off */
115 (1u << 10); /* drawing to display area allowed */
117}
118
119// Variant that takes an explicit ABR field. Use for textured-rect
120// semi-trans tests where rasterSetAbr() would inadvertently reset the
121// texpage to (tx=0, ty=0) - rect commands carry no embedded tpage
122// word, so the current E1 state is load-bearing.
123static inline void setTexpageAbr(uint16_t tx, uint16_t ty, uint16_t depth,
124 uint16_t abr) {
125 uint32_t cmd = 0xe1000000u |
126 ((tx & 0xf)) |
127 ((ty & 1) << 4) |
128 ((abr & 3) << 5) |
129 ((depth & 3) << 7) |
130 (0u << 9) |
131 (1u << 10);
133}
134
135// Send GP0(E2) to set texture window. mask_x/mask_y are 5-bit "mask
136// after >>3" values (so logical mask = mask*8). offset_x/offset_y same.
137static inline void setTextureWindow(uint8_t mask_x, uint8_t mask_y,
138 uint8_t off_x, uint8_t off_y) {
139 uint32_t cmd = 0xe2000000u |
140 ((uint32_t)(mask_x & 0x1f)) |
141 ((uint32_t)(mask_y & 0x1f) << 5) |
142 ((uint32_t)(off_x & 0x1f) << 10) |
143 ((uint32_t)(off_y & 0x1f) << 15);
145}
146
147// --------------------------------------------------------------------------
148// Fixture upload
149// --------------------------------------------------------------------------
150
151// 4-bit CLUT: 16 entries, each entry = 16-bit 5:5:5 VRAM pixel.
152// Palette[i] = vram555(i, 31-i, 0) so each index produces a unique
153// recognizable color. 0 -> (0, 31, 0) green-ish, 15 -> (15, 16, 0)
154// orange-ish.
155static inline void uploadClut4(void) {
156 uint16_t clut[16];
157 for (int i = 0; i < 16; i++) {
158 clut[i] = rasterVram555((uint8_t)i, (uint8_t)(31 - i), 0);
159 }
160 waitGPU();
161 GPU_DATA = 0xa0000000u;
162 GPU_DATA = ((uint32_t)(uint16_t)CLUT4_VRAM_Y << 16) |
163 (uint32_t)(uint16_t)CLUT4_VRAM_X;
164 GPU_DATA = ((uint32_t)(uint16_t)1 << 16) | (uint32_t)(uint16_t)16;
165 /* 16 pixels = 8 words */
166 for (int i = 0; i < 8; i++) {
167 GPU_DATA = (uint32_t)clut[i * 2] |
168 ((uint32_t)clut[i * 2 + 1] << 16);
169 }
170}
171
172// 8-bit CLUT: 256 entries. Same palette function but extended:
173// palette[i] = vram555(i & 0x1f, (255 - i) & 0x1f, ((i >> 5) & 0x1f)).
174static inline void uploadClut8(void) {
175 uint16_t clut[256];
176 for (int i = 0; i < 256; i++) {
177 clut[i] = rasterVram555((uint8_t)(i & 0x1f),
178 (uint8_t)((255 - i) & 0x1f),
179 (uint8_t)((i >> 5) & 0x1f));
180 }
181 waitGPU();
182 GPU_DATA = 0xa0000000u;
183 GPU_DATA = ((uint32_t)(uint16_t)CLUT8_VRAM_Y << 16) |
184 (uint32_t)(uint16_t)CLUT8_VRAM_X;
185 GPU_DATA = ((uint32_t)(uint16_t)1 << 16) | (uint32_t)(uint16_t)256;
186 for (int i = 0; i < 128; i++) {
187 GPU_DATA = (uint32_t)clut[i * 2] |
188 ((uint32_t)clut[i * 2 + 1] << 16);
189 }
190}
191
192// 4-bit texture upload. The texpage cell at (TEX4_VRAM_BASE_X,
193// TEX4_VRAM_BASE_Y) holds 16 VRAM-pixels worth of data per row; each
194// VRAM pixel encodes 4 logical 4-bit texels. We upload a small region
195// 16 VRAM-pixels wide and 16 rows tall, encoding texel(u, v) = u & 0x0f.
196//
197// So:
198// u=0..15: texel = u
199// u=16..31: same pattern repeats (16 texels per VRAM-pixel-pattern)
200//
201// Width: 16 logical texels means 4 actual VRAM pixels per row.
202// We upload a 4-pixel-wide x 16-row block.
203static inline void uploadTex4(void) {
204 waitGPU();
205 GPU_DATA = 0xa0000000u;
206 GPU_DATA = ((uint32_t)(uint16_t)TEX4_VRAM_BASE_Y << 16) |
207 (uint32_t)(uint16_t)TEX4_VRAM_BASE_X;
208 GPU_DATA = ((uint32_t)(uint16_t)16 << 16) | (uint32_t)(uint16_t)4;
209 /* Each VRAM pixel encodes 4 4-bit texels: bits 0-3 = texel u%4=0,
210 bits 4-7 = u%4=1, bits 8-11 = u%4=2, bits 12-15 = u%4=3.
211 Pixel at (px, v) encodes texels (px*4..px*4+3, v).
212 Texel value = u & 0x0f. So pixel(px, v) = px*4 | ((px*4+1)<<4) |
213 ((px*4+2)<<8) | ((px*4+3)<<12) - except all u values are mod 16,
214 so for px=0..3 (covering u=0..15) we just get the linear pattern. */
215 for (int v = 0; v < 16; v++) {
216 /* 4 pixels per row = 2 words */
217 /* px=0: u=0,1,2,3 -> 0x3210 */
218 /* px=1: u=4,5,6,7 -> 0x7654 */
219 GPU_DATA = 0x76543210u;
220 /* px=2: u=8..11 -> 0xba98 */
221 /* px=3: u=12..15 -> 0xfedc */
222 GPU_DATA = 0xfedcba98u;
223 }
224}
225
226// 8-bit texture upload. Each VRAM pixel encodes 2 logical 8-bit texels.
227// We upload a 32-pixel-wide (64 logical texels) x 16-row block, with
228// texel(u, v) = u & 0xff.
229static inline void uploadTex8(void) {
230 waitGPU();
231 GPU_DATA = 0xa0000000u;
232 GPU_DATA = ((uint32_t)(uint16_t)TEX8_VRAM_BASE_Y << 16) |
233 (uint32_t)(uint16_t)TEX8_VRAM_BASE_X;
234 GPU_DATA = ((uint32_t)(uint16_t)16 << 16) | (uint32_t)(uint16_t)32;
235 for (int v = 0; v < 16; v++) {
236 for (int px = 0; px < 32; px += 2) {
237 /* px maps to u=px*2 and u=px*2+1 in the low byte and high
238 byte. Pack two VRAM pixels per word. */
239 uint32_t lo_pix = ((uint32_t)(px * 2 + 0) & 0xff) |
240 (((uint32_t)(px * 2 + 1) & 0xff) << 8);
241 uint32_t hi_pix = ((uint32_t)(px * 2 + 2) & 0xff) |
242 (((uint32_t)(px * 2 + 3) & 0xff) << 8);
243 GPU_DATA = (lo_pix & 0xffff) | ((hi_pix & 0xffff) << 16);
244 }
245 }
246}
247
248// 15-bit texture upload. Each VRAM pixel IS the texel. texel(u, v) =
249// vram555(u & 0x1f, v & 0x1f, ((u + v) & 0x1f)) so each texel is a
250// recognizable function of its address.
251static inline void uploadTex15(void) {
252 waitGPU();
253 GPU_DATA = 0xa0000000u;
254 GPU_DATA = ((uint32_t)(uint16_t)TEX15_VRAM_BASE_Y << 16) |
255 (uint32_t)(uint16_t)TEX15_VRAM_BASE_X;
256 GPU_DATA = ((uint32_t)(uint16_t)16 << 16) | (uint32_t)(uint16_t)64;
257 for (int v = 0; v < 16; v++) {
258 for (int u = 0; u < 64; u += 2) {
259 uint16_t t0 = rasterVram555((uint8_t)(u & 0x1f),
260 (uint8_t)(v & 0x1f),
261 (uint8_t)((u + v) & 0x1f));
262 uint16_t t1 = rasterVram555((uint8_t)((u + 1) & 0x1f),
263 (uint8_t)(v & 0x1f),
264 (uint8_t)((u + 1 + v) & 0x1f));
265 GPU_DATA = (uint32_t)t0 | ((uint32_t)t1 << 16);
266 }
267 }
268}
269
270// Bulk fixture upload - call once at BEFORE_ALL.
271static inline void uploadAllTextureFixtures(void) {
272 uploadClut4();
273 uploadClut8();
274 uploadTex4();
275 uploadTex8();
276 uploadTex15();
277}
278
279// Expected CLUT-lookup value for an 8-bit/4-bit texture sample at
280// logical UV position. Same encoding as the upload functions so tests
281// can predict what a textured draw should produce.
282static inline uint16_t expectedClut4Color(uint8_t u) {
283 return rasterVram555((uint8_t)(u & 0xf),
284 (uint8_t)(31 - (u & 0xf)), 0);
285}
286
287static inline uint16_t expectedClut8Color(uint8_t u) {
288 return rasterVram555((uint8_t)(u & 0x1f),
289 (uint8_t)((255 - u) & 0x1f),
290 (uint8_t)((u >> 5) & 0x1f));
291}
292
293static inline uint16_t expectedTex15Color(uint8_t u, uint8_t v) {
294 return rasterVram555((uint8_t)(u & 0x1f),
295 (uint8_t)(v & 0x1f),
296 (uint8_t)((u + v) & 0x1f));
297}
298
299// --------------------------------------------------------------------------
300// Textured primitive senders
301// --------------------------------------------------------------------------
302
303// GP0(0x24) flat textured triangle - opaque, with texture blending.
304// command color is the blend modulation (0x80 = neutral, no modulation).
305// V0/V1/V2: x,y vertices. U0/V0_uv etc: texture UVs.
306// clut_field: encoded CLUT location (use CLUT4_FIELD / CLUT8_FIELD / 0).
307// tpage_field: encoded texpage (use TEX4_TPAGE / TEX8_TPAGE / TEX15_TPAGE).
308static inline void rasterTexTri(uint32_t cmdColor,
309 int16_t x0, int16_t y0, uint8_t u0, uint8_t v0,
310 int16_t x1, int16_t y1, uint8_t u1, uint8_t v1,
311 int16_t x2, int16_t y2, uint8_t u2, uint8_t v2,
312 uint16_t clut_field, uint16_t tpage_field) {
313 waitGPU();
314 GPU_DATA = 0x24000000u | (cmdColor & 0x00ffffffu);
315 GPU_DATA = ((uint32_t)(uint16_t)y0 << 16) | (uint32_t)(uint16_t)x0;
316 GPU_DATA = ((uint32_t)clut_field << 16) |
317 ((uint32_t)v0 << 8) | (uint32_t)u0;
318 GPU_DATA = ((uint32_t)(uint16_t)y1 << 16) | (uint32_t)(uint16_t)x1;
319 GPU_DATA = ((uint32_t)tpage_field << 16) |
320 ((uint32_t)v1 << 8) | (uint32_t)u1;
321 GPU_DATA = ((uint32_t)(uint16_t)y2 << 16) | (uint32_t)(uint16_t)x2;
322 GPU_DATA = (0u << 16) | ((uint32_t)v2 << 8) | (uint32_t)u2;
323}
324
325// Neutral modulation color: 0x808080 makes the textured pixel pass
326// through unchanged (texel * 128 / 128 == texel, per the soft
327// renderer's modulation formula).
328#define TEX_MOD_NEUTRAL 0x808080u
329
330// GP0(0x2C) flat textured opaque quad. Word layout (9 words total):
331// 0: 0x2C << 24 | cmdColor
332// 1: y0 << 16 | x0
333// 2: clut_field << 16 | v0 << 8 | u0
334// 3: y1 << 16 | x1
335// 4: tpage_field << 16 | v1 << 8 | u1
336// 5: y2 << 16 | x2
337// 6: 0 | v2 << 8 | u2
338// 7: y3 << 16 | x3
339// 8: 0 | v3 << 8 | u3
340//
341// Vertex ordering matters: the GPU draws v0-v1-v2 and v1-v2-v3
342// triangles internally (soft renderer's 4-vertex setupSections path
343// uses the full quad; hardware's order independence is among the
344// things phase-8 characterizes).
345static inline void rasterFlatTexQuad(uint32_t cmdColor,
346 int16_t x0, int16_t y0, uint8_t u0, uint8_t v0,
347 int16_t x1, int16_t y1, uint8_t u1, uint8_t v1,
348 int16_t x2, int16_t y2, uint8_t u2, uint8_t v2,
349 int16_t x3, int16_t y3, uint8_t u3, uint8_t v3,
350 uint16_t clut_field, uint16_t tpage_field) {
351 waitGPU();
352 GPU_DATA = 0x2c000000u | (cmdColor & 0x00ffffffu);
353 GPU_DATA = ((uint32_t)(uint16_t)y0 << 16) | (uint32_t)(uint16_t)x0;
354 GPU_DATA = ((uint32_t)clut_field << 16) |
355 ((uint32_t)v0 << 8) | (uint32_t)u0;
356 GPU_DATA = ((uint32_t)(uint16_t)y1 << 16) | (uint32_t)(uint16_t)x1;
357 GPU_DATA = ((uint32_t)tpage_field << 16) |
358 ((uint32_t)v1 << 8) | (uint32_t)u1;
359 GPU_DATA = ((uint32_t)(uint16_t)y2 << 16) | (uint32_t)(uint16_t)x2;
360 GPU_DATA = (0u << 16) | ((uint32_t)v2 << 8) | (uint32_t)u2;
361 GPU_DATA = ((uint32_t)(uint16_t)y3 << 16) | (uint32_t)(uint16_t)x3;
362 GPU_DATA = (0u << 16) | ((uint32_t)v3 << 8) | (uint32_t)u3;
363}
364
365// GP0(0x64) variable-size textured opaque rectangle.
366// word 0: 0x64 << 24 | cmdColor (modulation, 0x808080 = neutral)
367// word 1: y << 16 | x (top-left)
368// word 2: clut_field << 16 | v << 8 | u (UV at top-left)
369// word 3: h << 16 | w
370// Texture page state must be set via E1 (or recently-issued textured
371// primitive) - the rect command does NOT carry a tpage field word.
372static inline void rasterTexRect(uint32_t cmdColor,
373 int16_t x, int16_t y,
374 uint8_t u, uint8_t v,
375 int16_t w, int16_t h,
376 uint16_t clut_field) {
377 waitGPU();
378 GPU_DATA = 0x64000000u | (cmdColor & 0x00ffffffu);
379 GPU_DATA = ((uint32_t)(uint16_t)y << 16) | (uint32_t)(uint16_t)x;
380 GPU_DATA = ((uint32_t)clut_field << 16) |
381 ((uint32_t)v << 8) | (uint32_t)u;
382 GPU_DATA = ((uint32_t)(uint16_t)h << 16) | (uint32_t)(uint16_t)w;
383}
384
385// GP0(0x66) semi-trans variable-size textured rectangle. Same layout
386// as 0x64; ABR blend mode comes from the current E1 tpage state.
387static inline void rasterTexRectSemi(uint32_t cmdColor,
388 int16_t x, int16_t y,
389 uint8_t u, uint8_t v,
390 int16_t w, int16_t h,
391 uint16_t clut_field) {
392 waitGPU();
393 GPU_DATA = 0x66000000u | (cmdColor & 0x00ffffffu);
394 GPU_DATA = ((uint32_t)(uint16_t)y << 16) | (uint32_t)(uint16_t)x;
395 GPU_DATA = ((uint32_t)clut_field << 16) |
396 ((uint32_t)v << 8) | (uint32_t)u;
397 GPU_DATA = ((uint32_t)(uint16_t)h << 16) | (uint32_t)(uint16_t)w;
398}
399
400// GP0(0x2E) semi-trans flat textured quad. Same layout as 0x2C; only
401// the command opcode byte differs. ABR is read from the embedded tpage
402// field (bit 5-6 of texpageField).
403static inline void rasterFlatTexQuadSemi(uint32_t cmdColor,
404 int16_t x0, int16_t y0, uint8_t u0, uint8_t v0,
405 int16_t x1, int16_t y1, uint8_t u1, uint8_t v1,
406 int16_t x2, int16_t y2, uint8_t u2, uint8_t v2,
407 int16_t x3, int16_t y3, uint8_t u3, uint8_t v3,
408 uint16_t clut_field, uint16_t tpage_field) {
409 waitGPU();
410 GPU_DATA = 0x2e000000u | (cmdColor & 0x00ffffffu);
411 GPU_DATA = ((uint32_t)(uint16_t)y0 << 16) | (uint32_t)(uint16_t)x0;
412 GPU_DATA = ((uint32_t)clut_field << 16) |
413 ((uint32_t)v0 << 8) | (uint32_t)u0;
414 GPU_DATA = ((uint32_t)(uint16_t)y1 << 16) | (uint32_t)(uint16_t)x1;
415 GPU_DATA = ((uint32_t)tpage_field << 16) |
416 ((uint32_t)v1 << 8) | (uint32_t)u1;
417 GPU_DATA = ((uint32_t)(uint16_t)y2 << 16) | (uint32_t)(uint16_t)x2;
418 GPU_DATA = (0u << 16) | ((uint32_t)v2 << 8) | (uint32_t)u2;
419 GPU_DATA = ((uint32_t)(uint16_t)y3 << 16) | (uint32_t)(uint16_t)x3;
420 GPU_DATA = (0u << 16) | ((uint32_t)v3 << 8) | (uint32_t)u3;
421}
uint32_t cmd
Definition dma.c:108
sendGPUData(0xe1000000)
int i
Definition gte-regio.c:287
#define GPU_DATA
Definition hwregs.h:52
void uint32_t(classId, spec)
#define CLUT8_VRAM_Y
Definition texture-fixtures.h:75
#define TEX4_VRAM_BASE_Y
Definition texture-fixtures.h:58
#define TEX15_VRAM_BASE_X
Definition texture-fixtures.h:67
#define CLUT8_VRAM_X
Definition texture-fixtures.h:74
#define TEX4_VRAM_BASE_X
Definition texture-fixtures.h:57
#define TEX8_VRAM_BASE_X
Definition texture-fixtures.h:62
#define TEX8_VRAM_BASE_Y
Definition texture-fixtures.h:63
#define CLUT4_VRAM_Y
Definition texture-fixtures.h:73
#define TEX15_VRAM_BASE_Y
Definition texture-fixtures.h:68
#define CLUT4_VRAM_X
Definition texture-fixtures.h:72
uint16_t v2
Definition timers.c:262
uint16_t v1
Definition timers.c:260