BIG FIFO Commit PART 1! Sometimes you need to write everything from 0, so 10 days later Ive rewrited some parts of the FIFO in Dual Core mode. Is pending use the same code for SC mode.

- Improved the GP Register Status: now this is all the time from the fifo loop.
- Improved the Interrupts manage:
	1) Removed All UpdateInturrupts from CommandProcessor Writes and Read.
	2) now the CP interrupts are schedule from the video thread and the fifo loop waiting until this happens
	3) considering Inmediate mode for the CP interrupts
	3) Implemented Interrupt CP Cache State
	4) Implemented only Overflow interrupt in GatherPipeCheck because this need to be detected quickly.
- Implemented Overflow handling like a real HW, when Hiwatermark interrupt happens this write ClearRegister with True en HI and False in LO (FifoIntReset) after that a Control Register is writed and the FIFO is processed  to LO Watermark.
- Removed all ugly code from LO and HI watermark manage
- Removed all ugly code from BP manage
- Change >= by == in the BP clauses
- Removed speed hack (1024 chunk) for better GP Status Control.
- Commented GXSetGPFifo very soon hack
- Commented FackWatchDog hack
- Commented FIFO_RW_DISTANCE = WritePointer hack

This is the beginning and the base for the future., If this broke your favorite game my apologize, only report this and will try solve it. If you have a Overflown don't worry, I've implemented the real solution code using the Overflow Interruption only need continue working for a perfect protection. Why I did it? Because is preferable a accurate and clean fifo instead hack y fifo for improve that. Thanks to DONKO for you awesome Video Plug in and skid for the chatting. PD: I have 7-10 fps more in the star fox video. bye :P


git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@6554 8ced0084-cf51-0410-be5f-012b33b47a6e
This commit is contained in:
Marcos Vitali
2010-12-11 12:42:55 +00:00
parent 24569aca3d
commit a9ca9cfd9b
5 changed files with 275 additions and 150 deletions

View File

