[rlsw] Use ESP-DSP for 4x4 matrix multiply and per-vertex MVP transform
Adds an opt-in ESP-DSP code path for ESP32 / ESP32-S3 builds. ESP-DSP is
ESP-IDF's official optimized math library and ships hand-vectorized
kernels that beat the scalar implementations on Xtensa.
Two integration points:
1. `sw_matrix_mul_rst` -> `dspm_mult_4x4x4_f32` for any 4x4*4x4 multiply
(used for MVP build, gluLookAt, push/multiply, etc.). rlsw stores
matrices column-major and ESP-DSP reads row-major; the comment on the
call site explains why the flat-buffer call still produces the
correct column-major product (transpose-of-transposes equivalence).
2. `sw_immediate_push_vertex` -> `dspm_mult_4x4x1_f32` for the per-vertex
clip-space transform. Because ESP-DSP expects a row-major matrix in
this case, a row-major copy `matMVP_rm[16]` is maintained alongside
`matMVP` and refreshed once per `isDirtyMVP` rebuild in
`sw_immediate_begin`. Cost is 16 scalar copies per matrix update,
amortized over thousands of vertices per frame.
Detection is **opt-in** via `SW_USE_ESP_DSP` so existing ESP-IDF projects
that don't depend on the `esp-dsp` component keep building unchanged.
A user enables it from CMakeLists.txt (or anywhere before including
rlgl.h):
target_compile_definitions(${COMPONENT_LIB} PRIVATE SW_USE_ESP_DSP=1)
and adds the dependency to `idf_component.yml`:
espressif/esp-dsp: "^1.4.0"
Measured on ESP32-S3 @ 240 MHz, R5G6B5 240x240, textured 3D model:
contributes meaningfully to the overall frame-time improvement
(combined with sw_rcp).
Made-with: Cursor
This commit is contained in:
parent
fd57316ff0
commit
b45ccc044d
42
src/external/rlsw.h
vendored
42
src/external/rlsw.h
vendored
|
|
@ -844,6 +844,17 @@ SWAPI void swGetFramebufferAttachmentParameteriv(SWattachment attachment, SWatta
|
|||
#endif
|
||||
#endif
|
||||
|
||||
// ESP-DSP acceleration: ESP-IDF ships an optimized math library that includes
|
||||
// `dspm_mult_4x4x4_f32` (4x4 matrix multiply) and `dspm_mult_4x4x1_f32`
|
||||
// (matrix * vector). These are S3-tuned hand-vectorized kernels that beat the
|
||||
// scalar versions for both throughput and code-size. Detection is opt-in to
|
||||
// keep the dependency optional: define SW_USE_ESP_DSP from your build system
|
||||
// (or rely on the `idf_component.yml` example shown in the rlsw docs).
|
||||
#if defined(ESP_PLATFORM) && defined(SW_USE_ESP_DSP)
|
||||
#define SW_HAS_ESP_DSP
|
||||
#include "dspm_mult.h"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
#define SW_CURLY_INIT(name) name
|
||||
#else
|
||||
|
|
@ -1038,6 +1049,9 @@ typedef struct {
|
|||
SWmatrix currentMatrixMode; // Current matrix mode (e.g., sw_MODELVIEW, sw_PROJECTION)
|
||||
sw_matrix_t *currentMatrix; // Pointer to the currently used matrix according to the mode
|
||||
sw_matrix_t matMVP; // Model view projection matrix, calculated and used internally
|
||||
#ifdef SW_HAS_ESP_DSP
|
||||
float matMVP_rm[16]; // Row-major MVP, kept in sync for esp-dsp dspm_mult_4x4x1_f32 vertex transform
|
||||
#endif
|
||||
bool isDirtyMVP; // Indicates if the MVP matrix should be rebuilt
|
||||
|
||||
sw_handle_t boundFramebufferId; // Framebuffer currently bound
|
||||
|
|
@ -1141,6 +1155,14 @@ static inline void sw_matrix_id(sw_matrix_t dst)
|
|||
|
||||
static inline void sw_matrix_mul_rst(float *SW_RESTRICT dst, const float *SW_RESTRICT left, const float *SW_RESTRICT right)
|
||||
{
|
||||
#ifdef SW_HAS_ESP_DSP
|
||||
// dspm_mult_4x4x4_f32 treats its operands as row-major. rlsw stores matrices
|
||||
// column-major, so passing them flat is equivalent to passing transposes:
|
||||
// dspm_mult(L^T, R^T) computes (L^T)*(R^T) = (R*L)^T, written back into a
|
||||
// flat array gives the same bit pattern as the column-major product (R*L)
|
||||
// -- exactly the semantic the scalar fallback below has.
|
||||
dspm_mult_4x4x4_f32(left, right, dst);
|
||||
#else
|
||||
float l00 = left[0], l01 = left[1], l02 = left[2], l03 = left[3];
|
||||
float l10 = left[4], l11 = left[5], l12 = left[6], l13 = left[7];
|
||||
float l20 = left[8], l21 = left[9], l22 = left[10], l23 = left[11];
|
||||
|
|
@ -1165,6 +1187,7 @@ static inline void sw_matrix_mul_rst(float *SW_RESTRICT dst, const float *SW_RES
|
|||
dst[7] = l10*right[3] + l11*right[7] + l12*right[11] + l13*right[15];
|
||||
dst[11] = l20*right[3] + l21*right[7] + l22*right[11] + l23*right[15];
|
||||
dst[15] = l30*right[3] + l31*right[7] + l32*right[11] + l33*right[15];
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void sw_matrix_mul(sw_matrix_t dst, const sw_matrix_t left, const sw_matrix_t right)
|
||||
|
|
@ -3818,6 +3841,19 @@ static void sw_immediate_begin(SWdraw mode)
|
|||
RLSW.stackModelview[RLSW.stackModelviewCounter - 1],
|
||||
RLSW.stackProjection[RLSW.stackProjectionCounter - 1]);
|
||||
|
||||
#ifdef SW_HAS_ESP_DSP
|
||||
// Pre-transpose to row-major so dspm_mult_4x4x1_f32(matMVP_rm, v, out)
|
||||
// computes M*v directly in the per-vertex hot path. 16 scalar copies
|
||||
// per MVP update vs. saving ~20 cycles per vertex transform.
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
for (int j = 0; j < 4; j++)
|
||||
{
|
||||
RLSW.matMVP_rm[4*i + j] = RLSW.matMVP[4*j + i];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
RLSW.isDirtyMVP = false;
|
||||
}
|
||||
|
||||
|
|
@ -3869,11 +3905,17 @@ static void sw_immediate_push_vertex(const float position[4])
|
|||
sw_vertex_t *vertex = &RLSW.primitive.buffer[RLSW.primitive.vertexCount++];
|
||||
|
||||
// Calculate clip coordinates
|
||||
#ifdef SW_HAS_ESP_DSP
|
||||
// dspm_mult_4x4x1_f32 declares its inputs non-const; rlsw treats them as
|
||||
// read-only and the cast is safe (the kernel only loads from B).
|
||||
dspm_mult_4x4x1_f32(RLSW.matMVP_rm, (float *)position, vertex->position);
|
||||
#else
|
||||
const float *m = RLSW.matMVP;
|
||||
vertex->position[0] = m[0]*position[0] + m[4]*position[1] + m[8]*position[2] + m[12]*position[3];
|
||||
vertex->position[1] = m[1]*position[0] + m[5]*position[1] + m[9]*position[2] + m[13]*position[3];
|
||||
vertex->position[2] = m[2]*position[0] + m[6]*position[1] + m[10]*position[2] + m[14]*position[3];
|
||||
vertex->position[3] = m[3]*position[0] + m[7]*position[1] + m[11]*position[2] + m[15]*position[3];
|
||||
#endif
|
||||
|
||||
// Copy the attributes in the current vertex
|
||||
for (int i = 0; i < 4; i++) vertex->color[i] = RLSW.primitive.color[i];
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user