From cbb786c7f02270eb80a1949650d236ec21ca2819 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Mon, 18 Feb 2013 23:25:06 +0100 Subject: [PATCH] Restructure Windows "gameloop" to be more similar to others. --- Core/Core.cpp | 14 ++- Core/HLE/sceDisplay.cpp | 189 ++++++++++++++++++------------- Core/Host.h | 1 - Windows/OpenGLBase.cpp | 9 +- Windows/OpenGLBase.h | 5 +- Windows/WindowsHost.cpp | 5 - Windows/WindowsHost.h | 1 - android/jni/NativeApp.cpp | 1 - headless/Headless.cpp | 20 ++-- headless/StubHost.h | 6 +- headless/WindowsHeadlessHost.cpp | 4 +- headless/WindowsHeadlessHost.h | 3 +- 12 files changed, 147 insertions(+), 111 deletions(-) diff --git a/Core/Core.cpp b/Core/Core.cpp index fb8f92966c5a..c2de39e7874b 100644 --- a/Core/Core.cpp +++ b/Core/Core.cpp @@ -22,6 +22,9 @@ #include "Core.h" #include "MemMap.h" #include "MIPS/MIPS.h" +#ifdef _WIN32 +#include "Windows/OpenGLBase.h" +#endif #include "Host.h" @@ -64,7 +67,16 @@ bool Core_IsStepping() void Core_RunLoop() { - currentMIPS->RunLoopUntil(0xFFFFFFFFFFFFFFFULL); + while (!coreState) { + currentMIPS->RunLoopUntil(0xFFFFFFFFFFFFFFFULL); + if (coreState == CORE_NEXTFRAME) + { +#ifdef _WIN32 + GL_SwapBuffers(); +#endif + coreState = CORE_RUNNING; + } + } } void Core_DoSingleStep() diff --git a/Core/HLE/sceDisplay.cpp b/Core/HLE/sceDisplay.cpp index 86bf36750a94..ed552dcbaa72 100644 --- a/Core/HLE/sceDisplay.cpp +++ b/Core/HLE/sceDisplay.cpp @@ -80,6 +80,7 @@ static bool framebufIsLatched; static int enterVblankEvent = -1; static int leaveVblankEvent = -1; +static int afterFlipEvent = -1; static int hCount; static int hCountTotal; //unused @@ -107,6 +108,7 @@ enum { void hleEnterVblank(u64 userdata, int cyclesLate); void hleLeaveVblank(u64 userdata, int cyclesLate); +void hleAfterFlip(u64 userdata, int cyclesLate); void __DisplayInit() { gpuStats.reset(); @@ -118,6 +120,7 @@ void __DisplayInit() { enterVblankEvent = CoreTiming::RegisterEvent("EnterVBlank", &hleEnterVblank); leaveVblankEvent = CoreTiming::RegisterEvent("LeaveVBlank", &hleLeaveVblank); + afterFlipEvent = CoreTiming::RegisterEvent("AfterFlip", &hleAfterFlip); CoreTiming::ScheduleEvent(msToCycles(frameMs - vblankMs), enterVblankEvent, 0); isVblank = 0; @@ -202,6 +205,93 @@ float calculateFPS() return fps; } +void DebugStats() +{ + gpu->UpdateStats(); + char stats[2048]; + + sprintf(stats, + "Frames: %i\n" + "DL processing time: %0.2f ms\n" + "Kernel processing time: %0.2f ms\n" + "Slowest syscall: %s : %0.2f ms\n" + "Most active syscall: %s : %0.2f ms\n" + "Draw calls: %i, flushes %i\n" + "Cached Draw calls: %i\n" + "Num Tracked Vertex Arrays: %i\n" + "Vertices Submitted: %i\n" + "Cached Vertices Drawn: %i\n" + "Uncached Vertices Drawn: %i\n" + "FBOs active: %i\n" + "Textures active: %i, decoded: %i\n" + "Texture invalidations: %i\n" + "Vertex shaders loaded: %i\n" + "Fragment shaders loaded: %i\n" + "Combined shaders loaded: %i\n", + gpuStats.numFrames, + gpuStats.msProcessingDisplayLists * 1000.0f, + kernelStats.msInSyscalls * 1000.0f, + kernelStats.slowestSyscallName ? kernelStats.slowestSyscallName : "(none)", + kernelStats.slowestSyscallTime * 1000.0f, + kernelStats.summedSlowestSyscallName ? kernelStats.summedSlowestSyscallName : "(none)", + kernelStats.summedSlowestSyscallTime * 1000.0f, + gpuStats.numDrawCalls, + gpuStats.numFlushes, + gpuStats.numCachedDrawCalls, + gpuStats.numTrackedVertexArrays, + gpuStats.numVertsSubmitted, + gpuStats.numCachedVertsDrawn, + gpuStats.numUncachedVertsDrawn, + gpuStats.numFBOs, + gpuStats.numTextures, + gpuStats.numTexturesDecoded, + gpuStats.numTextureInvalidations, + gpuStats.numVertexShaders, + gpuStats.numFragmentShaders, + gpuStats.numShaders + ); + + float zoom = 0.3f; /// g_Config.iWindowZoom; + float soff = 0.3f; + PPGeBegin(); + PPGeDrawText(stats, soff, soff, 0, zoom, 0xCC000000); + PPGeDrawText(stats, -soff, -soff, 0, zoom, 0xCC000000); + PPGeDrawText(stats, 0, 0, 0, zoom, 0xFFFFFFFF); + PPGeEnd(); + + gpuStats.resetFrame(); + kernelStats.ResetFrame(); +} + +// Let's collect all the throttling and frameskipping logic here. +void DoFrameTiming(bool &throttle) { +#ifdef _WIN32 + throttle = !GetAsyncKeyState(VK_TAB); +#else + throttle = false; +#endif + if (PSP_CoreParameter().headLess) + throttle = false; + + if (throttle) { + // Best place to throttle the frame rate on non vsynced platforms is probably here. Let's try it. + time_update(); + if (lastFrameTime == 0.0) + lastFrameTime = time_now_d(); + + // First, check if we are already behind. + // Wait until it's time. + while (time_now_d() < lastFrameTime + 1.0 / 60.0) { + Common::SleepCurrentThread(1); + time_update(); + } + // Advance lastFrameTime by a constant amount each frame, + // but don't let it get too far behind. + lastFrameTime = std::max(lastFrameTime + 1.0 / 60.0, time_now_d() - 1.5 / 60.0); + } +} + + void hleEnterVblank(u64 userdata, int cyclesLate) { int vbCount = userdata; @@ -223,7 +313,7 @@ void hleEnterVblank(u64 userdata, int cyclesLate) { // Trigger VBlank interrupt handlers. __TriggerInterrupt(PSP_INTR_IMMEDIATE | PSP_INTR_ONLY_IF_ENABLED | PSP_INTR_ALWAYS_RESCHED, PSP_VBLANK_INTR, PSP_INTR_SUB_ALL); - CoreTiming::ScheduleEvent(msToCycles(vblankMs) - cyclesLate, leaveVblankEvent, vbCount+1); + CoreTiming::ScheduleEvent(msToCycles(vblankMs) - cyclesLate, leaveVblankEvent, vbCount + 1); // TODO: Should this be done here or in hleLeaveVblank? if (framebufIsLatched) { @@ -233,65 +323,11 @@ void hleEnterVblank(u64 userdata, int cyclesLate) { gpu->SetDisplayFramebuffer(framebuf.topaddr, framebuf.pspFramebufLinesize, framebuf.pspFramebufFormat); } - // Draw screen overlays before blitting. Saves and restores the Ge context. gpuStats.numFrames++; // Now we can subvert the Ge engine in order to draw custom overlays like stat counters etc. if (g_Config.bShowDebugStats && gpuStats.numDrawCalls) { - gpu->UpdateStats(); - char stats[2048]; - - sprintf(stats, - "Frames: %i\n" - "DL processing time: %0.2f ms\n" - "Kernel processing time: %0.2f ms\n" - "Slowest syscall: %s : %0.2f ms\n" - "Most active syscall: %s : %0.2f ms\n" - "Draw calls: %i, flushes %i\n" - "Cached Draw calls: %i\n" - "Num Tracked Vertex Arrays: %i\n" - "Vertices Submitted: %i\n" - "Cached Vertices Drawn: %i\n" - "Uncached Vertices Drawn: %i\n" - "FBOs active: %i\n" - "Textures active: %i, decoded: %i\n" - "Texture invalidations: %i\n" - "Vertex shaders loaded: %i\n" - "Fragment shaders loaded: %i\n" - "Combined shaders loaded: %i\n", - gpuStats.numFrames, - gpuStats.msProcessingDisplayLists * 1000.0f, - kernelStats.msInSyscalls * 1000.0f, - kernelStats.slowestSyscallName ? kernelStats.slowestSyscallName : "(none)", - kernelStats.slowestSyscallTime * 1000.0f, - kernelStats.summedSlowestSyscallName ? kernelStats.summedSlowestSyscallName : "(none)", - kernelStats.summedSlowestSyscallTime * 1000.0f, - gpuStats.numDrawCalls, - gpuStats.numFlushes, - gpuStats.numCachedDrawCalls, - gpuStats.numTrackedVertexArrays, - gpuStats.numVertsSubmitted, - gpuStats.numCachedVertsDrawn, - gpuStats.numUncachedVertsDrawn, - gpuStats.numFBOs, - gpuStats.numTextures, - gpuStats.numTexturesDecoded, - gpuStats.numTextureInvalidations, - gpuStats.numVertexShaders, - gpuStats.numFragmentShaders, - gpuStats.numShaders - ); - - float zoom = 0.3f; /// g_Config.iWindowZoom; - float soff = 0.3f; - PPGeBegin(); - PPGeDrawText(stats, soff, soff, 0, zoom, 0xCC000000); - PPGeDrawText(stats, -soff, -soff, 0, zoom, 0xCC000000); - PPGeDrawText(stats, 0, 0, 0, zoom, 0xFFFFFFFF); - PPGeEnd(); - - gpuStats.resetFrame(); - kernelStats.ResetFrame(); + DebugStats(); } if (g_Config.bShowFPSCounter) { @@ -313,41 +349,32 @@ void hleEnterVblank(u64 userdata, int cyclesLate) { PPGeEnd(); } + // Draw screen overlays before blitting. Saves and restores the Ge context. // Yeah, this has to be the right moment to end the frame. Give the graphics backend opportunity // to blit the framebuffer, in order to support half-framerate games that otherwise wouldn't have // anything to draw here. gpu->CopyDisplayToOutput(); - host->EndFrame(); - -#ifdef _WIN32 - // Best place to throttle the frame rate on non vsynced platforms is probably here. Let's try it. - time_update(); - if (lastFrameTime == 0.0) - lastFrameTime = time_now_d(); - if (!GetAsyncKeyState(VK_TAB) && !PSP_CoreParameter().headLess) { - while (time_now_d() < lastFrameTime + 1.0 / 60.0) { - Common::SleepCurrentThread(1); - time_update(); - } - // Advance lastFrameTime by a constant amount each frame, - // but don't let it get too far behind. - lastFrameTime = std::max(lastFrameTime + 1.0 / 60.0, time_now_d() - 1.5 / 60.0); - } + bool throttle; + + DoFrameTiming(throttle); - // We are going to have to do something about audio timing for platforms that - // are vsynced to something that's not exactly 60fps.. + // Setting CORE_NEXTFRAME causes a swap. + coreState = CORE_NEXTFRAME; + + CoreTiming::ScheduleEvent(0 - cyclesLate, afterFlipEvent, 0); -#endif + // Returning here with coreState == CORE_NEXTFRAME causes a buffer flip to happen (next frame). + // Right after, we regain control for a little bit in hleAfterFlip. I think that's a great + // place to do housekeeping. +} +void hleAfterFlip(u64 userdata, int cyclesLate) +{ + // This checks input on PC. Fine to do even if not calling BeginFrame. host->BeginFrame(); - gpu->BeginFrame(); - // Tell the emu core that it's time to stop emulating - // Win32 doesn't need this. -#ifndef _WIN32 - coreState = CORE_NEXTFRAME; -#endif + gpu->BeginFrame(); // doesn't really matter if begin or end of frame. } void hleLeaveVblank(u64 userdata, int cyclesLate) { diff --git a/Core/Host.h b/Core/Host.h index c60d270a66bd..01573a1e655a 100644 --- a/Core/Host.h +++ b/Core/Host.h @@ -46,7 +46,6 @@ class Host virtual void InitGL() = 0; virtual void BeginFrame() {} - virtual void EndFrame() {} virtual void ShutdownGL() = 0; virtual void InitSound(PMixer *mixer) = 0; diff --git a/Windows/OpenGLBase.cpp b/Windows/OpenGLBase.cpp index f739dcab1e68..92d4af4c14ec 100644 --- a/Windows/OpenGLBase.cpp +++ b/Windows/OpenGLBase.cpp @@ -49,14 +49,7 @@ void GL_Resized() // Resize And Initialize The GL Window glstate.viewport.restore(); } - -void GL_BeginFrame() -{ - -} - - -void GL_EndFrame() +void GL_SwapBuffers() { SwapBuffers(hDC); } diff --git a/Windows/OpenGLBase.h b/Windows/OpenGLBase.h index 50a154089f67..b26ed0ebfda6 100644 --- a/Windows/OpenGLBase.h +++ b/Windows/OpenGLBase.h @@ -2,9 +2,10 @@ #pragma once +#define WIN32_LEAN_AND_MEAN +#include bool GL_Init(HWND window); void GL_Shutdown(); void GL_Resized(); -void GL_BeginFrame(); -void GL_EndFrame(); +void GL_SwapBuffers(); diff --git a/Windows/WindowsHost.cpp b/Windows/WindowsHost.cpp index f7afab484fe7..cef4e558e6d8 100644 --- a/Windows/WindowsHost.cpp +++ b/Windows/WindowsHost.cpp @@ -117,11 +117,6 @@ void WindowsHost::BeginFrame() for (auto iter = this->input.begin(); iter != this->input.end(); iter++) if ((*iter)->UpdateState() == 0) break; // *iter is std::shared_ptr, **iter is InputDevice - GL_BeginFrame(); -} -void WindowsHost::EndFrame() -{ - GL_EndFrame(); } void WindowsHost::BootDone() diff --git a/Windows/WindowsHost.h b/Windows/WindowsHost.h index c46c88854352..923acf10b8ca 100644 --- a/Windows/WindowsHost.h +++ b/Windows/WindowsHost.h @@ -21,7 +21,6 @@ class WindowsHost : public Host void InitGL(); void BeginFrame(); - void EndFrame(); void ShutdownGL(); void InitSound(PMixer *mixer); diff --git a/android/jni/NativeApp.cpp b/android/jni/NativeApp.cpp index 7bde05e9d266..786d51b9b785 100644 --- a/android/jni/NativeApp.cpp +++ b/android/jni/NativeApp.cpp @@ -101,7 +101,6 @@ class NativeHost : public Host virtual void InitGL() {} virtual void BeginFrame() {} - virtual void EndFrame() {} virtual void ShutdownGL() {} virtual void InitSound(PMixer *mixer); diff --git a/headless/Headless.cpp b/headless/Headless.cpp index 7129523f43fb..d9289c8cba70 100644 --- a/headless/Headless.cpp +++ b/headless/Headless.cpp @@ -4,12 +4,13 @@ #include -#include "../Core/Config.h" -#include "../Core/Core.h" -#include "../Core/CoreTiming.h" -#include "../Core/System.h" -#include "../Core/MIPS/MIPS.h" -#include "../Core/Host.h" +#include "Core/Config.h" +#include "Core/Core.h" +#include "Core/CoreTiming.h" +#include "Core/System.h" +#include "Core/MIPS/MIPS.h" +#include "Core/Host.h" +#include "Windows/OpenGLBase.h" #include "Log.h" #include "LogManager.h" @@ -46,6 +47,9 @@ class PrintfLogger : public LogListener } }; +// Temporary hack around annoying linking error. +void GL_SwapBuffers() { } + void printUsage(const char *progname, const char *reason) { if (reason != NULL) @@ -192,8 +196,10 @@ int main(int argc, const char* argv[]) mipsr4k.RunLoopUntil(nowTicks + frameTicks); // If we were rendering, this might be a nice time to do something about it. - if (coreState == CORE_NEXTFRAME) + if (coreState == CORE_NEXTFRAME) { + headlessHost->SwapBuffers(); coreState = CORE_RUNNING; + } } host->ShutdownGL(); diff --git a/headless/StubHost.h b/headless/StubHost.h index 1533c381c30f..19e71c3b68cb 100644 --- a/headless/StubHost.h +++ b/headless/StubHost.h @@ -35,7 +35,6 @@ class HeadlessHost : public Host virtual void InitGL() {} virtual void BeginFrame() {} - virtual void EndFrame() {} virtual void ShutdownGL() {} virtual void InitSound(PMixer *mixer) {} @@ -53,4 +52,9 @@ class HeadlessHost : public Host virtual void SetComparisonScreenshot(const std::string &filename) {} virtual bool isGLWorking() { return false; } + + + // Unique for HeadlessHost + virtual void SwapBuffers() {} + }; \ No newline at end of file diff --git a/headless/WindowsHeadlessHost.cpp b/headless/WindowsHeadlessHost.cpp index 5e7448ae2371..a6629982f2db 100644 --- a/headless/WindowsHeadlessHost.cpp +++ b/headless/WindowsHeadlessHost.cpp @@ -218,7 +218,7 @@ void WindowsHeadlessHost::BeginFrame() } -void WindowsHeadlessHost::EndFrame() +void WindowsHeadlessHost::SwapBuffers() { - SwapBuffers(hDC); + ::SwapBuffers(hDC); } diff --git a/headless/WindowsHeadlessHost.h b/headless/WindowsHeadlessHost.h index 5abc20ca0545..d624f4d3ccea 100644 --- a/headless/WindowsHeadlessHost.h +++ b/headless/WindowsHeadlessHost.h @@ -30,10 +30,11 @@ class WindowsHeadlessHost : public HeadlessHost public: virtual void InitGL(); virtual void BeginFrame(); - virtual void EndFrame(); virtual void ShutdownGL(); virtual bool isGLWorking() { return glOkay; } + virtual void SwapBuffers(); + virtual void SendDebugOutput(const std::string &output); virtual void SendDebugScreenshot(const u8 *pixbuf, u32 w, u32 h); virtual void SetComparisonScreenshot(const std::string &filename);