From 332bb7feeb0608d1946b582a7d199e5e7c8b689d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 11 Oct 2020 13:07:08 +0200 Subject: [PATCH] VK: Whenever safely possible, shrink the render area. We just set the render area to the union of the scissor rects used in a pass. Might help some games on some mobile hardware, a little bit. Possibly #13464? --- Common/GPU/Vulkan/VulkanQueueRunner.cpp | 24 ++++++-- Common/GPU/Vulkan/VulkanQueueRunner.h | 1 + Common/GPU/Vulkan/VulkanRenderManager.cpp | 27 +++++++-- Common/GPU/Vulkan/VulkanRenderManager.h | 69 ++++++++++++++++++++++- GPU/Vulkan/DebugVisVulkan.cpp | 4 +- GPU/Vulkan/DebugVisVulkan.h | 2 +- UI/EmuScreen.cpp | 2 +- 7 files changed, 115 insertions(+), 14 deletions(-) diff --git a/Common/GPU/Vulkan/VulkanQueueRunner.cpp b/Common/GPU/Vulkan/VulkanQueueRunner.cpp index a1c117932c12..2140824ab70a 100644 --- a/Common/GPU/Vulkan/VulkanQueueRunner.cpp +++ b/Common/GPU/Vulkan/VulkanQueueRunner.cpp @@ -633,7 +633,9 @@ std::string VulkanQueueRunner::StepToString(const VKRStep &step) const { { int w = step.render.framebuffer ? step.render.framebuffer->width : vulkan_->GetBackbufferWidth(); int h = step.render.framebuffer ? step.render.framebuffer->height : vulkan_->GetBackbufferHeight(); - snprintf(buffer, sizeof(buffer), "RENDER %s (draws: %d, %dx%d, fb: %p, )", step.tag, step.render.numDraws, w, h, step.render.framebuffer); + int actual_w = step.render.renderArea.extent.width; + int actual_h = step.render.renderArea.extent.height; + snprintf(buffer, sizeof(buffer), "RENDER %s (draws: %d, %dx%d/%dx%d, fb: %p, )", step.tag, step.render.numDraws, actual_w, actual_h, w, h, step.render.framebuffer); break; } case VKRStepType::COPY: @@ -1414,6 +1416,8 @@ void VulkanQueueRunner::PerformBindFramebufferAsRenderTarget(const VKRStep &step } } else { framebuf = backbuffer_; + + // Raw, rotated backbuffer size. w = vulkan_->GetBackbufferWidth(); h = vulkan_->GetBackbufferHeight(); renderPass = GetBackbufferRenderPass(); @@ -1426,10 +1430,20 @@ void VulkanQueueRunner::PerformBindFramebufferAsRenderTarget(const VKRStep &step VkRenderPassBeginInfo rp_begin = { VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO }; rp_begin.renderPass = renderPass; rp_begin.framebuffer = framebuf; - rp_begin.renderArea.offset.x = 0; - rp_begin.renderArea.offset.y = 0; - rp_begin.renderArea.extent.width = w; - rp_begin.renderArea.extent.height = h; + + VkRect2D rc = step.render.renderArea; + if (!step.render.framebuffer) { + // Rendering to backbuffer, must rotate, just like scissors. + DisplayRect rotated_rc{ rc.offset.x, rc.offset.y, (int)rc.extent.width, (int)rc.extent.height }; + RotateRectToDisplay(rotated_rc, vulkan_->GetBackbufferWidth(), vulkan_->GetBackbufferHeight()); + + rc.offset.x = rotated_rc.x; + rc.offset.y = rotated_rc.y; + rc.extent.width = rotated_rc.w; + rc.extent.height = rotated_rc.h; + } + + rp_begin.renderArea = rc; rp_begin.clearValueCount = numClearVals; rp_begin.pClearValues = numClearVals ? clearVal : nullptr; vkCmdBeginRenderPass(cmd, &rp_begin, VK_SUBPASS_CONTENTS_INLINE); diff --git a/Common/GPU/Vulkan/VulkanQueueRunner.h b/Common/GPU/Vulkan/VulkanQueueRunner.h index 4353a329811b..5a514a170c31 100644 --- a/Common/GPU/Vulkan/VulkanQueueRunner.h +++ b/Common/GPU/Vulkan/VulkanQueueRunner.h @@ -152,6 +152,7 @@ struct VKRStep { VkImageLayout finalColorLayout; VkImageLayout finalDepthStencilLayout; u32 pipelineFlags; + VkRect2D renderArea; } render; struct { VKRFramebuffer *src; diff --git a/Common/GPU/Vulkan/VulkanRenderManager.cpp b/Common/GPU/Vulkan/VulkanRenderManager.cpp index 8dcdcd969db9..e9bdf76103d3 100644 --- a/Common/GPU/Vulkan/VulkanRenderManager.cpp +++ b/Common/GPU/Vulkan/VulkanRenderManager.cpp @@ -260,8 +260,8 @@ void VulkanRenderManager::CreateBackbuffers() { if (InitDepthStencilBuffer(cmdInit)) { InitBackbufferFramebuffers(vulkan_->GetBackbufferWidth(), vulkan_->GetBackbufferHeight()); } - curWidth_ = -1; - curHeight_ = -1; + curWidthRaw_ = -1; + curHeightRaw_ = -1; if (HasBackbuffers()) { VLOG("Backbuffers Created"); @@ -529,6 +529,14 @@ void VulkanRenderManager::EndCurRenderStep() { // We'll often be able to avoid loading/saving the depth/stencil buffer. if (curRenderStep_) { curRenderStep_->render.pipelineFlags = curPipelineFlags_; + // We don't do this optimization for very small targets, probably not worth it. + if (!curRenderArea_.Empty() && (curWidth_ > 32 && curHeight_ > 32)) { + curRenderStep_->render.renderArea = curRenderArea_.ToVkRect2D(); + } else { + curRenderStep_->render.renderArea.offset = {}; + curRenderStep_->render.renderArea.extent = { (uint32_t)curWidth_, (uint32_t)curHeight_ }; + } + curRenderArea_.Reset(); // We no longer have a current render step. curRenderStep_ = nullptr; @@ -638,11 +646,20 @@ void VulkanRenderManager::BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRR curStepHasViewport_ = false; curStepHasScissor_ = false; if (fb) { + curWidthRaw_ = fb->width; + curHeightRaw_ = fb->height; curWidth_ = fb->width; curHeight_ = fb->height; } else { - curWidth_ = vulkan_->GetBackbufferWidth(); - curHeight_ = vulkan_->GetBackbufferHeight(); + curWidthRaw_ = vulkan_->GetBackbufferWidth(); + curHeightRaw_ = vulkan_->GetBackbufferHeight(); + if (g_display_rotation == DisplayRotation::ROTATE_90 || g_display_rotation == DisplayRotation::ROTATE_270) { + curWidth_ = curHeightRaw_; + curHeight_ = curWidthRaw_; + } else { + curWidth_ = curWidthRaw_; + curHeight_ = curHeightRaw_; + } } // See above - we add a clear afterward if only one side for depth/stencil CLEAR/KEEP. @@ -944,6 +961,8 @@ void VulkanRenderManager::Clear(uint32_t clearColor, float clearZ, int clearSten data.clear.clearMask = clearMask; curRenderStep_->commands.push_back(data); } + + curRenderArea_.SetRect(0, 0, curWidth_, curHeight_); } void VulkanRenderManager::CopyFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkOffset2D dstPos, VkImageAspectFlags aspectMask, const char *tag) { diff --git a/Common/GPU/Vulkan/VulkanRenderManager.h b/Common/GPU/Vulkan/VulkanRenderManager.h index 1aa2dc515b41..19fb6b1fb354 100644 --- a/Common/GPU/Vulkan/VulkanRenderManager.h +++ b/Common/GPU/Vulkan/VulkanRenderManager.h @@ -61,6 +61,53 @@ enum { MAX_TIMESTAMP_QUERIES = 128, }; +struct BoundingRect { + int x1; + int y1; + int x2; + int y2; + + BoundingRect() { + Reset(); + } + + void Reset() { + x1 = 65535; + y1 = 65535; + x2 = -65535; + y2 = -65535; + } + + bool Empty() const { + return x2 < 0; + } + + void SetRect(int x, int y, int width, int height) { + x1 = x; + y1 = y; + x2 = width; + y2 = height; + } + + void Apply(const VkRect2D &rect) { + if (rect.offset.x < x1) x1 = rect.offset.x; + if (rect.offset.y < y1) y1 = rect.offset.y; + int rect_x2 = rect.offset.x + rect.extent.width; + int rect_y2 = rect.offset.y + rect.extent.height; + if (rect_x2 > x2) x2 = rect_x2; + if (rect_y2 > y2) y2 = rect_y2; + } + + VkRect2D ToVkRect2D() const { + VkRect2D rect; + rect.offset.x = x1; + rect.offset.y = y1; + rect.extent.width = x2 - x1; + rect.extent.height = y2 - y1; + return rect; + } +}; + class VulkanRenderManager { public: VulkanRenderManager(VulkanContext *vulkan); @@ -131,10 +178,22 @@ class VulkanRenderManager { curStepHasViewport_ = true; } - void SetScissor(const VkRect2D &rc) { + void SetScissor(VkRect2D rc) { _dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER); _dbg_assert_((int)rc.extent.width >= 0); _dbg_assert_((int)rc.extent.height >= 0); + + // Clamp to curWidth_/curHeight_. Apparently an issue. + if ((int)(rc.offset.x + rc.extent.width) > curWidth_) { + rc.extent.width = curWidth_ - rc.offset.x; + } + if ((int)(rc.offset.y + rc.extent.height) > curHeight_) { + rc.extent.height = curHeight_ - rc.offset.y; + } + _dbg_assert_((int)(rc.offset.x + rc.extent.width) <= curWidth_); + _dbg_assert_((int)(rc.offset.y + rc.extent.height) <= curHeight_); + curRenderArea_.Apply(rc); + VkRenderData data{ VKRRenderCommand::SCISSOR }; data.scissor.scissor = rc; curRenderStep_->commands.push_back(data); @@ -323,8 +382,15 @@ class VulkanRenderManager { int outOfDateFrames_ = 0; // Submission time state + + // Note: These are raw backbuffer-sized. Rotated. + int curWidthRaw_ = -1; + int curHeightRaw_ = -1; + + // Pre-rotation (as you'd expect). int curWidth_ = -1; int curHeight_ = -1; + bool insideFrame_ = false; // This is the offset within this frame, in case of a mid-frame sync. int renderStepOffset_ = 0; @@ -332,6 +398,7 @@ class VulkanRenderManager { bool curStepHasViewport_ = false; bool curStepHasScissor_ = false; u32 curPipelineFlags_ = 0; + BoundingRect curRenderArea_; std::vector steps_; bool splitSubmit_ = false; diff --git a/GPU/Vulkan/DebugVisVulkan.cpp b/GPU/Vulkan/DebugVisVulkan.cpp index 7d74f571d90c..9d1365bb5f10 100644 --- a/GPU/Vulkan/DebugVisVulkan.cpp +++ b/GPU/Vulkan/DebugVisVulkan.cpp @@ -19,8 +19,8 @@ #include "Common/GPU/thin3d.h" #include "Common/UI/Context.h" #include "Common/UI/View.h" -#include "Common/System/System.h" #include "Common/System/Display.h" +#include "Common/System/System.h" #include "DebugVisVulkan.h" #include "Common/GPU/Vulkan/VulkanMemory.h" @@ -100,7 +100,7 @@ void DrawAllocatorVis(UIContext *ui, GPUInterface *gpu) { iter->Release(); } -void DrawProfilerVis(UIContext *ui, GPUInterface *gpu) { +void DrawGPUProfilerVis(UIContext *ui, GPUInterface *gpu) { if (!gpu) { return; } diff --git a/GPU/Vulkan/DebugVisVulkan.h b/GPU/Vulkan/DebugVisVulkan.h index b616d856f325..210771412a6d 100644 --- a/GPU/Vulkan/DebugVisVulkan.h +++ b/GPU/Vulkan/DebugVisVulkan.h @@ -24,4 +24,4 @@ class UIContext; // gpu MUST be an instance of GPU_Vulkan. If not, will definitely crash. void DrawAllocatorVis(UIContext *ui, GPUInterface *gpu); -void DrawProfilerVis(UIContext *ui, GPUInterface *gpu); +void DrawGPUProfilerVis(UIContext *ui, GPUInterface *gpu); diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index b3d11af15507..60a36c42e5ac 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -1574,7 +1574,7 @@ void EmuScreen::renderUI() { } if (g_Config.iGPUBackend == (int)GPUBackend::VULKAN && g_Config.bShowGpuProfile) { - DrawProfilerVis(ctx, gpu); + DrawGPUProfilerVis(ctx, gpu); } #endif