summaryrefslogtreecommitdiff
path: root/Source/Core/VideoCommon/Src/CommandProcessor.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/Core/VideoCommon/Src/CommandProcessor.cpp')
-rw-r--r--Source/Core/VideoCommon/Src/CommandProcessor.cpp317
1 files changed, 223 insertions, 94 deletions
diff --git a/Source/Core/VideoCommon/Src/CommandProcessor.cpp b/Source/Core/VideoCommon/Src/CommandProcessor.cpp
index 8f9b621284..df0fc6fc02 100644
--- a/Source/Core/VideoCommon/Src/CommandProcessor.cpp
+++ b/Source/Core/VideoCommon/Src/CommandProcessor.cpp
@@ -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)
+{
+ 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, 0);
+ 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