diff --git a/Common/GPU/MiscTypes.h b/Common/GPU/MiscTypes.h index 834db32f19..877d046dcd 100644 --- a/Common/GPU/MiscTypes.h +++ b/Common/GPU/MiscTypes.h @@ -1,6 +1,7 @@ #pragma once #include "Common/Common.h" +#include "Common/Data/Collections/FastVec.h" // Flags and structs shared between backends that haven't found a good home. @@ -34,4 +35,6 @@ struct FrameTimeData { double earliestPresentTime; double presentMargin; }; + constexpr size_t FRAME_TIME_HISTORY_LENGTH = 32; +typedef HistoryBuffer FrameHistoryBuffer; diff --git a/Common/GPU/OpenGL/GLRenderManager.cpp b/Common/GPU/OpenGL/GLRenderManager.cpp index 0637d8f6ee..e5713fd0b1 100644 --- a/Common/GPU/OpenGL/GLRenderManager.cpp +++ b/Common/GPU/OpenGL/GLRenderManager.cpp @@ -37,7 +37,7 @@ GLRTexture::~GLRTexture() { } } -GLRenderManager::GLRenderManager(HistoryBuffer &frameTimeHistory) : frameTimeHistory_(frameTimeHistory) { +GLRenderManager::GLRenderManager(FrameHistoryBuffer &frameTimeHistory) : frameTimeHistory_(frameTimeHistory) { // size_t sz = sizeof(GLRRenderData); // _dbg_assert_(sz == 88); } diff --git a/Common/GPU/OpenGL/GLRenderManager.h b/Common/GPU/OpenGL/GLRenderManager.h index 1fb5c82473..9f724a6429 100644 --- a/Common/GPU/OpenGL/GLRenderManager.h +++ b/Common/GPU/OpenGL/GLRenderManager.h @@ -227,7 +227,7 @@ struct GLRRenderThreadTask { // directly in the destructor. class GLRenderManager { public: - GLRenderManager(HistoryBuffer &frameTimeHistory); + GLRenderManager(FrameHistoryBuffer &frameTimeHistory); ~GLRenderManager(); GLRenderManager(GLRenderManager &) = delete; @@ -912,5 +912,5 @@ private: InvalidationCallback invalidationCallback_; uint64_t frameIdGen_ = FRAME_TIME_HISTORY_LENGTH; - HistoryBuffer &frameTimeHistory_; + FrameHistoryBuffer &frameTimeHistory_; }; diff --git a/Common/GPU/Vulkan/VulkanRenderManager.cpp b/Common/GPU/Vulkan/VulkanRenderManager.cpp index 284a017ea6..e686ec2009 100644 --- a/Common/GPU/Vulkan/VulkanRenderManager.cpp +++ b/Common/GPU/Vulkan/VulkanRenderManager.cpp @@ -248,7 +248,7 @@ bool VKRComputePipeline::CreateAsync(VulkanContext *vulkan) { return true; } -VulkanRenderManager::VulkanRenderManager(VulkanContext *vulkan, bool useThread, HistoryBuffer &frameTimeHistory) +VulkanRenderManager::VulkanRenderManager(VulkanContext *vulkan, bool useThread, FrameHistoryBuffer &frameTimeHistory) : vulkan_(vulkan), queueRunner_(vulkan), initTimeMs_("initTimeMs"), totalGPUTimeMs_("totalGPUTimeMs"), diff --git a/Common/GPU/Vulkan/VulkanRenderManager.h b/Common/GPU/Vulkan/VulkanRenderManager.h index 7d2d9f819f..620223b0e4 100644 --- a/Common/GPU/Vulkan/VulkanRenderManager.h +++ b/Common/GPU/Vulkan/VulkanRenderManager.h @@ -182,7 +182,7 @@ struct CompileQueueEntry { class VulkanRenderManager { public: - VulkanRenderManager(VulkanContext *vulkan, bool useThread, HistoryBuffer &frameTimeHistory); + VulkanRenderManager(VulkanContext *vulkan, bool useThread, FrameHistoryBuffer &frameTimeHistory); ~VulkanRenderManager(); // Makes sure that the GPU has caught up enough that we can start writing buffers of this frame again. @@ -542,5 +542,5 @@ private: std::function invalidationCallback_; uint64_t frameIdGen_ = FRAME_TIME_HISTORY_LENGTH; - HistoryBuffer &frameTimeHistory_; + FrameHistoryBuffer &frameTimeHistory_; }; diff --git a/Common/GPU/thin3d.h b/Common/GPU/thin3d.h index 551964b396..e8b0008549 100644 --- a/Common/GPU/thin3d.h +++ b/Common/GPU/thin3d.h @@ -858,12 +858,12 @@ public: return ""; } - const HistoryBuffer &FrameTimeHistory() const { + const FrameHistoryBuffer &FrameTimeHistory() const { return frameTimeHistory_; } protected: - HistoryBuffer frameTimeHistory_; + FrameHistoryBuffer frameTimeHistory_; ShaderModule *vsPresets_[VS_MAX_PRESET]; ShaderModule *fsPresets_[FS_MAX_PRESET]; diff --git a/Core/FrameTiming.cpp b/Core/FrameTiming.cpp index 748c4538ee..04e5ec0cc0 100644 --- a/Core/FrameTiming.cpp +++ b/Core/FrameTiming.cpp @@ -96,8 +96,26 @@ Draw::PresentMode ComputePresentMode(Draw::DrawContext *draw, int *interval) { return mode; } -void FrameTiming::BeforeCPUSlice() { +void FrameTiming::BeforeCPUSlice(const FrameHistoryBuffer &frameHistory) { cpuSliceStartTime = time_now_d(); + + // Here we can examine the frame history for anomalies to correct. + nudge_ = 0.0; + + const FrameTimeData &oldData = frameHistory[3]; + if (oldData.queuePresent == 0.0) { + // No data to look at. + return; + } + + if (oldData.afterFenceWait - oldData.frameBegin > 0.001) { + nudge_ = (oldData.afterFenceWait - oldData.frameBegin) * 0.1; + } + + if (oldData.firstSubmit - oldData.afterFenceWait > cpuTime) { + // Not sure how this grows so large sometimes. + nudge_ = (oldData.firstSubmit - oldData.afterFenceWait - cpuTime) * 0.1; + } } void FrameTiming::SetTimeStep(float scaledTimeStep) { @@ -106,9 +124,9 @@ void FrameTiming::SetTimeStep(float scaledTimeStep) { double now = time_now_d(); cpuTime = now - cpuSliceStartTime; - this->timeStep = scaledTimeStep + nudge_; + this->timeStep = scaledTimeStep; - // Sync up lastPresentTime with the current time if it's way off. + // Sync up lastPresentTime with the current time if it's way off. TODO: This should probably drift. if (lastPresentTime < now - 0.5f) { lastPresentTime = now; } @@ -128,7 +146,7 @@ void FrameTiming::BeforePresent() { return; // Wait until we hit the next present time. Ideally we'll be fairly close here due to the previous AfterPresent wait. - nextPresentTime = lastPresentTime + this->timeStep; + nextPresentTime = lastPresentTime + this->timeStep + nudge_; while (true) { double remaining = nextPresentTime - time_now_d(); if (remaining <= 0.0) diff --git a/Core/FrameTiming.h b/Core/FrameTiming.h index 5aa4fde5fb..60afe65f71 100644 --- a/Core/FrameTiming.h +++ b/Core/FrameTiming.h @@ -27,7 +27,7 @@ public: void Reset(Draw::DrawContext *draw); - void BeforeCPUSlice(); + void BeforeCPUSlice(const FrameHistoryBuffer &frameHistory); void SetTimeStep(float scaledTimeStep); void AfterCPUSlice(); void BeforePresent(); diff --git a/Core/HLE/sceDisplay.cpp b/Core/HLE/sceDisplay.cpp index c1fb54ff32..e8d70116f5 100644 --- a/Core/HLE/sceDisplay.cpp +++ b/Core/HLE/sceDisplay.cpp @@ -442,7 +442,7 @@ static void DoFrameTiming(bool throttle, bool *skipFrame, float scaledTimestep) #ifdef _WIN32 sleep_ms(1); // Sleep for 1ms on this thread #else - const double left = g_frameTiming.nextFrameTime - g_frameTiming.curFrameTime; + const double left = nextFrameTime - curFrameTime; usleep((long)(left * 1000000)); #endif } @@ -637,9 +637,13 @@ void __DisplayFlip(int cyclesLate) { bool throttle = FrameTimingThrottled(); int fpsLimit = FrameTimingLimit(); float scaledTimestep = (float)numVBlanksSinceFlip * timePerVblank; + const bool fbReallyDirty = gpu->FramebufferReallyDirty(); if (fpsLimit > 0 && fpsLimit != framerate) { scaledTimestep *= (float)framerate / fpsLimit; } + bool skipFrame; + int maxFrameskip; + int frameSkipNum; // If the ideal case, use the new timing path. g_frameTiming.usePresentTiming = g_Config.iFrameSkip == 0 && !refreshRateNeedsSkip; @@ -659,7 +663,6 @@ void __DisplayFlip(int cyclesLate) { } // Setting CORE_NEXTFRAME (which Core_NextFrame does) causes a swap. - const bool fbReallyDirty = gpu->FramebufferReallyDirty(); if (fbReallyDirty || noRecentFlip || postEffectRequiresFlip) { // Check first though, might've just quit / been paused. if (!forceNoFlip && Core_NextFrame()) { @@ -674,11 +677,10 @@ void __DisplayFlip(int cyclesLate) { gpuStats.numFlips++; } - bool skipFrame; DoFrameTiming(throttle, &skipFrame, scaledTimestep); - int maxFrameskip = 8; - int frameSkipNum = DisplayCalculateFrameSkip(); + maxFrameskip = 8; + frameSkipNum = DisplayCalculateFrameSkip(); if (throttle) { // 4 here means 1 drawn, 4 skipped - so 12 fps minimum. maxFrameskip = frameSkipNum; diff --git a/UI/DebugOverlay.cpp b/UI/DebugOverlay.cpp index efa9467619..aa3e6a6ceb 100644 --- a/UI/DebugOverlay.cpp +++ b/UI/DebugOverlay.cpp @@ -115,12 +115,13 @@ static void DrawFrameTiming(UIContext *ctx, const Bounds &bounds) { ctx->Draw()->SetFontScale(0.5f, 0.5f); snprintf(statBuf, sizeof(statBuf), - "Mode (interval): %s (%d)" + "Mode (interval): %s (%d) (%s)\n" "CPU time: %0.1fms\n" "Timestep: %0.1fms\n" "Postsleep: %0.1fms\n", Draw::PresentModeToString(g_frameTiming.presentMode), g_frameTiming.presentInterval, + g_frameTiming.usePresentTiming ? "new" : "old", g_frameTiming.cpuTime * 1000.0, g_frameTiming.timeStep * 1000.0, g_frameTiming.postSleep * 1000.0 diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index 0abf6f1d5e..720c0bd1df 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -1164,7 +1164,7 @@ void NativeFrame(GraphicsContext *graphicsContext) { g_screenManager->getUIContext()->SetTintSaturation(g_Config.fUITint, g_Config.fUISaturation); - g_frameTiming.BeforeCPUSlice(); + g_frameTiming.BeforeCPUSlice(g_draw->FrameTimeHistory()); // All actual rendering happen in here. g_screenManager->render(); if (g_screenManager->getUIContext()->Text()) {