Sorry, I forgot to implement LoWatermark code for SC, now both SC & DC are working.

By now I'm pretty sure some games are written in multi-thread, and they depend on Watermark to suspend/resume their threads.
So without Watermark, they will never work.

git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@5709 8ced0084-cf51-0410-be5f-012b33b47a6e
This commit is contained in:
ayuanx 2010-06-15 14:24:01 +00:00
parent af9c26ff41
commit 4464c11457
2 changed files with 78 additions and 110 deletions

View File

@ -122,7 +122,7 @@ void DoState(PointerWrap &p)
{ {
p.Do(m_CPStatusReg); p.Do(m_CPStatusReg);
p.Do(m_CPCtrlReg); p.Do(m_CPCtrlReg);
p.Do(m_CPClearReg); //p.Do(m_CPClearReg);
p.Do(m_bboxleft); p.Do(m_bboxleft);
p.Do(m_bboxtop); p.Do(m_bboxtop);
p.Do(m_bboxright); p.Do(m_bboxright);
@ -178,7 +178,7 @@ void Read16(u16& _rReturnValue, const u32 _Address)
{ {
case STATUS_REGISTER: case STATUS_REGISTER:
m_CPStatusReg.Breakpoint = fifo.bFF_Breakpoint; m_CPStatusReg.Breakpoint = fifo.bFF_Breakpoint;
m_CPStatusReg.ReadIdle = fifo.bFF_Breakpoint || !fifo.CPReadWriteDistance || !fifo.bFF_GPReadEnable; m_CPStatusReg.ReadIdle = !fifo.CPReadWriteDistance || !fifo.bFF_GPReadEnable;
m_CPStatusReg.CommandIdle = fifo.CPCmdIdle; m_CPStatusReg.CommandIdle = fifo.CPCmdIdle;
m_CPStatusReg.UnderflowLoWatermark = fifo.CPReadIdle; m_CPStatusReg.UnderflowLoWatermark = fifo.CPReadIdle;
@ -333,6 +333,7 @@ void Read16(u16& _rReturnValue, const u32 _Address)
WARN_LOG(COMMANDPROCESSOR, "(r16) unknown CP reg @ %08x", _Address); WARN_LOG(COMMANDPROCESSOR, "(r16) unknown CP reg @ %08x", _Address);
return; return;
} }
return; return;
} }
@ -340,8 +341,6 @@ void Write16(const u16 _Value, const u32 _Address)
{ {
INFO_LOG(COMMANDPROCESSOR, "(write16): 0x%04x @ 0x%08x",_Value,_Address); INFO_LOG(COMMANDPROCESSOR, "(write16): 0x%04x @ 0x%08x",_Value,_Address);
if (g_VideoInitialize.bOnThread)
{
// Force complete fifo flush if we attempt to set/reset the fifo (API GXSetGPFifo or equivalent) // Force complete fifo flush if we attempt to set/reset the fifo (API GXSetGPFifo or equivalent)
// It's kind of an API hack but it works for lots of games... and I hope it's the same way for every games. // It's kind of an API hack but it works for lots of games... and I hope it's the same way for every games.
// TODO: HLE for GX fifo's APIs? // TODO: HLE for GX fifo's APIs?
@ -355,6 +354,7 @@ void Write16(const u16 _Value, const u32 _Address)
// - spin until fifo is empty // - spin until fifo is empty
// - else // - else
// - normal write16 // - normal write16
if (((_Address&0xFFF) == CTRL_REGISTER) && (_Value == 0)) // API hack if (((_Address&0xFFF) == CTRL_REGISTER) && (_Value == 0)) // API hack
{ {
// weird MP1 redo that right after linking fifo with GP... hmmm // weird MP1 redo that right after linking fifo with GP... hmmm
@ -370,10 +370,15 @@ void Write16(const u16 _Value, const u32 _Address)
DEBUG_LOG(COMMANDPROCESSOR, "*********************** GXSetGPFifo very soon? ***********************"); DEBUG_LOG(COMMANDPROCESSOR, "*********************** GXSetGPFifo very soon? ***********************");
// (mb2) We don't sleep here since it could be a perf issue for super monkey ball (yup only this game IIRC) // (mb2) We don't sleep here since it could be a perf issue for super monkey ball (yup only this game IIRC)
// Touching that game is a no-go so I don't want to take the risk :p // Touching that game is a no-go so I don't want to take the risk :p
while (fifo.bFF_GPReadEnable && fifo.CPReadWriteDistance && (!fifo.bFF_BPEnable || (fifo.bFF_BPEnable && !fifo.bFF_Breakpoint)))
if (g_VideoInitialize.bOnThread)
{ {
while (fifo.bFF_GPReadEnable && fifo.CPReadWriteDistance)
s_fifoIdleEvent.Wait(); s_fifoIdleEvent.Wait();
} }
else
{
CatchUpGPU();
} }
} }
@ -400,12 +405,12 @@ void Write16(const u16 _Value, const u32 _Address)
if (tmpCtrl.FifoOverflowIntEnable) if (tmpCtrl.FifoOverflowIntEnable)
m_CPStatusReg.OverflowHiWatermark = false; m_CPStatusReg.OverflowHiWatermark = false;
UpdateInterrupts();
fifo.bFF_BPInt = tmpCtrl.BPInt; fifo.bFF_BPInt = tmpCtrl.BPInt;
fifo.bFF_BPEnable = tmpCtrl.BPEnable; fifo.bFF_BPEnable = tmpCtrl.BPEnable;
fifo.bFF_GPReadEnable = tmpCtrl.GPReadEnable; fifo.bFF_GPReadEnable = tmpCtrl.GPReadEnable;
UpdateInterrupts();
INFO_LOG(COMMANDPROCESSOR,"\t Write to CTRL_REGISTER : %04x", _Value); INFO_LOG(COMMANDPROCESSOR,"\t Write to CTRL_REGISTER : %04x", _Value);
DEBUG_LOG(COMMANDPROCESSOR, "\t GPREAD %s | BP %s | Int %s | OvF %s | UndF %s | LINK %s" DEBUG_LOG(COMMANDPROCESSOR, "\t GPREAD %s | BP %s | Int %s | OvF %s | UndF %s | LINK %s"
, fifo.bFF_GPReadEnable ? "ON" : "OFF" , fifo.bFF_GPReadEnable ? "ON" : "OFF"
@ -513,7 +518,7 @@ void Write16(const u16 _Value, const u32 _Address)
// Super monkey try to overwrite CPReadWriteDistance by an old saved RWD value. Which is lame for us. // Super monkey try to overwrite CPReadWriteDistance by an old saved RWD value. Which is lame for us.
// hack: We have to force CPU to think fifo is alway empty and on idle. // hack: We have to force CPU to think fifo is alway empty and on idle.
// When we fall here CPReadWriteDistance should be always null and the game should always want to overwrite it by 0. // When we fall here CPReadWriteDistance should be always null and the game should always want to overwrite it by 0.
// So, we can skip it. // To skip it, comment out the following write.
case FIFO_RW_DISTANCE_HI: case FIFO_RW_DISTANCE_HI:
WriteHigh((u32 &)fifo.CPReadWriteDistance, _Value); WriteHigh((u32 &)fifo.CPReadWriteDistance, _Value);
DEBUG_LOG(COMMANDPROCESSOR,"try to write to FIFO_RW_DISTANCE_HI : %04x", _Value); DEBUG_LOG(COMMANDPROCESSOR,"try to write to FIFO_RW_DISTANCE_HI : %04x", _Value);
@ -557,7 +562,7 @@ bool AllowIdleSkipping()
// if not then lock CPUThread until GP finish a frame. // if not then lock CPUThread until GP finish a frame.
void WaitForFrameFinish() void WaitForFrameFinish()
{ {
while ((fake_GPWatchdogLastToken == fifo.Fake_GPWDToken) && fifo.bFF_GPReadEnable && fifo.CPReadWriteDistance && (!fifo.bFF_BPEnable || (fifo.bFF_BPEnable && !fifo.bFF_Breakpoint))) while ((fake_GPWatchdogLastToken == fifo.Fake_GPWDToken) && fifo.bFF_GPReadEnable && fifo.CPReadWriteDistance)
{ {
s_fifoIdleEvent.Wait(); s_fifoIdleEvent.Wait();
} }
@ -569,14 +574,16 @@ void STACKALIGN GatherPipeBursted()
{ {
// if we aren't linked, we don't care about gather pipe data // if we aren't linked, we don't care about gather pipe data
if (!m_CPCtrlReg.GPLinkEnable) if (!m_CPCtrlReg.GPLinkEnable)
{
if (!g_VideoInitialize.bOnThread)
CatchUpGPU();
return; return;
}
_assert_msg_(COMMANDPROCESSOR, fifo.CPReadWriteDistance <= fifo.CPEnd - fifo.CPBase, _assert_msg_(COMMANDPROCESSOR, fifo.CPReadWriteDistance <= fifo.CPEnd - fifo.CPBase,
"FIFO is overflown by GatherPipe !\nCPU thread is too fast, lower the HiWatermark may help."); "FIFO is overflown by GatherPipe !\nCPU thread is too fast, lower the HiWatermark may help.");
if (g_VideoInitialize.bOnThread)
{
// update the fifo-pointer // update the fifo-pointer
if (fifo.CPWritePointer >= fifo.CPEnd) if (fifo.CPWritePointer >= fifo.CPEnd)
fifo.CPWritePointer = fifo.CPBase; fifo.CPWritePointer = fifo.CPBase;
@ -585,6 +592,9 @@ void STACKALIGN GatherPipeBursted()
Common::AtomicAdd(fifo.CPReadWriteDistance, GATHER_PIPE_SIZE); Common::AtomicAdd(fifo.CPReadWriteDistance, GATHER_PIPE_SIZE);
if (!g_VideoInitialize.bOnThread)
CatchUpGPU();
// The interrupt latency in Dolphin is much longer than Hardware, so we must be more vigilant on Watermark // The interrupt latency in Dolphin is much longer than Hardware, so we must be more vigilant on Watermark
if (fifo.CPReadWriteDistance >= fifo.CPHiWatermark - 32 * 20) if (fifo.CPReadWriteDistance >= fifo.CPHiWatermark - 32 * 20)
{ {
@ -592,55 +602,12 @@ void STACKALIGN GatherPipeBursted()
if (m_CPCtrlReg.FifoOverflowIntEnable) if (m_CPCtrlReg.FifoOverflowIntEnable)
UpdateInterrupts(); UpdateInterrupts();
} }
/*
// High watermark overflow handling (hacked way)
if (fifo.CPReadWriteDistance > fifo.CPHiWatermark)
{
// we should raise an Ov interrupt for an accurate fifo emulation and let PPC deal with it.
// But it slowdowns things because of if(interrupt blah blah){} blocks for each 32B fifo transactions.
// CPU would be a bit more loaded too by its interrupt handling...
// Eather way, CPU would have the ability to resume another thread.
// To be clear: this spin loop is like a critical section spin loop in the emulated GX thread hence "hacked way"
// Yes, in real life, the only purpose of the low watermark interrupt is just for cooling down OV contention.
// - @ game start -> watermark init: Overflow enabled, Underflow disabled
// - if (OV is raised)
// - CPU stop to write to fifo
// - enable Underflow interrupt (this only happens if OV is raised)
// - do other things
// - if (Underflow is raised (implicite: AND if an OV has been raised))
// - CPU can write to fifo
// - disable Underflow interrupt
INFO_LOG(COMMANDPROCESSOR, "(GatherPipeBursted): CPHiWatermark (Hi: 0x%04x, Lo: 0x%04x) reached (RWDistance: 0x%04x)", fifo.CPHiWatermark, fifo.CPLoWatermark, fifo.CPReadWriteDistance);
// Wait for GPU to catch up
while (fifo.CPReadWriteDistance > fifo.CPLoWatermark && fifo.bFF_GPReadEnable && (!fifo.bFF_BPEnable || (fifo.bFF_BPEnable && !fifo.bFF_Breakpoint)))
{
s_fifoIdleEvent.Wait();
}
}
*/
// check if we are in sync // check if we are in sync
_assert_msg_(COMMANDPROCESSOR, fifo.CPWritePointer == *(g_VideoInitialize.Fifo_CPUWritePointer), "FIFOs linked but out of sync"); _assert_msg_(COMMANDPROCESSOR, fifo.CPWritePointer == *(g_VideoInitialize.Fifo_CPUWritePointer), "FIFOs linked but out of sync");
_assert_msg_(COMMANDPROCESSOR, fifo.CPBase == *(g_VideoInitialize.Fifo_CPUBase), "FIFOs linked but out of sync"); _assert_msg_(COMMANDPROCESSOR, fifo.CPBase == *(g_VideoInitialize.Fifo_CPUBase), "FIFOs linked but out of sync");
_assert_msg_(COMMANDPROCESSOR, fifo.CPEnd == *(g_VideoInitialize.Fifo_CPUEnd), "FIFOs linked but out of sync"); _assert_msg_(COMMANDPROCESSOR, fifo.CPEnd == *(g_VideoInitialize.Fifo_CPUEnd), "FIFOs linked but out of sync");
} }
else
{
if (fifo.CPWritePointer >= fifo.CPEnd)
fifo.CPWritePointer = fifo.CPBase;
else
fifo.CPWritePointer += GATHER_PIPE_SIZE;
// check if we are in sync
_assert_msg_(COMMANDPROCESSOR, fifo.CPWritePointer == *(g_VideoInitialize.Fifo_CPUWritePointer), "FIFOs linked but out of sync");
_assert_msg_(COMMANDPROCESSOR, fifo.CPBase == *(g_VideoInitialize.Fifo_CPUBase), "FIFOs linked but out of sync");
_assert_msg_(COMMANDPROCESSOR, fifo.CPEnd == *(g_VideoInitialize.Fifo_CPUEnd), "FIFOs linked but out of sync");
fifo.CPReadWriteDistance += GATHER_PIPE_SIZE;
CatchUpGPU();
}
}
// This is only used in single core mode // This is only used in single core mode
void CatchUpGPU() void CatchUpGPU()
@ -649,13 +616,14 @@ void CatchUpGPU()
u8 *ptr = Memory_GetPtr(fifo.CPReadPointer); u8 *ptr = Memory_GetPtr(fifo.CPReadPointer);
// check if we are able to run this buffer // check if we are able to run this buffer
while (fifo.bFF_GPReadEnable && fifo.CPReadWriteDistance && (!fifo.bFF_BPEnable || (fifo.bFF_BPEnable && !fifo.bFF_Breakpoint))) while (fifo.bFF_GPReadEnable && fifo.CPReadWriteDistance)
{ {
// check if we are on a breakpoint // check if we are on a breakpoint
if (fifo.bFF_BPEnable && ((fifo.CPReadPointer <= fifo.CPBreakpoint) && (fifo.CPReadPointer + 32 > fifo.CPBreakpoint))) if (fifo.bFF_BPEnable && ((fifo.CPReadPointer <= fifo.CPBreakpoint) && (fifo.CPReadPointer + 32 > fifo.CPBreakpoint)))
{ {
//_assert_msg_(POWERPC,0,"BP: %08x",fifo.CPBreakpoint); //_assert_msg_(POWERPC,0,"BP: %08x",fifo.CPBreakpoint);
fifo.bFF_Breakpoint = 1; Common::AtomicStore(fifo.bFF_GPReadEnable, false);
Common::AtomicStore(fifo.bFF_Breakpoint, true);
if (fifo.bFF_BPInt) if (fifo.bFF_BPInt)
UpdateInterrupts(); UpdateInterrupts();
break; break;
@ -671,23 +639,22 @@ void CatchUpGPU()
// increase the ReadPtr // increase the ReadPtr
if (fifo.CPReadPointer >= fifo.CPEnd) if (fifo.CPReadPointer >= fifo.CPEnd)
{ {
fifo.CPReadPointer = fifo.CPBase;
// adjust, take care
ptr -= fifo.CPReadPointer - fifo.CPBase; ptr -= fifo.CPReadPointer - fifo.CPBase;
DEBUG_LOG(COMMANDPROCESSOR, "Fifo Loop"); fifo.CPReadPointer = fifo.CPBase;
DEBUG_LOG(COMMANDPROCESSOR, "Fifo wraps to base");
} }
else else
{ {
fifo.CPReadPointer += 32;
ptr += 32; ptr += 32;
fifo.CPReadPointer += 32;
} }
fifo.CPReadWriteDistance -= 32; fifo.CPReadWriteDistance -= 32;
} }
if (fifo.CPReadWriteDistance >= fifo.CPHiWatermark) if (!fifo.CPReadIdle && fifo.CPReadWriteDistance < fifo.CPLoWatermark)
{ {
m_CPStatusReg.OverflowHiWatermark = true; Common::AtomicStore(fifo.CPReadIdle, true);
if (m_CPCtrlReg.FifoOverflowIntEnable) if (m_CPCtrlReg.FifoUnderflowIntEnable)
UpdateInterrupts(); UpdateInterrupts();
} }
} }

View File

@ -148,7 +148,7 @@ void Fifo_EnterLoop(const SVideoInitialize &video_initialize)
VideoFifo_CheckSwapRequest(); VideoFifo_CheckSwapRequest();
// check if we are able to run this buffer // check if we are able to run this buffer
while (_fifo.bFF_GPReadEnable && _fifo.CPReadWriteDistance && (!_fifo.bFF_BPEnable || (_fifo.bFF_BPEnable && !_fifo.bFF_Breakpoint))) while (_fifo.bFF_GPReadEnable && _fifo.CPReadWriteDistance)
{ {
if (!fifoStateRun) if (!fifoStateRun)
break; break;
@ -165,7 +165,8 @@ void Fifo_EnterLoop(const SVideoInitialize &video_initialize)
{ {
if ((readPtr <= _fifo.CPBreakpoint) && (readPtr + 32 > _fifo.CPBreakpoint)) if ((readPtr <= _fifo.CPBreakpoint) && (readPtr + 32 > _fifo.CPBreakpoint))
{ {
Common::AtomicStore(_fifo.bFF_Breakpoint, 1); Common::AtomicStore(_fifo.bFF_GPReadEnable, false);
Common::AtomicStore(_fifo.bFF_Breakpoint, true);
if (_fifo.bFF_BPInt) if (_fifo.bFF_BPInt)
CommandProcessor::UpdateInterruptsFromVideoPlugin(); CommandProcessor::UpdateInterruptsFromVideoPlugin();
CommandProcessor::FifoCriticalLeave(); CommandProcessor::FifoCriticalLeave();