blob: f37a91daa21c4ad9ed05c0c572bc54556f539d02 [file] [log] [blame]
/* Copyright (c) 2017 Hans-Kristian Arntzen
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
// Based on the following files from the Granite rendering engine:
// - vulkan/render_pass.cpp
#include "lib/escher/third_party/granite/vk/render_pass.h"
#include "lib/escher/impl/vulkan_utils.h"
#include "lib/escher/resources/resource_recycler.h"
#include "lib/escher/util/bit_ops.h"
#include "lib/escher/util/image_utils.h"
#include "lib/escher/util/stack_allocator.h"
#include "lib/escher/vk/render_pass_info.h"
namespace escher {
namespace impl {
const ResourceTypeInfo impl::RenderPass::kTypeInfo(
"impl::RenderPass", ResourceType::kResource, ResourceType::kImplRenderPass);
namespace {
// Helper function for constructor. If |info| has explicit subpasses, return a
// pointer to the first one. Otherwise, populate |default_subpass| and return
// the pointer to it.
const RenderPassInfo::Subpass* GetPointerToFirstSubpass(
const RenderPassInfo& info, RenderPassInfo::Subpass* default_subpass) {
if (info.subpasses.empty()) {
// Populate default subpass.
default_subpass->num_color_attachments = info.num_color_attachments;
default_subpass->depth_stencil_mode =
RenderPassInfo::DepthStencil::kReadWrite;
for (uint32_t i = 0; i < info.num_color_attachments; i++) {
default_subpass->color_attachments[i] = i;
}
return default_subpass;
}
return info.subpasses.data();
}
// Helper function for constructor. Return true if the attachment requires
// an implicit layout transition (because it is a swapchain image or transient
// attachment), and false otherwise.
bool FillColorAttachmentDescription(
const RenderPassInfo& info,
vk::AttachmentDescription* attachment_descriptions, uint32_t index) {
#ifndef NDEBUG
FXL_DCHECK(info.color_attachments[index]);
auto pair = image_utils::IsDepthStencilFormat(
info.color_attachments[index]->image()->format());
FXL_DCHECK(!pair.first && !pair.second)
<< "Color attachment cannot use depth/stencil format.";
#endif
auto& image = info.color_attachments[index]->image();
auto& desc = attachment_descriptions[index];
// TODO(ES-73): support for transient images. What's missing?
FXL_DCHECK(!image->is_transient() || !image->is_swapchain_image())
<< "transient and swapchain images not yet handled.";
const auto load_store_ops_pair = info.LoadStoreOpsForColorAttachment(index);
const vk::AttachmentLoadOp load_op = load_store_ops_pair.first;
const vk::AttachmentStoreOp store_op = load_store_ops_pair.second;
desc.flags = vk::AttachmentDescriptionFlags();
desc.format = image->vk_format();
desc.samples = SampleCountFlagBitsFromInt(image->info().sample_count);
desc.loadOp = load_op;
desc.storeOp = store_op;
// Stencil ops are inapplicable (this is a color attachment), hence eDontCare.
desc.stencilLoadOp = vk::AttachmentLoadOp::eDontCare;
desc.stencilStoreOp = vk::AttachmentStoreOp::eDontCare;
if (image->info().is_transient()) {
FXL_DCHECK(load_op == vk::AttachmentLoadOp::eDontCare);
desc.initialLayout = vk::ImageLayout::eUndefined;
// This will be filled in later with the layout of the last subpass that
// uses this attachment, in order to avoid an unnecessary transition at
// the end of the render pass.
desc.finalLayout = vk::ImageLayout::eUndefined;
return true;
} else if (image->is_swapchain_image()) {
desc.initialLayout = vk::ImageLayout::eUndefined;
desc.finalLayout = image->swapchain_layout();
return true;
} else if (info.op_flags & RenderPassInfo::kOptimalColorLayoutOp) {
desc.initialLayout = vk::ImageLayout::eColorAttachmentOptimal;
// This will be filled in later with the layout of the last subpass that
// uses this attachment, in order to avoid an unnecessary transition at
// the end of the render pass.
desc.finalLayout = vk::ImageLayout::eUndefined;
return false;
} else {
desc.initialLayout = vk::ImageLayout::eGeneral;
desc.finalLayout = vk::ImageLayout::eGeneral;
return false;
}
}
// Helper function for constructor. Return true if the attachment requires
// an implicit layout transition (because it is a swapchain image or transient
// attachment), and false otherwise.
bool FillDepthStencilAttachmentDescription(const RenderPassInfo& info,
vk::AttachmentDescription* desc) {
auto& image = info.depth_stencil_attachment->image();
desc->flags = vk::AttachmentDescriptionFlags();
desc->format = image->vk_format();
desc->samples = SampleCountFlagBitsFromInt(image->info().sample_count);
desc->loadOp = vk::AttachmentLoadOp::eDontCare;
desc->storeOp = vk::AttachmentStoreOp::eDontCare;
const auto load_store_ops_pair = info.LoadStoreOpsForDepthStencilAttachment();
const vk::AttachmentLoadOp load_op = load_store_ops_pair.first;
const vk::AttachmentStoreOp store_op = load_store_ops_pair.second;
const auto bool_pair = image_utils::IsDepthStencilFormat(desc->format);
if (bool_pair.first) {
// Attachment has a depth component.
desc->loadOp = load_op;
desc->storeOp = store_op;
}
if (bool_pair.second) {
// Attachment has a stencil component.
desc->stencilLoadOp = load_op;
desc->stencilStoreOp = store_op;
}
if (image->info().is_transient()) {
FXL_DCHECK(load_op != vk::AttachmentLoadOp::eLoad);
desc->initialLayout = vk::ImageLayout::eUndefined;
// This will be filled in later with the layout of the last subpass that
// uses this attachment, in order to avoid an unnecessary transition at
// the end of the render pass.
desc->finalLayout = vk::ImageLayout::eUndefined;
return true;
}
vk::ImageLayout layout = vk::ImageLayout::eGeneral;
if (info.op_flags & RenderPassInfo::kOptimalDepthStencilLayoutOp) {
layout = vk::ImageLayout::eDepthStencilAttachmentOptimal;
} else if (info.op_flags & RenderPassInfo::kDepthStencilReadOnlyLayoutOp) {
// NOTE: this flag and the one above are mutually exclusive. This is
// enforced by RenderPassInfo::Validate().
layout = vk::ImageLayout::eDepthStencilReadOnlyOptimal;
}
// Since the attachment is not being loaded, we make the render pass more
// more flexible regarding the attachments it will accept by setting the
// layout to eUndefined. This should incur no additional
// performance cost.
desc->initialLayout = desc->loadOp == vk::AttachmentLoadOp::eLoad
? layout
: vk::ImageLayout::eUndefined;
// TODO(ES-83): If the attachment is not being stored, then the most
// performant choice is to leave it in the same layout as the last subpass
// that uses this attachment, in order to avoid an extra transition at the end
// of the render pass (which would be indicated by setting |finalLayout| to
// |eDontCare|, so that it would later automatically be set to
// |current_layout|).
desc->finalLayout = layout;
return false;
}
// Find and return the vk::AttachmentReference that references the specified
// attachment in the specified subpass, or nullptr if the attachment is not
// referenced as a color attachment in that subpass.
vk::AttachmentReference* FindColorAttachmentRef(vk::SubpassDescription* subpass,
uint32_t attachment_index) {
auto* colors = subpass->pColorAttachments;
const uint32_t count = subpass->colorAttachmentCount;
for (uint32_t i = 0; i < count; i++) {
if (colors[i].attachment == attachment_index) {
return const_cast<vk::AttachmentReference*>(&colors[i]);
}
}
return nullptr;
};
// Find and return the vk::AttachmentReference that references the specified
// attachment in the specified subpass, or nullptr if the attachment is not
// referenced as a resolve attachment in that subpass.
vk::AttachmentReference* FindResolveAttachmentRef(
vk::SubpassDescription* subpass, uint32_t attachment_index) {
if (auto* resolves = subpass->pResolveAttachments) {
const uint32_t count = subpass->colorAttachmentCount;
for (uint32_t i = 0; i < count; i++) {
if (resolves[i].attachment == attachment_index) {
return const_cast<vk::AttachmentReference*>(&resolves[i]);
}
}
}
return nullptr;
};
// Find and return the vk::AttachmentReference that references the specified
// attachment in the specified subpass, or nullptr if the attachment is not
// referenced as an input attachment in that subpass.
vk::AttachmentReference* FindInputAttachmentRef(vk::SubpassDescription* subpass,
uint32_t attachment_index) {
auto* inputs = subpass->pInputAttachments;
const uint32_t count = subpass->inputAttachmentCount;
for (uint32_t i = 0; i < count; i++) {
if (inputs[i].attachment == attachment_index) {
return const_cast<vk::AttachmentReference*>(&inputs[i]);
}
}
return nullptr;
}
// Find and return the vk::AttachmentReference that references the specified
// attachment in the specified subpass, or nullptr if the attachment is not
// the depth-stencil attachment for that subpass.
vk::AttachmentReference* FindDepthStencilAttachmentRef(
vk::SubpassDescription* subpass, uint32_t attachment_index) {
if (subpass->pDepthStencilAttachment->attachment == attachment_index) {
return const_cast<vk::AttachmentReference*>(
subpass->pDepthStencilAttachment);
}
return nullptr;
};
} // namespace
RenderPass::RenderPass(ResourceRecycler* recycler, const RenderPassInfo& info)
: Resource(recycler) {
FXL_DCHECK(info.Validate());
num_color_attachments_ = info.num_color_attachments;
// If the RenderPassInfo doesn't have any subpasses, set up a single default
// subpass, otherwise use the provided subpasses. Either way, subsequent code
// will use |info_subpasses| and |num_info_subpasses| without considering
// where they came from.
RenderPassInfo::Subpass default_subpass;
const RenderPassInfo::Subpass* info_subpasses =
GetPointerToFirstSubpass(info, &default_subpass);
const uint32_t num_info_subpasses =
info.subpasses.empty() ? 1 : info.subpasses.size();
FXL_DCHECK(num_info_subpasses <= 32);
vk::AttachmentDescription attachments[VulkanLimits::kNumColorAttachments + 1];
const uint32_t num_attachments =
info.num_color_attachments + (info.depth_stencil_attachment ? 1 : 0);
uint32_t implicit_transitions = 0;
// Initialize description of each color attachment (load/store ops, format,
// and layout).
for (uint32_t i = 0; i < info.num_color_attachments; i++) {
if (FillColorAttachmentDescription(info, attachments, i)) {
implicit_transitions |= 1u << i;
}
color_formats_[i] = attachments[i].format;
}
// Initialize description of depth-stencil attachment (load/store ops, format,
// and layout).
depth_stencil_format_ = vk::Format::eUndefined;
if (info.depth_stencil_attachment) {
auto attachment_description = attachments + info.num_color_attachments;
if (FillDepthStencilAttachmentDescription(info, attachment_description)) {
implicit_transitions |= 1u << info.num_color_attachments;
}
depth_stencil_format_ = attachment_description->format;
FXL_DCHECK(image_utils::IsDepthFormat(depth_stencil_format_) ||
image_utils::IsStencilFormat(depth_stencil_format_));
}
std::vector<vk::SubpassDescription> vk_subpass_descriptions(
num_info_subpasses);
std::vector<vk::SubpassDependency> vk_subpass_dependencies;
// Initialize a vk::SubpassDescription for each subpass. For each of the
// attachment types (input, color, resolve, depth-stencil, and preserve), the
// correct number of vk::AttachmentReferencess are allocated. Each is
// initialized with the proper attachment index, but their layouts are left to
// be filled in later.
StackAllocator<vk::AttachmentReference, 1024> reference_allocator;
for (uint32_t i = 0; i < num_info_subpasses; i++) {
// Allocate enough vk::AttachmentReferences for all of the
// color/input/resolve/depth attachments used by the i-th subpass.
auto* color_att_refs = reference_allocator.AllocateFilled(
info_subpasses[i].num_color_attachments);
auto* input_att_refs = reference_allocator.AllocateFilled(
info_subpasses[i].num_input_attachments);
auto* resolve_att_refs = reference_allocator.AllocateFilled(
info_subpasses[i].num_color_attachments);
auto* depth_att_ref = reference_allocator.AllocateFilled(1);
auto& subpass = vk_subpass_descriptions[i];
subpass.pipelineBindPoint = vk::PipelineBindPoint::eGraphics;
subpass.colorAttachmentCount = info_subpasses[i].num_color_attachments;
subpass.pColorAttachments = color_att_refs;
subpass.inputAttachmentCount = info_subpasses[i].num_input_attachments;
subpass.pInputAttachments = input_att_refs;
subpass.pDepthStencilAttachment = depth_att_ref;
if (info_subpasses[i].num_resolve_attachments) {
// TODO(ES-83): evaluate tradeoffs of relaxing this constraint. How often
// would it be beneficial to resolve some subset of the attachments? How
// much less convenient would the API become? e.g. what changes would
// need to be made to the RenderPassInfo struct?
FXL_DCHECK(info_subpasses[i].num_color_attachments ==
info_subpasses[i].num_resolve_attachments);
subpass.pResolveAttachments = resolve_att_refs;
}
for (uint32_t j = 0; j < subpass.colorAttachmentCount; j++) {
auto att = info_subpasses[i].color_attachments[j];
FXL_DCHECK(att == VK_ATTACHMENT_UNUSED || (att < num_attachments));
color_att_refs[j].attachment = att;
// Will be filled in below, with the value of |current_layout|.
color_att_refs[j].layout = vk::ImageLayout::eUndefined;
}
for (uint32_t j = 0; j < subpass.inputAttachmentCount; j++) {
auto att = info_subpasses[i].input_attachments[j];
FXL_DCHECK(att == VK_ATTACHMENT_UNUSED || (att < num_attachments));
input_att_refs[j].attachment = att;
// Will be filled in below, with the value of |current_layout|.
input_att_refs[j].layout = vk::ImageLayout::eUndefined;
}
if (subpass.pResolveAttachments) {
for (uint32_t j = 0; j < subpass.colorAttachmentCount; j++) {
auto att = info_subpasses[i].resolve_attachments[j];
FXL_DCHECK(att == VK_ATTACHMENT_UNUSED || (att < num_attachments));
resolve_att_refs[j].attachment = att;
// Will be filled in below, with the value of |current_layout|.
resolve_att_refs[j].layout = vk::ImageLayout::eUndefined;
}
}
depth_att_ref->attachment = (info.depth_stencil_attachment &&
info_subpasses[i].depth_stencil_mode !=
RenderPassInfo::DepthStencil::kNone)
? info.num_color_attachments
: VK_ATTACHMENT_UNUSED;
// To be filled in later.
depth_att_ref->layout = vk::ImageLayout::eUndefined;
}
// Now, figure out how each attachment is used throughout the subpasses.
// Either we don't care (inherit previous pass), or we need something
// specific. Start with initial layouts.
uint32_t preserve_masks[VulkanLimits::kNumColorAttachments + 1] = {};
// Last subpass which makes use of an attachment.
uint32_t last_subpass_for_attachment[VulkanLimits::kNumColorAttachments + 1] =
{};
// 1 << subpass bit set if there are color attachment self-dependencies in
// the subpass.
uint32_t color_self_dependencies = 0;
// 1 << subpass bit set if there are depth-stencil attachment
// self-dependencies in the subpass.
uint32_t depth_self_dependencies = 0;
// 1 << subpass bit set if any input attachment is read in the subpass.
uint32_t input_attachment_read = 0;
uint32_t color_attachment_read_write = 0;
uint32_t depth_stencil_attachment_write = 0;
uint32_t depth_stencil_attachment_read = 0;
uint32_t external_color_dependencies = 0;
uint32_t external_depth_dependencies = 0;
uint32_t external_input_dependencies = 0;
// Iterate through each attachment and:
// - find/remember the last subpass that the attachment is used by.
// - find the first subpass that the attachment is used by, and generate
// an explicit EXTERNAL subpass dependency.
// - set the attachment's layout, depending on its usage (for example, one
// might be used as both a color and an input attachment, and this has
// implications for the set of allowable layouts).
// - avoid gratuitous layout transitions if no "finalLayout" is specified for
// the attachment: simply use the layout of the attachment from the last
// subpass it appears in.
for (uint32_t attachment = 0; attachment < num_attachments; attachment++) {
// Track whether an attachment has been used within any subpass so far.
bool used = false;
auto current_layout = attachments[attachment].initialLayout;
for (uint32_t subpass = 0; subpass < num_info_subpasses; subpass++) {
auto vk_subpass_desc = &vk_subpass_descriptions[subpass];
auto* color = FindColorAttachmentRef(vk_subpass_desc, attachment);
auto* resolve = FindResolveAttachmentRef(vk_subpass_desc, attachment);
auto* input = FindInputAttachmentRef(vk_subpass_desc, attachment);
auto* depth = FindDepthStencilAttachmentRef(vk_subpass_desc, attachment);
// Sanity check.
if (color || resolve) {
FXL_DCHECK(!depth);
FXL_DCHECK(!color || !resolve);
}
// If the attachment was not used in this subpass, but it was used in a
// previous subpass, then preserve its contents in case it is required by
// a subsequent subpass.
if (!color && !input && !depth && !resolve) {
if (used) {
// NOTE: this is overly conservative because we may be preserving an
// attachment that isn't used in any subsequent passes. This is
// addressed later, by zeroing bits corresponding to subpasses after
// the last subpass that the attachment is used.
preserve_masks[attachment] |= 1u << subpass;
}
continue;
}
last_subpass_for_attachment[attachment] = subpass;
if (!used && (implicit_transitions & (1u << attachment))) {
// This is the first subpass we need implicit transitions.
if (color)
external_color_dependencies |= 1u << subpass;
if (depth)
external_depth_dependencies |= 1u << subpass;
if (input)
external_input_dependencies |= 1u << subpass;
// NOTE: |resolve| isn't considered because it always depends on a
// previous |color| attachment.
}
used = true;
if (resolve) {
// No particular layout preference.
if (current_layout != vk::ImageLayout::eGeneral) {
current_layout = vk::ImageLayout::eColorAttachmentOptimal;
}
resolve->layout = current_layout;
color_attachment_read_write |= 1u << subpass;
} else if (color && input) {
// If used as both input attachment and color attachment in same
// subpass, layout must be eGeneral.
current_layout = vk::ImageLayout::eGeneral;
color->layout = current_layout;
input->layout = current_layout;
color_self_dependencies |= 1u << subpass;
color_attachment_read_write |= 1u << subpass;
input_attachment_read |= 1u << subpass;
} else if (color) {
// No particular layout preference.
if (current_layout != vk::ImageLayout::eGeneral) {
current_layout = vk::ImageLayout::eColorAttachmentOptimal;
}
color->layout = current_layout;
color_attachment_read_write |= 1u << subpass;
} else if (depth && input) {
// Layout depends on the depth mode.
FXL_DCHECK(info_subpasses[subpass].depth_stencil_mode !=
RenderPassInfo::DepthStencil::kNone);
if (info_subpasses[subpass].depth_stencil_mode ==
RenderPassInfo::DepthStencil::kReadWrite) {
// If used as both input attachment and writable depth attachment in
// same subpass, layout must be eGeneral.
current_layout = vk::ImageLayout::eGeneral;
depth_self_dependencies |= 1u << subpass;
depth_stencil_attachment_write |= 1u << subpass;
} else if (current_layout != vk::ImageLayout::eGeneral) {
// No particular layout preference; might as well use the optimal.
current_layout = vk::ImageLayout::eDepthStencilReadOnlyOptimal;
}
depth->layout = current_layout;
input->layout = current_layout;
depth_stencil_attachment_read |= 1u << subpass;
input_attachment_read |= 1u << subpass;
} else if (depth) {
// Layout depends on depth mode.
if (info_subpasses[subpass].depth_stencil_mode ==
RenderPassInfo::DepthStencil::kReadWrite) {
if (current_layout != vk::ImageLayout::eGeneral) {
// No particular layout preference; might as well use the optimal.
current_layout = vk::ImageLayout::eDepthStencilAttachmentOptimal;
}
depth_stencil_attachment_write |= 1u << subpass;
} else if (current_layout != vk::ImageLayout::eGeneral) {
// No particular layout preference; might as well use the optimal.
current_layout = vk::ImageLayout::eDepthStencilReadOnlyOptimal;
}
depth->layout = current_layout;
depth_stencil_attachment_read |= 1u << subpass;
} else if (input) {
// No particular layout preference.
if (current_layout != vk::ImageLayout::eGeneral) {
current_layout = vk::ImageLayout::eShaderReadOnlyOptimal;
}
// If the attachment is first used as an input attachment, the initial
// layout should actually be vk::ImageLayout::eShaderReadOnlyOptimal.
if (!used && attachments[attachment].initialLayout ==
vk::ImageLayout::eColorAttachmentOptimal) {
attachments[attachment].initialLayout = current_layout;
}
input->layout = current_layout;
} else {
FXL_DCHECK(false) << "Unhandled attachment usage.";
}
}
FXL_DCHECK(used) << "attachment[" << attachment
<< "] was not used in any subpass.";
// If we don't have a specific layout we need to end up in, use the last one
// to avoid unnecessary layout transitions.
if (attachments[attachment].finalLayout == vk::ImageLayout::eUndefined) {
FXL_DCHECK(current_layout != vk::ImageLayout::eUndefined);
attachments[attachment].finalLayout = current_layout;
}
}
// Preserve attachments between subpasses.
StackAllocator<uint32_t, 1024> preserve_allocator;
for (uint32_t attachment = 0; attachment < num_attachments; attachment++) {
// As mentioned above, do not preserve attachments beyond the last subpass
// where they are used.
// TODO(ES-83): add ClearBitsAtAndAboveIndex() to bit_ops.h
preserve_masks[attachment] &=
(1u << last_subpass_for_attachment[attachment]) - 1;
}
for (uint32_t subpass_index = 0; subpass_index < num_info_subpasses;
subpass_index++) {
// Count the number of attachments to be preserved in this subpass.
uint32_t preserve_count = 0;
for (uint32_t attachment = 0; attachment < num_attachments; attachment++) {
if (preserve_masks[attachment] & (1u << subpass_index)) {
preserve_count++;
}
}
// Allocate and write attachment preservation info.
auto* preserve = preserve_allocator.AllocateFilled(preserve_count);
for (uint32_t attachment = 0; attachment < num_attachments; attachment++) {
if (preserve_masks[attachment] & (1u << subpass_index)) {
*preserve++ = attachment;
}
}
auto& subpass = vk_subpass_descriptions[subpass_index];
subpass.pPreserveAttachments = preserve;
subpass.preserveAttachmentCount = preserve_count;
}
// Add external subpass dependencies.
//
// TODO(ES-83): Section 7.1 of the Vulkan spec ("Render Pass Creation") states
// that external subpass dependencies are implicitly specified when not given
// explicitly by the user. Such implicit dependencies use conservative stage
// and access masks. It is easy to do better for external "src" dependencies,
// as we do below. However, external "dst" dependencies are more difficult,
// because we may not know whether the output will be presented to the display
// vs. sampled as a texture. Revisit this at some point to decide whether we
// can be more efficient (perhaps this can be addressed as part of the future
// "frame graph" design).
ForEachBitIndex(
external_color_dependencies | external_depth_dependencies |
external_input_dependencies,
[&](uint32_t subpass_index) {
vk_subpass_dependencies.emplace_back();
auto& dep = vk_subpass_dependencies.back();
dep.srcSubpass = VK_SUBPASS_EXTERNAL;
dep.dstSubpass = subpass_index;
if (external_color_dependencies & (1u << subpass_index)) {
dep.srcStageMask |= vk::PipelineStageFlagBits::eColorAttachmentOutput;
dep.dstStageMask |= vk::PipelineStageFlagBits::eColorAttachmentOutput;
dep.srcAccessMask |= vk::AccessFlagBits::eColorAttachmentWrite;
dep.dstAccessMask |= vk::AccessFlagBits::eColorAttachmentRead |
vk::AccessFlagBits::eColorAttachmentWrite;
}
if (external_depth_dependencies & (1u << subpass_index)) {
dep.srcStageMask |= vk::PipelineStageFlagBits::eEarlyFragmentTests |
vk::PipelineStageFlagBits::eLateFragmentTests;
dep.dstStageMask |= vk::PipelineStageFlagBits::eEarlyFragmentTests |
vk::PipelineStageFlagBits::eLateFragmentTests;
dep.srcAccessMask |= vk::AccessFlagBits::eDepthStencilAttachmentWrite;
dep.dstAccessMask |=
vk::AccessFlagBits::eDepthStencilAttachmentWrite |
vk::AccessFlagBits::eDepthStencilAttachmentRead;
}
if (external_input_dependencies & (1u << subpass_index)) {
dep.srcStageMask |= vk::PipelineStageFlagBits::eColorAttachmentOutput;
dep.dstStageMask |= vk::PipelineStageFlagBits::eFragmentShader;
dep.srcAccessMask |= vk::AccessFlagBits::eColorAttachmentWrite |
vk::AccessFlagBits::eDepthStencilAttachmentWrite;
dep.dstAccessMask |= vk::AccessFlagBits::eInputAttachmentRead;
}
});
// We previously identified subpasses where an input attachment depends on
// color/depth data generated by a previous subpass. For each of these, we
// now generate the necessary VkSubpassDependency structs.
ForEachBitIndex(
color_self_dependencies | depth_self_dependencies, [&](uint32_t subpass) {
vk_subpass_dependencies.emplace_back();
auto& dep = vk_subpass_dependencies.back();
dep.srcSubpass = subpass;
dep.dstSubpass = subpass;
dep.dependencyFlags = vk::DependencyFlagBits::eByRegion;
if (color_self_dependencies & (1u << subpass)) {
dep.srcStageMask |= vk::PipelineStageFlagBits::eColorAttachmentOutput;
dep.srcAccessMask |= vk::AccessFlagBits::eColorAttachmentWrite;
}
if (depth_self_dependencies & (1u << subpass)) {
dep.srcStageMask |= vk::PipelineStageFlagBits::eEarlyFragmentTests |
vk::PipelineStageFlagBits::eLateFragmentTests;
dep.srcAccessMask |= vk::AccessFlagBits::eDepthStencilAttachmentWrite;
}
dep.dstStageMask = vk::PipelineStageFlagBits::eFragmentShader;
dep.dstAccessMask = vk::AccessFlagBits::eInputAttachmentRead;
});
// Flush and invalidate caches between each subpass.
for (uint32_t subpass = 1; subpass < num_info_subpasses; subpass++) {
vk_subpass_dependencies.emplace_back();
auto& dep = vk_subpass_dependencies.back();
dep.srcSubpass = subpass - 1;
dep.dstSubpass = subpass;
dep.dependencyFlags = vk::DependencyFlagBits::eByRegion;
if (color_attachment_read_write & (1u << (subpass - 1))) {
dep.srcStageMask |= vk::PipelineStageFlagBits::eColorAttachmentOutput;
dep.srcAccessMask |= vk::AccessFlagBits::eColorAttachmentWrite;
}
if (depth_stencil_attachment_write & (1u << (subpass - 1))) {
dep.srcStageMask |= vk::PipelineStageFlagBits::eEarlyFragmentTests |
vk::PipelineStageFlagBits::eLateFragmentTests;
dep.srcAccessMask |= vk::AccessFlagBits::eDepthStencilAttachmentWrite;
}
if (color_attachment_read_write & (1u << subpass)) {
dep.dstStageMask |= vk::PipelineStageFlagBits::eColorAttachmentOutput;
dep.dstAccessMask |= vk::AccessFlagBits::eColorAttachmentWrite |
vk::AccessFlagBits::eColorAttachmentRead;
}
if (depth_stencil_attachment_read & (1u << subpass)) {
dep.dstStageMask |= vk::PipelineStageFlagBits::eEarlyFragmentTests |
vk::PipelineStageFlagBits::eLateFragmentTests;
dep.dstAccessMask |= vk::AccessFlagBits::eDepthStencilAttachmentRead;
}
if (depth_stencil_attachment_write & (1u << subpass)) {
dep.dstStageMask |= vk::PipelineStageFlagBits::eEarlyFragmentTests |
vk::PipelineStageFlagBits::eLateFragmentTests;
dep.dstAccessMask |= vk::AccessFlagBits::eDepthStencilAttachmentWrite |
vk::AccessFlagBits::eDepthStencilAttachmentRead;
}
if (input_attachment_read & (1u << subpass)) {
dep.dstStageMask |= vk::PipelineStageFlagBits::eFragmentShader;
dep.dstAccessMask |= vk::AccessFlagBits::eInputAttachmentRead;
}
}
// Store the important subpass information for later.
for (auto& subpass : vk_subpass_descriptions) {
SubpassInfo subpass_info = {};
subpass_info.num_color_attachments = subpass.colorAttachmentCount;
subpass_info.num_input_attachments = subpass.inputAttachmentCount;
subpass_info.depth_stencil_attachment = *subpass.pDepthStencilAttachment;
memcpy(subpass_info.color_attachments, subpass.pColorAttachments,
subpass.colorAttachmentCount * sizeof(*subpass.pColorAttachments));
memcpy(subpass_info.input_attachments, subpass.pInputAttachments,
subpass.inputAttachmentCount * sizeof(*subpass.pInputAttachments));
uint32_t samples = 0;
for (uint32_t i = 0; i < subpass_info.num_color_attachments; i++) {
if (subpass_info.color_attachments[i].attachment ==
VK_ATTACHMENT_UNUSED) {
continue;
}
uint32_t samp = SampleCountFlagBitsToInt(
attachments[subpass_info.color_attachments[i].attachment].samples);
if (samples && (samp != samples)) {
FXL_DCHECK(samp == samples);
}
samples = samp;
}
if (subpass_info.depth_stencil_attachment.attachment !=
VK_ATTACHMENT_UNUSED) {
uint32_t samp = SampleCountFlagBitsToInt(
attachments[subpass_info.depth_stencil_attachment.attachment]
.samples);
if (samples && (samp != samples)) {
FXL_DCHECK(samp == samples);
}
samples = samp;
}
FXL_DCHECK(samples > 0);
subpass_info.samples = impl::SampleCountFlagBitsFromInt(samples);
subpasses_.push_back(subpass_info);
}
// Finally! Build the render pass!!
vk::RenderPassCreateInfo render_pass_create_info;
render_pass_create_info.subpassCount = num_info_subpasses;
render_pass_create_info.pSubpasses = vk_subpass_descriptions.data();
render_pass_create_info.pAttachments = attachments;
render_pass_create_info.attachmentCount = num_attachments;
render_pass_create_info.dependencyCount = vk_subpass_dependencies.size();
render_pass_create_info.pDependencies = vk_subpass_dependencies.empty()
? nullptr
: vk_subpass_dependencies.data();
render_pass_ = ESCHER_CHECKED_VK_RESULT(
vk_device().createRenderPass(render_pass_create_info));
}
RenderPass::~RenderPass() { vk_device().destroyRenderPass(render_pass_); }
} // namespace impl
} // namespace escher