#ifndef EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_COMMANDSCHEDULER_H_ #define EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_COMMANDSCHEDULER_H_ #include "common/common.h" #include "common/uniqueFunction.h" #include "graphics/host_gpu/renderer/dynamicState.h" #include "graphics/host_gpu/renderer/masterSemaphore.h" #include "graphics/host_gpu/renderer/render.h" #include #include #include #include #include namespace Libs::Graphics { class CommandScheduler { public: CommandScheduler(RenderContext& context, GraphicContext& graphics); ~CommandScheduler(); KYTY_CLASS_NO_COPY(CommandScheduler); void Begin(HW::Context& registers, HW::UserConfig& user_config, HW::Shader& shaders); void BeginRendering(const RenderState& state); void EndRendering(); void Flush(); void Flush(SubmitInfo& submit); void FlushAndWait(); void Finish(); 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(); void Wait(uint64_t tick); void PopPendingOperations(); void DrainPriorityOperations(); void WaitPriorityOperations(uint64_t tick); // Guest-memory completions use the priority queue; normal callbacks maintain GPU resources. void DeferOperation(Common::UniqueFunction&& operation); void DeferPriorityOperation(Common::UniqueFunction&& operation); [[nodiscard]] bool HasPendingPriorityOperations(); [[nodiscard]] static bool InDeferredOperation() noexcept; [[nodiscard]] bool Active() const noexcept { return m_command.m_registers != nullptr; } void CheckActive() const; CommandBuffer& Current(); [[nodiscard]] uint64_t CurrentTick() const noexcept { return m_master.CurrentTick(); } [[nodiscard]] bool IsFree(uint64_t tick); [[nodiscard]] MasterSemaphore& GetMasterSemaphore() noexcept { return m_master; } [[nodiscard]] DynamicState& GetDynamicState() noexcept { return m_dynamic_state; } [[nodiscard]] RenderContext& Context() const noexcept { return m_context; } [[nodiscard]] GraphicContext& Graphics() const noexcept { return m_graphics; } private: class CommandPool { public: CommandPool(GraphicContext& graphics, MasterSemaphore& master); ~CommandPool(); KYTY_CLASS_NO_COPY(CommandPool); vk::CommandBuffer Commit(); private: static constexpr size_t GrowStep = 4; 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 }; struct PendingOperation { Common::UniqueFunction callback; uint64_t tick = 0; }; void BeginNext(); void PriorityOperationsThread(std::stop_token stop); void QueueOperation(Common::UniqueFunction&& operation, bool priority); void RunOperation(Common::UniqueFunction&& operation); MasterSemaphore m_master; RenderContext& m_context; GraphicContext& m_graphics; CommandPool m_command_pool; CommandBuffer m_command; DynamicState m_dynamic_state; 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; }; } // namespace Libs::Graphics #endif // EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_COMMANDSCHEDULER_H_