From 5c04af50a406fe8d81ed5dbd83a691b96d34838d Mon Sep 17 00:00:00 2001 From: John Peterson Date: Mon, 15 Jun 2009 04:30:02 +0000 Subject: [PATCH] Attempt to calculate actual refresh rate (i.e. a CPU-GPU synced Mhz), no real success. Anybody have any ideas? Is there no indication from the game when the screen refresh should occur? No, not what I could find, we currently calculate the refresh rate and m_VBeamPos from the CPU ticks progress. That works perfectly if the CPU and GPU is perfectly synced as in the single core and no-idle skipping mode. So I guess it's possible that the game doesn't indicate when the screen should be refreshed, but rather that the hardware calculate that from the CPU ticks progress. That leaves us with a problem in the dual core and idle skipping modes to calculate a CPU-GPU synced CPU ticks progress. git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@3447 8ced0084-cf51-0410-be5f-012b33b47a6e --- Source/Core/AudioCommon/Src/Mixer.cpp | 13 +- Source/Core/Common/Src/MathUtil.cpp | 11 ++ Source/Core/Common/Src/MathUtil.h | 1 + Source/Core/Core/Src/Core.cpp | 161 ++++++++++++++----- Source/Core/Core/Src/CoreTiming.cpp | 1 + Source/Core/Core/Src/HW/CommandProcessor.cpp | 2 +- Source/Core/Core/Src/HW/PixelEngine.cpp | 2 +- Source/Core/Core/Src/HW/VideoInterface.cpp | 46 +++++- Source/Core/Core/Src/HW/VideoInterface.h | 1 + Source/Core/VideoCommon/Src/BPStructs.cpp | 3 +- Source/Core/VideoCommon/Src/Fifo.cpp | 54 ++++--- Source/Plugins/Plugin_VideoOGL/Src/main.cpp | 25 ++- 12 files changed, 242 insertions(+), 78 deletions(-) diff --git a/Source/Core/AudioCommon/Src/Mixer.cpp b/Source/Core/AudioCommon/Src/Mixer.cpp index 2e9f54d327..264254e6ed 100644 --- a/Source/Core/AudioCommon/Src/Mixer.cpp +++ b/Source/Core/AudioCommon/Src/Mixer.cpp @@ -92,14 +92,16 @@ void CMixer::PushSamples(short *samples, int num_stereo_samples, int core_sample // Write Other Audio if (!m_throttle) - return; - + return; + // ----------------------------------------------------------------------- + // The auto throttle function. This loop will put a ceiling on the CPU MHz. + // ŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻ /* This is only needed for non-AX sound, currently directly streamed and DTK sound. For AX we call SoundStream::Update in AXTask() for example. */ - while (m_queueSize > queue_maxlength / 2) { - + while (m_queueSize > queue_maxlength / 2) + { // Urgh. if (g_dspInitialize.pEmulatorState) { if (*g_dspInitialize.pEmulatorState != 0) @@ -108,7 +110,8 @@ void CMixer::PushSamples(short *samples, int num_stereo_samples, int core_sample soundStream->Update(); Common::SleepCurrentThread(0); } - + // ----------------------------------------------------------------------- + push_sync.Enter(); while (num_stereo_samples) { acc += core_sample_rate; diff --git a/Source/Core/Common/Src/MathUtil.cpp b/Source/Core/Common/Src/MathUtil.cpp index 88e929ec41..afd1e3e2cb 100644 --- a/Source/Core/Common/Src/MathUtil.cpp +++ b/Source/Core/Common/Src/MathUtil.cpp @@ -111,6 +111,17 @@ void MatrixMul(int n, const float *a, const float *b, float *result) } } +// Calculate sum of a float list +float MathFloatVectorSum(std::vector Vec) +{ + float Sum = 0.0; + for(int i = 0; i < Vec.size(); i++) + { + Sum += Vec.at(i); + } + return Sum; +} + void Matrix33::LoadIdentity(Matrix33 &mtx) { memset(mtx.data, 0, sizeof(mtx.data)); diff --git a/Source/Core/Common/Src/MathUtil.h b/Source/Core/Common/Src/MathUtil.h index 1226fc4f48..0064b01d45 100644 --- a/Source/Core/Common/Src/MathUtil.h +++ b/Source/Core/Common/Src/MathUtil.h @@ -121,6 +121,7 @@ inline double pow2(double x) {return x * x;} void SaveSSEState(); void LoadSSEState(); void LoadDefaultSSEState(); +float MathFloatVectorSum(std::vector); #define ROUND_UP(x, a) (((x) + (a) - 1) & ~((a) - 1)) diff --git a/Source/Core/Core/Src/Core.cpp b/Source/Core/Core/Src/Core.cpp index e744e0b7bc..bef4999574 100644 --- a/Source/Core/Core/Src/Core.cpp +++ b/Source/Core/Core/Src/Core.cpp @@ -29,6 +29,7 @@ #include "Timer.h" #include "Common.h" #include "StringUtil.h" +#include "MathUtil.h" #include "Console.h" #include "Core.h" @@ -672,61 +673,143 @@ void Callback_VideoCopiedToXFB() frames++; + // ----------------------------------------------------------------------- + // Custom frame limiter + // ŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻ if (targetfps > 0) { new_frametime = Timer.GetTimeDifference() - old_frametime; - old_frametime = Timer.GetTimeDifference(); - wait_frametime = (1000/targetfps) - (u16)new_frametime; if (targetfps < 35) wait_frametime--; if (wait_frametime > 0) Common::SleepCurrentThread(wait_frametime*2); } + // ----------------------------------------------------------------------- + + // ----------------------------------------------------------------------- + // Is it possible to calculate the CPU-GPU synced ticks for the dual core mode too? + // And possible the idle skipping mode too? + // ŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻ + static int Diff = 0, DistOld = 0; + Diff = CommandProcessor::fifo.CPReadWriteDistance - DistOld; + // If the CPReadWriteDistance has increased since the last frame we assume the CPU has raced + // ahead of the GPU and we adjust the ticks. Why multiply the difference with 700? I don't know, + // please fix it if possible. + if (Diff > 0) VideoInterface::SyncTicksProgress -= Diff * 700; + DistOld = CommandProcessor::fifo.CPReadWriteDistance; + // ----------------------------------------------------------------------- if (Timer.GetTimeDifference() >= 1000) { - old_frametime=0; - - u64 newTicks = CoreTiming::GetTicks(); - u64 newIdleTicks = CoreTiming::GetIdleTicks(); - - s64 diff = (newTicks - ticks) / 1000000; - s64 idleDiff = (newIdleTicks - idleTicks) / 1000000; - - ticks = newTicks; - idleTicks = newIdleTicks; - + // Time passed float t = (float)(Timer.GetTimeDifference()) / 1000.f; - char temp[256]; - sprintf(temp, "FPS:%8.2f - Core: %s | %s - Speed: %i MHz [Real: %i + IdleSkip: %i] / %i MHz", - (float)frames / t, -#if defined(JITTEST) && JITTEST -#ifdef _M_IX86 - _CoreParameter.bUseJIT ? "JIT32IL" : "Int32", -#else - _CoreParameter.bUseJIT ? "JIT64IL" : "Int64", -#endif -#else -#ifdef _M_IX86 - _CoreParameter.bUseJIT ? "JIT32" : "Int32", -#else - _CoreParameter.bUseJIT ? "JIT64" : "Int64", -#endif -#endif - _CoreParameter.bUseDualCore ? "DC" : "SC", - (int)(diff), - (int)(diff - idleDiff), - (int)(idleDiff), - SystemTimers::GetTicksPerSecond() / 1000000); - + // Use extended or summary information. The summary information does not print the ticks data, + // that's more of a debugging interest, it can always be optional of course if someone is interested. + //#define EXTENDED_INFO + #ifdef EXTENDED_INFO + u64 newTicks = CoreTiming::GetTicks(); + u64 newIdleTicks = CoreTiming::GetIdleTicks(); + + s64 diff = (newTicks - ticks) / 1000000; + s64 idleDiff = (newIdleTicks - idleTicks) / 1000000; + + ticks = newTicks; + idleTicks = newIdleTicks; + + float TicksPercentage = (float)diff / (float)(SystemTimers::GetTicksPerSecond() / 1000000) * 100; + #endif + + float FPS = (float)frames / t; + float FPS_To_VPS_Rate = ((float)FPS / VideoInterface::ActualRefreshRate); + // --------------------------------------------------------------------- + // For the sake of the dual core mode calculate an average to somewhat reduce the variations + // in the FPS/VPS rate + // ______________________________ + /**/ + if (_CoreParameter.bUseDualCore) + { + static std::vector FPSVPSList; + static float AverageOver = 5.0; + if (FPSVPSList.size() == AverageOver) FPSVPSList.erase(FPSVPSList.begin()); + FPSVPSList.push_back(FPS_To_VPS_Rate); + if (FPSVPSList.size() == AverageOver) + FPS_To_VPS_Rate = MathFloatVectorSum(FPSVPSList) / AverageOver; + } + + // --------------------------------------------------------------------- + // Correct the FPS/VPS rate for temporary CPU-GPU timing variations. This rate can only be 1/Integer + // so we set it to either 0.33, 0.5 or 1.0 depending on which it's closest to. + /* + 1. Notice: This rate can currently not be calculated with any accuracy at all when idle skipping + is on (the suggested tick rate in that case is much to high in proportion to the actual FPS) + 2. When dual core is enabled the CommandProcessor allow some elasticity in the FPS/VPS rate. This + is especially noticable in Zelda TP who's FPS/VPS for me varies between 0.25 and 0.6 as a + result of this. + 3. PAL 50Hz games: Are 'patched' so that they still run at the correct speed. So if the NTSC 60Hz + version has a FPS/VPS of 0.5 the 50Hz game will run at 0.6. + */ + // ______________________________ + /**/ + if (FPS_To_VPS_Rate > 0 && FPS_To_VPS_Rate < ((1.0/3.0 + 1.0/2.0)/2)) FPS_To_VPS_Rate = 1.0/3.0; + else if (FPS_To_VPS_Rate > ((1.0/3.0 + 1.0/2.0)/2) && FPS_To_VPS_Rate < ((1.0/2.0 + 1.0/1.0)/2)) FPS_To_VPS_Rate = 1.0/2.0; + else FPS_To_VPS_Rate = 1.0; + // PAL patch adjustment + if (VideoInterface::TargetRefreshRate == 50) FPS_To_VPS_Rate = FPS_To_VPS_Rate * 1.2; + + // --------------------------------------------------------------------- + float TargetFPS = FPS_To_VPS_Rate * (float)VideoInterface::TargetRefreshRate; + float FPSPercentage = (FPS / TargetFPS) * 100.0; + float VPSPercentage = (VideoInterface::ActualRefreshRate / (float)VideoInterface::TargetRefreshRate) * 100.0; + + // Settings are shown the same for both extended and summary info + std::string SSettings = StringFromFormat(" | Core: %s %s", + #if defined(JITTEST) && JITTEST + #ifdef _M_IX86 + _CoreParameter.bUseJIT ? "JIT32IL" : "Int32", + #else + _CoreParameter.bUseJIT ? "JIT64IL" : "Int64", + #endif + #else + #ifdef _M_IX86 + _CoreParameter.bUseJIT ? "JIT32" : "Int32", + #else + _CoreParameter.bUseJIT ? "JIT64" : "Int64", + #endif + #endif + _CoreParameter.bUseDualCore ? "DC" : "SC"); + // Show ~ to indicate that the value may be wrong because the ticks are wrong + std::string IdleSkipMessage = ""; + if (_CoreParameter.bSkipIdle || _CoreParameter.bUseDualCore) IdleSkipMessage = "~"; + #ifdef EXTENDED_INFO + std::string SFPS = StringFromFormat("FPS: %4.1f/%s%2.0f (%s%3.0f%% | %s%1.2f) VPS:%4.0f/%i (%3.0f%%)", + FPS, IdleSkipMessage.c_str(), TargetFPS, + IdleSkipMessage.c_str(), FPSPercentage, IdleSkipMessage.c_str(), FPS_To_VPS_Rate, + VideoInterface::ActualRefreshRate, VideoInterface::TargetRefreshRate, VPSPercentage); + std::string STicks = StringFromFormat(" | CPU: %s%i MHz [Real: %i + IdleSkip: %i] / %i MHz (%s%3.0f%%)", + IdleSkipMessage.c_str(), + (int)(diff), + (int)(diff - idleDiff), + (int)(idleDiff), + SystemTimers::GetTicksPerSecond() / 1000000, + IdleSkipMessage.c_str(), + TicksPercentage); + // Summary information + #else + std::string SFPS = StringFromFormat("FPS: %4.1f/%s%2.0f (%s%3.0f%%)", + FPS, IdleSkipMessage.c_str(), TargetFPS, IdleSkipMessage.c_str(), FPSPercentage); + std::string STicks = ""; + #endif + + std::string SMessage = StringFromFormat("%s%s%s", SFPS.c_str(), SSettings.c_str(), STicks.c_str()); + + // Show message if (g_pUpdateFPSDisplay != NULL) - g_pUpdateFPSDisplay(temp); - - Host_UpdateStatusBar(temp); - + g_pUpdateFPSDisplay(SMessage.c_str()); + Host_UpdateStatusBar(SMessage.c_str()); + // Reset frame counter frames = 0; Timer.Update(); } diff --git a/Source/Core/Core/Src/CoreTiming.cpp b/Source/Core/Core/Src/CoreTiming.cpp index 92d7c75b5b..a5fe497579 100644 --- a/Source/Core/Core/Src/CoreTiming.cpp +++ b/Source/Core/Core/Src/CoreTiming.cpp @@ -22,6 +22,7 @@ #include "CoreTiming.h" #include "StringUtil.h" +#include namespace CoreTiming { diff --git a/Source/Core/Core/Src/HW/CommandProcessor.cpp b/Source/Core/Core/Src/HW/CommandProcessor.cpp index c7cc4d21e9..4630de0dd7 100644 --- a/Source/Core/Core/Src/HW/CommandProcessor.cpp +++ b/Source/Core/Core/Src/HW/CommandProcessor.cpp @@ -641,7 +641,7 @@ void STACKALIGN GatherPipeBursted() } - +// This is mostly used in single core mode void CatchUpGPU() { // check if we are able to run this buffer diff --git a/Source/Core/Core/Src/HW/PixelEngine.cpp b/Source/Core/Core/Src/HW/PixelEngine.cpp index 590a59402d..947019acb2 100644 --- a/Source/Core/Core/Src/HW/PixelEngine.cpp +++ b/Source/Core/Core/Src/HW/PixelEngine.cpp @@ -258,7 +258,7 @@ void SetToken(const u16 _token, const int _bSetTokenAcknowledge) } // SetFinish -// THIS IS EXECUTED FROM VIDEO THREAD +// THIS IS EXECUTED FROM VIDEO THREAD (BPStructs.cpp) when a new frame has been drawn void SetFinish() { CommandProcessor::IncrementGPWDToken(); // for DC watchdog hack diff --git a/Source/Core/Core/Src/HW/VideoInterface.cpp b/Source/Core/Core/Src/HW/VideoInterface.cpp index c4d231ba6d..4baa5f3a36 100644 --- a/Source/Core/Core/Src/HW/VideoInterface.cpp +++ b/Source/Core/Core/Src/HW/VideoInterface.cpp @@ -28,6 +28,9 @@ #include "../PluginManager.h" #include "../CoreTiming.h" #include "../HW/SystemTimers.h" +#include "StringUtil.h" + +#include namespace VideoInterface { @@ -336,7 +339,7 @@ static u32 LineCount = 0; static u32 LinesPerField = 0; static u64 LastTime = 0; static u32 NextXFBRender = 0; - +int TargetRefreshRate = 0, SyncTicksProgress = 0; float ActualRefreshRate = 0.0; void DoState(PointerWrap &p) { @@ -965,6 +968,7 @@ void UpdateInterrupts() } } +// This function is unused void GenerateVIInterrupt(VIInterruptType _VIInterrupt) { switch(_VIInterrupt) @@ -1001,6 +1005,10 @@ u8* GetXFBPointerBottom() return Memory::GetPointer(m_XFBInfoBottom.FBB); } + +////////////////////////////////////////////////////////////////////////////////////////// +// Screenshot and screen message +// ŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻ void UpdateTiming() { switch (m_DisplayControlRegister.FMT) @@ -1027,9 +1035,42 @@ void UpdateTiming() break; } } +////////////////////////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////////////////////////// +// Purpose 1: Send VI interrupt for every screen refresh +// Purpose 2: Execute XFB copy in homebrew games +// Run when: This is run 7200 times per second on full speed +// ŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻ void Update() { + // ----------------------------------------------------------------------- + // Calculate actual refresh rate + // ŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻ + // Update the target refresh rate + TargetRefreshRate = (m_DisplayControlRegister.FMT == 0 || m_DisplayControlRegister.FMT == 2) + ? 60 : 50; + + // Calculate actual refresh rate + static u64 LastTick = 0; + static int UpdateCheck = timeGetTime() + 1000, TickProgress = 0; + if (UpdateCheck < timeGetTime()) + { + UpdateCheck = timeGetTime() + 1000; + TickProgress = CoreTiming::GetTicks() - LastTick; + // Calculated CPU-GPU synced ticks for the dual core mode too + NOTICE_LOG(VIDEO, "Removed: %s Mhz", ThS(SyncTicksProgress / 1000000, false).c_str()); + SyncTicksProgress += TickProgress; + // Multipled by two because of the way TicksPerFrame is calculated (divided by 25 and 30 + // rather than 50 and 60) + ActualRefreshRate = ((float)SyncTicksProgress / (float)TicksPerFrame) * 2.0; + LastTick = CoreTiming::GetTicks(); + SyncTicksProgress = 0; + } + // ----------------------------------------------------------------------- + + // Go through all lines while ((CoreTiming::GetTicks() - LastTime) > (TicksPerFrame / LineCount)) { LastTime += (TicksPerFrame / LineCount); @@ -1101,5 +1142,6 @@ void Update() } } } +////////////////////////////////////////////////////////////////////////////////////////// -} +} // namespace diff --git a/Source/Core/Core/Src/HW/VideoInterface.h b/Source/Core/Core/Src/HW/VideoInterface.h index 3307d4045f..bc3e4ca68e 100644 --- a/Source/Core/Core/Src/HW/VideoInterface.h +++ b/Source/Core/Core/Src/HW/VideoInterface.h @@ -52,6 +52,7 @@ namespace VideoInterface // Update and draw framebuffer(s) void Update(); + extern float ActualRefreshRate; extern int TargetRefreshRate, SyncTicksProgress; // UpdateInterrupts: check if we have to generate a new VI Interrupt void UpdateInterrupts(); diff --git a/Source/Core/VideoCommon/Src/BPStructs.cpp b/Source/Core/VideoCommon/Src/BPStructs.cpp index d88f32ca3d..76326969bb 100644 --- a/Source/Core/VideoCommon/Src/BPStructs.cpp +++ b/Source/Core/VideoCommon/Src/BPStructs.cpp @@ -160,7 +160,8 @@ void BPWritten(const Bypass& bp) PixelShaderManager::SetDestAlpha(bpmem.dstalpha); break; } - case BPMEM_SETDRAWDONE: // This is called when the game is done drawing (eg: like in DX: Begin(); Draw(); End();) + // This is called when the game is done drawing the new frame (eg: like in DX: Begin(); Draw(); End();) + case BPMEM_SETDRAWDONE: switch (bp.newvalue & 0xFF) { case 0x02: diff --git a/Source/Core/VideoCommon/Src/Fifo.cpp b/Source/Core/VideoCommon/Src/Fifo.cpp index b70a3a2c41..14f6a14061 100644 --- a/Source/Core/VideoCommon/Src/Fifo.cpp +++ b/Source/Core/VideoCommon/Src/Fifo.cpp @@ -76,26 +76,6 @@ u8* FAKE_GetFifoEndPtr() return &videoBuffer[size]; } -// The loop in EnterLoop sends data through this function. -// TODO: Possibly inline it? This one is exported so it will likely not be inlined at all. -void Video_SendFifoData(u8* _uData, u32 len) -{ - if (size + len >= FIFO_SIZE) - { - int pos = (int)(g_pVideoData - videoBuffer); - if (size - pos > pos) - { - PanicAlert("FIFO out of bounds (sz = %i, at %08x)", size, pos); - } - memmove(&videoBuffer[0], &videoBuffer[pos], size - pos); - size -= pos; - g_pVideoData = FAKE_GetFifoStartPtr(); - } - memcpy(videoBuffer + size, _uData, len); - size += len; - OpcodeDecoder_Run(); -} - // Executed from another thread, no the graphics thread! // Basically, all it does is set a flag so that the loop will eventually exit, then // waits for the event to be set, which happens when the loop does exit. @@ -124,7 +104,35 @@ void Fifo_ExitLoopNonBlocking() { fifoStateRun = false; } -// +////////////////////////////////////////////////////////////////////////////////////////// +// Description: Fifo_EnterLoop() sends data through this function. +// TODO: Possibly inline it? This one is exported so it will likely not be inlined at all. +// ŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻ +void Video_SendFifoData(u8* _uData, u32 len) +{ + if (size + len >= FIFO_SIZE) + { + int pos = (int)(g_pVideoData - videoBuffer); + if (size - pos > pos) + { + PanicAlert("FIFO out of bounds (sz = %i, at %08x)", size, pos); + } + memmove(&videoBuffer[0], &videoBuffer[pos], size - pos); + size -= pos; + g_pVideoData = FAKE_GetFifoStartPtr(); + } + // Copy new video instructions to videoBuffer for future use in rendering the new picture + memcpy(videoBuffer + size, _uData, len); + size += len; + OpcodeDecoder_Run(); +} +////////////////////////////////////////////////////////////////////////////////////////// + + +////////////////////////////////////////////////////////////////////////////////////////// +// Description: Main FIFO update loop +// Purpose: Keep the Core HW updated about the CPU-GPU distance +// ŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻ void Fifo_EnterLoop(const SVideoInitialize &video_initialize) { fifoStateRun = true; @@ -160,7 +168,7 @@ void Fifo_EnterLoop(const SVideoInitialize &video_initialize) video_initialize.pPeekMessages(); peek_counter = 0; } - // read the data and send it to the VideoPlugin + // Create pointer to video data and send it to the VideoPlugin u32 readPtr = _fifo.CPReadPointer; u8 *uData = video_initialize.pGetMemoryPointer(readPtr); @@ -204,6 +212,7 @@ void Fifo_EnterLoop(const SVideoInitialize &video_initialize) readPtr += distToSend; #endif } + // Execute new instructions found in uData Video_SendFifoData(uData, distToSend); Common::SyncInterlockedExchange((LONG*)&_fifo.CPReadPointer, readPtr); Common::SyncInterlockedExchangeAdd((LONG*)&_fifo.CPReadWriteDistance, -distToSend); @@ -217,3 +226,4 @@ void Fifo_EnterLoop(const SVideoInitialize &video_initialize) fifo_exit_event.SetTimer(); #endif } +////////////////////////////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/Source/Plugins/Plugin_VideoOGL/Src/main.cpp b/Source/Plugins/Plugin_VideoOGL/Src/main.cpp index 1b7f8e2bc6..dcd8a9a16b 100644 --- a/Source/Plugins/Plugin_VideoOGL/Src/main.cpp +++ b/Source/Plugins/Plugin_VideoOGL/Src/main.cpp @@ -379,9 +379,10 @@ void Shutdown(void) OpenGL_Shutdown(); } -// ------------------------------- + +////////////////////////////////////////////////////////////////////////////////////////// // Enter and exit the video loop -// ------------------------------- +// ŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻ void Video_EnterLoop() { Fifo_EnterLoop(g_VideoInitialize); @@ -391,13 +392,27 @@ void Video_ExitLoop() { Fifo_ExitLoop(); } +////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////// +// Screenshot and screen message +// ŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻ void Video_Screenshot(const char *_szFilename) { Renderer::SetScreenshot(_szFilename); } +void Video_AddMessage(const char* pstr, u32 milliseconds) +{ + OSD::AddMessage(pstr, milliseconds); +} +////////////////////////////////////////////////////////////////////////////////////////// + + +////////////////////////////////////////////////////////////////////////////////////////// +// Run from the CPU thread (from VideoInterface.cpp) for certain homebrew games only +// ŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻ void Video_UpdateXFB(u8* _pXFB, u32 _dwWidth, u32 _dwHeight, s32 _dwYOffset, bool scheduling) { if (g_Config.bUseXFB && XFB_isInit()) @@ -422,8 +437,4 @@ void Video_UpdateXFB(u8* _pXFB, u32 _dwWidth, u32 _dwHeight, s32 _dwYOffset, boo } } } - -void Video_AddMessage(const char* pstr, u32 milliseconds) -{ - OSD::AddMessage(pstr, milliseconds); -} +/////////////////////////////////////////////////////////////////////////////////////////