Restructure Windows "gameloop" to be more similar to others.
This commit is contained in:
parent
f8058e4bae
commit
cbb786c7f0
12 changed files with 150 additions and 114 deletions
|
@ -22,6 +22,9 @@
|
||||||
#include "Core.h"
|
#include "Core.h"
|
||||||
#include "MemMap.h"
|
#include "MemMap.h"
|
||||||
#include "MIPS/MIPS.h"
|
#include "MIPS/MIPS.h"
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include "Windows/OpenGLBase.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "Host.h"
|
#include "Host.h"
|
||||||
|
|
||||||
|
@ -64,7 +67,16 @@ bool Core_IsStepping()
|
||||||
|
|
||||||
void Core_RunLoop()
|
void Core_RunLoop()
|
||||||
{
|
{
|
||||||
|
while (!coreState) {
|
||||||
currentMIPS->RunLoopUntil(0xFFFFFFFFFFFFFFFULL);
|
currentMIPS->RunLoopUntil(0xFFFFFFFFFFFFFFFULL);
|
||||||
|
if (coreState == CORE_NEXTFRAME)
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
GL_SwapBuffers();
|
||||||
|
#endif
|
||||||
|
coreState = CORE_RUNNING;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core_DoSingleStep()
|
void Core_DoSingleStep()
|
||||||
|
|
|
@ -80,6 +80,7 @@ static bool framebufIsLatched;
|
||||||
|
|
||||||
static int enterVblankEvent = -1;
|
static int enterVblankEvent = -1;
|
||||||
static int leaveVblankEvent = -1;
|
static int leaveVblankEvent = -1;
|
||||||
|
static int afterFlipEvent = -1;
|
||||||
|
|
||||||
static int hCount;
|
static int hCount;
|
||||||
static int hCountTotal; //unused
|
static int hCountTotal; //unused
|
||||||
|
@ -107,6 +108,7 @@ enum {
|
||||||
|
|
||||||
void hleEnterVblank(u64 userdata, int cyclesLate);
|
void hleEnterVblank(u64 userdata, int cyclesLate);
|
||||||
void hleLeaveVblank(u64 userdata, int cyclesLate);
|
void hleLeaveVblank(u64 userdata, int cyclesLate);
|
||||||
|
void hleAfterFlip(u64 userdata, int cyclesLate);
|
||||||
|
|
||||||
void __DisplayInit() {
|
void __DisplayInit() {
|
||||||
gpuStats.reset();
|
gpuStats.reset();
|
||||||
|
@ -118,6 +120,7 @@ void __DisplayInit() {
|
||||||
|
|
||||||
enterVblankEvent = CoreTiming::RegisterEvent("EnterVBlank", &hleEnterVblank);
|
enterVblankEvent = CoreTiming::RegisterEvent("EnterVBlank", &hleEnterVblank);
|
||||||
leaveVblankEvent = CoreTiming::RegisterEvent("LeaveVBlank", &hleLeaveVblank);
|
leaveVblankEvent = CoreTiming::RegisterEvent("LeaveVBlank", &hleLeaveVblank);
|
||||||
|
afterFlipEvent = CoreTiming::RegisterEvent("AfterFlip", &hleAfterFlip);
|
||||||
|
|
||||||
CoreTiming::ScheduleEvent(msToCycles(frameMs - vblankMs), enterVblankEvent, 0);
|
CoreTiming::ScheduleEvent(msToCycles(frameMs - vblankMs), enterVblankEvent, 0);
|
||||||
isVblank = 0;
|
isVblank = 0;
|
||||||
|
@ -202,42 +205,8 @@ float calculateFPS()
|
||||||
return fps;
|
return fps;
|
||||||
}
|
}
|
||||||
|
|
||||||
void hleEnterVblank(u64 userdata, int cyclesLate) {
|
void DebugStats()
|
||||||
int vbCount = userdata;
|
{
|
||||||
|
|
||||||
DEBUG_LOG(HLE, "Enter VBlank %i", vbCount);
|
|
||||||
|
|
||||||
isVblank = 1;
|
|
||||||
|
|
||||||
// Fire the vblank listeners before we wake threads.
|
|
||||||
__DisplayFireVblank();
|
|
||||||
|
|
||||||
// Wake up threads waiting for VBlank
|
|
||||||
for (size_t i = 0; i < vblankWaitingThreads.size(); i++) {
|
|
||||||
if (--vblankWaitingThreads[i].vcountUnblock == 0) {
|
|
||||||
__KernelResumeThreadFromWait(vblankWaitingThreads[i].threadID, 0);
|
|
||||||
vblankWaitingThreads.erase(vblankWaitingThreads.begin() + i--);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
|
|
||||||
// TODO: Should this be done here or in hleLeaveVblank?
|
|
||||||
if (framebufIsLatched) {
|
|
||||||
DEBUG_LOG(HLE, "Setting latched framebuffer %08x (prev: %08x)", latchedFramebuf.topaddr, framebuf.topaddr);
|
|
||||||
framebuf = latchedFramebuf;
|
|
||||||
framebufIsLatched = false;
|
|
||||||
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();
|
gpu->UpdateStats();
|
||||||
char stats[2048];
|
char stats[2048];
|
||||||
|
|
||||||
|
@ -292,6 +261,73 @@ void hleEnterVblank(u64 userdata, int cyclesLate) {
|
||||||
|
|
||||||
gpuStats.resetFrame();
|
gpuStats.resetFrame();
|
||||||
kernelStats.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;
|
||||||
|
|
||||||
|
DEBUG_LOG(HLE, "Enter VBlank %i", vbCount);
|
||||||
|
|
||||||
|
isVblank = 1;
|
||||||
|
|
||||||
|
// Fire the vblank listeners before we wake threads.
|
||||||
|
__DisplayFireVblank();
|
||||||
|
|
||||||
|
// Wake up threads waiting for VBlank
|
||||||
|
for (size_t i = 0; i < vblankWaitingThreads.size(); i++) {
|
||||||
|
if (--vblankWaitingThreads[i].vcountUnblock == 0) {
|
||||||
|
__KernelResumeThreadFromWait(vblankWaitingThreads[i].threadID, 0);
|
||||||
|
vblankWaitingThreads.erase(vblankWaitingThreads.begin() + i--);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
// TODO: Should this be done here or in hleLeaveVblank?
|
||||||
|
if (framebufIsLatched) {
|
||||||
|
DEBUG_LOG(HLE, "Setting latched framebuffer %08x (prev: %08x)", latchedFramebuf.topaddr, framebuf.topaddr);
|
||||||
|
framebuf = latchedFramebuf;
|
||||||
|
framebufIsLatched = false;
|
||||||
|
gpu->SetDisplayFramebuffer(framebuf.topaddr, framebuf.pspFramebufLinesize, framebuf.pspFramebufFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
DebugStats();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (g_Config.bShowFPSCounter) {
|
if (g_Config.bShowFPSCounter) {
|
||||||
|
@ -313,41 +349,32 @@ void hleEnterVblank(u64 userdata, int cyclesLate) {
|
||||||
PPGeEnd();
|
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
|
// 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
|
// to blit the framebuffer, in order to support half-framerate games that otherwise wouldn't have
|
||||||
// anything to draw here.
|
// anything to draw here.
|
||||||
gpu->CopyDisplayToOutput();
|
gpu->CopyDisplayToOutput();
|
||||||
|
|
||||||
host->EndFrame();
|
bool throttle;
|
||||||
|
|
||||||
#ifdef _WIN32
|
DoFrameTiming(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();
|
|
||||||
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
|
// Setting CORE_NEXTFRAME causes a swap.
|
||||||
// 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
|
|
||||||
coreState = CORE_NEXTFRAME;
|
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) {
|
void hleLeaveVblank(u64 userdata, int cyclesLate) {
|
||||||
|
|
|
@ -46,7 +46,6 @@ public:
|
||||||
|
|
||||||
virtual void InitGL() = 0;
|
virtual void InitGL() = 0;
|
||||||
virtual void BeginFrame() {}
|
virtual void BeginFrame() {}
|
||||||
virtual void EndFrame() {}
|
|
||||||
virtual void ShutdownGL() = 0;
|
virtual void ShutdownGL() = 0;
|
||||||
|
|
||||||
virtual void InitSound(PMixer *mixer) = 0;
|
virtual void InitSound(PMixer *mixer) = 0;
|
||||||
|
|
|
@ -49,14 +49,7 @@ void GL_Resized() // Resize And Initialize The GL Window
|
||||||
glstate.viewport.restore();
|
glstate.viewport.restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GL_SwapBuffers()
|
||||||
void GL_BeginFrame()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void GL_EndFrame()
|
|
||||||
{
|
{
|
||||||
SwapBuffers(hDC);
|
SwapBuffers(hDC);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,10 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
bool GL_Init(HWND window);
|
bool GL_Init(HWND window);
|
||||||
void GL_Shutdown();
|
void GL_Shutdown();
|
||||||
void GL_Resized();
|
void GL_Resized();
|
||||||
void GL_BeginFrame();
|
void GL_SwapBuffers();
|
||||||
void GL_EndFrame();
|
|
||||||
|
|
|
@ -117,11 +117,6 @@ void WindowsHost::BeginFrame()
|
||||||
for (auto iter = this->input.begin(); iter != this->input.end(); iter++)
|
for (auto iter = this->input.begin(); iter != this->input.end(); iter++)
|
||||||
if ((*iter)->UpdateState() == 0)
|
if ((*iter)->UpdateState() == 0)
|
||||||
break; // *iter is std::shared_ptr, **iter is InputDevice
|
break; // *iter is std::shared_ptr, **iter is InputDevice
|
||||||
GL_BeginFrame();
|
|
||||||
}
|
|
||||||
void WindowsHost::EndFrame()
|
|
||||||
{
|
|
||||||
GL_EndFrame();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WindowsHost::BootDone()
|
void WindowsHost::BootDone()
|
||||||
|
|
|
@ -21,7 +21,6 @@ public:
|
||||||
|
|
||||||
void InitGL();
|
void InitGL();
|
||||||
void BeginFrame();
|
void BeginFrame();
|
||||||
void EndFrame();
|
|
||||||
void ShutdownGL();
|
void ShutdownGL();
|
||||||
|
|
||||||
void InitSound(PMixer *mixer);
|
void InitSound(PMixer *mixer);
|
||||||
|
|
|
@ -101,7 +101,6 @@ public:
|
||||||
|
|
||||||
virtual void InitGL() {}
|
virtual void InitGL() {}
|
||||||
virtual void BeginFrame() {}
|
virtual void BeginFrame() {}
|
||||||
virtual void EndFrame() {}
|
|
||||||
virtual void ShutdownGL() {}
|
virtual void ShutdownGL() {}
|
||||||
|
|
||||||
virtual void InitSound(PMixer *mixer);
|
virtual void InitSound(PMixer *mixer);
|
||||||
|
|
|
@ -4,12 +4,13 @@
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#include "../Core/Config.h"
|
#include "Core/Config.h"
|
||||||
#include "../Core/Core.h"
|
#include "Core/Core.h"
|
||||||
#include "../Core/CoreTiming.h"
|
#include "Core/CoreTiming.h"
|
||||||
#include "../Core/System.h"
|
#include "Core/System.h"
|
||||||
#include "../Core/MIPS/MIPS.h"
|
#include "Core/MIPS/MIPS.h"
|
||||||
#include "../Core/Host.h"
|
#include "Core/Host.h"
|
||||||
|
#include "Windows/OpenGLBase.h"
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
#include "LogManager.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)
|
void printUsage(const char *progname, const char *reason)
|
||||||
{
|
{
|
||||||
if (reason != NULL)
|
if (reason != NULL)
|
||||||
|
@ -192,9 +196,11 @@ int main(int argc, const char* argv[])
|
||||||
mipsr4k.RunLoopUntil(nowTicks + frameTicks);
|
mipsr4k.RunLoopUntil(nowTicks + frameTicks);
|
||||||
|
|
||||||
// If we were rendering, this might be a nice time to do something about it.
|
// 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;
|
coreState = CORE_RUNNING;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
host->ShutdownGL();
|
host->ShutdownGL();
|
||||||
PSP_Shutdown();
|
PSP_Shutdown();
|
||||||
|
|
|
@ -35,7 +35,6 @@ public:
|
||||||
|
|
||||||
virtual void InitGL() {}
|
virtual void InitGL() {}
|
||||||
virtual void BeginFrame() {}
|
virtual void BeginFrame() {}
|
||||||
virtual void EndFrame() {}
|
|
||||||
virtual void ShutdownGL() {}
|
virtual void ShutdownGL() {}
|
||||||
|
|
||||||
virtual void InitSound(PMixer *mixer) {}
|
virtual void InitSound(PMixer *mixer) {}
|
||||||
|
@ -53,4 +52,9 @@ public:
|
||||||
virtual void SetComparisonScreenshot(const std::string &filename) {}
|
virtual void SetComparisonScreenshot(const std::string &filename) {}
|
||||||
|
|
||||||
virtual bool isGLWorking() { return false; }
|
virtual bool isGLWorking() { return false; }
|
||||||
|
|
||||||
|
|
||||||
|
// Unique for HeadlessHost
|
||||||
|
virtual void SwapBuffers() {}
|
||||||
|
|
||||||
};
|
};
|
|
@ -218,7 +218,7 @@ void WindowsHeadlessHost::BeginFrame()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WindowsHeadlessHost::EndFrame()
|
void WindowsHeadlessHost::SwapBuffers()
|
||||||
{
|
{
|
||||||
SwapBuffers(hDC);
|
::SwapBuffers(hDC);
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,10 +30,11 @@ class WindowsHeadlessHost : public HeadlessHost
|
||||||
public:
|
public:
|
||||||
virtual void InitGL();
|
virtual void InitGL();
|
||||||
virtual void BeginFrame();
|
virtual void BeginFrame();
|
||||||
virtual void EndFrame();
|
|
||||||
virtual void ShutdownGL();
|
virtual void ShutdownGL();
|
||||||
virtual bool isGLWorking() { return glOkay; }
|
virtual bool isGLWorking() { return glOkay; }
|
||||||
|
|
||||||
|
virtual void SwapBuffers();
|
||||||
|
|
||||||
virtual void SendDebugOutput(const std::string &output);
|
virtual void SendDebugOutput(const std::string &output);
|
||||||
virtual void SendDebugScreenshot(const u8 *pixbuf, u32 w, u32 h);
|
virtual void SendDebugScreenshot(const u8 *pixbuf, u32 w, u32 h);
|
||||||
virtual void SetComparisonScreenshot(const std::string &filename);
|
virtual void SetComparisonScreenshot(const std::string &filename);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue