From 80727184621d3c3febdd259402482f3e0d09c4a6 Mon Sep 17 00:00:00 2001 From: nmzik Date: Wed, 26 Aug 2026 10:03:43 +0200 Subject: [PATCH] graphics: replace command fences with timeline reuse --- src/graphics/guest_gpu/graphicsRun.cpp | 5 +- .../host_gpu/renderer/cache/bufferCache.cpp | 6 +- .../renderer/cache/gpuResourceManager.cpp | 2 +- .../host_gpu/renderer/commandScheduler.cpp | 321 ++++++++---------- .../host_gpu/renderer/commandScheduler.h | 98 ++---- src/graphics/host_gpu/renderer/context.cpp | 155 +-------- .../renderer/pipeline/descriptors.cpp | 2 +- src/graphics/host_gpu/renderer/render.h | 32 +- .../host_gpu/renderer/renderContext.cpp | 2 +- .../presentation/window/swapchain.cpp | 54 +-- tests/ShaderRecompilerComputeTests.cpp | 57 +--- 11 files changed, 248 insertions(+), 486 deletions(-) diff --git a/src/graphics/guest_gpu/graphicsRun.cpp b/src/graphics/guest_gpu/graphicsRun.cpp index 3fbeaee17..38ccd08c2 100644 --- a/src/graphics/guest_gpu/graphicsRun.cpp +++ b/src/graphics/guest_gpu/graphicsRun.cpp @@ -255,8 +255,7 @@ void CommandProcessor::BufferFlush() { } void CommandProcessor::BufferFlushAndWait() { - auto& submitted = GetScheduler().FlushAndGetSubmitted(); - submitted.WaitForFenceOnly(); + GetScheduler().FlushAndWait(); } void CommandProcessor::BufferWait() { @@ -1622,7 +1621,7 @@ void CommandProcessor::PrepareCpuFlip(uint64_t request_id) { } void CommandProcessor::SynchronizeGpu() { - GetScheduler().FinishCurrent(); + GetScheduler().Finish(); } bool GuestGpu::IsGpuThread() noexcept { diff --git a/src/graphics/host_gpu/renderer/cache/bufferCache.cpp b/src/graphics/host_gpu/renderer/cache/bufferCache.cpp index 889af21f2..4fbcb1c86 100644 --- a/src/graphics/host_gpu/renderer/cache/bufferCache.cpp +++ b/src/graphics/host_gpu/renderer/cache/bufferCache.cpp @@ -143,7 +143,7 @@ void BufferCache::DownloadBufferMemory(std::span copies) { } download.Commit(); const auto completion_tick = m_scheduler.CurrentTick(); - m_scheduler.FinishCurrent(); + m_scheduler.Finish(); m_scheduler.WaitPriorityOperations(completion_tick); cursor = 0; for (const auto& copy: batch) { @@ -315,7 +315,7 @@ BufferId BufferCache::FindBuffer(uint64_t vaddr, uint64_t size) { BufferId BufferCache::CreateBuffer(uint64_t vaddr, uint64_t size) { auto& command = m_scheduler.Current(); - EXIT_IF(command.IsInvalid() || command.IsExecute()); + EXIT_IF(command.IsInvalid()); auto begin = vaddr & ~(CACHING_PAGE_SIZE - 1); auto end = (vaddr + size + CACHING_PAGE_SIZE - 1) & ~(CACHING_PAGE_SIZE - 1); auto first = m_buffers.lower_bound(begin); @@ -428,7 +428,7 @@ std::pair BufferCache::ObtainBuffer(uint64_t vaddr, uint64_t bool is_written, bool is_texel_buffer, BufferId id) { auto& command = m_scheduler.Current(); - if (command.IsInvalid() || command.IsExecute() || vaddr == 0 || size == 0 || + if (command.IsInvalid() || vaddr == 0 || size == 0 || vaddr >= TRACKER_ADDRESS_SIZE || size > TRACKER_ADDRESS_SIZE - vaddr) { EXIT("BufferCache: buffer request requires a recording command buffer\n"); } diff --git a/src/graphics/host_gpu/renderer/cache/gpuResourceManager.cpp b/src/graphics/host_gpu/renderer/cache/gpuResourceManager.cpp index b09e95996..35caadf81 100644 --- a/src/graphics/host_gpu/renderer/cache/gpuResourceManager.cpp +++ b/src/graphics/host_gpu/renderer/cache/gpuResourceManager.cpp @@ -60,7 +60,7 @@ void GpuResourceManager::UnmapMemory(uint64_t vaddr, uint64_t size) { const auto unmap = [this, vaddr, size] { if (m_scheduler.Active()) { const auto tick = m_scheduler.CurrentTick(); - m_scheduler.FinishCurrent(); + m_scheduler.Finish(); m_scheduler.WaitPriorityOperations(tick); } m_buffer_cache.InvalidateMemory(vaddr, size); diff --git a/src/graphics/host_gpu/renderer/commandScheduler.cpp b/src/graphics/host_gpu/renderer/commandScheduler.cpp index f56cd84db..2abf691b2 100644 --- a/src/graphics/host_gpu/renderer/commandScheduler.cpp +++ b/src/graphics/host_gpu/renderer/commandScheduler.cpp @@ -1,96 +1,97 @@ #include "graphics/host_gpu/renderer/commandScheduler.h" #include "common/assert.h" +#include "common/logging/log.h" #include "graphics/host_gpu/graphicContext.h" #include +#include +#include namespace Libs::Graphics { static thread_local CommandScheduler* g_deferred_callback_scheduler = nullptr; -void CommandSlot::Reset() { - EXIT_IF(buffer == nullptr); - const auto result = buffer.reset(vk::CommandBufferResetFlagBits::eReleaseResources); - if (result != vk::Result::eSuccess) { - EXIT("failed to reset Vulkan command buffer: %s (%d)\n", VulkanToString(result).c_str(), - static_cast(result)); - } +namespace { + +void ReportVulkanFatal(const char* what, vk::Result result, uint64_t tick, uint32_t debug_op, + uint64_t debug_submit, uint32_t arg0, uint32_t arg1, uint32_t arg2, + uint32_t arg3, uint64_t arg4) { + LOGF("%s failed: %s (%d), tick=%" PRIu64 " debug_op=%u debug_submit=%" PRIu64 + " args=%u,%u,%u,%u,0x%016" PRIx64 "\n", + what, VulkanToString(result).c_str(), static_cast(result), tick, debug_op, + debug_submit, arg0, arg1, arg2, arg3, arg4); + std::printf("%s failed: %s (%d), tick=%" PRIu64 " debug_op=%u debug_submit=%" PRIu64 + " args=%u,%u,%u,%u,0x%016" PRIx64 "\n", + what, VulkanToString(result).c_str(), static_cast(result), tick, debug_op, + debug_submit, arg0, arg1, arg2, arg3, arg4); + std::fflush(stdout); } -CommandScheduler::CommandPool::~CommandPool() { - Destroy(); -} - -void CommandScheduler::CommandPool::Create(GraphicContext& graphics) { - EXIT_IF(m_pool != nullptr || m_graphics != nullptr || - graphics.queue_family == static_cast(-1)); - m_graphics = &graphics; +} // namespace +CommandScheduler::CommandPool::CommandPool(GraphicContext& graphics, MasterSemaphore& master) + : m_graphics(graphics), m_master(master) { + EXIT_IF(graphics.queue_family == static_cast(-1)); vk::CommandPoolCreateInfo create {}; create.sType = vk::StructureType::eCommandPoolCreateInfo; create.queueFamilyIndex = graphics.queue_family; - create.flags = vk::CommandPoolCreateFlagBits::eResetCommandBuffer; + create.flags = vk::CommandPoolCreateFlagBits::eTransient | + vk::CommandPoolCreateFlagBits::eResetCommandBuffer; const auto result = graphics.device.createCommandPool(&create, nullptr, &m_pool); EXIT_NOT_IMPLEMENTED(result != vk::Result::eSuccess || m_pool == nullptr); } -CommandSlot* CommandScheduler::CommandPool::CreateSlot() { - EXIT_IF(m_graphics == nullptr); - auto& graphics = *m_graphics; +CommandScheduler::CommandPool::~CommandPool() { + m_graphics.device.destroyCommandPool(m_pool, nullptr); +} + +size_t CommandScheduler::CommandPool::Grow() { + const auto first = m_ticks.size(); + m_ticks.resize(first + GrowStep); + m_buffers.resize(first + GrowStep); vk::CommandBufferAllocateInfo allocate {}; allocate.sType = vk::StructureType::eCommandBufferAllocateInfo; allocate.commandPool = m_pool; allocate.level = vk::CommandBufferLevel::ePrimary; - allocate.commandBufferCount = 1; - vk::CommandBuffer buffer = nullptr; - EXIT_IF(graphics.device.allocateCommandBuffers(&allocate, &buffer) != vk::Result::eSuccess); - - vk::FenceCreateInfo fence_create {}; - fence_create.sType = vk::StructureType::eFenceCreateInfo; - fence_create.flags = vk::FenceCreateFlagBits::eSignaled; - vk::Fence fence = nullptr; - if (graphics.device.createFence(&fence_create, nullptr, &fence) != vk::Result::eSuccess) { - graphics.device.freeCommandBuffers(m_pool, 1, &buffer); - EXIT("failed to create command-buffer fence\n"); - } - - auto& slot = m_slots.emplace_back(); - slot.pool_mutex = &m_mutex; - slot.id = static_cast(m_slots.size() - 1); - slot.buffer = buffer; - slot.fence = fence; - return &slot; + allocate.commandBufferCount = static_cast(GrowStep); + EXIT_IF(m_graphics.device.allocateCommandBuffers(&allocate, m_buffers.data() + first) != + vk::Result::eSuccess); + return first; } -CommandSlot* CommandScheduler::CommandPool::Allocate(GraphicContext& graphics) { - Common::LockGuard lock(m_mutex); - if (m_pool == nullptr) { - Create(graphics); - } - EXIT_IF(m_graphics != &graphics); - auto found = std::ranges::find_if(m_slots, [](const auto& slot) { return !slot.busy; }); - auto* slot = found != m_slots.end() ? &*found : CreateSlot(); - slot->busy = true; - slot->Reset(); - return slot; -} +vk::CommandBuffer CommandScheduler::CommandPool::Commit() { + auto gpu_tick = m_master.KnownGpuTick(); + const auto search = [this, &gpu_tick](size_t begin, size_t end) -> std::optional { + for (size_t index = begin; index < end; ++index) { + if (gpu_tick >= m_ticks[index]) { + m_ticks[index] = m_master.CurrentTick(); + return index; + } + } + return std::nullopt; + }; -void CommandScheduler::CommandPool::Destroy() { - Common::LockGuard lock(m_mutex); - if (m_pool == nullptr) { - return; + auto found = search(m_hint, m_ticks.size()); + if (!found) { + m_master.Refresh(); + gpu_tick = m_master.KnownGpuTick(); + found = search(m_hint, m_ticks.size()); } - EXIT_IF(std::ranges::any_of(m_slots, [](const auto& slot) { return slot.busy; })); - EXIT_IF(m_graphics == nullptr); - for (const auto& slot: m_slots) { - m_graphics->device.destroyFence(slot.fence, nullptr); + if (!found) { + found = search(0, m_hint); } - m_graphics->device.destroyCommandPool(m_pool, nullptr); - m_slots.clear(); - m_pool = nullptr; - m_graphics = nullptr; + if (!found) { + found = Grow(); + m_ticks[*found] = m_master.CurrentTick(); + } + + m_hint = *found + 1; + if (m_hint == m_ticks.size()) { + m_hint = 0; + } + return m_buffers[*found]; } bool CommandScheduler::InDeferredOperation() noexcept { @@ -99,10 +100,8 @@ bool CommandScheduler::InDeferredOperation() noexcept { CommandScheduler::CommandScheduler(RenderContext& context, GraphicContext& graphics) : m_master(graphics), m_context(context), m_graphics(graphics), - m_priority_thread([this](std::stop_token stop) { PriorityOperationsThread(stop); }) { - m_buffers.reserve(CommandBufferGrowStep); - m_buffer_ticks.reserve(CommandBufferGrowStep); -} + m_command_pool(graphics, m_master), m_command(*this), + m_priority_thread([this](std::stop_token stop) { PriorityOperationsThread(stop); }) {} CommandScheduler::~CommandScheduler() { Shutdown(); @@ -128,9 +127,11 @@ void CommandScheduler::Shutdown() { } m_operation_state = OperationState::Draining; } - if (Active() && m_recording) { - Finish(); + if (!m_command.IsInvalid()) { + Submit(); } + m_master.Wait(CurrentTick() - 1); + PopPendingOperations(); DrainPriorityOperations(); m_priority_thread.request_stop(); m_operation_available.notify_all(); @@ -156,14 +157,10 @@ void CommandScheduler::Begin(HW::Context& registers, HW::UserConfig& user_config m_user_config = &user_config; m_shaders = &shaders; - if (!Active()) { - m_current = static_cast(GrowCommandBuffers()); - } - - BindCurrent(); - if (!m_recording) { - Current().Begin(); - m_recording = true; + if (m_command.IsInvalid()) { + BeginNext(); + } else { + BindCurrent(); } } @@ -172,7 +169,7 @@ void CommandScheduler::BeginRendering(const RenderState& state) { } void CommandScheduler::EndRendering() { - if (Active() && m_recording) { + if (Active() && !m_command.IsInvalid()) { Current().EndRendering(); } } @@ -183,60 +180,40 @@ void CommandScheduler::Flush() { } void CommandScheduler::Flush(SubmitInfo& submit) { - SubmitCurrent(submit); + Submit(submit); BeginNext(); } -CommandBuffer& CommandScheduler::FlushAndGetSubmitted() { - SubmitInfo submit; - auto& submitted = SubmitCurrent(submit); +void CommandScheduler::FlushAndWait() { + const auto tick = Submit(); + m_master.Wait(tick); BeginNext(); - return submitted; } void CommandScheduler::Finish() { CheckActive(); - const auto tick = CurrentTick(); - if (m_recording) { - SubmitInfo submit; - SubmitCurrent(submit); + if (!m_command.IsInvalid()) { + Submit(); } - for (auto& buffer: m_buffers) { - buffer->WaitForFenceAndReset(); - } - m_master.Wait(tick); + m_master.Wait(CurrentTick() - 1); PopPendingOperations(); - BindCurrent(); - Current().Begin(); - m_recording = true; -} - -void CommandScheduler::FinishCurrent() { - SubmitInfo submit; - auto& submitted = SubmitCurrent(submit); - submitted.WaitForFenceAndReset(); - m_master.Refresh(); - PopPendingOperations(); - submitted.Begin(); - m_recording = true; + BeginNext(); } void CommandScheduler::Wait(uint64_t tick) { - CheckActive(); EXIT_IF(tick > CurrentTick()); - if (tick >= CurrentTick()) { + if (tick == CurrentTick()) { + CheckActive(); // A stream-buffer wrap can wait while a draw is being prepared through a reference to - // Current(). Recycle the same command object so that reference remains valid. Deferred + // Current(). The wrapper stays stable while its pooled Vulkan buffer is retired. Deferred // resources are released only at the next GPU operation boundary. - SubmitInfo submit; - auto& submitted = SubmitCurrent(submit); - submitted.WaitForFenceAndReset(); - m_master.Refresh(); - submitted.Begin(); - m_recording = true; - return; + const auto submitted_tick = Submit(); + EXIT_IF(submitted_tick != tick); + m_master.Wait(tick); + BeginNext(); + } else { + m_master.Wait(tick); } - m_master.Wait(tick); } void CommandScheduler::PopPendingOperations() { @@ -365,83 +342,81 @@ bool CommandScheduler::IsFree(uint64_t tick) { return m_master.IsFree(tick); } -CommandSlot* CommandScheduler::AllocateCommandBuffer() { - return m_command_pool.Allocate(m_graphics); -} - -uint64_t CommandScheduler::NextSubmitSequence() noexcept { - return m_submit_sequence.fetch_add(1, std::memory_order_relaxed) + 1; -} - void CommandScheduler::CheckActive() const { - EXIT_IF(!Active() || static_cast(m_current) >= m_buffers.size()); + EXIT_IF(!Active()); } -RenderCommandBuffer& CommandScheduler::Current() const { +RenderCommandBuffer& CommandScheduler::Current() { CheckActive(); - EXIT_IF(m_buffers[m_current] == nullptr); - return *m_buffers[m_current]; + return m_command; } -void CommandScheduler::BindCurrent() const { +void CommandScheduler::BindCurrent() { EXIT_IF(m_registers == nullptr || m_user_config == nullptr || m_shaders == nullptr); - Current().Bind(*m_registers, *m_user_config, *m_shaders); + m_command.Bind(*m_registers, *m_user_config, *m_shaders); } -CommandBuffer& CommandScheduler::SubmitCurrent(SubmitInfo& submit) { - CheckActive(); - EXIT_IF(!m_recording); - auto& submitted = Current(); - submitted.End(); - const auto signal_tick = m_master.NextTick(); - submit.AddSignal(m_master.Handle(), signal_tick); - submitted.Execute(submit); - m_buffer_ticks[static_cast(m_current)] = signal_tick; - m_recording = false; - return submitted; +CommandBuffer& CommandScheduler::BeginCommand() { + EXIT_IF(!m_command.IsInvalid()); + m_command.m_buffer = m_command_pool.Commit(); + m_command.Begin(); + return m_command; } -int CommandScheduler::FindReusableBuffer(uint64_t gpu_tick) const { - EXIT_IF(m_buffer_ticks.size() != m_buffers.size()); - const auto buffer_count = m_buffers.size(); - for (size_t offset = 1; offset <= buffer_count; ++offset) { - const auto candidate = (static_cast(m_current) + offset) % buffer_count; - if (gpu_tick >= m_buffer_ticks[candidate]) { - return static_cast(candidate); - } +uint64_t CommandScheduler::Submit(SubmitInfo submit) { + EXIT_IF(m_command.IsInvalid()); + EXIT_IF(submit.num_wait_semaphores > SubmitInfo::MaxSemaphores || + submit.num_signal_semaphores >= SubmitInfo::MaxSemaphores); + + m_command.End(); + const auto buffer = m_command.m_buffer; + auto& graphics = m_graphics; + EXIT_IF(graphics.queue == nullptr); + + vk::Result result; + uint64_t tick; + { + Common::LockGuard lock(graphics.queue_mutex); + tick = m_master.NextTick(); + submit.AddSignal(m_master.Handle(), tick); + + vk::TimelineSemaphoreSubmitInfo timeline_info {}; + timeline_info.sType = vk::StructureType::eTimelineSemaphoreSubmitInfo; + timeline_info.waitSemaphoreValueCount = submit.num_wait_semaphores; + timeline_info.pWaitSemaphoreValues = submit.wait_ticks.data(); + timeline_info.signalSemaphoreValueCount = submit.num_signal_semaphores; + timeline_info.pSignalSemaphoreValues = submit.signal_ticks.data(); + + vk::SubmitInfo submit_info {}; + submit_info.sType = vk::StructureType::eSubmitInfo; + submit_info.pNext = &timeline_info; + submit_info.waitSemaphoreCount = submit.num_wait_semaphores; + submit_info.pWaitSemaphores = submit.wait_semaphores.data(); + submit_info.pWaitDstStageMask = submit.wait_stages.data(); + submit_info.commandBufferCount = 1; + submit_info.pCommandBuffers = &buffer; + submit_info.signalSemaphoreCount = submit.num_signal_semaphores; + submit_info.pSignalSemaphores = submit.signal_semaphores.data(); + + result = graphics.queue.submit(1, &submit_info, nullptr); } - return -1; -} -size_t CommandScheduler::GrowCommandBuffers() { - const auto first = m_buffers.size(); - const auto end = first + CommandBufferGrowStep; - m_buffers.reserve(end); - m_buffer_ticks.reserve(end); - for (size_t i = first; i < end; ++i) { - m_buffers.emplace_back(std::make_unique(*this)); - m_buffer_ticks.push_back(0); + if (result != vk::Result::eSuccess) { + ReportVulkanFatal("vkQueueSubmit", result, tick, m_command.m_debug_op, + m_command.m_debug_submit_id, m_command.m_debug_arg0, + m_command.m_debug_arg1, m_command.m_debug_arg2, m_command.m_debug_arg3, + m_command.m_debug_arg4); } - return first; + EXIT_NOT_IMPLEMENTED(result != vk::Result::eSuccess); + + m_command.m_buffer = nullptr; + return tick; } void CommandScheduler::BeginNext() { - EXIT_IF(m_recording); - - auto candidate = FindReusableBuffer(m_master.KnownGpuTick()); - if (candidate < 0) { - m_master.Refresh(); - candidate = FindReusableBuffer(m_master.KnownGpuTick()); - } - if (candidate < 0) { - candidate = static_cast(GrowCommandBuffers()); - } - - m_current = candidate; - Current().WaitForFenceAndReset(); + EXIT_IF(!m_command.IsInvalid()); BindCurrent(); - Current().Begin(); - m_recording = true; + BeginCommand(); } } // namespace Libs::Graphics diff --git a/src/graphics/host_gpu/renderer/commandScheduler.h b/src/graphics/host_gpu/renderer/commandScheduler.h index bfa7f502e..b72b615dd 100644 --- a/src/graphics/host_gpu/renderer/commandScheduler.h +++ b/src/graphics/host_gpu/renderer/commandScheduler.h @@ -6,11 +6,7 @@ #include "graphics/host_gpu/renderer/masterSemaphore.h" #include "graphics/host_gpu/renderer/render.h" -#include -#include #include -#include -#include #include #include @@ -20,16 +16,6 @@ namespace Libs::Graphics { -struct CommandSlot { - Common::Mutex* pool_mutex = nullptr; - uint32_t id = 0; - vk::CommandBuffer buffer = nullptr; - vk::Fence fence = nullptr; - bool busy = false; - - void Reset(); -}; - class CommandScheduler { public: CommandScheduler(RenderContext& context, GraphicContext& graphics); @@ -41,9 +27,10 @@ public: void EndRendering(); void Flush(); void Flush(SubmitInfo& submit); - CommandBuffer& FlushAndGetSubmitted(); + void FlushAndWait(); void Finish(); - void FinishCurrent(); + CommandBuffer& BeginCommand(); + uint64_t Submit(SubmitInfo submit = {}); // Deferred callbacks can observe an externally owned drain, but cannot initiate shutdown: // the priority runner cannot join itself. void Shutdown(); @@ -55,9 +42,9 @@ public: void DeferPriorityOperation(Common::UniqueFunction&& operation); [[nodiscard]] static bool InDeferredOperation() noexcept; - [[nodiscard]] bool Active() const noexcept { return m_current >= 0; } + [[nodiscard]] bool Active() const noexcept { return m_registers != nullptr; } void CheckActive() const; - RenderCommandBuffer& Current() const; + RenderCommandBuffer& Current(); [[nodiscard]] uint64_t CurrentTick() const noexcept { return m_master.CurrentTick(); } [[nodiscard]] bool IsFree(uint64_t tick); [[nodiscard]] MasterSemaphore& GetMasterSemaphore() noexcept { return m_master; } @@ -65,25 +52,25 @@ public: [[nodiscard]] GraphicContext& Graphics() const noexcept { return m_graphics; } private: - static constexpr size_t CommandBufferGrowStep = 4; - class CommandPool { public: - CommandPool() = default; + CommandPool(GraphicContext& graphics, MasterSemaphore& master); ~CommandPool(); KYTY_CLASS_NO_COPY(CommandPool); - CommandSlot* Allocate(GraphicContext& graphics); + vk::CommandBuffer Commit(); private: - void Create(GraphicContext& graphics); - CommandSlot* CreateSlot(); - void Destroy(); + static constexpr size_t GrowStep = 4; - GraphicContext* m_graphics = nullptr; - Common::Mutex m_mutex; - vk::CommandPool m_pool = nullptr; - std::deque m_slots; + size_t Grow(); + + GraphicContext& m_graphics; + MasterSemaphore& m_master; + vk::CommandPool m_pool = nullptr; + std::vector m_buffers; + std::vector m_ticks; + size_t m_hint = 0; }; enum class OperationState { Open, Draining, Closed }; @@ -93,39 +80,28 @@ private: uint64_t tick = 0; }; - void BindCurrent() const; - CommandBuffer& SubmitCurrent(SubmitInfo& submit); - void BeginNext(); - void PopPendingOperations(bool refresh_gpu_tick); - [[nodiscard]] int FindReusableBuffer(uint64_t gpu_tick) const; - [[nodiscard]] size_t GrowCommandBuffers(); - void PriorityOperationsThread(std::stop_token stop); - void RunOperation(Common::UniqueFunction&& operation); - [[nodiscard]] CommandSlot* AllocateCommandBuffer(); - [[nodiscard]] uint64_t NextSubmitSequence() noexcept; + void BindCurrent(); + void BeginNext(); + void PopPendingOperations(bool refresh_gpu_tick); + void PriorityOperationsThread(std::stop_token stop); + void RunOperation(Common::UniqueFunction&& operation); - MasterSemaphore m_master; - RenderContext& m_context; - GraphicContext& m_graphics; - CommandPool m_command_pool; - std::vector> m_buffers; - std::vector m_buffer_ticks; - std::queue m_pending_operations; - std::queue m_priority_operations; - std::mutex m_operation_mutex; - std::condition_variable m_operation_available; - std::jthread m_priority_thread; - bool m_priority_active = false; - uint64_t m_priority_active_tick = 0; - OperationState m_operation_state = OperationState::Open; - int m_current = -1; - bool m_recording = false; - HW::Context* m_registers = nullptr; - HW::UserConfig* m_user_config = nullptr; - HW::Shader* m_shaders = nullptr; - std::atomic m_submit_sequence = 0; - - friend class CommandBuffer; + MasterSemaphore m_master; + RenderContext& m_context; + GraphicContext& m_graphics; + CommandPool m_command_pool; + RenderCommandBuffer m_command; + std::queue m_pending_operations; + std::queue m_priority_operations; + std::mutex m_operation_mutex; + std::condition_variable m_operation_available; + std::jthread m_priority_thread; + bool m_priority_active = false; + uint64_t m_priority_active_tick = 0; + OperationState m_operation_state = OperationState::Open; + HW::Context* m_registers = nullptr; + HW::UserConfig* m_user_config = nullptr; + HW::Shader* m_shaders = nullptr; }; } // namespace Libs::Graphics diff --git a/src/graphics/host_gpu/renderer/context.cpp b/src/graphics/host_gpu/renderer/context.cpp index 34805a904..f5dce5225 100644 --- a/src/graphics/host_gpu/renderer/context.cpp +++ b/src/graphics/host_gpu/renderer/context.cpp @@ -1,7 +1,5 @@ #include "common/assert.h" #include "common/common.h" -#include "common/emulatorConfig.h" -#include "common/logging/log.h" #include "common/profiler.h" #include "common/threads.h" #include "graphics/host_gpu/graphicContext.h" @@ -16,70 +14,29 @@ #include #include -#include #include namespace Libs::Graphics { -namespace { - -void ReportVulkanFatal(const char* what, vk::Result result, uint32_t slot, uint64_t submit_seq, - uint32_t debug_op, uint64_t debug_submit, uint32_t arg0, uint32_t arg1, - uint32_t arg2, uint32_t arg3, uint64_t arg4) { - LOGF("%s failed: %s (%d), slot=%u submit_seq=%" PRIu64 " debug_op=%u debug_submit=%" PRIu64 - " args=%u,%u,%u,%u,0x%016" PRIx64 "\n", - what, VulkanToString(result).c_str(), static_cast(result), slot, submit_seq, debug_op, - debug_submit, arg0, arg1, arg2, arg3, arg4); - std::printf("%s failed: %s (%d), slot=%u submit_seq=%" PRIu64 - " debug_op=%u debug_submit=%" PRIu64 " args=%u,%u,%u,%u,0x%016" PRIx64 "\n", - what, VulkanToString(result).c_str(), static_cast(result), slot, submit_seq, - debug_op, debug_submit, arg0, arg1, arg2, arg3, arg4); - std::fflush(stdout); -} - -} // namespace - CommandBuffer::CommandBuffer(CommandScheduler& scheduler) - : m_context(scheduler.Context()), m_scheduler(scheduler), m_graphics(scheduler.Graphics()), - m_slot(scheduler.AllocateCommandBuffer()) {} - -CommandBuffer::~CommandBuffer() { - Release(); -} + : m_context(scheduler.Context()), m_graphics(scheduler.Graphics()) {} bool CommandBuffer::IsInvalid() const { - return m_slot == nullptr; + return m_buffer == nullptr; } vk::CommandBuffer CommandBuffer::Handle() const { EXIT_IF(IsInvalid()); - - const auto handle = m_slot->buffer; - EXIT_IF(handle == nullptr); - return handle; + return m_buffer; } -void CommandBuffer::Release() { - EXIT_IF(IsInvalid()); - - Common::LockGuard lock(*m_slot->pool_mutex); - - WaitForFence(); - - m_slot->busy = false; - m_slot->Reset(); - m_slot = nullptr; - - EXIT_NOT_IMPLEMENTED(!IsInvalid()); -} - -void CommandBuffer::Begin() const { - EXIT_IF(m_rendering); +void CommandBuffer::Begin() { + EXIT_IF(m_rendering || IsInvalid()); auto buffer = Handle(); vk::CommandBufferBeginInfo begin_info {}; begin_info.sType = vk::StructureType::eCommandBufferBeginInfo; begin_info.pNext = nullptr; - begin_info.flags = {}; + begin_info.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit; begin_info.pInheritanceInfo = nullptr; auto result = buffer.begin(&begin_info); @@ -107,106 +64,6 @@ void CommandBuffer::SetDebugInfo(uint32_t op, uint64_t submit_id, uint32_t arg0, m_debug_arg4 = arg4; } -void CommandBuffer::Execute(const SubmitInfo& submit) { - EXIT_IF(IsInvalid()); - EXIT_IF(m_execute); - EXIT_IF(submit.num_wait_semaphores > SubmitInfo::MaxSemaphores || - submit.num_signal_semaphores > SubmitInfo::MaxSemaphores); - - auto buffer = Handle(); - auto fence = m_slot->fence; - - vk::TimelineSemaphoreSubmitInfo timeline_info {}; - timeline_info.sType = vk::StructureType::eTimelineSemaphoreSubmitInfo; - timeline_info.waitSemaphoreValueCount = submit.num_wait_semaphores; - timeline_info.pWaitSemaphoreValues = submit.wait_ticks.data(); - timeline_info.signalSemaphoreValueCount = submit.num_signal_semaphores; - timeline_info.pSignalSemaphoreValues = submit.signal_ticks.data(); - - vk::SubmitInfo submit_info {}; - submit_info.sType = vk::StructureType::eSubmitInfo; - submit_info.pNext = &timeline_info; - submit_info.waitSemaphoreCount = submit.num_wait_semaphores; - submit_info.pWaitSemaphores = submit.wait_semaphores.data(); - submit_info.pWaitDstStageMask = submit.wait_stages.data(); - submit_info.commandBufferCount = 1; - submit_info.pCommandBuffers = &buffer; - submit_info.signalSemaphoreCount = submit.num_signal_semaphores; - submit_info.pSignalSemaphores = submit.signal_semaphores.data(); - - auto& graphics = m_graphics; - EXIT_IF(graphics.queue == nullptr); - - auto result = graphics.device.resetFences(1, &fence); - if (result != vk::Result::eSuccess) { - ReportVulkanFatal("vkResetFences (before submit)", result, m_slot->id, m_submit_seq, - m_debug_op, m_debug_submit_id, m_debug_arg0, m_debug_arg1, m_debug_arg2, - m_debug_arg3, m_debug_arg4); - } - EXIT_NOT_IMPLEMENTED(result != vk::Result::eSuccess); - - if (Config::GraphicsDebugDumpEnabled()) { - LOGF("vkQueueSubmit begin: slot=%u waits=%u signals=%u debug_op=%u debug_submit=%" PRIu64 - " args=%u,%u,%u,%u,0x%016" PRIx64 "\n", - m_slot->id, submit.num_wait_semaphores, submit.num_signal_semaphores, m_debug_op, - m_debug_submit_id, m_debug_arg0, m_debug_arg1, m_debug_arg2, m_debug_arg3, - m_debug_arg4); - } - - { - Common::LockGuard lock(graphics.queue_mutex); - m_submit_seq = m_scheduler.NextSubmitSequence(); - result = graphics.queue.submit(1, &submit_info, fence); - } - - m_execute = true; - m_fence_waited = false; - - if (result != vk::Result::eSuccess) { - ReportVulkanFatal("vkQueueSubmit", result, m_slot->id, m_submit_seq, m_debug_op, - m_debug_submit_id, m_debug_arg0, m_debug_arg1, m_debug_arg2, m_debug_arg3, - m_debug_arg4); - } - EXIT_NOT_IMPLEMENTED(result != vk::Result::eSuccess); -} - -void CommandBuffer::WaitForFence() { - FinalizeFence(false); -} - -void CommandBuffer::WaitForFenceOnly() { - EXIT_IF(IsInvalid()); - if (!m_execute || m_fence_waited) { - return; - } - auto device = m_graphics.device; - auto result = device.waitForFences(1, &m_slot->fence, VK_TRUE, UINT64_MAX); - if (result != vk::Result::eSuccess) { - ReportVulkanFatal("vkWaitForFences", result, m_slot->id, m_submit_seq, m_debug_op, - m_debug_submit_id, m_debug_arg0, m_debug_arg1, m_debug_arg2, m_debug_arg3, - m_debug_arg4); - } - EXIT_NOT_IMPLEMENTED(result != vk::Result::eSuccess); - m_fence_waited = true; -} - -void CommandBuffer::WaitForFenceAndReset() { - FinalizeFence(true); -} - -void CommandBuffer::FinalizeFence(bool reset_recording) { - const bool was_executed = m_execute; - WaitForFenceOnly(); - if (was_executed) { - m_execute = false; - m_fence_waited = false; - if (reset_recording) { - Common::LockGuard lock(*m_slot->pool_mutex); - m_slot->Reset(); - } - } -} - void CommandBuffer::BeginRendering(const RenderState& state) const { EXIT_IF(state.width == 0 || state.height == 0 || state.num_layers == 0 || state.num_color_attachments > RENDER_COLOR_ATTACHMENTS_MAX); diff --git a/src/graphics/host_gpu/renderer/pipeline/descriptors.cpp b/src/graphics/host_gpu/renderer/pipeline/descriptors.cpp index 627eb9258..bbf05b851 100644 --- a/src/graphics/host_gpu/renderer/pipeline/descriptors.cpp +++ b/src/graphics/host_gpu/renderer/pipeline/descriptors.cpp @@ -891,7 +891,7 @@ static vk::Sampler NativeSampler(RenderContext& context, static BufferView NativeUpload(RenderContext& context, std::span data) { EXIT_IF(data.empty()); auto& command_buffer = context.GetCommandScheduler().Current(); - EXIT_IF(command_buffer.IsInvalid() || command_buffer.IsExecute()); + EXIT_IF(command_buffer.IsInvalid()); auto& buffer = context.GetBufferCache().GetUtilityBuffer(MemoryUsage::Stream); const auto offset = buffer.Copy(data.data(), data.size_bytes(), 256); return {.buffer = buffer.Handle(), .offset = offset, .range = data.size_bytes()}; diff --git a/src/graphics/host_gpu/renderer/render.h b/src/graphics/host_gpu/renderer/render.h index 7a42cda16..48badb12e 100644 --- a/src/graphics/host_gpu/renderer/render.h +++ b/src/graphics/host_gpu/renderer/render.h @@ -25,7 +25,6 @@ class Shader; struct GraphicContext; struct ShaderBufferResource; struct ShaderComputeInputInfo; -struct CommandSlot; struct RenderDepthInfo; struct RenderColorInfo; struct DrawCallInfo; @@ -77,40 +76,31 @@ struct SubmitInfo { class CommandBuffer { public: - explicit CommandBuffer(CommandScheduler& scheduler); - ~CommandBuffer(); + ~CommandBuffer() = default; KYTY_CLASS_NO_COPY(CommandBuffer); [[nodiscard]] bool IsInvalid() const; - void Begin() const; - void End() const; - void Execute(const SubmitInfo& submit = {}); void SetDebugInfo(uint32_t op, uint64_t submit_id, uint32_t arg0 = 0, uint32_t arg1 = 0, uint32_t arg2 = 0, uint32_t arg3 = 0, uint64_t arg4 = 0); void BeginRendering(const RenderState& state) const; void EndRendering() const; - void WaitForFenceOnly(); - void WaitForFence(); - void WaitForFenceAndReset(); [[nodiscard]] vk::CommandBuffer Handle() const; [[nodiscard]] GraphicContext& GetGraphics() const noexcept { return m_graphics; } [[nodiscard]] RenderContext& GetContext() const noexcept { return m_context; } - [[nodiscard]] bool IsExecute() const { return m_execute; } + +protected: + explicit CommandBuffer(CommandScheduler& scheduler); private: - void Release(); - void FinalizeFence(bool reset_recording); + void Begin(); + void End() const; RenderContext& m_context; - CommandScheduler& m_scheduler; GraphicContext& m_graphics; - CommandSlot* m_slot = nullptr; - bool m_execute = false; - bool m_fence_waited = false; - uint64_t m_submit_seq = 0; + vk::CommandBuffer m_buffer = nullptr; uint32_t m_debug_op = 0; uint64_t m_debug_submit_id = 0; uint32_t m_debug_arg0 = 0; @@ -120,12 +110,12 @@ private: uint64_t m_debug_arg4 = 0; mutable RenderState m_render_state; mutable bool m_rendering = false; + + friend class CommandScheduler; }; class RenderCommandBuffer final: public CommandBuffer { public: - explicit RenderCommandBuffer(CommandScheduler& scheduler): CommandBuffer(scheduler) {} - void Bind(HW::Context& registers, HW::UserConfig& user_config, HW::Shader& shaders) noexcept { m_registers = ®isters; m_user_config = &user_config; @@ -137,9 +127,13 @@ public: [[nodiscard]] HW::Shader& GetShaders() const noexcept { return *m_shaders; } private: + explicit RenderCommandBuffer(CommandScheduler& scheduler): CommandBuffer(scheduler) {} + HW::Context* m_registers = nullptr; HW::UserConfig* m_user_config = nullptr; HW::Shader* m_shaders = nullptr; + + friend class CommandScheduler; }; class RenderExecutor { diff --git a/src/graphics/host_gpu/renderer/renderContext.cpp b/src/graphics/host_gpu/renderer/renderContext.cpp index 7111962c6..224beccff 100644 --- a/src/graphics/host_gpu/renderer/renderContext.cpp +++ b/src/graphics/host_gpu/renderer/renderContext.cpp @@ -38,7 +38,7 @@ void RenderContext::ShutdownGpu() { } if (m_video_out != nullptr) { if (m_command_scheduler.Active()) { - m_command_scheduler.FinishCurrent(); + m_command_scheduler.Finish(); } m_command_scheduler.DrainPriorityOperations(); m_video_out = nullptr; diff --git a/src/graphics/presentation/window/swapchain.cpp b/src/graphics/presentation/window/swapchain.cpp index 4b9e2d4ec..e3977089a 100644 --- a/src/graphics/presentation/window/swapchain.cpp +++ b/src/graphics/presentation/window/swapchain.cpp @@ -58,10 +58,10 @@ namespace Libs::Graphics { struct Presenter::Frame { - VulkanImage image; - std::unique_ptr present_commands; - bool busy = false; - bool reusing_last = false; + VulkanImage image; + uint64_t present_tick = 0; + bool busy = false; + bool reusing_last = false; void Configure(GraphicContext& graphics, vk::Extent2D extent, vk::Format format); void Transit(vk::CommandBuffer command, vk::ImageLayout layout, vk::AccessFlags2 access); @@ -71,12 +71,11 @@ struct Presenter::Frame { class FramePool final { public: - explicit FramePool(WindowContext& window): m_window(window) {} + FramePool(WindowContext& window, CommandScheduler& scheduler) + : m_window(window), m_scheduler(scheduler) {} ~FramePool() { + m_scheduler.Wait(m_scheduler.CurrentTick() - 1); for (auto& frame: m_frames) { - if (frame->present_commands != nullptr) { - frame->present_commands->WaitForFenceOnly(); - } if (frame->image.image != nullptr) { m_window.graphic_ctx.DeleteImage(frame->image); } @@ -188,15 +187,10 @@ public: } private: - static void WaitForFrame(Presenter::Frame& frame) { - // The producer only waits here. Reset stays on the presentation thread that owns the - // allocating Vulkan command pool. - if (frame.present_commands != nullptr) { - frame.present_commands->WaitForFenceOnly(); - } - } + void WaitForFrame(Presenter::Frame& frame) { m_scheduler.Wait(frame.present_tick); } WindowContext& m_window; + CommandScheduler& m_scheduler; Common::Mutex m_mutex; Common::CondVar m_available; std::vector> m_frames; @@ -330,7 +324,7 @@ public: [[nodiscard]] Status AcquireNextImage(); [[nodiscard]] bool PrepareImeOverlay(); void RecordPresentCommands(CommandBuffer& command, VulkanImage& source, bool draw_ime_overlay); - void Submit(CommandBuffer& command); + uint64_t Submit(CommandScheduler& scheduler); [[nodiscard]] Status Present(); [[nodiscard]] uint32_t ImageCount() const noexcept { @@ -358,7 +352,7 @@ private: struct Presenter::Impl { explicit Impl(WindowContext& owner) : renderer(*owner.render_context), window(owner), swapchain(owner), - present_scheduler(renderer, owner.graphic_ctx), frames(owner) { + present_scheduler(renderer, owner.graphic_ctx), frames(owner, present_scheduler) { EXIT_IF(owner.render_context == nullptr); swapchain.Create(); frames.Initialize(swapchain.ImageCount(), swapchain.Format()); @@ -628,7 +622,6 @@ void Swapchain::RecordPresentCommands(CommandBuffer& command, VulkanImage& sourc } EXIT_IF(m_image_index >= m_images.size()); auto vk_command = command.Handle(); - command.Begin(); vk::ImageMemoryBarrier to_transfer {}; to_transfer.sType = vk::StructureType::eImageMemoryBarrier; @@ -699,15 +692,14 @@ void Swapchain::RecordPresentCommands(CommandBuffer& command, VulkanImage& sourc vk::DependencyFlagBits::eByRegion, 0, nullptr, 0, nullptr, 1, &to_present); } - command.End(); } -void Swapchain::Submit(CommandBuffer& command) { +uint64_t Swapchain::Submit(CommandScheduler& scheduler) { EXIT_IF(m_frame_index >= m_image_acquired.size() || m_image_index >= m_render_complete.size()); SubmitInfo submit; submit.AddWait(m_image_acquired[m_frame_index], 1, vk::PipelineStageFlagBits::eTransfer); submit.AddSignal(m_render_complete[m_image_index]); - command.Execute(submit); + return scheduler.Submit(submit); } Swapchain::Status Swapchain::Present() { @@ -782,15 +774,9 @@ Presenter::Frame& Presenter::PrepareBlankFrame(uint32_t width, uint32_t height, EXIT_IF(producer->IsInvalid()); frame->Clear(*producer, clear); } else { - if (frame->present_commands == nullptr) { - frame->present_commands = std::make_unique(m_impl->present_scheduler); - } - auto& command = *frame->present_commands; - command.WaitForFenceAndReset(); - command.Begin(); + auto& command = m_impl->present_scheduler.BeginCommand(); frame->Clear(command, clear); - command.End(); - command.Execute(); + frame->present_tick = m_impl->present_scheduler.Submit(); } return *frame; } @@ -848,16 +834,12 @@ void Presenter::Present(Frame& frame, bool reuse) { m_impl->RecoverSwapchain(status); continue; } - if (frame.present_commands == nullptr) { - frame.present_commands = std::make_unique(m_impl->present_scheduler); - } { Common::LockGuard render_lock(m_impl->renderer.GetMutex()); - frame.present_commands->WaitForFenceAndReset(); - auto& command = *frame.present_commands; - const bool draw_ime_overlay = ime_visual.active && swapchain.PrepareImeOverlay(); + auto& command = m_impl->present_scheduler.BeginCommand(); + const bool draw_ime_overlay = ime_visual.active && swapchain.PrepareImeOverlay(); swapchain.RecordPresentCommands(command, frame.image, draw_ime_overlay); - swapchain.Submit(command); + frame.present_tick = swapchain.Submit(m_impl->present_scheduler); } status = swapchain.Present(); if (status != Swapchain::Status::Success) { diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index 1186cad52..eb37799a7 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -1496,26 +1496,6 @@ public: return Renderer(); } - void CheckCommandPoolGrowth() { - EnsureRuntimeContext(); - std::vector> commands; - for (uint32_t i = 0; i < 12; i++) { - commands.push_back( - std::make_unique(Renderer().GetCommandScheduler())); - } - for (auto &command : commands) { - Require("CommandPoolGrowth", "allocation", !command->IsInvalid(), - "unified command pool failed to grow"); - command->Begin(); - command->End(); - command->Execute(); - } - for (auto &command : commands) { - command->WaitForFence(); - } - std::printf("[host] %-32s ok\n", "CommandPoolGrowth"); - } - void CheckDescriptorHeapLargeSet() { EnsureRuntimeContext(); std::array bindings{}; @@ -1569,7 +1549,7 @@ public: for (uint32_t i = 0; i < 22; i++) { commit(); } - scheduler.FinishCurrent(); + scheduler.Finish(); for (uint32_t i = 0; i < 21; i++) { commit(); } @@ -3223,7 +3203,7 @@ public: BufferCacheTestAccess::PageOwner( cache, index_begin + 2 * index_page) == index_bridge, "bridging acquisition did not publish its merged Buffer range"); - scheduler.FinishCurrent(); + scheduler.Finish(); constexpr uint64_t recycled_offset = index_offset + 4 * index_page; const auto recycled = cache.FindBuffer(base + recycled_offset + 0x100, sizeof(uint32_t)); @@ -4528,7 +4508,7 @@ public: layered_raw_d16_readback.size, vk::PipelineStageFlagBits::eTransfer, vk::AccessFlagBits::eTransferWrite); - scheduler.FinishCurrent(); + scheduler.Finish(); const auto layered_raw_d16_words = ReadBuffer(name, layered_raw_d16_readback, 6); std::array layered_raw_d16_after{}; @@ -4598,7 +4578,7 @@ public: const bool compressed_block_download = TextureCacheTestAccess::TryDownload(texture_cache, compressed_block_image); - scheduler.FinishCurrent(); + scheduler.Finish(); scheduler.DrainPriorityOperations(); std::array block_alias_after{}; std::memcpy(block_alias_after.data(), memory + block_alias_offset, @@ -6151,7 +6131,7 @@ public: gc_before_completion == gc_stale_values, "GC submitted per image or published a readback before GPU " "completion"); - scheduler.FinishCurrent(); + scheduler.Finish(); scheduler.DrainPriorityOperations(); auto refreshed_buffer_alias = resources.GetBufferCache().ObtainBuffer( gc_image_desc_a.info.data.address, gc_image_desc_a.info.data.size, @@ -6183,7 +6163,7 @@ public: HostReadBarrier(alias_readback.buffer, alias_readback.size, vk::PipelineStageFlagBits::eTransfer, vk::AccessFlagBits::eTransferWrite); - scheduler.FinishCurrent(); + scheduler.Finish(); Require(name, "post-image Buffer alias content", ReadBuffer(name, alias_readback, 1) == std::vector{gc_image_values[0]}, @@ -6224,7 +6204,7 @@ public: submit_before_completion == submit_readback_stale && scheduler.CurrentTick() == submit_readback_tick, "submit-time linear readback published synchronously"); - scheduler.FinishCurrent(); + scheduler.Finish(); scheduler.DrainPriorityOperations(); uint32_t submit_after_completion = 0; Libs::LibKernel::Memory::TryReadBacking( @@ -6293,7 +6273,7 @@ public: linear_depth_before == linear_depth_guest && scheduler.CurrentTick() == linear_depth_tick, "linear depth readback published before GPU completion"); - scheduler.FinishCurrent(); + scheduler.Finish(); scheduler.DrainPriorityOperations(); std::array linear_depth_after{}; std::array linear_stencil_after{}; @@ -6394,7 +6374,7 @@ public: tiled_depth_before == tiled_depth_guest && scheduler.CurrentTick() == tiled_depth_tick, "tiled depth readback published before GPU completion"); - scheduler.FinishCurrent(); + scheduler.Finish(); scheduler.DrainPriorityOperations(); std::vector tiled_depth_after(tiled_depth_guest.size()); std::memcpy(tiled_depth_after.data(), memory + tiled_depth_offset, @@ -6453,7 +6433,7 @@ public: HostReadBarrier(tiled_depth_output.buffer, tiled_depth_size, vk::PipelineStageFlagBits::eTransfer, vk::AccessFlagBits::eTransferWrite); - scheduler.FinishCurrent(); + scheduler.Finish(); const auto tiled_depth_words = ReadBuffer(name, tiled_depth_output, tiled_depth_guest.size()); bool tiled_depth_matches = true; @@ -6516,7 +6496,7 @@ public: name, "tiled D16 download queue", TextureCacheTestAccess::TryDownload(texture_cache, tiled_d16_image), "layered tiled D16 fallback readback was rejected"); - scheduler.FinishCurrent(); + scheduler.Finish(); scheduler.DrainPriorityOperations(); std::vector tiled_d16_after(tiled_d16_guest.size()); std::array tiled_d16_stencil_after{}; @@ -6573,7 +6553,7 @@ public: HostReadBarrier(tiled_d16_output.buffer, tiled_depth_size, vk::PipelineStageFlagBits::eTransfer, vk::AccessFlagBits::eTransferWrite); - scheduler.FinishCurrent(); + scheduler.Finish(); const auto tiled_d16_linear_words = ReadBuffer(name, tiled_d16_output, tiled_d16_words.size()); std::vector tiled_d16_linear_values(tiled_d16_guest.size()); @@ -6647,7 +6627,7 @@ public: HostReadBarrier(d16_storage_readback.buffer, d16_storage_readback.size, vk::PipelineStageFlagBits::eTransfer, vk::AccessFlagBits::eTransferWrite); - scheduler.FinishCurrent(); + scheduler.Finish(); const auto d16_storage_words = ReadBuffer(name, d16_storage_readback, 2); std::array d16_storage_values{}; std::memcpy(d16_storage_values.data(), d16_storage_words.data(), @@ -6690,7 +6670,7 @@ public: depth_image_retired, depth_proxy_retired, scheduler.CurrentTick(), depth_gc_tick, depth_before_completion, stale_added_stencil_depth) .c_str()); - scheduler.FinishCurrent(); + scheduler.Finish(); scheduler.DrainPriorityOperations(); float depth_after_completion = 0.0f; Libs::LibKernel::Memory::TryReadBacking( @@ -6789,7 +6769,7 @@ public: !TextureCacheTestAccess::Contains(texture_cache, image) && scheduler.CurrentTick() == tick, "near-capacity readback was rejected or synchronously submitted"); - scheduler.FinishCurrent(); + scheduler.Finish(); scheduler.DrainPriorityOperations(); bool content = true; for (const auto offset : sample_offsets) { @@ -7388,7 +7368,7 @@ public: color.desc.info.mip_layout[0].size == allocation_size && image.usage.render_target && image.IsGpuModified(), "supported depth-tiled color target was not resolved"); - scheduler.FinishCurrent(); + scheduler.Finish(); scheduler.DrainPriorityOperations(); std::vector cleared(allocation_size); Require(name, "clear guest backing", @@ -8097,7 +8077,7 @@ public: !TextureCacheTestAccess::PendingDownload(texture_cache, storage_id), "submit-time processing retained an acquired storage request"); RenderExecutorTestAccess::ResetBindings(executor); - scheduler.FinishCurrent(); + scheduler.Finish(); scheduler.DrainPriorityOperations(); uint32_t storage_downloaded_value = 0; Libs::LibKernel::Memory::TryReadBacking(storage_address, @@ -8748,7 +8728,7 @@ public: rebound_depth.height = 1; rebound_depth.samples = 1; rebound_depth.image_id = depth_id; - scheduler.FinishCurrent(); + scheduler.Finish(); Require(name, "deferred target slot erasure", TextureCacheTestAccess::Owner(texture_cache, target_subresource_id) == nullptr, @@ -24302,7 +24282,6 @@ int main(int argc, char **argv) { vulkan.CheckGraphicsPushConstantBank(); vulkan.CheckGpuMappedRangeLifecycle(); vulkan.CheckStreamBufferRing(); - vulkan.CheckCommandPoolGrowth(); vulkan.CheckGpuTilerCpuParity(); #if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS vulkan.CheckRenderExecutorColorVolumeDiscovery();