diff --git a/source/renderer/backend/IDevice.h b/source/renderer/backend/IDevice.h index 0ee2fbb7db..4c791339e7 100644 --- a/source/renderer/backend/IDevice.h +++ b/source/renderer/backend/IDevice.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2025 Wildfire Games. +/* Copyright (C) 2026 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -28,6 +28,7 @@ #include #include #include +#include #include class CShaderDefines; @@ -217,6 +218,26 @@ public: virtual uint64_t GetQueryResult(const uint32_t handle) = 0; virtual const Capabilities& GetCapabilities() const = 0; + + /** + * Collects backend-specific statistics. + */ + struct StatisticsItem + { + std::string_view name; + std::string_view unit; + std::variant value; + + // clang can't do emplace_back yet because of the aggregate type. + StatisticsItem( + std::string_view name, std::string_view unit, + std::variant value) + : name(name), unit(unit), value(value) + { + } + }; + using StatisticsVector = std::vector; + virtual void CollectStatistics(StatisticsVector& statistics) const = 0; }; } // namespace Backend diff --git a/source/renderer/backend/dummy/Device.h b/source/renderer/backend/dummy/Device.h index dd9046cde0..8a0286c07d 100644 --- a/source/renderer/backend/dummy/Device.h +++ b/source/renderer/backend/dummy/Device.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2025 Wildfire Games. +/* Copyright (C) 2026 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -115,6 +115,8 @@ public: const Capabilities& GetCapabilities() const override { return m_Capabilities; } + void CollectStatistics(StatisticsVector&) const override {} + protected: std::string m_Name; diff --git a/source/renderer/backend/gl/Device.cpp b/source/renderer/backend/gl/Device.cpp index 39d55d2e95..c484cc858a 100644 --- a/source/renderer/backend/gl/Device.cpp +++ b/source/renderer/backend/gl/Device.cpp @@ -1079,6 +1079,11 @@ void CDevice::InsertTimestampQuery(const uint32_t handle) #endif } +void CDevice::CollectStatistics(StatisticsVector& statistics) const +{ + statistics.emplace_back("Backbuffer count", "", static_cast(m_Backbuffers.size())); +} + std::unique_ptr CreateDevice(SDL_Window* window) { return GL::CDevice::Create(window); diff --git a/source/renderer/backend/gl/Device.h b/source/renderer/backend/gl/Device.h index ee7b71eab6..7f249a0be2 100644 --- a/source/renderer/backend/gl/Device.h +++ b/source/renderer/backend/gl/Device.h @@ -137,6 +137,8 @@ public: const Capabilities& GetCapabilities() const override { return m_Capabilities; } + void CollectStatistics(StatisticsVector& statistics) const override; + private: CDevice(); diff --git a/source/renderer/backend/vulkan/DescriptorManager.cpp b/source/renderer/backend/vulkan/DescriptorManager.cpp index 4b420a01af..7b29f16d3f 100644 --- a/source/renderer/backend/vulkan/DescriptorManager.cpp +++ b/source/renderer/backend/vulkan/DescriptorManager.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2025 Wildfire Games. +/* Copyright (C) 2026 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -464,6 +464,19 @@ void CDescriptorManager::OnDeviceObjectDestroy(const DeviceObjectUID uid) m_UIDToSingleTypePoolMap.erase(it); } +void CDescriptorManager::CollectStatistics(IDevice::StatisticsVector& statistics) const +{ + const uint32_t descriptorPoolCount{std::transform_reduce( + m_SingleTypePools.begin(), m_SingleTypePools.end(), 0u, std::plus(), + [](const auto& cacheItem) + { + return static_cast(cacheItem.second.size()); + })}; + + statistics.emplace_back("Cached VkDescriptorPool count", "", descriptorPoolCount); + statistics.emplace_back("Cached VkDescriptorSet count", "", static_cast(m_SingleTypeSets.size())); +} + } // namespace Vulkan } // namespace Backend diff --git a/source/renderer/backend/vulkan/DescriptorManager.h b/source/renderer/backend/vulkan/DescriptorManager.h index dc1fe6159e..a19f59e3dd 100644 --- a/source/renderer/backend/vulkan/DescriptorManager.h +++ b/source/renderer/backend/vulkan/DescriptorManager.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2025 Wildfire Games. +/* Copyright (C) 2026 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -86,6 +86,8 @@ public: const std::vector& GetDescriptorSetLayouts() const { return m_DescriptorSetLayouts; } + void CollectStatistics(IDevice::StatisticsVector& statistics) const; + private: struct SingleTypePool { diff --git a/source/renderer/backend/vulkan/Device.cpp b/source/renderer/backend/vulkan/Device.cpp index cf5f30d98b..ec26b7309b 100644 --- a/source/renderer/backend/vulkan/Device.cpp +++ b/source/renderer/backend/vulkan/Device.cpp @@ -981,6 +981,30 @@ uint64_t CDevice::GetQueryResult(const uint32_t handle) return data; } +void CDevice::CollectStatistics(StatisticsVector& statistics) const +{ + VmaBudget heapBudgets[VK_MAX_MEMORY_HEAPS]; + vmaGetHeapBudgets(m_VMAAllocator, heapBudgets); + + VmaStatistics totalStatistics{}; + for (uint32_t index{0}; index < m_ChoosenDevice.memoryProperties.memoryHeapCount; ++index) + { + totalStatistics.blockCount += heapBudgets[index].statistics.blockCount; + totalStatistics.allocationCount += heapBudgets[index].statistics.allocationCount; + totalStatistics.blockBytes += heapBudgets[index].statistics.blockBytes; + totalStatistics.allocationBytes += heapBudgets[index].statistics.allocationBytes; + } + + statistics.emplace_back("VMA total blockCount", "", totalStatistics.blockCount); + statistics.emplace_back("VMA total allocationCount", "", totalStatistics.allocationCount); + statistics.emplace_back("VMA total blockBytes", "MiB", static_cast(totalStatistics.blockBytes / MiB)); + statistics.emplace_back("VMA total allocationBytes", "MiB", static_cast(totalStatistics.allocationBytes / MiB)); + + m_DescriptorManager->CollectStatistics(statistics); + m_SamplerManager->CollectStatistics(statistics); + m_RenderPassManager->CollectStatistics(statistics); +} + bool CDevice::IsFormatSupportedForUsage(const Format format, const uint32_t usage) const { VkFormatProperties formatProperties{}; diff --git a/source/renderer/backend/vulkan/Device.h b/source/renderer/backend/vulkan/Device.h index 4867db9e89..404f0b027b 100644 --- a/source/renderer/backend/vulkan/Device.h +++ b/source/renderer/backend/vulkan/Device.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2025 Wildfire Games. +/* Copyright (C) 2026 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -145,6 +145,8 @@ public: const Capabilities& GetCapabilities() const override { return m_Capabilities; } + void CollectStatistics(StatisticsVector& statistics) const override; + VkDevice GetVkDevice() const { return m_Device; } VmaAllocator GetVMAAllocator() { return m_VMAAllocator; } @@ -188,6 +190,8 @@ public: DeviceObjectUID GenerateNextDeviceObjectUID(); + uint32_t GetFrameID() const { return m_FrameID; } + private: CDevice(); diff --git a/source/renderer/backend/vulkan/RenderPassManager.cpp b/source/renderer/backend/vulkan/RenderPassManager.cpp index a9779195fe..5c85133ff0 100644 --- a/source/renderer/backend/vulkan/RenderPassManager.cpp +++ b/source/renderer/backend/vulkan/RenderPassManager.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2025 Wildfire Games. +/* Copyright (C) 2026 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -198,6 +198,11 @@ VkRenderPass CRenderPassManager::GetOrCreateRenderPass( return renderPass; } +void CRenderPassManager::CollectStatistics(IDevice::StatisticsVector& statistics) const +{ + statistics.emplace_back("VkRenderPass count", "", static_cast(m_RenderPassMap.size())); +} + } // namespace Vulkan } // namespace Backend diff --git a/source/renderer/backend/vulkan/RenderPassManager.h b/source/renderer/backend/vulkan/RenderPassManager.h index cf8a5d61b5..8aae14e899 100644 --- a/source/renderer/backend/vulkan/RenderPassManager.h +++ b/source/renderer/backend/vulkan/RenderPassManager.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2025 Wildfire Games. +/* Copyright (C) 2026 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -18,6 +18,8 @@ #ifndef INCLUDED_RENDERER_BACKEND_VULKAN_RENDERPASSMANAGER #define INCLUDED_RENDERER_BACKEND_VULKAN_RENDERPASSMANAGER +#include "renderer/backend/IDevice.h" + #include #include #include @@ -57,6 +59,8 @@ public: SColorAttachment* colorAttachment, SDepthStencilAttachment* depthStencilAttachment); + void CollectStatistics(IDevice::StatisticsVector& statistics) const; + private: CDevice* m_Device = nullptr; diff --git a/source/renderer/backend/vulkan/SamplerManager.cpp b/source/renderer/backend/vulkan/SamplerManager.cpp index 755a6c013b..6e96e9ae71 100644 --- a/source/renderer/backend/vulkan/SamplerManager.cpp +++ b/source/renderer/backend/vulkan/SamplerManager.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2025 Wildfire Games. +/* Copyright (C) 2026 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -141,6 +141,11 @@ VkSampler CSamplerManager::GetOrCreateSampler( return sampler; } +void CSamplerManager::CollectStatistics(IDevice::StatisticsVector& statistics) const +{ + statistics.emplace_back("Samplers count", "", static_cast(m_SamplerMap.size())); +} + } // namespace Vulkan } // namespace Backend diff --git a/source/renderer/backend/vulkan/SamplerManager.h b/source/renderer/backend/vulkan/SamplerManager.h index 8ce79390bf..aa6468cfa4 100644 --- a/source/renderer/backend/vulkan/SamplerManager.h +++ b/source/renderer/backend/vulkan/SamplerManager.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2025 Wildfire Games. +/* Copyright (C) 2026 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -18,6 +18,7 @@ #ifndef INCLUDED_RENDERER_BACKEND_VULKAN_SAMPLERMANAGER #define INCLUDED_RENDERER_BACKEND_VULKAN_SAMPLERMANAGER +#include "renderer/backend/IDevice.h" #include "renderer/backend/Sampler.h" #include @@ -52,6 +53,8 @@ public: */ VkSampler GetOrCreateSampler(const Sampler::Desc& samplerDesc); + void CollectStatistics(IDevice::StatisticsVector& statistics) const; + private: CDevice* m_Device = nullptr;