Core audio system work (Watch for regressions please!):

* Restore Audio Throttle to function properly, broken by Ayuanx many hundreds of revisions back. 
* Simplify DSPLLE JIT dispatcher in preparation for an asm rewrite
* Remove hack that made DSPLLE JIT seem faster than it was by running fewer cycles, but resulting in bad sound. This shows off how mysteriously slow it is - I don't understand why it's not faster. Use the DSPLLE interpreter for now if you want to use DSPLLE.
* Made "DSPLLE on Thread" work properly with correct-ish timing - although the speed benefit is really small now.

If it seems like this change slows anything non-LLE down, try turning off Audio Throttle and use the frame limiter in options instead.

git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@5541 8ced0084-cf51-0410-be5f-012b33b47a6e
This commit is contained in:
hrydgard 2010-05-29 21:34:34 +00:00
parent 6ea0d50872
commit 1d1b08a091
6 changed files with 76 additions and 55 deletions

View File

@ -575,25 +575,25 @@ void GenerateDSPInterruptFromPlugin(DSPInterruptType type, bool _bSet)
// This happens at 4 khz, since 32 bytes at 4khz = 4 bytes at 32 khz (16bit stereo pcm) // This happens at 4 khz, since 32 bytes at 4khz = 4 bytes at 32 khz (16bit stereo pcm)
void UpdateAudioDMA() void UpdateAudioDMA()
{ {
if (g_audioDMA.AudioDMAControl.Enable && g_audioDMA.BlocksLeft) if (g_audioDMA.BlocksLeft)
{ {
// Read audio at g_audioDMA.ReadAddress in RAM and push onto an // Read audio at g_audioDMA.ReadAddress in RAM and push onto an
// external audio fifo in the emulator, to be mixed with the disc // external audio fifo in the emulator, to be mixed with the disc
// streaming output. If that audio queue fills up, we delay the // streaming output. If that audio queue fills up, we delay the
// emulator. // emulator.
dsp_plugin->DSP_SendAIBuffer(g_audioDMA.ReadAddress, 8);
// AyuanX: let's do it in a bundle to speed up g_audioDMA.ReadAddress += 32;
if (g_audioDMA.BlocksLeft == g_audioDMA.AudioDMAControl.NumBlocks)
dsp_plugin->DSP_SendAIBuffer(g_audioDMA.SourceAddress, g_audioDMA.AudioDMAControl.NumBlocks * 8);
//g_audioDMA.ReadAddress += 32;
g_audioDMA.BlocksLeft--; g_audioDMA.BlocksLeft--;
if (g_audioDMA.BlocksLeft == 0) if (g_audioDMA.BlocksLeft == 0)
{ {
GenerateDSPInterrupt(DSP::INT_AID); GenerateDSPInterrupt(DSP::INT_AID);
//g_audioDMA.ReadAddress = g_audioDMA.SourceAddress; if (g_audioDMA.AudioDMAControl.Enable)
{
g_audioDMA.BlocksLeft = g_audioDMA.AudioDMAControl.NumBlocks; g_audioDMA.BlocksLeft = g_audioDMA.AudioDMAControl.NumBlocks;
g_audioDMA.ReadAddress = g_audioDMA.SourceAddress;
}
//DEBUG_LOG(DSPLLE, "ADMA read addresses: %08x", g_audioDMA.ReadAddress); //DEBUG_LOG(DSPLLE, "ADMA read addresses: %08x", g_audioDMA.ReadAddress);
} }
} }

View File

@ -187,6 +187,7 @@ void DSPCore_CheckExternalInterrupt()
void DSPCore_CheckExceptions() void DSPCore_CheckExceptions()
{ {
// Early out to skip the loop in the common case.
if (g_dsp.exceptions == 0) if (g_dsp.exceptions == 0)
return; return;
@ -219,10 +220,15 @@ void DSPCore_CheckExceptions()
// Handle state changes and stepping. // Handle state changes and stepping.
int DSPCore_RunCycles(int cycles) int DSPCore_RunCycles(int cycles)
{ {
if(jit) { static int spare_cycles = 0;
jit->RunBlock(cycles); if (jit)
{
// DSPCore_CheckExceptions();
// DSPCore_CheckExternalInterrupt();
spare_cycles = jit->RunForCycles(cycles + spare_cycles);
return 0; return 0;
} }
while (cycles > 0) { while (cycles > 0) {
reswitch: reswitch:
switch (core_state) switch (core_state)

View File

@ -215,53 +215,52 @@ const u8 *DSPEmitter::Compile(int start_addr) {
void STACKALIGN DSPEmitter::CompileDispatcher() void STACKALIGN DSPEmitter::CompileDispatcher()
{ {
/*
// TODO // TODO
enterDispatcher = GetCodePtr();
AlignCode16();
ABI_PushAllCalleeSavedRegsAndAdjustStack();
const u8 *outer_loop = GetCodePtr();
//Landing pad for drec space
ABI_PopAllCalleeSavedRegsAndAdjustStack();
RET();*/
} }
// Don't use the % operator in the inner loop. It's slow. // Don't use the % operator in the inner loop. It's slow.
void STACKALIGN DSPEmitter::RunBlock(int cycles) int STACKALIGN DSPEmitter::RunForCycles(int cycles)
{ {
// How does this variable work? const int idle_cycles = 1000;
static int idleskip = 0;
#define BURST_LENGTH 512 // Must be a power of two
u16 block_cycles = BURST_LENGTH + 1;
// Trigger an external interrupt at the start of the cycle
while (!(g_dsp.cr & CR_HALT)) while (!(g_dsp.cr & CR_HALT))
{ {
if (block_cycles > BURST_LENGTH) DSPCore_CheckExternalInterrupt();
{ DSPCore_CheckExceptions();
block_cycles = 0;
}
// Compile the block if needed // Compile the block if needed
if (!blocks[g_dsp.pc]) u16 block_addr = g_dsp.pc;
if (!blocks[block_addr])
{ {
blockSize[g_dsp.pc] = 0;
CompileCurrent(); CompileCurrent();
} }
int block_size = blockSize[block_addr];
// Execute the block if we have enough cycles // Execute the block if we have enough cycles
if (cycles > blockSize[g_dsp.pc]) if (cycles > block_size)
{ {
u16 start_addr = g_dsp.pc; blocks[block_addr]();
if (DSPAnalyzer::code_flags[block_addr] & DSPAnalyzer::CODE_IDLE_SKIP) {
// 5%. Not sure where the rationale originally came from. if (cycles > idle_cycles)
if (((idleskip & 127) > 121) && cycles -= idle_cycles;
(DSPAnalyzer::code_flags[g_dsp.pc] & DSPAnalyzer::CODE_IDLE_SKIP)) { else
block_cycles = 0; cycles = 0;
} else { } else {
blocks[g_dsp.pc](); cycles -= block_size;
} }
idleskip++;
if ((idleskip & (BURST_LENGTH - 1)) == 0)
idleskip = 0;
block_cycles += blockSize[start_addr];
cycles -= blockSize[start_addr];
} }
else { else {
break; break;
} }
} }
return cycles;
} }

View File

@ -44,7 +44,7 @@ public:
const u8 *Compile(int start_addr); const u8 *Compile(int start_addr);
void STACKALIGN RunBlock(int cycles); int STACKALIGN RunForCycles(int cycles);
// Register helpers // Register helpers
void setCompileSR(u16 bit); void setCompileSR(u16 bit);
@ -101,11 +101,19 @@ private:
u16 *blockSize; u16 *blockSize;
u16 compileSR; u16 compileSR;
// CALL this to start the dispatcher
u8 *enterDispatcher;
// JMP here when a block should be dispatches. make sure you're in a block
// or at the same stack level already.
u8 *dispatcher; u8 *dispatcher;
// The index of the last stored ext value (compile time). // The index of the last stored ext value (compile time).
int storeIndex; int storeIndex;
// Counts down.
// int cycles;
DISALLOW_COPY_AND_ASSIGN(DSPEmitter); DISALLOW_COPY_AND_ASSIGN(DSPEmitter);
void ToMask(Gen::X64Reg value_reg = Gen::EDI, Gen::X64Reg temp_reg = Gen::ESI); void ToMask(Gen::X64Reg value_reg = Gen::EDI, Gen::X64Reg temp_reg = Gen::ESI);

View File

@ -104,8 +104,8 @@ void Run()
gdsp_running = true; gdsp_running = true;
while (!(g_dsp.cr & CR_HALT) && gdsp_running) while (!(g_dsp.cr & CR_HALT) && gdsp_running)
{ {
if(jit) if (jit)
jit->RunBlock(1); jit->RunForCycles(1);
else { else {
// Automatically let the other threads work if we're idle skipping // Automatically let the other threads work if we're idle skipping
if(DSPAnalyzer::code_flags[g_dsp.pc] & DSPAnalyzer::CODE_IDLE_SKIP) if(DSPAnalyzer::code_flags[g_dsp.pc] & DSPAnalyzer::CODE_IDLE_SKIP)
@ -180,6 +180,7 @@ int RunCyclesDebug(int cycles)
cycles--; cycles--;
if (cycles < 0) if (cycles < 0)
return 0; return 0;
// We don't bother directly supporting pause - if the main emu pauses, // We don't bother directly supporting pause - if the main emu pauses,
// it just won't call this function anymore. // it just won't call this function anymore.
} }

View File

@ -17,6 +17,7 @@
#include "Common.h" // Common #include "Common.h" // Common
#include "Atomic.h"
#include "CommonTypes.h" #include "CommonTypes.h"
#include "LogManager.h" #include "LogManager.h"
#include "Thread.h" #include "Thread.h"
@ -49,7 +50,7 @@ SoundStream *soundStream = NULL;
bool g_InitMixer = false; bool g_InitMixer = false;
bool bIsRunning = false; bool bIsRunning = false;
u32 cycle_count = 0; volatile u32 cycle_count = 0;
// Standard crap to make wxWidgets happy // Standard crap to make wxWidgets happy
#ifdef _WIN32 #ifdef _WIN32
@ -219,17 +220,19 @@ THREAD_RETURN dsp_thread(void* lpParameter)
{ {
while (bIsRunning) while (bIsRunning)
{ {
u32 cycles = 0; int cycles = (int)cycle_count;
if (cycles > 0) {
if (jit) if (jit)
{ {
cycles = cycle_count; cycles -= DSPCore_RunCycles(cycles);
DSPCore_RunCycles(cycles); }
else {
cycles -= DSPInterpreter::RunCycles(cycles);
}
Common::AtomicAdd(cycle_count, -cycles);
} }
else
DSPInterpreter::Run();
cycle_count -= cycles; // yield?
} }
return 0; return 0;
} }
@ -374,7 +377,8 @@ void DSP_WriteMailboxLow(bool _CPUMailbox, u16 _uLowMail)
void DSP_Update(int cycles) void DSP_Update(int cycles)
{ {
int cyclesRatio = cycles / (jit?20:6); int dsp_cycles = cycles / 6; //(jit?20:6);
// Sound stream update job has been handled by AudioDMA routine, which is more efficient // Sound stream update job has been handled by AudioDMA routine, which is more efficient
/* /*
// This gets called VERY OFTEN. The soundstream update might be expensive so only do it 200 times per second or something. // This gets called VERY OFTEN. The soundstream update might be expensive so only do it 200 times per second or something.
@ -398,11 +402,14 @@ void DSP_Update(int cycles)
if (!g_dspInitialize.bOnThread) if (!g_dspInitialize.bOnThread)
{ {
// ~1/6th as many cycles as the period PPC-side. // ~1/6th as many cycles as the period PPC-side.
DSPCore_RunCycles(cyclesRatio);; DSPCore_RunCycles(dsp_cycles);
} }
else else
{ {
cycle_count += (cyclesRatio); // Wait for dsp thread to catch up reasonably. Note: this logic should be thought through.
while (cycle_count > dsp_cycles)
;
Common::AtomicAdd(cycle_count, dsp_cycles);
} }
} }