From dbcb444c05a0117db1ddefb756ac5ea7264c471f Mon Sep 17 00:00:00 2001 From: nmzik Date: Wed, 22 Jul 2026 17:44:16 +0200 Subject: [PATCH] unify graphics and compute on one Vulkan queue --- .../command_processor/commandProcessor.h | 21 +- src/graphics/guest_gpu/graphicsRun.cpp | 113 +++---- src/graphics/host_gpu/gpuTiler.cpp | 12 +- src/graphics/host_gpu/graphicContext.h | 17 +- .../host_gpu/renderer/bufferCache.cpp | 56 +--- src/graphics/host_gpu/renderer/bufferCache.h | 1 - src/graphics/host_gpu/renderer/context.cpp | 286 +++++++----------- .../host_gpu/renderer/dummyTextureCache.cpp | 5 +- src/graphics/host_gpu/renderer/image.cpp | 81 +++-- src/graphics/host_gpu/renderer/render.h | 32 +- .../host_gpu/renderer/resourceMutex.h | 2 +- .../host_gpu/renderer/textureCache.cpp | 18 +- src/graphics/host_gpu/renderer/tiler.cpp | 6 +- src/graphics/host_gpu/transfer.cpp | 67 +--- src/graphics/host_gpu/transfer.h | 10 +- src/graphics/host_gpu/vulkanInstance.h | 21 +- .../presentation/window/swapchain.cpp | 40 +-- .../presentation/window/vulkanWindow.cpp | 245 +++------------ tests/ShaderRecompilerComputeTests.cpp | 70 +++-- 19 files changed, 352 insertions(+), 751 deletions(-) diff --git a/src/graphics/guest_gpu/command_processor/commandProcessor.h b/src/graphics/guest_gpu/command_processor/commandProcessor.h index afcfa4856..f5fe4f7a3 100644 --- a/src/graphics/guest_gpu/command_processor/commandProcessor.h +++ b/src/graphics/guest_gpu/command_processor/commandProcessor.h @@ -9,6 +9,7 @@ #include #include +#include namespace Libs::Graphics { @@ -23,8 +24,6 @@ public: CommandScheduler(HW::Context& registers, HW::UserConfig& user_config, HW::Shader& shaders) : m_registers(registers), m_user_config(user_config), m_shaders(shaders) {} - void SetQueue(int queue) { m_queue = queue; } - int Queue() const { return m_queue; } bool Active() const { return m_current >= 0 && m_current < BuffersNum; } void CheckActive() const { EXIT_IF(!Active()); } @@ -40,7 +39,7 @@ public: } for (auto& buf: m_buffers) { EXIT_IF(buf != nullptr); - buf = new RenderCommandBuffer(m_queue, m_registers, m_user_config, m_shaders); + buf = new RenderCommandBuffer(m_registers, m_user_config, m_shaders); } m_current = 0; Current().Begin(); @@ -103,7 +102,6 @@ private: RenderCommandBuffer* m_buffers[BuffersNum] = {}; int m_current = -1; - int m_queue = -1; HW::Context& m_registers; HW::UserConfig& m_user_config; HW::Shader& m_shaders; @@ -118,7 +116,7 @@ public: int64_t flip_arg = 0; }; - CommandProcessor(): m_scheduler(m_ctx, m_ucfg, m_sh_ctx) {} + CommandProcessor(); ~CommandProcessor() { KYTY_NOT_IMPLEMENTED; } KYTY_CLASS_NO_COPY(CommandProcessor); @@ -226,9 +224,6 @@ public: void Run(uint32_t* data, uint32_t num_dw); - void SetQueue(int queue); - [[nodiscard]] int GetQueue() const { return m_scheduler.Queue(); } - [[nodiscard]] const FlipInfo& GetFlip() const { return m_flip; } void SetFlip(const FlipInfo& flip) { m_flip = flip; } @@ -264,11 +259,11 @@ private: uint64_t m_dispatch_indirect_args_base_addr = 0; uint32_t m_num_instances = 1; - inline static Common::Mutex m_mutex; - inline static std::array m_processors {}; - inline static bool m_readback_active = false; - inline static bool m_readback_finished = false; - Common::Mutex m_run_mutex; + inline static Common::Mutex m_mutex; + inline static std::vector m_processors; + inline static bool m_readback_active = false; + inline static bool m_readback_finished = false; + Common::Mutex m_run_mutex; CommandScheduler m_scheduler; diff --git a/src/graphics/guest_gpu/graphicsRun.cpp b/src/graphics/guest_gpu/graphicsRun.cpp index 2f47d1e47..cb20f6016 100644 --- a/src/graphics/guest_gpu/graphicsRun.cpp +++ b/src/graphics/guest_gpu/graphicsRun.cpp @@ -11,7 +11,6 @@ #include "graphics/guest_gpu/command_processor/pm4Dispatch.h" #include "graphics/guest_gpu/hardwareContext.h" #include "graphics/guest_gpu/pm4.h" -#include "graphics/host_gpu/graphicContext.h" #include "graphics/host_gpu/objects/label.h" #include "graphics/host_gpu/renderer/render.h" #include "graphics/host_gpu/renderer/renderContext.h" @@ -176,9 +175,6 @@ public: Start(); } - [[nodiscard]] int GetQueueId() const { return m_queue_id; } - void SetQueueId(int id) { m_queue_id = id; } - private: void Start() { Common::Thread t(ThreadRun, this); @@ -193,8 +189,7 @@ private: bool m_done = true; bool m_idle = true; - CommandProcessor* m_cp = nullptr; - int m_queue_id = -1; + CommandProcessor* m_cp = nullptr; struct DirectBatch { OwnedCmdBuffer buffer; @@ -206,6 +201,10 @@ private: class Gpu { public: + static constexpr uint32_t ComputePipeCount = 7; + static constexpr uint32_t RingsPerComputePipe = 8; + static constexpr uint32_t ComputeRingCount = ComputePipeCount * RingsPerComputePipe; + Gpu() { EXIT_NOT_IMPLEMENTED(!Common::Thread::IsMainThread()); Init(); @@ -228,15 +227,15 @@ private: void Init(); void WaitLocked(); - ComputeRing* GetRing(uint32_t ring_id); + ComputeRing* GetComputeRing(uint32_t ring_index); Common::Mutex m_mutex; CommandProcessor* m_gfx_cp = nullptr; GraphicsRing* m_gfx_ring = nullptr; - CommandProcessor* m_compute_cp[8] = {}; - ComputeRing* m_compute_ring[64] = {}; + std::array m_compute_cp {}; + std::array m_compute_ring {}; std::atomic_int m_done_num = 0; }; @@ -274,19 +273,11 @@ void Gpu::SubmitCompute(uint32_t queue, uint32_t* cmd_buffer, uint32_t num_dw, GpuMutexLock lock(m_mutex); constexpr uint32_t compute_queue_base = 0x20u; - constexpr uint32_t compute_queue_num = 7u * 8u; EXIT_NOT_IMPLEMENTED(queue < compute_queue_base || - queue >= compute_queue_base + compute_queue_num); + queue >= compute_queue_base + ComputeRingCount); uint32_t compute_queue = queue - compute_queue_base; - uint32_t pipe_id = (compute_queue >> 3u) & 0x7u; - uint32_t queue_id = compute_queue & 0x7u; - EXIT_NOT_IMPLEMENTED(pipe_id >= 7u); - EXIT_NOT_IMPLEMENTED(queue_id >= 8u); - - uint32_t ring_id = compute_queue + 1u; - - auto* ring = GetRing(ring_id); + auto* ring = GetComputeRing(compute_queue); ring->Submit(std::move(buffer), trigger_agc_interrupt_on_done); } @@ -297,10 +288,10 @@ void Gpu::SubmitFlipPreparation() { } void Gpu::Done() { - GraphicsRing* gfx_ring = nullptr; - CommandProcessor* gfx_cp = nullptr; - ComputeRing* compute_rings[64] {}; - CommandProcessor* compute_cps[8] {}; + GraphicsRing* gfx_ring = nullptr; + CommandProcessor* gfx_cp = nullptr; + std::array compute_rings {}; + std::array compute_cps {}; { GpuMutexLock lock(m_mutex); @@ -308,8 +299,8 @@ void Gpu::Done() { gfx_ring = m_gfx_ring; gfx_cp = m_gfx_cp; - std::copy(std::begin(m_compute_ring), std::end(m_compute_ring), std::begin(compute_rings)); - std::copy(std::begin(m_compute_cp), std::end(m_compute_cp), std::begin(compute_cps)); + compute_rings = m_compute_ring; + compute_cps = m_compute_cp; m_done_num++; } @@ -347,15 +338,15 @@ int Gpu::GetFrameNum() { } void Gpu::WaitLocked() { - GraphicsRing* gfx_ring = nullptr; - CommandProcessor* gfx_cp = nullptr; - ComputeRing* compute_rings[64] {}; - CommandProcessor* compute_cps[8] {}; + GraphicsRing* gfx_ring = nullptr; + CommandProcessor* gfx_cp = nullptr; + std::array compute_rings {}; + std::array compute_cps {}; - gfx_ring = m_gfx_ring; - gfx_cp = m_gfx_cp; - std::copy(std::begin(m_compute_ring), std::end(m_compute_ring), std::begin(compute_rings)); - std::copy(std::begin(m_compute_cp), std::end(m_compute_cp), std::begin(compute_cps)); + gfx_ring = m_gfx_ring; + gfx_cp = m_gfx_cp; + compute_rings = m_compute_ring; + compute_cps = m_compute_cp; if (gfx_ring != nullptr) { gfx_ring->WaitForIdle(); @@ -381,40 +372,28 @@ void Gpu::Init() { m_gfx_cp = new CommandProcessor; m_gfx_ring = new GraphicsRing; - m_gfx_cp->SetQueue(GraphicContext::QUEUE_GFX); m_gfx_ring->SetCp(*m_gfx_cp); - - EXIT_IF(GraphicContext::QUEUE_COMPUTE_NUM < 8); } -ComputeRing* Gpu::GetRing(uint32_t ring_id) { - int v = static_cast(ring_id - 1); - int pipe_id = v / 8; - int queue_id = v % 8; +ComputeRing* Gpu::GetComputeRing(uint32_t ring_index) { + EXIT_IF(ring_index >= ComputeRingCount); + const auto pipe_id = ring_index / RingsPerComputePipe; if (m_compute_cp[pipe_id] == nullptr) { m_compute_cp[pipe_id] = new CommandProcessor; - m_compute_cp[pipe_id]->SetQueue(GraphicContext::QUEUE_COMPUTE_START + pipe_id); } - if (m_compute_ring[v] == nullptr) { - m_compute_ring[v] = new ComputeRing; - m_compute_ring[v]->SetQueueId(queue_id); - m_compute_ring[v]->SetCp(*m_compute_cp[pipe_id]); + if (m_compute_ring[ring_index] == nullptr) { + m_compute_ring[ring_index] = new ComputeRing; + m_compute_ring[ring_index]->SetCp(*m_compute_cp[pipe_id]); } - return m_compute_ring[v]; + return m_compute_ring[ring_index]; } -void CommandProcessor::SetQueue(int queue) { +CommandProcessor::CommandProcessor(): m_scheduler(m_ctx, m_ucfg, m_sh_ctx) { Common::LockGuard lock(m_mutex); - if (queue < 0 || queue >= GraphicContext::QUEUES_NUM || - (m_processors[queue] != nullptr && m_processors[queue] != this)) { - EXIT("invalid command-processor queue registration: queue=%d owner=%p\n", queue, - static_cast(m_processors[queue])); - } - m_scheduler.SetQueue(queue); - m_processors[queue] = this; + m_processors.push_back(this); } void CommandProcessor::FinishReadbackTransaction() { @@ -429,19 +408,11 @@ void CommandProcessor::FinishReadbackTransaction() { } void CommandProcessor::FinishCommandProcessors() { - std::array processors {}; - uint32_t processor_count = 0; for (auto* processor: m_processors) { - if (processor == nullptr || - std::find(processors.begin(), processors.begin() + processor_count, processor) != - processors.begin() + processor_count) { - continue; - } - processors[processor_count++] = processor; processor->m_scheduler.SubmitForReadback(); } - for (uint32_t i = 0; i < processor_count; i++) { - processors[i]->m_scheduler.ResumeAfterReadback(); + for (auto* processor: m_processors) { + processor->m_scheduler.ResumeAfterReadback(); } } @@ -881,8 +852,8 @@ void ComputeRing::ThreadRun(void* data) { static std::atomic compute_batch_log_count {0}; if (num_dw <= 128 && buffer != nullptr && compute_batch_log_count.fetch_add(1) < 32) { - LOGF("compute direct batch: queue=%d, data=0x%016" PRIx64 ", num_dw=%" PRIu32 "\n", - ring->m_queue_id, reinterpret_cast(buffer), num_dw); + LOGF("compute direct batch: data=0x%016" PRIx64 ", num_dw=%" PRIu32 "\n", + reinterpret_cast(buffer), num_dw); for (uint32_t i = 0; i < std::min(num_dw, 16); i++) { LOGF("\t compute[%02" PRIu32 "] = 0x%08" PRIx32 "\n", i, buffer[i]); } @@ -1356,11 +1327,11 @@ void CommandProcessor::DispatchDirect(uint32_t thread_group_x, uint32_t thread_g const auto& cs = m_sh_ctx.GetCs().cs_regs; const auto& oa = m_ucfg.GetGdsOaCounter(m_ucfg.GetGdsOaState().GetIndex()); LOGF("QueuePoint DispatchDirect: frame=%u submit=%" PRIu64 - " queue=%d groups=%ux%ux%u local=%ux%ux%u mode=0x%08" PRIx32 - " wave=%u cs=0x%016" PRIx64 " oa_index=%u oa_enabled=%s oa_addr=0x%04" PRIx32 - " oa_space=0x%08" PRIx32 "\n", - frame_num, m_submit_id, m_scheduler.Queue(), thread_group_x, thread_group_y, - thread_group_z, std::max(cs.num_thread_x, 1u), std::max(cs.num_thread_y, 1u), + " groups=%ux%ux%u local=%ux%ux%u mode=0x%08" PRIx32 " wave=%u cs=0x%016" PRIx64 + " oa_index=%u oa_enabled=%s oa_addr=0x%04" PRIx32 " oa_space=0x%08" PRIx32 + "\n", + frame_num, m_submit_id, thread_group_x, thread_group_y, thread_group_z, + std::max(cs.num_thread_x, 1u), std::max(cs.num_thread_y, 1u), std::max(cs.num_thread_z, 1u), mode, static_cast(cs.wave_size), cs.data_addr, m_ucfg.GetGdsOaState().GetIndex(), oa.IsCounterEnabled() ? "true" : "false", oa.GetAddressBytes(), diff --git a/src/graphics/host_gpu/gpuTiler.cpp b/src/graphics/host_gpu/gpuTiler.cpp index 22b807f34..28a3c6f03 100644 --- a/src/graphics/host_gpu/gpuTiler.cpp +++ b/src/graphics/host_gpu/gpuTiler.cpp @@ -422,7 +422,7 @@ void TileCompute::Execute(bool to_tiled, const void* input, void* output, uint64 graphics.device.updateDescriptorSets(static_cast(writes.size()), writes.data(), 0, nullptr); - CommandBuffer command(GraphicContext::QUEUE_UTIL); + CommandBuffer command; command.Begin(); auto vk_command = command.Handle(); if (input != nullptr) { @@ -520,9 +520,8 @@ void TileCompute::Release() { } // namespace -void GpuDetile(const void* tiled, void* linear, uint64_t tiled_capacity, - uint64_t linear_capacity, std::span infos, - const GpuTileRecord& after) { +void GpuDetile(const void* tiled, void* linear, uint64_t tiled_capacity, uint64_t linear_capacity, + std::span infos, const GpuTileRecord& after) { Common::LockGuard lock(g_tiler_mutex); if (!g_tiler) { g_tiler = std::make_unique(GetRenderContext().GetGraphics()); @@ -530,9 +529,8 @@ void GpuDetile(const void* tiled, void* linear, uint64_t tiled_capacity, g_tiler->Run(false, tiled, linear, tiled_capacity, linear_capacity, infos, after); } -void GpuTile(const void* linear, void* tiled, uint64_t tiled_capacity, - uint64_t linear_capacity, std::span infos, - const GpuTileRecord& before) { +void GpuTile(const void* linear, void* tiled, uint64_t tiled_capacity, uint64_t linear_capacity, + std::span infos, const GpuTileRecord& before) { Common::LockGuard lock(g_tiler_mutex); if (!g_tiler) { g_tiler = std::make_unique(GetRenderContext().GetGraphics()); diff --git a/src/graphics/host_gpu/graphicContext.h b/src/graphics/host_gpu/graphicContext.h index 2b872e732..fdc0441f0 100644 --- a/src/graphics/host_gpu/graphicContext.h +++ b/src/graphics/host_gpu/graphicContext.h @@ -3,11 +3,9 @@ #include "common/abi.h" #include "common/common.h" -#include "common/threads.h" #include "graphics/host_gpu/vulkanCommon.h" // IWYU pragma: export #include "graphics/host_gpu/vulkanInstance.h" -#include #include #include #include @@ -35,16 +33,6 @@ struct VulkanSwapchain { uint32_t present_frame = 0; }; -struct VulkanCommandPool { - Common::Mutex mutex; - vk::CommandPool pool = nullptr; - std::unique_ptr buffers; - std::unique_ptr fences; - std::unique_ptr semaphores; - std::unique_ptr busy; - uint32_t buffers_count = 0; -}; - struct GraphicContext: public VulkanInstance { [[nodiscard]] bool CreateAllocator(); void DestroyAllocator(); @@ -60,9 +48,8 @@ struct GraphicContext: public VulkanInstance { std::vector& device_extensions); void LoadHardwareRayTracingFunctions() const; - uint32_t screen_width = 0; - uint32_t screen_height = 0; - std::array queue_mutexes; + uint32_t screen_width = 0; + uint32_t screen_height = 0; }; struct VulkanMemory { diff --git a/src/graphics/host_gpu/renderer/bufferCache.cpp b/src/graphics/host_gpu/renderer/bufferCache.cpp index 834cd3fbb..5b5388791 100644 --- a/src/graphics/host_gpu/renderer/bufferCache.cpp +++ b/src/graphics/host_gpu/renderer/bufferCache.cpp @@ -166,14 +166,9 @@ bool MergeOverlappingBufferCacheRange(BufferCacheRange& merged, return true; } -bool CanMergeBufferCacheQueueMask(uint64_t queue_mask, uint32_t queue) noexcept { - return queue < 64 && (queue_mask & ~(uint64_t {1} << queue)) == 0; -} - struct BufferCache::CachedBuffer { - uint64_t vaddr = 0; - uint64_t size = 0; - uint64_t queue_mask = 0; + uint64_t vaddr = 0; + uint64_t size = 0; std::shared_ptr buffer; }; @@ -392,30 +387,13 @@ struct BufferCache::ReadbackWorker { static_cast(command.get()), static_cast(mapped), static_cast(readback.buffer)); } - const auto family = cache.m_graphics.queues[GraphicContext::QUEUE_UTIL].family; - if (family == static_cast(-1) || - cache.m_graphics.queues[GraphicContext::QUEUE_GFX].family != family) { - EXIT("BufferCache: utility and graphics queues must share a valid family, " - "util=%u " - "gfx=%u\n", - family, cache.m_graphics.queues[GraphicContext::QUEUE_GFX].family); - } - for (int i = GraphicContext::QUEUE_COMPUTE_START; - i < GraphicContext::QUEUE_COMPUTE_START + GraphicContext::QUEUE_COMPUTE_NUM; - i++) { - if (cache.m_graphics.queues[i].family != family) { - EXIT("BufferCache: compute queue %d family mismatch, expected=%u " - "actual=%u\n", - i, family, cache.m_graphics.queues[i].family); - } - } readback.usage = vk::BufferUsageFlagBits::eTransferDst; readback.memory.property = vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent | vk::MemoryPropertyFlagBits::eHostCached; cache.m_graphics.CreateBuffer(READBACK_CAPACITY, readback); cache.m_graphics.MapMemory(readback.memory, mapped); - command = std::make_unique(GraphicContext::QUEUE_UTIL); + command = std::make_unique(); state.store(State::Idle, std::memory_order_release); state.notify_all(); continue; @@ -550,7 +528,7 @@ BufferCache::~BufferCache() { } } if (!m_buffers.empty()) { - Transfer::WaitForGraphicsIdle(); + Transfer::WaitForQueueIdle(); } m_buffers.clear(); } @@ -649,13 +627,10 @@ void BufferCache::UnmapMemory(uint64_t vaddr, uint64_t size) { BufferBinding BufferCache::ObtainBuffer(CommandBuffer& command, uint64_t vaddr, uint64_t size, bool is_written, bool is_read, bool is_formatted) { if (command.IsInvalid() || command.IsExecute() || vaddr == 0 || size == 0 || - size > UINT64_MAX - vaddr || command.GetQueue() < 0 || command.GetQueue() >= 64) { - EXIT("BufferCache: invalid buffer request, queue=%d addr=0x%016" PRIx64 - " size=0x%016" PRIx64 "\n", - command.GetQueue(), vaddr, size); + size > UINT64_MAX - vaddr) { + EXIT("BufferCache: invalid buffer request, addr=0x%016" PRIx64 " size=0x%016" PRIx64 "\n", + vaddr, size); } - const auto queue = static_cast(command.GetQueue()); - const auto queue_mask = uint64_t {1} << queue; ValidateGpuAccess(vaddr, size, is_read, is_written); const auto begin = AlignDown(vaddr); const auto end = AlignUp(vaddr + size); @@ -754,12 +729,6 @@ BufferBinding BufferCache::ObtainBuffer(CommandBuffer& command, uint64_t vaddr, " size=0x%016" PRIx64 " buffer=%p\n", old.vaddr, old.size, static_cast(old.buffer.get())); } - if (!CanMergeBufferCacheQueueMask(old.queue_mask, queue)) { - EXIT("BufferCache: cross-queue overlap merge is unsupported, " - "addr=0x%016" PRIx64 " size=0x%016" PRIx64 " used_queues=0x%016" PRIx64 - " requested_queue=%u\n", - old.vaddr, old.size, old.queue_mask, queue); - } std::vector> uploads; m_memory_tracker.ForEachUploadRange( old.vaddr, old.size, false, @@ -860,7 +829,6 @@ BufferBinding BufferCache::ObtainBuffer(CommandBuffer& command, uint64_t vaddr, if (is_written) { m_gpu_modified_ranges.Add(vaddr, size); } - cached.queue_mask |= queue_mask; command.RetainResourceUntilFence(cached.buffer); return {*cached.buffer, vaddr - cached.vaddr}; } @@ -944,7 +912,7 @@ BufferImageCopySource BufferCache::ObtainBufferForImage(uint64_t vaddr, uint64_t vaddr, size, GraphicsRunIsCommandProcessorThread(), GraphicsRunSubmissionLockHeld(), LabelInCallback()); } - Transfer::WaitForGraphicsIdle(); + Transfer::WaitForQueueIdle(); auto backing_writes = ReserveBackingWrites(m_page_manager, dirty_ranges); uint64_t downloaded = 0; m_memory_tracker.ForEachDownloadRange( @@ -1039,10 +1007,6 @@ vk::BufferMemoryBarrier MakeDmaBarrier(VulkanBuffer& buffer, uint64_t offset, ui barrier.size = size; return barrier; } - -vk::CommandBuffer GetDmaCommandBuffer(CommandBuffer& command) { - return command.Handle(); -} } // namespace void BufferCache::FillBuffer(CommandBuffer* command, uint64_t vaddr, uint64_t size, @@ -1086,7 +1050,7 @@ void BufferCache::FillBuffer(CommandBuffer* command, uint64_t vaddr, uint64_t si const auto before = MakeDmaBarrier( dst, dst_offset, size, vk::AccessFlagBits::eMemoryRead | vk::AccessFlagBits::eMemoryWrite, vk::AccessFlagBits::eTransferWrite); - const auto vk_buffer = GetDmaCommandBuffer(*command); + const auto vk_buffer = command->Handle(); vk_buffer.pipelineBarrier( vk::PipelineStageFlagBits::eAllCommands, vk::PipelineStageFlagBits::eTransfer, vk::DependencyFlagBits::eByRegion, 0, nullptr, 1, &before, 0, nullptr); @@ -1179,7 +1143,7 @@ void BufferCache::CopyBuffer(CommandBuffer* command, uint64_t dst_vaddr, uint64_ MakeDmaBarrier(src, src_offset, size, vk::AccessFlagBits::eMemoryWrite, vk::AccessFlagBits::eTransferRead), }; - const auto vk_buffer = GetDmaCommandBuffer(*command); + const auto vk_buffer = command->Handle(); vk_buffer.pipelineBarrier(vk::PipelineStageFlagBits::eAllCommands, vk::PipelineStageFlagBits::eTransfer, vk::DependencyFlagBits::eByRegion, 0, nullptr, 2, before, 0, nullptr); diff --git a/src/graphics/host_gpu/renderer/bufferCache.h b/src/graphics/host_gpu/renderer/bufferCache.h index 27db90576..99c98c1bb 100644 --- a/src/graphics/host_gpu/renderer/bufferCache.h +++ b/src/graphics/host_gpu/renderer/bufferCache.h @@ -42,7 +42,6 @@ struct BufferBinding { [[nodiscard]] bool MergeOverlappingBufferCacheRange(BufferCacheRange& merged, BufferCacheRange candidate) noexcept; -[[nodiscard]] bool CanMergeBufferCacheQueueMask(uint64_t queue_mask, uint32_t queue) noexcept; class BufferCache { public: diff --git a/src/graphics/host_gpu/renderer/context.cpp b/src/graphics/host_gpu/renderer/context.cpp index e1dcbad90..6c6501ff3 100644 --- a/src/graphics/host_gpu/renderer/context.cpp +++ b/src/graphics/host_gpu/renderer/context.cpp @@ -18,17 +18,13 @@ #include "graphics/host_gpu/vulkanCommon.h" #include -#include #include #include +#include #include namespace Libs::Graphics { static std::atomic g_command_buffer_submit_seq = 0; -static void RequireValidQueueId(int queue_id) { - EXIT_IF(queue_id < 0 || queue_id >= GraphicContext::QUEUES_NUM); -} - static void ResetNativeCommandBuffer(vk::CommandBuffer buffer) { EXIT_IF(buffer == nullptr); const auto result = buffer.reset(vk::CommandBufferResetFlagBits::eReleaseResources); @@ -38,34 +34,34 @@ static void ResetNativeCommandBuffer(vk::CommandBuffer buffer) { } } -class CommandPool { -public: - CommandPool() = default; - ~CommandPool() // NOLINT - { - // TODO(): check if destructor is called from std::_Exit() - // DeleteAll(); - } - - KYTY_CLASS_NO_COPY(CommandPool); - - VulkanCommandPool* GetPool(int queue_id) { - RequireValidQueueId(queue_id); - if (m_pools[queue_id] == nullptr) { - Create(queue_id); - } - return m_pools[queue_id]; - } - void DeleteAll(); - -private: - void Create(int queue_id); - - std::array m_pools {}; +struct CommandSlot { + Common::Mutex* pool_mutex = nullptr; + uint32_t id = 0; + vk::CommandBuffer buffer = nullptr; + vk::Fence fence = nullptr; + bool busy = false; }; -static RenderContext* g_render_ctx = nullptr; -static thread_local CommandPool g_command_pool; +class ThreadCommandPool { +public: + ThreadCommandPool() = default; + + KYTY_CLASS_NO_COPY(ThreadCommandPool); + + CommandSlot* Allocate(); + void Destroy(); + +private: + void Create(); + CommandSlot* CreateSlot(); + + Common::Mutex m_mutex; + vk::CommandPool m_pool = nullptr; + std::deque m_slots; +}; + +static RenderContext* g_render_ctx = nullptr; +static thread_local ThreadCommandPool g_command_pool; RenderContext& GetRenderContext() noexcept { return *g_render_ctx; @@ -96,152 +92,110 @@ void GraphicsRenderInit(GraphicContext& graphics) { g_render_ctx = new RenderContext(graphics); } -void GraphicsRenderReleaseThreadCommandPools() { - g_command_pool.DeleteAll(); +void GraphicsRenderReleaseThreadCommandPool() { + g_command_pool.Destroy(); } -CommandBuffer::CommandBuffer(int queue) - : m_graphics(GetRenderContext().GetGraphics()), m_queue(queue), m_host_stream(m_graphics) { - Allocate(); -} +CommandBuffer::CommandBuffer() + : m_graphics(GetRenderContext().GetGraphics()), m_slot(g_command_pool.Allocate()), + m_host_stream(m_graphics) {} -void CommandPool::Create(int queue_id) { - RequireValidQueueId(queue_id); - - auto& graphics = GetRenderContext().GetGraphics(); - auto*& pool = m_pools[queue_id]; - EXIT_IF(pool != nullptr); - - EXIT_IF(graphics.queues[queue_id].family == static_cast(-1)); - - pool = new VulkanCommandPool; +void ThreadCommandPool::Create() { + auto& graphics = GetRenderContext().GetGraphics(); + EXIT_IF(m_pool != nullptr || graphics.queue_family == static_cast(-1)); vk::CommandPoolCreateInfo pool_info {}; pool_info.sType = vk::StructureType::eCommandPoolCreateInfo; pool_info.pNext = nullptr; - pool_info.queueFamilyIndex = graphics.queues[queue_id].family; + pool_info.queueFamilyIndex = graphics.queue_family; pool_info.flags = vk::CommandPoolCreateFlagBits::eResetCommandBuffer; - const auto result = graphics.device.createCommandPool(&pool_info, nullptr, &pool->pool); - EXIT_NOT_IMPLEMENTED(result != vk::Result::eSuccess || pool->pool == nullptr); - - pool->buffers_count = 8; - pool->buffers = std::make_unique(pool->buffers_count); - pool->fences = std::make_unique(pool->buffers_count); - pool->semaphores = std::make_unique(pool->buffers_count); - pool->busy = std::make_unique(pool->buffers_count); + const auto result = graphics.device.createCommandPool(&pool_info, nullptr, &m_pool); + EXIT_NOT_IMPLEMENTED(result != vk::Result::eSuccess || m_pool == nullptr); +} +CommandSlot* ThreadCommandPool::CreateSlot() { + auto& graphics = GetRenderContext().GetGraphics(); vk::CommandBufferAllocateInfo alloc_info {}; alloc_info.sType = vk::StructureType::eCommandBufferAllocateInfo; - alloc_info.commandPool = pool->pool; + alloc_info.commandPool = m_pool; alloc_info.level = vk::CommandBufferLevel::ePrimary; - alloc_info.commandBufferCount = pool->buffers_count; + alloc_info.commandBufferCount = 1; - if (graphics.device.allocateCommandBuffers(&alloc_info, pool->buffers.get()) != - vk::Result::eSuccess) { + vk::CommandBuffer buffer = nullptr; + if (graphics.device.allocateCommandBuffers(&alloc_info, &buffer) != vk::Result::eSuccess) { EXIT("Can't allocate command buffers"); } - for (uint32_t i = 0; i < pool->buffers_count; i++) { - pool->busy[i] = false; + vk::FenceCreateInfo fence_info {}; + fence_info.sType = vk::StructureType::eFenceCreateInfo; + fence_info.flags = vk::FenceCreateFlagBits::eSignaled; - vk::FenceCreateInfo fence_info {}; - fence_info.sType = vk::StructureType::eFenceCreateInfo; - fence_info.pNext = nullptr; - fence_info.flags = vk::FenceCreateFlagBits::eSignaled; - - if (graphics.device.createFence(&fence_info, nullptr, &pool->fences[i]) != - vk::Result::eSuccess) { - EXIT("Can't create fence"); - } - - vk::SemaphoreCreateInfo semaphore_info {}; - semaphore_info.sType = vk::StructureType::eSemaphoreCreateInfo; - semaphore_info.pNext = nullptr; - semaphore_info.flags = {}; - - if (graphics.device.createSemaphore(&semaphore_info, nullptr, &pool->semaphores[i]) != - vk::Result::eSuccess) { - EXIT("Can't create semaphore"); - } - - EXIT_IF(pool->buffers[i] == nullptr); - EXIT_IF(pool->fences[i] == nullptr); - EXIT_IF(pool->semaphores[i] == nullptr); + vk::Fence fence = nullptr; + if (graphics.device.createFence(&fence_info, nullptr, &fence) != vk::Result::eSuccess) { + graphics.device.freeCommandBuffers(m_pool, 1, &buffer); + EXIT("Can't create fence"); } + 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; } -void CommandPool::DeleteAll() { - auto& graphics = GetRenderContext().GetGraphics(); - - for (auto& pool: m_pools) { - if (pool != nullptr) { - for (uint32_t i = 0; i < pool->buffers_count; i++) { - graphics.device.destroySemaphore(pool->semaphores[i], nullptr); - graphics.device.destroyFence(pool->fences[i], nullptr); - } - - graphics.device.freeCommandBuffers(pool->pool, pool->buffers_count, - pool->buffers.get()); - - graphics.device.destroyCommandPool(pool->pool, nullptr); - - delete pool; - pool = nullptr; - } +CommandSlot* ThreadCommandPool::Allocate() { + Common::LockGuard lock(m_mutex); + if (m_pool == nullptr) { + Create(); } + auto it = std::ranges::find_if(m_slots, [](const auto& slot) { return !slot.busy; }); + auto* slot = it != m_slots.end() ? &*it : CreateSlot(); + slot->busy = true; + ResetNativeCommandBuffer(slot->buffer); + return slot; +} + +void ThreadCommandPool::Destroy() { + Common::LockGuard lock(m_mutex); + if (m_pool == nullptr) { + return; + } + EXIT_IF(std::ranges::any_of(m_slots, [](const auto& slot) { return slot.busy; })); + auto& graphics = GetRenderContext().GetGraphics(); + for (const auto& slot: m_slots) { + graphics.device.destroyFence(slot.fence, nullptr); + } + graphics.device.destroyCommandPool(m_pool, nullptr); + m_slots.clear(); + m_pool = nullptr; } bool CommandBuffer::IsInvalid() const { - if (m_pool != nullptr) { - Common::LockGuard lock(m_pool->mutex); - - return (m_index == static_cast(-1) || m_index >= m_pool->buffers_count); - } - - return true; + return m_slot == nullptr; } vk::CommandBuffer CommandBuffer::Handle() const { EXIT_IF(IsInvalid()); - const auto handle = m_pool->buffers[m_index]; + const auto handle = m_slot->buffer; EXIT_IF(handle == nullptr); return handle; } -void CommandBuffer::Allocate() { - EXIT_IF(!IsInvalid()); - - m_pool = g_command_pool.GetPool(m_queue); - - Common::LockGuard lock(m_pool->mutex); - - for (uint32_t i = 0; i < m_pool->buffers_count; i++) { - if (!m_pool->busy[i]) { - m_pool->busy[i] = true; - ResetNativeCommandBuffer(m_pool->buffers[i]); - m_index = i; - break; - } - } - - EXIT_NOT_IMPLEMENTED(IsInvalid()); -} - -void CommandBuffer::Free() { +void CommandBuffer::Release() { EXIT_IF(IsInvalid()); - Common::LockGuard lock(m_pool->mutex); + Common::LockGuard lock(*m_slot->pool_mutex); WaitForFence(); m_host_stream.Release(); - m_pool->busy[m_index] = false; - ResetNativeCommandBuffer(m_pool->buffers[m_index]); + m_slot->busy = false; + ResetNativeCommandBuffer(m_slot->buffer); ReleaseResourcesAfterFence(); - m_index = static_cast(-1); + m_slot = nullptr; EXIT_NOT_IMPLEMENTED(!IsInvalid()); } @@ -305,27 +259,22 @@ void CommandBuffer::Execute() { Submit(nullptr, {}, nullptr); } -void CommandBuffer::ExecuteWithSemaphore(vk::Semaphore signal_semaphore) { - Submit(nullptr, {}, ResolveSignalSemaphore(signal_semaphore)); -} - void CommandBuffer::ExecuteWithSemaphore(vk::Semaphore wait_semaphore, vk::PipelineStageFlags wait_stage, vk::Semaphore signal_semaphore) { - EXIT_IF(wait_semaphore == nullptr); - Submit(wait_semaphore, wait_stage, ResolveSignalSemaphore(signal_semaphore)); + EXIT_IF(wait_semaphore == nullptr || signal_semaphore == nullptr); + Submit(wait_semaphore, wait_stage, signal_semaphore); } void CommandBuffer::Submit(vk::Semaphore wait_semaphore, vk::PipelineStageFlags wait_stage, vk::Semaphore signal_semaphore) { - RequireValidQueueId(m_queue); EXIT_IF(IsInvalid()); EXIT_IF(m_execute); const bool has_wait = wait_semaphore != nullptr; const bool has_signal = signal_semaphore != nullptr; auto buffer = Handle(); - auto fence = m_pool->fences[m_index]; + auto fence = m_slot->fence; vk::SubmitInfo submit_info {}; submit_info.sType = vk::StructureType::eSubmitInfo; @@ -339,7 +288,7 @@ void CommandBuffer::Submit(vk::Semaphore wait_semaphore, vk::PipelineStageFlags submit_info.pSignalSemaphores = has_signal ? &signal_semaphore : nullptr; auto& graphics = GetRenderContext().GetGraphics(); - const auto& queue = graphics.queues[m_queue]; + EXIT_IF(graphics.queue == nullptr); auto result = graphics.device.resetFences(1, &fence); if (result != vk::Result::eSuccess) { @@ -348,46 +297,33 @@ void CommandBuffer::Submit(vk::Semaphore wait_semaphore, vk::PipelineStageFlags } EXIT_NOT_IMPLEMENTED(result != vk::Result::eSuccess); - if (queue.mutex != nullptr) { - queue.mutex->Lock(); - } - if (Config::GraphicsDebugDumpEnabled()) { - LOGF("vkQueueSubmit begin: queue=%d index=%u wait_semaphore=%p signal_semaphore=%p" + LOGF("vkQueueSubmit begin: slot=%u wait_semaphore=%p signal_semaphore=%p" " debug_op=%u debug_submit=%" PRIu64 " args=%u,%u,%u,%u,0x%016" PRIx64 "\n", - m_queue, m_index, static_cast(wait_semaphore), - static_cast(signal_semaphore), m_debug_op, m_debug_submit_id, m_debug_arg0, - m_debug_arg1, m_debug_arg2, m_debug_arg3, m_debug_arg4); + m_slot->id, static_cast(wait_semaphore), static_cast(signal_semaphore), + m_debug_op, m_debug_submit_id, m_debug_arg0, m_debug_arg1, m_debug_arg2, m_debug_arg3, + m_debug_arg4); } - result = queue.vk_queue.submit(1, &submit_info, fence); - - if (queue.mutex != nullptr) { - queue.mutex->Unlock(); + { + Common::LockGuard lock(graphics.queue_mutex); + m_submit_seq = g_command_buffer_submit_seq.fetch_add(1, std::memory_order_relaxed) + 1; + result = graphics.queue.submit(1, &submit_info, fence); } m_execute = true; m_fence_waited = false; - m_submit_seq = g_command_buffer_submit_seq.fetch_add(1, std::memory_order_relaxed) + 1; if (result != vk::Result::eSuccess) { - LOGF("vkQueueSubmit failed: %s (%d), queue=%d index=%u submit_seq=%" PRIu64 + LOGF("vkQueueSubmit failed: %s (%d), slot=%u submit_seq=%" PRIu64 " debug_op=%u debug_submit=%" PRIu64 " args=%u,%u,%u,%u,0x%016" PRIx64 "\n", - VulkanToString(result).c_str(), static_cast(result), m_queue, m_index, - 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); + VulkanToString(result).c_str(), static_cast(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); } -vk::Semaphore CommandBuffer::ResolveSignalSemaphore(vk::Semaphore semaphore) const { - if (semaphore != nullptr) { - return semaphore; - } - EXIT_IF(IsInvalid()); - return m_pool->semaphores[m_index]; -} - void CommandBuffer::WaitForFence() { FinalizeFence(false); } @@ -398,13 +334,13 @@ void CommandBuffer::WaitForFenceOnly() { return; } auto device = GetRenderContext().GetGraphics().device; - auto result = device.waitForFences(1, &m_pool->fences[m_index], VK_TRUE, UINT64_MAX); + auto result = device.waitForFences(1, &m_slot->fence, VK_TRUE, UINT64_MAX); if (result != vk::Result::eSuccess) { - LOGF("vkWaitForFences failed: %s (%d), queue=%d index=%u submit_seq=%" PRIu64 + LOGF("vkWaitForFences failed: %s (%d), slot=%u submit_seq=%" PRIu64 " debug_op=%u debug_submit=%" PRIu64 " args=%u,%u,%u,%u,0x%016" PRIx64 "\n", - VulkanToString(result).c_str(), static_cast(result), m_queue, m_index, - 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); + VulkanToString(result).c_str(), static_cast(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; @@ -421,7 +357,7 @@ void CommandBuffer::FinalizeFence(bool reset_recording) { m_execute = false; m_fence_waited = false; if (reset_recording) { - ResetNativeCommandBuffer(m_pool->buffers[m_index]); + ResetNativeCommandBuffer(m_slot->buffer); m_recording_generation++; } } diff --git a/src/graphics/host_gpu/renderer/dummyTextureCache.cpp b/src/graphics/host_gpu/renderer/dummyTextureCache.cpp index cd7125a37..98f23ea28 100644 --- a/src/graphics/host_gpu/renderer/dummyTextureCache.cpp +++ b/src/graphics/host_gpu/renderer/dummyTextureCache.cpp @@ -24,7 +24,7 @@ DummyTextureCache::~DummyTextureCache() { if (!populated(m_sampled) && !populated(m_storage)) { return; } - Transfer::WaitForGraphicsIdle(); + Transfer::WaitForQueueIdle(); const auto destroy = [](auto& slots) { for (auto& slot: slots) { if (slot.image != nullptr) { @@ -42,8 +42,7 @@ VulkanImage& DummyTextureCache::Get(Usage usage, bool uint_format, bool image_3d auto& slots = usage == Usage::Storage ? m_storage : m_sampled; auto& slot = slots[DummyTextureIndex(uint_format, image_3d)]; if (slot.image == nullptr) { - slot.image = ImageOps::CreateDummyTexture(uint_format, image_3d, - usage == Usage::Storage); + slot.image = ImageOps::CreateDummyTexture(uint_format, image_3d, usage == Usage::Storage); } return *slot.image; } diff --git a/src/graphics/host_gpu/renderer/image.cpp b/src/graphics/host_gpu/renderer/image.cpp index c33b73274..87ae7cdcf 100644 --- a/src/graphics/host_gpu/renderer/image.cpp +++ b/src/graphics/host_gpu/renderer/image.cpp @@ -50,8 +50,7 @@ TextureImageCreateParams MakeImageParams(const ImageInfo& info, bool storage) { return params; } -bool RenderTargetSupportsStorage(vk::Format format, - vk::ImageCreateFlags flags) { +bool RenderTargetSupportsStorage(vk::Format format, vk::ImageCreateFlags flags) { const auto compatible = SrgbStorageViewFormat(format); const auto required_flags = vk::ImageCreateFlagBits::eMutableFormat | vk::ImageCreateFlagBits::eExtendedUsage; @@ -72,12 +71,12 @@ vk::ImageCreateFlags RenderTargetCreateFlags(vk::Format format) { : vk::ImageCreateFlags {0}; } -vk::ImageUsageFlags RenderTargetUsage(vk::Format format, - vk::ImageCreateFlags flags, uint32_t samples) { +vk::ImageUsageFlags RenderTargetUsage(vk::Format format, vk::ImageCreateFlags flags, + uint32_t samples) { auto& graphics = GetRenderContext().GetGraphics(); - auto usage = static_cast(vk::ImageUsageFlagBits::eColorAttachment) | - static_cast(vk::ImageUsageFlagBits::eTransferSrc) | - static_cast(vk::ImageUsageFlagBits::eTransferDst); + auto usage = static_cast(vk::ImageUsageFlagBits::eColorAttachment) | + static_cast(vk::ImageUsageFlagBits::eTransferSrc) | + static_cast(vk::ImageUsageFlagBits::eTransferDst); if (samples == 1) { usage |= vk::ImageUsageFlagBits::eSampled; if (RenderTargetSupportsStorage(format, flags)) { @@ -155,21 +154,19 @@ GpuTextureVulkanImage* CreateTexture(const ImageInfo& info, bool storage, return image; } -void CreateTextureViews(GpuTextureVulkanImage& image, - const ImageInfo& info, bool storage, vk::ComponentMapping components) { +void CreateTextureViews(GpuTextureVulkanImage& image, const ImageInfo& info, bool storage, + vk::ComponentMapping components) { if (storage) { TextureCreateImageViews(image, components, info.type, 0, 0, 1, info.depth, false, TextureFormatUsage::Sampled | TextureFormatUsage::Storage); } else { - TextureCreateImageViews(image, components, info.type, info.base_array, - info.base_level, info.view_levels, info.depth, true, - TextureFormatUsage::Sampled); + TextureCreateImageViews(image, components, info.type, info.base_array, info.base_level, + info.view_levels, info.depth, true, TextureFormatUsage::Sampled); } } -void UploadRenderTargetLayers(RenderTextureVulkanImage& image, - const RenderTargetInfo& info, uint32_t base_layer, - uint32_t layer_count, bool refresh) { +void UploadRenderTargetLayers(RenderTextureVulkanImage& image, const RenderTargetInfo& info, + uint32_t base_layer, uint32_t layer_count, bool refresh) { if (info.layers == 0 || info.size % info.layers != 0 || layer_count == 0 || base_layer >= info.layers || layer_count > info.layers - base_layer || base_layer >= image.layers || layer_count > image.layers - base_layer) { @@ -182,7 +179,7 @@ void UploadRenderTargetLayers(RenderTextureVulkanImage& image, info.samples, image.samples); } if (refresh) { - Transfer::WaitForGraphicsIdle(); + Transfer::WaitForQueueIdle(); } const auto slice_size = info.size / info.layers; const auto upload_size = slice_size * layer_count; @@ -207,9 +204,9 @@ void UploadRenderTargetLayers(RenderTextureVulkanImage& image, region.dst_layer += base_layer; } const auto source_address = info.address + slice_size * base_layer; - TextureUploadGuestImage(image, reinterpret_cast(source_address), - upload_size, regions, layout, format, info.width, info.height, - layer_count, info.levels, "TextureCache render target", + TextureUploadGuestImage(image, reinterpret_cast(source_address), upload_size, + regions, layout, format, info.width, info.height, layer_count, + info.levels, "TextureCache render target", vk::ImageLayout::eGeneral); return; } @@ -221,23 +218,22 @@ void UploadRenderTargetLayers(RenderTextureVulkanImage& image, "TextureCache render target"); auto regions = TextureBuildUploadRegions(layout, info.format, info.width, info.height, 1, 1, true, false, TextureUploadDestination::MipLevels); - TextureUploadGuestImage(image, reinterpret_cast(info.address), - slice_size, regions, layout, format, info.width, info.height, 1, 1, + TextureUploadGuestImage(image, reinterpret_cast(info.address), slice_size, + regions, layout, format, info.width, info.height, 1, 1, "TextureCache render target", vk::ImageLayout::eGeneral); } else { - Transfer::UploadImage(image, reinterpret_cast(info.address), - slice_size, info.pitch, vk::ImageLayout::eGeneral); + Transfer::UploadImage(image, reinterpret_cast(info.address), slice_size, + info.pitch, vk::ImageLayout::eGeneral); } } -void UploadRenderTarget(RenderTextureVulkanImage& image, - const RenderTargetInfo& info, bool refresh) { +void UploadRenderTarget(RenderTextureVulkanImage& image, const RenderTargetInfo& info, + bool refresh) { UploadRenderTargetLayers(image, info, 0, info.layers, refresh); } -RenderTextureVulkanImage* CreateRenderTarget( - const RenderTargetInfo& info) { - auto& graphics = GetRenderContext().GetGraphics(); +RenderTextureVulkanImage* CreateRenderTarget(const RenderTargetInfo& info) { + auto& graphics = GetRenderContext().GetGraphics(); auto* image = new RenderTextureVulkanImage; image->extent.width = info.width; image->extent.height = info.height; @@ -270,7 +266,7 @@ RenderTextureVulkanImage* CreateRenderTarget( } DepthStencilVulkanImage* CreateDepthTarget(const DepthTargetInfo& info) { - auto& graphics = GetRenderContext().GetGraphics(); + auto& graphics = GetRenderContext().GetGraphics(); vk::ImageCreateInfo create {}; create.sType = vk::StructureType::eImageCreateInfo; create.imageType = vk::ImageType::e2D; @@ -350,7 +346,7 @@ void ValidateVideoOut(const VideoOutInfo& info) { } VideoOutVulkanImage* CreateVideoOut(const VideoOutInfo& info) { - auto& graphics = GetRenderContext().GetGraphics(); + auto& graphics = GetRenderContext().GetGraphics(); auto* image = new VideoOutVulkanImage; image->extent.width = info.width; image->extent.height = info.height; @@ -379,15 +375,14 @@ VideoOutVulkanImage* CreateVideoOut(const VideoOutInfo& info) { return image; } -void UploadVideoOut(VideoOutVulkanImage& image, const VideoOutInfo& info, - bool refresh) { +void UploadVideoOut(VideoOutVulkanImage& image, const VideoOutInfo& info, bool refresh) { if (info.compression != VideoOutCompression::Uncompressed) { EXIT("TextureCache: compressed video-out guest upload is unsupported, " "addr=0x%016" PRIx64 " metadata=0x%016" PRIx64 " dcc=0x%08" PRIx32 "\n", info.address, info.metadata_address, info.dcc_control); } if (refresh) { - Transfer::WaitForGraphicsIdle(); + Transfer::WaitForQueueIdle(); } image.layout = vk::ImageLayout::eUndefined; if (!info.bgra16) { @@ -396,9 +391,9 @@ void UploadVideoOut(VideoOutVulkanImage& image, const VideoOutInfo& info, info.tile_mode, info.size, false, false, "VideoOut"); auto regions = TextureBuildUploadRegions(layout, info.format, info.width, info.height, 1, 1, false, false, TextureUploadDestination::MipLevels); - TextureUploadGuestImage(image, reinterpret_cast(info.address), - info.size, regions, layout, info.guest_format, info.width, - info.height, 1, 1, "VideoOut", vk::ImageLayout::eGeneral); + TextureUploadGuestImage(image, reinterpret_cast(info.address), info.size, + regions, layout, info.guest_format, info.width, info.height, 1, 1, + "VideoOut", vk::ImageLayout::eGeneral); return; } Transfer::ScratchBuffer scratch(info.size); @@ -416,11 +411,10 @@ void UploadVideoOut(VideoOutVulkanImage& image, const VideoOutInfo& info, info.height, 1, info.pitch}; - GpuDetile(reinterpret_cast(info.address), scratch.Data(), info.size, - info.size, std::span(&tile_info, 1)); + GpuDetile(reinterpret_cast(info.address), scratch.Data(), info.size, info.size, + std::span(&tile_info, 1)); SwapVideoOutBgra16(scratch.Data(), info.size); - Transfer::UploadImage(image, scratch.Data(), info.size, info.pitch, - vk::ImageLayout::eGeneral); + Transfer::UploadImage(image, scratch.Data(), info.size, info.pitch, vk::ImageLayout::eGeneral); } void SwapVideoOutBgra16(void* data, uint64_t size) { @@ -430,8 +424,7 @@ void SwapVideoOutBgra16(void* data, uint64_t size) { } } -GpuTextureVulkanImage* CreateDummyTexture(bool uint_format, bool image_3d, - bool storage) { +GpuTextureVulkanImage* CreateDummyTexture(bool uint_format, bool image_3d, bool storage) { auto* image = storage ? static_cast(new StorageTextureVulkanImage) : new TextureVulkanImage; auto usage = storage ? TextureFormatUsage::Storage : TextureFormatUsage::Sampled; @@ -443,8 +436,8 @@ GpuTextureVulkanImage* CreateDummyTexture(bool uint_format, bool image_3d, static constexpr uint32_t zero = 0; Transfer::UploadImage(*image, &zero, sizeof(zero), 1, layout); - TextureCreateImageViews(*image, components, params.type, 0, params.base_level, - params.levels, params.depth, params.allow_cube_view, params.view_usage); + TextureCreateImageViews(*image, components, params.type, 0, params.base_level, params.levels, + params.depth, params.allow_cube_view, params.view_usage); return image; } diff --git a/src/graphics/host_gpu/renderer/render.h b/src/graphics/host_gpu/renderer/render.h index 529c3e5c2..44777663e 100644 --- a/src/graphics/host_gpu/renderer/render.h +++ b/src/graphics/host_gpu/renderer/render.h @@ -26,7 +26,7 @@ struct DepthStencilVulkanImage; struct TextureVulkanImage; struct StorageTextureVulkanImage; struct RenderTextureVulkanImage; -struct VulkanCommandPool; +struct CommandSlot; struct VulkanBuffer; struct VulkanDescriptorSet; struct VulkanFramebuffer; @@ -68,19 +68,16 @@ private: class CommandBuffer { public: - explicit CommandBuffer(int queue); - ~CommandBuffer() { Free(); } + CommandBuffer(); + ~CommandBuffer() { Release(); } KYTY_CLASS_NO_COPY(CommandBuffer); [[nodiscard]] bool IsInvalid() const; - void Allocate(); - void Free(); void Begin() const; void End() const; void Execute(); - void ExecuteWithSemaphore(vk::Semaphore signal_semaphore = nullptr); void ExecuteWithSemaphore(vk::Semaphore wait_semaphore, vk::PipelineStageFlags wait_stage, vk::Semaphore signal_semaphore); void SetDebugInfo(uint32_t op, uint64_t submit_id, uint32_t arg0 = 0, uint32_t arg1 = 0, @@ -97,7 +94,6 @@ public: [[nodiscard]] vk::CommandBuffer Handle() const; [[nodiscard]] GraphicContext& GetGraphics() const noexcept { return m_graphics; } - [[nodiscard]] int GetQueue() const { return m_queue; } [[nodiscard]] bool IsExecute() const { return m_execute; } [[nodiscard]] uint64_t GetRecordingGeneration() const { return m_recording_generation; } @@ -106,16 +102,14 @@ private: void Submit(vk::Semaphore wait_semaphore, vk::PipelineStageFlags wait_stage, vk::Semaphore signal_semaphore); - [[nodiscard]] vk::Semaphore ResolveSignalSemaphore(vk::Semaphore semaphore) const; - void FinalizeFence(bool reset_recording); - void ReleaseResourcesAfterFence(); - void DeleteBuffersAfterFence(); - void RecycleDescriptorsAfterFence(); + void Release(); + void FinalizeFence(bool reset_recording); + void ReleaseResourcesAfterFence(); + void DeleteBuffersAfterFence(); + void RecycleDescriptorsAfterFence(); GraphicContext& m_graphics; - VulkanCommandPool* m_pool = nullptr; - uint32_t m_index = static_cast(-1); - int m_queue = -1; + CommandSlot* m_slot = nullptr; bool m_execute = false; bool m_fence_waited = false; uint64_t m_submit_seq = 0; @@ -135,10 +129,8 @@ private: class RenderCommandBuffer final: public CommandBuffer { public: - RenderCommandBuffer(int queue, HW::Context& registers, HW::UserConfig& user_config, - HW::Shader& shaders) - : CommandBuffer(queue), m_registers(registers), m_user_config(user_config), - m_shaders(shaders) {} + RenderCommandBuffer(HW::Context& registers, HW::UserConfig& user_config, HW::Shader& shaders) + : m_registers(registers), m_user_config(user_config), m_shaders(shaders) {} [[nodiscard]] HW::Context& GetRegisters() const noexcept { return m_registers; } [[nodiscard]] HW::UserConfig& GetUserConfig() const noexcept { return m_user_config; } @@ -162,7 +154,7 @@ void RenderDispatchDirect(uint64_t submit_id, RenderCommandBuffer& buffer, uint3 uint32_t thread_group_y, uint32_t thread_group_z, uint32_t mode); void GraphicsRenderInit(GraphicContext& graphics); -void GraphicsRenderReleaseThreadCommandPools(); +void GraphicsRenderReleaseThreadCommandPool(); [[nodiscard]] bool ResolveComputeImageClear(const ShaderComputeInputInfo& input, uint32_t group_x, uint32_t group_y, uint32_t group_z, uint32_t mode, diff --git a/src/graphics/host_gpu/renderer/resourceMutex.h b/src/graphics/host_gpu/renderer/resourceMutex.h index c717b7760..d27b9680e 100644 --- a/src/graphics/host_gpu/renderer/resourceMutex.h +++ b/src/graphics/host_gpu/renderer/resourceMutex.h @@ -9,7 +9,7 @@ namespace Libs::Graphics { // Owner-tracked shared buffer/image transaction. External faults pause GPU submissions first; -// command-processor faults drain their queue before entering this transaction. +// command-processor faults drain pending guest processors before entering this transaction. class ResourceMutex final { public: class FaultScope final { diff --git a/src/graphics/host_gpu/renderer/textureCache.cpp b/src/graphics/host_gpu/renderer/textureCache.cpp index 005e49d9c..903cbb6bd 100644 --- a/src/graphics/host_gpu/renderer/textureCache.cpp +++ b/src/graphics/host_gpu/renderer/textureCache.cpp @@ -1008,7 +1008,7 @@ void TextureCache::MaterializeImagesToGuestLocked( const std::vector>& images) { if (std::any_of(images.begin(), images.end(), [](const auto& cached) { return cached->gpu_modified; })) { - Transfer::WaitForGraphicsIdle(); + Transfer::WaitForQueueIdle(); } for (const auto& cached: images) { if (!cached->gpu_modified) { @@ -1269,7 +1269,7 @@ void TextureCache::RetireSampledTargetAliases(const ImageInfo& requested) { } RequireRetirementIsolation(retire, "sampled target", requested.address, requested.size); if (wait_idle) { - Transfer::WaitForGraphicsIdle(); + Transfer::WaitForQueueIdle(); } for (auto* cached: retire) { if (!cached->gpu_modified) { @@ -1360,7 +1360,7 @@ void TextureCache::RetireStorageDepthAliasLocked(const ImageInfo& requested) { if (selected == nullptr) { return; } - Transfer::WaitForGraphicsIdle(); + Transfer::WaitForQueueIdle(); const auto transfer = m_readback->DownloadDepthTarget(*selected, false); for (const auto& range: transfer.Ranges()) { m_memory_tracker.ForEachDownloadRange(range.address, range.size, @@ -1373,7 +1373,7 @@ void TextureCache::RetireStorageDepthAliasLocked(const ImageInfo& requested) { TextureCache::~TextureCache() { m_readback.reset(); if (!m_images.empty()) { - Transfer::WaitForGraphicsIdle(); + Transfer::WaitForQueueIdle(); } for (const auto& image: m_images) { UnregisterImageLocked(*image, false); @@ -1488,7 +1488,7 @@ VulkanImage& TextureCache::FindTexture(CommandBuffer& command, const ImageInfo& // Kyty does not yet copy between those independently allocated Vulkan images, so use the // existing synchronized tiled readback seam and rebuild the complete chain from coherent // guest backing. This is an uncommon ownership transition, not a frame lookup fast path. - Transfer::WaitForGraphicsIdle(); + Transfer::WaitForQueueIdle(); for (auto* cached: storage_retire) { if (!cached->gpu_modified || cached->buffer_modified || cached->info.IsCpuDirty() || !m_memory_tracker.IsRegionGpuModified(cached->info.address, cached->info.size) || @@ -2632,7 +2632,7 @@ void TextureCache::UnregisterVideoOutSurfaces(const std::vectorAddress(), cached->Size()); } } - Transfer::WaitForGraphicsIdle(); + Transfer::WaitForQueueIdle(); for (auto* cached: selected) { if (cached->gpu_modified) { m_memory_tracker.UnmarkRegionAsGpuModified(cached->Address(), cached->Size()); @@ -2992,7 +2992,7 @@ void TextureCache::SynchronizeColorImageToBufferLocked(CachedImage& cached, uint target.address, target.size); } - Transfer::WaitForGraphicsIdle(); + Transfer::WaitForQueueIdle(); std::vector regions; std::vector tiled_regions; TextureUploadLayout tiled_layout {}; @@ -3100,7 +3100,7 @@ void TextureCache::SynchronizeDepthImageToBufferLocked(CachedImage& cached, uint info.layers, static_cast(info.format), info.guest_format, info.bytes_per_element, has_stencil, has_htile, cached.gpu_modified, cached.buffer_modified); } - Transfer::WaitForGraphicsIdle(); + Transfer::WaitForQueueIdle(); const auto regions = Transfer::MakeLayeredImageBufferCopies( 1, info.size, info.pitch, info.width, info.height, vk::ImageAspectFlagBits::eDepth); TileBlockLayout block {}; @@ -3790,7 +3790,7 @@ void TextureCache::UnmapMemory(uint64_t vaddr, uint64_t size) { } } if (wait_idle) { - Transfer::WaitForGraphicsIdle(); + Transfer::WaitForQueueIdle(); } for (auto& cached: m_images) { if (!cached->OverlapsRange(vaddr, size, false) || !cached->gpu_modified) { diff --git a/src/graphics/host_gpu/renderer/tiler.cpp b/src/graphics/host_gpu/renderer/tiler.cpp index 5f920b1df..d01a79b94 100644 --- a/src/graphics/host_gpu/renderer/tiler.cpp +++ b/src/graphics/host_gpu/renderer/tiler.cpp @@ -106,7 +106,7 @@ void UploadPromotedD16Depth(DepthStencilVulkanImage& image, const DepthTargetInf void Tiler::DetileImage(GpuTextureVulkanImage& image, const ImageInfo& info, const BufferImageCopySource& source, bool refresh, bool storage) const { - if (refresh) Transfer::WaitForGraphicsIdle(); + if (refresh) Transfer::WaitForQueueIdle(); const bool array_texture = TextureIsLayeredTexture(info.type); const bool volume_texture = TextureIs3DTexture(info.type); @@ -127,7 +127,7 @@ void Tiler::DetileImage(DepthStencilVulkanImage& image, const DepthTargetInfo& i const BufferImageCopySource& source, bool refresh, uint32_t base_layer) const { EXIT_NOT_IMPLEMENTED(info.samples != 1 || image.samples != 1); - if (refresh) Transfer::WaitForGraphicsIdle(); + if (refresh) Transfer::WaitForQueueIdle(); if (DepthAspectTransferBytes(info.format) != info.bytes_per_element) { switch (info.format) { @@ -149,7 +149,7 @@ void Tiler::DetileStencil(DepthStencilVulkanImage& image, const DepthTargetInfo& const BufferImageCopySource& source, bool refresh, uint32_t base_layer) const { EXIT_NOT_IMPLEMENTED(info.samples != 1 || image.samples != 1); - if (refresh) Transfer::WaitForGraphicsIdle(); + if (refresh) Transfer::WaitForQueueIdle(); const auto format = Prospero::GpuEnumValue(Prospero::BufferFormat::k8UInt); const auto pitch = TileGetTexturePitch(format, info.width, 1, diff --git a/src/graphics/host_gpu/transfer.cpp b/src/graphics/host_gpu/transfer.cpp index 3be122e54..299dfa58b 100644 --- a/src/graphics/host_gpu/transfer.cpp +++ b/src/graphics/host_gpu/transfer.cpp @@ -82,39 +82,14 @@ bool GuestBufferIsTiled(uint64_t vaddr, uint64_t size) { return true; } -void WaitForGraphicsIdle() { - auto& graphics = GetRenderContext().GetGraphics(); - Common::Mutex* locked[GraphicContext::QUEUES_NUM] {}; - int locked_num = 0; - - for (int id = 0; id < GraphicContext::QUEUES_NUM; id++) { - auto* mutex = graphics.queues[id].mutex; - if (mutex == nullptr || graphics.queues[id].vk_queue == nullptr) { - continue; - } - - bool already_locked = false; - for (int i = 0; i < locked_num; i++) { - if (locked[i] == mutex) { - already_locked = true; - break; - } - } - - if (!already_locked) { - mutex->Lock(); - locked[locked_num++] = mutex; - } - } - - auto result = graphics.device.waitIdle(); - - for (int i = locked_num - 1; i >= 0; i--) { - locked[i]->Unlock(); - } +void WaitForQueueIdle() { + auto& graphics = GetRenderContext().GetGraphics(); + EXIT_IF(graphics.queue == nullptr); + Common::LockGuard lock(graphics.queue_mutex); + const auto result = graphics.queue.waitIdle(); if (result != vk::Result::eSuccess) { - LOGF("vkDeviceWaitIdle failed: %s (%d)\n", VulkanToString(result).c_str(), + LOGF("vkQueueWaitIdle failed: %s (%d)\n", VulkanToString(result).c_str(), static_cast(result)); } EXIT_NOT_IMPLEMENTED(result != vk::Result::eSuccess); @@ -126,9 +101,7 @@ static void SetImageLayout(vk::CommandBuffer buffer, VulkanImage& dst_image, uin template static void ExecuteImmediateCommands(const Recorder& recorder) { - // Keep synchronous utility submission behind one boundary so adopting a central scheduler does - // not touch every caller. - CommandBuffer command(GraphicContext::QUEUE_UTIL); + CommandBuffer command; command.Begin(); recorder(command, command.Handle()); command.End(); @@ -286,7 +259,7 @@ public: void UploadToBuffer(VulkanBuffer& dst_buffer, const void* src_data, uint64_t size, uint64_t dst_offset) { - RecordUpload(src_data, size, [&](CommandBuffer&, vk::CommandBuffer vk_command) { + RecordUpload(src_data, size, [&](CommandBuffer&, vk::CommandBuffer vk_command) { SetBufferMemoryBarrier( vk_command, m_buffer.buffer, 0, size, vk::AccessFlagBits::eMemoryWrite, vk::AccessFlagBits::eTransferRead, vk::PipelineStageFlagBits::eAllCommands, @@ -314,7 +287,7 @@ public: vk::ImageAspectFlags copy_aspect, vk::ImageLayout initial_layout, vk::ImageLayout final_layout) { const auto transition_aspects = GetTransferAspects(image, copy_aspect); - RecordUpload(src_data, size, [&](CommandBuffer& command, vk::CommandBuffer) { + RecordUpload(src_data, size, [&](CommandBuffer& command, vk::CommandBuffer) { const auto region = MakeBufferImageCopy(0, src_pitch, copy_aspect, 0, 0, {0, 0, 0}, {image.extent.width, image.extent.height, 1}); RecordBufferToImageCopy(command, m_buffer, image, @@ -325,7 +298,7 @@ public: void UploadToImage(VulkanImage& dst_image, const void* src_data, uint64_t size, std::span regions, vk::ImageLayout dst_layout) { - RecordUpload(src_data, size, [&](CommandBuffer& command, vk::CommandBuffer) { + RecordUpload(src_data, size, [&](CommandBuffer& command, vk::CommandBuffer) { vk::ImageAspectFlags transition_aspects = {}; for (const auto& region: regions) { transition_aspects |= GetTransferAspects(dst_image, region.aspect); @@ -400,13 +373,10 @@ private: static_cast(size))); } - template + template void RecordUpload(const void* src_data, uint64_t size, const Recorder& recorder) { Common::LockGuard lock(m_mutex); CopyFromHost(src_data, size, vk::BufferUsageFlagBits::eTransferSrc); - if constexpr (WaitIdle) { - WaitForGraphicsIdle(); - } ExecuteImmediateCommands(recorder); } @@ -880,12 +850,7 @@ void CopyImageViaBuffer(CommandBuffer& buffer, VulkanImage& src_image, std::min(src_image.extent.height, MAX_COPY_BUFFER_SIZE / row_bytes)); const auto copy_buffer_size = row_bytes * rows_per_chunk; - // Kyty does not yet have a cross-queue scheduler. Drain submitted work once before - // recording the recreate copy; commands already recorded on this buffer remain ordered by the - // barriers below. The preservation policy stays isolated here so a future scheduler can replace - // this synchronization without changing texture-cache ownership logic. - auto& graphics = buffer.GetGraphics(); - WaitForGraphicsIdle(); + auto& graphics = buffer.GetGraphics(); auto* copy_buffer = new VulkanBuffer; copy_buffer->usage = vk::BufferUsageFlagBits::eTransferSrc | vk::BufferUsageFlagBits::eTransferDst; @@ -1077,14 +1042,6 @@ void DownloadBuffer(VulkanBuffer& src_buffer, uint64_t src_offset, void* dst_dat auto& graphics = GetRenderContext().GetGraphics(); EXIT_IF(size == 0); EXIT_IF(src_offset > src_buffer.buffer_size || size > src_buffer.buffer_size - src_offset); - const auto family = graphics.queues[GraphicContext::QUEUE_UTIL].family; - EXIT_IF(family == static_cast(-1)); - for (int i = GraphicContext::QUEUE_COMPUTE_START; - i < GraphicContext::QUEUE_COMPUTE_START + GraphicContext::QUEUE_COMPUTE_NUM; i++) { - EXIT_IF(graphics.queues[i].family != family); - } - EXIT_IF(graphics.queues[GraphicContext::QUEUE_GFX].family != family); - if (src_buffer.memory.property & vk::MemoryPropertyFlagBits::eHostVisible) { void* mapped = nullptr; graphics.MapMemory(src_buffer.memory, mapped); diff --git a/src/graphics/host_gpu/transfer.h b/src/graphics/host_gpu/transfer.h index 1afe9b715..a1e174da9 100644 --- a/src/graphics/host_gpu/transfer.h +++ b/src/graphics/host_gpu/transfer.h @@ -52,10 +52,10 @@ struct ImageImageCopy { explicit ImageImageCopy(VulkanImage& source): src_image(source) {} VulkanImage& src_image; - uint32_t src_level = 0; - uint32_t dst_level = 0; - uint32_t width = 0; - uint32_t height = 0; + uint32_t src_level = 0; + uint32_t dst_level = 0; + uint32_t width = 0; + uint32_t height = 0; uint32_t src_layer = 0; uint32_t dst_layer = 0; vk::ImageAspectFlags src_aspect = vk::ImageAspectFlagBits::eColor; @@ -134,7 +134,7 @@ bool GuestBufferIsTiled(uint64_t vaddr, uint64_t size); bool IsBlockCompressedFormat(vk::Format format); uint32_t BlockCompressedBytesPerBlock(vk::Format format); -void WaitForGraphicsIdle(); +void WaitForQueueIdle(); inline std::pair MipmapAtlasOffset(uint32_t lod, uint32_t width, uint32_t height) { uint32_t mip_width = width; diff --git a/src/graphics/host_gpu/vulkanInstance.h b/src/graphics/host_gpu/vulkanInstance.h index 0bc8b5f5f..01c5657f3 100644 --- a/src/graphics/host_gpu/vulkanInstance.h +++ b/src/graphics/host_gpu/vulkanInstance.h @@ -15,24 +15,7 @@ namespace Libs::Graphics { inline constexpr uint32_t VULKAN_TARGET_API_VERSION = VK_API_VERSION_1_3; -struct VulkanQueueInfo { - Common::Mutex* mutex = nullptr; - uint32_t family = static_cast(-1); - uint32_t index = static_cast(-1); - vk::Queue vk_queue = nullptr; -}; - struct VulkanInstance { - static constexpr int QUEUES_NUM = 11; - static constexpr int QUEUE_GFX = 8; - static constexpr int QUEUE_GFX_NUM = 1; - static constexpr int QUEUE_UTIL = 9; - static constexpr int QUEUE_UTIL_NUM = 1; - static constexpr int QUEUE_PRESENT = 10; - static constexpr int QUEUE_PRESENT_NUM = 1; - static constexpr int QUEUE_COMPUTE_START = 0; - static constexpr int QUEUE_COMPUTE_NUM = 8; - vk::Instance instance = nullptr; vk::DebugUtilsMessengerEXT debug_messenger = nullptr; vk::PhysicalDevice physical_device = nullptr; @@ -48,7 +31,9 @@ struct VulkanInstance { uint32_t min_subgroup_size = 0; uint32_t max_subgroup_size = 0; vk::ShaderStageFlags required_subgroup_size_stages = {}; - VulkanQueueInfo queues[QUEUES_NUM]; + Common::Mutex queue_mutex; + uint32_t queue_family = static_cast(-1); + vk::Queue queue = nullptr; [[nodiscard]] const vk::PhysicalDeviceProperties& GetPhysicalDeviceProperties() const { return physical_device_properties; diff --git a/src/graphics/presentation/window/swapchain.cpp b/src/graphics/presentation/window/swapchain.cpp index 8a26cbc15..df9792280 100644 --- a/src/graphics/presentation/window/swapchain.cpp +++ b/src/graphics/presentation/window/swapchain.cpp @@ -226,14 +226,12 @@ VulkanSwapchain::~VulkanSwapchain() = default; create_info.imageArrayLayers = 1; create_info.imageUsage = vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eTransferDst; - create_info.imageSharingMode = vk::SharingMode::eExclusive; - create_info.queueFamilyIndexCount = 0; - create_info.pQueueFamilyIndices = nullptr; - create_info.preTransform = r.capabilities.currentTransform; - create_info.compositeAlpha = vk::CompositeAlphaFlagBitsKHR::eOpaque; - create_info.presentMode = vk::PresentModeKHR::eFifo; - create_info.clipped = VK_TRUE; - create_info.oldSwapchain = nullptr; + create_info.imageSharingMode = vk::SharingMode::eExclusive; + create_info.preTransform = r.capabilities.currentTransform; + create_info.compositeAlpha = vk::CompositeAlphaFlagBitsKHR::eOpaque; + create_info.presentMode = vk::PresentModeKHR::eFifo; + create_info.clipped = VK_TRUE; + create_info.oldSwapchain = nullptr; swapchain_format = create_info.imageFormat; swapchain_extent = extent; @@ -331,7 +329,7 @@ static void VulkanDeleteSwapchain(VulkanSwapchain* s) { auto swapchain_owner = std::unique_ptr(s); auto& graphics = g_window_ctx->graphic_ctx; - Transfer::WaitForGraphicsIdle(); + Transfer::WaitForQueueIdle(); if (s->image_acquired_semaphores != nullptr) { for (uint32_t i = 0; i < s->swapchain_images_count; i++) { @@ -380,15 +378,9 @@ static void VulkanRecreateSwapchain() { g_window_ctx->swapchain = VulkanCreateSwapchain(2); } -static void ValidatePreparedCommand(CommandBuffer& buffer) { - if (buffer.IsInvalid() || buffer.GetQueue() != GraphicContext::QUEUE_GFX) { - EXIT("prepared frames must be recorded on the graphics queue\n"); - } -} - PreparedFrame& WindowPrepareFrame(CommandBuffer& buffer, VideoOutVulkanImage& image) { KYTY_PROFILER_FUNCTION(); - ValidatePreparedCommand(buffer); + EXIT_IF(buffer.IsInvalid()); if (image.format == vk::Format::eUndefined) { EXIT("unsupported presentation source, image=%p\n", static_cast(&image)); } @@ -408,7 +400,7 @@ PreparedFrame& WindowPrepareFrame(CommandBuffer& buffer, VideoOutVulkanImage& im PreparedFrame& WindowPrepareBlankFrame(CommandBuffer& buffer, uint32_t width, uint32_t height, bool opaque) { KYTY_PROFILER_FUNCTION(); - ValidatePreparedCommand(buffer); + EXIT_IF(buffer.IsInvalid()); auto* pool = GetPreparedFramePool(); auto format = pool->GetFormat(); auto* frame = pool->Acquire(); @@ -462,7 +454,7 @@ void WindowPresentFrame(PreparedFrame& frame) { } EXIT_NOT_IMPLEMENTED(swapchain->current_index == static_cast(-1)); if (frame.present_commands == nullptr) { - frame.present_commands = std::make_unique(GraphicContext::QUEUE_GFX); + frame.present_commands = std::make_unique(); } frame.present_commands->WaitForFenceAndReset(); auto& buffer = *frame.present_commands; @@ -510,14 +502,10 @@ void WindowPresentFrame(PreparedFrame& frame) { present.waitSemaphoreCount = 1; present.pResults = nullptr; - const auto& queue = g_window_ctx->graphic_ctx.queues[GraphicContext::QUEUE_PRESENT]; - - if (queue.mutex != nullptr) { - queue.mutex->Lock(); - } - result = queue.vk_queue.presentKHR(&present); - if (queue.mutex != nullptr) { - queue.mutex->Unlock(); + auto& graphics = g_window_ctx->graphic_ctx; + { + Common::LockGuard lock(graphics.queue_mutex); + result = graphics.queue.presentKHR(&present); } switch (result) { case vk::Result::eSuccess: break; diff --git a/src/graphics/presentation/window/vulkanWindow.cpp b/src/graphics/presentation/window/vulkanWindow.cpp index 0ce31ace9..2df2a4bc7 100644 --- a/src/graphics/presentation/window/vulkanWindow.cpp +++ b/src/graphics/presentation/window/vulkanWindow.cpp @@ -124,128 +124,39 @@ static bool CheckFormat(vk::PhysicalDevice device, vk::Format format, bool tile, return (supported_features & features) == features; } -struct QueueInfo { - uint32_t family = 0; - uint32_t index = 0; - bool graphics = false; - bool compute = false; - bool present = false; -}; - -struct VulkanQueues { - uint32_t family_count = 0; - std::vector family_used; - std::vector available; - std::vector graphics; - std::vector compute; - std::vector present; -}; - -static void VulkanDumpQueues(const VulkanQueues& qs) { - LOGF("Queues selected:\n" - "\t family_count = %u\n", - qs.family_count); - std::vector nums; - for (auto u: qs.family_used) { - nums.push_back(fmt::format("{}", u)); - } - LOGF("\t family_used = [%s]\n" - "\t graphics:\n", - Common::Concat(nums, ", ").c_str()); - for (const auto& q: qs.graphics) { - LOGF("\t\t family = %u, index = %u\n", q.family, q.index); - } - LOGF("\t compute:\n"); - for (const auto& q: qs.compute) { - LOGF("\t\t family = %u, index = %u\n", q.family, q.index); - } - LOGF("\t present:\n"); - for (const auto& q: qs.present) { - LOGF("\t\t family = %u, index = %u\n", q.family, q.index); - } -} - -static VulkanQueues VulkanFindQueues(vk::PhysicalDevice device, vk::SurfaceKHR surface, - uint32_t graphics_num, uint32_t compute_num, - uint32_t present_num) { +static uint32_t VulkanFindQueueFamily(vk::PhysicalDevice device, vk::SurfaceKHR surface) { EXIT_IF(device == nullptr); EXIT_IF(surface == nullptr); - VulkanQueues qs; - uint32_t queue_family_count = 0; device.getQueueFamilyProperties(&queue_family_count, nullptr); std::vector queue_families(queue_family_count); device.getQueueFamilyProperties(&queue_family_count, queue_families.data()); - qs.family_count = queue_family_count; - - uint32_t family = 0; - for (auto& f: queue_families) { - vk::Bool32 presentation_supported = VK_FALSE; + const auto required = vk::QueueFlagBits::eGraphics | vk::QueueFlagBits::eCompute; + for (uint32_t family = 0; family < queue_family_count; family++) { + const auto& properties = queue_families[family]; + vk::Bool32 presentation_supported = VK_FALSE; RequireVulkanSuccess(device.getSurfaceSupportKHR(family, surface, &presentation_supported), "vkGetPhysicalDeviceSurfaceSupportKHR"); LOGF("\tqueue family: %s [count = %u], [present = %s]\n", - VulkanToString(f.queueFlags).c_str(), f.queueCount, + VulkanToString(properties.queueFlags).c_str(), properties.queueCount, (presentation_supported == VK_TRUE ? "true" : "false")); - - for (uint32_t i = 0; i < f.queueCount; i++) { - QueueInfo info; - info.family = family; - info.index = i; - info.graphics = static_cast(f.queueFlags & vk::QueueFlagBits::eGraphics); - info.compute = static_cast(f.queueFlags & vk::QueueFlagBits::eCompute); - info.present = (presentation_supported == VK_TRUE); - - qs.available.push_back(info); - } - - qs.family_used.push_back(0); - - family++; - } - - auto select_queues = [&qs](uint32_t count, auto matches, auto& selected) { - for (uint32_t i = 0; i < count; i++) { - auto it = std::find_if(qs.available.begin(), qs.available.end(), matches); - if (it == qs.available.end()) { - continue; - } - - qs.family_used[it->family]++; - selected.push_back(*it); - qs.available.erase(it); - } - }; - - select_queues(graphics_num, [](const auto& q) { return q.graphics; }, qs.graphics); - - const uint32_t graphics_family = - qs.graphics.empty() ? static_cast(-1) : qs.graphics.front().family; - select_queues( - compute_num, - [graphics_family](const auto& q) { return q.compute && q.family == graphics_family; }, - qs.compute); - if (compute_num != 0 && qs.compute.empty()) { - auto graphics_compute = std::find_if(qs.graphics.begin(), qs.graphics.end(), - [](const auto& q) { return q.compute; }); - if (graphics_compute != qs.graphics.end()) { - // Reuse the universal graphics queue when Intel GPUs expose no spare compute queue. - qs.compute.push_back(*graphics_compute); + if (properties.queueCount != 0 && (properties.queueFlags & required) == required && + presentation_supported == VK_TRUE) { + LOGF("\tselected universal queue family %u\n", family); + return family; } } - - select_queues(present_num, [](const auto& q) { return q.present; }, qs.present); - - return qs; + return static_cast(-1); } // NOLINTNEXTLINE(readability-function-cognitive-complexity) static void VulkanFindPhysicalDevice(vk::Instance instance, vk::SurfaceKHR surface, const std::vector& device_extensions, SurfaceCapabilities& out_capabilities, - vk::PhysicalDevice& out_device, VulkanQueues& out_queues) { + vk::PhysicalDevice& out_device, uint32_t& out_queue_family) { EXIT_IF(instance == nullptr); EXIT_IF(surface == nullptr); @@ -255,8 +166,8 @@ static void VulkanFindPhysicalDevice(vk::Instance instance, vk::SurfaceKHR surfa }); EXIT_NOT_IMPLEMENTED(devices.empty()); - vk::PhysicalDevice best_device = nullptr; - VulkanQueues best_queues; + vk::PhysicalDevice best_device = nullptr; + uint32_t best_queue_family = static_cast(-1); SurfaceCapabilities best_capabilities; for (const auto& device: devices) { @@ -304,16 +215,9 @@ static void VulkanFindPhysicalDevice(vk::Instance instance, vk::SurfaceKHR surfa device.getFeatures2(&device_features2); - auto qs = - VulkanFindQueues(device, surface, GraphicContext::QUEUE_GFX_NUM, - GraphicContext::QUEUE_COMPUTE_NUM, GraphicContext::QUEUE_PRESENT_NUM); - - VulkanDumpQueues(qs); - - if (qs.graphics.size() != GraphicContext::QUEUE_GFX_NUM || - !(qs.compute.size() >= 1 && qs.compute.size() <= GraphicContext::QUEUE_COMPUTE_NUM) || - qs.present.size() != GraphicContext::QUEUE_PRESENT_NUM) { - LOGF("Not enough queues\n"); + const auto queue_family = VulkanFindQueueFamily(device, surface); + if (queue_family == static_cast(-1)) { + LOGF("No universal graphics, compute, and presentation queue\n"); skip_device = true; } @@ -504,13 +408,13 @@ static void VulkanFindPhysicalDevice(vk::Instance instance, vk::SurfaceKHR surfa if (best_device == nullptr || device_properties.deviceType == vk::PhysicalDeviceType::eDiscreteGpu) { best_device = device; - best_queues = qs; + best_queue_family = queue_family; best_capabilities = std::move(candidate_capabilities); } } - out_device = best_device; - out_queues = best_queues; + out_device = best_device; + out_queue_family = best_queue_family; if (best_device != nullptr) { out_capabilities = std::move(best_capabilities); } @@ -558,35 +462,19 @@ static void VulkanInitSubgroupSizeControl(vk::PhysicalDevice physical_device) { graphics.subgroup_size_control_enabled ? "true" : "false"); } -static vk::Device VulkanCreateDevice(vk::PhysicalDevice physical_device, vk::SurfaceKHR surface, - const VulkanExtensions& r, const VulkanQueues& queues, +static vk::Device VulkanCreateDevice(vk::PhysicalDevice physical_device, const VulkanExtensions& r, + uint32_t queue_family, const std::vector& device_extensions) { EXIT_IF(physical_device == nullptr); auto& graphics = g_window_ctx->graphic_ctx; - EXIT_IF(surface == nullptr); + EXIT_IF(queue_family == static_cast(-1)); - std::vector queue_create_info(queues.family_count); - std::vector> queue_priority(queues.family_count); - uint32_t queue_create_info_num = 0; - - for (uint32_t i = 0; i < queues.family_count; i++) { - if (queues.family_used[i] != 0) { - for (uint32_t pi = 0; pi < queues.family_used[i]; pi++) { - queue_priority[queue_create_info_num].push_back(1.0f); - } - - queue_create_info[queue_create_info_num].sType = - vk::StructureType::eDeviceQueueCreateInfo; - queue_create_info[queue_create_info_num].pNext = nullptr; - queue_create_info[queue_create_info_num].flags = {}; - queue_create_info[queue_create_info_num].queueFamilyIndex = i; - queue_create_info[queue_create_info_num].queueCount = queues.family_used[i]; - queue_create_info[queue_create_info_num].pQueuePriorities = - queue_priority[queue_create_info_num].data(); - - queue_create_info_num++; - } - } + const float queue_priority = 1.0f; + vk::DeviceQueueCreateInfo queue_create_info {}; + queue_create_info.sType = vk::StructureType::eDeviceQueueCreateInfo; + queue_create_info.queueFamilyIndex = queue_family; + queue_create_info.queueCount = 1; + queue_create_info.pQueuePriorities = &queue_priority; vk::PhysicalDeviceColorWriteEnableFeaturesEXT color_write_ext {}; color_write_ext.sType = vk::StructureType::ePhysicalDeviceColorWriteEnableFeaturesEXT; @@ -678,8 +566,8 @@ static vk::Device VulkanCreateDevice(vk::PhysicalDevice physical_device, vk::Sur create_info.sType = vk::StructureType::eDeviceCreateInfo; create_info.pNext = &features13; create_info.flags = {}; - create_info.pQueueCreateInfos = queue_create_info.data(); - create_info.queueCreateInfoCount = queue_create_info_num; + create_info.pQueueCreateInfos = &queue_create_info; + create_info.queueCreateInfoCount = 1; create_info.enabledLayerCount = (r.enable_validation_layers ? static_cast(r.required_layers.size()) : 0); create_info.ppEnabledLayerNames = @@ -865,64 +753,6 @@ static VKAPI_ATTR vk::Result VKAPI_CALL VulkanCreateDebugUtilsMessengerEXT( return vk::Result::eErrorExtensionNotPresent; } -static void VulkanCreateQueues(const VulkanQueues& queues) { - auto& graphics = g_window_ctx->graphic_ctx; - EXIT_IF(graphics.device == nullptr); - EXIT_IF(queues.graphics.size() != 1); - EXIT_IF(queues.present.size() != 1); - EXIT_IF(!(queues.compute.size() >= 1 && - queues.compute.size() <= GraphicContext::QUEUE_COMPUTE_NUM)); - - auto get_queue = [&graphics](int id, const QueueInfo& info) { - graphics.queues[id].family = info.family; - graphics.queues[id].index = info.index; - EXIT_IF(graphics.queues[id].vk_queue != nullptr); - graphics.device.getQueue(graphics.queues[id].family, graphics.queues[id].index, - &graphics.queues[id].vk_queue); - EXIT_NOT_IMPLEMENTED(graphics.queues[id].vk_queue == nullptr); - }; - - get_queue(GraphicContext::QUEUE_GFX, queues.graphics[0]); - graphics.queues[GraphicContext::QUEUE_UTIL].family = - graphics.queues[GraphicContext::QUEUE_GFX].family; - graphics.queues[GraphicContext::QUEUE_UTIL].index = - graphics.queues[GraphicContext::QUEUE_GFX].index; - graphics.queues[GraphicContext::QUEUE_UTIL].vk_queue = - graphics.queues[GraphicContext::QUEUE_GFX].vk_queue; - LOGF("Vulkan queue: using graphics queue for utility submissions to preserve resource " - "ordering\n"); - get_queue(GraphicContext::QUEUE_PRESENT, queues.present[0]); - - for (int id = 0; id < GraphicContext::QUEUE_COMPUTE_NUM; id++) { - get_queue(GraphicContext::QUEUE_COMPUTE_START + id, - queues.compute[id % queues.compute.size()]); - } - - for (int id = 0; id < GraphicContext::QUEUES_NUM; id++) { - auto& queue = graphics.queues[id]; - if (queue.vk_queue == nullptr) { - continue; - } - EXIT_IF(queue.mutex != nullptr); - - for (int other_id = 0; other_id < id; other_id++) { - auto& other = graphics.queues[other_id]; - if (other.vk_queue != queue.vk_queue) { - continue; - } - - EXIT_IF(other.mutex == nullptr); - queue.mutex = other.mutex; - LOGF("Vulkan queue: sharing mutex for queue ids %d and %d\n", other_id, id); - break; - } - - if (queue.mutex == nullptr) { - queue.mutex = &graphics.queue_mutexes[id]; - } - } -} - static void VulkanCheckInstanceVersion() { uint32_t version = VK_API_VERSION_1_0; @@ -1061,11 +891,11 @@ void VulkanCreate(WindowContext& window) { window.surface_capabilities = new SurfaceCapabilities {}; - VulkanQueues queues; + uint32_t queue_family = static_cast(-1); VulkanFindPhysicalDevice(window.graphic_ctx.instance, window.surface, device_extensions, *window.surface_capabilities, window.graphic_ctx.physical_device, - queues); + queue_family); if (window.graphic_ctx.physical_device == nullptr) { EXIT("Could not find suitable device"); @@ -1102,19 +932,20 @@ void VulkanCreate(WindowContext& window) { VulkanInitSubgroupSizeControl(window.graphic_ctx.physical_device); - window.graphic_ctx.device = VulkanCreateDevice(window.graphic_ctx.physical_device, - window.surface, r, queues, device_extensions); + window.graphic_ctx.device = + VulkanCreateDevice(window.graphic_ctx.physical_device, r, queue_family, device_extensions); if (window.graphic_ctx.device == nullptr) { EXIT("Could not create device"); } VULKAN_HPP_DEFAULT_DISPATCHER.init(window.graphic_ctx.device); + window.graphic_ctx.queue_family = queue_family; + window.graphic_ctx.device.getQueue(queue_family, 0, &window.graphic_ctx.queue); + EXIT_IF(window.graphic_ctx.queue == nullptr); if (!window.graphic_ctx.CreateAllocator()) { EXIT("Could not create Vulkan memory allocator"); } - VulkanCreateQueues(queues); - window.swapchain = VulkanCreateSwapchain(2); RenderDocSetActiveWindow(window.graphic_ctx.instance, window.window); } diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index 5b40e3c2c..78d906c31 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -855,6 +855,22 @@ public: [[nodiscard]] vk::Device Device() const { return m_device; } + void CheckCommandPoolGrowth() { + EnsureRuntimeContext(); + std::array commands; + 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 CheckMutableStorageSrgbView() { constexpr const char *name = "StorageTextureMutableSrgbView"; vk::ImageCreateInfo image_info{}; @@ -1350,7 +1366,7 @@ public: target_info.tile_mode = linear; { - CommandBuffer command(GraphicContext::QUEUE_GFX); + CommandBuffer command; (void)texture_cache.FindTexture(command, sampled_info, false); auto& target = texture_cache.FindRenderTarget(command, target_info); @@ -3047,12 +3063,8 @@ private: m_physical_device.getProperties( &m_runtime_context.physical_device_properties); m_runtime_context.physical_device_memory_properties = m_memory_properties; - for (auto &queue : m_runtime_context.queues) { - queue.mutex = &m_runtime_queue_mutex; - queue.family = m_queue_family; - queue.index = 0; - queue.vk_queue = m_queue; - } + m_runtime_context.queue_family = m_queue_family; + m_runtime_context.queue = m_queue; VmaVulkanFunctions functions{}; functions.vkGetInstanceProcAddr = @@ -3172,7 +3184,7 @@ private: if (m_device != nullptr) { RequireVulkanSuccess(m_device.waitIdle(), "vkDeviceWaitIdle"); if (m_runtime_context.allocator != nullptr) { - GraphicsRenderReleaseThreadCommandPools(); + GraphicsRenderReleaseThreadCommandPool(); Transfer::ReleaseCachedResources(); vmaDestroyAllocator(m_runtime_context.allocator); m_runtime_context.allocator = nullptr; @@ -3413,7 +3425,6 @@ private: vk::CommandPool m_command_pool = nullptr; u32 m_queue_family = 0; vk::PhysicalDeviceMemoryProperties m_memory_properties{}; - Common::Mutex m_runtime_queue_mutex; GraphicContext m_runtime_context{}; }; @@ -10540,13 +10551,6 @@ void CheckBufferCacheRangeMerge() { MergeOverlappingBufferCacheRange(merged, {0x10000, 0x1000}) && merged.address == 0xc000 && merged.size == 0xe000, "contained range changed the cache union"); - Require("BufferCacheRangeMerge", "queue ownership", - CanMergeBufferCacheQueueMask(0, 3) && - CanMergeBufferCacheQueueMask(uint64_t{1} << 3u, 3) && - !CanMergeBufferCacheQueueMask( - (uint64_t{1} << 2u) | (uint64_t{1} << 3u), 3) && - !CanMergeBufferCacheQueueMask(0, 64), - "cross-queue or invalid queue ownership was accepted"); std::printf("[host] %-32s ok\n", "BufferCacheRangeMerge"); } @@ -13907,26 +13911,27 @@ struct FenceLifetimeProbe { bool *destroyed = nullptr; }; -void CheckCrossQueueImageLifetime() { +void CheckSharedFenceResourceLifetime() { bool destroyed = false; auto image = std::make_shared(&destroyed); - FenceResourceRetainer graphics; - FenceResourceRetainer compute; - graphics.Retain(image); - compute.Retain(image); - graphics.Retain(image); + FenceResourceRetainer first; + FenceResourceRetainer second; + first.Retain(image); + second.Retain(image); + first.Retain(image); image.reset(); - Require("CrossQueueImageLifetime", "retained", - !destroyed && !graphics.Empty() && !compute.Empty(), + Require("SharedFenceResourceLifetime", "retained", + !destroyed && !first.Empty() && !second.Empty(), "cache removal destroyed an image retained by command buffers"); - graphics.ReleaseAfterFence(); - Require("CrossQueueImageLifetime", "first fence", - !destroyed && graphics.Empty() && !compute.Empty(), - "first command-buffer fence destroyed another queue's image"); - compute.ReleaseAfterFence(); - Require("CrossQueueImageLifetime", "last fence", destroyed && compute.Empty(), + first.ReleaseAfterFence(); + Require("SharedFenceResourceLifetime", "first fence", + !destroyed && first.Empty() && !second.Empty(), + "first command-buffer fence destroyed another buffer's image"); + second.ReleaseAfterFence(); + Require("SharedFenceResourceLifetime", "last fence", + destroyed && second.Empty(), "last referencing command-buffer fence did not destroy the image"); - std::printf("[host] %-32s ok\n", "CrossQueueImageLifetime"); + std::printf("[host] %-32s ok\n", "SharedFenceResourceLifetime"); } void CheckHostDmaMetadataReuse() { @@ -14216,7 +14221,7 @@ int main(int argc, char **argv) { CheckStencilAttachmentAccess(); CheckDepthTargetFootprints(); CheckHtileClearTargetResolution(); - CheckCrossQueueImageLifetime(); + CheckSharedFenceResourceLifetime(); CheckHostDmaMetadataReuse(); #else (void)argc; @@ -14228,6 +14233,7 @@ int main(int argc, char **argv) { CheckEmbeddedFetchLaneSpill(); CheckPs5GameExampleImageClearRuntimeShape(); VulkanHarness vulkan; + vulkan.CheckCommandPoolGrowth(); vulkan.CheckGpuTilerCpuParity(); vulkan.CheckQueryRegionImageClassification(); vulkan.CheckMutableStorageSrgbView();