diff --git a/Common/Vulkan/VulkanImage.cpp b/Common/Vulkan/VulkanImage.cpp index 256d6791ce..3d7ce8ae87 100644 --- a/Common/Vulkan/VulkanImage.cpp +++ b/Common/Vulkan/VulkanImage.cpp @@ -1,5 +1,6 @@ #include "Common/Vulkan/VulkanImage.h" #include "Common/Vulkan/VulkanMemory.h" +#include "Common/Log.h" VkResult VulkanTexture::Create(int w, int h, VkFormat format) { tex_width = w; @@ -294,6 +295,7 @@ bool VulkanTexture::CreateDirect(VkCommandBuffer cmd, int w, int h, int numMips, res = vkAllocateMemory(vulkan_->GetDevice(), &mem_alloc, NULL, &mem); if (res != VK_SUCCESS) { + _assert_msg_(G3D, res != VK_ERROR_TOO_MANY_OBJECTS, "Too many Vulkan memory objects!"); assert(res == VK_ERROR_OUT_OF_HOST_MEMORY || res == VK_ERROR_OUT_OF_DEVICE_MEMORY || res == VK_ERROR_TOO_MANY_OBJECTS); return false; } diff --git a/Common/Vulkan/VulkanImage.h b/Common/Vulkan/VulkanImage.h index 26f51cc2dd..a6193f38ad 100644 --- a/Common/Vulkan/VulkanImage.h +++ b/Common/Vulkan/VulkanImage.h @@ -8,7 +8,7 @@ class VulkanDeviceAllocator; // Not very optimal - if you have many small textures you should use other strategies. class VulkanTexture { public: - VulkanTexture(VulkanContext *vulkan, VulkanDeviceAllocator *allocator = nullptr) + VulkanTexture(VulkanContext *vulkan, VulkanDeviceAllocator *allocator) : vulkan_(vulkan), image(VK_NULL_HANDLE), mem(VK_NULL_HANDLE), view(VK_NULL_HANDLE), tex_width(0), tex_height(0), numMips_(1), format_(VK_FORMAT_UNDEFINED), mappableImage(VK_NULL_HANDLE), mappableMemory(VK_NULL_HANDLE), diff --git a/Common/Vulkan/VulkanMemory.h b/Common/Vulkan/VulkanMemory.h index 15f09d1265..f0b7dfb6f0 100644 --- a/Common/Vulkan/VulkanMemory.h +++ b/Common/Vulkan/VulkanMemory.h @@ -146,6 +146,10 @@ public: static const size_t ALLOCATE_FAILED = -1; + int GetBlockCount() const { return (int)slabs_.size(); } + int GetMinSlabSize() const { return (int)minSlabSize_; } + int GetMaxSlabSize() const { return (int)maxSlabSize_; } + private: static const size_t SLAB_GRAIN_SIZE = 1024; static const uint8_t SLAB_GRAIN_SHIFT = 10; diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index 164c77263c..3fd12ba85e 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -296,7 +296,7 @@ void DrawEngineVulkan::BeginFrame() { if (!nullTexture_) { ILOG("INIT : Creating null texture"); VkCommandBuffer cmdInit = (VkCommandBuffer)draw_->GetNativeObject(Draw::NativeObject::INIT_COMMANDBUFFER); - nullTexture_ = new VulkanTexture(vulkan_); + nullTexture_ = new VulkanTexture(vulkan_, textureCache_->GetAllocator()); int w = 8; int h = 8; nullTexture_->CreateDirect(cmdInit, w, h, 1, VK_FORMAT_A8B8G8R8_UNORM_PACK32, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, diff --git a/GPU/Vulkan/FramebufferVulkan.cpp b/GPU/Vulkan/FramebufferVulkan.cpp index 018a0ed9b7..cd6c904331 100644 --- a/GPU/Vulkan/FramebufferVulkan.cpp +++ b/GPU/Vulkan/FramebufferVulkan.cpp @@ -205,7 +205,8 @@ void FramebufferManagerVulkan::MakePixelTexture(const u8 *srcPixels, GEBufferFor VkCommandBuffer initCmd = (VkCommandBuffer)draw_->GetNativeObject(Draw::NativeObject::INIT_COMMANDBUFFER); - drawPixelsTex_ = new VulkanTexture(vulkan_); + // There's only ever a few of these alive, don't need to stress the allocator with these big ones. + drawPixelsTex_ = new VulkanTexture(vulkan_, nullptr); if (!drawPixelsTex_->CreateDirect(initCmd, width, height, 1, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT)) { // out of memory? delete drawPixelsTex_; diff --git a/GPU/Vulkan/GPU_Vulkan.cpp b/GPU/Vulkan/GPU_Vulkan.cpp index a862bee967..c89c589c82 100644 --- a/GPU/Vulkan/GPU_Vulkan.cpp +++ b/GPU/Vulkan/GPU_Vulkan.cpp @@ -638,6 +638,8 @@ void GPU_Vulkan::DeviceRestore() { void GPU_Vulkan::GetStats(char *buffer, size_t bufsize) { const DrawEngineVulkanStats &drawStats = drawEngine_.GetStats(); + char texStats[256]; + textureCacheVulkan_->GetStats(texStats, sizeof(texStats)); float vertexAverageCycles = gpuStats.numVertsSubmitted > 0 ? (float)gpuStats.vertexGPUCycles / (float)gpuStats.numVertsSubmitted : 0.0f; snprintf(buffer, bufsize - 1, "DL processing time: %0.2f ms\n" @@ -652,7 +654,8 @@ void GPU_Vulkan::GetStats(char *buffer, size_t bufsize) { "Textures active: %i, decoded: %i invalidated: %i\n" "Readbacks: %d\n" "Vertex, Fragment, Pipelines loaded: %i, %i, %i\n" - "Pushbuffer space used: UBO %d, Vtx %d, Idx %d\n", + "Pushbuffer space used: UBO %d, Vtx %d, Idx %d\n" + "%s\n", gpuStats.msProcessingDisplayLists * 1000.0f, gpuStats.numDrawCalls, gpuStats.numFlushes, @@ -674,7 +677,8 @@ void GPU_Vulkan::GetStats(char *buffer, size_t bufsize) { pipelineManager_->GetNumPipelines(), drawStats.pushUBOSpaceUsed, drawStats.pushVertexSpaceUsed, - drawStats.pushIndexSpaceUsed + drawStats.pushIndexSpaceUsed, + texStats ); } diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index 0f01b3fddb..518eb9f7e3 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -816,6 +816,10 @@ bool TextureCacheVulkan::GetCurrentTextureDebug(GPUDebugBuffer &buffer, int leve // So let's dirty the things that are involved in Vulkan dynamic state. Readbacks are not frequent so this won't hurt other backends. gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE); framebufferManager_->RebindFramebuffer(); - return true; } + +void TextureCacheVulkan::GetStats(char *ptr, size_t size) { + snprintf(ptr, size, "Alloc: %d blocks\nSlab min/max: %d/%d\n", + allocator_->GetBlockCount(), allocator_->GetMinSlabSize(), allocator_->GetMaxSlabSize()); +} diff --git a/GPU/Vulkan/TextureCacheVulkan.h b/GPU/Vulkan/TextureCacheVulkan.h index f06bd8b614..30ca6cd166 100644 --- a/GPU/Vulkan/TextureCacheVulkan.h +++ b/GPU/Vulkan/TextureCacheVulkan.h @@ -107,6 +107,10 @@ public: bool GetCurrentTextureDebug(GPUDebugBuffer &buffer, int level) override; + void GetStats(char *ptr, size_t size); + + VulkanDeviceAllocator *GetAllocator() { return allocator_; } + protected: void BindTexture(TexCacheEntry *entry) override; void Unbind() override; diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index 13ddf77678..d16c4e9230 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -312,9 +312,9 @@ struct DescriptorSetKey { class VKTexture : public Texture { public: - VKTexture(VulkanContext *vulkan, VkCommandBuffer cmd, const TextureDesc &desc) + VKTexture(VulkanContext *vulkan, VkCommandBuffer cmd, const TextureDesc &desc, VulkanDeviceAllocator *alloc) : vulkan_(vulkan), mipLevels_(desc.mipLevels), format_(desc.format) { - bool result = Create(cmd, desc); + bool result = Create(cmd, desc, alloc); assert(result); } @@ -327,7 +327,7 @@ public: private: void SetImageData(VkCommandBuffer cmd, int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data); - bool Create(VkCommandBuffer cmd, const TextureDesc &desc); + bool Create(VkCommandBuffer cmd, const TextureDesc &desc, VulkanDeviceAllocator *alloc); void Destroy() { if (vkTex_) { @@ -497,6 +497,8 @@ private: VulkanRenderManager renderManager_; + VulkanDeviceAllocator *allocator_ = nullptr; + VKPipeline *curPipeline_ = nullptr; VKBuffer *curVBuffers_[4]{}; int curVBufferOffsets_[4]{}; @@ -657,7 +659,7 @@ enum class TextureState { PENDING_DESTRUCTION, }; -bool VKTexture::Create(VkCommandBuffer cmd, const TextureDesc &desc) { +bool VKTexture::Create(VkCommandBuffer cmd, const TextureDesc &desc, VulkanDeviceAllocator *alloc) { // Zero-sized textures not allowed. if (desc.width * desc.height * desc.depth == 0) return false; @@ -666,7 +668,7 @@ bool VKTexture::Create(VkCommandBuffer cmd, const TextureDesc &desc) { width_ = desc.width; height_ = desc.height; depth_ = desc.depth; - vkTex_ = new VulkanTexture(vulkan_); + vkTex_ = new VulkanTexture(vulkan_, alloc); if (desc.initData.size()) { for (int i = 0; i < (int)desc.initData.size(); i++) { this->SetImageData(cmd, 0, 0, 0, width_, height_, depth_, i, 0, desc.initData[i]); @@ -758,9 +760,12 @@ VKContext::VKContext(VulkanContext *vulkan, bool splitSubmit) pipelineCache_ = vulkan_->CreatePipelineCache(); renderManager_.SetSplitSubmit(splitSubmit); + + allocator_ = new VulkanDeviceAllocator(vulkan_, 256 * 1024, 2048 * 1024); } VKContext::~VKContext() { + delete allocator_; // This also destroys all descriptor sets. for (int i = 0; i < VulkanContext::MAX_INFLIGHT_FRAMES; i++) { frame_[i].descSets_.clear(); @@ -996,7 +1001,7 @@ InputLayout *VKContext::CreateInputLayout(const InputLayoutDesc &desc) { } Texture *VKContext::CreateTexture(const TextureDesc &desc) { - return new VKTexture(vulkan_, renderManager_.GetInitCmd(), desc); + return new VKTexture(vulkan_, renderManager_.GetInitCmd(), desc, allocator_); } void VKTexture::SetImageData(VkCommandBuffer cmd, int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data) {