// Copyright 2019 yuzu Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. #pragma once #include #include #include #include #include #include #include "common/threadsafe_queue.h" #include "video_core/gpu.h" namespace Tegra { struct FramebufferConfig; class DmaPusher; } // namespace Tegra namespace Core { class System; namespace Timing { struct EventType; } // namespace Timing } // namespace Core namespace VideoCommon::GPUThread { /// Command to signal to the GPU thread that processing has ended struct EndProcessingCommand final {}; /// Command to signal to the GPU thread that a command list is ready for processing struct SubmitListCommand final { explicit SubmitListCommand(Tegra::CommandList&& entries) : entries{std::move(entries)} {} Tegra::CommandList entries; }; /// Command to signal to the GPU thread that a swap buffers is pending struct SwapBuffersCommand final { explicit SwapBuffersCommand(std::optional framebuffer) : framebuffer{std::move(framebuffer)} {} std::optional framebuffer; }; /// Command to signal to the GPU thread to flush a region struct FlushRegionCommand final { explicit constexpr FlushRegionCommand(CacheAddr addr, u64 size) : addr{addr}, size{size} {} CacheAddr addr; u64 size; }; /// Command to signal to the GPU thread to invalidate a region struct InvalidateRegionCommand final { explicit constexpr InvalidateRegionCommand(CacheAddr addr, u64 size) : addr{addr}, size{size} {} CacheAddr addr; u64 size; }; /// Command to signal to the GPU thread to flush and invalidate a region struct FlushAndInvalidateRegionCommand final { explicit constexpr FlushAndInvalidateRegionCommand(CacheAddr addr, u64 size) : addr{addr}, size{size} {} CacheAddr addr; u64 size; }; using CommandData = std::variant; struct CommandDataContainer { CommandDataContainer() = default; CommandDataContainer(CommandData&& data, u64 next_fence) : data{std::move(data)}, fence{next_fence} {} CommandDataContainer& operator=(const CommandDataContainer& t) { data = std::move(t.data); fence = t.fence; return *this; } CommandData data; u64 fence{}; }; /// Struct used to synchronize the GPU thread struct SynchState final { std::atomic_bool is_running{true}; std::atomic_int queued_frame_count{}; std::mutex synchronization_mutex; std::mutex commands_mutex; std::condition_variable commands_condition; std::condition_variable synchronization_condition; /// Returns true if the gap in GPU commands is small enough that we can consider the CPU and GPU /// synchronized. This is entirely empirical. bool IsSynchronized() const { constexpr std::size_t max_queue_gap{5}; return queue.Size() <= max_queue_gap; } void TrySynchronize() { if (IsSynchronized()) { std::lock_guard lock{synchronization_mutex}; synchronization_condition.notify_one(); } } void WaitForSynchronization(u64 fence); void SignalCommands() { if (queue.Empty()) { return; } commands_condition.notify_one(); } void WaitForCommands() { std::unique_lock lock{commands_mutex}; commands_condition.wait(lock, [this] { return !queue.Empty(); }); } using CommandQueue = Common::SPSCQueue; CommandQueue queue; u64 last_fence{}; std::atomic signaled_fence{}; }; /// Class used to manage the GPU thread class ThreadManager final { public: explicit ThreadManager(Core::System& system, VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_pusher); ~ThreadManager(); /// Push GPU command entries to be processed void SubmitList(Tegra::CommandList&& entries); /// Swap buffers (render frame) void SwapBuffers( std::optional> framebuffer); /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory void FlushRegion(CacheAddr addr, u64 size); /// Notify rasterizer that any caches of the specified region should be invalidated void InvalidateRegion(CacheAddr addr, u64 size); /// Notify rasterizer that any caches of the specified region should be flushed and invalidated void FlushAndInvalidateRegion(CacheAddr addr, u64 size); private: /// Pushes a command to be executed by the GPU thread u64 PushCommand(CommandData&& command_data); private: SynchState state; Core::System& system; Core::Timing::EventType* synchronization_event{}; std::thread thread; std::thread::id thread_id; }; } // namespace VideoCommon::GPUThread