@ -73,7 +73,7 @@
#include "MathUtil.h"
#include "Thread.h"
#include "Atomic.h"
#include "OpcodeDecoding.h"
#include "Fifo.h"
#include "ChunkFile.h"
#include "CommandProcessor.h"
@ -84,9 +84,10 @@ namespace CommandProcessor
int et_UpdateInterrupts;
void UpdateInterrupts_Wrapper(u64 userdata, int cyclesLate)
{
UpdateInterrupts();
UpdateInterrupts(userdata);
}
// look for 1002 verts, breakpoint there, see why next draw is flushed
@ -109,8 +110,12 @@ u16 m_tokenReg;
static u32 fake_GPWatchdogLastToken = 0;
static Common::EventEx s_fifoIdleEvent;
static Common::CriticalSection sFifoCritical;
static bool bProcessFifoToLoWatemark = false;
volatile bool isFifoBusy = false; //This state is changed when the FIFO is processing data.
volatile bool interruptSet= false;
volatile bool interruptWaiting= false;
void FifoCriticalEnter()
{
@ -163,8 +168,15 @@ void Init()
memset(&fifo,0,sizeof(fifo));
fifo.CPCmdIdle = 1 ;
fifo.CPReadIdle = 1; // We use it as UnderFlow flag now, otherwise we need a new volatile variable
fifo.CPReadIdle = 1;
fifo.bFF_Breakpoint = 0;
fifo.bFF_HiWatermark = 0;
fifo.bFF_HiWatermarkInt = 0;
fifo.bFF_LoWatermark = 0;
fifo.bFF_LoWatermarkInt = 0;
interruptSet = false;
interruptWaiting = false;
s_fifoIdleEvent.Init();
@ -178,19 +190,30 @@ void Shutdown()
void Read16(u16& _rReturnValue, const u32 _Address)
{
INFO_LOG(COMMANDPROCESSOR, "(r): 0x%08x", _Address);
switch (_Address & 0xFFF)
{
case STATUS_REGISTER:
m_CPStatusReg.Breakpoint = fifo.bFF_Breakpoint;
m_CPStatusReg.ReadIdle = !fifo.CPReadWriteDistance || !fifo.bFF_GPReadEnable;
m_CPStatusReg.CommandIdle = fifo.CPCmdIdle;
m_CPStatusReg.UnderflowLoWatermark = fifo.CPReadIdle;
// hack: CPU will always believe fifo is empty and on idle
//m_CPStatusReg.ReadIdle = 1;
//m_CPStatusReg.CommandIdle = 1;
// Here always there is one fifo attached to the GPU
if (g_VideoInitialize.bOnThread)
{
m_CPStatusReg.Breakpoint = fifo.bFF_Breakpoint;
m_CPStatusReg.ReadIdle = (fifo.CPReadPointer == fifo.CPWritePointer) || (fifo.CPReadPointer == fifo.CPBreakpoint);
m_CPStatusReg.CommandIdle = fifo.CPCmdIdle;
m_CPStatusReg.UnderflowLoWatermark = fifo.bFF_LoWatermark;
m_CPStatusReg.OverflowHiWatermark = fifo.bFF_HiWatermark;
}
else
{
// Single Core MODE
m_CPStatusReg.Breakpoint = fifo.bFF_Breakpoint;
m_CPStatusReg.ReadIdle = !fifo.CPReadWriteDistance || !fifo.bFF_GPReadEnable;
m_CPStatusReg.CommandIdle = fifo.CPCmdIdle;
m_CPStatusReg.UnderflowLoWatermark = fifo.bFF_LoWatermark;
}
INFO_LOG(COMMANDPROCESSOR,"\t Read from STATUS_REGISTER : %04x", m_CPStatusReg.Hex);
DEBUG_LOG(COMMANDPROCESSOR, "(r) status: iBP %s | fReadIdle %s | fCmdIdle %s | iOvF %s | iUndF %s"
, m_CPStatusReg.Breakpoint ? "ON" : "OFF"
@ -249,15 +272,15 @@ void Read16(u16& _rReturnValue, const u32 _Address)
return;
case FIFO_READ_POINTER_LO:
//_rReturnValue = ReadLow (fifo.CPReadPointer);
_rReturnValue = ReadLow (fifo.CPReadPointer);
// hack: CPU will always believe fifo is empty and on idle
_rReturnValue = ReadLow (fifo.CPWritePointer);
//_rReturnValue = ReadLow (fifo.CPWritePointer);
DEBUG_LOG(COMMANDPROCESSOR, "read FIFO_READ_POINTER_LO : %04x", _rReturnValue);
return;
case FIFO_READ_POINTER_HI:
//_rReturnValue = ReadHigh(fifo.CPReadPointer);
_rReturnValue = ReadHigh(fifo.CPReadPointer);
// hack: CPU will always believe fifo is empty and on idle
_rReturnValue = ReadHigh(fifo.CPWritePointer);
//_rReturnValue = ReadHigh(fifo.CPWritePointer);
DEBUG_LOG(COMMANDPROCESSOR, "read FIFO_READ_POINTER_HI : %04x", _rReturnValue);
return;
@ -344,6 +367,7 @@ void Read16(u16& _rReturnValue, const u32 _Address)
void Write16(const u16 _Value, const u32 _Address)
{
INFO_LOG(COMMANDPROCESSOR, "(write16): 0x%04x @ 0x%08x",_Value,_Address);
// Force complete fifo flush if we attempt to set/reset the fifo (API GXSetGPFifo or equivalent)
@ -378,8 +402,8 @@ void Write16(const u16 _Value, const u32 _Address)
if (g_VideoInitialize.bOnThread)
{
while (fifo.bFF_GPReadEnable && fifo.CPReadWriteDistance)
s_fifoIdleEvent.Wait();
//ProcessFifoAllDistance();
}
else
{
@ -401,22 +425,25 @@ void Write16(const u16 _Value, const u32 _Address)
{
UCPCtrlReg tmpCtrl(_Value);
m_CPCtrlReg.Hex = tmpCtrl.Hex;
u32 tmpFifoGPReadEnable = fifo.bFF_GPReadEnable;
if((fifo.bFF_GPReadEnable && !tmpCtrl.GPReadEnable) || (fifo.bFF_BPInt && !tmpCtrl.BPInt))
Common::AtomicStore(fifo.bFF_Breakpoint, false);
if (tmpCtrl.FifoUnderflowIntEnable)
Common::AtomicStore(fifo.CPReadIdle, false);
if (tmpCtrl.FifoOverflowIntEnable)
m_CPStatusReg.OverflowHiWatermark = false;
UpdateInterrupts();
// If the new fifo is being attached We make sure there wont be SetFinish event pending.
// This protection fix eternal darkness booting, because the second SetFinish event when it is booting
// seems invalid or has a bug and hang the game.
// Single Core MODE
if (!g_VideoInitialize.bOnThread)
{
Common::AtomicStore(fifo.bFF_Breakpoint, false);
if (tmpCtrl.FifoUnderflowIntEnable)
Common::AtomicStore(fifo.bFF_LoWatermark, false);
if (tmpCtrl.FifoOverflowIntEnable)
m_CPStatusReg.OverflowHiWatermark = false;
UpdateInterruptsScMode();
}
if (!fifo.bFF_GPReadEnable && tmpCtrl.GPReadEnable && !tmpCtrl.BPEnable)
{
PixelEngine::ResetSetFinish();
@ -424,22 +451,13 @@ void Write16(const u16 _Value, const u32 _Address)
fifo.bFF_BPInt = tmpCtrl.BPInt;
fifo.bFF_BPEnable = tmpCtrl.BPEnable;
if(tmpFifoGPReadEnable == fifo.bFF_GPReadEnable)
{
fifo.bFF_GPReadEnable = tmpCtrl.GPReadEnable;
if (fifo.bFF_GPReadEnable && (fifo.CPReadWriteDistance >= fifo.CPHiWatermark))
{
// A little trick to prevent FIFO from overflown in dual core mode
while (fifo.bFF_GPReadEnable && (fifo.CPReadWriteDistance >= fifo.CPHiWatermark))
Common::YieldCPU();
if (!m_CPStatusReg.OverflowHiWatermark)
m_CPStatusReg.OverflowHiWatermark = true;
}
fifo.bFF_GPReadEnable = tmpCtrl.GPReadEnable;
fifo.bFF_HiWatermarkInt = tmpCtrl.FifoOverflowIntEnable;
fifo.bFF_LoWatermarkInt = tmpCtrl.FifoUnderflowIntEnable;
}
// If overflown happens process the fifo to LoWatemark
if (bProcessFifoToLoWatemark)
ProcessFifoToLoWatemark();
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"
@ -456,11 +474,24 @@ void Write16(const u16 _Value, const u32 _Address)
case CLEAR_REGISTER:
{
UCPClearReg tmpCtrl(_Value);
if (tmpCtrl.ClearFifoOverflow)
m_CPStatusReg.OverflowHiWatermark = false;
if (tmpCtrl.ClearFifoUnderflow)
Common::AtomicStore(fifo.CPReadIdle, false);
UpdateInterrupts();
if (g_VideoInitialize.bOnThread)
{
if (!tmpCtrl.ClearFifoUnderflow && tmpCtrl.ClearFifoOverflow)
bProcessFifoToLoWatemark = true;
}
else
{
// Single Core MODE
if (tmpCtrl.ClearFifoOverflow)
m_CPStatusReg.OverflowHiWatermark = false;
if (tmpCtrl.ClearFifoUnderflow)
Common::AtomicStore(fifo.bFF_LoWatermark, false);
UpdateInterruptsScMode();
}
DEBUG_LOG(COMMANDPROCESSOR,"\t write to CLEAR_REGISTER : %04x", _Value);
}
break;
@ -519,8 +550,6 @@ void Write16(const u16 _Value, const u32 _Address)
break;
case FIFO_HI_WATERMARK_HI:
WriteHigh((u32 &)fifo.CPHiWatermark, _Value);
// Tune this when you see lots of FIFO overflown by GatherPipe
//HiWatermark_Tighter = fifo.CPHiWatermark - 32 * g_ActiveConfig.iFIFOWatermarkTightness;
DEBUG_LOG(COMMANDPROCESSOR,"\t write to FIFO_HI_WATERMARK_HI : %04x", _Value);
break;
@ -561,6 +590,8 @@ void Write16(const u16 _Value, const u32 _Address)
if (!g_VideoInitialize.bOnThread)
CatchUpGPU();
}
void Read32(u32& _rReturnValue, const u32 _Address)
@ -580,34 +611,34 @@ void IncrementGPWDToken()
Common::AtomicIncrement(fifo.Fake_GPWDToken);
}
bool AllowIdleSkipping()
{
return !g_VideoInitialize.bOnThread || !m_CPCtrlReg.BPEnable;
}
// Check every FAKE_GP_WATCHDOG_PERIOD if a PE-frame-finish occurred
// if not then lock CPUThread until GP finish a frame.
void WaitForFrameFinish()
{
while ((fake_GPWatchdogLastToken == fifo.Fake_GPWDToken) && fifo.bFF_GPReadEnable && fifo.CPReadWriteDistance)
{
s_fifoIdleEvent.Wait();
}
fake_GPWatchdogLastToken = fifo.Fake_GPWDToken;
//while ((fake_GPWatchdogLastToken == fifo.Fake_GPWDToken) && fifo.bFF_GPReadEnable && fifo.CPReadWriteDistance)
//{
// s_fifoIdleEvent.Wait();
//}
//fake_GPWatchdogLastToken = fifo.Fake_GPWDToken;
}
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;
}
if (g_VideoInitialize.bOnThread)
SetOverflowStatusFromGatherPipe();
// update the fifo-pointer
if (fifo.CPWritePointer >= fifo.CPEnd)
fifo.CPWritePointer = fifo.CPBase;
@ -616,38 +647,22 @@ void STACKALIGN GatherPipeBursted()
Common::AtomicAdd(fifo.CPReadWriteDistance, GATHER_PIPE_SIZE);
if (g_VideoInitialize.bOnThread)
{
if (fifo.CPReadWriteDistance >= fifo.CPHiWatermark)
{
// A little trick to prevent FIFO from overflown in dual core mode
while (fifo.bFF_GPReadEnable && (fifo.CPReadWriteDistance >= fifo.CPHiWatermark))
Common::YieldCPU();
if (!m_CPStatusReg.OverflowHiWatermark)
{
m_CPStatusReg.OverflowHiWatermark = true;
if (m_CPCtrlReg.FifoOverflowIntEnable)
UpdateInterrupts();
}
}
}
else
if (!g_VideoInitialize.bOnThread)
{
CatchUpGPU();
if (!m_CPStatusReg.OverflowHiWatermark && fifo.CPReadWriteDistance >= fifo.CPHiWatermark)
{
m_CPStatusReg.OverflowHiWatermark = true;
if (m_CPCtrlReg.FifoOverflowIntEnable)
UpdateInterrupts();
}
UpdateInterruptsScMode();
}
}
_assert_msg_(COMMANDPROCESSOR, fifo.CPReadWriteDistance <= fifo.CPEnd - fifo.CPBase,
"FIFO is overflown by GatherPipe !\nCPU thread is too fast, try changing the watermark tightness in the game properties.");
"FIFO is overflown by GatherPipe !\nCPU thread is too fast, try changing the watermark tightness in the game properties.");
// check if we are in sync
_assert_msg_(COMMANDPROCESSOR, fifo.CPWritePointer == *(g_VideoInitialize.Fifo_CPUWritePointer), "FIFOs linked but out of sync");
@ -655,6 +670,7 @@ void STACKALIGN GatherPipeBursted()
_assert_msg_(COMMANDPROCESSOR, fifo.CPEnd == *(g_VideoInitialize.Fifo_CPUEnd), "FIFOs linked but out of sync");
}
// This is only used in single core mode
void CatchUpGPU()
{
@ -672,7 +688,7 @@ void CatchUpGPU()
Common::AtomicStore(fifo.bFF_GPReadEnable, false);
Common::AtomicStore(fifo.bFF_Breakpoint, true);
if (fifo.bFF_BPInt)
UpdateInterrupts();
UpdateInterruptsScMode();
break;
}
@ -681,6 +697,7 @@ void CatchUpGPU()
SaveSSEState();
LoadDefaultSSEState();
Fifo_SendFifoData(ptr,32);
OpcodeDecoder_Run(g_bSkipCurrentFrame);
LoadSSEState();
// increase the ReadPtr
@ -698,26 +715,48 @@ void CatchUpGPU()
fifo.CPReadWriteDistance -= 32;
}
if (!fifo.CPReadIdle && fifo.CPReadWriteDistance < fifo.CPLoWatermark)
if (!fifo.bFF_LoWatermark && fifo.CPReadWriteDistance < fifo.CPLoWatermark)
{
Common::AtomicStore(fifo.CPReadIdle, true);
Common::AtomicStore(fifo.bFF_LoWatermark, true);
if (m_CPCtrlReg.FifoUnderflowIntEnable)
UpdateInterrupts();
UpdateInterruptsScMode();
}
}
void UpdateInterrupts()
void UpdateInterruptsScMode()
{
bool active = (fifo.bFF_BPInt && fifo.bFF_Breakpoint)
|| (m_CPCtrlReg.FifoUnderflowIntEnable && fifo.CPReadIdle)
|| (m_CPCtrlReg.FifoUnderflowIntEnable && fifo.bFF_LoWatermark)
|| (m_CPCtrlReg.FifoOverflowIntEnable && m_CPStatusReg.OverflowHiWatermark);
INFO_LOG(COMMANDPROCESSOR, "Fifo Interrupt: %s", (active)? "Asserted" : "Deasserted");
g_VideoInitialize.pSetInterrupt(INT_CAUSE_CP, active);
}
void UpdateInterruptsFromVideoPlugin()
void UpdateInterrupts(u64 userdata)
{
g_VideoInitialize.pScheduleEvent_Threadsafe(0, et_UpdateInterrupts, 0);
if (userdata)
{
interruptSet = true;
INFO_LOG(COMMANDPROCESSOR,"Interrupt set");
g_VideoInitialize.pSetInterrupt(INT_CAUSE_CP, true);
}
else
{
if(!fifo.bFF_Breakpoint)
{
interruptSet = false;
INFO_LOG(COMMANDPROCESSOR,"Interrupt cleared %i %i %i %i", fifo.CPReadPointer, fifo.CPBreakpoint, fifo.CPWritePointer, fifo.CPReadWriteDistance);
g_VideoInitialize.pSetInterrupt(INT_CAUSE_CP, false);
}
}
interruptWaiting = false;
}
void UpdateInterruptsFromVideoPlugin(u64 userdata)
{
g_VideoInitialize.pScheduleEvent_Threadsafe(0, et_UpdateInterrupts, userdata);
}
void SetFifoIdleFromVideoPlugin()
@ -731,12 +770,102 @@ void SetFifoIdleFromVideoPlugin()
// to 0 when PI_FIFO_RESET occurs.
void AbortFrame()
{
Fifo_SetRendering(false);
while(!fifo.CPCmdIdle)
Common::YieldCPU();
ProcessFifoAllDistance();
Fifo_SetRendering(true);
PixelEngine::ResetSetToken();
PixelEngine::ResetSetFinish();
}
void SetOverflowStatusFromGatherPipe()
{
if (!fifo.bFF_HiWatermarkInt) return;
// overflow check
fifo.bFF_HiWatermark = (fifo.CPReadWriteDistance > fifo.CPHiWatermark);
fifo.bFF_LoWatermark = (fifo.CPReadWriteDistance < fifo.CPLoWatermark);
bool interrupt = fifo.bFF_HiWatermark && fifo.bFF_HiWatermarkInt &&
m_CPCtrlReg.GPLinkEnable && m_CPCtrlReg.GPReadEnable;
if (interrupt != interruptSet && interrupt)
CommandProcessor::UpdateInterrupts(true);
}
void SetStatus()
{
// overflow check
fifo.bFF_HiWatermark = (fifo.CPReadWriteDistance > fifo.CPHiWatermark);
fifo.bFF_LoWatermark = (fifo.CPReadWriteDistance < fifo.CPLoWatermark);
// breakpoint
if (fifo.bFF_BPEnable)
{
if (fifo.CPBreakpoint == fifo.CPReadPointer)
{
if (!fifo.bFF_Breakpoint)
INFO_LOG(COMMANDPROCESSOR, "Hit breakpoint at %i %i", fifo.CPReadPointer);
fifo.bFF_Breakpoint = true;
}
else
{
if (fifo.bFF_Breakpoint)
INFO_LOG(COMMANDPROCESSOR, "Cleared breakpoint at %i %i", fifo.CPReadPointer);
fifo.bFF_Breakpoint = false;
}
}
else
{
if (fifo.bFF_Breakpoint)
INFO_LOG(COMMANDPROCESSOR, "Cleared breakpoint at %i %i", fifo.CPReadPointer);
fifo.bFF_Breakpoint = false;
}
bool bpInt = fifo.bFF_Breakpoint && fifo.bFF_BPInt;
bool ovfInt = fifo.bFF_HiWatermark && fifo.bFF_HiWatermarkInt;
bool undfInt = fifo.bFF_LoWatermark && fifo.bFF_LoWatermarkInt;
bool interrupt = (bpInt || ovfInt || undfInt) && m_CPCtrlReg.GPLinkEnable && m_CPCtrlReg.GPReadEnable;
if (interrupt != interruptSet && !interruptWaiting)
{
u64 userdata = interrupt?1:0;
if (g_VideoInitialize.bOnThread)
{
interruptWaiting = true;
CommandProcessor::UpdateInterruptsFromVideoPlugin(userdata);
}
else
CommandProcessor::UpdateInterrupts(userdata);
}
}
void ProcessFifoToLoWatemark()
{
if (g_VideoInitialize.bOnThread)
{
while (!CommandProcessor::interruptWaiting && fifo.bFF_GPReadEnable &&
fifo.CPReadWriteDistance > fifo.CPLoWatermark && !AtBreakpoint())
Common::YieldCPU();
}
bProcessFifoToLoWatemark = false;
}
void ProcessFifoAllDistance()
{
if (g_VideoInitialize.bOnThread)
{
while (!CommandProcessor::interruptWaiting && fifo.bFF_GPReadEnable &&
fifo.CPReadWriteDistance && !AtBreakpoint())
Common::YieldCPU();
}
}
} // end of namespace CommandProcessor