AX-HLE: delay sending interrupt when done processing command list

Fixes https://bugs.dolphin-emu.org/issues/10265 (Star Wars: The Clone
Wars hangs on loading screen with DSP-HLE and JIT Recompiler).

The Clone Wars hangs upon initial boot if this interrupt happens too
quickly after submitting a command list. When played in DSP-LLE, the
interrupt lags by about 160,000 cycles, though any value greater than or
equal to 814 will work. In other games, the lag can be as small as 50,000
cycles (in Metroid Prime) and as large as 718,092 cycles (in Tales of
Symphonia!).

All credit to @hthh, who put in a heroic(!) amount of detective work and
discovered that The Clone Wars tracks a "AXCommandListCycles" variable
which matches the aforementioned 160,000 cycles. It's initialized to ~2500
cycles for a minimal, empty command list, so that should be a safe number
for pretty much anything a game does (*crosses fingers*).
This commit is contained in:
Michael Maltese 2017-05-08 17:16:47 -07:00
parent b47d44ab15
commit 43c09c63d8
5 changed files with 25 additions and 9 deletions

View File

@ -408,15 +408,14 @@ static void GenerateDSPInterrupt(u64 DSPIntType, s64 cyclesLate)
// DSP_CONTROL - we mask by (INT_DSP | INT_ARAM | INT_AID) just to ensure people
// don't call this with bogus values.
s_dspState.Hex |= (DSPIntType & (INT_DSP | INT_ARAM | INT_AID));
UpdateInterrupts();
}
// CALLED FROM DSP EMULATOR, POSSIBLY THREADED
void GenerateDSPInterruptFromDSPEmu(DSPInterruptType type)
void GenerateDSPInterruptFromDSPEmu(DSPInterruptType type, int cycles_into_future)
{
// TODO: Maybe rethink this? The timing is unpredictable.
CoreTiming::ScheduleEvent(0, s_et_GenerateDSPInterrupt, type, CoreTiming::FromThread::ANY);
CoreTiming::ScheduleEvent(cycles_into_future, s_et_GenerateDSPInterrupt, type,
CoreTiming::FromThread::ANY);
}
// called whenever SystemTimers thinks the DSP deserves a few more cycles

View File

@ -69,7 +69,8 @@ DSPEmulator* GetDSPEmulator();
void DoState(PointerWrap& p);
void GenerateDSPInterruptFromDSPEmu(DSPInterruptType type);
// TODO: Maybe rethink this? The timing is unpredictable.
void GenerateDSPInterruptFromDSPEmu(DSPInterruptType type, int cycles_into_future = 0);
// Audio/DSP Helper
u8 ReadARAM(u32 address);

View File

@ -22,13 +22,13 @@ CMailHandler::~CMailHandler()
Clear();
}
void CMailHandler::PushMail(u32 _Mail, bool interrupt)
void CMailHandler::PushMail(u32 _Mail, bool interrupt, int cycles_into_future)
{
if (interrupt)
{
if (m_Mails.empty())
{
DSP::GenerateDSPInterruptFromDSPEmu(DSP::INT_DSP);
DSP::GenerateDSPInterruptFromDSPEmu(DSP::INT_DSP, cycles_into_future);
}
else
{

View File

@ -21,7 +21,8 @@ public:
CMailHandler();
~CMailHandler();
void PushMail(u32 _Mail, bool interrupt = false);
// TODO: figure out correct timing for interrupts rather than defaulting to "immediately."
void PushMail(u32 _Mail, bool interrupt = false, int cycles_into_future = 0);
void Clear();
void Halt(bool _Halt);
void DoState(PointerWrap& p);

View File

@ -77,7 +77,22 @@ void AXUCode::LoadResamplingCoefficients()
void AXUCode::SignalWorkEnd()
{
// Signal end of processing
m_mail_handler.PushMail(DSP_YIELD, true);
// TODO: figure out how many cycles this is actually supposed to take
// The Clone Wars hangs upon initial boot if this interrupt happens too quickly after submitting a
// command list. When played in DSP-LLE, the interrupt lags by about 160,000 cycles, though any
// value greater than or equal to 814 will work here. In other games, the lag can be as small as
// 50,000 cycles (in Metroid Prime) and as large as 718,092 cycles (in Tales of Symphonia!).
// On the PowerPC side, hthh_ discovered that The Clone Wars tracks a "AXCommandListCycles"
// variable which matches the aforementioned 160,000 cycles. It's initialized to ~2500 cycles for
// a minimal, empty command list, so that should be a safe number for pretty much anything a game
// does.
// For more information, see https://bugs.dolphin-emu.org/issues/10265.
constexpr int AX_EMPTY_COMMAND_LIST_CYCLES = 2500;
m_mail_handler.PushMail(DSP_YIELD, true, AX_EMPTY_COMMAND_LIST_CYCLES);
}
void AXUCode::HandleCommandList()