diff --git a/Core/CoreTiming.cpp b/Core/CoreTiming.cpp index 006d7137e3..079828db53 100644 --- a/Core/CoreTiming.cpp +++ b/Core/CoreTiming.cpp @@ -17,6 +17,7 @@ #include #include +#include #include #include #include @@ -29,7 +30,6 @@ #include "Core/Core.h" #include "Core/Config.h" #include "Core/HLE/sceKernelThread.h" -#include "Core/HLE/sceDisplay.h" #include "Core/MIPS/MIPS.h" #include "Core/Reporting.h" diff --git a/Core/Debugger/WebSocket/GPUStatsSubscriber.cpp b/Core/Debugger/WebSocket/GPUStatsSubscriber.cpp index 489ef4efc8..0d579b6747 100644 --- a/Core/Debugger/WebSocket/GPUStatsSubscriber.cpp +++ b/Core/Debugger/WebSocket/GPUStatsSubscriber.cpp @@ -18,7 +18,6 @@ #include #include #include "Core/Debugger/WebSocket/GPUStatsSubscriber.h" -#include "Core/HLE/sceDisplay.h" #include "Core/HW/Display.h" #include "Core/System.h" diff --git a/Core/Debugger/WebSocket/InputBroadcaster.cpp b/Core/Debugger/WebSocket/InputBroadcaster.cpp index 388000dbbc..16fdc1cbe6 100644 --- a/Core/Debugger/WebSocket/InputBroadcaster.cpp +++ b/Core/Debugger/WebSocket/InputBroadcaster.cpp @@ -20,7 +20,7 @@ #include "Core/Debugger/WebSocket/InputSubscriber.h" #include "Core/Debugger/WebSocket/WebSocketUtils.h" #include "Core/HLE/sceCtrl.h" -#include "Core/HLE/sceDisplay.h" +#include "Core/HW/Display.h" // Button press state change (input.buttons) // diff --git a/Core/Debugger/WebSocket/InputSubscriber.cpp b/Core/Debugger/WebSocket/InputSubscriber.cpp index facedff0fc..36278eea0e 100644 --- a/Core/Debugger/WebSocket/InputSubscriber.cpp +++ b/Core/Debugger/WebSocket/InputSubscriber.cpp @@ -21,7 +21,7 @@ #include "Core/Debugger/WebSocket/InputSubscriber.h" #include "Core/Debugger/WebSocket/WebSocketUtils.h" #include "Core/HLE/sceCtrl.h" -#include "Core/HLE/sceDisplay.h" +#include "Core/HW/Display.h" // This is also used in InputBroadcaster. const std::unordered_map buttonLookup = { diff --git a/Core/Dialog/PSPOskDialog.cpp b/Core/Dialog/PSPOskDialog.cpp index e8cbf0ed64..4188b50748 100755 --- a/Core/Dialog/PSPOskDialog.cpp +++ b/Core/Dialog/PSPOskDialog.cpp @@ -28,8 +28,8 @@ #include "Core/Dialog/PSPOskDialog.h" #include "Core/Util/PPGeDraw.h" #include "Core/HLE/sceCtrl.h" -#include "Core/HLE/sceDisplay.h" #include "Core/HLE/sceUtility.h" +#include "Core/HW/Display.h" #include "Core/Config.h" #include "Core/Reporting.h" #include "GPU/GPUState.h" diff --git a/Core/HLE/HLE.cpp b/Core/HLE/HLE.cpp index 7365620acb..896bc3cac8 100644 --- a/Core/HLE/HLE.cpp +++ b/Core/HLE/HLE.cpp @@ -35,7 +35,6 @@ #include "Core/MIPS/MIPS.h" #include "Core/MIPS/MIPSCodeUtils.h" #include "Core/HLE/HLETables.h" -#include "Core/HLE/sceDisplay.h" #include "Core/HLE/sceIo.h" #include "Core/HLE/sceAudio.h" #include "Core/HLE/sceKernelMemory.h" diff --git a/Core/HLE/sceDisplay.cpp b/Core/HLE/sceDisplay.cpp index 8528562204..e665d35445 100644 --- a/Core/HLE/sceDisplay.cpp +++ b/Core/HLE/sceDisplay.cpp @@ -93,11 +93,6 @@ static int lagSyncEvent = -1; static double lastLagSync = 0.0; static bool lagSyncScheduled = false; -// hCount is computed now. -static int vCount; -// The "AccumulatedHcount" can be adjusted, this is the base. -static u32 hCountBase; -static int isVblank; static int numSkippedFrames; static bool hasSetMode; static int resumeMode; @@ -116,12 +111,8 @@ static const double timePerVblank = 1.001f / 60.0f; static double curFrameTime; static double lastFrameTime; static double nextFrameTime; -static int numVBlanks; static int numVBlanksSinceFlip; -static u64 frameStartTicks; -const int hCountPerVblank = 286; - const int PSP_DISPLAY_MODE_LCD = 0; std::vector vblankWaitingThreads; @@ -143,24 +134,6 @@ enum { PSP_DISPLAY_SETBUF_NEXTFRAME = 1 }; -static int lastFpsFrame = 0; -static double lastFpsTime = 0.0; -static double fps = 0.0; -static double fpsHistory[120]; -static int fpsHistorySize = (int)ARRAY_SIZE(fpsHistory); -static int fpsHistoryPos = 0; -static int fpsHistoryValid = 0; -static double frameTimeHistory[600]; -static double frameSleepHistory[600]; -static const int frameTimeHistorySize = (int)ARRAY_SIZE(frameTimeHistory); -static int frameTimeHistoryPos = 0; -static int frameTimeHistoryValid = 0; -static double lastFrameTimeHistory = 0.0; -static int lastNumFlips = 0; -static float flips = 0.0f; -static int actualFlips = 0; // taking frameskip into account -static int lastActualFlips = 0; -static float actualFps = 0; // For the "max 60 fps" setting. static int lastFlipsTooFrequent = 0; static u64 lastFlipCycles = 0; @@ -173,9 +146,6 @@ void hleLagSync(u64 userdata, int cyclesLate); void __DisplayVblankBeginCallback(SceUID threadID, SceUID prevCallbackId); void __DisplayVblankEndCallback(SceUID threadID, SceUID prevCallbackId); -int __DisplayGetFlipCount() { return actualFlips; } -int __DisplayGetVCount() { return vCount; } -int __DisplayGetNumVblanks() { return numVBlanks; } void __DisplayFlip(int cyclesLate); @@ -202,7 +172,6 @@ void __DisplayInit() { width = 480; height = 272; numSkippedFrames = 0; - numVBlanks = 0; numVBlanksSinceFlip = 0; flippedThisFrame = false; framebufIsLatched = false; @@ -223,25 +192,10 @@ void __DisplayInit() { ScheduleLagSync(); CoreTiming::ScheduleEvent(msToCycles(frameMs - vblankMs), enterVblankEvent, 0); - isVblank = 0; - frameStartTicks = 0; - vCount = 0; - hCountBase = 0; curFrameTime = 0.0; nextFrameTime = 0.0; lastFrameTime = 0.0; - flips = 0; - fps = 0.0; - actualFlips = 0; - lastActualFlips = 0; - lastNumFlips = 0; - fpsHistoryValid = 0; - fpsHistoryPos = 0; - frameTimeHistoryValid = 0; - frameTimeHistoryPos = 0; - lastFrameTimeHistory = 0.0; - __KernelRegisterWaitTypeFuncs(WAITTYPE_VBLANK, __DisplayVblankBeginCallback, __DisplayVblankEndCallback); } @@ -259,16 +213,7 @@ void __DisplayDoState(PointerWrap &p) { Do(p, framebuf); Do(p, latchedFramebuf); Do(p, framebufIsLatched); - Do(p, frameStartTicks); - Do(p, vCount); - if (s <= 2) { - double oldHCountBase; - Do(p, oldHCountBase); - hCountBase = (int) oldHCountBase; - } else { - Do(p, hCountBase); - } - Do(p, isVblank); + DisplayHWDoState(p, s <= 2); Do(p, hasSetMode); Do(p, mode); Do(p, resumeMode); @@ -371,7 +316,7 @@ void __DisplayVblankBeginCallback(SceUID threadID, SceUID prevCallbackId) { return; } - vblankPausedWaits[pauseKey] = vCount + waitData.vcountUnblock; + vblankPausedWaits[pauseKey] = __DisplayGetVCount() + waitData.vcountUnblock; DEBUG_LOG(SCEDISPLAY, "sceDisplayWaitVblankCB: Suspending vblank wait for callback"); } @@ -386,116 +331,16 @@ void __DisplayVblankEndCallback(SceUID threadID, SceUID prevCallbackId) { int vcountUnblock = vblankPausedWaits[pauseKey]; vblankPausedWaits.erase(pauseKey); - if (vcountUnblock <= vCount) { + if (vcountUnblock <= __DisplayGetVCount()) { __KernelResumeThreadFromWait(threadID, 0); return; } // Still have to wait a bit longer. - vblankWaitingThreads.push_back(WaitVBlankInfo(__KernelGetCurThread(), vcountUnblock - vCount)); + vblankWaitingThreads.push_back(WaitVBlankInfo(__KernelGetCurThread(), vcountUnblock - __DisplayGetVCount())); DEBUG_LOG(SCEDISPLAY, "sceDisplayWaitVblankCB: Resuming vblank wait from callback"); } -// TODO: Also average actualFps -void __DisplayGetFPS(float *out_vps, float *out_fps, float *out_actual_fps) { - *out_vps = fps; - *out_fps = flips; - *out_actual_fps = actualFps; -} - -void __DisplayGetVPS(float *out_vps) { - *out_vps = fps; -} - -void __DisplayGetAveragedFPS(float *out_vps, float *out_fps) { - float avg = 0.0; - if (fpsHistoryValid > 0) { - for (int i = 0; i < fpsHistoryValid; ++i) { - avg += fpsHistory[i]; - } - avg /= (double) fpsHistoryValid; - } - - *out_vps = *out_fps = avg; -} - -static bool IsRunningSlow() { - // Allow for some startup turbulence for 8 seconds before assuming things are bad. - if (fpsHistoryValid >= 8) { - // Look at only the last 15 samples (starting at the 14th sample behind current.) - int rangeStart = fpsHistoryPos - std::min(fpsHistoryValid, 14); - - double best = 0.0; - for (int i = rangeStart; i <= fpsHistoryPos; ++i) { - // rangeStart may have been negative if near a wrap around. - int index = (fpsHistorySize + i) % fpsHistorySize; - best = std::max(fpsHistory[index], best); - } - - return best < System_GetPropertyFloat(SYSPROP_DISPLAY_REFRESH_RATE) * 0.97; - } - - return false; -} - -static void CalculateFPS() { - double now = time_now_d(); - - if (now >= lastFpsTime + 1.0) { - double frames = (numVBlanks - lastFpsFrame); - actualFps = (actualFlips - lastActualFlips); - - fps = frames / (now - lastFpsTime); - flips = 60.0 * (double) (gpuStats.numFlips - lastNumFlips) / frames; - - lastFpsFrame = numVBlanks; - lastNumFlips = gpuStats.numFlips; - lastActualFlips = actualFlips; - lastFpsTime = now; - - fpsHistory[fpsHistoryPos++] = fps; - fpsHistoryPos = fpsHistoryPos % fpsHistorySize; - if (fpsHistoryValid < fpsHistorySize) { - ++fpsHistoryValid; - } - } - - if (g_Config.bDrawFrameGraph || coreCollectDebugStats) { - frameTimeHistory[frameTimeHistoryPos++] = now - lastFrameTimeHistory; - lastFrameTimeHistory = now; - frameTimeHistoryPos = frameTimeHistoryPos % frameTimeHistorySize; - if (frameTimeHistoryValid < frameTimeHistorySize) { - ++frameTimeHistoryValid; - } - frameSleepHistory[frameTimeHistoryPos] = 0.0; - } -} - -double *__DisplayGetFrameTimes(int *out_valid, int *out_pos, double **out_sleep) { - *out_valid = frameTimeHistoryValid; - *out_pos = frameTimeHistoryPos; - *out_sleep = frameSleepHistory; - return frameTimeHistory; -} - -void __DisplayGetDebugStats(char *stats, size_t bufsize) { - char statbuf[4096]; - gpu->GetStats(statbuf, sizeof(statbuf)); - - snprintf(stats, bufsize, - "Kernel processing time: %0.2f ms\n" - "Slowest syscall: %s : %0.2f ms\n" - "Most active syscall: %s : %0.2f ms\n%s", - kernelStats.msInSyscalls * 1000.0f, - kernelStats.slowestSyscallName ? kernelStats.slowestSyscallName : "(none)", - kernelStats.slowestSyscallTime * 1000.0f, - kernelStats.summedSlowestSyscallName ? kernelStats.summedSlowestSyscallName : "(none)", - kernelStats.summedSlowestSyscallTime * 1000.0f, - statbuf); -} - - - void __DisplaySetWasPaused() { wasPaused = true; } @@ -524,18 +369,6 @@ static void DoFrameDropLogging(float scaledTimestep) { } } -static int CalculateFrameSkip() { - int frameSkipNum; - if (g_Config.iFrameSkipType == 1) { - // Calculate the frames to skip dynamically using the set percentage of the current fps - frameSkipNum = ceil( flips * (static_cast(g_Config.iFrameSkip) / 100.00) ); - } else { - // Use the set number of frames to skip - frameSkipNum = g_Config.iFrameSkip; - } - return frameSkipNum; -} - // Let's collect all the throttling and frameskipping logic here. static void DoFrameTiming(bool &throttle, bool &skipFrame, float timestep) { PROFILE_THIS_SCOPE("timing"); @@ -570,7 +403,7 @@ static void DoFrameTiming(bool &throttle, bool &skipFrame, float timestep) { } // Auto-frameskip automatically if speed limit is set differently than the default. - int frameSkipNum = CalculateFrameSkip(); + int frameSkipNum = DisplayCalculateFrameSkip(); if (g_Config.bAutoFrameSkip) { // autoframeskip // Argh, we are falling behind! Let's skip a frame and see if we catch up. @@ -646,7 +479,7 @@ static void DoFrameIdleTiming() { } if (g_Config.bDrawFrameGraph || coreCollectDebugStats) { - frameSleepHistory[frameTimeHistoryPos] += time_now_d() - before; + DisplayNotifySleep(time_now_d() - before); } } } @@ -657,13 +490,7 @@ void hleEnterVblank(u64 userdata, int cyclesLate) { VERBOSE_LOG(SCEDISPLAY, "Enter VBlank %i", vbCount); - isVblank = 1; - vCount++; // vCount increases at each VBLANK. - hCountBase += hCountPerVblank; // This is the "accumulated" hcount base. - if (hCountBase > 0x7FFFFFFF) { - hCountBase -= 0x80000000; - } - frameStartTicks = CoreTiming::GetTicks(); + DisplayFireVblankStart(); CoreTiming::ScheduleEvent(msToCycles(vblankMs) - cyclesLate, leaveVblankEvent, vbCount + 1); @@ -688,7 +515,6 @@ void hleEnterVblank(u64 userdata, int cyclesLate) { __KernelReSchedule("entered vblank"); } - numVBlanks++; numVBlanksSinceFlip++; // TODO: Should this be done here or in hleLeaveVblank? @@ -729,8 +555,8 @@ void __DisplayFlip(int cyclesLate) { const bool fbDirty = gpu->FramebufferDirty(); if (fbDirty || noRecentFlip || postEffectRequiresFlip) { - int frameSleepPos = frameTimeHistoryPos; - CalculateFPS(); + int frameSleepPos = DisplayGetSleepPos(); + double frameSleepStart = time_now_d(); DisplayFireFlip(); // Let the user know if we're running slow, so they know to adjust settings. @@ -739,7 +565,7 @@ void __DisplayFlip(int cyclesLate) { if (!g_Config.bHideSlowWarnings && !hasNotifiedSlow && PSP_CoreParameter().fpsLimit == FPSLimit::NORMAL && - IsRunningSlow()) { + DisplayIsRunningSlow()) { #ifndef _DEBUG auto err = GetI18NCategory("Error"); if (g_Config.bSoftwareRendering) { @@ -774,7 +600,7 @@ void __DisplayFlip(int cyclesLate) { if (!forceNoFlip && Core_NextFrame()) { gpu->CopyDisplayToOutput(fbReallyDirty); if (fbReallyDirty) { - actualFlips++; + DisplayFireActualFlip(); } } } @@ -787,7 +613,7 @@ void __DisplayFlip(int cyclesLate) { DoFrameTiming(throttle, skipFrame, (float)numVBlanksSinceFlip * timePerVblank); int maxFrameskip = 8; - int frameSkipNum = CalculateFrameSkip(); + int frameSkipNum = DisplayCalculateFrameSkip(); if (throttle) { // 4 here means 1 drawn, 4 skipped - so 12 fps minimum. maxFrameskip = frameSkipNum; @@ -813,7 +639,7 @@ void __DisplayFlip(int cyclesLate) { if (g_Config.bDrawFrameGraph || coreCollectDebugStats) { // Track how long we sleep (whether vsync or sleep_ms.) - frameSleepHistory[frameSleepPos] += time_now_d() - lastFrameTimeHistory; + DisplayNotifySleep(time_now_d() - frameSleepStart, frameSleepPos); } } else { // Okay, there's no new frame to draw. But audio may be playing, so we need to time still. @@ -832,13 +658,12 @@ void hleAfterFlip(u64 userdata, int cyclesLate) { } void hleLeaveVblank(u64 userdata, int cyclesLate) { - isVblank = 0; flippedThisFrame = false; VERBOSE_LOG(SCEDISPLAY,"Leave VBlank %i", (int)userdata - 1); CoreTiming::ScheduleEvent(msToCycles(frameMs - vblankMs) - cyclesLate, enterVblankEvent, userdata); // Fire the vblank listeners after the vblank completes. - DisplayFireVblank(); + DisplayFireVblankEnd(); } void hleLagSync(u64 userdata, int cyclesLate) { @@ -877,16 +702,16 @@ void hleLagSync(u64 userdata, int cyclesLate) { ScheduleLagSync(over - emuOver); if (g_Config.bDrawFrameGraph || coreCollectDebugStats) { - frameSleepHistory[frameTimeHistoryPos] += now - before; + DisplayNotifySleep(now - before); } } static u32 sceDisplayIsVblank() { - return hleLogSuccessI(SCEDISPLAY, isVblank); + return hleLogSuccessI(SCEDISPLAY, DisplayIsVblank()); } static int DisplayWaitForVblanks(const char *reason, int vblanks, bool callbacks = false) { - const s64 ticksIntoFrame = CoreTiming::GetTicks() - frameStartTicks; + const s64 ticksIntoFrame = CoreTiming::GetTicks() - DisplayFrameStartTicks(); const s64 cyclesToNextVblank = msToCycles(frameMs) - ticksIntoFrame; // These syscalls take about 115 us, so if the next vblank is before then, we're waiting extra. @@ -1066,7 +891,7 @@ static u32 sceDisplayWaitVblankStart() { } static u32 sceDisplayWaitVblank() { - if (!isVblank) { + if (!DisplayIsVblank()) { return DisplayWaitForVblanks("vblank waited", 1); } else { hleEatCycles(1110); @@ -1088,7 +913,7 @@ static u32 sceDisplayWaitVblankStartMulti(int vblanks) { } static u32 sceDisplayWaitVblankCB() { - if (!isVblank) { + if (!DisplayIsVblank()) { return DisplayWaitForVblanksCB("vblank waited", 1); } else { hleEatCycles(1110); @@ -1116,20 +941,7 @@ static u32 sceDisplayWaitVblankStartMultiCB(int vblanks) { static u32 sceDisplayGetVcount() { hleEatCycles(150); hleReSchedule("get vcount"); - return hleLogSuccessVerboseI(SCEDISPLAY, vCount); -} - -static u32 __DisplayGetCurrentHcount() { - const int ticksIntoFrame = CoreTiming::GetTicks() - frameStartTicks; - const int ticksPerVblank = CoreTiming::GetClockFrequencyHz() / 60 / hCountPerVblank; - // Can't seem to produce a 0 on real hardware, offsetting by 1 makes things look right. - return 1 + (ticksIntoFrame / ticksPerVblank); -} - -static u32 __DisplayGetAccumulatedHcount() { - // The hCount is always a positive int, and wraps from 0x7FFFFFFF -> 0. - int value = hCountBase + __DisplayGetCurrentHcount(); - return value & 0x7FFFFFFF; + return hleLogSuccessVerboseI(SCEDISPLAY, __DisplayGetVCount()); } static u32 sceDisplayGetCurrentHcount() { @@ -1145,7 +957,7 @@ static int sceDisplayAdjustAccumulatedHcount(int value) { // Since it includes the current hCount, find the difference to apply to the base. u32 accumHCount = __DisplayGetAccumulatedHcount(); int diff = value - accumHCount; - hCountBase += diff; + DisplayAdjustAccumulatedHcount(diff); return hleLogSuccessI(SCEDISPLAY, 0); } @@ -1179,8 +991,8 @@ static u32 sceDisplayGetMode(u32 modeAddr, u32 widthAddr, u32 heightAddr) { static u32 sceDisplayIsVsync() { u64 now = CoreTiming::GetTicks(); - u64 start = frameStartTicks + msToCycles(vsyncStartMs); - u64 end = frameStartTicks + msToCycles(vsyncEndMs); + u64 start = DisplayFrameStartTicks() + msToCycles(vsyncStartMs); + u64 end = DisplayFrameStartTicks() + msToCycles(vsyncEndMs); return hleLogSuccessI(SCEDISPLAY, now >= start && now <= end ? 1 : 0); } diff --git a/Core/HLE/sceDisplay.h b/Core/HLE/sceDisplay.h index b4d79e425a..f1911a49ec 100644 --- a/Core/HLE/sceDisplay.h +++ b/Core/HLE/sceDisplay.h @@ -29,15 +29,6 @@ void Register_sceDisplay(); bool __DisplayGetFramebuf(PSPPointer *topaddr, u32 *linesize, u32 *pixelFormat, int mode); void __DisplaySetFramebuf(u32 topaddr, int linesize, int pixelformat, int sync); -void __DisplayGetDebugStats(char stats[], size_t bufsize); -void __DisplayGetFPS(float *out_vps, float *out_fps, float *out_actual_fps); -void __DisplayGetVPS(float *out_vps); -void __DisplayGetAveragedFPS(float *out_vps, float *out_fps); -double *__DisplayGetFrameTimes(int *out_valid, int *out_pos, double **out_sleep); -int __DisplayGetNumVblanks(); -int __DisplayGetVCount(); -int __DisplayGetFlipCount(); - // Call this when resuming to avoid a small speedup burst void __DisplaySetWasPaused(); diff --git a/Core/HW/Display.cpp b/Core/HW/Display.cpp index 2c1136e6d9..1335593cd4 100644 --- a/Core/HW/Display.cpp +++ b/Core/HW/Display.cpp @@ -16,9 +16,20 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include +#include #include #include +#include "Common/Common.h" +#include "Common/Serialize/SerializeFuncs.h" +#include "Common/System/System.h" +#include "Common/TimeUtil.h" +#include "Core/Config.h" +#include "Core/Core.h" +#include "Core/CoreTiming.h" +#include "Core/HLE/sceKernel.h" #include "Core/HW/Display.h" +#include "GPU/GPU.h" +#include "GPU/GPUInterface.h" // Called when vblank happens (like an internal interrupt.) Not part of state, should be static. static std::mutex listenersLock; @@ -26,12 +37,204 @@ static std::vector vblankListeners; typedef std::pair FlipListener; static std::vector flipListeners; -void DisplayFireVblank() { +static uint64_t frameStartTicks; +static int numVBlanks; +// hCount is computed now. +static int vCount; +// The "AccumulatedHcount" can be adjusted, this is the base. +static uint32_t hCountBase; +static int isVblank; +static constexpr int hCountPerVblank = 286; + +// FPS stats for frameskip, FPS display, etc. +static int lastFpsFrame = 0; +static double lastFpsTime = 0.0; +static double fps = 0.0; +static int lastNumFlips = 0; +static float flips = 0.0f; +static int actualFlips = 0; // taking frameskip into account +static int lastActualFlips = 0; +static float actualFps = 0; + +// FPS stats for averaging. +static double fpsHistory[120]; +static constexpr int fpsHistorySize = (int)ARRAY_SIZE(fpsHistory); +static int fpsHistoryPos = 0; +static int fpsHistoryValid = 0; + +// Frame time stats. +static double frameTimeHistory[600]; +static double frameSleepHistory[600]; +static constexpr int frameTimeHistorySize = (int)ARRAY_SIZE(frameTimeHistory); +static int frameTimeHistoryPos = 0; +static int frameTimeHistoryValid = 0; +static double lastFrameTimeHistory = 0.0; + +static void CalculateFPS() { + double now = time_now_d(); + + if (now >= lastFpsTime + 1.0) { + double frames = (numVBlanks - lastFpsFrame); + actualFps = (actualFlips - lastActualFlips); + + fps = frames / (now - lastFpsTime); + flips = 60.0 * (double)(gpuStats.numFlips - lastNumFlips) / frames; + + lastFpsFrame = numVBlanks; + lastNumFlips = gpuStats.numFlips; + lastActualFlips = actualFlips; + lastFpsTime = now; + + fpsHistory[fpsHistoryPos++] = fps; + fpsHistoryPos = fpsHistoryPos % fpsHistorySize; + if (fpsHistoryValid < fpsHistorySize) { + ++fpsHistoryValid; + } + } + + if (g_Config.bDrawFrameGraph || coreCollectDebugStats) { + frameTimeHistory[frameTimeHistoryPos++] = now - lastFrameTimeHistory; + lastFrameTimeHistory = now; + frameTimeHistoryPos = frameTimeHistoryPos % frameTimeHistorySize; + if (frameTimeHistoryValid < frameTimeHistorySize) { + ++frameTimeHistoryValid; + } + frameSleepHistory[frameTimeHistoryPos] = 0.0; + } +} + +// TODO: Also average actualFps +void __DisplayGetFPS(float *out_vps, float *out_fps, float *out_actual_fps) { + *out_vps = fps; + *out_fps = flips; + *out_actual_fps = actualFps; +} + +void __DisplayGetVPS(float *out_vps) { + *out_vps = fps; +} + +void __DisplayGetAveragedFPS(float *out_vps, float *out_fps) { + float avg = 0.0; + if (fpsHistoryValid > 0) { + for (int i = 0; i < fpsHistoryValid; ++i) { + avg += fpsHistory[i]; + } + avg /= (double)fpsHistoryValid; + } + + *out_vps = *out_fps = avg; +} + +int __DisplayGetFlipCount() { + return actualFlips; +} + +int __DisplayGetNumVblanks() { + return numVBlanks; +} + +int __DisplayGetVCount() { + return vCount; +} + +bool DisplayIsVblank() { + return isVblank != 0; +} + +uint64_t DisplayFrameStartTicks() { + return frameStartTicks; +} + +uint32_t __DisplayGetCurrentHcount() { + const int ticksIntoFrame = CoreTiming::GetTicks() - frameStartTicks; + const int ticksPerVblank = CoreTiming::GetClockFrequencyHz() / 60 / hCountPerVblank; + // Can't seem to produce a 0 on real hardware, offsetting by 1 makes things look right. + return 1 + (ticksIntoFrame / ticksPerVblank); +} + +uint32_t __DisplayGetAccumulatedHcount() { + // The hCount is always a positive int, and wraps from 0x7FFFFFFF -> 0. + int value = hCountBase + __DisplayGetCurrentHcount(); + return value & 0x7FFFFFFF; +} + +void DisplayAdjustAccumulatedHcount(uint32_t diff) { + hCountBase += diff; +} + +double *__DisplayGetFrameTimes(int *out_valid, int *out_pos, double **out_sleep) { + *out_valid = frameTimeHistoryValid; + *out_pos = frameTimeHistoryPos; + *out_sleep = frameSleepHistory; + return frameTimeHistory; +} + +int DisplayGetSleepPos() { + return frameTimeHistoryPos; +} + +void DisplayNotifySleep(double t, int pos) { + if (pos < 0) + pos = frameTimeHistoryPos; + frameSleepHistory[pos] += t; +} + +void __DisplayGetDebugStats(char *stats, size_t bufsize) { + char statbuf[4096]; + gpu->GetStats(statbuf, sizeof(statbuf)); + + snprintf(stats, bufsize, + "Kernel processing time: %0.2f ms\n" + "Slowest syscall: %s : %0.2f ms\n" + "Most active syscall: %s : %0.2f ms\n%s", + kernelStats.msInSyscalls * 1000.0f, + kernelStats.slowestSyscallName ? kernelStats.slowestSyscallName : "(none)", + kernelStats.slowestSyscallTime * 1000.0f, + kernelStats.summedSlowestSyscallName ? kernelStats.summedSlowestSyscallName : "(none)", + kernelStats.summedSlowestSyscallTime * 1000.0f, + statbuf); +} + +bool DisplayIsRunningSlow() { + // Allow for some startup turbulence for 8 seconds before assuming things are bad. + if (fpsHistoryValid >= 8) { + // Look at only the last 15 samples (starting at the 14th sample behind current.) + int rangeStart = fpsHistoryPos - std::min(fpsHistoryValid, 14); + + double best = 0.0; + for (int i = rangeStart; i <= fpsHistoryPos; ++i) { + // rangeStart may have been negative if near a wrap around. + int index = (fpsHistorySize + i) % fpsHistorySize; + best = std::max(fpsHistory[index], best); + } + + return best < System_GetPropertyFloat(SYSPROP_DISPLAY_REFRESH_RATE) * 0.97; + } + + return false; +} + +void DisplayFireVblankStart() { + frameStartTicks = CoreTiming::GetTicks(); + numVBlanks++; + + isVblank = 1; + vCount++; // vCount increases at each VBLANK. + hCountBase += hCountPerVblank; // This is the "accumulated" hcount base. + if (hCountBase > 0x7FFFFFFF) { + hCountBase -= 0x80000000; + } +} + +void DisplayFireVblankEnd() { std::vector toCall = [] { std::lock_guard guard(listenersLock); return vblankListeners; }(); + isVblank = 0; + for (VblankCallback cb : toCall) { cb(); } @@ -43,11 +246,18 @@ void DisplayFireFlip() { return flipListeners; }(); + // This is also the right time to calculate FPS. + CalculateFPS(); + for (FlipListener cb : toCall) { cb.first(cb.second); } } +void DisplayFireActualFlip() { + actualFlips++; +} + void __DisplayListenVblank(VblankCallback callback) { std::lock_guard guard(listenersLock); vblankListeners.push_back(callback); @@ -65,8 +275,37 @@ void __DisplayForgetFlip(FlipCallback callback, void *userdata) { }), flipListeners.end()); } -void DisplayHWInit() { +int DisplayCalculateFrameSkip() { + int frameSkipNum; + if (g_Config.iFrameSkipType == 1) { + // Calculate the frames to skip dynamically using the set percentage of the current fps + frameSkipNum = ceil(flips * (static_cast(g_Config.iFrameSkip) / 100.00)); + } else { + // Use the set number of frames to skip + frameSkipNum = g_Config.iFrameSkip; + } + return frameSkipNum; +} +void DisplayHWInit() { + frameStartTicks = 0; + numVBlanks = 0; + isVblank = 0; + vCount = 0; + hCountBase = 0; + + flips = 0; + fps = 0.0; + actualFlips = 0; + lastActualFlips = 0; + lastNumFlips = 0; + + fpsHistoryValid = 0; + fpsHistoryPos = 0; + + frameTimeHistoryValid = 0; + frameTimeHistoryPos = 0; + lastFrameTimeHistory = 0.0; } void DisplayHWShutdown() { @@ -74,3 +313,16 @@ void DisplayHWShutdown() { vblankListeners.clear(); flipListeners.clear(); } + +void DisplayHWDoState(PointerWrap &p, int hleCompatV2) { + Do(p, frameStartTicks); + Do(p, vCount); + if (hleCompatV2) { + double oldHCountBase; + Do(p, oldHCountBase); + hCountBase = (int)oldHCountBase; + } else { + Do(p, hCountBase); + } + Do(p, isVblank); +} diff --git a/Core/HW/Display.h b/Core/HW/Display.h index e8e9161d82..241b43ec0d 100644 --- a/Core/HW/Display.h +++ b/Core/HW/Display.h @@ -17,6 +17,10 @@ #pragma once +#include + +class PointerWrap; + typedef void (*VblankCallback)(); // Listen for vblank events. void __DisplayListenVblank(VblankCallback callback); @@ -24,8 +28,31 @@ typedef void (*FlipCallback)(void *userdata); void __DisplayListenFlip(FlipCallback callback, void *userdata); void __DisplayForgetFlip(FlipCallback callback, void *userdata); -void DisplayFireVblank(); +int __DisplayGetFlipCount(); +int __DisplayGetNumVblanks(); +int __DisplayGetVCount(); +bool DisplayIsVblank(); +uint64_t DisplayFrameStartTicks(); +uint32_t __DisplayGetCurrentHcount(); +uint32_t __DisplayGetAccumulatedHcount(); +void DisplayAdjustAccumulatedHcount(uint32_t diff); + +void __DisplayGetDebugStats(char stats[], size_t bufsize); +void __DisplayGetAveragedFPS(float *out_vps, float *out_fps); +void __DisplayGetFPS(float *out_vps, float *out_fps, float *out_actual_fps); +void __DisplayGetVPS(float *out_vps); +double *__DisplayGetFrameTimes(int *out_valid, int *out_pos, double **out_sleep); +int DisplayGetSleepPos(); +void DisplayNotifySleep(double t, int pos = -1); +bool DisplayIsRunningSlow(); + +void DisplayFireVblankStart(); +void DisplayFireVblankEnd(); void DisplayFireFlip(); +void DisplayFireActualFlip(); + +int DisplayCalculateFrameSkip(); void DisplayHWInit(); void DisplayHWShutdown(); +void DisplayHWDoState(PointerWrap &p, int hleCompatV2); diff --git a/Core/MIPS/MIPS.cpp b/Core/MIPS/MIPS.cpp index 3bcf4d2a88..41cf598a94 100644 --- a/Core/MIPS/MIPS.cpp +++ b/Core/MIPS/MIPS.cpp @@ -33,7 +33,6 @@ #include "Core/MIPS/IR/IRJit.h" #include "Core/Reporting.h" #include "Core/System.h" -#include "Core/HLE/sceDisplay.h" #include "Core/MIPS/JitCommon/JitCommon.h" #include "Core/CoreTiming.h" diff --git a/Core/Reporting.cpp b/Core/Reporting.cpp index fc02b27f31..3ff8fecc44 100644 --- a/Core/Reporting.cpp +++ b/Core/Reporting.cpp @@ -40,8 +40,8 @@ #include "Core/FileSystems/BlockDevices.h" #include "Core/FileSystems/MetaFileSystem.h" #include "Core/HLE/Plugins.h" -#include "Core/HLE/sceDisplay.h" #include "Core/HLE/sceKernelMemory.h" +#include "Core/HW/Display.h" #include "Core/ELF/ParamSFO.h" #include "GPU/GPUInterface.h" #include "GPU/GPUState.h" diff --git a/Core/SaveState.cpp b/Core/SaveState.cpp index a2e98429bc..bf9f3070ca 100644 --- a/Core/SaveState.cpp +++ b/Core/SaveState.cpp @@ -40,8 +40,8 @@ #include "Core/FileSystems/MetaFileSystem.h" #include "Core/ELF/ParamSFO.h" #include "Core/HLE/HLE.h" -#include "Core/HLE/sceDisplay.h" #include "Core/HLE/ReplaceTables.h" +#include "Core/HLE/sceDisplay.h" #include "Core/HLE/sceKernel.h" #include "Core/HLE/sceUtility.h" #include "Core/MemMap.h" diff --git a/GPU/Common/PresentationCommon.cpp b/GPU/Common/PresentationCommon.cpp index abc19f2851..b08469837b 100644 --- a/GPU/Common/PresentationCommon.cpp +++ b/GPU/Common/PresentationCommon.cpp @@ -30,7 +30,7 @@ #include "Core/ConfigValues.h" #include "Core/Host.h" #include "Core/System.h" -#include "Core/HLE/sceDisplay.h" +#include "Core/HW/Display.h" #include "GPU/Common/PostShader.h" #include "GPU/Common/PresentationCommon.h" #include "Common/GPU/ShaderTranslation.h" diff --git a/GPU/Common/PresentationCommon.h b/GPU/Common/PresentationCommon.h index 63d8e4a2dc..7ce81117b8 100644 --- a/GPU/Common/PresentationCommon.h +++ b/GPU/Common/PresentationCommon.h @@ -17,6 +17,7 @@ #pragma once +#include "Common/Common.h" #include "Common/GPU/Shader.h" struct CardboardSettings { diff --git a/GPU/Vulkan/FramebufferManagerVulkan.cpp b/GPU/Vulkan/FramebufferManagerVulkan.cpp index 314eb3e0b8..707c20da6e 100644 --- a/GPU/Vulkan/FramebufferManagerVulkan.cpp +++ b/GPU/Vulkan/FramebufferManagerVulkan.cpp @@ -34,7 +34,6 @@ #include "Core/ConfigValues.h" #include "Core/System.h" #include "Core/Reporting.h" -#include "Core/HLE/sceDisplay.h" #include "GPU/ge_constants.h" #include "GPU/GPUInterface.h" #include "GPU/GPUState.h" diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index 09ef3f434f..b776fbe8d5 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -63,7 +63,6 @@ using namespace std::placeholders; #include "GPU/Vulkan/DebugVisVulkan.h" #endif #include "Core/HLE/sceCtrl.h" -#include "Core/HLE/sceDisplay.h" #include "Core/HLE/sceSas.h" #include "Core/Debugger/SymbolMap.h" #include "Core/SaveState.h" diff --git a/UI/MainScreen.cpp b/UI/MainScreen.cpp index 4e86ac1abb..5198a2b23d 100644 --- a/UI/MainScreen.cpp +++ b/UI/MainScreen.cpp @@ -62,7 +62,6 @@ #include "GPU/GPUInterface.h" #include "Common/Data/Text/I18n.h" -#include "Core/HLE/sceDisplay.h" #include "Core/HLE/sceUmd.h" bool MainScreen::showHomebrewTab = false;