Skip to content

Commit 7d5b61c

Browse files
[rlsw][SEGFAULT] Fix triangle and quad spans applying pixels out of bounds (#5849)
* fix triangle and quad spans applying pixels out of bounds * remove off by one errors on x/y LoopMax * apply the RASTER_QUAD offset at the loop start so it increments correctly * fix missing endif * remove include guard to allow dyMin usage * early exit if nothing to draw on a span * incorporate dxStart into xSubstep to make xOffset calculate a single time * remove ghost comment * early exit for quads, with a float cast on the left and top distance calculation * remove duplicate xLoopEnd
1 parent b48933b commit 7d5b61c

1 file changed

Lines changed: 66 additions & 18 deletions

File tree

src/external/rlsw.h

Lines changed: 66 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5385,6 +5385,17 @@ static void SW_RASTER_TRIANGLE_SPAN(const sw_vertex_t *start, const sw_vertex_t
53855385
int xEnd = (int)end->position[0];
53865386
if (xStart == xEnd) return;
53875387

5388+
// Intercept the span bounds to ensure we don't write before the framebuffer.
5389+
int xLoopStart = (xStart >= 0)? xStart : 0;
5390+
int xLoopEnd = (xEnd <= RLSW.colorBuffer->width)? xEnd : RLSW.colorBuffer->width;
5391+
// Nothing to draw.
5392+
if (xLoopStart >= xLoopEnd) return;
5393+
5394+
// Get the current row and skip if outside the framebuffer.
5395+
// Maybe this check is better suited elsewhere?
5396+
int y = (int)start->position[1];
5397+
if (y < 0 || y >= RLSW.colorBuffer->height) return;
5398+
53885399
// Compute the inverse horizontal distance along the X axis
53895400
float dxRcp = sw_rcp(end->position[0] - start->position[0]);
53905401

@@ -5405,40 +5416,42 @@ static void SW_RASTER_TRIANGLE_SPAN(const sw_vertex_t *start, const sw_vertex_t
54055416
#endif
54065417

54075418
// Compute the subpixel distance to traverse before the first pixel
5419+
// Also step further into them to move away from the colorbuffer edge.
54085420
float xSubstep = 1.0f - sw_fract(start->position[0]);
5421+
float dxStart = (float)(xLoopStart - xStart);
5422+
float xOffset = xSubstep + dxStart;
54095423

5410-
// Initializing the interpolation starting values
5411-
float w = start->position[3] + dWdx*xSubstep;
5424+
// Initializing the interpolation starting values.
5425+
float w = start->position[3] + dWdx*xOffset;
54125426
float color[4] = {
5413-
start->color[0] + dCdx[0]*xSubstep,
5414-
start->color[1] + dCdx[1]*xSubstep,
5415-
start->color[2] + dCdx[2]*xSubstep,
5416-
start->color[3] + dCdx[3]*xSubstep
5427+
start->color[0] + dCdx[0]*xOffset,
5428+
start->color[1] + dCdx[1]*xOffset,
5429+
start->color[2] + dCdx[2]*xOffset,
5430+
start->color[3] + dCdx[3]*xOffset
54175431
};
54185432
#ifdef SW_ENABLE_DEPTH_TEST
5419-
float z = start->position[2] + dZdx*xSubstep;
5433+
float z = start->position[2] + dZdx*xOffset;
54205434
#endif
54215435
#ifdef SW_ENABLE_TEXTURE
5422-
float u = start->texcoord[0] + dUdx*xSubstep;
5423-
float v = start->texcoord[1] + dVdx*xSubstep;
5436+
float u = start->texcoord[0] + dUdx*xOffset;
5437+
float v = start->texcoord[1] + dVdx*xOffset;
54245438
#endif
54255439

54265440
// Pre-calculate the starting pointers for the framebuffer row
5427-
int y = (int)start->position[1];
5428-
int baseOffset = y*RLSW.colorBuffer->width + xStart;
5441+
int baseOffset = y*RLSW.colorBuffer->width + xLoopStart;
54295442
uint8_t *cPtr = (uint8_t *)(RLSW.colorBuffer->pixels) + baseOffset*SW_FRAMEBUFFER_COLOR_SIZE;
54305443
#ifdef SW_ENABLE_DEPTH_TEST
54315444
uint8_t *dPtr = (uint8_t *)(RLSW.depthBuffer->pixels) + baseOffset*SW_FRAMEBUFFER_DEPTH_SIZE;
54325445
#endif
54335446

54345447
#define SW_AFFINE_BLOCK 16
54355448

5436-
int x = xStart;
5437-
while (x < xEnd)
5449+
int x = xLoopStart;
5450+
while (x < xLoopEnd)
54385451
{
54395452
// Clamp last block to remaining pixels
54405453
int blockEnd = x + SW_AFFINE_BLOCK;
5441-
if (blockEnd > xEnd) blockEnd = xEnd;
5454+
if (blockEnd > xLoopEnd) blockEnd = xLoopEnd;
54425455
float blockLenF = (float)(blockEnd - x);
54435456
float blockLenRcp = sw_rcp(blockLenF);
54445457

@@ -5673,6 +5686,14 @@ static void SW_RASTER_QUAD(const sw_vertex_t *a, const sw_vertex_t *b,
56735686
int xMax = (int)br->position[0];
56745687
int yMax = (int)br->position[1];
56755688

5689+
// Exit early if no pixels to draw. Use these later for loop boundaries
5690+
int yLoopMin = (yMin >= 0)? yMin : 0;
5691+
int xLoopMin = (xMin >= 0)? xMin : 0;
5692+
int yLoopMax = (yMax <= RLSW.colorBuffer->height)? yMax : RLSW.colorBuffer->height;
5693+
int xLoopMax = (xMax <= RLSW.colorBuffer->width)? xMax : RLSW.colorBuffer->width;
5694+
5695+
if (yLoopMin >= yLoopMax || xLoopMin >= xLoopMax) return;
5696+
56765697
float w = (float)(xMax - xMin);
56775698
float h = (float)(yMax - yMin);
56785699
if ((w <= 0) || (h <= 0)) return;
@@ -5726,21 +5747,44 @@ static void SW_RASTER_QUAD(const sw_vertex_t *a, const sw_vertex_t *b,
57265747
uint8_t *dPixels = RLSW.depthBuffer->pixels;
57275748
#endif
57285749

5729-
for (int y = yMin; y < yMax; y++)
5750+
// Calculate the distance the in-bounds boundary is from the quad's edges, only on the left and top.
5751+
float dxMin = (float)(xLoopMin - xMin);
5752+
float dyMin = (float)(yLoopMin - yMin);
5753+
5754+
// Correct our start by how far we clipped outside the framebuffer.
5755+
cRow[0] += dCdx[0]*dxMin + dCdy[0]*dyMin;
5756+
cRow[1] += dCdx[1]*dxMin + dCdy[1]*dyMin;
5757+
cRow[2] += dCdx[2]*dxMin + dCdy[2]*dyMin;
5758+
cRow[3] += dCdx[3]*dxMin + dCdy[3]*dyMin;
5759+
#ifdef SW_ENABLE_DEPTH_TEST
5760+
zRow += dZdy*dyMin + dZdx*dxMin;
5761+
#endif
5762+
#ifdef SW_ENABLE_TEXTURE
5763+
uRow += dUdy*dyMin + dUdx*dxMin;
5764+
vRow += dVdy*dyMin + dVdx*dxMin;
5765+
#endif
5766+
5767+
for (int y = yLoopMin; y < yLoopMax; y++)
57305768
{
5731-
int baseOffset = y*stride + xMin;
5769+
int baseOffset = y*stride + xLoopMin;
57325770
uint8_t *cPtr = cPixels + baseOffset*SW_FRAMEBUFFER_COLOR_SIZE;
57335771
#ifdef SW_ENABLE_DEPTH_TEST
57345772
uint8_t *dPtr = dPixels + baseOffset*SW_FRAMEBUFFER_DEPTH_SIZE;
5773+
// Copy the cursors so we increment them ourselves without destroying the offset maths.
57355774
float z = zRow;
57365775
#endif
57375776
#ifdef SW_ENABLE_TEXTURE
57385777
float u = uRow;
57395778
float v = vRow;
57405779
#endif
5741-
float color[4] = { cRow[0], cRow[1], cRow[2], cRow[3] };
5780+
float color[4] = {
5781+
cRow[0],
5782+
cRow[1],
5783+
cRow[2],
5784+
cRow[3]
5785+
};
57425786

5743-
for (int x = xMin; x < xMax; x++)
5787+
for (int x = xLoopMin; x < xLoopMax; x++)
57445788
{
57455789
float srcColor[4] = { color[0], color[1], color[2], color[3] };
57465790

@@ -5779,6 +5823,7 @@ static void SW_RASTER_QUAD(const sw_vertex_t *a, const sw_vertex_t *b,
57795823
#ifdef SW_ENABLE_DEPTH_TEST
57805824
discard:
57815825
#endif
5826+
// We move one pixel over without touching the original "start offset"
57825827
color[0] += dCdx[0];
57835828
color[1] += dCdx[1];
57845829
color[2] += dCdx[2];
@@ -5801,6 +5846,9 @@ static void SW_RASTER_QUAD(const sw_vertex_t *a, const sw_vertex_t *b,
58015846
cPtr += SW_FRAMEBUFFER_COLOR_SIZE;
58025847
}
58035848

5849+
// The for loop is clamped to the right side of the screen.
5850+
// However, these cursor start vars are still on the left.
5851+
// That's fine. We just need to advance to the next row.
58045852
cRow[0] += dCdy[0];
58055853
cRow[1] += dCdy[1];
58065854
cRow[2] += dCdy[2];

0 commit comments

Comments
 (0)