Consolidate AddAttachment Loops + Rename Renderpass -> RenderPass

This commit is contained in:
PixelyIon 2021-10-24 02:31:22 +05:30
parent 92a21ea616
commit 830a800d9e
4 changed files with 64 additions and 59 deletions

View File

@ -7,18 +7,18 @@
namespace skyline::gpu::interconnect { namespace skyline::gpu::interconnect {
CommandExecutor::CommandExecutor(const DeviceState &state) : gpu(*state.gpu) {} CommandExecutor::CommandExecutor(const DeviceState &state) : gpu(*state.gpu) {}
bool CommandExecutor::CreateRenderpass(vk::Rect2D renderArea) { bool CommandExecutor::CreateRenderPass(vk::Rect2D renderArea) {
if (renderpass && renderpass->renderArea != renderArea) { if (renderPass && renderPass->renderArea != renderArea) {
nodes.emplace_back(std::in_place_type_t<node::RenderpassEndNode>()); nodes.emplace_back(std::in_place_type_t<node::RenderPassEndNode>());
renderpass = nullptr; renderPass = nullptr;
} }
bool newRenderpass{renderpass == nullptr}; bool newRenderPass{renderPass == nullptr};
if (newRenderpass) if (newRenderPass)
// We need to create a render pass if one doesn't already exist or the current one isn't compatible // We need to create a render pass if one doesn't already exist or the current one isn't compatible
renderpass = &std::get<node::RenderpassNode>(nodes.emplace_back(std::in_place_type_t<node::RenderpassNode>(), renderArea)); renderPass = &std::get<node::RenderPassNode>(nodes.emplace_back(std::in_place_type_t<node::RenderPassNode>(), renderArea));
return newRenderpass; return newRenderPass;
} }
void CommandExecutor::AddSubpass(const std::function<void(vk::raii::CommandBuffer &, const std::shared_ptr<FenceCycle> &, GPU &)> &function, vk::Rect2D renderArea, std::vector<TextureView> inputAttachments, std::vector<TextureView> colorAttachments, std::optional<TextureView> depthStencilAttachment) { void CommandExecutor::AddSubpass(const std::function<void(vk::raii::CommandBuffer &, const std::shared_ptr<FenceCycle> &, GPU &)> &function, vk::Rect2D renderArea, std::vector<TextureView> inputAttachments, std::vector<TextureView> colorAttachments, std::optional<TextureView> depthStencilAttachment) {
@ -28,22 +28,22 @@ namespace skyline::gpu::interconnect {
if (depthStencilAttachment) if (depthStencilAttachment)
syncTextures.emplace(depthStencilAttachment->backing.get()); syncTextures.emplace(depthStencilAttachment->backing.get());
bool newRenderpass{CreateRenderpass(renderArea)}; bool newRenderPass{CreateRenderPass(renderArea)};
renderpass->AddSubpass(inputAttachments, colorAttachments, depthStencilAttachment ? &*depthStencilAttachment : nullptr); renderPass->AddSubpass(inputAttachments, colorAttachments, depthStencilAttachment ? &*depthStencilAttachment : nullptr);
if (newRenderpass) if (newRenderPass)
nodes.emplace_back(std::in_place_type_t<node::FunctionNode>(), function); nodes.emplace_back(std::in_place_type_t<node::FunctionNode>(), function);
else else
nodes.emplace_back(std::in_place_type_t<node::NextSubpassFunctionNode>(), function); nodes.emplace_back(std::in_place_type_t<node::NextSubpassFunctionNode>(), function);
} }
void CommandExecutor::AddClearColorSubpass(TextureView attachment, const vk::ClearColorValue &value) { void CommandExecutor::AddClearColorSubpass(TextureView attachment, const vk::ClearColorValue &value) {
bool newRenderpass{CreateRenderpass(vk::Rect2D{ bool newRenderPass{CreateRenderPass(vk::Rect2D{
.extent = attachment.backing->dimensions, .extent = attachment.backing->dimensions,
})}; })};
renderpass->AddSubpass({}, attachment, nullptr); renderPass->AddSubpass({}, attachment, nullptr);
if (renderpass->ClearColorAttachment(0, value)) { if (renderPass->ClearColorAttachment(0, value)) {
if (!newRenderpass) if (!newRenderPass)
nodes.emplace_back(std::in_place_type_t<node::NextSubpassNode>()); nodes.emplace_back(std::in_place_type_t<node::NextSubpassNode>());
} else { } else {
auto function{[scissor = attachment.backing->dimensions, value](vk::raii::CommandBuffer &commandBuffer, const std::shared_ptr<FenceCycle> &, GPU &) { auto function{[scissor = attachment.backing->dimensions, value](vk::raii::CommandBuffer &commandBuffer, const std::shared_ptr<FenceCycle> &, GPU &) {
@ -58,7 +58,7 @@ namespace skyline::gpu::interconnect {
}); });
}}; }};
if (newRenderpass) if (newRenderPass)
nodes.emplace_back(std::in_place_type_t<node::FunctionNode>(), function); nodes.emplace_back(std::in_place_type_t<node::FunctionNode>(), function);
else else
nodes.emplace_back(std::in_place_type_t<node::NextSubpassFunctionNode>(), function); nodes.emplace_back(std::in_place_type_t<node::NextSubpassFunctionNode>(), function);
@ -69,9 +69,9 @@ namespace skyline::gpu::interconnect {
if (!nodes.empty()) { if (!nodes.empty()) {
TRACE_EVENT("gpu", "CommandExecutor::Execute"); TRACE_EVENT("gpu", "CommandExecutor::Execute");
if (renderpass) { if (renderPass) {
nodes.emplace_back(std::in_place_type_t<node::RenderpassEndNode>()); nodes.emplace_back(std::in_place_type_t<node::RenderPassEndNode>());
renderpass = nullptr; renderPass = nullptr;
} }
gpu.scheduler.SubmitWithCycle([this](vk::raii::CommandBuffer &commandBuffer, const std::shared_ptr<FenceCycle> &cycle) { gpu.scheduler.SubmitWithCycle([this](vk::raii::CommandBuffer &commandBuffer, const std::shared_ptr<FenceCycle> &cycle) {
@ -83,10 +83,10 @@ namespace skyline::gpu::interconnect {
#define NODE(name) [&](name& node) { node(commandBuffer, cycle, gpu); } #define NODE(name) [&](name& node) { node(commandBuffer, cycle, gpu); }
std::visit(VariantVisitor{ std::visit(VariantVisitor{
NODE(FunctionNode), NODE(FunctionNode),
NODE(RenderpassNode), NODE(RenderPassNode),
NODE(NextSubpassNode), NODE(NextSubpassNode),
NODE(NextSubpassFunctionNode), NODE(NextSubpassFunctionNode),
NODE(RenderpassEndNode), NODE(RenderPassEndNode),
}, node); }, node);
#undef NODE #undef NODE
} }

View File

@ -16,13 +16,13 @@ namespace skyline::gpu::interconnect {
private: private:
GPU &gpu; GPU &gpu;
boost::container::stable_vector<node::NodeVariant> nodes; boost::container::stable_vector<node::NodeVariant> nodes;
node::RenderpassNode *renderpass{}; node::RenderPassNode *renderPass{};
std::unordered_set<Texture*> syncTextures; //!< All textures that need to be synced prior to and after execution std::unordered_set<Texture*> syncTextures; //!< All textures that need to be synced prior to and after execution
/** /**
* @return If a new renderpass was created by the function or the current one was reused as it was compatible * @return If a new render pass was created by the function or the current one was reused as it was compatible
*/ */
bool CreateRenderpass(vk::Rect2D renderArea); bool CreateRenderPass(vk::Rect2D renderArea);
public: public:
CommandExecutor(const DeviceState &state); CommandExecutor(const DeviceState &state);

View File

@ -4,16 +4,16 @@
#include "command_nodes.h" #include "command_nodes.h"
namespace skyline::gpu::interconnect::node { namespace skyline::gpu::interconnect::node {
RenderpassNode::Storage::~Storage() { RenderPassNode::Storage::~Storage() {
if (device) { if (device) {
if (framebuffer) if (framebuffer)
(**device).destroy(framebuffer, nullptr, *device->getDispatcher()); (**device).destroy(framebuffer, nullptr, *device->getDispatcher());
if (renderpass) if (renderPass)
(**device).destroy(renderpass, nullptr, *device->getDispatcher()); (**device).destroy(renderPass, nullptr, *device->getDispatcher());
} }
} }
u32 RenderpassNode::AddAttachment(TextureView &view) { u32 RenderPassNode::AddAttachment(TextureView &view) {
auto &textures{storage->textures}; auto &textures{storage->textures};
auto texture{std::find(textures.begin(), textures.end(), view.backing)}; auto texture{std::find(textures.begin(), textures.end(), view.backing)};
if (texture == textures.end()) if (texture == textures.end())
@ -29,43 +29,47 @@ namespace skyline::gpu::interconnect::node {
.initialLayout = view.backing->layout, .initialLayout = view.backing->layout,
.finalLayout = view.backing->layout, .finalLayout = view.backing->layout,
}); });
return attachments.size() - 1; return static_cast<u32>(attachments.size() - 1);
} else { } else {
// If we've got a match from a previous subpass, we need to preserve the attachment till the current subpass // If we've got a match from a previous subpass, we need to preserve the attachment till the current subpass
auto attachmentIndex{std::distance(attachments.begin(), attachment)}; auto attachmentIndex{std::distance(attachments.begin(), attachment)};
auto it{subpassDescriptions.begin()}; auto it{subpassDescriptions.begin()};
for (; it != subpassDescriptions.end(); it++) { auto getSubpassAttachmentRange{[this] (const vk::SubpassDescription& subpassDescription) {
// Find the bounds for the attachment references belonging to the current subpass // Find the bounds for the attachment references belonging to the current subpass
auto referenceBeginIt{attachmentReferences.begin()}; auto referenceBeginIt{attachmentReferences.begin()};
referenceBeginIt += reinterpret_cast<uintptr_t>(it->pInputAttachments) / sizeof(vk::AttachmentReference); referenceBeginIt += reinterpret_cast<uintptr_t>(subpassDescription.pInputAttachments) / sizeof(vk::AttachmentReference);
auto referenceEndIt{referenceBeginIt + it->inputAttachmentCount + it->colorAttachmentCount}; // We depend on all attachments being contiguous for a subpass, this will horribly break if that assumption is broken auto referenceEndIt{referenceBeginIt + subpassDescription.inputAttachmentCount + subpassDescription.colorAttachmentCount}; // We depend on all attachments being contiguous for a subpass, this will horribly break if that assumption is broken
if (reinterpret_cast<uintptr_t>(it->pDepthStencilAttachment) != NoDepthStencil) if (reinterpret_cast<uintptr_t>(subpassDescription.pDepthStencilAttachment) != NoDepthStencil)
referenceEndIt++; referenceEndIt++;
return std::make_pair(referenceBeginIt, referenceEndIt);
}};
// We want to find the first subpass that utilizes the attachment we want to preserve
for (; it != subpassDescriptions.end(); it++) {
auto [attachmentReferenceBegin, attachmentReferenceEnd]{getSubpassAttachmentRange(*it)};
// Iterate over all attachment references in the current subpass to see if they point to our target attachment // Iterate over all attachment references in the current subpass to see if they point to our target attachment
if (std::find_if(referenceBeginIt, referenceEndIt, [&](const vk::AttachmentReference &reference) { if (std::find_if(attachmentReferenceBegin, attachmentReferenceEnd, [&](const vk::AttachmentReference &reference) {
return reference.attachment == attachmentIndex; return reference.attachment == attachmentIndex;
}) != referenceEndIt) }) != attachmentReferenceEnd)
break; // The iterator should be set to the first subpass that utilizes the attachment we want to preserve break;
} }
if (it == subpassDescriptions.end()) if (it == subpassDescriptions.end())
// We should never have a case where an attachment is bound to the render pass but not utilized by any subpass
throw exception("Cannot find corresponding subpass for attachment #{}", attachmentIndex); throw exception("Cannot find corresponding subpass for attachment #{}", attachmentIndex);
// We want to preserve the attachment for all subpasses till the current subpass
auto lastUsageIt{it}; //!< The last subpass that the attachment has been used in for creating a dependency auto lastUsageIt{it}; //!< The last subpass that the attachment has been used in for creating a dependency
for (; it != subpassDescriptions.end(); it++) { for (; it != subpassDescriptions.end(); it++) {
auto referenceBeginIt{attachmentReferences.begin()}; auto [attachmentReferenceBegin, attachmentReferenceEnd]{getSubpassAttachmentRange(*it)};
referenceBeginIt += reinterpret_cast<uintptr_t>(it->pInputAttachments) / sizeof(vk::AttachmentReference);
auto referenceEndIt{referenceBeginIt + it->inputAttachmentCount + it->colorAttachmentCount}; if (std::find_if(attachmentReferenceBegin, attachmentReferenceEnd, [&](const vk::AttachmentReference &reference) {
if (reinterpret_cast<uintptr_t>(it->pDepthStencilAttachment) != NoDepthStencil)
referenceEndIt++;
if (std::find_if(referenceBeginIt, referenceEndIt, [&](const vk::AttachmentReference &reference) {
return reference.attachment == attachmentIndex; return reference.attachment == attachmentIndex;
}) != referenceEndIt) { }) != attachmentReferenceEnd) {
lastUsageIt = it; lastUsageIt = it;
continue; // If a subpass uses an attachment then it doesn't need to be preserved continue; // If a subpass uses an attachment then it doesn't need to be preserved
} }
@ -75,6 +79,7 @@ namespace skyline::gpu::interconnect::node {
subpassPreserveAttachments.push_back(attachmentIndex); subpassPreserveAttachments.push_back(attachmentIndex);
} }
// We want to ensure writes to the attachment from the last subpass using it are complete prior to using it in the latest subpass
vk::SubpassDependency dependency{ vk::SubpassDependency dependency{
.srcSubpass = static_cast<u32>(std::distance(subpassDescriptions.begin(), lastUsageIt)), .srcSubpass = static_cast<u32>(std::distance(subpassDescriptions.begin(), lastUsageIt)),
.dstSubpass = static_cast<uint32_t>(subpassDescriptions.size()), // We assume that the next subpass is using the attachment .dstSubpass = static_cast<uint32_t>(subpassDescriptions.size()), // We assume that the next subpass is using the attachment
@ -92,7 +97,7 @@ namespace skyline::gpu::interconnect::node {
} }
} }
void RenderpassNode::AddSubpass(span<TextureView> inputAttachments, span<TextureView> colorAttachments, TextureView *depthStencilAttachment) { void RenderPassNode::AddSubpass(span<TextureView> inputAttachments, span<TextureView> colorAttachments, TextureView *depthStencilAttachment) {
attachmentReferences.reserve(attachmentReferences.size() + inputAttachments.size() + colorAttachments.size() + (depthStencilAttachment ? 1 : 0)); attachmentReferences.reserve(attachmentReferences.size() + inputAttachments.size() + colorAttachments.size() + (depthStencilAttachment ? 1 : 0));
auto inputAttachmentsOffset{attachmentReferences.size() * sizeof(vk::AttachmentReference)}; auto inputAttachmentsOffset{attachmentReferences.size() * sizeof(vk::AttachmentReference)};
@ -132,7 +137,7 @@ namespace skyline::gpu::interconnect::node {
}); });
} }
bool RenderpassNode::ClearColorAttachment(u32 colorAttachment, const vk::ClearColorValue &value) { bool RenderPassNode::ClearColorAttachment(u32 colorAttachment, const vk::ClearColorValue &value) {
auto attachmentReference{RebasePointer(attachmentReferences, subpassDescriptions.back().pColorAttachments) + colorAttachment}; auto attachmentReference{RebasePointer(attachmentReferences, subpassDescriptions.back().pColorAttachments) + colorAttachment};
auto attachmentIndex{attachmentReference->attachment}; auto attachmentIndex{attachmentReference->attachment};
@ -155,7 +160,7 @@ namespace skyline::gpu::interconnect::node {
return false; return false;
} }
void RenderpassNode::operator()(vk::raii::CommandBuffer &commandBuffer, const std::shared_ptr<FenceCycle> &cycle, GPU &gpu) { void RenderPassNode::operator()(vk::raii::CommandBuffer &commandBuffer, const std::shared_ptr<FenceCycle> &cycle, GPU &gpu) {
storage->device = &gpu.vkDevice; storage->device = &gpu.vkDevice;
auto preserveAttachmentIt{preserveAttachmentReferences.begin()}; auto preserveAttachmentIt{preserveAttachmentReferences.begin()};
@ -181,7 +186,7 @@ namespace skyline::gpu::interconnect::node {
texture->WaitOnFence(); texture->WaitOnFence();
} }
auto renderpass{(*gpu.vkDevice).createRenderPass(vk::RenderPassCreateInfo{ auto renderPass{(*gpu.vkDevice).createRenderPass(vk::RenderPassCreateInfo{
.attachmentCount = static_cast<u32>(attachmentDescriptions.size()), .attachmentCount = static_cast<u32>(attachmentDescriptions.size()),
.pAttachments = attachmentDescriptions.data(), .pAttachments = attachmentDescriptions.data(),
.subpassCount = static_cast<u32>(subpassDescriptions.size()), .subpassCount = static_cast<u32>(subpassDescriptions.size()),
@ -189,10 +194,10 @@ namespace skyline::gpu::interconnect::node {
.dependencyCount = static_cast<u32>(subpassDependencies.size()), .dependencyCount = static_cast<u32>(subpassDependencies.size()),
.pDependencies = subpassDependencies.data(), .pDependencies = subpassDependencies.data(),
}, nullptr, *gpu.vkDevice.getDispatcher())}; }, nullptr, *gpu.vkDevice.getDispatcher())};
storage->renderpass = renderpass; storage->renderPass = renderPass;
auto framebuffer{(*gpu.vkDevice).createFramebuffer(vk::FramebufferCreateInfo{ auto framebuffer{(*gpu.vkDevice).createFramebuffer(vk::FramebufferCreateInfo{
.renderPass = renderpass, .renderPass = renderPass,
.attachmentCount = static_cast<u32>(attachments.size()), .attachmentCount = static_cast<u32>(attachments.size()),
.pAttachments = attachments.data(), .pAttachments = attachments.data(),
.width = renderArea.extent.width, .width = renderArea.extent.width,
@ -202,7 +207,7 @@ namespace skyline::gpu::interconnect::node {
storage->framebuffer = framebuffer; storage->framebuffer = framebuffer;
commandBuffer.beginRenderPass(vk::RenderPassBeginInfo{ commandBuffer.beginRenderPass(vk::RenderPassBeginInfo{
.renderPass = renderpass, .renderPass = renderPass,
.framebuffer = framebuffer, .framebuffer = framebuffer,
.renderArea = renderArea, .renderArea = renderArea,
.clearValueCount = static_cast<u32>(clearValues.size()), .clearValueCount = static_cast<u32>(clearValues.size()),

View File

@ -24,9 +24,9 @@ namespace skyline::gpu::interconnect::node {
using FunctionNode = FunctionNodeBase<>; using FunctionNode = FunctionNodeBase<>;
/** /**
* @brief Creates and begins a VkRenderpass alongside managing all resources bound to it and to the subpasses inside it * @brief Creates and begins a VkRenderPass alongside managing all resources bound to it and to the subpasses inside it
*/ */
struct RenderpassNode { struct RenderPassNode {
private: private:
/** /**
* @brief Storage for all resources in the VkRenderPass that have their lifetimes bond to the completion fence * @brief Storage for all resources in the VkRenderPass that have their lifetimes bond to the completion fence
@ -34,7 +34,7 @@ namespace skyline::gpu::interconnect::node {
struct Storage : public FenceCycleDependency { struct Storage : public FenceCycleDependency {
vk::raii::Device *device{}; vk::raii::Device *device{};
vk::Framebuffer framebuffer{}; vk::Framebuffer framebuffer{};
vk::RenderPass renderpass{}; vk::RenderPass renderPass{};
std::vector<std::shared_ptr<Texture>> textures; std::vector<std::shared_ptr<Texture>> textures;
~Storage(); ~Storage();
@ -65,7 +65,7 @@ namespace skyline::gpu::interconnect::node {
vk::Rect2D renderArea; vk::Rect2D renderArea;
std::vector<vk::ClearValue> clearValues; std::vector<vk::ClearValue> clearValues;
RenderpassNode(vk::Rect2D renderArea) : storage(std::make_shared<Storage>()), renderArea(renderArea) {} RenderPassNode(vk::Rect2D renderArea) : storage(std::make_shared<Storage>()), renderArea(renderArea) {}
/** /**
* @note Any preservation of attachments from previous subpasses is automatically handled by this * @note Any preservation of attachments from previous subpasses is automatically handled by this
@ -90,7 +90,7 @@ namespace skyline::gpu::interconnect::node {
}; };
/** /**
* @brief A node which progresses to the next subpass during a renderpass * @brief A node which progresses to the next subpass during a render pass
*/ */
struct NextSubpassNode { struct NextSubpassNode {
void operator()(vk::raii::CommandBuffer &commandBuffer, const std::shared_ptr<FenceCycle> &cycle, GPU &gpu) { void operator()(vk::raii::CommandBuffer &commandBuffer, const std::shared_ptr<FenceCycle> &cycle, GPU &gpu) {
@ -111,13 +111,13 @@ namespace skyline::gpu::interconnect::node {
}; };
/** /**
* @brief Ends a VkRenderpass that would be created prior with RenderpassNode * @brief Ends a VkRenderPass that would be created prior with RenderPassNode
*/ */
struct RenderpassEndNode { struct RenderPassEndNode {
void operator()(vk::raii::CommandBuffer &commandBuffer, const std::shared_ptr<FenceCycle> &cycle, GPU &gpu) { void operator()(vk::raii::CommandBuffer &commandBuffer, const std::shared_ptr<FenceCycle> &cycle, GPU &gpu) {
commandBuffer.endRenderPass(); commandBuffer.endRenderPass();
} }
}; };
using NodeVariant = std::variant<FunctionNode, RenderpassNode, NextSubpassNode, NextSubpassFunctionNode, RenderpassEndNode>; //!< A variant encompassing all command nodes types using NodeVariant = std::variant<FunctionNode, RenderPassNode, NextSubpassNode, NextSubpassFunctionNode, RenderPassEndNode>; //!< A variant encompassing all command nodes types
} }