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 "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()
|
||||
{
|
||||
while (!coreState) {
|
||||
currentMIPS->RunLoopUntil(0xFFFFFFFFFFFFFFFULL);
|
||||
if (coreState == CORE_NEXTFRAME)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
GL_SwapBuffers();
|
||||
#endif
|
||||
coreState = CORE_RUNNING;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Core_DoSingleStep()
|
||||
|
|
|
@ -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,42 +205,8 @@ float calculateFPS()
|
|||
return fps;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// 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) {
|
||||
void DebugStats()
|
||||
{
|
||||
gpu->UpdateStats();
|
||||
char stats[2048];
|
||||
|
||||
|
@ -292,6 +261,73 @@ void hleEnterVblank(u64 userdata, int cyclesLate) {
|
|||
|
||||
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;
|
||||
|
||||
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) {
|
||||
|
@ -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;
|
||||
|
||||
#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);
|
||||
}
|
||||
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..
|
||||
|
||||
#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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -21,7 +21,6 @@ public:
|
|||
|
||||
void InitGL();
|
||||
void BeginFrame();
|
||||
void EndFrame();
|
||||
void ShutdownGL();
|
||||
|
||||
void InitSound(PMixer *mixer);
|
||||
|
|
|
@ -101,7 +101,6 @@ public:
|
|||
|
||||
virtual void InitGL() {}
|
||||
virtual void BeginFrame() {}
|
||||
virtual void EndFrame() {}
|
||||
virtual void ShutdownGL() {}
|
||||
|
||||
virtual void InitSound(PMixer *mixer);
|
||||
|
|
|
@ -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,9 +196,11 @@ 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();
|
||||
PSP_Shutdown();
|
||||
|
|
|
@ -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() {}
|
||||
|
||||
};
|
|
@ -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:
|
||||
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);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue