diff --git a/CMakeLists.txt b/CMakeLists.txt index b0228ab24b..945dcadbe4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -631,6 +631,8 @@ add_library(Common STATIC Common/File/FileDescriptor.h Common/GPU/DataFormat.h Common/GPU/MiscTypes.h + Common/GPU/GPUBackendCommon.cpp + Common/GPU/GPUBackendCommon.h Common/GPU/thin3d.cpp Common/GPU/thin3d.h Common/GPU/thin3d_create.h diff --git a/Common/Common.vcxproj b/Common/Common.vcxproj index 3ecf5c41ed..2b5d570753 100644 --- a/Common/Common.vcxproj +++ b/Common/Common.vcxproj @@ -450,6 +450,7 @@ + @@ -894,6 +895,7 @@ + diff --git a/Common/Common.vcxproj.filters b/Common/Common.vcxproj.filters index 719cbe3e64..bd6cb81587 100644 --- a/Common/Common.vcxproj.filters +++ b/Common/Common.vcxproj.filters @@ -449,9 +449,6 @@ Render - - GPU - GPU\Vulkan @@ -503,6 +500,12 @@ GPU\OpenGL + + GPU + + + GPU + @@ -941,6 +944,9 @@ Data\Collections + + GPU + @@ -1089,4 +1095,4 @@ ext\basis_universal - + \ No newline at end of file diff --git a/Common/GPU/GPUBackendCommon.cpp b/Common/GPU/GPUBackendCommon.cpp new file mode 100644 index 0000000000..b53001484d --- /dev/null +++ b/Common/GPU/GPUBackendCommon.cpp @@ -0,0 +1,28 @@ +#include +#include + +#include "Common/GPU/GPUBackendCommon.h" + +// Global push buffer tracker for GPU memory profiling. +// Don't want to manually dig up all the active push buffers. +static std::mutex g_pushBufferListMutex; +static std::set g_pushBuffers; + +std::vector GetActiveGPUMemoryManagers() { + std::vector buffers; + std::lock_guard guard(g_pushBufferListMutex); + for (auto iter : g_pushBuffers) { + buffers.push_back(iter); + } + return buffers; +} + +void RegisterGPUMemoryManager(GPUMemoryManager *manager) { + std::lock_guard guard(g_pushBufferListMutex); + g_pushBuffers.insert(manager); +} + +void UnregisterGPUMemoryManager(GPUMemoryManager *manager) { + std::lock_guard guard(g_pushBufferListMutex); + g_pushBuffers.erase(manager); +} diff --git a/Common/GPU/GPUBackendCommon.h b/Common/GPU/GPUBackendCommon.h new file mode 100644 index 0000000000..e465b75df4 --- /dev/null +++ b/Common/GPU/GPUBackendCommon.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +// Just an abstract thing to get debug information. +class GPUMemoryManager { +public: + virtual ~GPUMemoryManager() {} + + virtual void GetDebugString(char *buffer, size_t bufSize) const = 0; + virtual const char *Name() const = 0; // for sorting +}; + +std::vector GetActiveGPUMemoryManagers(); + +void RegisterGPUMemoryManager(GPUMemoryManager *manager); +void UnregisterGPUMemoryManager(GPUMemoryManager *manager); diff --git a/Common/GPU/OpenGL/GLMemory.cpp b/Common/GPU/OpenGL/GLMemory.cpp index 9bd61fb497..8680a82a54 100644 --- a/Common/GPU/OpenGL/GLMemory.cpp +++ b/Common/GPU/OpenGL/GLMemory.cpp @@ -2,6 +2,7 @@ #include "Common/GPU/OpenGL/GLMemory.h" #include "Common/GPU/OpenGL/GLRenderManager.h" #include "Common/GPU/OpenGL/GLFeatures.h" +#include "Common/Data/Text/Parsers.h" extern std::thread::id renderThreadId; #if MAX_LOGLEVEL >= DEBUG_LEVEL @@ -64,9 +65,11 @@ bool GLRBuffer::Unmap() { GLPushBuffer::GLPushBuffer(GLRenderManager *render, GLuint target, size_t size, const char *tag) : render_(render), size_(size), target_(target), tag_(tag) { bool res = AddBuffer(); _assert_(res); + RegisterGPUMemoryManager(this); } GLPushBuffer::~GLPushBuffer() { + UnregisterGPUMemoryManager(this); Destroy(true); } @@ -279,3 +282,7 @@ void GLPushBuffer::UnmapDevice() { } } } + +void GLPushBuffer::GetDebugString(char *buffer, size_t bufSize) const { + snprintf(buffer, bufSize, "%s: %d/%d", tag_, NiceSizeFormat(this->offset_).c_str(), NiceSizeFormat(this->size_).c_str()); +} diff --git a/Common/GPU/OpenGL/GLMemory.h b/Common/GPU/OpenGL/GLMemory.h index 835188f41a..0c28aee6de 100644 --- a/Common/GPU/OpenGL/GLMemory.h +++ b/Common/GPU/OpenGL/GLMemory.h @@ -4,6 +4,7 @@ #include #include +#include "Common/GPU/GPUBackendCommon.h" #include "Common/GPU/OpenGL/GLCommon.h" #include "Common/Log.h" @@ -61,7 +62,7 @@ class GLRenderManager; // trouble. // We need to manage the lifetime of this together with the other resources so its destructor // runs on the render thread. -class GLPushBuffer { +class GLPushBuffer : public GPUMemoryManager { public: friend class GLRenderManager; @@ -78,6 +79,10 @@ public: void Reset() { offset_ = 0; } + void GetDebugString(char *buffer, size_t bufSize) const override; + + const char *Name() const override { return tag_; }; // for sorting + // Utility for users of this class, not used internally. const uint32_t INVALID_OFFSET = 0xFFFFFFFF; diff --git a/Common/GPU/Vulkan/VulkanMemory.cpp b/Common/GPU/Vulkan/VulkanMemory.cpp index 1a718c95a7..910288ff3a 100644 --- a/Common/GPU/Vulkan/VulkanMemory.cpp +++ b/Common/GPU/Vulkan/VulkanMemory.cpp @@ -35,37 +35,15 @@ using namespace PPSSPP_VK; // Always keep around push buffers at least this long (seconds). static const double PUSH_GARBAGE_COLLECTION_DELAY = 10.0; -// Global push buffer tracker for vulkan memory profiling. -// Don't want to manually dig up all the active push buffers. -static std::mutex g_pushBufferListMutex; -static std::set g_pushBuffers; - -std::vector GetActiveVulkanMemoryManagers() { - std::vector buffers; - std::lock_guard guard(g_pushBufferListMutex); - for (auto iter : g_pushBuffers) { - buffers.push_back(iter); - } - return buffers; -} - VulkanPushBuffer::VulkanPushBuffer(VulkanContext *vulkan, const char *name, size_t size, VkBufferUsageFlags usage) : vulkan_(vulkan), name_(name), size_(size), usage_(usage) { - { - std::lock_guard guard(g_pushBufferListMutex); - g_pushBuffers.insert(this); - } - + RegisterGPUMemoryManager(this); bool res = AddBuffer(); _assert_(res); } VulkanPushBuffer::~VulkanPushBuffer() { - { - std::lock_guard guard(g_pushBufferListMutex); - g_pushBuffers.erase(this); - } - + UnregisterGPUMemoryManager(this); _dbg_assert_(!writePtr_); _assert_(buffers_.empty()); } @@ -276,11 +254,7 @@ VkResult VulkanDescSetPool::Recreate(bool grow) { VulkanPushPool::VulkanPushPool(VulkanContext *vulkan, const char *name, size_t originalBlockSize, VkBufferUsageFlags usage) : vulkan_(vulkan), name_(name), originalBlockSize_(originalBlockSize), usage_(usage) { - { - std::lock_guard guard(g_pushBufferListMutex); - g_pushBuffers.insert(this); - } - + RegisterGPUMemoryManager(this); for (int i = 0; i < VulkanContext::MAX_INFLIGHT_FRAMES; i++) { blocks_.push_back(CreateBlock(originalBlockSize)); blocks_.back().original = true; @@ -289,11 +263,7 @@ VulkanPushPool::VulkanPushPool(VulkanContext *vulkan, const char *name, size_t o } VulkanPushPool::~VulkanPushPool() { - { - std::lock_guard guard(g_pushBufferListMutex); - g_pushBuffers.erase(this); - } - + UnregisterGPUMemoryManager(this); _dbg_assert_(blocks_.empty()); } diff --git a/Common/GPU/Vulkan/VulkanMemory.h b/Common/GPU/Vulkan/VulkanMemory.h index 766f497a79..53f96ae689 100644 --- a/Common/GPU/Vulkan/VulkanMemory.h +++ b/Common/GPU/Vulkan/VulkanMemory.h @@ -6,6 +6,7 @@ #include #include "Common/GPU/Vulkan/VulkanContext.h" +#include "Common/GPU/GPUBackendCommon.h" // Forward declaration VK_DEFINE_HANDLE(VmaAllocation); @@ -14,22 +15,13 @@ VK_DEFINE_HANDLE(VmaAllocation); // // Vulkan memory management utils. -// Just an abstract thing to get debug information. -class VulkanMemoryManager { -public: - virtual ~VulkanMemoryManager() {} - - virtual void GetDebugString(char *buffer, size_t bufSize) const = 0; - virtual const char *Name() const = 0; // for sorting -}; - // VulkanPushBuffer // Simple incrementing allocator. // Use these to push vertex, index and uniform data. Generally you'll have two or three of these // and alternate on each frame. Make sure not to reset until the fence from the last time you used it // has completed. // NOTE: This has now been replaced with VulkanPushPool for all uses except the vertex cache. -class VulkanPushBuffer : public VulkanMemoryManager { +class VulkanPushBuffer : public GPUMemoryManager { struct BufInfo { VkBuffer buffer; VmaAllocation allocation; @@ -108,7 +100,7 @@ private: // Simple memory pushbuffer pool that can share blocks between the "frames", to reduce the impact of push memory spikes - // a later frame can gobble up redundant buffers from an earlier frame even if they don't share frame index. // NOT thread safe! Can only be used from one thread (our main thread). -class VulkanPushPool : public VulkanMemoryManager { +class VulkanPushPool : public GPUMemoryManager { public: VulkanPushPool(VulkanContext *vulkan, const char *name, size_t originalBlockSize, VkBufferUsageFlags usage); ~VulkanPushPool(); @@ -212,5 +204,3 @@ private: bool grow_; }; -std::vector GetActiveVulkanMemoryManagers(); - diff --git a/GPU/Vulkan/DebugVisVulkan.cpp b/GPU/Vulkan/DebugVisVulkan.cpp index 23994d4e4e..7fa7b55fe0 100644 --- a/GPU/Vulkan/DebugVisVulkan.cpp +++ b/GPU/Vulkan/DebugVisVulkan.cpp @@ -30,6 +30,7 @@ #include "ext/vma/vk_mem_alloc.h" #include "DebugVisVulkan.h" +#include "Common/GPU/GPUBackendCommon.h" #include "Common/GPU/Vulkan/VulkanMemory.h" #include "Common/GPU/Vulkan/VulkanImage.h" #include "Common/Data/Text/Parsers.h" @@ -41,7 +42,7 @@ #undef DrawText -bool comparePushBufferNames(const VulkanMemoryManager *a, const VulkanMemoryManager *b) { +bool comparePushBufferNames(const GPUMemoryManager *a, const GPUMemoryManager *b) { return strcmp(a->Name(), b->Name()) < 0; } @@ -49,35 +50,34 @@ void DrawGPUMemoryVis(UIContext *ui, GPUInterface *gpu) { // This one will simply display stats. Draw::DrawContext *draw = ui->GetDrawContext(); - VulkanContext *vulkan = (VulkanContext *)draw->GetNativeObject(Draw::NativeObject::CONTEXT); - if (!vulkan) { - return; - } - - VmaTotalStatistics vmaStats; - vmaCalculateStatistics(vulkan->Allocator(), &vmaStats); - - std::vector budgets; - budgets.resize(vulkan->GetMemoryProperties().memoryHeapCount); - vmaGetHeapBudgets(vulkan->Allocator(), &budgets[0]); - - size_t totalBudget = 0; - size_t totalUsedBytes = 0; - for (auto &budget : budgets) { - totalBudget += budget.budget; - totalUsedBytes += budget.usage; - } - std::stringstream str; - str << vulkan->GetPhysicalDeviceProperties().properties.deviceName << std::endl; - str << "Allocated " << NiceSizeFormat(vmaStats.total.statistics.allocationBytes) << " in " << vmaStats.total.statistics.allocationCount << " allocs" << std::endl; - // Note: The overall number includes stuff like descriptor sets pools and other things that are not directly visible as allocations. - str << "Overall " << NiceSizeFormat(totalUsedBytes) << " used out of " << NiceSizeFormat(totalBudget) << " available" << std::endl; + + VulkanContext *vulkan = (VulkanContext *)draw->GetNativeObject(Draw::NativeObject::CONTEXT); + if (vulkan) { + VmaTotalStatistics vmaStats; + vmaCalculateStatistics(vulkan->Allocator(), &vmaStats); + + std::vector budgets; + budgets.resize(vulkan->GetMemoryProperties().memoryHeapCount); + vmaGetHeapBudgets(vulkan->Allocator(), &budgets[0]); + + size_t totalBudget = 0; + size_t totalUsedBytes = 0; + for (auto &budget : budgets) { + totalBudget += budget.budget; + totalUsedBytes += budget.usage; + } + + str << vulkan->GetPhysicalDeviceProperties().properties.deviceName << std::endl; + str << "Allocated " << NiceSizeFormat(vmaStats.total.statistics.allocationBytes) << " in " << vmaStats.total.statistics.allocationCount << " allocs" << std::endl; + // Note: The overall number includes stuff like descriptor sets pools and other things that are not directly visible as allocations. + str << "Overall " << NiceSizeFormat(totalUsedBytes) << " used out of " << NiceSizeFormat(totalBudget) << " available" << std::endl; + } str << "Push buffers:" << std::endl; // Now list the various push buffers. - auto managers = GetActiveVulkanMemoryManagers(); + auto managers = GetActiveGPUMemoryManagers(); std::sort(managers.begin(), managers.end(), comparePushBufferNames); char buffer[512]; diff --git a/UI/DevScreens.cpp b/UI/DevScreens.cpp index b9e739239a..36aaf564f7 100644 --- a/UI/DevScreens.cpp +++ b/UI/DevScreens.cpp @@ -100,8 +100,8 @@ void DevMenuScreen::CreatePopupContents(UI::ViewGroup *parent) { items->Add(new Choice(sy->T("Developer Tools")))->OnClick.Handle(this, &DevMenuScreen::OnDeveloperTools); items->Add(new Choice(dev->T("Jit Compare")))->OnClick.Handle(this, &DevMenuScreen::OnJitCompare); items->Add(new Choice(dev->T("Shader Viewer")))->OnClick.Handle(this, &DevMenuScreen::OnShaderView); - if (g_Config.iGPUBackend == (int)GPUBackend::VULKAN) { - items->Add(new CheckBox(&g_Config.bShowAllocatorDebug, dev->T("Allocator Viewer"))); + if (g_Config.iGPUBackend == (int)GPUBackend::VULKAN || g_Config.iGPUBackend == (int)GPUBackend::OPENGL) { + items->Add(new CheckBox(&g_Config.bShowAllocatorDebug, dev->T("GPU Allocator Viewer"))); } if (g_Config.iGPUBackend == (int)GPUBackend::VULKAN || g_Config.iGPUBackend == (int)GPUBackend::OPENGL) { items->Add(new CheckBox(&g_Config.bShowGpuProfile, dev->T("GPU Profile"))); diff --git a/UWP/CommonUWP/CommonUWP.vcxproj b/UWP/CommonUWP/CommonUWP.vcxproj index 353aa305bb..bc4f8a41f5 100644 --- a/UWP/CommonUWP/CommonUWP.vcxproj +++ b/UWP/CommonUWP/CommonUWP.vcxproj @@ -287,6 +287,7 @@ + @@ -443,6 +444,7 @@ + diff --git a/UWP/CommonUWP/CommonUWP.vcxproj.filters b/UWP/CommonUWP/CommonUWP.vcxproj.filters index 18faba3ae6..feefa90d95 100644 --- a/UWP/CommonUWP/CommonUWP.vcxproj.filters +++ b/UWP/CommonUWP/CommonUWP.vcxproj.filters @@ -435,6 +435,9 @@ File + + GPU + @@ -823,6 +826,9 @@ File + + GPU + diff --git a/android/jni/Android.mk b/android/jni/Android.mk index 05d45cac5e..f28625f35b 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -189,6 +189,7 @@ EXEC_AND_LIB_FILES := \ $(SRC)/Common/File/DirListing.cpp \ $(SRC)/Common/File/FileDescriptor.cpp \ $(SRC)/Common/GPU/thin3d.cpp \ + $(SRC)/Common/GPU/GPUBackendCommon.cpp \ $(SRC)/Common/GPU/Shader.cpp \ $(SRC)/Common/GPU/ShaderWriter.cpp \ $(SRC)/Common/GPU/ShaderTranslation.cpp \ diff --git a/libretro/Makefile.common b/libretro/Makefile.common index 2ad3b8a43d..ac0d81c09c 100644 --- a/libretro/Makefile.common +++ b/libretro/Makefile.common @@ -286,6 +286,7 @@ SOURCES_CXX += \ $(COMMONDIR)/File/DirListing.cpp \ $(COMMONDIR)/GPU/thin3d.cpp \ $(COMMONDIR)/GPU/Shader.cpp \ + $(COMMONDIR)/GPU/GPUBackendCommon.cpp \ $(COMMONDIR)/GPU/ShaderWriter.cpp \ $(COMMONDIR)/GPU/ShaderTranslation.cpp \ $(COMMONDIR)/GPU/OpenGL/thin3d_gl.cpp \