Restructure Windows "gameloop" to be more similar to others.

This commit is contained in:
Henrik Rydgard 2013-02-18 23:25:06 +01:00
parent f8058e4bae
commit cbb786c7f0
12 changed files with 150 additions and 114 deletions

View File

@ -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()

View File

@ -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();
bool throttle;
DoFrameTiming(throttle);
#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);
}
// We are going to have to do something about audio timing for platforms that
// are vsynced to something that's not exactly 60fps..
#endif
host->BeginFrame();
gpu->BeginFrame();
// Tell the emu core that it's time to stop emulating
// Win32 doesn't need this.
#ifndef _WIN32
// Setting CORE_NEXTFRAME causes a swap.
coreState = CORE_NEXTFRAME;
#endif
CoreTiming::ScheduleEvent(0 - cyclesLate, afterFlipEvent, 0);
// 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(); // doesn't really matter if begin or end of frame.
}
void hleLeaveVblank(u64 userdata, int cyclesLate) {

View File

@ -46,7 +46,6 @@ public:
virtual void InitGL() = 0;
virtual void BeginFrame() {}
virtual void EndFrame() {}
virtual void ShutdownGL() = 0;
virtual void InitSound(PMixer *mixer) = 0;

View File

@ -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);
}

View File

@ -2,9 +2,10 @@
#pragma once
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
bool GL_Init(HWND window);
void GL_Shutdown();
void GL_Resized();
void GL_BeginFrame();
void GL_EndFrame();
void GL_SwapBuffers();

View File

@ -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()

View File

@ -21,7 +21,6 @@ public:
void InitGL();
void BeginFrame();
void EndFrame();
void ShutdownGL();
void InitSound(PMixer *mixer);

View File

@ -101,7 +101,6 @@ public:
virtual void InitGL() {}
virtual void BeginFrame() {}
virtual void EndFrame() {}
virtual void ShutdownGL() {}
virtual void InitSound(PMixer *mixer);

View File

@ -4,12 +4,13 @@
#include <stdio.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 "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 @@ public:
}
};
// 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();

View File

@ -35,7 +35,6 @@ public:
virtual void InitGL() {}
virtual void BeginFrame() {}
virtual void EndFrame() {}
virtual void ShutdownGL() {}
virtual void InitSound(PMixer *mixer) {}
@ -53,4 +52,9 @@ public:
virtual void SetComparisonScreenshot(const std::string &filename) {}
virtual bool isGLWorking() { return false; }
// Unique for HeadlessHost
virtual void SwapBuffers() {}
};

View File

@ -218,7 +218,7 @@ void WindowsHeadlessHost::BeginFrame()
}
void WindowsHeadlessHost::EndFrame()
void WindowsHeadlessHost::SwapBuffers()
{
SwapBuffers(hDC);
::SwapBuffers(hDC);
}

View File

@ -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);