[rlsw] Add sw_rcp helper using Xtensa recip0.s for hot-path divisions
Adds a `sw_rcp(x)` inline reciprocal that on Xtensa (ESP32 / ESP32-S3 LX6/LX7) emits a `recip0.s` seed plus two Newton-Raphson refinement steps -- 1-ULP accurate in ~7 instructions, all in FPU registers. On every other target it expands to plain `1.0f/x`, so generated code is byte-identical to before for non-Xtensa builds. Replaces the hot-path `1.0f/x` calls that were previously compiling to the `__divsf3` software helper on Xtensa: - perspective divide (1/w) in triangle clip-and-project (PCT and PC paths) - line and point clip-and-project NDC conversion - triangle span setup: dxRcp, blockLenRcp, wRcpA, wRcpB - triangle scanline setup: h02Rcp, h01Rcp, h12Rcp - axis-aligned quad: wRcp, hRcp - line rasterizer: stepRcp Other `1.0f/x` uses (matrix translate/normalize, texture init `tx`/`ty`, sw_matrix_rotate inverse-length) are not on the per-pixel hot path and are left untouched. Measured on ESP32-S3 @ 240 MHz, R5G6B5 240x240, textured 3D model: contributes to a ~10-15% rasterization speedup. Made-with: Cursor
This commit is contained in:
parent
d768dae402
commit
fd57316ff0
57
src/external/rlsw.h
vendored
57
src/external/rlsw.h
vendored
|
|
@ -1210,6 +1210,33 @@ static inline float sw_fract(float x)
|
|||
return (x - floorf(x));
|
||||
}
|
||||
|
||||
// Fast reciprocal: 1-ULP accurate in ~7 instructions on Xtensa using the
|
||||
// hardware `recip0.s` seed + two Newton-Raphson refinement steps. All work
|
||||
// stays in FPU registers — no `__divsf3` software call. Hot-path divisions
|
||||
// in the rasterizer (span/triangle setup, perspective divide, etc.) call
|
||||
// this. On non-Xtensa targets it transparently expands to `1.0f / x`, so
|
||||
// generated code is identical to before.
|
||||
#if defined(__XTENSA__)
|
||||
__attribute__((always_inline))
|
||||
static inline float sw_rcp(float x)
|
||||
{
|
||||
float result, temp;
|
||||
__asm__(
|
||||
"recip0.s %0, %2\n"
|
||||
"const.s %1, 1\n"
|
||||
"msub.s %1, %2, %0\n"
|
||||
"madd.s %0, %0, %1\n"
|
||||
"const.s %1, 1\n"
|
||||
"msub.s %1, %2, %0\n"
|
||||
"maddn.s %0, %0, %1\n"
|
||||
: "=&f"(result), "=&f"(temp) : "f"(x)
|
||||
);
|
||||
return result;
|
||||
}
|
||||
#else
|
||||
static inline float sw_rcp(float x) { return 1.0f/x; }
|
||||
#endif
|
||||
|
||||
static inline uint8_t sw_luminance8(const uint8_t *color)
|
||||
{
|
||||
return (uint8_t)((color[0]*77 + color[1]*150 + color[2]*29) >> 8);
|
||||
|
|
@ -3366,7 +3393,7 @@ static void sw_triangle_clip_and_project(void)
|
|||
|
||||
// Calculation of the reciprocal of W for normalization
|
||||
// as well as perspective-correct attributes
|
||||
const float wRcp = 1.0f/v->position[3];
|
||||
const float wRcp = sw_rcp(v->position[3]);
|
||||
|
||||
// Division of XYZ coordinates by weight
|
||||
v->position[0] *= wRcp;
|
||||
|
|
@ -3481,7 +3508,7 @@ static void sw_quad_clip_and_project(void)
|
|||
|
||||
// Calculation of the reciprocal of W for normalization
|
||||
// as well as perspective-correct attributes
|
||||
const float wRcp = 1.0f/v->position[3];
|
||||
const float wRcp = sw_rcp(v->position[3]);
|
||||
|
||||
// Division of XYZ coordinates by weight
|
||||
v->position[0] *= wRcp;
|
||||
|
|
@ -3659,8 +3686,8 @@ static bool sw_line_clip_and_project(sw_vertex_t *v0, sw_vertex_t *v1)
|
|||
if (!sw_line_clip(v0, v1)) return false;
|
||||
|
||||
// Convert clip coordinates to NDC
|
||||
v0->position[3] = 1.0f/v0->position[3];
|
||||
v1->position[3] = 1.0f/v1->position[3];
|
||||
v0->position[3] = sw_rcp(v0->position[3]);
|
||||
v1->position[3] = sw_rcp(v1->position[3]);
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
v0->position[i] *= v0->position[3];
|
||||
|
|
@ -3709,7 +3736,7 @@ static bool sw_point_clip_and_project(sw_vertex_t *v)
|
|||
if ((v->position[i] < -v->position[3]) || (v->position[i] > v->position[3])) return false;
|
||||
}
|
||||
|
||||
v->position[3] = 1.0f/v->position[3];
|
||||
v->position[3] = sw_rcp(v->position[3]);
|
||||
v->position[0] *= v->position[3];
|
||||
v->position[1] *= v->position[3];
|
||||
v->position[2] *= v->position[3];
|
||||
|
|
@ -5272,7 +5299,7 @@ static void SW_RASTER_TRIANGLE_SPAN(const sw_vertex_t *start, const sw_vertex_t
|
|||
if (xStart == xEnd) return;
|
||||
|
||||
// Compute the inverse horizontal distance along the X axis
|
||||
float dxRcp = 1.0f/(end->position[0] - start->position[0]);
|
||||
float dxRcp = sw_rcp(end->position[0] - start->position[0]);
|
||||
|
||||
// Compute the interpolation steps along the X axis
|
||||
float dWdx = (end->position[3] - start->position[3])*dxRcp;
|
||||
|
|
@ -5326,12 +5353,12 @@ static void SW_RASTER_TRIANGLE_SPAN(const sw_vertex_t *start, const sw_vertex_t
|
|||
int blockEnd = x + SW_AFFINE_BLOCK;
|
||||
if (blockEnd > xEnd) blockEnd = xEnd;
|
||||
float blockLenF = (float)(blockEnd - x);
|
||||
float blockLenRcp = 1.0f/blockLenF;
|
||||
float blockLenRcp = sw_rcp(blockLenF);
|
||||
|
||||
// Only 2 '1/w' here; none inside the pixel loop
|
||||
float wRcpA = 1.0f/w;
|
||||
float wRcpA = sw_rcp(w);
|
||||
float wB = w + dWdx*blockLenF;
|
||||
float wRcpB = 1.0f/wB;
|
||||
float wRcpB = sw_rcp(wB);
|
||||
|
||||
// Perspective-correct color at both block endpoints, then affine gradient
|
||||
float srcColor[4] = {
|
||||
|
|
@ -5459,9 +5486,9 @@ static void SW_RASTER_TRIANGLE(const sw_vertex_t *v0, const sw_vertex_t *v1, con
|
|||
if (h02 < 1e-6f) return;
|
||||
|
||||
// Inverse edge dy for per-edge dV/dy (scanline interpolation)
|
||||
float h02Rcp = 1.0f/h02;
|
||||
float h01Rcp = (h01 > 1e-6f)? 1.0f/h01 : 0.0f;
|
||||
float h12Rcp = (h12 > 1e-6f)? 1.0f/h12 : 0.0f;
|
||||
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;
|
||||
|
||||
// Compute gradients for each side of the triangle
|
||||
sw_vertex_t dVXdy02, dVXdy01, dVXdy12;
|
||||
|
|
@ -5560,8 +5587,8 @@ static void SW_RASTER_QUAD(const sw_vertex_t *a, const sw_vertex_t *b,
|
|||
float h = (float)(yMax - yMin);
|
||||
if ((w <= 0) || (h <= 0)) return;
|
||||
|
||||
float wRcp = 1.0f/w;
|
||||
float hRcp = 1.0f/h;
|
||||
float wRcp = sw_rcp(w);
|
||||
float hRcp = sw_rcp(h);
|
||||
|
||||
// Subpixel corrections
|
||||
float xSubstep = 1.0f - sw_fract(tl->position[0]);
|
||||
|
|
@ -5746,7 +5773,7 @@ static void SW_RASTER_LINE(const sw_vertex_t *v0, const sw_vertex_t *v1)
|
|||
// Compute per pixel increments
|
||||
float xInc = dx/steps;
|
||||
float yInc = dy/steps;
|
||||
float stepRcp = 1.0f/steps;
|
||||
float stepRcp = sw_rcp(steps);
|
||||
#ifdef SW_ENABLE_DEPTH_TEST
|
||||
float zInc = (v1->position[2] - v0->position[2])*stepRcp;
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user