diff --git a/Source/Core/Common/Src/PluginVideo.cpp b/Source/Core/Common/Src/PluginVideo.cpp index bc8a2e9164..08174fefcb 100644 --- a/Source/Core/Common/Src/PluginVideo.cpp +++ b/Source/Core/Common/Src/PluginVideo.cpp @@ -38,6 +38,7 @@ PluginVideo::PluginVideo(const char *_Filename) : CPlugin(_Filename), validVideo Video_PixelEngineWrite32 = 0; Video_GatherPipeBursted = 0; Video_WaitForFrameFinish = 0; + Video_IsFifoBusy = 0; Video_Prepare = reinterpret_cast (LoadSymbol("Video_Prepare")); @@ -71,7 +72,8 @@ PluginVideo::PluginVideo(const char *_Filename) : CPlugin(_Filename), validVideo (LoadSymbol("Video_GatherPipeBursted")); Video_WaitForFrameFinish = reinterpret_cast (LoadSymbol("Video_WaitForFrameFinish")); - + Video_IsFifoBusy = reinterpret_cast + (LoadSymbol("Video_IsFifoBusy")); if ((Video_Prepare != 0) && (Video_BeginField != 0) && (Video_EndField != 0) && @@ -88,7 +90,8 @@ PluginVideo::PluginVideo(const char *_Filename) : CPlugin(_Filename), validVideo (Video_PixelEngineWrite16 != 0) && (Video_PixelEngineWrite32 != 0) && (Video_GatherPipeBursted != 0) && - (Video_WaitForFrameFinish != 0)) + (Video_WaitForFrameFinish != 0) && + (Video_IsFifoBusy != 0)) validVideo = true; } diff --git a/Source/Core/Common/Src/PluginVideo.h b/Source/Core/Common/Src/PluginVideo.h index bee2ea7153..04a69aec92 100644 --- a/Source/Core/Common/Src/PluginVideo.h +++ b/Source/Core/Common/Src/PluginVideo.h @@ -39,6 +39,7 @@ typedef void (__cdecl* TVideo_Read32)(u32& _rReturnValue, const u32 _Address); typedef void (__cdecl* TVideo_Write32)(const u32 _Data, const u32 _Address); typedef void (__cdecl* TVideo_GatherPipeBursted)(); typedef void (__cdecl* TVideo_WaitForFrameFinish)(); +typedef bool (__cdecl* TVideo_IsFifoBusy)(); class PluginVideo : public CPlugin { @@ -66,7 +67,7 @@ public: TVideo_Write32 Video_PixelEngineWrite32; TVideo_GatherPipeBursted Video_GatherPipeBursted; TVideo_WaitForFrameFinish Video_WaitForFrameFinish; - + TVideo_IsFifoBusy Video_IsFifoBusy; private: bool validVideo; }; diff --git a/Source/Core/Core/Src/CoreTiming.cpp b/Source/Core/Core/Src/CoreTiming.cpp index a8ef993bd8..8dd4dcbbd7 100644 --- a/Source/Core/Core/Src/CoreTiming.cpp +++ b/Source/Core/Core/Src/CoreTiming.cpp @@ -22,6 +22,7 @@ #include "CoreTiming.h" #include "Core.h" #include "StringUtil.h" +#include "PluginManager.h" #define MAX_SLICE_LENGTH 20000 @@ -41,6 +42,7 @@ struct BaseEvent s64 time; u64 userdata; int type; + bool fifoWait; // Event *next; }; @@ -208,7 +210,7 @@ u64 GetIdleTicks() // This is to be called when outside threads, such as the graphics thread, wants to // schedule things to be executed on the main thread. -void ScheduleEvent_Threadsafe(int cyclesIntoFuture, int event_type, u64 userdata) +void ScheduleEvent_Threadsafe(int cyclesIntoFuture, int event_type, u64 userdata, bool fifoWait) { externalEventSection.Enter(); Event *ne = GetNewTsEvent(); @@ -216,6 +218,7 @@ void ScheduleEvent_Threadsafe(int cyclesIntoFuture, int event_type, u64 userdata ne->type = event_type; ne->next = 0; ne->userdata = userdata; + ne->fifoWait = fifoWait; if(!tsFirst) tsFirst = ne; if(tsLast) @@ -235,7 +238,7 @@ void ScheduleEvent_Threadsafe_Immediate(int event_type, u64 userdata) externalEventSection.Leave(); } else - ScheduleEvent_Threadsafe(0, event_type, userdata); + ScheduleEvent_Threadsafe(0, event_type, userdata, false); } void ClearPendingEvents() @@ -275,7 +278,7 @@ void ScheduleEvent(int cyclesIntoFuture, int event_type, u64 userdata) ne->userdata = userdata; ne->type = event_type; ne->time = globalTimer + cyclesIntoFuture; - + ne->fifoWait = false; AddEventToQueue(ne); } @@ -337,8 +340,33 @@ void ResetSliceLength() maxSliceLength = MAX_SLICE_LENGTH; } -void Advance() -{ + +//This raise only the events required while the fifo is processing data +void ProcessFifoWaitEvents() +{ + MoveEvents(); + + while (first) + { + if ((first->time <= globalTimer) && first->fifoWait) + { + + Event* evt = first; + first = first->next; + event_types[evt->type].callback(evt->userdata, (int)(globalTimer - evt->time)); + FreeEvent(evt); + } + else + { + break; + } + } + +} + +void MoveEvents() +{ + externalEventSection.Enter(); // Move events from async queue into main queue while (tsFirst) @@ -360,6 +388,13 @@ void Advance() } externalEventSection.Leave(); +} + +void Advance() +{ + + MoveEvents(); + int cyclesExecuted = slicelength - downcount; globalTimer += cyclesExecuted; downcount = slicelength; @@ -410,6 +445,15 @@ void Idle() { //DEBUG_LOG(POWERPC, "Idle"); + //When the FIFO is processing data we must not advance because in this way + //the VI will be desynchronized. So, We are waiting until the FIFO finish and + //while we process only the events required by the FIFO. + while (CPluginManager::GetInstance().GetVideo()->Video_IsFifoBusy()) + { + ProcessFifoWaitEvents(); + Common::YieldCPU(); + } + idledCycles += downcount; downcount = 0; diff --git a/Source/Core/Core/Src/CoreTiming.h b/Source/Core/Core/Src/CoreTiming.h index 2665d39f43..1b40749882 100644 --- a/Source/Core/Core/Src/CoreTiming.h +++ b/Source/Core/Core/Src/CoreTiming.h @@ -57,13 +57,15 @@ void UnregisterAllEvents(); // userdata MAY NOT CONTAIN POINTERS. userdata might get written and reloaded from disk, // when we implement state saves. void ScheduleEvent(int cyclesIntoFuture, int event_type, u64 userdata=0); -void ScheduleEvent_Threadsafe(int cyclesIntoFuture, int event_type, u64 userdata=0); +void ScheduleEvent_Threadsafe(int cyclesIntoFuture, int event_type, u64 userdata=0, bool fifoWait=false); void ScheduleEvent_Threadsafe_Immediate(int event_type, u64 userdata=0); // We only permit one event of each type in the queue at a time. void RemoveEvent(int event_type); bool IsScheduled(int event_type); void Advance(); +void MoveEvents(); +void ProcessFifoWaitEvents(); // Pretend that the main CPU has executed enough cycles to reach the next event. void Idle(); diff --git a/Source/Core/VideoCommon/Src/CommandProcessor.cpp b/Source/Core/VideoCommon/Src/CommandProcessor.cpp index c59f9b7b40..21c9919393 100644 --- a/Source/Core/VideoCommon/Src/CommandProcessor.cpp +++ b/Source/Core/VideoCommon/Src/CommandProcessor.cpp @@ -110,6 +110,8 @@ static u32 fake_GPWatchdogLastToken = 0; static Common::EventEx s_fifoIdleEvent; static Common::CriticalSection sFifoCritical; +volatile bool isFifoBusy = false; //This state is changed when the FIFO is processing data. + void FifoCriticalEnter() { sFifoCritical.Enter(); @@ -597,8 +599,8 @@ void STACKALIGN GatherPipeBursted() if (g_VideoInitialize.bOnThread) { // A little trick to prevent FIFO from overflown in dual core mode (n < 100 to avoid dead lock) - for (int cnt = 0; fifo.CPReadWriteDistance > fifo.CPEnd - fifo.CPBase && cnt < 100; cnt++) - Common::SwitchCurrentThread(); + //for (int cnt = 0; fifo.CPReadWriteDistance > fifo.CPEnd - fifo.CPBase && cnt < 100; cnt++) + // Common::SwitchCurrentThread(); } else { @@ -683,7 +685,7 @@ void UpdateInterrupts() void UpdateInterruptsFromVideoPlugin() { - g_VideoInitialize.pScheduleEvent_Threadsafe(0, et_UpdateInterrupts, 0); + g_VideoInitialize.pScheduleEvent_Threadsafe(0, et_UpdateInterrupts, 0, true); } void SetFifoIdleFromVideoPlugin() diff --git a/Source/Core/VideoCommon/Src/CommandProcessor.h b/Source/Core/VideoCommon/Src/CommandProcessor.h index 40771ecb54..4c29d46ca6 100644 --- a/Source/Core/VideoCommon/Src/CommandProcessor.h +++ b/Source/Core/VideoCommon/Src/CommandProcessor.h @@ -25,11 +25,12 @@ class PointerWrap; extern bool MT; + namespace CommandProcessor { -extern SCPFifoStruct fifo; //This one is shared between gfx thread and emulator thread - +extern SCPFifoStruct fifo; //This one is shared between gfx thread and emulator thread. +extern volatile bool isFifoBusy; //This one is used for sync gfx thread and emulator thread. // internal hardware addresses enum { diff --git a/Source/Core/VideoCommon/Src/Fifo.cpp b/Source/Core/VideoCommon/Src/Fifo.cpp index ab8bf897c2..166d7a027f 100644 --- a/Source/Core/VideoCommon/Src/Fifo.cpp +++ b/Source/Core/VideoCommon/Src/Fifo.cpp @@ -147,9 +147,13 @@ void Fifo_EnterLoop(const SVideoInitialize &video_initialize) VideoFifo_CheckEFBAccess(); 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) { + // while the FIFO is processing data we activate this for sync with emulator thread. + CommandProcessor::isFifoBusy = true; + if (!fifoStateRun) break; @@ -170,6 +174,7 @@ void Fifo_EnterLoop(const SVideoInitialize &video_initialize) if (_fifo.bFF_BPInt) CommandProcessor::UpdateInterruptsFromVideoPlugin(); CommandProcessor::FifoCriticalLeave(); + CommandProcessor::isFifoBusy = false; break; } @@ -205,15 +210,19 @@ void Fifo_EnterLoop(const SVideoInitialize &video_initialize) // Those two are pretty important and must be called in the FIFO Loop. // If we don't, s_swapRequested (OGL only) or s_efbAccessRequested won't be set to false // leading the CPU thread to wait in Video_BeginField or Video_AccessEFB thus slowing things down. + VideoFifo_CheckEFBAccess(); VideoFifo_CheckSwapRequest(); + CommandProcessor::isFifoBusy = false; } - + + if (!_fifo.CPReadIdle && _fifo.CPReadWriteDistance < _fifo.CPLoWatermark) { Common::AtomicStore(_fifo.CPReadIdle, true); CommandProcessor::UpdateInterruptsFromVideoPlugin(); } + _fifo.CPCmdIdle = true; CommandProcessor::SetFifoIdleFromVideoPlugin(); if (EmuRunning) diff --git a/Source/Core/VideoCommon/Src/PixelEngine.cpp b/Source/Core/VideoCommon/Src/PixelEngine.cpp index 920041912b..8bfe6a314e 100644 --- a/Source/Core/VideoCommon/Src/PixelEngine.cpp +++ b/Source/Core/VideoCommon/Src/PixelEngine.cpp @@ -354,7 +354,7 @@ void SetToken(const u16 _token, const int _bSetTokenAcknowledge) // This seems smelly... CommandProcessor::IncrementGPWDToken(); // for DC watchdog hack since PEToken seems to be a frame-finish too g_VideoInitialize.pScheduleEvent_Threadsafe( - 0, et_SetTokenOnMainThread, _token | (_bSetTokenAcknowledge << 16)); + 0, et_SetTokenOnMainThread, _token | (_bSetTokenAcknowledge << 16), true); } else // set token value { @@ -373,7 +373,7 @@ void SetFinish() { CommandProcessor::IncrementGPWDToken(); // for DC watchdog hack g_VideoInitialize.pScheduleEvent_Threadsafe( - 0, et_SetFinishOnMainThread, 0); + 0, et_SetFinishOnMainThread, 0, true); INFO_LOG(PIXELENGINE, "VIDEO Set Finish"); } diff --git a/Source/PluginSpecs/pluginspecs_video.h b/Source/PluginSpecs/pluginspecs_video.h index 1b5e3bbff5..2821ace9fe 100644 --- a/Source/PluginSpecs/pluginspecs_video.h +++ b/Source/PluginSpecs/pluginspecs_video.h @@ -14,7 +14,7 @@ typedef void (*TimedCallback)(u64 userdata, int cyclesLate); typedef void (*TSetInterrupt)(u32 _causemask, bool _bSet); typedef int (*TRegisterEvent)(const char *name, TimedCallback callback); -typedef void (*TScheduleEvent_Threadsafe)(int cyclesIntoFuture, int event_type, u64 userdata); +typedef void (*TScheduleEvent_Threadsafe)(int cyclesIntoFuture, int event_type, u64 userdata, bool fifoWait); typedef unsigned char* (*TGetMemoryPointer)(const unsigned int _iAddress); typedef void (*TVideoLog)(const char* _pMessage, int _bBreak); typedef void (*TSysMessage)(const char *fmt, ...); @@ -182,5 +182,14 @@ EXPORT void CALL Video_PixelEngineWrite32(const u32 _Data, const u32 _Address); EXPORT void CALL Video_GatherPipeBursted(void); EXPORT void CALL Video_WaitForFrameFinish(void); +// __________________________________________________________________________________________________ +// Function: Video_IsFifoBusy +// Purpose: Return if the FIFO is proecessing data, that is used for sync gfx thread and emulator +// thread in CoreTiming +// input: none +// output: bool +// +EXPORT bool CALL Video_IsFifoBusy(void); + #include "ExportEpilog.h" #endif diff --git a/Source/Plugins/Plugin_VideoDX11/Src/main.cpp b/Source/Plugins/Plugin_VideoDX11/Src/main.cpp index 9c934ae1e2..1dc010d577 100644 --- a/Source/Plugins/Plugin_VideoDX11/Src/main.cpp +++ b/Source/Plugins/Plugin_VideoDX11/Src/main.cpp @@ -461,3 +461,8 @@ void Video_WaitForFrameFinish(void) { CommandProcessor::WaitForFrameFinish(); } + +bool Video_IsFifoBusy(void) +{ + return CommandProcessor::isFifoBusy; +} diff --git a/Source/Plugins/Plugin_VideoDX9/Src/main.cpp b/Source/Plugins/Plugin_VideoDX9/Src/main.cpp index c9d6c4d68f..160afdd22f 100644 --- a/Source/Plugins/Plugin_VideoDX9/Src/main.cpp +++ b/Source/Plugins/Plugin_VideoDX9/Src/main.cpp @@ -506,3 +506,8 @@ void Video_WaitForFrameFinish(void) { CommandProcessor::WaitForFrameFinish(); } + +bool Video_IsFifoBusy(void) +{ + return CommandProcessor::isFifoBusy; +} diff --git a/Source/Plugins/Plugin_VideoOGL/Src/main.cpp b/Source/Plugins/Plugin_VideoOGL/Src/main.cpp index 89c1381cd6..db51766be4 100644 --- a/Source/Plugins/Plugin_VideoOGL/Src/main.cpp +++ b/Source/Plugins/Plugin_VideoOGL/Src/main.cpp @@ -508,3 +508,7 @@ void Video_WaitForFrameFinish(void) CommandProcessor::WaitForFrameFinish(); } +bool Video_IsFifoBusy(void) +{ + return CommandProcessor::isFifoBusy; +} diff --git a/Source/Plugins/Plugin_VideoSoftware/Src/CommandProcessor.cpp b/Source/Plugins/Plugin_VideoSoftware/Src/CommandProcessor.cpp index 6b2d0733b4..200d1200e3 100644 --- a/Source/Plugins/Plugin_VideoSoftware/Src/CommandProcessor.cpp +++ b/Source/Plugins/Plugin_VideoSoftware/Src/CommandProcessor.cpp @@ -322,7 +322,7 @@ void UpdateInterrupts(u64 userdata) void UpdateInterruptsFromVideoPlugin(u64 userdata) { - g_VideoInitialize.pScheduleEvent_Threadsafe(0, et_UpdateInterrupts, userdata); + g_VideoInitialize.pScheduleEvent_Threadsafe(0, et_UpdateInterrupts, userdata, true); } void ReadFifo() diff --git a/Source/Plugins/Plugin_VideoSoftware/Src/PixelEngine.cpp b/Source/Plugins/Plugin_VideoSoftware/Src/PixelEngine.cpp index 119a3b5417..255c195d84 100644 --- a/Source/Plugins/Plugin_VideoSoftware/Src/PixelEngine.cpp +++ b/Source/Plugins/Plugin_VideoSoftware/Src/PixelEngine.cpp @@ -155,7 +155,7 @@ void SetToken(const u16 _token, const int _bSetTokenAcknowledge) if (_bSetTokenAcknowledge) // set token INT { g_VideoInitialize.pScheduleEvent_Threadsafe( - 0, et_SetTokenOnMainThread, _token | (_bSetTokenAcknowledge << 16)); + 0, et_SetTokenOnMainThread, _token | (_bSetTokenAcknowledge << 16), true); } } @@ -164,7 +164,7 @@ void SetToken(const u16 _token, const int _bSetTokenAcknowledge) void SetFinish() { g_VideoInitialize.pScheduleEvent_Threadsafe( - 0, et_SetFinishOnMainThread, 0); + 0, et_SetFinishOnMainThread, 0, true); INFO_LOG(PIXELENGINE, "VIDEO Set Finish"); } diff --git a/Source/Plugins/Plugin_VideoSoftware/Src/main.cpp b/Source/Plugins/Plugin_VideoSoftware/Src/main.cpp index 29d6084306..fe77401ebd 100644 --- a/Source/Plugins/Plugin_VideoSoftware/Src/main.cpp +++ b/Source/Plugins/Plugin_VideoSoftware/Src/main.cpp @@ -209,3 +209,8 @@ void Video_GatherPipeBursted(void) void Video_WaitForFrameFinish(void) { } + +bool Video_IsFifoBusy(void) +{ + return false; +}