diff --git a/Common/Data/Collections/FastVec.h b/Common/Data/Collections/FastVec.h index 88d8971c9f..b8139ac590 100644 --- a/Common/Data/Collections/FastVec.h +++ b/Common/Data/Collections/FastVec.h @@ -157,14 +157,37 @@ private: }; // Simple cyclical vector. -// Initially, doesn't do any sanity checking. template class HistoryBuffer { public: + T &Add(size_t index) { + _dbg_assert_((int64_t)index >= 0); + if (index > maxIndex_) + maxIndex_ = index; + T &entry = data_[index % size]; + entry = T{}; + return entry; + } + + const T &Back(size_t index) const { + _dbg_assert_(index < maxIndex_ && index < size); + return data_[(maxIndex_ - index) % size]; + } + // Out of bounds (past size() - 1) is undefined behavior. - T &operator[] (const size_t index) { return data_[index % size]; } - const T &operator[] (const size_t index) const { return data_[index % size]; } + T &operator[] (const size_t index) { + _dbg_assert_(index <= maxIndex_); + return data_[index % size]; + } + const T &operator[] (const size_t index) const { + _dbg_assert_(index <= maxIndex_); + return data_[index % size]; + } + size_t MaxIndex() const { + return maxIndex_; + } private: T data_[size]{}; + size_t maxIndex_ = 0; }; diff --git a/Common/GPU/MiscTypes.h b/Common/GPU/MiscTypes.h index 8bd365508a..834db32f19 100644 --- a/Common/GPU/MiscTypes.h +++ b/Common/GPU/MiscTypes.h @@ -34,3 +34,4 @@ struct FrameTimeData { double earliestPresentTime; double presentMargin; }; +constexpr size_t FRAME_TIME_HISTORY_LENGTH = 32; diff --git a/Common/GPU/Vulkan/VulkanRenderManager.cpp b/Common/GPU/Vulkan/VulkanRenderManager.cpp index 683b6cf515..7ec9c1ac47 100644 --- a/Common/GPU/Vulkan/VulkanRenderManager.cpp +++ b/Common/GPU/Vulkan/VulkanRenderManager.cpp @@ -250,12 +250,13 @@ bool VKRComputePipeline::CreateAsync(VulkanContext *vulkan) { return true; } -VulkanRenderManager::VulkanRenderManager(VulkanContext *vulkan, bool useThread) +VulkanRenderManager::VulkanRenderManager(VulkanContext *vulkan, bool useThread, HistoryBuffer &frameTimeHistory) : vulkan_(vulkan), queueRunner_(vulkan), initTimeMs_("initTimeMs"), totalGPUTimeMs_("totalGPUTimeMs"), renderCPUTimeMs_("renderCPUTimeMs"), - useRenderThread_(useThread) + useRenderThread_(useThread), + frameTimeHistory_(frameTimeHistory) { inflightFramesAtStart_ = vulkan_->GetInflightFrames(); @@ -552,13 +553,13 @@ void VulkanRenderManager::PresentWaitThreadFunc() { while (run_) { const uint64_t timeout = 1000000000ULL; // 1 sec if (VK_SUCCESS == vkWaitForPresentKHR(vulkan_->GetDevice(), vulkan_->GetSwapchain(), waitedId, timeout)) { - frameTimeData_[waitedId].actualPresent = time_now_d(); - frameTimeData_[waitedId].waitCount++; + frameTimeHistory_[waitedId].actualPresent = time_now_d(); + frameTimeHistory_[waitedId].waitCount++; waitedId++; } else { // We caught up somehow, which is a bad sign (we should have blocked, right?). Maybe we should break out of the loop? sleep_ms(1); - frameTimeData_[waitedId].waitCount++; + frameTimeHistory_[waitedId].waitCount++; } _dbg_assert_(waitedId <= frameIdGen_); } @@ -580,11 +581,11 @@ void VulkanRenderManager::PollPresentTiming() { vkGetPastPresentationTimingGOOGLE(vulkan_->GetDevice(), vulkan_->GetSwapchain(), &count, timings); for (uint32_t i = 0; i < count; i++) { uint64_t presentId = timings[i].presentID; - frameTimeData_[presentId].actualPresent = from_time_raw(timings[i].actualPresentTime); - frameTimeData_[presentId].desiredPresentTime = from_time_raw(timings[i].desiredPresentTime); - frameTimeData_[presentId].earliestPresentTime = from_time_raw(timings[i].earliestPresentTime); + frameTimeHistory_[presentId].actualPresent = from_time_raw(timings[i].actualPresentTime); + frameTimeHistory_[presentId].desiredPresentTime = from_time_raw(timings[i].desiredPresentTime); + frameTimeHistory_[presentId].earliestPresentTime = from_time_raw(timings[i].earliestPresentTime); double presentMargin = from_time_raw_relative(timings[i].presentMargin); - frameTimeData_[presentId].presentMargin = presentMargin; + frameTimeHistory_[presentId].presentMargin = presentMargin; } delete[] timings; } @@ -623,16 +624,16 @@ void VulkanRenderManager::BeginFrame(bool enableProfiling, bool enableLogProfile int validBits = vulkan_->GetQueueFamilyProperties(vulkan_->GetGraphicsQueueFamilyIndex()).timestampValidBits; + FrameTimeData &frameTimeData = frameTimeHistory_.Add(frameId); + frameTimeData.frameId = frameId; + frameTimeData.frameBegin = frameBeginTime; + frameTimeData.afterFenceWait = time_now_d(); + // Can't set this until after the fence. frameData.profile.enabled = enableProfiling; frameData.profile.timestampsEnabled = enableProfiling && validBits > 0; frameData.frameId = frameId; - frameTimeData_[frameId] = {}; - frameTimeData_[frameId].frameId = frameId; - frameTimeData_[frameId].frameBegin = frameBeginTime; - frameTimeData_[frameId].afterFenceWait = time_now_d(); - uint64_t queryResults[MAX_TIMESTAMP_QUERIES]; if (enableProfiling) { @@ -1391,7 +1392,7 @@ void VulkanRenderManager::Run(VKRRenderThreadTask &task) { if (task.runType == VKRRunType::PRESENT) { if (!frameData.skipSwap) { VkResult res = frameData.QueuePresent(vulkan_, frameDataShared_); - frameTimeData_[frameData.frameId].queuePresent = time_now_d(); + frameTimeHistory_[frameData.frameId].queuePresent = time_now_d(); if (res == VK_ERROR_OUT_OF_DATE_KHR) { // We clearly didn't get this in vkAcquireNextImageKHR because of the skipSwap check above. // Do the increment. @@ -1414,8 +1415,8 @@ void VulkanRenderManager::Run(VKRRenderThreadTask &task) { _dbg_assert_(!frameData.hasPresentCommands); - if (!frameTimeData_[frameData.frameId].firstSubmit) { - frameTimeData_[frameData.frameId].firstSubmit = time_now_d(); + if (!frameTimeHistory_[frameData.frameId].firstSubmit) { + frameTimeHistory_[frameData.frameId].firstSubmit = time_now_d(); } frameData.SubmitPending(vulkan_, FrameSubmitType::Pending, frameDataShared_); diff --git a/Common/GPU/Vulkan/VulkanRenderManager.h b/Common/GPU/Vulkan/VulkanRenderManager.h index fbe1bbc5fc..7520122dab 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); + VulkanRenderManager(VulkanContext *vulkan, bool useThread, HistoryBuffer &frameTimeHistory); ~VulkanRenderManager(); // Makes sure that the GPU has caught up enough that we can start writing buffers of this frame again. @@ -458,17 +458,6 @@ public: void ResetStats(); void DrainCompileQueue(); - // framesBack is the number of frames into the past to look. - FrameTimeData GetFrameTimeData(int framesBack) const { - FrameTimeData data; - if (framesBack >= frameIdGen_) { - data = {}; - return data; - } - data = frameTimeData_[frameIdGen_ - framesBack]; - return data; - } - private: void EndCurRenderStep(); @@ -553,5 +542,5 @@ private: std::function invalidationCallback_; uint64_t frameIdGen_ = 31; - HistoryBuffer frameTimeData_; + HistoryBuffer &frameTimeHistory_; }; diff --git a/Common/GPU/Vulkan/thin3d_vulkan.cpp b/Common/GPU/Vulkan/thin3d_vulkan.cpp index 3111f9db21..2c2d5a109e 100644 --- a/Common/GPU/Vulkan/thin3d_vulkan.cpp +++ b/Common/GPU/Vulkan/thin3d_vulkan.cpp @@ -488,10 +488,6 @@ public: return frameCount_; } - FrameTimeData GetFrameTimeData(int framesBack) const override { - return renderManager_.GetFrameTimeData(framesBack); - } - void FlushState() override {} void ResetStats() override { @@ -564,7 +560,7 @@ private: AutoRef boundTextures_[MAX_BOUND_TEXTURES]; AutoRef boundSamplers_[MAX_BOUND_TEXTURES]; VkImageView boundImageView_[MAX_BOUND_TEXTURES]{}; - TextureBindFlags boundTextureFlags_[MAX_BOUND_TEXTURES]; + TextureBindFlags boundTextureFlags_[MAX_BOUND_TEXTURES]{}; VulkanPushPool *push_ = nullptr; @@ -866,7 +862,7 @@ static DataFormat DataFormatFromVulkanDepth(VkFormat fmt) { } VKContext::VKContext(VulkanContext *vulkan, bool useRenderThread) - : vulkan_(vulkan), renderManager_(vulkan, useRenderThread) { + : vulkan_(vulkan), renderManager_(vulkan, useRenderThread, frameTimeHistory_) { shaderLanguageDesc_.Init(GLSL_VULKAN); VkFormat depthStencilFormat = vulkan->GetDeviceInfo().preferredDepthStencilFormat; diff --git a/Common/GPU/thin3d.h b/Common/GPU/thin3d.h index 56e9e4aa79..bfcfc6aaa8 100644 --- a/Common/GPU/thin3d.h +++ b/Common/GPU/thin3d.h @@ -18,6 +18,7 @@ #include "Common/GPU/Shader.h" #include "Common/GPU/MiscTypes.h" #include "Common/Data/Collections/Slice.h" +#include "Common/Data/Collections/FastVec.h" namespace Lin { class Matrix4x4; @@ -853,15 +854,17 @@ public: // Total amount of frames rendered. Unaffected by game pause, so more robust than gpuStats.numFlips virtual int GetFrameCount() = 0; - virtual FrameTimeData GetFrameTimeData(int framesBack) const { - return FrameTimeData{}; - } - virtual std::string GetGpuProfileString() const { return ""; } + const HistoryBuffer &FrameTimeHistory() const { + return frameTimeHistory_; + } + protected: + HistoryBuffer frameTimeHistory_; + ShaderModule *vsPresets_[VS_MAX_PRESET]; ShaderModule *fsPresets_[FS_MAX_PRESET]; diff --git a/UI/DebugOverlay.cpp b/UI/DebugOverlay.cpp index ca444bdef9..f48f1bf826 100644 --- a/UI/DebugOverlay.cpp +++ b/UI/DebugOverlay.cpp @@ -115,15 +115,18 @@ static void DrawFrameTiming(UIContext *ctx, const Bounds &bounds) { ctx->Draw()->SetFontScale(0.5f, 0.5f); snprintf(statBuf, sizeof(statBuf), - "Present mode (interval): %s (%d)", + "Mode (interval): %s (%d)", Draw::PresentModeToString(g_frameTiming.presentMode), g_frameTiming.presentInterval); ctx->Draw()->DrawTextRect(ubuntu24, statBuf, bounds.x + 10, bounds.y + 50, bounds.w - 20, bounds.h - 30, 0xFFFFFFFF, FLAG_DYNAMIC_ASCII); for (int i = 0; i < 5; i++) { - FrameTimeData data = ctx->GetDrawContext()->GetFrameTimeData(6 + i); - FrameTimeData prevData = ctx->GetDrawContext()->GetFrameTimeData(7 + i); + size_t curIndex = i + 6; + size_t prevIndex = i + 7; + + FrameTimeData data = ctx->GetDrawContext()->FrameTimeHistory().Back(curIndex); + FrameTimeData prevData = ctx->GetDrawContext()->FrameTimeHistory().Back(prevIndex); if (data.frameBegin == 0.0) { snprintf(statBuf, sizeof(statBuf), "(No frame time data)"); } else {