From a7b3a1eb96d3a39870e193c17fd5cb0e2c5f0f70 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 24 Dec 2017 10:31:25 -0800 Subject: [PATCH 1/3] Vulkan: Decimate the texture allocator. Thin3D wasn't calling Begin/End, which lead to leaks eventually and OOM. Was causing softgpu to crash. --- Common/Vulkan/VulkanMemory.cpp | 4 ++-- Common/Vulkan/VulkanMemory.h | 6 +++--- ext/native/thin3d/thin3d_vulkan.cpp | 2 ++ 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Common/Vulkan/VulkanMemory.cpp b/Common/Vulkan/VulkanMemory.cpp index a77fa7e878a3..d19467847e73 100644 --- a/Common/Vulkan/VulkanMemory.cpp +++ b/Common/Vulkan/VulkanMemory.cpp @@ -157,7 +157,7 @@ void VulkanPushBuffer::Unmap() { } VulkanDeviceAllocator::VulkanDeviceAllocator(VulkanContext *vulkan, size_t minSlabSize, size_t maxSlabSize) - : vulkan_(vulkan), lastSlab_(0), minSlabSize_(minSlabSize), maxSlabSize_(maxSlabSize), memoryTypeIndex_(UNDEFINED_MEMORY_TYPE), destroyed_(false) { + : vulkan_(vulkan), minSlabSize_(minSlabSize), maxSlabSize_(maxSlabSize) { assert((minSlabSize_ & (SLAB_GRAIN_SIZE - 1)) == 0); } @@ -290,7 +290,7 @@ int VulkanDeviceAllocator::ComputeUsagePercent() const { blocksUsed += slabs_[i].usage[j] != 0 ? 1 : 0; } } - return 100 * blocksUsed / blockSum; + return blockSum == 0 ? 0 : 100 * blocksUsed / blockSum; } void VulkanDeviceAllocator::Free(VkDeviceMemory deviceMemory, size_t offset) { diff --git a/Common/Vulkan/VulkanMemory.h b/Common/Vulkan/VulkanMemory.h index 94cd1e837dfe..5541f0d628ed 100644 --- a/Common/Vulkan/VulkanMemory.h +++ b/Common/Vulkan/VulkanMemory.h @@ -190,9 +190,9 @@ class VulkanDeviceAllocator { VulkanContext *const vulkan_; std::vector slabs_; - size_t lastSlab_; + size_t lastSlab_ = 0; size_t minSlabSize_; const size_t maxSlabSize_; - uint32_t memoryTypeIndex_; - bool destroyed_; + uint32_t memoryTypeIndex_ = UNDEFINED_MEMORY_TYPE; + bool destroyed_ = false; }; diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index d4437d7ab8d6..f8906188a7c1 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -779,6 +779,7 @@ void VKContext::BeginFrame() { // OK, we now know that nothing is reading from this frame's data pushbuffer, push_->Reset(); push_->Begin(vulkan_); + allocator_->Begin(); frame.descSets_.clear(); VkResult result = vkResetDescriptorPool(device_, frame.descriptorPool, 0); @@ -792,6 +793,7 @@ void VKContext::WaitRenderCompletion(Framebuffer *fbo) { void VKContext::EndFrame() { // Stop collecting data in the frame's data pushbuffer. push_->End(); + allocator_->End(); renderManager_.Finish(); From 206979fed61653061665f6b37c64076f956449d7 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 24 Dec 2017 11:05:52 -0800 Subject: [PATCH 2/3] Software: Execute bounding box tests. Fixes #10148. --- GPU/Software/SoftGpu.cpp | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/GPU/Software/SoftGpu.cpp b/GPU/Software/SoftGpu.cpp index c9d8c50bac90..d449c2d357a7 100644 --- a/GPU/Software/SoftGpu.cpp +++ b/GPU/Software/SoftGpu.cpp @@ -471,10 +471,33 @@ void SoftGPU::ExecuteOp(u32 op, u32 diff) { break; case GE_CMD_BOUNDINGBOX: - if (data != 0) + if (data == 0) { + currentList->bboxResult = false; + } else if (((data & 7) == 0) && data <= 64) { // Sanity check DEBUG_LOG(G3D, "Unsupported bounding box: %06x", data); - // bounding box test. Let's assume the box was within the drawing region. - currentList->bboxResult = true; + void *control_points = Memory::GetPointer(gstate_c.vertexAddr); + if (!control_points) { + ERROR_LOG_REPORT_ONCE(boundingbox, G3D, "Invalid verts in bounding box check"); + currentList->bboxResult = true; + return; + } + + if (gstate.vertType & GE_VTYPE_IDX_MASK) { + ERROR_LOG_REPORT_ONCE(boundingbox, G3D, "Indexed bounding box data not supported."); + // Data seems invalid. Let's assume the box test passed. + currentList->bboxResult = true; + return; + } + + // Test if the bounding box is within the drawing region. + int bytesRead; + currentList->bboxResult = drawEngineCommon_->TestBoundingBox(control_points, data, gstate.vertType, &bytesRead); + AdvanceVerts(gstate.vertType, data, bytesRead); + } else { + ERROR_LOG_REPORT_ONCE(boundingbox, G3D, "Bad bounding box data: %06x", data); + // Data seems invalid. Let's assume the box test passed. + currentList->bboxResult = true; + } break; case GE_CMD_VERTEXTYPE: From 0956fa3ff6e2dcc7aff606b51500cb2a96e6d4a8 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 24 Dec 2017 11:52:15 -0800 Subject: [PATCH 3/3] GPU: Reduce depth blits when not updated. If the game initially clears the depth in a buffer, but then never uses that depth again, don't keep blitting depth. Improves #8538, by preventing the depth blits in this case. --- GPU/Common/FramebufferCommon.cpp | 4 ++-- GPU/Common/FramebufferCommon.h | 6 ++++-- GPU/D3D11/FramebufferManagerD3D11.cpp | 1 + GPU/GLES/FramebufferManagerGLES.cpp | 2 +- GPU/Vulkan/FramebufferVulkan.cpp | 1 + 5 files changed, 9 insertions(+), 5 deletions(-) diff --git a/GPU/Common/FramebufferCommon.cpp b/GPU/Common/FramebufferCommon.cpp index 3a997f59aeb5..9478826b2877 100644 --- a/GPU/Common/FramebufferCommon.cpp +++ b/GPU/Common/FramebufferCommon.cpp @@ -440,7 +440,6 @@ VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(const Frame vfb->drawnFormat = params.fmt; vfb->usageFlags = FB_USAGE_RENDERTARGET; SetColorUpdated(vfb, skipDrawReason); - vfb->depthUpdated = false; u32 byteSize = FramebufferByteSize(vfb); u32 fb_address_mem = (params.fb_address & 0x3FFFFFFF) | 0x04000000; @@ -588,7 +587,8 @@ void FramebufferManagerCommon::NotifyRenderFramebufferSwitched(VirtualFramebuffe // Copy depth pixel value from the read framebuffer to the draw framebuffer if (prevVfb && !g_Config.bDisableSlowFramebufEffects) { - if (!prevVfb->fbo || !vfb->fbo || !useBufferedRendering_ || !prevVfb->depthUpdated || isClearingDepth) { + bool hasNewerDepth = prevVfb->last_frame_depth_render != 0 && prevVfb->last_frame_depth_render >= vfb->last_frame_depth_updated; + if (!prevVfb->fbo || !vfb->fbo || !useBufferedRendering_ || !hasNewerDepth || isClearingDepth) { // If depth wasn't updated, then we're at least "two degrees" away from the data. // This is an optimization: it probably doesn't need to be copied in this case. } else { diff --git a/GPU/Common/FramebufferCommon.h b/GPU/Common/FramebufferCommon.h index 926f3211c35d..13b8a840ec1b 100644 --- a/GPU/Common/FramebufferCommon.h +++ b/GPU/Common/FramebufferCommon.h @@ -79,9 +79,10 @@ struct VirtualFramebuffer { int last_frame_displayed; int last_frame_clut; int last_frame_failed; + int last_frame_depth_updated; + int last_frame_depth_render; u32 clutUpdatedBytes; bool memoryUpdated; - bool depthUpdated; bool firstFrameSaved; u32 fb_address; @@ -283,7 +284,8 @@ class FramebufferManagerCommon { void SetDepthUpdated() { if (currentRenderVfb_) { - currentRenderVfb_->depthUpdated = true; + currentRenderVfb_->last_frame_depth_render = gpuStats.numFlips; + currentRenderVfb_->last_frame_depth_updated = gpuStats.numFlips; } } void SetColorUpdated(int skipDrawReason) { diff --git a/GPU/D3D11/FramebufferManagerD3D11.cpp b/GPU/D3D11/FramebufferManagerD3D11.cpp index 075b39ae3033..4d29240d2ab6 100644 --- a/GPU/D3D11/FramebufferManagerD3D11.cpp +++ b/GPU/D3D11/FramebufferManagerD3D11.cpp @@ -539,6 +539,7 @@ void FramebufferManagerD3D11::BlitFramebufferDepth(VirtualFramebuffer *src, Virt // TODO: Currently, this copies depth AND stencil, which is a problem. See #9740. draw_->CopyFramebufferImage(src->fbo, 0, 0, 0, 0, dst->fbo, 0, 0, 0, 0, src->renderWidth, src->renderHeight, 1, Draw::FB_DEPTH_BIT); RebindFramebuffer(); + dst->last_frame_depth_updated = gpuStats.numFlips; } } diff --git a/GPU/GLES/FramebufferManagerGLES.cpp b/GPU/GLES/FramebufferManagerGLES.cpp index ff379d910e82..c51b454706be 100644 --- a/GPU/GLES/FramebufferManagerGLES.cpp +++ b/GPU/GLES/FramebufferManagerGLES.cpp @@ -529,7 +529,7 @@ void FramebufferManagerGLES::BlitFramebufferDepth(VirtualFramebuffer *src, Virtu // Let's only do this if not clearing depth. glstate.scissorTest.force(false); draw_->BlitFramebuffer(src->fbo, 0, 0, w, h, dst->fbo, 0, 0, w, h, Draw::FB_DEPTH_BIT, Draw::FB_BLIT_NEAREST); - // WARNING: If we set dst->depthUpdated here, our optimization above would be pointless. + dst->last_frame_depth_updated = gpuStats.numFlips; glstate.scissorTest.restore(); } } diff --git a/GPU/Vulkan/FramebufferVulkan.cpp b/GPU/Vulkan/FramebufferVulkan.cpp index 36fa27829115..f174ffeb3142 100644 --- a/GPU/Vulkan/FramebufferVulkan.cpp +++ b/GPU/Vulkan/FramebufferVulkan.cpp @@ -412,6 +412,7 @@ void FramebufferManagerVulkan::BlitFramebufferDepth(VirtualFramebuffer *src, Vir if (matchingDepthBuffer && matchingRenderSize && matchingSize) { // TODO: Currently, this copies depth AND stencil, which is a problem. See #9740. draw_->CopyFramebufferImage(src->fbo, 0, 0, 0, 0, dst->fbo, 0, 0, 0, 0, src->renderWidth, src->renderHeight, 1, Draw::FB_DEPTH_BIT); + dst->last_frame_depth_updated = gpuStats.numFlips; } else if (matchingDepthBuffer && matchingSize) { /* int w = std::min(src->renderWidth, dst->renderWidth);