mirror of
https://github.com/open-goal/jak-project
synced 2026-07-09 23:01:56 -04:00
[Jak3] Adjust overlord SPU dma to avoid audio hangs (#3804)
Change jak 3 SPU DMA to run the interrupt handler "immediately" (or at least before `DMA_SendToSPUAndSync` returns). This fixes an issue where audio can hang during fast cutscene playback. I'm hoping it fixes more issues with looping/stuck audio as well, but this needs more testing. I originally wanted to do it this way, but thought that it didn't work - from Jak 2 it seemed like things broke if the DMA was too fast. But, at least for Jak 3, everything seems to work like this. This will remove a huge source of non-deterministic timing in audio stuff and hopefully make things easier to debug. It also means that a large portion of streaming audio code will never have to run - from the game's point of view there's always the next SPU buffer available. If this works well, I might revisit jak 2 as well. Co-authored-by: water111 <awaterford1111445@gmail.com>
This commit is contained in:
@@ -48,6 +48,7 @@ struct DmaInterruptHandlerHack {
|
||||
sceSdTransIntrHandler cb = nullptr;
|
||||
void* data;
|
||||
int countdown = 0;
|
||||
bool pending = false;
|
||||
} g_DmaInterruptHack;
|
||||
|
||||
} // namespace
|
||||
@@ -87,10 +88,20 @@ void set_dma_intr_handler_hack(s32 chan, sceSdTransIntrHandler cb, void* data) {
|
||||
g_DmaInterruptHack.cb = cb;
|
||||
g_DmaInterruptHack.data = data;
|
||||
g_DmaInterruptHack.countdown = 10;
|
||||
g_DmaInterruptHack.pending = true;
|
||||
}
|
||||
|
||||
int SPUDmaIntr(int channel, void* userdata);
|
||||
|
||||
void complete_dma_now() {
|
||||
if (g_DmaInterruptHack.pending) {
|
||||
int chan = g_DmaInterruptHack.chan;
|
||||
void* data = g_DmaInterruptHack.data;
|
||||
g_DmaInterruptHack = {};
|
||||
SPUDmaIntr(chan, data);
|
||||
}
|
||||
}
|
||||
|
||||
void dma_intr_hack() {
|
||||
if (g_DmaInterruptHack.countdown) {
|
||||
g_DmaInterruptHack.countdown--;
|
||||
@@ -498,12 +509,26 @@ int DMA_SendToSPUAndSync(const u8* iop_mem,
|
||||
}
|
||||
}
|
||||
|
||||
// kick off dma, if we decided not to queue.
|
||||
// Note on DMA interrupts.
|
||||
// The DMA completion interrupt handler function may start more DMA transfers.
|
||||
// If the second transfer's completion interrupt runs before the first transfer's completion
|
||||
// interrupt returns, things break. This wasn't an issue on the real PS2 since the DMA takes
|
||||
// longer. On PC, this means that we can't just call the completion handler from the DMA start
|
||||
// function. Instead, put it at the end of this function.
|
||||
|
||||
// kick off dma, if we decided not to queue. This copies data immediately to the SPU buffer, but
|
||||
// doesn't run the completion interrupt.
|
||||
if (!defer) {
|
||||
g_bSpuDmaBusy = true;
|
||||
set_dma_intr_handler_hack(g_nSpuDmaChannel, SPUDmaIntr, user_data);
|
||||
voice_trans_wrapper(g_nSpuDmaChannel, 0, iop_mem, spu_addr, length);
|
||||
}
|
||||
|
||||
// run completion interrupts. the interrupt may start another DMA transfer, which should also
|
||||
// finish here.
|
||||
while (g_DmaInterruptHack.pending) {
|
||||
complete_dma_now();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -821,7 +821,7 @@ u32 ISOThread() {
|
||||
// ISOFileDef* file_def = nullptr;
|
||||
|
||||
while (true) {
|
||||
dma_intr_hack();
|
||||
// dma_intr_hack();
|
||||
// Part 1: Handle incoming messages from the user:
|
||||
|
||||
int poll_result = PollMbx((MsgPacket**)&mbx_cmd, g_nISOMbx);
|
||||
|
||||
Reference in New Issue
Block a user