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_CPCtrlReg);
p.Do(m_CPClearReg);
//p.Do(m_CPClearReg);
p.Do(m_bboxleft);
p.Do(m_bboxtop);
p.Do(m_bboxright);
@ -178,7 +178,7 @@ void Read16(u16& _rReturnValue, const u32 _Address)
{
case STATUS_REGISTER:
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.UnderflowLoWatermark = fifo.CPReadIdle;
@ -333,6 +333,7 @@ void Read16(u16& _rReturnValue, const u32 _Address)
WARN_LOG(COMMANDPROCESSOR, "(r16) unknown CP reg @ %08x", _Address);
return;
}
return;
}
@ -340,40 +341,44 @@ void Write16(const u16 _Value, const u32 _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)
// 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?
// Here is the hack:
// - if (attempt to overwrite CTRL_REGISTER by 0x0000)
// // then we assume CPReadWriteDistance will be overwrited very soon.
// - if (fifo is not empty)
// // (not 100% sure): shouln't happen unless PPC think having trouble with the sync
// // and it attempt a fifo recovery (look for PI_FIFO_RESET in log).
// // If we want to emulate self fifo recovery we need proper GX metrics emulation... yeah sure :p
// - spin until fifo is empty
// - else
// - normal write16
if (((_Address&0xFFF) == CTRL_REGISTER) && (_Value == 0)) // API hack
{
// weird MP1 redo that right after linking fifo with GP... hmmm
//_dbg_assert_msg_(COMMANDPROCESSOR, fifo.CPReadWriteDistance == 0,
// "WTF! Something went wrong with GP/PPC the sync! -> CPReadWriteDistance: 0x%08X\n"
// " - The fifo is not empty but we are going to lock it anyway.\n"
// " - \"Normaly\", this is due to fifo-hang-so-lets-attempt-recovery.\n"
// " - The bad news is dolphin don't support special recovery features like GXfifo's metric yet.\n"
// " - The good news is, the time you read that message, the fifo should be empty now :p\n"
// " - Anyway, fifo flush will be forced if you press OK and dolphin might continue to work...\n"
// " - We aren't betting on that :)", fifo.CPReadWriteDistance);
// 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.
// TODO: HLE for GX fifo's APIs?
// Here is the hack:
// - if (attempt to overwrite CTRL_REGISTER by 0x0000)
// // then we assume CPReadWriteDistance will be overwrited very soon.
// - if (fifo is not empty)
// // (not 100% sure): shouln't happen unless PPC think having trouble with the sync
// // and it attempt a fifo recovery (look for PI_FIFO_RESET in log).
// // If we want to emulate self fifo recovery we need proper GX metrics emulation... yeah sure :p
// - spin until fifo is empty
// - else
// - normal write16
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)
// 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 (((_Address&0xFFF) == CTRL_REGISTER) && (_Value == 0)) // API hack
{
// weird MP1 redo that right after linking fifo with GP... hmmm
//_dbg_assert_msg_(COMMANDPROCESSOR, fifo.CPReadWriteDistance == 0,
// "WTF! Something went wrong with GP/PPC the sync! -> CPReadWriteDistance: 0x%08X\n"
// " - The fifo is not empty but we are going to lock it anyway.\n"
// " - \"Normaly\", this is due to fifo-hang-so-lets-attempt-recovery.\n"
// " - The bad news is dolphin don't support special recovery features like GXfifo's metric yet.\n"
// " - The good news is, the time you read that message, the fifo should be empty now :p\n"
// " - Anyway, fifo flush will be forced if you press OK and dolphin might continue to work...\n"
// " - We aren't betting on that :)", fifo.CPReadWriteDistance);
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)
// Touching that game is a no-go so I don't want to take the risk :p
if (g_VideoInitialize.bOnThread)
{
while (fifo.bFF_GPReadEnable && fifo.CPReadWriteDistance)
s_fifoIdleEvent.Wait();
}
}
else
{
CatchUpGPU();
}
}
@ -400,12 +405,12 @@ void Write16(const u16 _Value, const u32 _Address)
if (tmpCtrl.FifoOverflowIntEnable)
m_CPStatusReg.OverflowHiWatermark = false;
UpdateInterrupts();
fifo.bFF_BPInt = tmpCtrl.BPInt;
fifo.bFF_BPEnable = tmpCtrl.BPEnable;
fifo.bFF_GPReadEnable = tmpCtrl.GPReadEnable;
UpdateInterrupts();
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"
, 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.
// 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.
// So, we can skip it.
// To skip it, comment out the following write.
case FIFO_RW_DISTANCE_HI:
WriteHigh((u32 &)fifo.CPReadWriteDistance, _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.
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();
}
@ -569,77 +574,39 @@ void STACKALIGN GatherPipeBursted()
{
// if we aren't linked, we don't care about gather pipe data
if (!m_CPCtrlReg.GPLinkEnable)
{
if (!g_VideoInitialize.bOnThread)
CatchUpGPU();
return;
}
_assert_msg_(COMMANDPROCESSOR, fifo.CPReadWriteDistance <= fifo.CPEnd - fifo.CPBase,
"FIFO is overflown by GatherPipe !\nCPU thread is too fast, lower the HiWatermark may help.");
if (g_VideoInitialize.bOnThread)
{
// update the fifo-pointer
if (fifo.CPWritePointer >= fifo.CPEnd)
fifo.CPWritePointer = fifo.CPBase;
else
fifo.CPWritePointer += GATHER_PIPE_SIZE;
Common::AtomicAdd(fifo.CPReadWriteDistance, GATHER_PIPE_SIZE);
// 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)
{
m_CPStatusReg.OverflowHiWatermark = true;
if (m_CPCtrlReg.FifoOverflowIntEnable)
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
_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");
}
// update the fifo-pointer
if (fifo.CPWritePointer >= fifo.CPEnd)
fifo.CPWritePointer = fifo.CPBase;
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.CPWritePointer += GATHER_PIPE_SIZE;
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
if (fifo.CPReadWriteDistance >= fifo.CPHiWatermark - 32 * 20)
{
m_CPStatusReg.OverflowHiWatermark = true;
if (m_CPCtrlReg.FifoOverflowIntEnable)
UpdateInterrupts();
}
// 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");
}
// This is only used in single core mode
@ -649,13 +616,14 @@ void CatchUpGPU()
u8 *ptr = Memory_GetPtr(fifo.CPReadPointer);
// 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
if (fifo.bFF_BPEnable && ((fifo.CPReadPointer <= fifo.CPBreakpoint) && (fifo.CPReadPointer + 32 > 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)
UpdateInterrupts();
break;
@ -671,23 +639,22 @@ void CatchUpGPU()
// increase the ReadPtr
if (fifo.CPReadPointer >= fifo.CPEnd)
{
fifo.CPReadPointer = fifo.CPBase;
// adjust, take care
ptr -= fifo.CPReadPointer - fifo.CPBase;
DEBUG_LOG(COMMANDPROCESSOR, "Fifo Loop");
fifo.CPReadPointer = fifo.CPBase;
DEBUG_LOG(COMMANDPROCESSOR, "Fifo wraps to base");
}
else
{
fifo.CPReadPointer += 32;
ptr += 32;
fifo.CPReadPointer += 32;
}
fifo.CPReadWriteDistance -= 32;
}
if (fifo.CPReadWriteDistance >= fifo.CPHiWatermark)
if (!fifo.CPReadIdle && fifo.CPReadWriteDistance < fifo.CPLoWatermark)
{
m_CPStatusReg.OverflowHiWatermark = true;
if (m_CPCtrlReg.FifoOverflowIntEnable)
Common::AtomicStore(fifo.CPReadIdle, true);
if (m_CPCtrlReg.FifoUnderflowIntEnable)
UpdateInterrupts();
}
}

View File

@ -148,7 +148,7 @@ void Fifo_EnterLoop(const SVideoInitialize &video_initialize)
VideoFifo_CheckSwapRequest();
// 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)
break;
@ -165,7 +165,8 @@ void Fifo_EnterLoop(const SVideoInitialize &video_initialize)
{
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)
CommandProcessor::UpdateInterruptsFromVideoPlugin();
CommandProcessor::FifoCriticalLeave();