Nugget
Bare-metal libraries and examples for the original PlayStation
Loading...
Searching...
No Matches
rectangle-clipping.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// Rectangle clipping suite. GP0(0x60) flat variable-size rectangle.
28// Drawing area = (0,0) .. (1024, 512) by default. Tests:
29// - Rect entirely inside the draw area (R1).
30// - Rect with top-left outside the draw area (R2). Top-left clipping.
31// - Rect that exactly equals the draw area edge.
32// - Rect with negative coordinates after drawing offset.
33
34// clang-format off
35
37
38// R1: 4x4 rectangle at (10, 10), GREEN. Fully inside draw area.
39static void rasterDrawRectR1(void) {
40 rasterReset();
41 rasterClearTestRegion(0, 0, 32, 32);
42 rasterFlatRect(RASTER_CMD_GREEN, 10, 10, 4, 4);
43 rasterFlushPrimitive();
44}
45
46// R2: 8x8 rectangle starting at (-2, -2), GREEN. Top-left 4x4 quadrant
47// of the rect is outside the draw area; only the bottom-right 6x6 region
48// remains and should fill VRAM (0,0)..(5,5).
49//
50// Note: GP0(0x60) takes UNSIGNED 16-bit coordinates in the second word
51// (x | y<<16). Negative -2 sign-extends to 0xfffe which would naturally
52// be a huge X. But the drawing-area clipping should clamp this. The
53// observation IS the test - if the GPU treats -2 as 0xfffe and clips
54// everything (no draw), we want to see that in the OBS log.
55//
56// To get a meaningful test of "negative top-left clipping" we instead
57// shift the drawing area so the test rect crosses its top-left edge
58// with VALID 11-bit coordinates. Set draw area to (4, 4)..(end), draw
59// rect at (2, 2) size 6x6. Then VRAM (4,4)..(7,7) should fill and
60// VRAM (2,2)..(3,3) should stay sentinel.
61static void rasterDrawRectR2(void) {
62 rasterReset();
63 rasterClearTestRegion(0, 0, 16, 16);
64 // Shrink draw area to (4, 4)..(16, 16).
65 setDrawingArea(4, 4, 16, 16);
66 rasterFlatRect(RASTER_CMD_GREEN, 2, 2, 6, 6);
67 rasterFlushPrimitive();
68}
69
70) // CESTER_BODY
71
72// --------------------------------------------------------------------------
73// Rect R1: fully inside draw area
74// --------------------------------------------------------------------------
75
76CESTER_TEST(rectR1_top_left, gpu_raster_phase1,
77 rasterDrawRectR1();
79)
80
81CESTER_TEST(rectR1_bottom_right_interior, gpu_raster_phase1,
82 rasterDrawRectR1();
84)
85
86CESTER_TEST(rectR1_right_edge_excluded, gpu_raster_phase1,
87 rasterDrawRectR1();
89)
90
91CESTER_TEST(rectR1_bottom_edge_excluded, gpu_raster_phase1,
92 rasterDrawRectR1();
94)
95
96CESTER_TEST(rectR1_before_left_edge, gpu_raster_phase1,
97 rasterDrawRectR1();
99)
100
101// --------------------------------------------------------------------------
102// Rect R2: top-left clipped by drawing area (4,4)
103// --------------------------------------------------------------------------
104//
105// EXPECT_RECT_R2_PIXEL_0_0 in raster-expected.h was written assuming the
106// default draw area (0,0). Here we use a shifted draw area (4,4) and
107// expect the rect at (2,2) size 6x6 to fill VRAM (4,4)..(7,7) only.
108// The pixels we assert against differ from the placeholders; redirect
109// to the actual coordinates this test exercises.
110
111CESTER_TEST(rectR2_clipped_pixel_4_4_corner, gpu_raster_phase1,
112 rasterDrawRectR2();
113 // (4,4) is inside both the rect and the shrunk draw area: should fill.
115)
116
117CESTER_TEST(rectR2_clipped_pixel_7_7_bottom_right, gpu_raster_phase1,
118 rasterDrawRectR2();
119 // (7,7) is the last interior pixel given the rect (2,2)..(8,8)
120 // exclusive right/bottom under top-left rule.
122)
123
124CESTER_TEST(rectR2_clipped_pixel_2_2_top_left_outside_draw_area,
125 gpu_raster_phase1,
126 rasterDrawRectR2();
127 // (2,2) is inside the rect but OUTSIDE the draw area (which starts
128 // at 4). Should be clipped -> sentinel.
130)
131
132CESTER_TEST(rectR2_clipped_pixel_3_3_just_inside_rect_outside_draw,
133 gpu_raster_phase1,
134 rasterDrawRectR2();
135 // (3,3) is inside the rect but just outside the shifted draw area.
136 // Should be sentinel.
138)
139
140CESTER_TEST(rectR2_clipped_pixel_8_8_outside_rect, gpu_raster_phase1,
141 rasterDrawRectR2();
142 // (8,8) is past the rect's exclusive right/bottom edge.
144)
CESTER_TEST(cddaLevelHalf, test_instances, static const uint8_t atv[4]={0x40, 0x00, 0x40, 0x00};unsigned changes=0;int peak=cddaPeak(atv, 1, &changes);ramsyscall_printf("CD-DA level at half ATV: peak %i, %u sign changes\n", peak, changes);cester_assert_int_ge(peak, 11300);cester_assert_int_le(peak, 11800);cester_assert_uint_ge(changes, 4);) CESTER_TEST(cddaLevelClipped
CESTER_BODY(static uint16_t s_cddaCapture;static int cddaSpuDma(uint32_t spuAddr, void *buf, uint32_t bytes, int isRead) { const uint16_t tsa=spuAddr > > 3;const uint16_t mode=isRead ? 0x30 :0x20;SPU_RAM_DTA=tsa;for(int i=0;i< 0xf01 &&SPU_RAM_DTA !=tsa;i++);SPU_CTRL=(SPU_CTRL &~0x30)|mode;for(volatile int i=0;i< 60;i++);for(int i=0;i< 0xf01 &&(SPU_STATUS &0x30) !=mode;i++);CDDA_SPU_DELAY=(CDDA_SPU_DELAY &0xf0ffffff)|(isRead ? 0x22000000 :0x20000000);DMA_CTRL[DMA_SPU].MADR=(uint32_t) buf &0x1fffffff;DMA_CTRL[DMA_SPU].BCR=((bytes > > 6)<< 16)|0x10;DMA_CTRL[DMA_SPU].CHCR=isRead ? 0x01000200 :0x01000201;int ok=0;for(unsigned t=0;t< 4000000u;t++) { if((DMA_CTRL[DMA_SPU].CHCR &0x01000000)==0) { ok=1;break;} } SPU_CTRL=SPU_CTRL &~0x30;for(volatile int i=0;i< 60;i++);return ok;} static int cddaSimpleCmd(uint8_t cmd) { uint8_t response[16];CDROM_REG0=0;CDROM_REG1=cmd;uint32_t timeout=2000000;int ok=waitCDRomIRQWithTimeout(&timeout);ackCDRomCause();readResponse(response);return ok;} static void cddaWaitPauseComplete() { initializeTime();uint32_t elapsed=0;while(elapsed< 5000000u) { uint32_t budget=200000u;int ok=waitCDRomIRQWithTimeout(&budget);elapsed=budget;if(!ok) continue;uint8_t cause=ackCDRomCause();uint8_t response[16];readResponse(response);if(cause==2) return;} } static int cddaAudioSetup(const uint8_t atv[4], int demute) { if(!resetCDRom()) return 0;DPCR|=0x000b0000;SPU_CTRL=0;for(volatile int i=0;i< 20000;i++);SPU_VOL_MAIN_LEFT=0;SPU_VOL_MAIN_RIGHT=0;SPU_KEY_OFF_LOW=0xffff;SPU_KEY_OFF_HIGH=0xffff;SPU_RAM_DTC=4;SPU_CTRL=0x8001;for(volatile int i=0;i< 20000;i++);for(int i=0;i< 0x400;i++) s_cddaCapture[i]=CDDA_MARKER;if(!cddaSpuDma(0x000, s_cddaCapture, 0x800, 0)) return 0;if(!cddaSimpleCmd(demute ? CDL_DEMUTE :CDL_MUTE)) return 0;CDROM_REG0=2;CDROM_REG2=atv[0];CDROM_REG3=atv[1];CDROM_REG0=3;CDROM_REG1=atv[2];CDROM_REG2=atv[3];CDROM_REG0=3;CDROM_REG3=0x20;CDROM_REG0=0;SPU_VOL_CD_LEFT=0x7fff;SPU_VOL_CD_RIGHT=0x7fff;SPU_CTRL=SPU_CTRL|0x8001;return 1;} static int cddaBufferPeak(unsigned *signChanges) { int peak=0;unsigned changes=0;int prev=0;for(int i=0;i< 0x200;i++) { int v=(int16_t) s_cddaCapture[i];int a=v< 0 ? -v :v;if(a > peak) peak=a;int sign=v >=0 ? 1 :-1;if(i &&sign !=prev) changes++;prev=sign;} *signChanges=changes;return peak;} static int cddaCapturePeak(unsigned *signChanges) { int peak=0;for(unsigned attempt=0;attempt< 8;attempt++) { if(attempt) { initializeTime();while(updateTime()< 250000u);} if(!cddaSpuDma(0x000, s_cddaCapture, 0x800, 1)) return -1;peak=cddaBufferPeak(signChanges);if(peak) { if(attempt) ramsyscall_printf("capture was empty %u time(s) before audio\n", attempt);break;} } return peak;} static int cddaPeak(const uint8_t atv[4], int demute, unsigned *signChanges) { if(!cddaAudioSetup(atv, demute)) return -1;if(!setMode(0)) return -1;CDROM_REG0=0;CDROM_REG2=0x02;if(!cddaSimpleCmd(CDL_PLAY)) return -1;initializeTime();while(updateTime()< 1500000u);int peak=cddaCapturePeak(signChanges);CDROM_REG0=0;CDROM_REG1=CDL_PAUSE;cddaWaitPauseComplete();CDROM_REG0=0;CDROM_REG2=0x00;CDROM_REG2=0x02;CDROM_REG2=0x16;if(cddaSimpleCmd(CDL_SETLOC) &&cddaSimpleCmd(CDL_SEEKL)) cddaWaitPauseComplete();return peak;}[0x400])
Definition cdda.c:37
#define EXPECT_RECT_R1_PIXEL_10_14
Definition raster-expected.h:217
#define EXPECT_RECT_R1_PIXEL_9_10
Definition raster-expected.h:218
#define EXPECT_RECT_R1_PIXEL_10_10
Definition raster-expected.h:214
#define EXPECT_RECT_R1_PIXEL_13_13
Definition raster-expected.h:215
#define EXPECT_RECT_R1_PIXEL_14_10
Definition raster-expected.h:216
#define RASTER_VRAM_GREEN
Definition raster-helpers.h:127
#define ASSERT_PIXEL_UNTOUCHED(x_, y_)
Definition raster-helpers.h:486
#define RASTER_CMD_GREEN
Definition raster-helpers.h:126
#define ASSERT_PIXEL_EQ(expected, x_, y_)
Definition raster-helpers.h:473