feat: add an in-game performance panel (#1090)

Add in-game performance panel

Co-authored-by: nmzik <Nmzik@mail.ru>
This commit is contained in:
Juan Pablo Farias
2026-10-17 09:47:51 -03:00
committed by GitHub
parent 415387f00d
commit 6aa3846fb2
35 changed files with 1788 additions and 34 deletions
+9
View File
@@ -8,6 +8,7 @@
#include <map>
#include <mutex>
#include <span>
#include <tuple>
#include <vector>
#include <vk_mem_alloc.h>
@@ -99,6 +100,14 @@ struct GraphicContext {
[[nodiscard]] bool CanReportMemoryUsage() const noexcept { return memory_budget_ext_enabled; }
[[nodiscard]] uint64_t GetDeviceMemoryUsage() const;
[[nodiscard]] uint64_t GetTotalMemoryBudget() const;
struct HeapUsage {
uint64_t usage = 0; // Whole process, as reported by VK_EXT_memory_budget.
uint64_t budget = 0;
uint32_t allocations = 0;
bool device_local = false;
};
[[nodiscard]] uint32_t GetHeapUsage(std::span<HeapUsage, VK_MAX_MEMORY_HEAPS> heaps) const;
[[nodiscard]] bool CreateImage(const vk::ImageCreateInfo& info, VulkanImage& image);
void DeleteImage(VulkanImage& image);
+4
View File
@@ -2,6 +2,7 @@
#include "common/alignment.h"
#include "common/assert.h"
#include "common/debugCounters.h"
#include "common/logging/log.h"
#include "common/profiler.h"
#include "graphics/guest_gpu/graphicsRun.h"
@@ -177,6 +178,7 @@ bool BufferCache::DownloadBufferMemory(Buffer& buffer, uint64_t vaddr, uint64_t
if (copies.empty()) {
return false;
}
Common::DebugCounters::Add(Common::DebugCounters::Counter::BufferDownloadBytes, total_size);
auto [mapped, offset] = m_download_buffer.Map(total_size, 64);
std::unique_ptr<Buffer> temporary;
@@ -467,6 +469,7 @@ vk::Buffer BufferCache::UploadCopies(Buffer& buffer, std::span<vk::BufferCopy> c
if (copies.empty()) {
return nullptr;
}
Common::DebugCounters::Add(Common::DebugCounters::Counter::BufferUploadBytes, total_size);
const auto source = UploadMemory(m_graphics, m_scheduler, m_staging_buffer, total_size, 4,
vk::BufferUsageFlagBits::eTransferSrc, [&](uint8_t* mapped, uint64_t base_offset) {
@@ -658,6 +661,7 @@ void BufferCache::RunGarbageCollector() {
} else {
m_memory_tracker.UntrackMemory(buffer.CpuAddress(), buffer.Size());
DeleteBuffer(id);
Common::DebugCounters::Add(Common::DebugCounters::Counter::BufferEvictions);
}
return ++retire_count == limit;
});
+5
View File
@@ -2,6 +2,7 @@
#include "common/alignment.h"
#include "common/assert.h"
#include "common/debugCounters.h"
#include "common/emulatorConfig.h"
#include "common/logging/log.h"
#include "common/profiler.h"
@@ -954,6 +955,7 @@ void TextureCache::UploadImage(Image& image, Buffer& source, uint64_t source_off
const auto linear = m_tiler.Detile(source.Handle(), source_offset, info);
const auto copies = info.BufferCopies(linear.offset);
destination.Upload(copies, linear.buffer, linear.offset, linear.size);
Common::DebugCounters::Add(Common::DebugCounters::Counter::TextureUploadBytes, linear.size);
return;
}
@@ -999,6 +1001,7 @@ void TextureCache::UploadImage(Image& image, Buffer& source, uint64_t source_off
copies[0].imageExtent = info.extent;
}
destination.Upload(copies, linear.buffer, linear.offset, linear.size);
Common::DebugCounters::Add(Common::DebugCounters::Counter::TextureUploadBytes, linear.size);
}
void TextureCache::InitializeImage(ImageId id) {
@@ -1774,6 +1777,7 @@ bool TextureCache::DownloadImageMemory(ImageId id) {
download.Flush(offset, range.size);
DownloadImage(image, download, offset, range.size, image.info.resources.levels);
Common::DebugCounters::Add(Common::DebugCounters::Counter::TextureDownloadBytes, range.size);
vk::BufferMemoryBarrier barrier {};
barrier.srcAccessMask = vk::AccessFlagBits::eMemoryWrite | vk::AccessFlagBits::eTransferWrite |
vk::AccessFlagBits::eShaderWrite;
@@ -1943,6 +1947,7 @@ void TextureCache::RunGarbageCollector() {
}
}
FreeImage(id);
Common::DebugCounters::Add(Common::DebugCounters::Counter::TextureEvictions);
if (m_total_used_memory < m_critical_gc_memory && aggressive) {
deletions >>= 2;
aggressive = false;
@@ -1,6 +1,7 @@
#include "graphics/host_gpu/renderer/commandScheduler.h"
#include "common/assert.h"
#include "common/debugCounters.h"
#include "common/logging/log.h"
#include "graphics/host_gpu/graphicContext.h"
@@ -373,6 +374,7 @@ uint64_t CommandScheduler::Submit(SubmitInfo submit) {
result = graphics.queue.submit(1, &submit_info, nullptr);
}
Common::DebugCounters::Add(Common::DebugCounters::Counter::QueueSubmits);
if (result != vk::Result::eSuccess) {
ReportVulkanFatal("vkQueueSubmit", result, tick, m_command.m_debug_op,
@@ -1,6 +1,7 @@
#include "graphics/host_gpu/renderer/masterSemaphore.h"
#include "common/assert.h"
#include "common/debugCounters.h"
#include "graphics/host_gpu/graphicContext.h"
namespace Libs::Graphics {
@@ -49,6 +50,8 @@ void MasterSemaphore::Wait(uint64_t tick) {
wait_info.pSemaphores = &m_semaphore;
wait_info.pValues = &tick;
Common::DebugCounters::ScopedTimer timer(Common::DebugCounters::Counter::GpuWaits,
Common::DebugCounters::Counter::GpuWaitNs);
const auto result = m_graphics.device.waitSemaphores(&wait_info, UINT64_MAX);
EXIT_NOT_IMPLEMENTED(result != vk::Result::eSuccess);
Refresh();
@@ -1,6 +1,7 @@
#include "graphics/host_gpu/renderer/pipeline/pipelineCache.h"
#include "common/assert.h"
#include "common/debugCounters.h"
#include "common/emulatorConfig.h"
#include "common/file.h"
#include "common/logging/log.h"
@@ -24,6 +25,7 @@
#include <array>
#include <atomic>
#include <cctype>
#include <chrono>
#include <cstdio>
#include <cstring>
#include <fmt/format.h>
@@ -299,6 +301,9 @@ struct PipelineCache::ProgramCache {
ShaderRecompiler::IR::ResourceSpecialization specialization,
uint32_t push_data_start_dword,
std::span<const uint32_t> function_code) {
Common::DebugCounters::ScopedTimer timer(Common::DebugCounters::Counter::ShadersCompiled,
Common::DebugCounters::Counter::ShaderCompileNs,
Common::DebugCounters::Peak::ShaderCompileNs);
auto result = ShaderRecompiler::CompileProgram(std::move(translated), options,
specialization, push_data_start_dword);
if (!ValidateShaderSpirv(options.dump_label, options.shader_hash, result.spirv,
@@ -993,8 +998,13 @@ PipelineCache::Pipeline& PipelineCache::GetGraphicsPipeline(
auto cached = std::make_unique<Pipeline>();
LogPipelineTrace("CreatePipelineInternal begin", vs_id, ps_id);
CreatePipelineInternal(m_graphics, *cached, rendering, key.vertex_input, vertex_info,
ps_input_info, programs, static_params, m_driver_cache);
{
Common::DebugCounters::ScopedTimer timer(Common::DebugCounters::Counter::PipelinesCreated,
Common::DebugCounters::Counter::PipelineCreateNs,
Common::DebugCounters::Peak::PipelineCreateNs);
CreatePipelineInternal(m_graphics, *cached, rendering, key.vertex_input, vertex_info,
ps_input_info, programs, static_params, m_driver_cache);
}
LogPipelineTrace("CreatePipelineInternal done", vs_id, ps_id);
EXIT_NOT_IMPLEMENTED(cached->pipeline == nullptr);
@@ -1024,7 +1034,13 @@ PipelineCache::GetComputePipeline(const ShaderComputeInputInfo& input_info,
}
auto cached = std::make_unique<Pipeline>();
CreatePipelineInternal(m_graphics, *cached, input_info, compute_program.module, m_driver_cache);
{
Common::DebugCounters::ScopedTimer timer(Common::DebugCounters::Counter::PipelinesCreated,
Common::DebugCounters::Counter::PipelineCreateNs,
Common::DebugCounters::Peak::PipelineCreateNs);
CreatePipelineInternal(m_graphics, *cached, input_info, compute_program.module,
m_driver_cache);
}
EXIT_NOT_IMPLEMENTED(cached->pipeline == nullptr);
EXIT_NOT_IMPLEMENTED(cached->pipeline_layout == nullptr);
@@ -1,5 +1,6 @@
#include "common/assert.h"
#include "common/common.h"
#include "common/debugCounters.h"
#include "common/emulatorConfig.h"
#include "common/file.h"
#include "common/logging/log.h"
@@ -432,6 +433,7 @@ void RenderExecutor::DispatchDirect(uint64_t submit_id, CommandBuffer& buffer,
vk_buffer.bindPipeline(vk::PipelineBindPoint::eCompute, pipeline.pipeline);
vk_buffer.dispatch(thread_group_x, thread_group_y, thread_group_z);
Common::DebugCounters::Add(Common::DebugCounters::Counter::Dispatches);
// The removed host fence also ordered read-only dispatches before later writers.
ShaderAccessBarrier(vk_buffer, vk::PipelineStageFlagBits::eComputeShader);
@@ -501,6 +503,7 @@ void RenderExecutor::DispatchIndirect(uint64_t submit_id, CommandBuffer& buffer,
1, &barrier, 0, nullptr, 0, nullptr);
vk_buffer.bindPipeline(vk::PipelineBindPoint::eCompute, pipeline.pipeline);
vk_buffer.dispatchIndirect(args_buffer->Handle(), args_offset);
Common::DebugCounters::Add(Common::DebugCounters::Counter::Dispatches);
ShaderAccessBarrier(vk_buffer, vk::PipelineStageFlagBits::eComputeShader);
ResetBindings();
}
@@ -2,6 +2,7 @@
#include "common/assert.h"
#include "common/common.h"
#include "common/debugCounters.h"
#include "common/emulatorConfig.h"
#include "common/file.h"
#include "common/logging/log.h"
@@ -1233,6 +1234,8 @@ void RenderExecutor::ExecutePreparedDraw(uint64_t submit_id, CommandBuffer& buff
} else {
EmitDrawPrimitives(ucfg, vk_buffer, draw, emit);
}
Common::DebugCounters::Add(Common::DebugCounters::Counter::Draws);
Common::DebugCounters::Add(Common::DebugCounters::Counter::DrawInstances, draw.instance_count);
if (!draw.IsIndexed()) {
SetDrawDebugPhase(buffer, submit_id, draw, 0x600u);
+18
View File
@@ -97,6 +97,24 @@ uint64_t GraphicContext::GetDeviceMemoryUsage() const {
return usage;
}
uint32_t GraphicContext::GetHeapUsage(std::span<HeapUsage, VK_MAX_MEMORY_HEAPS> heaps) const {
if (allocator == nullptr) {
return 0;
}
VmaBudget budgets[VK_MAX_MEMORY_HEAPS] {};
vmaGetHeapBudgets(allocator, budgets);
const auto count = physical_device_memory_properties.memoryHeapCount;
for (uint32_t heap = 0; heap < count; heap++) {
const auto& properties = physical_device_memory_properties.memoryHeaps[heap];
heaps[heap] = {.usage = budgets[heap].usage,
.budget = budgets[heap].budget,
.allocations = budgets[heap].statistics.allocationCount,
.device_local = static_cast<bool>(properties.flags &
vk::MemoryHeapFlagBits::eDeviceLocal)};
}
return count;
}
uint64_t GraphicContext::GetTotalMemoryBudget() const {
if (allocator == nullptr) {
return 0;