From b5ef080f89a8b268c9b5e03db842da97dcd4117c Mon Sep 17 00:00:00 2001 From: Bigfoot71 Date: Sat, 18 Jul 2026 23:03:31 +0200 Subject: [PATCH] vector op helper macro + pick sampler once before primitive drawing --- src/external/rlsw.h | 475 +++++++++++++++++++------------------------- 1 file changed, 209 insertions(+), 266 deletions(-) diff --git a/src/external/rlsw.h b/src/external/rlsw.h index c27fab8f9..60ef52fa3 100644 --- a/src/external/rlsw.h +++ b/src/external/rlsw.h @@ -793,14 +793,14 @@ SWAPI void swGetFramebufferAttachmentParameteriv(SWattachment attachment, SWatta #if defined(NDEBUG) #if defined(_MSC_VER) - #define SW_FORCE_INLINE __forceinline + #define SW_INLINE __forceinline #elif defined(__GNUC__) || defined(__clang__) - #define SW_FORCE_INLINE inline __attribute__((always_inline)) + #define SW_INLINE inline __attribute__((always_inline)) #else - #define SW_FORCE_INLINE inline + #define SW_INLINE inline #endif #else - #define SW_FORCE_INLINE inline + #define SW_INLINE inline #endif #if defined(_M_X64) || defined(__x86_64__) @@ -966,6 +966,7 @@ typedef enum { } sw_pixel_alpha_t; // Forward declarations +typedef struct sw_texture sw_texture_t; typedef struct sw_vertex sw_vertex_t; // 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_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 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 } sw_vertex_t; -typedef struct { +typedef struct sw_texture { void *pixels; // Texture pixels sw_pixel_read_color8_f readColor8; // Texel read RGBA8 sw_pixel_read_color_f readColor; // Texel read RGBA32F @@ -1167,6 +1171,13 @@ static const int SW_PRIMITIVE_VERTEX_COUNT[] = // 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 //---------------------------------------------------------------------------------- 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)); } -static SW_FORCE_INLINE float sw_rcp(float x) +static SW_INLINE float sw_rcp(float x) { #if defined(__XTENSA__) // 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, - float u, float v, float dUdx, float dUdy, float dVdx, float dVdy) +// Resolves which sample function to use from the texel-to-pixel derivative magnitude +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 // 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 L = (du > dv)? du : dv; - // Calculate the derivatives for each axis - float dU2 = dUdx*dUdx + dUdy*dUdy; - float dV2 = dVdx*dVdx + dVdy*dVdy; + float dU2 = dTdx[0]*dTdx[0] + dTdy[0]*dTdy[0]; + float dV2 = dTdx[1]*dTdx[1] + dTdy[1]*dTdy[1]; float L2 = (dU2 > dV2)? dU2 : dV2; SWfilter filter = (L2 > 1.0f)? tex->minFilter : tex->magFilter; - - 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; - } + return (filter == SW_LINEAR)? sw_texture_sample_linear : sw_texture_sample_nearest; } //------------------------------------------------------------------------------------------- @@ -5247,82 +5251,80 @@ void swGetFramebufferAttachmentParameteriv(SWattachment attachment, SWattachget #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 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 - #define SW_SPAN_FLAT_COLOR_PARAM - #define SW_SPAN_FLAT_COLOR_ARG -#else - #define SW_SPAN_FLAT_COLOR_PARAM , const float flatColor[4] - #define SW_SPAN_FLAT_COLOR_ARG , flatColor -#endif +// True when a perspective-correct interpolation pass is needed (color or UVs); +// lets SPAN skip all w-tracking and block subdivision otherwise +#define SW_RASTER_TRIANGLE_NEEDS_PERSPECTIVE \ + (((SW_RASTER_TRIANGLE_FLAGS) & SW_STATE_COLOR_INTERP) || ((SW_RASTER_TRIANGLE_FLAGS) & SW_STATE_TEXTURE_2D)) #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_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; - out->position[1] = (b->position[1] - a->position[1])*scale; - out->position[2] = (b->position[2] - a->position[2])*scale; - out->position[3] = (b->position[3] - a->position[3])*scale; - +#if SW_RASTER_TRIANGLE_NEEDS_PERSPECTIVE + float dWdx; +#endif +#if (SW_RASTER_TRIANGLE_FLAGS) & SW_STATE_DEPTH_TEST + float dZdx; +#endif #if (SW_RASTER_TRIANGLE_FLAGS) & SW_STATE_COLOR_INTERP - out->color[0] = (b->color[0] - a->color[0])*scale; - out->color[1] = (b->color[1] - a->color[1])*scale; - out->color[2] = (b->color[2] - a->color[2])*scale; - out->color[3] = (b->color[3] - a->color[3])*scale; + float dCdx[4]; +#else + float flatColor[4]; #endif - #if (SW_RASTER_TRIANGLE_FLAGS) & SW_STATE_TEXTURE_2D - out->texcoord[0] = (b->texcoord[0] - a->texcoord[0])*scale; - out->texcoord[1] = (b->texcoord[1] - a->texcoord[1])*scale; + float dTdx[2]; + 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 } -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]; - out->position[1] += gradients->position[1]; - out->position[2] += gradients->position[2]; - out->position[3] += gradients->position[3]; - + SW_VEC_OP(out->position[i] += inc->position[i], i, 4); #if (SW_RASTER_TRIANGLE_FLAGS) & SW_STATE_COLOR_INTERP - out->color[0] += gradients->color[0]; - out->color[1] += gradients->color[1]; - out->color[2] += gradients->color[2]; - out->color[3] += gradients->color[3]; + SW_VEC_OP(out->color[i] += inc->color[i], i, 4); #endif - #if (SW_RASTER_TRIANGLE_FLAGS) & SW_STATE_TEXTURE_2D - out->texcoord[0] += gradients->texcoord[0]; - out->texcoord[1] += gradients->texcoord[1]; + SW_VEC_OP(out->texcoord[i] += inc->texcoord[i], i, 2); #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; - out->position[1] += gradients->position[1]*scale; - out->position[2] += gradients->position[2]*scale; - out->position[3] += gradients->position[3]*scale; - + SW_VEC_OP(out->position[i] += inc->position[i]*scale, i, 4); #if (SW_RASTER_TRIANGLE_FLAGS) & SW_STATE_COLOR_INTERP - out->color[0] += gradients->color[0]*scale; - out->color[1] += gradients->color[1]*scale; - out->color[2] += gradients->color[2]*scale; - out->color[3] += gradients->color[3]*scale; + SW_VEC_OP(out->color[i] += inc->color[i]*scale, i, 4); #endif - #if (SW_RASTER_TRIANGLE_FLAGS) & SW_STATE_TEXTURE_2D - out->texcoord[0] += gradients->texcoord[0]*scale; - out->texcoord[1] += gradients->texcoord[1]*scale; + SW_VEC_OP(out->texcoord[i] += inc->texcoord[i]*scale, i, 2); #endif } -static void SW_RASTER_TRIANGLE_SPAN(const sw_vertex_t *start, const sw_vertex_t *end, - float dUdy, float dVdy SW_SPAN_FLAT_COLOR_PARAM) +static void SW_RASTER_TRIANGLE_SPAN(const sw_vertex_t *start, const sw_vertex_t *end, const SW_SPAN_CTX *ctx) { + // 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 int xStart = (int)start->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; 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 // Also step further into them to move away from the colorbuffer edge 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 #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 - float w = start->position[3] + dWdx*xOffset; - #if (SW_RASTER_TRIANGLE_FLAGS) & SW_STATE_COLOR_INTERP - float color[4] = { - start->color[0] + dCdx[0]*xOffset, - start->color[1] + dCdx[1]*xOffset, - start->color[2] + dCdx[2]*xOffset, - start->color[3] + dCdx[3]*xOffset - }; + float color[4]; + SW_VEC_OP(color[i] = start->color[i] + ctx->dCdx[i]*xOffset, i, 4); +#else + // Flat: constant across the whole triangle, computed once for the whole span + const float *srcColor = ctx->flatColor; #endif #if (SW_RASTER_TRIANGLE_FLAGS) & SW_STATE_TEXTURE_2D - float u = start->texcoord[0] + dUdx*xOffset; - float v = start->texcoord[1] + dVdx*xOffset; + float texcoord[2]; + SW_VEC_OP(texcoord[i] = start->texcoord[i] + ctx->dTdx[i]*xOffset, i, 2); #endif // 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; while (x < xLoopEnd) { + #if SW_RASTER_TRIANGLE_NEEDS_PERSPECTIVE // Clamp last block to remaining pixels int blockEnd = x + SW_AFFINE_BLOCK; 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 float wRcpA = sw_rcp(w); - float wB = w + dWdx*blockLenF; + float wB = w + ctx->dWdx*blockLenF; 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 // Perspective-correct color at both block endpoints, then affine gradient - float srcColor[4] = { - color[0]*wRcpA, color[1]*wRcpA, color[2]*wRcpA, color[3]*wRcpA - }; - 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; + float srcColor[4], dSrcColor[4]; + 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); #endif #if (SW_RASTER_TRIANGLE_FLAGS) & SW_STATE_TEXTURE_2D // Perspective-correct UVs at both endpoints, then affine gradient - float uAffine = u*wRcpA; - float vAffine = v*wRcpA; - float dUaffine = ((u + dUdx*blockLenF)*wRcpB - uAffine)*blockLenRcp; - float dVaffine = ((v + dVdx*blockLenF)*wRcpB - vAffine)*blockLenRcp; + float tcAffine[2], dTcAffine[2]; + SW_VEC_OP(tcAffine[i] = texcoord[i]*wRcpA, i, 2); + SW_VEC_OP(dTcAffine[i] = ((texcoord[i] + ctx->dTdx[i]*blockLenF)*wRcpB - tcAffine[i])*blockLenRcp, i, 2); #endif // 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 { - float texColor[4]; - sw_texture_sample(texColor, RLSW.boundTexture, uAffine, vAffine, dUdx, dUdy, dVdx, dVdy); - float finalColor[4] = { - srcColor[0]*texColor[0], - srcColor[1]*texColor[1], - srcColor[2]*texColor[2], - srcColor[3]*texColor[3] - }; + float finalColor[4]; + ctx->sample(finalColor, RLSW.boundTexture, tcAffine[0], tcAffine[1]); + SW_VEC_OP(finalColor[i] *= srcColor[i], i, 4); + #if (SW_RASTER_TRIANGLE_FLAGS) & SW_STATE_BLEND { 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 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 cPtr += SW_FRAMEBUFFER_COLOR_SIZE; - - #if (SW_RASTER_TRIANGLE_FLAGS) & SW_STATE_DEPTH_TEST - { - z += dZdx; - 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_DEPTH_TEST + dPtr += SW_FRAMEBUFFER_DEPTH_SIZE; + z += ctx->dZdx; + #endif #if (SW_RASTER_TRIANGLE_FLAGS) & SW_STATE_COLOR_INTERP - color[0] += dCdx[0]*blockLenF; - color[1] += dCdx[1]*blockLenF; - color[2] += dCdx[2]*blockLenF; - color[3] += dCdx[3]*blockLenF; + SW_VEC_OP(srcColor[i] += dSrcColor[i], i, 4); #endif #if (SW_RASTER_TRIANGLE_FLAGS) & SW_STATE_TEXTURE_2D - u += dUdx*blockLenF; - v += dVdx*blockLenF; + SW_VEC_OP(tcAffine[i] += dTcAffine[i], i, 2); #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 @@ -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 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) float h02Rcp = sw_rcp(h02); float h01Rcp = (h01 > 1e-6f)? sw_rcp(h01) : 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 sw_vertex_t dVXdy02, dVXdy01, dVXdy12; 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 sw_vertex_t lVert = *v0, rVert = *v0; - SW_VFMA(&lVert, &dVXdy02, y0Substep); - SW_VFMA(&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 + SW_VMAD(&lVert, &dVXdy02, y0Substep); + SW_VMAD(&rVert, &dVXdy01, y0Substep); // Y bounds (vertical clipping) 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]); const sw_vertex_t *a = longSideIsLeft? &lVert : &rVert; 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(&rVert, &dVXdy01); } // Get a copy of next right for interpolation and apply substep correction rVert = *v1; - SW_VFMA(&rVert, &dVXdy12, y1Substep); + SW_VMAD(&rVert, &dVXdy12, y1Substep); // Scanline for the lower part of the triangle 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]); const sw_vertex_t *a = longSideIsLeft? &lVert : &rVert; 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(&rVert, &dVXdy12); } } -#undef SW_SPAN_FLAT_COLOR_PARAM -#undef SW_SPAN_FLAT_COLOR_ARG - #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 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 + // Gradients along X (tl->tr) and Y (tl->bl) float dZdx = (tr->position[2] - tl->position[2])*wRcp; float dZdy = (bl->position[2] - tl->position[2])*hRcp; float zRow = tl->position[2] + dZdx*xSubstep + dZdy*ySubstep; #endif -#if (SW_RASTER_QUAD_FLAGS) & SW_STATE_TEXTURE_2D - float dUdx = (tr->texcoord[0] - tl->texcoord[0])*wRcp; - float dVdx = (tr->texcoord[1] - tl->texcoord[1])*wRcp; - float dUdy = (bl->texcoord[0] - tl->texcoord[0])*hRcp; - float dVdy = (bl->texcoord[1] - tl->texcoord[1])*hRcp; - float uRow = tl->texcoord[0] + dUdx*xSubstep + dUdy*ySubstep; - float vRow = tl->texcoord[1] + dVdx*xSubstep + dVdy*ySubstep; +#if (SW_RASTER_QUAD_FLAGS) & SW_STATE_COLOR_INTERP + float dCdx[4], dCdy[4], cRow[4]; + SW_VEC_OP(dCdx[i] = (tr->color[i] - tl->color[i])*wRcp, i, 4); + SW_VEC_OP(dCdy[i] = (bl->color[i] - tl->color[i])*hRcp, i, 4); + SW_VEC_OP(cRow[i] = tl->color[i] + dCdx[i]*xSubstep + dCdy[i]*ySubstep, i, 4); +#else + const float *flatColor = tl->color; #endif -#if (SW_RASTER_QUAD_FLAGS) & SW_STATE_COLOR_INTERP - float cRow[4] = { - tl->color[0] + dCdx[0]*xSubstep + dCdy[0]*ySubstep, - tl->color[1] + dCdx[1]*xSubstep + dCdy[1]*ySubstep, - tl->color[2] + dCdx[2]*xSubstep + dCdy[2]*ySubstep, - tl->color[3] + dCdx[3]*xSubstep + dCdy[3]*ySubstep, - }; +#if (SW_RASTER_QUAD_FLAGS) & SW_STATE_TEXTURE_2D + float dTdx[2], dTdy[2], tcRow[2]; + SW_VEC_OP(dTdx[i] = (tr->texcoord[i] - tl->texcoord[i])*wRcp, i, 2); + SW_VEC_OP(dTdy[i] = (bl->texcoord[i] - tl->texcoord[i])*hRcp, i, 2); + SW_VEC_OP(tcRow[i] = tl->texcoord[i] + dTdx[i]*xSubstep + dTdy[i]*ySubstep, i, 2); + + // 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 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); // Correct our start by how far it's clipped outside the framebuffer -#if (SW_RASTER_QUAD_FLAGS) & SW_STATE_COLOR_INTERP - 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 +#if (SW_RASTER_QUAD_FLAGS) & SW_STATE_DEPTH_TEST zRow += dZdy*dyMin + dZdx*dxMin; - #endif - #if (SW_RASTER_QUAD_FLAGS) & SW_STATE_TEXTURE_2D - uRow += dUdy*dyMin + dUdx*dxMin; - vRow += dVdy*dyMin + dVdx*dxMin; - #endif +#endif +#if (SW_RASTER_QUAD_FLAGS) & SW_STATE_COLOR_INTERP + SW_VEC_OP(cRow[i] += dCdx[i]*dxMin + dCdy[i]*dyMin, i, 4); +#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++) { @@ -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; #if (SW_RASTER_QUAD_FLAGS) & SW_STATE_DEPTH_TEST uint8_t *dPtr = dPixels + baseOffset*SW_FRAMEBUFFER_DEPTH_SIZE; - // Copy the cursors without destroying the offset maths float z = zRow; #endif #if (SW_RASTER_QUAD_FLAGS) & SW_STATE_TEXTURE_2D - float u = uRow; - float v = vRow; + float tc[2] = { tcRow[0], tcRow[1] }; #endif #if (SW_RASTER_QUAD_FLAGS) & SW_STATE_COLOR_INTERP 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++) { #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 - 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 #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 { float texColor[4]; - sw_texture_sample(texColor, RLSW.boundTexture, u, v, dUdx, dUdy, dVdx, dVdy); - srcColor[0] *= texColor[0]; - srcColor[1] *= texColor[1]; - srcColor[2] *= texColor[2]; - srcColor[3] *= texColor[3]; + sample(texColor, RLSW.boundTexture, tc[0], tc[1]); + SW_VEC_OP(srcColor[i] *= texColor[i], i, 4); } #endif @@ -5791,25 +5750,17 @@ static void SW_RASTER_QUAD(const sw_vertex_t *a, const sw_vertex_t *b, #endif // Move one pixel over without touching the original "start offset" #if (SW_RASTER_QUAD_FLAGS) & SW_STATE_COLOR_INTERP - color[0] += dCdx[0]; - color[1] += dCdx[1]; - color[2] += dCdx[2]; - color[3] += dCdx[3]; + SW_VEC_OP(color[i] += dCdx[i], i, 4); #endif - #if (SW_RASTER_QUAD_FLAGS) & SW_STATE_DEPTH_TEST - { - z += dZdx; - dPtr += SW_FRAMEBUFFER_DEPTH_SIZE; - } - #endif + #if (SW_RASTER_QUAD_FLAGS) & SW_STATE_DEPTH_TEST + z += dZdx; + dPtr += SW_FRAMEBUFFER_DEPTH_SIZE; + #endif - #if (SW_RASTER_QUAD_FLAGS) & SW_STATE_TEXTURE_2D - { - u += dUdx; - v += dVdx; - } - #endif + #if (SW_RASTER_QUAD_FLAGS) & SW_STATE_TEXTURE_2D + SW_VEC_OP(tc[i] += dTdx[i], i, 2); + #endif 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 // However, these cursor start vars are still on the left // 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 - cRow[0] += dCdy[0]; - cRow[1] += dCdy[1]; - cRow[2] += dCdy[2]; - cRow[3] += dCdy[3]; + SW_VEC_OP(cRow[i] += dCdy[i], i, 4); + #endif + #if (SW_RASTER_QUAD_FLAGS) & SW_STATE_TEXTURE_2D + SW_VEC_OP(tcRow[i] += dTdy[i], i, 2); #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