vector op helper macro + pick sampler once before primitive drawing

This commit is contained in:
Bigfoot71 2026-07-18 23:03:31 +02:00
commit b5ef080f89

475
src/external/rlsw.h vendored
View file

@ -793,14 +793,14 @@ SWAPI void swGetFramebufferAttachmentParameteriv(SWattachment attachment, SWatta
#if defined(NDEBUG) #if defined(NDEBUG)
#if defined(_MSC_VER) #if defined(_MSC_VER)
#define SW_FORCE_INLINE __forceinline #define SW_INLINE __forceinline
#elif defined(__GNUC__) || defined(__clang__) #elif defined(__GNUC__) || defined(__clang__)
#define SW_FORCE_INLINE inline __attribute__((always_inline)) #define SW_INLINE inline __attribute__((always_inline))
#else #else
#define SW_FORCE_INLINE inline #define SW_INLINE inline
#endif #endif
#else #else
#define SW_FORCE_INLINE inline #define SW_INLINE inline
#endif #endif
#if defined(_M_X64) || defined(__x86_64__) #if defined(_M_X64) || defined(__x86_64__)
@ -966,6 +966,7 @@ typedef enum {
} sw_pixel_alpha_t; } sw_pixel_alpha_t;
// Forward declarations // Forward declarations
typedef struct sw_texture sw_texture_t;
typedef struct sw_vertex sw_vertex_t; typedef struct sw_vertex sw_vertex_t;
// Pixel getter functions // Pixel getter functions
@ -976,6 +977,9 @@ typedef void (*sw_pixel_read_color_f)(float *SW_RESTRICT, const void *SW_RESTRIC
typedef void (*sw_pixel_write_color8_f)(void *SW_RESTRICT, const uint8_t *SW_RESTRICT, uint32_t); typedef void (*sw_pixel_write_color8_f)(void *SW_RESTRICT, const uint8_t *SW_RESTRICT, uint32_t);
typedef void (*sw_pixel_write_color_f)(void *SW_RESTRICT, const float *SW_RESTRICT, uint32_t); typedef void (*sw_pixel_write_color_f)(void *SW_RESTRICT, const float *SW_RESTRICT, uint32_t);
// Texture sampler function
typedef void (*sw_texture_sampler_f)(float *SW_RESTRICT, const sw_texture_t *SW_RESTRICT, float, float);
// Color blending function // Color blending function
typedef void (*sw_blend_f)(float *SW_RESTRICT, const float *SW_RESTRICT); typedef void (*sw_blend_f)(float *SW_RESTRICT, const float *SW_RESTRICT);
@ -993,7 +997,7 @@ typedef struct sw_vertex {
float texcoord[2]; // Texture coordinates float texcoord[2]; // Texture coordinates
} sw_vertex_t; } sw_vertex_t;
typedef struct { typedef struct sw_texture {
void *pixels; // Texture pixels void *pixels; // Texture pixels
sw_pixel_read_color8_f readColor8; // Texel read RGBA8 sw_pixel_read_color8_f readColor8; // Texel read RGBA8
sw_pixel_read_color_f readColor; // Texel read RGBA32F sw_pixel_read_color_f readColor; // Texel read RGBA32F
@ -1167,6 +1171,13 @@ static const int SW_PRIMITIVE_VERTEX_COUNT[] =
// Internal Functions Definitions // Internal Functions Definitions
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Common helper macros
//----------------------------------------------------------------------------------
#define SW_VEC_OP(expr, i_name, count) do { \
for (int i_name = 0; i_name < (count); i_name++) expr; \
} while (0)
//----------------------------------------------------------------------------------
// Math helper functions // Math helper functions
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
static inline void sw_matrix_id(sw_matrix_t dst) static inline void sw_matrix_id(sw_matrix_t dst)
@ -1257,7 +1268,7 @@ static inline float sw_fract(float x)
return (x - floorf(x)); return (x - floorf(x));
} }
static SW_FORCE_INLINE float sw_rcp(float x) static SW_INLINE float sw_rcp(float x)
{ {
#if defined(__XTENSA__) #if defined(__XTENSA__)
// Xtensa architecture optimization // Xtensa architecture optimization
@ -2464,8 +2475,8 @@ static inline void sw_texture_sample_linear(float *SW_RESTRICT color, const sw_t
} }
} }
static inline void sw_texture_sample(float *SW_RESTRICT color, const sw_texture_t *SW_RESTRICT tex, // Resolves which sample function to use from the texel-to-pixel derivative magnitude
float u, float v, float dUdx, float dUdy, float dVdx, float dVdy) static inline sw_texture_sampler_f sw_texture_pick_sampler(const sw_texture_t *tex, const float dTdx[2], const float dTdy[2])
{ {
// NOTE: Commented there is the previous method used // NOTE: Commented there is the previous method used
// There was no need to compute the square root because // There was no need to compute the square root because
@ -2474,19 +2485,12 @@ static inline void sw_texture_sample(float *SW_RESTRICT color, const sw_texture_
//float dv = sqrtf(dVdx*dVdx + dVdy*dVdy); //float dv = sqrtf(dVdx*dVdx + dVdy*dVdy);
//float L = (du > dv)? du : dv; //float L = (du > dv)? du : dv;
// Calculate the derivatives for each axis float dU2 = dTdx[0]*dTdx[0] + dTdy[0]*dTdy[0];
float dU2 = dUdx*dUdx + dUdy*dUdy; float dV2 = dTdx[1]*dTdx[1] + dTdy[1]*dTdy[1];
float dV2 = dVdx*dVdx + dVdy*dVdy;
float L2 = (dU2 > dV2)? dU2 : dV2; float L2 = (dU2 > dV2)? dU2 : dV2;
SWfilter filter = (L2 > 1.0f)? tex->minFilter : tex->magFilter; SWfilter filter = (L2 > 1.0f)? tex->minFilter : tex->magFilter;
return (filter == SW_LINEAR)? sw_texture_sample_linear : sw_texture_sample_nearest;
switch (filter)
{
case SW_NEAREST: sw_texture_sample_nearest(color, tex, u, v); break;
case SW_LINEAR: sw_texture_sample_linear(color, tex, u, v); break;
default: break;
}
} }
//------------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------------
@ -5247,82 +5251,80 @@ void swGetFramebufferAttachmentParameteriv(SWattachment attachment, SWattachget
#define SW_RASTER_TRIANGLE_FLAGS SW_FLAGS(SW_RASTER_TRIANGLE_STATES) #define SW_RASTER_TRIANGLE_FLAGS SW_FLAGS(SW_RASTER_TRIANGLE_STATES)
#define SW_RASTER_TRIANGLE_SPAN SW_CONCATX(sw_raster_triangle_span_, SW_NAME(SW_RASTER_TRIANGLE_STATES)) #define SW_RASTER_TRIANGLE_SPAN SW_CONCATX(sw_raster_triangle_span_, SW_NAME(SW_RASTER_TRIANGLE_STATES))
#define SW_RASTER_TRIANGLE SW_CONCATX(sw_raster_triangle_, SW_NAME(SW_RASTER_TRIANGLE_STATES)) #define SW_RASTER_TRIANGLE SW_CONCATX(sw_raster_triangle_, SW_NAME(SW_RASTER_TRIANGLE_STATES))
#define SW_SPAN_CTX SW_CONCATX(sw_span_ctx_, SW_NAME(SW_RASTER_TRIANGLE_STATES))
#if (SW_RASTER_TRIANGLE_FLAGS) & SW_STATE_COLOR_INTERP // True when a perspective-correct interpolation pass is needed (color or UVs);
#define SW_SPAN_FLAT_COLOR_PARAM // lets SPAN skip all w-tracking and block subdivision otherwise
#define SW_SPAN_FLAT_COLOR_ARG #define SW_RASTER_TRIANGLE_NEEDS_PERSPECTIVE \
#else (((SW_RASTER_TRIANGLE_FLAGS) & SW_STATE_COLOR_INTERP) || ((SW_RASTER_TRIANGLE_FLAGS) & SW_STATE_TEXTURE_2D))
#define SW_SPAN_FLAT_COLOR_PARAM , const float flatColor[4]
#define SW_SPAN_FLAT_COLOR_ARG , flatColor
#endif
#define SW_GRAD SW_CONCATX(sw_grad_, SW_NAME(SW_RASTER_TRIANGLE_STATES)) #define SW_GRAD SW_CONCATX(sw_grad_, SW_NAME(SW_RASTER_TRIANGLE_STATES))
#define SW_VADD SW_CONCATX(sw_vadd_, SW_NAME(SW_RASTER_TRIANGLE_STATES)) #define SW_VADD SW_CONCATX(sw_vadd_, SW_NAME(SW_RASTER_TRIANGLE_STATES))
#define SW_VFMA SW_CONCATX(sw_vfma_, SW_NAME(SW_RASTER_TRIANGLE_STATES)) #define SW_VMAD SW_CONCATX(sw_vmad_, SW_NAME(SW_RASTER_TRIANGLE_STATES))
static SW_FORCE_INLINE void SW_GRAD(sw_vertex_t *SW_RESTRICT out, const sw_vertex_t *SW_RESTRICT a, const sw_vertex_t *SW_RESTRICT b, float scale) // Per-triangle constants reused by every scanline: attribute x-derivatives
// (constant across a planar triangle) plus whatever is fixed once and for
// all when its interpolation is disabled (flat color, resolved sampler)
typedef struct
{ {
out->position[0] = (b->position[0] - a->position[0])*scale; #if SW_RASTER_TRIANGLE_NEEDS_PERSPECTIVE
out->position[1] = (b->position[1] - a->position[1])*scale; float dWdx;
out->position[2] = (b->position[2] - a->position[2])*scale; #endif
out->position[3] = (b->position[3] - a->position[3])*scale; #if (SW_RASTER_TRIANGLE_FLAGS) & SW_STATE_DEPTH_TEST
float dZdx;
#endif
#if (SW_RASTER_TRIANGLE_FLAGS) & SW_STATE_COLOR_INTERP #if (SW_RASTER_TRIANGLE_FLAGS) & SW_STATE_COLOR_INTERP
out->color[0] = (b->color[0] - a->color[0])*scale; float dCdx[4];
out->color[1] = (b->color[1] - a->color[1])*scale; #else
out->color[2] = (b->color[2] - a->color[2])*scale; float flatColor[4];
out->color[3] = (b->color[3] - a->color[3])*scale;
#endif #endif
#if (SW_RASTER_TRIANGLE_FLAGS) & SW_STATE_TEXTURE_2D #if (SW_RASTER_TRIANGLE_FLAGS) & SW_STATE_TEXTURE_2D
out->texcoord[0] = (b->texcoord[0] - a->texcoord[0])*scale; float dTdx[2];
out->texcoord[1] = (b->texcoord[1] - a->texcoord[1])*scale; float dTdy[2];
sw_texture_sampler_f sample;
#endif
} SW_SPAN_CTX;
static inline void SW_GRAD(sw_vertex_t *SW_RESTRICT out, const sw_vertex_t *SW_RESTRICT a, const sw_vertex_t *SW_RESTRICT b, float scale)
{
SW_VEC_OP(out->position[i] = (b->position[i] - a->position[i])*scale, i, 4);
#if (SW_RASTER_TRIANGLE_FLAGS) & SW_STATE_COLOR_INTERP
SW_VEC_OP(out->color[i] = (b->color[i] - a->color[i])*scale, i, 4);
#endif
#if (SW_RASTER_TRIANGLE_FLAGS) & SW_STATE_TEXTURE_2D
SW_VEC_OP(out->texcoord[i] = (b->texcoord[i] - a->texcoord[i])*scale, i, 2);
#endif #endif
} }
static SW_FORCE_INLINE void SW_VADD(sw_vertex_t *SW_RESTRICT out, const sw_vertex_t *SW_RESTRICT gradients) static inline void SW_VADD(sw_vertex_t *SW_RESTRICT out, const sw_vertex_t *SW_RESTRICT inc)
{ {
out->position[0] += gradients->position[0]; SW_VEC_OP(out->position[i] += inc->position[i], i, 4);
out->position[1] += gradients->position[1];
out->position[2] += gradients->position[2];
out->position[3] += gradients->position[3];
#if (SW_RASTER_TRIANGLE_FLAGS) & SW_STATE_COLOR_INTERP #if (SW_RASTER_TRIANGLE_FLAGS) & SW_STATE_COLOR_INTERP
out->color[0] += gradients->color[0]; SW_VEC_OP(out->color[i] += inc->color[i], i, 4);
out->color[1] += gradients->color[1];
out->color[2] += gradients->color[2];
out->color[3] += gradients->color[3];
#endif #endif
#if (SW_RASTER_TRIANGLE_FLAGS) & SW_STATE_TEXTURE_2D #if (SW_RASTER_TRIANGLE_FLAGS) & SW_STATE_TEXTURE_2D
out->texcoord[0] += gradients->texcoord[0]; SW_VEC_OP(out->texcoord[i] += inc->texcoord[i], i, 2);
out->texcoord[1] += gradients->texcoord[1];
#endif #endif
} }
static SW_FORCE_INLINE void SW_VFMA(sw_vertex_t *SW_RESTRICT out, const sw_vertex_t *SW_RESTRICT gradients, float scale) static inline void SW_VMAD(sw_vertex_t *SW_RESTRICT out, const sw_vertex_t *SW_RESTRICT inc, float scale)
{ {
out->position[0] += gradients->position[0]*scale; SW_VEC_OP(out->position[i] += inc->position[i]*scale, i, 4);
out->position[1] += gradients->position[1]*scale;
out->position[2] += gradients->position[2]*scale;
out->position[3] += gradients->position[3]*scale;
#if (SW_RASTER_TRIANGLE_FLAGS) & SW_STATE_COLOR_INTERP #if (SW_RASTER_TRIANGLE_FLAGS) & SW_STATE_COLOR_INTERP
out->color[0] += gradients->color[0]*scale; SW_VEC_OP(out->color[i] += inc->color[i]*scale, i, 4);
out->color[1] += gradients->color[1]*scale;
out->color[2] += gradients->color[2]*scale;
out->color[3] += gradients->color[3]*scale;
#endif #endif
#if (SW_RASTER_TRIANGLE_FLAGS) & SW_STATE_TEXTURE_2D #if (SW_RASTER_TRIANGLE_FLAGS) & SW_STATE_TEXTURE_2D
out->texcoord[0] += gradients->texcoord[0]*scale; SW_VEC_OP(out->texcoord[i] += inc->texcoord[i]*scale, i, 2);
out->texcoord[1] += gradients->texcoord[1]*scale;
#endif #endif
} }
static void SW_RASTER_TRIANGLE_SPAN(const sw_vertex_t *start, const sw_vertex_t *end, static void SW_RASTER_TRIANGLE_SPAN(const sw_vertex_t *start, const sw_vertex_t *end, const SW_SPAN_CTX *ctx)
float dUdy, float dVdy SW_SPAN_FLAT_COLOR_PARAM)
{ {
// Skip rows outside the framebuffer; can only trigger on FP rounding at
// clip boundaries since triangles are already clipped to the viewport
int y = (int)start->position[1];
if (y < 0 || y >= RLSW.colorBuffer->height) return;
// Gets the start/end coordinates and skip empty lines // Gets the start/end coordinates and skip empty lines
int xStart = (int)start->position[0]; int xStart = (int)start->position[0];
int xEnd = (int)end->position[0]; int xEnd = (int)end->position[0];
@ -5333,33 +5335,6 @@ static void SW_RASTER_TRIANGLE_SPAN(const sw_vertex_t *start, const sw_vertex_t
int xLoopEnd = (xEnd <= RLSW.colorBuffer->width)? xEnd : RLSW.colorBuffer->width; int xLoopEnd = (xEnd <= RLSW.colorBuffer->width)? xEnd : RLSW.colorBuffer->width;
if (xLoopStart >= xLoopEnd) return; // Nothing to draw if (xLoopStart >= xLoopEnd) return; // Nothing to draw
// Get the current row and skip if outside the framebuffer
// Maybe this check is better suited elsewhere?
int y = (int)start->position[1];
if (y < 0 || y >= RLSW.colorBuffer->height) return;
// Compute the inverse horizontal distance along the X axis
float dxRcp = sw_rcp(end->position[0] - start->position[0]);
// Compute the interpolation steps along the X axis
#if (SW_RASTER_TRIANGLE_FLAGS) & SW_STATE_DEPTH_TEST
float dZdx = (end->position[2] - start->position[2])*dxRcp;
#endif
float dWdx = (end->position[3] - start->position[3])*dxRcp;
#if (SW_RASTER_TRIANGLE_FLAGS) & SW_STATE_COLOR_INTERP
float dCdx[4] = {
(end->color[0] - start->color[0])*dxRcp,
(end->color[1] - start->color[1])*dxRcp,
(end->color[2] - start->color[2])*dxRcp,
(end->color[3] - start->color[3])*dxRcp
};
#endif
#if (SW_RASTER_TRIANGLE_FLAGS) & SW_STATE_TEXTURE_2D
float dUdx = (end->texcoord[0] - start->texcoord[0])*dxRcp;
float dVdx = (end->texcoord[1] - start->texcoord[1])*dxRcp;
#endif
// Compute the subpixel distance to traverse before the first pixel // Compute the subpixel distance to traverse before the first pixel
// Also step further into them to move away from the colorbuffer edge // Also step further into them to move away from the colorbuffer edge
float xSubstep = 1.0f - sw_fract(start->position[0]); float xSubstep = 1.0f - sw_fract(start->position[0]);
@ -5368,21 +5343,21 @@ static void SW_RASTER_TRIANGLE_SPAN(const sw_vertex_t *start, const sw_vertex_t
// Initializing the interpolation starting values // Initializing the interpolation starting values
#if (SW_RASTER_TRIANGLE_FLAGS) & SW_STATE_DEPTH_TEST #if (SW_RASTER_TRIANGLE_FLAGS) & SW_STATE_DEPTH_TEST
float z = start->position[2] + dZdx*xOffset; float z = start->position[2] + ctx->dZdx*xOffset;
#endif
#if SW_RASTER_TRIANGLE_NEEDS_PERSPECTIVE
float w = start->position[3] + ctx->dWdx*xOffset;
#endif #endif
float w = start->position[3] + dWdx*xOffset;
#if (SW_RASTER_TRIANGLE_FLAGS) & SW_STATE_COLOR_INTERP #if (SW_RASTER_TRIANGLE_FLAGS) & SW_STATE_COLOR_INTERP
float color[4] = { float color[4];
start->color[0] + dCdx[0]*xOffset, SW_VEC_OP(color[i] = start->color[i] + ctx->dCdx[i]*xOffset, i, 4);
start->color[1] + dCdx[1]*xOffset, #else
start->color[2] + dCdx[2]*xOffset, // Flat: constant across the whole triangle, computed once for the whole span
start->color[3] + dCdx[3]*xOffset const float *srcColor = ctx->flatColor;
};
#endif #endif
#if (SW_RASTER_TRIANGLE_FLAGS) & SW_STATE_TEXTURE_2D #if (SW_RASTER_TRIANGLE_FLAGS) & SW_STATE_TEXTURE_2D
float u = start->texcoord[0] + dUdx*xOffset; float texcoord[2];
float v = start->texcoord[1] + dVdx*xOffset; SW_VEC_OP(texcoord[i] = start->texcoord[i] + ctx->dTdx[i]*xOffset, i, 2);
#endif #endif
// Pre-calculate the starting pointers for the framebuffer row // Pre-calculate the starting pointers for the framebuffer row
@ -5397,6 +5372,7 @@ static void SW_RASTER_TRIANGLE_SPAN(const sw_vertex_t *start, const sw_vertex_t
int x = xLoopStart; int x = xLoopStart;
while (x < xLoopEnd) while (x < xLoopEnd)
{ {
#if SW_RASTER_TRIANGLE_NEEDS_PERSPECTIVE
// Clamp last block to remaining pixels // Clamp last block to remaining pixels
int blockEnd = x + SW_AFFINE_BLOCK; int blockEnd = x + SW_AFFINE_BLOCK;
if (blockEnd > xLoopEnd) blockEnd = xLoopEnd; if (blockEnd > xLoopEnd) blockEnd = xLoopEnd;
@ -5405,31 +5381,25 @@ static void SW_RASTER_TRIANGLE_SPAN(const sw_vertex_t *start, const sw_vertex_t
// Only 2 '1/w' here; none inside the pixel loop // Only 2 '1/w' here; none inside the pixel loop
float wRcpA = sw_rcp(w); float wRcpA = sw_rcp(w);
float wB = w + dWdx*blockLenF; float wB = w + ctx->dWdx*blockLenF;
float wRcpB = sw_rcp(wB); float wRcpB = sw_rcp(wB);
#else
// Nothing perspective-correct to interpolate: one block covers the whole span
int blockEnd = xLoopEnd;
#endif
#if (SW_RASTER_TRIANGLE_FLAGS) & SW_STATE_COLOR_INTERP #if (SW_RASTER_TRIANGLE_FLAGS) & SW_STATE_COLOR_INTERP
// Perspective-correct color at both block endpoints, then affine gradient // Perspective-correct color at both block endpoints, then affine gradient
float srcColor[4] = { float srcColor[4], dSrcColor[4];
color[0]*wRcpA, color[1]*wRcpA, color[2]*wRcpA, color[3]*wRcpA SW_VEC_OP(srcColor[i] = color[i]*wRcpA, i, 4);
}; SW_VEC_OP(dSrcColor[i] = ((color[i] + ctx->dCdx[i]*blockLenF)*wRcpB - srcColor[i])*blockLenRcp, i, 4);
float dSrcColordx[4] = {
((color[0] + dCdx[0]*blockLenF)*wRcpB - srcColor[0])*blockLenRcp,
((color[1] + dCdx[1]*blockLenF)*wRcpB - srcColor[1])*blockLenRcp,
((color[2] + dCdx[2]*blockLenF)*wRcpB - srcColor[2])*blockLenRcp,
((color[3] + dCdx[3]*blockLenF)*wRcpB - srcColor[3])*blockLenRcp
};
#else
// Flat: constant across the whole triangle, no perspective correction needed
const float *srcColor = flatColor;
#endif #endif
#if (SW_RASTER_TRIANGLE_FLAGS) & SW_STATE_TEXTURE_2D #if (SW_RASTER_TRIANGLE_FLAGS) & SW_STATE_TEXTURE_2D
// Perspective-correct UVs at both endpoints, then affine gradient // Perspective-correct UVs at both endpoints, then affine gradient
float uAffine = u*wRcpA; float tcAffine[2], dTcAffine[2];
float vAffine = v*wRcpA; SW_VEC_OP(tcAffine[i] = texcoord[i]*wRcpA, i, 2);
float dUaffine = ((u + dUdx*blockLenF)*wRcpB - uAffine)*blockLenRcp; SW_VEC_OP(dTcAffine[i] = ((texcoord[i] + ctx->dTdx[i]*blockLenF)*wRcpB - tcAffine[i])*blockLenRcp, i, 2);
float dVaffine = ((v + dVdx*blockLenF)*wRcpB - vAffine)*blockLenRcp;
#endif #endif
// Inner span pixel loop // Inner span pixel loop
@ -5445,14 +5415,10 @@ static void SW_RASTER_TRIANGLE_SPAN(const sw_vertex_t *start, const sw_vertex_t
#if (SW_RASTER_TRIANGLE_FLAGS) & SW_STATE_TEXTURE_2D #if (SW_RASTER_TRIANGLE_FLAGS) & SW_STATE_TEXTURE_2D
{ {
float texColor[4]; float finalColor[4];
sw_texture_sample(texColor, RLSW.boundTexture, uAffine, vAffine, dUdx, dUdy, dVdx, dVdy); ctx->sample(finalColor, RLSW.boundTexture, tcAffine[0], tcAffine[1]);
float finalColor[4] = { SW_VEC_OP(finalColor[i] *= srcColor[i], i, 4);
srcColor[0]*texColor[0],
srcColor[1]*texColor[1],
srcColor[2]*texColor[2],
srcColor[3]*texColor[3]
};
#if (SW_RASTER_TRIANGLE_FLAGS) & SW_STATE_BLEND #if (SW_RASTER_TRIANGLE_FLAGS) & SW_STATE_BLEND
{ {
float dstColor[4]; float dstColor[4];
@ -5481,42 +5447,30 @@ static void SW_RASTER_TRIANGLE_SPAN(const sw_vertex_t *start, const sw_vertex_t
#if (SW_RASTER_TRIANGLE_FLAGS) & SW_STATE_DEPTH_TEST #if (SW_RASTER_TRIANGLE_FLAGS) & SW_STATE_DEPTH_TEST
discard: discard:
#endif
#if (SW_RASTER_TRIANGLE_FLAGS) & SW_STATE_COLOR_INTERP
srcColor[0] += dSrcColordx[0];
srcColor[1] += dSrcColordx[1];
srcColor[2] += dSrcColordx[2];
srcColor[3] += dSrcColordx[3];
#endif #endif
cPtr += SW_FRAMEBUFFER_COLOR_SIZE; cPtr += SW_FRAMEBUFFER_COLOR_SIZE;
#if (SW_RASTER_TRIANGLE_FLAGS) & SW_STATE_DEPTH_TEST
#if (SW_RASTER_TRIANGLE_FLAGS) & SW_STATE_DEPTH_TEST dPtr += SW_FRAMEBUFFER_DEPTH_SIZE;
{ z += ctx->dZdx;
z += dZdx; #endif
dPtr += SW_FRAMEBUFFER_DEPTH_SIZE;
}
#endif
#if (SW_RASTER_TRIANGLE_FLAGS) & SW_STATE_TEXTURE_2D
{
uAffine += dUaffine;
vAffine += dVaffine;
}
#endif
}
// Advance perspective-space accumulators by the full block width
w = wB;
#if (SW_RASTER_TRIANGLE_FLAGS) & SW_STATE_COLOR_INTERP #if (SW_RASTER_TRIANGLE_FLAGS) & SW_STATE_COLOR_INTERP
color[0] += dCdx[0]*blockLenF; SW_VEC_OP(srcColor[i] += dSrcColor[i], i, 4);
color[1] += dCdx[1]*blockLenF;
color[2] += dCdx[2]*blockLenF;
color[3] += dCdx[3]*blockLenF;
#endif #endif
#if (SW_RASTER_TRIANGLE_FLAGS) & SW_STATE_TEXTURE_2D #if (SW_RASTER_TRIANGLE_FLAGS) & SW_STATE_TEXTURE_2D
u += dUdx*blockLenF; SW_VEC_OP(tcAffine[i] += dTcAffine[i], i, 2);
v += dVdx*blockLenF;
#endif #endif
}
#if SW_RASTER_TRIANGLE_NEEDS_PERSPECTIVE
// Advance perspective-space accumulators by the full block width
w = wB;
#if (SW_RASTER_TRIANGLE_FLAGS) & SW_STATE_COLOR_INTERP
SW_VEC_OP(color[i] += ctx->dCdx[i]*blockLenF, i, 4);
#endif
#if (SW_RASTER_TRIANGLE_FLAGS) & SW_STATE_TEXTURE_2D
SW_VEC_OP(texcoord[i] += ctx->dTdx[i]*blockLenF, i, 2);
#endif
#endif
} }
#undef SW_AFFINE_BLOCK #undef SW_AFFINE_BLOCK
@ -5539,13 +5493,47 @@ static void SW_RASTER_TRIANGLE(const sw_vertex_t *v0, const sw_vertex_t *v1, con
float h01 = y1 - y0; float h01 = y1 - y0;
float h12 = y2 - y1; float h12 = y2 - y1;
if (h02 < 1e-6f) return; if (h02 < 1e-6f) return; // Degenerate (zero screen height): nothing to rasterize
// Inverse edge dy for per-edge dV/dy (scanline interpolation) // Inverse edge dy for per-edge dV/dy (scanline interpolation)
float h02Rcp = sw_rcp(h02); float h02Rcp = sw_rcp(h02);
float h01Rcp = (h01 > 1e-6f)? sw_rcp(h01) : 0.0f; float h01Rcp = (h01 > 1e-6f)? sw_rcp(h01) : 0.0f;
float h12Rcp = (h12 > 1e-6f)? sw_rcp(h12) : 0.0f; float h12Rcp = (h12 > 1e-6f)? sw_rcp(h12) : 0.0f;
// D = twice the signed screen area; also the shared denominator of the
// barycentric plane solve used below for whole-triangle x/y derivatives
float e1x = v1->position[0] - v0->position[0];
float e2x = v2->position[0] - v0->position[0];
float D = e1x*h02 - e2x*h01;
if (fabsf(D) < 1e-9f) return; // Degenerate (zero screen area): nothing to rasterize
float DRcp = sw_rcp(D);
// Whole-triangle xy-derivatives of an affine attribute (constant for every scanline)
#define SW_DADX(A0, A1, A2) ((((A1)-(A0))*h02 - ((A2)-(A0))*h01)*DRcp)
#define SW_DADY(A0, A1, A2) ((((A2)-(A0))*e1x - ((A1)-(A0))*e2x)*DRcp)
SW_SPAN_CTX ctx;
#if SW_RASTER_TRIANGLE_NEEDS_PERSPECTIVE
ctx.dWdx = SW_DADX(v0->position[3], v1->position[3], v2->position[3]);
#endif
#if (SW_RASTER_TRIANGLE_FLAGS) & SW_STATE_DEPTH_TEST
ctx.dZdx = SW_DADX(v0->position[2], v1->position[2], v2->position[2]);
#endif
#if (SW_RASTER_TRIANGLE_FLAGS) & SW_STATE_COLOR_INTERP
SW_VEC_OP(ctx.dCdx[i] = SW_DADX(v0->color[i], v1->color[i], v2->color[i]), i, 4);
#else
float flatWRcp = sw_rcp(v0->position[3]);
SW_VEC_OP(ctx.flatColor[i] = v0->color[i]*flatWRcp, i, 4);
#endif
#if (SW_RASTER_TRIANGLE_FLAGS) & SW_STATE_TEXTURE_2D
SW_VEC_OP(ctx.dTdx[i] = SW_DADX(v0->texcoord[i], v1->texcoord[i], v2->texcoord[i]), i, 2);
SW_VEC_OP(ctx.dTdy[i] = SW_DADY(v0->texcoord[i], v1->texcoord[i], v2->texcoord[i]), i, 2);
ctx.sample = sw_texture_pick_sampler(RLSW.boundTexture, ctx.dTdx, ctx.dTdy);
#endif
#undef SW_DADX
#undef SW_DADY
// Compute gradients for each side of the triangle // Compute gradients for each side of the triangle
sw_vertex_t dVXdy02, dVXdy01, dVXdy12; sw_vertex_t dVXdy02, dVXdy01, dVXdy12;
SW_GRAD(&dVXdy02, v0, v2, h02Rcp); SW_GRAD(&dVXdy02, v0, v2, h02Rcp);
@ -5558,18 +5546,8 @@ static void SW_RASTER_TRIANGLE(const sw_vertex_t *v0, const sw_vertex_t *v1, con
// Get a copy of vertices for interpolation and apply substep correction // Get a copy of vertices for interpolation and apply substep correction
sw_vertex_t lVert = *v0, rVert = *v0; sw_vertex_t lVert = *v0, rVert = *v0;
SW_VFMA(&lVert, &dVXdy02, y0Substep); SW_VMAD(&lVert, &dVXdy02, y0Substep);
SW_VFMA(&rVert, &dVXdy01, y0Substep); SW_VMAD(&rVert, &dVXdy01, y0Substep);
#if !((SW_RASTER_TRIANGLE_FLAGS) & SW_STATE_COLOR_INTERP)
float invW = 1.0f / v0->position[3];
float flatColor[4] = {
v0->color[0]*invW,
v0->color[1]*invW,
v0->color[2]*invW,
v0->color[3]*invW
};
#endif
// Y bounds (vertical clipping) // Y bounds (vertical clipping)
int yTop = (int)y0; int yTop = (int)y0;
@ -5583,14 +5561,14 @@ static void SW_RASTER_TRIANGLE(const sw_vertex_t *v0, const sw_vertex_t *v1, con
bool longSideIsLeft = (lVert.position[0] < rVert.position[0]); bool longSideIsLeft = (lVert.position[0] < rVert.position[0]);
const sw_vertex_t *a = longSideIsLeft? &lVert : &rVert; const sw_vertex_t *a = longSideIsLeft? &lVert : &rVert;
const sw_vertex_t *b = longSideIsLeft? &rVert : &lVert; const sw_vertex_t *b = longSideIsLeft? &rVert : &lVert;
SW_RASTER_TRIANGLE_SPAN(a, b, dVXdy02.texcoord[0], dVXdy02.texcoord[1] SW_SPAN_FLAT_COLOR_ARG); SW_RASTER_TRIANGLE_SPAN(a, b, &ctx);
SW_VADD(&lVert, &dVXdy02); SW_VADD(&lVert, &dVXdy02);
SW_VADD(&rVert, &dVXdy01); SW_VADD(&rVert, &dVXdy01);
} }
// Get a copy of next right for interpolation and apply substep correction // Get a copy of next right for interpolation and apply substep correction
rVert = *v1; rVert = *v1;
SW_VFMA(&rVert, &dVXdy12, y1Substep); SW_VMAD(&rVert, &dVXdy12, y1Substep);
// Scanline for the lower part of the triangle // Scanline for the lower part of the triangle
for (int y = yMid; y < yBot; y++) for (int y = yMid; y < yBot; y++)
@ -5599,15 +5577,12 @@ static void SW_RASTER_TRIANGLE(const sw_vertex_t *v0, const sw_vertex_t *v1, con
bool longSideIsLeft = (lVert.position[0] < rVert.position[0]); bool longSideIsLeft = (lVert.position[0] < rVert.position[0]);
const sw_vertex_t *a = longSideIsLeft? &lVert : &rVert; const sw_vertex_t *a = longSideIsLeft? &lVert : &rVert;
const sw_vertex_t *b = longSideIsLeft? &rVert : &lVert; const sw_vertex_t *b = longSideIsLeft? &rVert : &lVert;
SW_RASTER_TRIANGLE_SPAN(a, b, dVXdy02.texcoord[0], dVXdy02.texcoord[1] SW_SPAN_FLAT_COLOR_ARG); SW_RASTER_TRIANGLE_SPAN(a, b, &ctx);
SW_VADD(&lVert, &dVXdy02); SW_VADD(&lVert, &dVXdy02);
SW_VADD(&rVert, &dVXdy12); SW_VADD(&rVert, &dVXdy12);
} }
} }
#undef SW_SPAN_FLAT_COLOR_PARAM
#undef SW_SPAN_FLAT_COLOR_ARG
#endif // SW_RASTER_TRIANGLE_STATES #endif // SW_RASTER_TRIANGLE_STATES
//------------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------------
@ -5662,46 +5637,30 @@ static void SW_RASTER_QUAD(const sw_vertex_t *a, const sw_vertex_t *b,
float xSubstep = 1.0f - sw_fract(tl->position[0]); float xSubstep = 1.0f - sw_fract(tl->position[0]);
float ySubstep = 1.0f - sw_fract(tl->position[1]); float ySubstep = 1.0f - sw_fract(tl->position[1]);
#if (SW_RASTER_QUAD_FLAGS) & SW_STATE_COLOR_INTERP
// Gradients along X (tl->tr) and Y (tl->bl)
float dCdx[4] = {
(tr->color[0] - tl->color[0])*wRcp,
(tr->color[1] - tl->color[1])*wRcp,
(tr->color[2] - tl->color[2])*wRcp,
(tr->color[3] - tl->color[3])*wRcp,
};
float dCdy[4] = {
(bl->color[0] - tl->color[0])*hRcp,
(bl->color[1] - tl->color[1])*hRcp,
(bl->color[2] - tl->color[2])*hRcp,
(bl->color[3] - tl->color[3])*hRcp,
};
#else
const float *flatColor = tl->color;
#endif
#if (SW_RASTER_QUAD_FLAGS) & SW_STATE_DEPTH_TEST #if (SW_RASTER_QUAD_FLAGS) & SW_STATE_DEPTH_TEST
// Gradients along X (tl->tr) and Y (tl->bl)
float dZdx = (tr->position[2] - tl->position[2])*wRcp; float dZdx = (tr->position[2] - tl->position[2])*wRcp;
float dZdy = (bl->position[2] - tl->position[2])*hRcp; float dZdy = (bl->position[2] - tl->position[2])*hRcp;
float zRow = tl->position[2] + dZdx*xSubstep + dZdy*ySubstep; float zRow = tl->position[2] + dZdx*xSubstep + dZdy*ySubstep;
#endif #endif
#if (SW_RASTER_QUAD_FLAGS) & SW_STATE_TEXTURE_2D #if (SW_RASTER_QUAD_FLAGS) & SW_STATE_COLOR_INTERP
float dUdx = (tr->texcoord[0] - tl->texcoord[0])*wRcp; float dCdx[4], dCdy[4], cRow[4];
float dVdx = (tr->texcoord[1] - tl->texcoord[1])*wRcp; SW_VEC_OP(dCdx[i] = (tr->color[i] - tl->color[i])*wRcp, i, 4);
float dUdy = (bl->texcoord[0] - tl->texcoord[0])*hRcp; SW_VEC_OP(dCdy[i] = (bl->color[i] - tl->color[i])*hRcp, i, 4);
float dVdy = (bl->texcoord[1] - tl->texcoord[1])*hRcp; SW_VEC_OP(cRow[i] = tl->color[i] + dCdx[i]*xSubstep + dCdy[i]*ySubstep, i, 4);
float uRow = tl->texcoord[0] + dUdx*xSubstep + dUdy*ySubstep; #else
float vRow = tl->texcoord[1] + dVdx*xSubstep + dVdy*ySubstep; const float *flatColor = tl->color;
#endif #endif
#if (SW_RASTER_QUAD_FLAGS) & SW_STATE_COLOR_INTERP #if (SW_RASTER_QUAD_FLAGS) & SW_STATE_TEXTURE_2D
float cRow[4] = { float dTdx[2], dTdy[2], tcRow[2];
tl->color[0] + dCdx[0]*xSubstep + dCdy[0]*ySubstep, SW_VEC_OP(dTdx[i] = (tr->texcoord[i] - tl->texcoord[i])*wRcp, i, 2);
tl->color[1] + dCdx[1]*xSubstep + dCdy[1]*ySubstep, SW_VEC_OP(dTdy[i] = (bl->texcoord[i] - tl->texcoord[i])*hRcp, i, 2);
tl->color[2] + dCdx[2]*xSubstep + dCdy[2]*ySubstep, SW_VEC_OP(tcRow[i] = tl->texcoord[i] + dTdx[i]*xSubstep + dTdy[i]*ySubstep, i, 2);
tl->color[3] + dCdx[3]*xSubstep + dCdy[3]*ySubstep,
}; // Constant across the whole quad: resolved once, called directly per pixel
sw_texture_sampler_f sample = sw_texture_pick_sampler(RLSW.boundTexture, dTdx, dTdy);
#endif #endif
int stride = RLSW.colorBuffer->width; int stride = RLSW.colorBuffer->width;
@ -5715,19 +5674,15 @@ static void SW_RASTER_QUAD(const sw_vertex_t *a, const sw_vertex_t *b,
float dyMin = (float)(yLoopMin - yMin); float dyMin = (float)(yLoopMin - yMin);
// Correct our start by how far it's clipped outside the framebuffer // Correct our start by how far it's clipped outside the framebuffer
#if (SW_RASTER_QUAD_FLAGS) & SW_STATE_COLOR_INTERP #if (SW_RASTER_QUAD_FLAGS) & SW_STATE_DEPTH_TEST
cRow[0] += dCdx[0]*dxMin + dCdy[0]*dyMin;
cRow[1] += dCdx[1]*dxMin + dCdy[1]*dyMin;
cRow[2] += dCdx[2]*dxMin + dCdy[2]*dyMin;
cRow[3] += dCdx[3]*dxMin + dCdy[3]*dyMin;
#endif
#if (SW_RASTER_QUAD_FLAGS) & SW_STATE_DEPTH_TEST
zRow += dZdy*dyMin + dZdx*dxMin; zRow += dZdy*dyMin + dZdx*dxMin;
#endif #endif
#if (SW_RASTER_QUAD_FLAGS) & SW_STATE_TEXTURE_2D #if (SW_RASTER_QUAD_FLAGS) & SW_STATE_COLOR_INTERP
uRow += dUdy*dyMin + dUdx*dxMin; SW_VEC_OP(cRow[i] += dCdx[i]*dxMin + dCdy[i]*dyMin, i, 4);
vRow += dVdy*dyMin + dVdx*dxMin; #endif
#endif #if (SW_RASTER_QUAD_FLAGS) & SW_STATE_TEXTURE_2D
SW_VEC_OP(tcRow[i] += dTdy[i]*dyMin + dTdx[i]*dxMin, i, 2);
#endif
for (int y = yLoopMin; y < yLoopMax; y++) for (int y = yLoopMin; y < yLoopMax; y++)
{ {
@ -5735,12 +5690,10 @@ static void SW_RASTER_QUAD(const sw_vertex_t *a, const sw_vertex_t *b,
uint8_t *cPtr = cPixels + baseOffset*SW_FRAMEBUFFER_COLOR_SIZE; uint8_t *cPtr = cPixels + baseOffset*SW_FRAMEBUFFER_COLOR_SIZE;
#if (SW_RASTER_QUAD_FLAGS) & SW_STATE_DEPTH_TEST #if (SW_RASTER_QUAD_FLAGS) & SW_STATE_DEPTH_TEST
uint8_t *dPtr = dPixels + baseOffset*SW_FRAMEBUFFER_DEPTH_SIZE; uint8_t *dPtr = dPixels + baseOffset*SW_FRAMEBUFFER_DEPTH_SIZE;
// Copy the cursors without destroying the offset maths
float z = zRow; float z = zRow;
#endif #endif
#if (SW_RASTER_QUAD_FLAGS) & SW_STATE_TEXTURE_2D #if (SW_RASTER_QUAD_FLAGS) & SW_STATE_TEXTURE_2D
float u = uRow; float tc[2] = { tcRow[0], tcRow[1] };
float v = vRow;
#endif #endif
#if (SW_RASTER_QUAD_FLAGS) & SW_STATE_COLOR_INTERP #if (SW_RASTER_QUAD_FLAGS) & SW_STATE_COLOR_INTERP
float color[4] = { cRow[0], cRow[1], cRow[2], cRow[3] }; float color[4] = { cRow[0], cRow[1], cRow[2], cRow[3] };
@ -5749,9 +5702,18 @@ static void SW_RASTER_QUAD(const sw_vertex_t *a, const sw_vertex_t *b,
for (int x = xLoopMin; x < xLoopMax; x++) for (int x = xLoopMin; x < xLoopMax; x++)
{ {
#if (SW_RASTER_QUAD_FLAGS) & SW_STATE_COLOR_INTERP #if (SW_RASTER_QUAD_FLAGS) & SW_STATE_COLOR_INTERP
float srcColor[4] = { color[0], color[1], color[2], color[3] }; #define SW_QUAD_BASE_COLOR color
#else #else
float srcColor[4] = { flatColor[0], flatColor[1], flatColor[2], flatColor[3] }; #define SW_QUAD_BASE_COLOR flatColor
#endif
#if (SW_RASTER_QUAD_FLAGS) & SW_STATE_TEXTURE_2D
// Mutable copy: about to be modulated by the sampled texel below
float srcColor[4];
SW_VEC_OP(srcColor[i] = SW_QUAD_BASE_COLOR[i], i, 4);
#else
// Never mutated past this point: alias directly, no copy needed
const float *srcColor = SW_QUAD_BASE_COLOR;
#endif #endif
#if (SW_RASTER_QUAD_FLAGS) & SW_STATE_DEPTH_TEST #if (SW_RASTER_QUAD_FLAGS) & SW_STATE_DEPTH_TEST
@ -5765,11 +5727,8 @@ static void SW_RASTER_QUAD(const sw_vertex_t *a, const sw_vertex_t *b,
#if (SW_RASTER_QUAD_FLAGS) & SW_STATE_TEXTURE_2D #if (SW_RASTER_QUAD_FLAGS) & SW_STATE_TEXTURE_2D
{ {
float texColor[4]; float texColor[4];
sw_texture_sample(texColor, RLSW.boundTexture, u, v, dUdx, dUdy, dVdx, dVdy); sample(texColor, RLSW.boundTexture, tc[0], tc[1]);
srcColor[0] *= texColor[0]; SW_VEC_OP(srcColor[i] *= texColor[i], i, 4);
srcColor[1] *= texColor[1];
srcColor[2] *= texColor[2];
srcColor[3] *= texColor[3];
} }
#endif #endif
@ -5791,25 +5750,17 @@ static void SW_RASTER_QUAD(const sw_vertex_t *a, const sw_vertex_t *b,
#endif #endif
// Move one pixel over without touching the original "start offset" // Move one pixel over without touching the original "start offset"
#if (SW_RASTER_QUAD_FLAGS) & SW_STATE_COLOR_INTERP #if (SW_RASTER_QUAD_FLAGS) & SW_STATE_COLOR_INTERP
color[0] += dCdx[0]; SW_VEC_OP(color[i] += dCdx[i], i, 4);
color[1] += dCdx[1];
color[2] += dCdx[2];
color[3] += dCdx[3];
#endif #endif
#if (SW_RASTER_QUAD_FLAGS) & SW_STATE_DEPTH_TEST #if (SW_RASTER_QUAD_FLAGS) & SW_STATE_DEPTH_TEST
{ z += dZdx;
z += dZdx; dPtr += SW_FRAMEBUFFER_DEPTH_SIZE;
dPtr += SW_FRAMEBUFFER_DEPTH_SIZE; #endif
}
#endif
#if (SW_RASTER_QUAD_FLAGS) & SW_STATE_TEXTURE_2D #if (SW_RASTER_QUAD_FLAGS) & SW_STATE_TEXTURE_2D
{ SW_VEC_OP(tc[i] += dTdx[i], i, 2);
u += dUdx; #endif
v += dVdx;
}
#endif
cPtr += SW_FRAMEBUFFER_COLOR_SIZE; cPtr += SW_FRAMEBUFFER_COLOR_SIZE;
} }
@ -5817,26 +5768,18 @@ static void SW_RASTER_QUAD(const sw_vertex_t *a, const sw_vertex_t *b,
// The for loop is clamped to the right side of the screen // The for loop is clamped to the right side of the screen
// However, these cursor start vars are still on the left // However, these cursor start vars are still on the left
// That's fine, advancing to the next row // That's fine, advancing to the next row
#if (SW_RASTER_QUAD_FLAGS) & SW_STATE_DEPTH_TEST
zRow += dZdy;
#endif
#if (SW_RASTER_QUAD_FLAGS) & SW_STATE_COLOR_INTERP #if (SW_RASTER_QUAD_FLAGS) & SW_STATE_COLOR_INTERP
cRow[0] += dCdy[0]; SW_VEC_OP(cRow[i] += dCdy[i], i, 4);
cRow[1] += dCdy[1]; #endif
cRow[2] += dCdy[2]; #if (SW_RASTER_QUAD_FLAGS) & SW_STATE_TEXTURE_2D
cRow[3] += dCdy[3]; SW_VEC_OP(tcRow[i] += dTdy[i], i, 2);
#endif #endif
#if (SW_RASTER_QUAD_FLAGS) & SW_STATE_DEPTH_TEST
{
zRow += dZdy;
}
#endif
#if (SW_RASTER_QUAD_FLAGS) & SW_STATE_TEXTURE_2D
{
uRow += dUdy;
vRow += dVdy;
}
#endif
} }
#undef SW_QUAD_BASE_COLOR
} }
#endif // SW_RASTER_QUAD_STATES #endif // SW_RASTER_QUAD_STATES