From cb7f68b98d422b975282027f3c83f22e0d78658d Mon Sep 17 00:00:00 2001 From: PixelyIon Date: Tue, 7 Dec 2021 02:41:07 +0530 Subject: [PATCH] Allow Attaching Texture/Buffers to `CommandExecutor` Switch from `SubmitWithCycle` to manually allocating the active command buffer to tag dependencies with the `FenceCycle` that prevents them from being mutated prior to execution. This new paradigm could also allow eager recording of commands with only submission being deferred. --- .../gpu/interconnect/command_executor.cpp | 50 +++++++++++++++++-- .../gpu/interconnect/command_executor.h | 22 +++++++- 2 files changed, 65 insertions(+), 7 deletions(-) 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 45a2cc77..cfa55b71 100644 --- a/app/src/main/cpp/skyline/gpu/interconnect/command_executor.cpp +++ b/app/src/main/cpp/skyline/gpu/interconnect/command_executor.cpp @@ -5,7 +5,11 @@ #include "command_executor.h" namespace skyline::gpu::interconnect { - CommandExecutor::CommandExecutor(const DeviceState &state) : gpu(*state.gpu) {} + CommandExecutor::CommandExecutor(const DeviceState &state) : gpu(*state.gpu), activeCommandBuffer(gpu.scheduler.AllocateCommandBuffer()), cycle(activeCommandBuffer.GetFenceCycle()) {} + + CommandExecutor::~CommandExecutor() { + cycle->Cancel(); + } bool CommandExecutor::CreateRenderPass(vk::Rect2D renderArea) { if (renderPass && renderPass->renderArea != renderArea) { @@ -21,12 +25,30 @@ namespace skyline::gpu::interconnect { return newRenderPass; } + void CommandExecutor::AttachTexture(const std::shared_ptr &texture) { + if (!syncTextures.contains(texture.get())) { + texture->WaitOnFence(); + texture->cycle = cycle; + cycle->AttachObject(texture); + syncTextures.emplace(texture.get()); + } + } + + void CommandExecutor::AttachBuffer(const std::shared_ptr &buffer) { + if (!syncBuffers.contains(buffer.get())) { + buffer->WaitOnFence(); + buffer->cycle = cycle; + cycle->AttachObject(buffer); + syncBuffers.emplace(buffer.get()); + } + } + void CommandExecutor::AddSubpass(const std::function &, GPU &)> &function, vk::Rect2D renderArea, span inputAttachments, span colorAttachments, TextureView *depthStencilAttachment) { for (const auto &attachments : {inputAttachments, colorAttachments}) for (const auto &attachment : attachments) - syncTextures.emplace(attachment->texture.get()); + AttachTexture(attachment->texture); if (depthStencilAttachment) - syncTextures.emplace(depthStencilAttachment->texture.get()); + AttachTexture(depthStencilAttachment->texture); bool newRenderPass{CreateRenderPass(renderArea)}; renderPass->AddSubpass(inputAttachments, colorAttachments, depthStencilAttachment ? &*depthStencilAttachment : nullptr); @@ -37,6 +59,8 @@ namespace skyline::gpu::interconnect { } void CommandExecutor::AddClearColorSubpass(TextureView *attachment, const vk::ClearColorValue &value) { + AttachTexture(attachment->texture); + bool newRenderPass{CreateRenderPass(vk::Rect2D{ .extent = attachment->texture->dimensions, })}; @@ -74,10 +98,18 @@ namespace skyline::gpu::interconnect { renderPass = nullptr; } - gpu.scheduler.SubmitWithCycle([this](vk::raii::CommandBuffer &commandBuffer, const std::shared_ptr &cycle) { + { + auto &commandBuffer{*activeCommandBuffer}; + commandBuffer.begin(vk::CommandBufferBeginInfo{ + .flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit, + }); + for (auto texture : syncTextures) texture->SynchronizeHostWithBuffer(commandBuffer, cycle); + for (auto buffer : syncBuffers) + buffer->SynchronizeHostWithCycle(cycle); + using namespace node; for (NodeVariant &node : nodes) { #define NODE(name) [&](name& node) { node(commandBuffer, cycle, gpu); } @@ -93,7 +125,15 @@ namespace skyline::gpu::interconnect { for (auto texture : syncTextures) texture->SynchronizeGuestWithBuffer(commandBuffer, cycle); - })->Wait(); + + for (auto buffer : syncBuffers) + buffer->SynchronizeGuestWithCycle(cycle); + + commandBuffer.end(); + + gpu.scheduler.SubmitCommandBuffer(commandBuffer, activeCommandBuffer.GetFence()); + cycle = activeCommandBuffer.Reset(); + } nodes.clear(); syncTextures.clear(); 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 ddd29e81..8b7c13d0 100644 --- a/app/src/main/cpp/skyline/gpu/interconnect/command_executor.h +++ b/app/src/main/cpp/skyline/gpu/interconnect/command_executor.h @@ -10,14 +10,17 @@ namespace skyline::gpu::interconnect { /** * @brief Assembles a Vulkan command stream with various nodes and manages execution of the produced graph - * @note This class is **NOT** thread-safe and should not be utilized by multiple threads concurrently + * @note This class is **NOT** thread-safe and should **ONLY** be utilized by a single thread */ class CommandExecutor { private: GPU &gpu; + CommandScheduler::ActiveCommandBuffer activeCommandBuffer; + std::shared_ptr cycle; boost::container::stable_vector nodes; node::RenderPassNode *renderPass{}; - std::unordered_set syncTextures; //!< All textures that need to be synced prior to and after execution + std::unordered_set syncTextures; //!< All textures that need to be synced prior to and after execution + std::unordered_set syncBuffers; //!< All buffers that need to be synced prior to and after execution /** * @return If a new render pass was created by the function or the current one was reused as it was compatible @@ -27,9 +30,24 @@ namespace skyline::gpu::interconnect { public: CommandExecutor(const DeviceState &state); + ~CommandExecutor(); + + /** + * @brief Attach the lifetime of the texture to the command buffer + * @note This'll automatically handle syncing of the texture in the most optimal way possible + */ + void AttachTexture(const std::shared_ptr &texture); + + /** + * @brief Attach the lifetime of a buffer to the command buffer + * @note This'll automatically handle syncing of the buffer in the most optimal way possible + */ + void AttachBuffer(const std::shared_ptr &buffer); + /** * @brief Adds a command that needs to be executed inside a subpass configured with certain attachments * @note Any texture supplied to this **must** be locked by the calling thread, it should also undergo no persistent layout transitions till execution + * @note All attachments will automatically be attached and aren't required to be attached prior */ void AddSubpass(const std::function &, GPU &)> &&function, vk::Rect2D renderArea, span inputAttachments = {}, span colorAttachments = {}, TextureView *depthStencilAttachment = {});