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?
This commit is contained in:
Henrik Rydgård 2020-10-11 13:07:08 +02:00
parent 495fd9a13c
commit 332bb7feeb
7 changed files with 115 additions and 14 deletions

View file

@ -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<int> 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);

View file

@ -152,6 +152,7 @@ struct VKRStep {
VkImageLayout finalColorLayout;
VkImageLayout finalDepthStencilLayout;
u32 pipelineFlags;
VkRect2D renderArea;
} render;
struct {
VKRFramebuffer *src;

View file

@ -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) {

View file

@ -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 @@ public:
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 @@ private:
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 @@ private:
bool curStepHasViewport_ = false;
bool curStepHasScissor_ = false;
u32 curPipelineFlags_ = 0;
BoundingRect curRenderArea_;
std::vector<VKRStep *> steps_;
bool splitSubmit_ = false;

View file

@ -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;
}

View file

@ -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);

View file

@ -1574,7 +1574,7 @@ void EmuScreen::renderUI() {
}
if (g_Config.iGPUBackend == (int)GPUBackend::VULKAN && g_Config.bShowGpuProfile) {
DrawProfilerVis(ctx, gpu);
DrawGPUProfilerVis(ctx, gpu);
}
#endif