diff --git a/Source/Core/Core/Src/CoreTiming.cpp b/Source/Core/Core/Src/CoreTiming.cpp index 1f1617a9d3..021ca18e26 100644 --- a/Source/Core/Core/Src/CoreTiming.cpp +++ b/Source/Core/Core/Src/CoreTiming.cpp @@ -22,6 +22,8 @@ #include "CoreTiming.h" #include "StringUtil.h" +#include "HW/CommandProcessor.h" // <- for DC watchdog hack + // TODO(ector): Replace new/delete in this file with a simple memory pool // Don't expect a massive speedup though. @@ -303,6 +305,10 @@ void Advance() globalTimer += cyclesExecuted; + // for DC watchdog hack + // TODO (mb2): add DC mode check + CommandProcessor::CheckGPWDInterruptForSpinlock(); + while (first) { if (first->time <= globalTimer) diff --git a/Source/Core/Core/Src/HW/CommandProcessor.cpp b/Source/Core/Core/Src/HW/CommandProcessor.cpp index 90f3bb9e22..7d1812f897 100644 --- a/Source/Core/Core/Src/HW/CommandProcessor.cpp +++ b/Source/Core/Core/Src/HW/CommandProcessor.cpp @@ -47,10 +47,10 @@ // TODO (mb2): // * raise watermark Ov/Un irq: POINTLESS since emulated GP timings can't be accuratly set. -// Only 3 choises IMHO for a correct emulated load balancing in DC mode: +// Only 3 choices IMHO for a correct emulated load balancing in DC mode: // - make our own GP watchdog hack that can lock CPU if GP too slow. // - hack directly something in PPC timings (dunno how) -// - boost GP so we can consider it as infinitly fast compared to CPU. +// - boost GP so we can consider it as infinitely fast compared to CPU. // * raise ReadIdle/CmdIdle flags and observe behaviour of MP1 & ZTP (at least) // * investigate VI and PI for fixing the DC ZTP bug. // * Clean useless comments and debug stuff in Read16, Write16, GatherPipeBursted when sync will be fixed for DC @@ -173,13 +173,31 @@ int et_UpdateInterrupts; // for GP watchdog hack void IncrementGPWDToken() { -#ifdef _WIN32 +#ifdef WIN32 InterlockedIncrement((LONG*)&fifo.Fake_GPWDToken); #else - Common::InterlockedIncrement((int*)&fifo.Fake_GPWDToken); + Common::InterlockedIncrement((int*)&fifo.Fake_GPWDToken); #endif } +void CheckGPWDInterruptForSpinlock() +{ + if (fifo.Fake_GPWDInterrupt) + { + // Wait for GPU to catch up + while (fifo.bFF_GPReadEnable && (fifo.CPReadWriteDistance > 0) && !(fifo.bFF_BPEnable && fifo.bFF_Breakpoint)) + //while (fifo.bFF_GPReadEnable && (fifo.CPReadWriteDistance > 0)) + ; + //if (!fifo.bFF_GPReadEnable) PanicAlert("WARNING: Fake_GPWDInterrupt raised but can't flush fifo. Fake_GPWDToken %i",fifo.Fake_GPWDToken); +#ifdef _WIN32 + InterlockedExchange((LONG*)&fifo.Fake_GPWDInterrupt, 0); +#else + Common::InterlockedExchange((int*)&fifo.Fake_GPWDInterrupt, 0); +#endif + LOGV(COMMANDPROCESSOR, 2, "!!! Fake_GPWDInterrupt raised. Fake_GPWDToken %i",fifo.Fake_GPWDToken); + } +} + void UpdateInterrupts_Wrapper(u64 userdata, int cyclesLate) { UpdateInterrupts(); @@ -500,19 +518,8 @@ void GatherPipeBursted() if (Core::g_CoreStartupParameter.bUseDualCore) { // for GP watchdog hack - if (fifo.Fake_GPWDInterrupt) - { - // Wait for GPU to catch up - while (fifo.CPReadWriteDistance > 0) // TOCHECK: more checks could be needed - ; -#ifdef _WIN32 - InterlockedExchange((LONG*)&fifo.Fake_GPWDInterrupt, 0); -#else - Common::InterlockedExchange((int*)&fifo.Fake_GPWDInterrupt, 0); -#endif - LOGV(COMMANDPROCESSOR, 2, "!!! Fake_GPWDInterrupt raised. Fake_GPWDToken %i",fifo.Fake_GPWDToken); - } - + // not very safe to do that here + CheckGPWDInterruptForSpinlock(); // update the fifo-pointer fifo.CPWritePointer += GPFifo::GATHER_PIPE_SIZE; @@ -521,15 +528,15 @@ void GatherPipeBursted() #ifdef _WIN32 InterlockedExchangeAdd((LONG*)&fifo.CPReadWriteDistance, GPFifo::GATHER_PIPE_SIZE); #else - Common::InterlockedExchangeAdd((int*)&fifo.CPReadWriteDistance, GPFifo::GATHER_PIPE_SIZE); + Common::InterlockedExchangeAdd((int*)&fifo.CPReadWriteDistance, GPFifo::GATHER_PIPE_SIZE); #endif // High watermark overflow handling (hacked way) u32 ct=0; - if (fifo.CPReadWriteDistance > (s32)fifo.CPHiWatermark) + if (fifo.CPReadWriteDistance > fifo.CPHiWatermark) { // we should raise an Ov interrupt for an accurate fifo emulation and let PPC deal with it. - // But it slowdown things because of if(interrupt blah blah){} blocks for each 32B fifo transactions. + // 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" @@ -546,7 +553,7 @@ void GatherPipeBursted() // Wait for GPU to catch up //while (!(fifo.bFF_BPEnable && fifo.bFF_Breakpoint) && fifo.CPReadWriteDistance > (s32)fifo.CPLoWatermark) - while (fifo.CPReadWriteDistance > (s32)fifo.CPLoWatermark) + while (fifo.CPReadWriteDistance > fifo.CPLoWatermark) { ct++; // dunno if others threads (like the audio thread) really need a forced context switch here diff --git a/Source/Core/Core/Src/HW/CommandProcessor.h b/Source/Core/Core/Src/HW/CommandProcessor.h index f8bb69c7f6..79b6024353 100644 --- a/Source/Core/Core/Src/HW/CommandProcessor.h +++ b/Source/Core/Core/Src/HW/CommandProcessor.h @@ -81,6 +81,7 @@ bool AllowIdleSkipping(); // for GP watchdog hack void IncrementGPWDToken(); +void CheckGPWDInterruptForSpinlock(); } // end of namespace CommandProcessor diff --git a/Source/Core/VideoCommon/Src/Fifo.cpp b/Source/Core/VideoCommon/Src/Fifo.cpp index e9badceb00..a59cf76ee5 100644 --- a/Source/Core/VideoCommon/Src/Fifo.cpp +++ b/Source/Core/VideoCommon/Src/Fifo.cpp @@ -89,28 +89,38 @@ void Video_SendFifoData(u8* _uData, u32 len) THREAD_RETURN GPWatchdogThread(void *pArg) { const SCPFifoStruct &_fifo = *(SCPFifoStruct*)pArg; - u32 token = 0; + u32 lastToken = 0; + u32 currentToken = 0; + int FourMsCount = 0; Common::SetCurrentThreadName("GPWatchdogThread"); - Common::Thread::SetCurrentThreadAffinity(2); //Force to second core - //int i=30*1000/16; - while (1) + + while (_fifo.bFF_GPReadEnable != ~0) // blah { - Common::SleepCurrentThread(8); // about 1s/60/2 (less than twice a frame should be enough) - //if (!_fifo.bFF_GPReadIdle) - - // TODO (mb2): FIX this !!! - //if (token == _fifo.Fake_GPWDToken) + // 4 ms should be enough insignificant + Common::SleepCurrentThread(4); + currentToken = _fifo.Fake_GPWDToken; + if (lastToken == currentToken) { + FourMsCount++; + // Threshold quite arbitrary. + // Assuming the PPC-frame-finish-watchdog use RTC(TOCHECK) and throw its exception after several times the normal frame rate + // I tested higher frame-periode-factor but 3 might be safe enough for DC stability for everyone. + // I may be wrong, so TOTEST on different machine like hell !!! + if (FourMsCount >= 3*16/4)// frame_periode_factor(3) * frame_periode(~16ms) / ms_step(4) + { #ifdef _WIN32 - InterlockedExchange((LONG*)&_fifo.Fake_GPWDInterrupt, 1); + InterlockedExchange((LONG*)&_fifo.Fake_GPWDInterrupt, 1); #else - Common::InterlockedExchange((int*)&_fifo.Fake_GPWDInterrupt, 1); + Common::InterlockedExchange((int*)&_fifo.Fake_GPWDInterrupt, 1); #endif //__Log(LogTypes::VIDEO,"!!! Watchdog hit",_fifo.CPReadWriteDistance); + } } - token = _fifo.Fake_GPWDToken; - //i--; + else + FourMsCount = 0; + + lastToken = currentToken; } return 0; } @@ -119,6 +129,7 @@ THREAD_RETURN GPWatchdogThread(void *pArg) void Fifo_EnterLoop(const SVideoInitialize &video_initialize) { SCPFifoStruct &_fifo = *video_initialize.pCPFifo; + u32 distToSend; #if defined(THREAD_VIDEO_WAKEUP_ONIDLE) && defined(_WIN32) HANDLE hEventOnIdle= OpenEventA(EVENT_ALL_ACCESS,FALSE,(LPCSTR)"EventOnIdle"); if (hEventOnIdle==NULL) PanicAlert("Fifo_EnterLoop() -> EventOnIdle NULL"); @@ -127,6 +138,14 @@ void Fifo_EnterLoop(const SVideoInitialize &video_initialize) // for GP watchdog hack Common::Thread *watchdogThread = NULL; watchdogThread = new Common::Thread(GPWatchdogThread, (void*)&_fifo); + // TODO (mb2): figure out why doesn't work on core 2 ??? + // may have to force it for DualCores + //watchdogThread->SetAffinity(1); +#ifdef _WIN32 + SetThreadPriority(watchdogThread, THREAD_PRIORITY_ABOVE_NORMAL); +#else + //TODO +#endif #ifdef _WIN32 // TODO(ector): Don't peek so often! @@ -139,8 +158,8 @@ void Fifo_EnterLoop(const SVideoInitialize &video_initialize) if (MsgWaitForMultipleObjects(1, &hEventOnIdle, FALSE, 1L, QS_ALLEVENTS) == WAIT_ABANDONED) break; #endif - if (_fifo.CPReadWriteDistance < 1) - //if (_fifo.CPReadWriteDistance < _fifo.CPLoWatermark) + //if (_fifo.CPReadWriteDistance < 1) + if (_fifo.CPReadWriteDistance < _fifo.CPLoWatermark) #if defined(THREAD_VIDEO_WAKEUP_ONIDLE) && defined(_WIN32) continue; #else @@ -157,12 +176,20 @@ void Fifo_EnterLoop(const SVideoInitialize &video_initialize) while(_fifo.bFF_GPReadEnable && (_fifo.CPReadWriteDistance > 0) ) #endif { - // check if we are on a breakpoint - if (_fifo.bFF_BPEnable) + // read the data and send it to the VideoPlugin + u8 *uData = video_initialize.pGetMemoryPointer(_fifo.CPReadPointer); + + u32 readPtr = _fifo.CPReadPointer; + // if we are on BP mode we must send 32B chunks to Video plugin + // for BP checking + if (_fifo.bFF_BPEnable) { - if (_fifo.CPReadPointer == _fifo.CPBreakpoint) + distToSend = 32; + readPtr += 32; + if (readPtr == _fifo.CPBreakpoint) { video_initialize.pLog("!!! BP irq raised",FALSE); + //PanicAlert("!!! BP irq raised"); #ifdef _WIN32 InterlockedExchange((LONG*)&_fifo.bFF_Breakpoint, 1); #else @@ -172,24 +199,23 @@ void Fifo_EnterLoop(const SVideoInitialize &video_initialize) break; } } - - // read the data and send it to the VideoPlugin - u8 *uData = video_initialize.pGetMemoryPointer(_fifo.CPReadPointer); - - u32 dist = _fifo.CPReadWriteDistance; - u32 readPtr = _fifo.CPReadPointer; - if ( (dist+readPtr) > _fifo.CPEnd) // TODO: better - { - dist =_fifo.CPEnd - readPtr; - readPtr = _fifo.CPBase; - } else - readPtr += dist; - - Video_SendFifoData(uData, dist); + { + distToSend = _fifo.CPReadWriteDistance; + if ( (distToSend+readPtr) > _fifo.CPEnd) // TODO: better + { + distToSend =_fifo.CPEnd - readPtr; + readPtr = _fifo.CPBase; + } + else + readPtr += distToSend; + } + // TODO (mb2): add warning comments here for BP irq + // sending the whole CPReadWriteDistance most of the time + Video_SendFifoData(uData, distToSend); #ifdef _WIN32 InterlockedExchange((LONG*)&_fifo.CPReadPointer, readPtr); - InterlockedExchangeAdd((LONG*)&_fifo.CPReadWriteDistance, -dist); + InterlockedExchangeAdd((LONG*)&_fifo.CPReadWriteDistance, -distToSend); #else Common::InterlockedExchange((int*)&_fifo.CPReadPointer, readPtr); Common::InterlockedExchangeAdd((int*)&_fifo.CPReadWriteDistance, -dist); @@ -200,5 +226,11 @@ void Fifo_EnterLoop(const SVideoInitialize &video_initialize) #if defined(THREAD_VIDEO_WAKEUP_ONIDLE) && defined(_WIN32) CloseHandle(hEventOnIdle); #endif + // for GP watchdog DC hack + // dummy finish signal to watchdog + _fifo.bFF_GPReadEnable = ~0; + if(watchdogThread) + watchdogThread->WaitForDeath(); + } diff --git a/Source/PluginSpecs/pluginspecs_video.h b/Source/PluginSpecs/pluginspecs_video.h index 6bd9972f56..6d2e505a53 100644 --- a/Source/PluginSpecs/pluginspecs_video.h +++ b/Source/PluginSpecs/pluginspecs_video.h @@ -30,7 +30,7 @@ typedef struct volatile u32 CPEnd; u32 CPHiWatermark; u32 CPLoWatermark; - volatile s32 CPReadWriteDistance; + volatile u32 CPReadWriteDistance; volatile u32 CPWritePointer; volatile u32 CPReadPointer; volatile u32 CPBreakpoint;