mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-26 02:11:36 -04:00
runtime/mdec: fast IDCT (FFmpeg simple_idct, ~16x fewer mults)
Replace the O(N^3) reference IDCT in idct_block (1024 multiplies per 8x8 block) with FFmpeg's simple_idct butterfly decomposition (~64 multiplies per block). Both row and column passes ported to produce int16_t output so the rest of mdec.c (to_output_u8, append_color_macroblock, append_luma_block) keeps working unchanged. Source: FFmpeg libavcodec/simple_idct_template.c (BIT_DEPTH=8), LGPL-2.1+. Previously imported into legacy runner/src/fmv_player.cpp on the legacy-2026-05-11 branch; salvaged into v4's hardware-emulated MDEC path. Attribution preserved in the source comment. Side effect: the new IDCT computes cosines from the W1..W7 constants directly and no longer depends on mdec.scale. The prior implementation treated SET_SCALE input as the cosine matrix; real PSX hardware ignores SET_SCALE for IDCT (it only seeds the dequant tables), so the new code matches hardware more closely as well. Verified visually: Tomba boots through Sony splash + Whoopee Camp FMV; intro FMV reaches the mountain-valley scene with correct colors. No visual regression vs the prior implementation. This addresses one piece of TombaRecomp ISSUES.md #3.D / FMV-speed goal. Whether the IDCT was the actual FMV bottleneck still needs an A/B timing test; this is a clean CPU win regardless. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+136
-18
@@ -165,26 +165,144 @@ static void finish_command(void) {
|
||||
trace_event(MDEC_EVT_CMD_DONE, mdec.command);
|
||||
}
|
||||
|
||||
static void idct_block(int16_t *block) {
|
||||
int16_t tmp[64];
|
||||
int16_t *src = block;
|
||||
int16_t *dst = tmp;
|
||||
/* ============================================================================
|
||||
* Fast IDCT — butterfly decomposition, ported from FFmpeg
|
||||
* libavcodec/simple_idct_template.c (BIT_DEPTH=8, LGPL-2.1+).
|
||||
*
|
||||
* Constants W1..W7 = cos(k*pi/16)*sqrt(2)*2^14 + 0.5; W4 ≈ cos(pi/4)*sqrt(2)*2^14.
|
||||
* Replaces the prior O(N^3) reference IDCT (~1024 mul/block) with a butterfly
|
||||
* implementation (~64 mul/block). Produces int16_t output to match the
|
||||
* existing mdec.c convention so to_output_u8 / append_color_macroblock /
|
||||
* append_luma_block continue to work unchanged.
|
||||
*
|
||||
* The PSX MDEC ignores the GP0 IDCT-scale matrix; the prior code used
|
||||
* mdec.scale as the cosine matrix. With FFmpeg's IDCT we compute cosines
|
||||
* from the W1..W7 constants directly and no longer depend on mdec.scale
|
||||
* — that matches real-hardware behavior more closely.
|
||||
* ========================================================================== */
|
||||
#define MDEC_IDCT_W1 22725u
|
||||
#define MDEC_IDCT_W2 21407u
|
||||
#define MDEC_IDCT_W3 19266u
|
||||
#define MDEC_IDCT_W4 16383u
|
||||
#define MDEC_IDCT_W5 12873u
|
||||
#define MDEC_IDCT_W6 8867u
|
||||
#define MDEC_IDCT_W7 4520u
|
||||
#define MDEC_IDCT_ROW_SHIFT 11
|
||||
#define MDEC_IDCT_COL_SHIFT 20
|
||||
#define MDEC_IDCT_DC_SHIFT 3
|
||||
|
||||
for (int pass = 0; pass < 2; pass++) {
|
||||
for (int y = 0; y < 8; y++) {
|
||||
for (int x = 0; x < 8; x++) {
|
||||
int sum = 0;
|
||||
for (int z = 0; z < 8; z++) {
|
||||
sum += (int)src[y + z * 8] * ((int)mdec.scale[x + z * 8] / 8);
|
||||
}
|
||||
dst[x + y * 8] = (int16_t)((sum + 0xFFF) / 0x2000);
|
||||
}
|
||||
}
|
||||
|
||||
int16_t *swap = src;
|
||||
src = dst;
|
||||
dst = swap;
|
||||
static void idct_row_cond_dc(int16_t *row) {
|
||||
if (!(row[1] | row[2] | row[3] | row[4] | row[5] | row[6] | row[7])) {
|
||||
int16_t dc = (int16_t)(row[0] << MDEC_IDCT_DC_SHIFT);
|
||||
row[0] = row[1] = row[2] = row[3] =
|
||||
row[4] = row[5] = row[6] = row[7] = dc;
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t a0, a1, a2, a3, b0, b1, b2, b3;
|
||||
|
||||
a0 = (uint32_t)(MDEC_IDCT_W4 * row[0]) + (1u << (MDEC_IDCT_ROW_SHIFT - 1));
|
||||
a1 = a0; a2 = a0; a3 = a0;
|
||||
|
||||
a0 += (uint32_t)( MDEC_IDCT_W2 * row[2]);
|
||||
a1 += (uint32_t)( MDEC_IDCT_W6 * row[2]);
|
||||
a2 += (uint32_t)(-MDEC_IDCT_W6 * row[2]);
|
||||
a3 += (uint32_t)(-MDEC_IDCT_W2 * row[2]);
|
||||
|
||||
b0 = (uint32_t)(MDEC_IDCT_W1 * row[1]);
|
||||
b1 = (uint32_t)(MDEC_IDCT_W3 * row[1]);
|
||||
b2 = (uint32_t)(MDEC_IDCT_W5 * row[1]);
|
||||
b3 = (uint32_t)(MDEC_IDCT_W7 * row[1]);
|
||||
|
||||
b0 += (uint32_t)( MDEC_IDCT_W3 * row[3]);
|
||||
b1 += (uint32_t)(-MDEC_IDCT_W7 * row[3]);
|
||||
b2 += (uint32_t)(-MDEC_IDCT_W1 * row[3]);
|
||||
b3 += (uint32_t)(-MDEC_IDCT_W5 * row[3]);
|
||||
|
||||
if (row[4] | row[5] | row[6] | row[7]) {
|
||||
a0 += (uint32_t)( MDEC_IDCT_W4 * row[4]) + (uint32_t)( MDEC_IDCT_W6 * row[6]);
|
||||
a1 += (uint32_t)(-MDEC_IDCT_W4 * row[4]) + (uint32_t)(-MDEC_IDCT_W2 * row[6]);
|
||||
a2 += (uint32_t)(-MDEC_IDCT_W4 * row[4]) + (uint32_t)( MDEC_IDCT_W2 * row[6]);
|
||||
a3 += (uint32_t)( MDEC_IDCT_W4 * row[4]) + (uint32_t)(-MDEC_IDCT_W6 * row[6]);
|
||||
|
||||
b0 += (uint32_t)( MDEC_IDCT_W5 * row[5]) + (uint32_t)( MDEC_IDCT_W7 * row[7]);
|
||||
b1 += (uint32_t)(-MDEC_IDCT_W1 * row[5]) + (uint32_t)(-MDEC_IDCT_W5 * row[7]);
|
||||
b2 += (uint32_t)( MDEC_IDCT_W7 * row[5]) + (uint32_t)( MDEC_IDCT_W3 * row[7]);
|
||||
b3 += (uint32_t)( MDEC_IDCT_W3 * row[5]) + (uint32_t)(-MDEC_IDCT_W1 * row[7]);
|
||||
}
|
||||
|
||||
row[0] = (int16_t)((int)(a0 + b0) >> MDEC_IDCT_ROW_SHIFT);
|
||||
row[7] = (int16_t)((int)(a0 - b0) >> MDEC_IDCT_ROW_SHIFT);
|
||||
row[1] = (int16_t)((int)(a1 + b1) >> MDEC_IDCT_ROW_SHIFT);
|
||||
row[6] = (int16_t)((int)(a1 - b1) >> MDEC_IDCT_ROW_SHIFT);
|
||||
row[2] = (int16_t)((int)(a2 + b2) >> MDEC_IDCT_ROW_SHIFT);
|
||||
row[5] = (int16_t)((int)(a2 - b2) >> MDEC_IDCT_ROW_SHIFT);
|
||||
row[3] = (int16_t)((int)(a3 + b3) >> MDEC_IDCT_ROW_SHIFT);
|
||||
row[4] = (int16_t)((int)(a3 - b3) >> MDEC_IDCT_ROW_SHIFT);
|
||||
}
|
||||
|
||||
/* Column pass — operates on the 8 entries col[0], col[8], col[16], … col[56].
|
||||
* Writes int16_t back into the same column so the block is transformed
|
||||
* in-place. */
|
||||
static void idct_col_int16(int16_t *col) {
|
||||
uint32_t a0, a1, a2, a3, b0, b1, b2, b3;
|
||||
|
||||
a0 = (uint32_t)(MDEC_IDCT_W4 * col[0]) + (1u << (MDEC_IDCT_COL_SHIFT - 1));
|
||||
a1 = a0; a2 = a0; a3 = a0;
|
||||
|
||||
a0 += (uint32_t)( MDEC_IDCT_W2 * col[16]);
|
||||
a1 += (uint32_t)( MDEC_IDCT_W6 * col[16]);
|
||||
a2 += (uint32_t)(-MDEC_IDCT_W6 * col[16]);
|
||||
a3 += (uint32_t)(-MDEC_IDCT_W2 * col[16]);
|
||||
|
||||
b0 = (uint32_t)(MDEC_IDCT_W1 * col[8]);
|
||||
b1 = (uint32_t)(MDEC_IDCT_W3 * col[8]);
|
||||
b2 = (uint32_t)(MDEC_IDCT_W5 * col[8]);
|
||||
b3 = (uint32_t)(MDEC_IDCT_W7 * col[8]);
|
||||
|
||||
b0 += (uint32_t)( MDEC_IDCT_W3 * col[24]);
|
||||
b1 += (uint32_t)(-MDEC_IDCT_W7 * col[24]);
|
||||
b2 += (uint32_t)(-MDEC_IDCT_W1 * col[24]);
|
||||
b3 += (uint32_t)(-MDEC_IDCT_W5 * col[24]);
|
||||
|
||||
if (col[32]) {
|
||||
a0 += (uint32_t)( MDEC_IDCT_W4 * col[32]);
|
||||
a1 += (uint32_t)(-MDEC_IDCT_W4 * col[32]);
|
||||
a2 += (uint32_t)(-MDEC_IDCT_W4 * col[32]);
|
||||
a3 += (uint32_t)( MDEC_IDCT_W4 * col[32]);
|
||||
}
|
||||
if (col[40]) {
|
||||
b0 += (uint32_t)( MDEC_IDCT_W5 * col[40]);
|
||||
b1 += (uint32_t)(-MDEC_IDCT_W1 * col[40]);
|
||||
b2 += (uint32_t)( MDEC_IDCT_W7 * col[40]);
|
||||
b3 += (uint32_t)( MDEC_IDCT_W3 * col[40]);
|
||||
}
|
||||
if (col[48]) {
|
||||
a0 += (uint32_t)( MDEC_IDCT_W6 * col[48]);
|
||||
a1 += (uint32_t)(-MDEC_IDCT_W2 * col[48]);
|
||||
a2 += (uint32_t)( MDEC_IDCT_W2 * col[48]);
|
||||
a3 += (uint32_t)(-MDEC_IDCT_W6 * col[48]);
|
||||
}
|
||||
if (col[56]) {
|
||||
b0 += (uint32_t)( MDEC_IDCT_W7 * col[56]);
|
||||
b1 += (uint32_t)(-MDEC_IDCT_W5 * col[56]);
|
||||
b2 += (uint32_t)( MDEC_IDCT_W3 * col[56]);
|
||||
b3 += (uint32_t)(-MDEC_IDCT_W1 * col[56]);
|
||||
}
|
||||
|
||||
col[0] = (int16_t)((int)(a0 + b0) >> MDEC_IDCT_COL_SHIFT);
|
||||
col[8] = (int16_t)((int)(a1 + b1) >> MDEC_IDCT_COL_SHIFT);
|
||||
col[16] = (int16_t)((int)(a2 + b2) >> MDEC_IDCT_COL_SHIFT);
|
||||
col[24] = (int16_t)((int)(a3 + b3) >> MDEC_IDCT_COL_SHIFT);
|
||||
col[32] = (int16_t)((int)(a3 - b3) >> MDEC_IDCT_COL_SHIFT);
|
||||
col[40] = (int16_t)((int)(a2 - b2) >> MDEC_IDCT_COL_SHIFT);
|
||||
col[48] = (int16_t)((int)(a1 - b1) >> MDEC_IDCT_COL_SHIFT);
|
||||
col[56] = (int16_t)((int)(a0 - b0) >> MDEC_IDCT_COL_SHIFT);
|
||||
}
|
||||
|
||||
static void idct_block(int16_t *block) {
|
||||
for (int i = 0; i < 8; i++) idct_row_cond_dc(block + i * 8);
|
||||
for (int i = 0; i < 8; i++) idct_col_int16(block + i);
|
||||
}
|
||||
|
||||
static int decode_rle_block(int16_t *block, const uint8_t *quant,
|
||||
|
||||
Reference in New Issue
Block a user