diff --git a/app/src/main/cpp/skyline/gpu/interconnect/command_executor.cpp b/app/src/main/cpp/skyline/gpu/interconnect/command_executor.cpp index db66e1e5..bbff096f 100644 --- a/app/src/main/cpp/skyline/gpu/interconnect/command_executor.cpp +++ b/app/src/main/cpp/skyline/gpu/interconnect/command_executor.cpp @@ -7,18 +7,18 @@ namespace skyline::gpu::interconnect { CommandExecutor::CommandExecutor(const DeviceState &state) : gpu(*state.gpu) {} - bool CommandExecutor::CreateRenderpass(vk::Rect2D renderArea) { - if (renderpass && renderpass->renderArea != renderArea) { - nodes.emplace_back(std::in_place_type_t()); - renderpass = nullptr; + bool CommandExecutor::CreateRenderPass(vk::Rect2D renderArea) { + if (renderPass && renderPass->renderArea != renderArea) { + nodes.emplace_back(std::in_place_type_t()); + renderPass = nullptr; } - bool newRenderpass{renderpass == nullptr}; - if (newRenderpass) + bool newRenderPass{renderPass == nullptr}; + if (newRenderPass) // We need to create a render pass if one doesn't already exist or the current one isn't compatible - renderpass = &std::get(nodes.emplace_back(std::in_place_type_t(), renderArea)); + renderPass = &std::get(nodes.emplace_back(std::in_place_type_t(), renderArea)); - return newRenderpass; + return newRenderPass; } void CommandExecutor::AddSubpass(const std::function &, GPU &)> &function, vk::Rect2D renderArea, std::vector inputAttachments, std::vector colorAttachments, std::optional depthStencilAttachment) { @@ -28,22 +28,22 @@ namespace skyline::gpu::interconnect { if (depthStencilAttachment) syncTextures.emplace(depthStencilAttachment->backing.get()); - bool newRenderpass{CreateRenderpass(renderArea)}; - renderpass->AddSubpass(inputAttachments, colorAttachments, depthStencilAttachment ? &*depthStencilAttachment : nullptr); - if (newRenderpass) + bool newRenderPass{CreateRenderPass(renderArea)}; + renderPass->AddSubpass(inputAttachments, colorAttachments, depthStencilAttachment ? &*depthStencilAttachment : nullptr); + if (newRenderPass) nodes.emplace_back(std::in_place_type_t(), function); else nodes.emplace_back(std::in_place_type_t(), function); } void CommandExecutor::AddClearColorSubpass(TextureView attachment, const vk::ClearColorValue &value) { - bool newRenderpass{CreateRenderpass(vk::Rect2D{ + bool newRenderPass{CreateRenderPass(vk::Rect2D{ .extent = attachment.backing->dimensions, })}; - renderpass->AddSubpass({}, attachment, nullptr); + renderPass->AddSubpass({}, attachment, nullptr); - if (renderpass->ClearColorAttachment(0, value)) { - if (!newRenderpass) + if (renderPass->ClearColorAttachment(0, value)) { + if (!newRenderPass) nodes.emplace_back(std::in_place_type_t()); } else { auto function{[scissor = attachment.backing->dimensions, value](vk::raii::CommandBuffer &commandBuffer, const std::shared_ptr &, GPU &) { @@ -58,7 +58,7 @@ namespace skyline::gpu::interconnect { }); }}; - if (newRenderpass) + if (newRenderPass) nodes.emplace_back(std::in_place_type_t(), function); else nodes.emplace_back(std::in_place_type_t(), function); @@ -69,9 +69,9 @@ namespace skyline::gpu::interconnect { if (!nodes.empty()) { TRACE_EVENT("gpu", "CommandExecutor::Execute"); - if (renderpass) { - nodes.emplace_back(std::in_place_type_t()); - renderpass = nullptr; + if (renderPass) { + nodes.emplace_back(std::in_place_type_t()); + renderPass = nullptr; } gpu.scheduler.SubmitWithCycle([this](vk::raii::CommandBuffer &commandBuffer, const std::shared_ptr &cycle) { @@ -83,10 +83,10 @@ namespace skyline::gpu::interconnect { #define NODE(name) [&](name& node) { node(commandBuffer, cycle, gpu); } std::visit(VariantVisitor{ NODE(FunctionNode), - NODE(RenderpassNode), + NODE(RenderPassNode), NODE(NextSubpassNode), NODE(NextSubpassFunctionNode), - NODE(RenderpassEndNode), + NODE(RenderPassEndNode), }, node); #undef NODE } diff --git a/app/src/main/cpp/skyline/gpu/interconnect/command_executor.h b/app/src/main/cpp/skyline/gpu/interconnect/command_executor.h index a63755e5..70f9a525 100644 --- a/app/src/main/cpp/skyline/gpu/interconnect/command_executor.h +++ b/app/src/main/cpp/skyline/gpu/interconnect/command_executor.h @@ -16,13 +16,13 @@ namespace skyline::gpu::interconnect { private: GPU &gpu; boost::container::stable_vector nodes; - node::RenderpassNode *renderpass{}; + node::RenderPassNode *renderPass{}; std::unordered_set 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: CommandExecutor(const DeviceState &state); diff --git a/app/src/main/cpp/skyline/gpu/interconnect/command_nodes.cpp b/app/src/main/cpp/skyline/gpu/interconnect/command_nodes.cpp index ac0fb18c..325ca112 100644 --- a/app/src/main/cpp/skyline/gpu/interconnect/command_nodes.cpp +++ b/app/src/main/cpp/skyline/gpu/interconnect/command_nodes.cpp @@ -4,16 +4,16 @@ #include "command_nodes.h" namespace skyline::gpu::interconnect::node { - RenderpassNode::Storage::~Storage() { + RenderPassNode::Storage::~Storage() { if (device) { if (framebuffer) (**device).destroy(framebuffer, nullptr, *device->getDispatcher()); - if (renderpass) - (**device).destroy(renderpass, nullptr, *device->getDispatcher()); + if (renderPass) + (**device).destroy(renderPass, nullptr, *device->getDispatcher()); } } - u32 RenderpassNode::AddAttachment(TextureView &view) { + u32 RenderPassNode::AddAttachment(TextureView &view) { auto &textures{storage->textures}; auto texture{std::find(textures.begin(), textures.end(), view.backing)}; if (texture == textures.end()) @@ -29,43 +29,47 @@ namespace skyline::gpu::interconnect::node { .initialLayout = view.backing->layout, .finalLayout = view.backing->layout, }); - return attachments.size() - 1; + return static_cast(attachments.size() - 1); } else { // 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 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 auto referenceBeginIt{attachmentReferences.begin()}; - referenceBeginIt += reinterpret_cast(it->pInputAttachments) / sizeof(vk::AttachmentReference); + referenceBeginIt += reinterpret_cast(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 - if (reinterpret_cast(it->pDepthStencilAttachment) != NoDepthStencil) + 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(subpassDescription.pDepthStencilAttachment) != NoDepthStencil) 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 - if (std::find_if(referenceBeginIt, referenceEndIt, [&](const vk::AttachmentReference &reference) { + if (std::find_if(attachmentReferenceBegin, attachmentReferenceEnd, [&](const vk::AttachmentReference &reference) { return reference.attachment == attachmentIndex; - }) != referenceEndIt) - break; // The iterator should be set to the first subpass that utilizes the attachment we want to preserve + }) != attachmentReferenceEnd) + break; } 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); + // 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 for (; it != subpassDescriptions.end(); it++) { - auto referenceBeginIt{attachmentReferences.begin()}; - referenceBeginIt += reinterpret_cast(it->pInputAttachments) / sizeof(vk::AttachmentReference); + auto [attachmentReferenceBegin, attachmentReferenceEnd]{getSubpassAttachmentRange(*it)}; - auto referenceEndIt{referenceBeginIt + it->inputAttachmentCount + it->colorAttachmentCount}; - if (reinterpret_cast(it->pDepthStencilAttachment) != NoDepthStencil) - referenceEndIt++; - - if (std::find_if(referenceBeginIt, referenceEndIt, [&](const vk::AttachmentReference &reference) { + if (std::find_if(attachmentReferenceBegin, attachmentReferenceEnd, [&](const vk::AttachmentReference &reference) { return reference.attachment == attachmentIndex; - }) != referenceEndIt) { + }) != attachmentReferenceEnd) { lastUsageIt = it; 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); } + // 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{ .srcSubpass = static_cast(std::distance(subpassDescriptions.begin(), lastUsageIt)), .dstSubpass = static_cast(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 inputAttachments, span colorAttachments, TextureView *depthStencilAttachment) { + void RenderPassNode::AddSubpass(span inputAttachments, span colorAttachments, TextureView *depthStencilAttachment) { attachmentReferences.reserve(attachmentReferences.size() + inputAttachments.size() + colorAttachments.size() + (depthStencilAttachment ? 1 : 0)); 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 attachmentIndex{attachmentReference->attachment}; @@ -155,7 +160,7 @@ namespace skyline::gpu::interconnect::node { return false; } - void RenderpassNode::operator()(vk::raii::CommandBuffer &commandBuffer, const std::shared_ptr &cycle, GPU &gpu) { + void RenderPassNode::operator()(vk::raii::CommandBuffer &commandBuffer, const std::shared_ptr &cycle, GPU &gpu) { storage->device = &gpu.vkDevice; auto preserveAttachmentIt{preserveAttachmentReferences.begin()}; @@ -181,7 +186,7 @@ namespace skyline::gpu::interconnect::node { texture->WaitOnFence(); } - auto renderpass{(*gpu.vkDevice).createRenderPass(vk::RenderPassCreateInfo{ + auto renderPass{(*gpu.vkDevice).createRenderPass(vk::RenderPassCreateInfo{ .attachmentCount = static_cast(attachmentDescriptions.size()), .pAttachments = attachmentDescriptions.data(), .subpassCount = static_cast(subpassDescriptions.size()), @@ -189,10 +194,10 @@ namespace skyline::gpu::interconnect::node { .dependencyCount = static_cast(subpassDependencies.size()), .pDependencies = subpassDependencies.data(), }, nullptr, *gpu.vkDevice.getDispatcher())}; - storage->renderpass = renderpass; + storage->renderPass = renderPass; auto framebuffer{(*gpu.vkDevice).createFramebuffer(vk::FramebufferCreateInfo{ - .renderPass = renderpass, + .renderPass = renderPass, .attachmentCount = static_cast(attachments.size()), .pAttachments = attachments.data(), .width = renderArea.extent.width, @@ -202,7 +207,7 @@ namespace skyline::gpu::interconnect::node { storage->framebuffer = framebuffer; commandBuffer.beginRenderPass(vk::RenderPassBeginInfo{ - .renderPass = renderpass, + .renderPass = renderPass, .framebuffer = framebuffer, .renderArea = renderArea, .clearValueCount = static_cast(clearValues.size()), diff --git a/app/src/main/cpp/skyline/gpu/interconnect/command_nodes.h b/app/src/main/cpp/skyline/gpu/interconnect/command_nodes.h index 7baf7895..f7511227 100644 --- a/app/src/main/cpp/skyline/gpu/interconnect/command_nodes.h +++ b/app/src/main/cpp/skyline/gpu/interconnect/command_nodes.h @@ -24,9 +24,9 @@ namespace skyline::gpu::interconnect::node { 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: /** * @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 { vk::raii::Device *device{}; vk::Framebuffer framebuffer{}; - vk::RenderPass renderpass{}; + vk::RenderPass renderPass{}; std::vector> textures; ~Storage(); @@ -65,7 +65,7 @@ namespace skyline::gpu::interconnect::node { vk::Rect2D renderArea; std::vector clearValues; - RenderpassNode(vk::Rect2D renderArea) : storage(std::make_shared()), renderArea(renderArea) {} + RenderPassNode(vk::Rect2D renderArea) : storage(std::make_shared()), renderArea(renderArea) {} /** * @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 { void operator()(vk::raii::CommandBuffer &commandBuffer, const std::shared_ptr &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 &cycle, GPU &gpu) { commandBuffer.endRenderPass(); } }; - using NodeVariant = std::variant; //!< A variant encompassing all command nodes types + using NodeVariant = std::variant; //!< A variant encompassing all command nodes types }