From 23ee402a1f6544a1aa66307c13cfb7e67ba2bc60 Mon Sep 17 00:00:00 2001 From: Ali-Z0 Date: Mon, 8 Jun 2026 12:18:02 +0200 Subject: [PATCH] Optimize reverb buffer locality --- libmikmod/playercode/virtch.c | 92 ++++++++++++++++++---------------- libmikmod/playercode/virtch2.c | 90 +++++++++++++++++---------------- 2 files changed, 97 insertions(+), 85 deletions(-) diff --git a/libmikmod/playercode/virtch.c b/libmikmod/playercode/virtch.c index b77152ec..e96af53f 100644 --- a/libmikmod/playercode/virtch.c +++ b/libmikmod/playercode/virtch.c @@ -107,6 +107,9 @@ static UWORD vc_mode; static int RVc1, RVc2, RVc3, RVc4, RVc5, RVc6, RVc7, RVc8; static ULONG RVRindex; +/* Single contiguous block for all reverb buffers */ +static SLONG *RVbuf_Block=NULL; + /* For Mono or Left Channel */ static SLONG *RVbufL1=NULL,*RVbufL2=NULL,*RVbufL3=NULL,*RVbufL4=NULL, *RVbufL5=NULL,*RVbufL6=NULL,*RVbufL7=NULL,*RVbufL8=NULL; @@ -645,8 +648,9 @@ static SLONGLONG MixSurroundInterp(const SWORD* srce,SLONG* dest,SLONGLONG idx,S static void (*MixReverb)(SLONG* srce,NATIVE count); -/* Reverb macros */ -#define COMPUTE_LOC(n) loc##n = RVRindex % RVc##n +/* Reverb macros -- no modulo in loop */ +#define INIT_LOC(n) loc##n = RVRindex % RVc##n +#define ADVANCE_LOC(n) if (++loc##n >= RVc##n) loc##n = 0 #define COMPUTE_LECHO(n) RVbufL##n [loc##n ]=speedup+((ReverbPct*RVbufL##n [loc##n ])>>7) #define COMPUTE_RECHO(n) RVbufR##n [loc##n ]=speedup+((ReverbPct*RVbufR##n [loc##n ])>>7) @@ -659,8 +663,8 @@ static void MixReverb_Normal(SLONG* srce,NATIVE count) ReverbPct=58+(md_reverb<<2); - COMPUTE_LOC(1); COMPUTE_LOC(2); COMPUTE_LOC(3); COMPUTE_LOC(4); - COMPUTE_LOC(5); COMPUTE_LOC(6); COMPUTE_LOC(7); COMPUTE_LOC(8); + INIT_LOC(1); INIT_LOC(2); INIT_LOC(3); INIT_LOC(4); + INIT_LOC(5); INIT_LOC(6); INIT_LOC(7); INIT_LOC(8); while(count--) { /* Compute the left channel echo buffers */ @@ -672,8 +676,8 @@ static void MixReverb_Normal(SLONG* srce,NATIVE count) /* Prepare to compute actual finalized data */ RVRindex++; - COMPUTE_LOC(1); COMPUTE_LOC(2); COMPUTE_LOC(3); COMPUTE_LOC(4); - COMPUTE_LOC(5); COMPUTE_LOC(6); COMPUTE_LOC(7); COMPUTE_LOC(8); + ADVANCE_LOC(1); ADVANCE_LOC(2); ADVANCE_LOC(3); ADVANCE_LOC(4); + ADVANCE_LOC(5); ADVANCE_LOC(6); ADVANCE_LOC(7); ADVANCE_LOC(8); /* left channel */ *srce++ +=RVbufL1[loc1]-RVbufL2[loc2]+RVbufL3[loc3]-RVbufL4[loc4]+ @@ -690,8 +694,8 @@ static void MixReverb_Stereo(SLONG* srce,NATIVE count) ReverbPct = 92+(md_reverb<<1); - COMPUTE_LOC(1); COMPUTE_LOC(2); COMPUTE_LOC(3); COMPUTE_LOC(4); - COMPUTE_LOC(5); COMPUTE_LOC(6); COMPUTE_LOC(7); COMPUTE_LOC(8); + INIT_LOC(1); INIT_LOC(2); INIT_LOC(3); INIT_LOC(4); + INIT_LOC(5); INIT_LOC(6); INIT_LOC(7); INIT_LOC(8); while(count--) { /* Compute the left channel echo buffers */ @@ -709,8 +713,8 @@ static void MixReverb_Stereo(SLONG* srce,NATIVE count) /* Prepare to compute actual finalized data */ RVRindex++; - COMPUTE_LOC(1); COMPUTE_LOC(2); COMPUTE_LOC(3); COMPUTE_LOC(4); - COMPUTE_LOC(5); COMPUTE_LOC(6); COMPUTE_LOC(7); COMPUTE_LOC(8); + ADVANCE_LOC(1); ADVANCE_LOC(2); ADVANCE_LOC(3); ADVANCE_LOC(4); + ADVANCE_LOC(5); ADVANCE_LOC(6); ADVANCE_LOC(7); ADVANCE_LOC(8); /* left channel then right channel */ *srce++ +=RVbufL1[loc1]-RVbufL2[loc2]+RVbufL3[loc3]-RVbufL4[loc4]+ @@ -1267,6 +1271,9 @@ int VC1_Init(void) int VC1_PlayStart(void) { + int total_L_size; + int total_block_size; + samplesthatfit=TICKLSIZE; if(vc_mode & DMODE_STEREO) samplesthatfit >>= 1; tickleft = 0; @@ -1280,25 +1287,36 @@ int VC1_PlayStart(void) RVc7 = (7813L * md_mixfreq) / REVERBERATION; RVc8 = (8828L * md_mixfreq) / REVERBERATION; - if(!(RVbufL1=(SLONG*)MikMod_calloc((RVc1+1),sizeof(SLONG)))) return 1; - if(!(RVbufL2=(SLONG*)MikMod_calloc((RVc2+1),sizeof(SLONG)))) return 1; - if(!(RVbufL3=(SLONG*)MikMod_calloc((RVc3+1),sizeof(SLONG)))) return 1; - if(!(RVbufL4=(SLONG*)MikMod_calloc((RVc4+1),sizeof(SLONG)))) return 1; - if(!(RVbufL5=(SLONG*)MikMod_calloc((RVc5+1),sizeof(SLONG)))) return 1; - if(!(RVbufL6=(SLONG*)MikMod_calloc((RVc6+1),sizeof(SLONG)))) return 1; - if(!(RVbufL7=(SLONG*)MikMod_calloc((RVc7+1),sizeof(SLONG)))) return 1; - if(!(RVbufL8=(SLONG*)MikMod_calloc((RVc8+1),sizeof(SLONG)))) return 1; + /* Single Contiguous Memory Block: Calculate the total amount of SLONG elements needed for the Left channel */ + total_L_size = (RVc1+1) + (RVc2+1) + (RVc3+1) + (RVc4+1) + (RVc5+1) + (RVc6+1) + (RVc7+1) + (RVc8+1); + total_block_size = total_L_size; + if (vc_mode & DMODE_STEREO) { + /* Double the allocation size if Stereo is enabled (Right channel) */ + total_block_size += total_L_size; + } + + /* Allocate one single contiguous block in RAM to avoid Heap Fragmentation and Cache Thrashing */ + if(!(RVbuf_Block=(SLONG*)MikMod_calloc(total_block_size, sizeof(SLONG)))) return 1; + + /* Distribute the pointers linearly within the contiguous block */ + RVbufL1 = RVbuf_Block; + RVbufL2 = RVbufL1 + (RVc1+1); + RVbufL3 = RVbufL2 + (RVc2+1); + RVbufL4 = RVbufL3 + (RVc3+1); + RVbufL5 = RVbufL4 + (RVc4+1); + RVbufL6 = RVbufL5 + (RVc5+1); + RVbufL7 = RVbufL6 + (RVc6+1); + RVbufL8 = RVbufL7 + (RVc7+1); - /* allocate reverb buffers for the right channel if in stereo mode only. */ if (vc_mode & DMODE_STEREO) { - if(!(RVbufR1=(SLONG*)MikMod_calloc((RVc1+1),sizeof(SLONG)))) return 1; - if(!(RVbufR2=(SLONG*)MikMod_calloc((RVc2+1),sizeof(SLONG)))) return 1; - if(!(RVbufR3=(SLONG*)MikMod_calloc((RVc3+1),sizeof(SLONG)))) return 1; - if(!(RVbufR4=(SLONG*)MikMod_calloc((RVc4+1),sizeof(SLONG)))) return 1; - if(!(RVbufR5=(SLONG*)MikMod_calloc((RVc5+1),sizeof(SLONG)))) return 1; - if(!(RVbufR6=(SLONG*)MikMod_calloc((RVc6+1),sizeof(SLONG)))) return 1; - if(!(RVbufR7=(SLONG*)MikMod_calloc((RVc7+1),sizeof(SLONG)))) return 1; - if(!(RVbufR8=(SLONG*)MikMod_calloc((RVc8+1),sizeof(SLONG)))) return 1; + RVbufR1 = RVbufL8 + (RVc8+1); /* Continue linearly from the end of the Left channel */ + RVbufR2 = RVbufR1 + (RVc1+1); + RVbufR3 = RVbufR2 + (RVc2+1); + RVbufR4 = RVbufR3 + (RVc3+1); + RVbufR5 = RVbufR4 + (RVc4+1); + RVbufR6 = RVbufR5 + (RVc5+1); + RVbufR7 = RVbufR6 + (RVc6+1); + RVbufR8 = RVbufR7 + (RVc7+1); } RVRindex = 0; @@ -1307,23 +1325,11 @@ int VC1_PlayStart(void) void VC1_PlayStop(void) { - MikMod_free(RVbufL1); - MikMod_free(RVbufL2); - MikMod_free(RVbufL3); - MikMod_free(RVbufL4); - MikMod_free(RVbufL5); - MikMod_free(RVbufL6); - MikMod_free(RVbufL7); - MikMod_free(RVbufL8); + /* Free the single contiguous block */ + MikMod_free(RVbuf_Block); + RVbuf_Block = NULL; + RVbufL1=RVbufL2=RVbufL3=RVbufL4=RVbufL5=RVbufL6=RVbufL7=RVbufL8=NULL; - MikMod_free(RVbufR1); - MikMod_free(RVbufR2); - MikMod_free(RVbufR3); - MikMod_free(RVbufR4); - MikMod_free(RVbufR5); - MikMod_free(RVbufR6); - MikMod_free(RVbufR7); - MikMod_free(RVbufR8); RVbufR1=RVbufR2=RVbufR3=RVbufR4=RVbufR5=RVbufR6=RVbufR7=RVbufR8=NULL; } diff --git a/libmikmod/playercode/virtch2.c b/libmikmod/playercode/virtch2.c index 28436853..2101b8d3 100644 --- a/libmikmod/playercode/virtch2.c +++ b/libmikmod/playercode/virtch2.c @@ -135,6 +135,9 @@ typedef void (*MikMod_callback_t)(unsigned char *data, size_t len); static int RVc1, RVc2, RVc3, RVc4, RVc5, RVc6, RVc7, RVc8; static ULONG RVRindex; +/* Single contiguous block for all reverb buffers */ +static SLONG *RVbuf_Block=NULL; + /* For Mono or Left Channel */ static SLONG *RVbufL1=NULL,*RVbufL2=NULL,*RVbufL3=NULL,*RVbufL4=NULL, *RVbufL5=NULL,*RVbufL6=NULL,*RVbufL7=NULL,*RVbufL8=NULL; @@ -598,8 +601,9 @@ static void(*Mix32to16)(SWORD* dste,const SLONG *srce,NATIVE count); static void(*Mix32to8)(SBYTE* dste,const SLONG *srce,NATIVE count); static void(*MixReverb)(SLONG *srce,NATIVE count); -/* Reverb macros */ -#define COMPUTE_LOC(n) loc##n = RVRindex % RVc##n +/* Reverb macros -- no modulo in loop */ +#define INIT_LOC(n) loc##n = RVRindex % RVc##n +#define ADVANCE_LOC(n) if (++loc##n >= RVc##n) loc##n = 0 #define COMPUTE_LECHO(n) RVbufL##n [loc##n ]=speedup+((ReverbPct*RVbufL##n [loc##n ])>>7) #define COMPUTE_RECHO(n) RVbufR##n [loc##n ]=speedup+((ReverbPct*RVbufR##n [loc##n ])>>7) @@ -611,8 +615,8 @@ static void MixReverb_Normal(SLONG *srce,NATIVE count) ReverbPct=58+(md_reverb*4); - COMPUTE_LOC(1); COMPUTE_LOC(2); COMPUTE_LOC(3); COMPUTE_LOC(4); - COMPUTE_LOC(5); COMPUTE_LOC(6); COMPUTE_LOC(7); COMPUTE_LOC(8); + INIT_LOC(1); INIT_LOC(2); INIT_LOC(3); INIT_LOC(4); + INIT_LOC(5); INIT_LOC(6); INIT_LOC(7); INIT_LOC(8); while(count--) { /* Compute the left channel echo buffers */ @@ -624,8 +628,8 @@ static void MixReverb_Normal(SLONG *srce,NATIVE count) /* Prepare to compute actual finalized data */ RVRindex++; - COMPUTE_LOC(1); COMPUTE_LOC(2); COMPUTE_LOC(3); COMPUTE_LOC(4); - COMPUTE_LOC(5); COMPUTE_LOC(6); COMPUTE_LOC(7); COMPUTE_LOC(8); + ADVANCE_LOC(1); ADVANCE_LOC(2); ADVANCE_LOC(3); ADVANCE_LOC(4); + ADVANCE_LOC(5); ADVANCE_LOC(6); ADVANCE_LOC(7); ADVANCE_LOC(8); /* left channel */ *srce++ +=RVbufL1[loc1]-RVbufL2[loc2]+RVbufL3[loc3]-RVbufL4[loc4]+ @@ -641,8 +645,8 @@ static void MixReverb_Stereo(SLONG *srce,NATIVE count) ReverbPct=58+(md_reverb*4); - COMPUTE_LOC(1); COMPUTE_LOC(2); COMPUTE_LOC(3); COMPUTE_LOC(4); - COMPUTE_LOC(5); COMPUTE_LOC(6); COMPUTE_LOC(7); COMPUTE_LOC(8); + INIT_LOC(1); INIT_LOC(2); INIT_LOC(3); INIT_LOC(4); + INIT_LOC(5); INIT_LOC(6); INIT_LOC(7); INIT_LOC(8); while(count--) { /* Compute the left channel echo buffers */ @@ -660,8 +664,8 @@ static void MixReverb_Stereo(SLONG *srce,NATIVE count) /* Prepare to compute actual finalized data */ RVRindex++; - COMPUTE_LOC(1); COMPUTE_LOC(2); COMPUTE_LOC(3); COMPUTE_LOC(4); - COMPUTE_LOC(5); COMPUTE_LOC(6); COMPUTE_LOC(7); COMPUTE_LOC(8); + ADVANCE_LOC(1); ADVANCE_LOC(2); ADVANCE_LOC(3); ADVANCE_LOC(4); + ADVANCE_LOC(5); ADVANCE_LOC(6); ADVANCE_LOC(7); ADVANCE_LOC(8); /* left channel */ *srce++ +=RVbufL1[loc1]-RVbufL2[loc2]+RVbufL3[loc3]-RVbufL4[loc4]+ @@ -1276,6 +1280,9 @@ int VC2_Init(void) int VC2_PlayStart(void) { + int total_L_size; + int total_block_size; + md_mode|=DMODE_INTERP; samplesthatfit = TICKLSIZE; @@ -1291,25 +1298,37 @@ int VC2_PlayStart(void) RVc7 = (7813L * md_mixfreq) / (REVERBERATION * 10); RVc8 = (8828L * md_mixfreq) / (REVERBERATION * 10); - if(!(RVbufL1=(SLONG*)MikMod_calloc((RVc1+1),sizeof(SLONG)))) return 1; - if(!(RVbufL2=(SLONG*)MikMod_calloc((RVc2+1),sizeof(SLONG)))) return 1; - if(!(RVbufL3=(SLONG*)MikMod_calloc((RVc3+1),sizeof(SLONG)))) return 1; - if(!(RVbufL4=(SLONG*)MikMod_calloc((RVc4+1),sizeof(SLONG)))) return 1; - if(!(RVbufL5=(SLONG*)MikMod_calloc((RVc5+1),sizeof(SLONG)))) return 1; - if(!(RVbufL6=(SLONG*)MikMod_calloc((RVc6+1),sizeof(SLONG)))) return 1; - if(!(RVbufL7=(SLONG*)MikMod_calloc((RVc7+1),sizeof(SLONG)))) return 1; - if(!(RVbufL8=(SLONG*)MikMod_calloc((RVc8+1),sizeof(SLONG)))) return 1; + /* Single Contiguous Memory Block: Calculate the total amount of SLONG elements needed for the Left channel */ + total_L_size = (RVc1+1) + (RVc2+1) + (RVc3+1) + (RVc4+1) + (RVc5+1) + (RVc6+1) + (RVc7+1) + (RVc8+1); + total_block_size = total_L_size; + if (vc_mode & DMODE_STEREO) { + /* Double the allocation size if Stereo is enabled (Right channel) */ + total_block_size += total_L_size; + } + + /* Allocate one single contiguous block in RAM to avoid Heap Fragmentation and Cache Thrashing */ + if(!(RVbuf_Block=(SLONG*)MikMod_calloc(total_block_size, sizeof(SLONG)))) return 1; + + /* Distribute the pointers linearly within the contiguous block */ + RVbufL1 = RVbuf_Block; + RVbufL2 = RVbufL1 + (RVc1+1); + RVbufL3 = RVbufL2 + (RVc2+1); + RVbufL4 = RVbufL3 + (RVc3+1); + RVbufL5 = RVbufL4 + (RVc4+1); + RVbufL6 = RVbufL5 + (RVc5+1); + RVbufL7 = RVbufL6 + (RVc6+1); + RVbufL8 = RVbufL7 + (RVc7+1); /* allocate reverb buffers for the right channel if in stereo mode only. */ if (vc_mode & DMODE_STEREO) { - if(!(RVbufR1=(SLONG*)MikMod_calloc((RVc1+1),sizeof(SLONG)))) return 1; - if(!(RVbufR2=(SLONG*)MikMod_calloc((RVc2+1),sizeof(SLONG)))) return 1; - if(!(RVbufR3=(SLONG*)MikMod_calloc((RVc3+1),sizeof(SLONG)))) return 1; - if(!(RVbufR4=(SLONG*)MikMod_calloc((RVc4+1),sizeof(SLONG)))) return 1; - if(!(RVbufR5=(SLONG*)MikMod_calloc((RVc5+1),sizeof(SLONG)))) return 1; - if(!(RVbufR6=(SLONG*)MikMod_calloc((RVc6+1),sizeof(SLONG)))) return 1; - if(!(RVbufR7=(SLONG*)MikMod_calloc((RVc7+1),sizeof(SLONG)))) return 1; - if(!(RVbufR8=(SLONG*)MikMod_calloc((RVc8+1),sizeof(SLONG)))) return 1; + RVbufR1 = RVbufL8 + (RVc8+1); /* Continue linearly from the end of the Left channel */ + RVbufR2 = RVbufR1 + (RVc1+1); + RVbufR3 = RVbufR2 + (RVc2+1); + RVbufR4 = RVbufR3 + (RVc3+1); + RVbufR5 = RVbufR4 + (RVc4+1); + RVbufR6 = RVbufR5 + (RVc5+1); + RVbufR7 = RVbufR6 + (RVc6+1); + RVbufR8 = RVbufR7 + (RVc7+1); } RVRindex = 0; @@ -1318,22 +1337,9 @@ int VC2_PlayStart(void) void VC2_PlayStop(void) { - MikMod_free(RVbufL1); - MikMod_free(RVbufL2); - MikMod_free(RVbufL3); - MikMod_free(RVbufL4); - MikMod_free(RVbufL5); - MikMod_free(RVbufL6); - MikMod_free(RVbufL7); - MikMod_free(RVbufL8); - MikMod_free(RVbufR1); - MikMod_free(RVbufR2); - MikMod_free(RVbufR3); - MikMod_free(RVbufR4); - MikMod_free(RVbufR5); - MikMod_free(RVbufR6); - MikMod_free(RVbufR7); - MikMod_free(RVbufR8); + /* Free the single contiguous block */ + MikMod_free(RVbuf_Block); + RVbuf_Block = NULL; RVbufL1=RVbufL2=RVbufL3=RVbufL4=RVbufL5=RVbufL6=RVbufL7=RVbufL8=NULL; RVbufR1=RVbufR2=RVbufR3=RVbufR4=RVbufR5=RVbufR6=RVbufR7=RVbufR8=NULL;