Skip to content

Commit

Permalink
Merge pull request #16072 from hrydgard/depth-free-renderpass
Browse files Browse the repository at this point in the history
Vulkan: Don't have renderpasses store/load depth buffers when we don't use them
  • Loading branch information
hrydgard committed Sep 22, 2022
2 parents c108db0 + 11b8078 commit c3cbb68
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 57 deletions.
71 changes: 38 additions & 33 deletions Common/GPU/Vulkan/VulkanQueueRunner.cpp
Expand Up @@ -30,16 +30,14 @@ static void MergeRenderAreaRectInto(VkRect2D *dest, VkRect2D &src) {
// We need to take the "max" of the features used in the two render passes.
RenderPassType MergeRPTypes(RenderPassType a, RenderPassType b) {
// Either both are backbuffer type, or neither are.
_dbg_assert_((a == RP_TYPE_BACKBUFFER) == (b == RP_TYPE_BACKBUFFER));
if (a == b) {
// Trivial merging case.
// These can't merge with other renderpasses
if (a == RP_TYPE_BACKBUFFER || b == RP_TYPE_BACKBUFFER) {
_dbg_assert_(a == b);
return a;
} else if (a == RP_TYPE_COLOR_DEPTH && b == RP_TYPE_COLOR_DEPTH_INPUT) {
return RP_TYPE_COLOR_DEPTH_INPUT;
} else if (a == RP_TYPE_COLOR_DEPTH_INPUT && b == RP_TYPE_COLOR_DEPTH) {
return RP_TYPE_COLOR_DEPTH_INPUT;
}
return a;

// The rest we can just OR together to get the maximum feature set.
return (RenderPassType)((u32)a | (u32)b);
}

void VulkanQueueRunner::CreateDeviceObjects() {
Expand Down Expand Up @@ -326,29 +324,33 @@ static VkAttachmentStoreOp ConvertStoreAction(VKRRenderPassStoreAction action) {
// Self-dependency: https://github.com/gpuweb/gpuweb/issues/442#issuecomment-547604827
// Also see https://www.khronos.org/registry/vulkan/specs/1.3-extensions/html/vkspec.html#synchronization-pipeline-barriers-subpass-self-dependencies

VkRenderPass CreateRP(VulkanContext *vulkan, const RPKey &key, RenderPassType rpType) {
bool selfDependency = rpType == RP_TYPE_COLOR_DEPTH_INPUT;
VkRenderPass CreateRenderPass(VulkanContext *vulkan, const RPKey &key, RenderPassType rpType) {
bool selfDependency = rpType == RP_TYPE_COLOR_INPUT || rpType == RP_TYPE_COLOR_DEPTH_INPUT;
bool isBackbuffer = rpType == RP_TYPE_BACKBUFFER;
bool hasDepth = rpType == RP_TYPE_BACKBUFFER || rpType == RP_TYPE_COLOR_DEPTH || rpType == RP_TYPE_COLOR_DEPTH_INPUT;

VkAttachmentDescription attachments[2] = {};
attachments[0].format = rpType == RP_TYPE_BACKBUFFER ? vulkan->GetSwapchainFormat() : VK_FORMAT_R8G8B8A8_UNORM;
attachments[0].format = isBackbuffer ? vulkan->GetSwapchainFormat() : VK_FORMAT_R8G8B8A8_UNORM;
attachments[0].samples = VK_SAMPLE_COUNT_1_BIT;
attachments[0].loadOp = ConvertLoadAction(key.colorLoadAction);
attachments[0].storeOp = ConvertStoreAction(key.colorStoreAction);
attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attachments[0].initialLayout = rpType == RP_TYPE_BACKBUFFER ? VK_IMAGE_LAYOUT_UNDEFINED : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
attachments[0].finalLayout = rpType == RP_TYPE_BACKBUFFER ? VK_IMAGE_LAYOUT_PRESENT_SRC_KHR : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
attachments[0].initialLayout = isBackbuffer ? VK_IMAGE_LAYOUT_UNDEFINED : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
attachments[0].finalLayout = isBackbuffer ? VK_IMAGE_LAYOUT_PRESENT_SRC_KHR : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
attachments[0].flags = 0;

attachments[1].format = vulkan->GetDeviceInfo().preferredDepthStencilFormat;
attachments[1].samples = VK_SAMPLE_COUNT_1_BIT;
attachments[1].loadOp = ConvertLoadAction(key.depthLoadAction);
attachments[1].storeOp = ConvertStoreAction(key.depthStoreAction);
attachments[1].stencilLoadOp = ConvertLoadAction(key.stencilLoadAction);
attachments[1].stencilStoreOp = ConvertStoreAction(key.stencilStoreAction);
attachments[1].initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
attachments[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
attachments[1].flags = 0;
if (hasDepth) {
attachments[1].format = vulkan->GetDeviceInfo().preferredDepthStencilFormat;
attachments[1].samples = VK_SAMPLE_COUNT_1_BIT;
attachments[1].loadOp = ConvertLoadAction(key.depthLoadAction);
attachments[1].storeOp = ConvertStoreAction(key.depthStoreAction);
attachments[1].stencilLoadOp = ConvertLoadAction(key.stencilLoadAction);
attachments[1].stencilStoreOp = ConvertStoreAction(key.stencilStoreAction);
attachments[1].initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
attachments[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
attachments[1].flags = 0;
}

VkAttachmentReference color_reference{};
color_reference.attachment = 0;
Expand All @@ -371,7 +373,9 @@ VkRenderPass CreateRP(VulkanContext *vulkan, const RPKey &key, RenderPassType rp
subpass.colorAttachmentCount = 1;
subpass.pColorAttachments = &color_reference;
subpass.pResolveAttachments = nullptr;
subpass.pDepthStencilAttachment = &depth_reference;
if (hasDepth) {
subpass.pDepthStencilAttachment = &depth_reference;
}
subpass.preserveAttachmentCount = 0;
subpass.pPreserveAttachments = nullptr;

Expand All @@ -380,20 +384,19 @@ VkRenderPass CreateRP(VulkanContext *vulkan, const RPKey &key, RenderPassType rp
size_t numDeps = 0;

VkRenderPassCreateInfo rp{ VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO };
rp.attachmentCount = 2;
rp.attachmentCount = hasDepth ? 2 : 1;
rp.pAttachments = attachments;
rp.subpassCount = 1;
rp.pSubpasses = &subpass;

if (rpType == RP_TYPE_BACKBUFFER) {
if (isBackbuffer) {
deps[numDeps].srcSubpass = VK_SUBPASS_EXTERNAL;
deps[numDeps].dstSubpass = 0;
deps[numDeps].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
deps[numDeps].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
deps[numDeps].srcAccessMask = 0;
deps[numDeps].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
numDeps++;
rp.dependencyCount = 1;
}

if (selfDependency) {
Expand Down Expand Up @@ -424,7 +427,7 @@ VkRenderPass VKRRenderPass::Get(VulkanContext *vulkan, RenderPassType rpType) {
// practical later when referring to it. Could change to on-demand if it feels motivated
// but I think the render pass objects are cheap.
if (!pass[(int)rpType]) {
pass[(int)rpType] = CreateRP(vulkan, key_, (RenderPassType)rpType);
pass[(int)rpType] = CreateRenderPass(vulkan, key_, (RenderPassType)rpType);
}
return pass[(int)rpType];
}
Expand Down Expand Up @@ -873,8 +876,10 @@ std::string VulkanQueueRunner::StepToString(const VKRStep &step) const {
const char *renderCmd;
switch (step.render.renderPassType) {
case RP_TYPE_BACKBUFFER: renderCmd = "BACKBUF"; break;
case RP_TYPE_COLOR_DEPTH: renderCmd = "RENDER"; break;
case RP_TYPE_COLOR_DEPTH_INPUT: renderCmd = "RENDER_INPUT"; break;
case RP_TYPE_COLOR: renderCmd = "RENDER"; break;
case RP_TYPE_COLOR_DEPTH: renderCmd = "RENDER_DEPTH"; break;
case RP_TYPE_COLOR_INPUT: renderCmd = "RENDER_INPUT"; break;
case RP_TYPE_COLOR_DEPTH_INPUT: renderCmd = "RENDER_DEPTH_INPUT"; break;
default: renderCmd = "N/A";
}
snprintf(buffer, sizeof(buffer), "%s %s (draws: %d, %dx%d/%dx%d, fb: %p, )", renderCmd, step.tag, step.render.numDraws, actual_w, actual_h, w, h, step.render.framebuffer);
Expand Down Expand Up @@ -1153,7 +1158,7 @@ void TransitionToOptimal(VkCommandBuffer cmd, VkImage colorImage, VkImageLayout
srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
break;
default:
_dbg_assert_msg_(false, "GetRenderPass: Unexpected color layout %d", (int)colorLayout);
_dbg_assert_msg_(false, "TransitionToOptimal: Unexpected color layout %d", (int)colorLayout);
break;
}
recordBarrier->TransitionImage(
Expand Down Expand Up @@ -1189,7 +1194,7 @@ void TransitionToOptimal(VkCommandBuffer cmd, VkImage colorImage, VkImageLayout
srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
break;
default:
_dbg_assert_msg_(false, "GetRenderPass: Unexpected depth layout %d", (int)depthStencilLayout);
_dbg_assert_msg_(false, "TransitionToOptimal: Unexpected depth layout %d", (int)depthStencilLayout);
break;
}
recordBarrier->TransitionImage(
Expand Down Expand Up @@ -1236,7 +1241,7 @@ void TransitionFromOptimal(VkCommandBuffer cmd, VkImage colorImage, VkImageLayou
// Nothing to do.
break;
default:
_dbg_assert_msg_(false, "GetRenderPass: Unexpected final color layout %d", (int)colorLayout);
_dbg_assert_msg_(false, "TransitionFromOptimal: Unexpected final color layout %d", (int)colorLayout);
break;
}
barrier[0].oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
Expand Down Expand Up @@ -1275,7 +1280,7 @@ void TransitionFromOptimal(VkCommandBuffer cmd, VkImage colorImage, VkImageLayou
// Nothing to do.
break;
default:
_dbg_assert_msg_(false, "GetRenderPass: Unexpected final depth layout %d", (int)depthStencilLayout);
_dbg_assert_msg_(false, "TransitionFromOptimal: Unexpected final depth layout %d", (int)depthStencilLayout);
break;
}
barrier[barrierCount].oldLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
Expand Down
12 changes: 9 additions & 3 deletions Common/GPU/Vulkan/VulkanQueueRunner.h
Expand Up @@ -43,18 +43,24 @@ enum class VKRRenderCommand : uint8_t {

enum class PipelineFlags {
NONE = 0,
USES_LINES = (1 << 2),
USES_BLEND_CONSTANT = (1 << 3),
USES_DEPTH_STENCIL = (1 << 4), // Reads or writes the depth buffer.
USES_DEPTH_STENCIL = (1 << 4), // Reads or writes the depth or stencil buffers.
USES_INPUT_ATTACHMENT = (1 << 5),
};
ENUM_CLASS_BITOPS(PipelineFlags);

// Pipelines need to be created for the right type of render pass.
enum RenderPassType {
RP_TYPE_BACKBUFFER,
// These four are organized so that bit 0 is DEPTH and bit 1 is INPUT, so
// they can be OR-ed together in MergeRPTypes.
RP_TYPE_COLOR,
RP_TYPE_COLOR_DEPTH,
RP_TYPE_COLOR_INPUT,
RP_TYPE_COLOR_DEPTH_INPUT,

// This is the odd one out, and gets special handling in MergeRPTypes.
RP_TYPE_BACKBUFFER, // For the backbuffer we can always use CLEAR/DONT_CARE, so bandwidth cost for a depth channel is negligible.

// Later will add pure-color render passes.
RP_TYPE_COUNT,
};
Expand Down
35 changes: 23 additions & 12 deletions Common/GPU/Vulkan/VulkanRenderManager.cpp
Expand Up @@ -158,33 +158,37 @@ VKRFramebuffer::VKRFramebuffer(VulkanContext *vk, VkCommandBuffer initCmd, VKRRe
// We create the actual framebuffer objects on demand, because some combinations might not make sense.
}

VkFramebuffer VKRFramebuffer::Get(VKRRenderPass *compatibleRenderPass, RenderPassType renderPassType) {
if (framebuf[(int)renderPassType]) {
return framebuf[(int)renderPassType];
VkFramebuffer VKRFramebuffer::Get(VKRRenderPass *compatibleRenderPass, RenderPassType rpType) {
if (framebuf[(int)rpType]) {
return framebuf[(int)rpType];
}

VkFramebufferCreateInfo fbci{ VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO };
VkImageView views[2]{};

fbci.renderPass = compatibleRenderPass->Get(vulkan_, renderPassType);
fbci.attachmentCount = 2;
fbci.pAttachments = views;
bool hasDepth = rpType == RP_TYPE_BACKBUFFER || rpType == RP_TYPE_COLOR_DEPTH || rpType == RP_TYPE_COLOR_DEPTH_INPUT;

views[0] = color.imageView;
views[1] = depth.imageView;
if (hasDepth) {
views[1] = depth.imageView;
}
fbci.renderPass = compatibleRenderPass->Get(vulkan_, rpType);
fbci.attachmentCount = hasDepth ? 2 : 1;
fbci.pAttachments = views;
fbci.width = width;
fbci.height = height;
fbci.layers = 1;

VkResult res = vkCreateFramebuffer(vulkan_->GetDevice(), &fbci, nullptr, &framebuf[(int)renderPassType]);
VkResult res = vkCreateFramebuffer(vulkan_->GetDevice(), &fbci, nullptr, &framebuf[(int)rpType]);
_assert_(res == VK_SUCCESS);

if (!tag_.empty() && vulkan_->Extensions().EXT_debug_utils) {
vulkan_->SetDebugName(color.image, VK_OBJECT_TYPE_IMAGE, StringFromFormat("fb_color_%s", tag_.c_str()).c_str());
vulkan_->SetDebugName(depth.image, VK_OBJECT_TYPE_IMAGE, StringFromFormat("fb_depth_%s", tag_.c_str()).c_str());
vulkan_->SetDebugName(framebuf[(int)renderPassType], VK_OBJECT_TYPE_FRAMEBUFFER, StringFromFormat("fb_%s", tag_.c_str()).c_str());
vulkan_->SetDebugName(framebuf[(int)rpType], VK_OBJECT_TYPE_FRAMEBUFFER, StringFromFormat("fb_%s", tag_.c_str()).c_str());
}

return framebuf[(int)renderPassType];
return framebuf[(int)rpType];
}

VKRFramebuffer::~VKRFramebuffer() {
Expand Down Expand Up @@ -656,15 +660,16 @@ void VulkanRenderManager::EndCurRenderStep() {
curRenderStep_->render.colorLoad, curRenderStep_->render.depthLoad, curRenderStep_->render.stencilLoad,
curRenderStep_->render.colorStore, curRenderStep_->render.depthStore, curRenderStep_->render.stencilStore,
};
RenderPassType rpType = RP_TYPE_COLOR_DEPTH;
// Save the accumulated pipeline flags so we can use that to configure the render pass.
// We'll often be able to avoid loading/saving the depth/stencil buffer.
curRenderStep_->render.pipelineFlags = curPipelineFlags_;
bool depthStencil = (curPipelineFlags_ & PipelineFlags::USES_DEPTH_STENCIL) != 0;
RenderPassType rpType = depthStencil ? RP_TYPE_COLOR_DEPTH : RP_TYPE_COLOR;
if (!curRenderStep_->render.framebuffer) {
rpType = RP_TYPE_BACKBUFFER;
} else if (curPipelineFlags_ & PipelineFlags::USES_INPUT_ATTACHMENT) {
// Not allowed on backbuffers.
rpType = RP_TYPE_COLOR_DEPTH_INPUT;
rpType = depthStencil ? RP_TYPE_COLOR_INPUT : RP_TYPE_COLOR_DEPTH_INPUT;
}
// TODO: Also add render pass types for depth/stencil-less.

Expand Down Expand Up @@ -714,9 +719,11 @@ void VulkanRenderManager::BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRR
}
if (depth == VKRRenderPassLoadAction::CLEAR) {
clearMask |= VK_IMAGE_ASPECT_DEPTH_BIT;
curPipelineFlags_ |= PipelineFlags::USES_DEPTH_STENCIL;
}
if (stencil == VKRRenderPassLoadAction::CLEAR) {
clearMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
curPipelineFlags_ |= PipelineFlags::USES_DEPTH_STENCIL;
}

// If we need a clear and the previous step has commands already, it's best to just add a clear and keep going.
Expand Down Expand Up @@ -997,6 +1004,10 @@ void VulkanRenderManager::Clear(uint32_t clearColor, float clearZ, int clearSten
curRenderStep_->render.depthLoad = (clearMask & VK_IMAGE_ASPECT_DEPTH_BIT) ? VKRRenderPassLoadAction::CLEAR : VKRRenderPassLoadAction::KEEP;
curRenderStep_->render.stencilLoad = (clearMask & VK_IMAGE_ASPECT_STENCIL_BIT) ? VKRRenderPassLoadAction::CLEAR : VKRRenderPassLoadAction::KEEP;

if (clearMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) {
curPipelineFlags_ |= PipelineFlags::USES_DEPTH_STENCIL;
}

// In case there were commands already.
curRenderStep_->render.numDraws = 0;
RemoveDrawCommands(&curRenderStep_->commands);
Expand Down
15 changes: 7 additions & 8 deletions GPU/Vulkan/PipelineManagerVulkan.cpp
Expand Up @@ -170,8 +170,8 @@ static std::string CutFromMain(std::string str) {
}

static VulkanPipeline *CreateVulkanPipeline(VulkanRenderManager *renderManager, VkPipelineCache pipelineCache,
VkPipelineLayout layout, PipelineFlags pipelineFlags, const VulkanPipelineRasterStateKey &key,
const DecVtxFormat *decFmt, VulkanVertexShader *vs, VulkanFragmentShader *fs, bool useHwTransform, u32 variantBitmask) {
VkPipelineLayout layout, PipelineFlags pipelineFlags, const VulkanPipelineRasterStateKey &key,
const DecVtxFormat *decFmt, VulkanVertexShader *vs, VulkanFragmentShader *fs, bool useHwTransform, u32 variantBitmask) {
VulkanPipeline *vulkanPipeline = new VulkanPipeline();
VKRGraphicsPipelineDesc *desc = &vulkanPipeline->desc;
desc->pipelineCache = pipelineCache;
Expand Down Expand Up @@ -221,7 +221,7 @@ static VulkanPipeline *CreateVulkanPipeline(VulkanRenderManager *renderManager,
VkDynamicState *dynamicStates = &desc->dynamicStates[0];
int numDyn = 0;
if (key.blendEnable &&
(UsesBlendConstant(key.srcAlpha) || UsesBlendConstant(key.srcColor) || UsesBlendConstant(key.destAlpha) || UsesBlendConstant(key.destColor))) {
(UsesBlendConstant(key.srcAlpha) || UsesBlendConstant(key.srcColor) || UsesBlendConstant(key.destAlpha) || UsesBlendConstant(key.destColor))) {
dynamicStates[numDyn++] = VK_DYNAMIC_STATE_BLEND_CONSTANTS;
useBlendConstant = true;
}
Expand All @@ -232,12 +232,12 @@ static VulkanPipeline *CreateVulkanPipeline(VulkanRenderManager *renderManager,
dynamicStates[numDyn++] = VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK;
dynamicStates[numDyn++] = VK_DYNAMIC_STATE_STENCIL_REFERENCE;
}

VkPipelineDynamicStateCreateInfo &ds = desc->ds;
ds.flags = 0;
ds.pDynamicStates = dynamicStates;
ds.dynamicStateCount = numDyn;

VkPipelineRasterizationStateCreateInfo &rs = desc->rs;
rs.flags = 0;
rs.depthBiasEnable = false;
Expand Down Expand Up @@ -299,10 +299,9 @@ static VulkanPipeline *CreateVulkanPipeline(VulkanRenderManager *renderManager,
VKRGraphicsPipeline *pipeline = renderManager->CreateGraphicsPipeline(desc, variantBitmask, "game");

vulkanPipeline->pipeline = pipeline;
if (useBlendConstant)
if (useBlendConstant) {
pipelineFlags |= PipelineFlags::USES_BLEND_CONSTANT;
if (key.topology == VK_PRIMITIVE_TOPOLOGY_LINE_LIST || key.topology == VK_PRIMITIVE_TOPOLOGY_LINE_STRIP)
pipelineFlags |= PipelineFlags::USES_LINES;
}
if (dss.depthTestEnable || dss.stencilTestEnable) {
pipelineFlags |= PipelineFlags::USES_DEPTH_STENCIL;
}
Expand Down
1 change: 0 additions & 1 deletion GPU/Vulkan/PipelineManagerVulkan.h
Expand Up @@ -58,7 +58,6 @@ struct VulkanPipeline {
PipelineFlags pipelineFlags; // PipelineFlags enum above.

bool UsesBlendConstant() const { return (pipelineFlags & PipelineFlags::USES_BLEND_CONSTANT) != 0; }
bool UsesLines() const { return (pipelineFlags & PipelineFlags::USES_LINES) != 0; }
bool UsesDepthStencil() const { return (pipelineFlags & PipelineFlags::USES_DEPTH_STENCIL) != 0; }
bool UsesInputAttachment() const { return (pipelineFlags & PipelineFlags::USES_INPUT_ATTACHMENT) != 0; }

Expand Down

0 comments on commit c3cbb68

Please sign in to comment.