| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250 |
- // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
- // SPDX-License-Identifier: GPL-2.0-or-later
- #pragma once
- #include <condition_variable>
- #include <cstddef>
- #include <functional>
- #include <memory>
- #include <thread>
- #include <utility>
- #include <queue>
- #include "common/alignment.h"
- #include "common/common_types.h"
- #include "common/polyfill_thread.h"
- #include "video_core/renderer_vulkan/vk_master_semaphore.h"
- #include "video_core/vulkan_common/vulkan_wrapper.h"
- namespace VideoCommon {
- template <typename Trait>
- class QueryCacheBase;
- }
- namespace Vulkan {
- class CommandPool;
- class Device;
- class Framebuffer;
- class GraphicsPipeline;
- class StateTracker;
- struct QueryCacheParams;
- /// The scheduler abstracts command buffer and fence management with an interface that's able to do
- /// OpenGL-like operations on Vulkan command buffers.
- class Scheduler {
- public:
- explicit Scheduler(const Device& device, StateTracker& state_tracker);
- ~Scheduler();
- /// Sends the current execution context to the GPU.
- u64 Flush(VkSemaphore signal_semaphore = nullptr, VkSemaphore wait_semaphore = nullptr);
- /// Sends the current execution context to the GPU and waits for it to complete.
- void Finish(VkSemaphore signal_semaphore = nullptr, VkSemaphore wait_semaphore = nullptr);
- /// Waits for the worker thread to finish executing everything. After this function returns it's
- /// safe to touch worker resources.
- void WaitWorker();
- /// Sends currently recorded work to the worker thread.
- void DispatchWork();
- /// Requests to begin a renderpass.
- void RequestRenderpass(const Framebuffer* framebuffer);
- /// Requests the current executino context to be able to execute operations only allowed outside
- /// of a renderpass.
- void RequestOutsideRenderPassOperationContext();
- /// Update the pipeline to the current execution context.
- bool UpdateGraphicsPipeline(GraphicsPipeline* pipeline);
- /// Update the rescaling state. Returns true if the state has to be updated.
- bool UpdateRescaling(bool is_rescaling);
- /// Invalidates current command buffer state except for render passes
- void InvalidateState();
- /// Assigns the query cache.
- void SetQueryCache(VideoCommon::QueryCacheBase<QueryCacheParams>& query_cache_) {
- query_cache = &query_cache_;
- }
- // Registers a callback to perform on queue submission.
- void RegisterOnSubmit(std::function<void()>&& func) {
- on_submit = std::move(func);
- }
- /// Send work to a separate thread.
- template <typename T>
- void Record(T&& command) {
- if (chunk->Record(command)) {
- return;
- }
- DispatchWork();
- (void)chunk->Record(command);
- }
- /// Returns the current command buffer tick.
- [[nodiscard]] u64 CurrentTick() const noexcept {
- return master_semaphore->CurrentTick();
- }
- /// Returns true when a tick has been triggered by the GPU.
- [[nodiscard]] bool IsFree(u64 tick) const noexcept {
- return master_semaphore->IsFree(tick);
- }
- /// Waits for the given tick to trigger on the GPU.
- void Wait(u64 tick) {
- if (tick >= master_semaphore->CurrentTick()) {
- // Make sure we are not waiting for the current tick without signalling
- Flush();
- }
- master_semaphore->Wait(tick);
- }
- /// Returns the master timeline semaphore.
- [[nodiscard]] MasterSemaphore& GetMasterSemaphore() const noexcept {
- return *master_semaphore;
- }
- std::mutex submit_mutex;
- private:
- class Command {
- public:
- virtual ~Command() = default;
- virtual void Execute(vk::CommandBuffer cmdbuf) const = 0;
- Command* GetNext() const {
- return next;
- }
- void SetNext(Command* next_) {
- next = next_;
- }
- private:
- Command* next = nullptr;
- };
- template <typename T>
- class TypedCommand final : public Command {
- public:
- explicit TypedCommand(T&& command_) : command{std::move(command_)} {}
- ~TypedCommand() override = default;
- TypedCommand(TypedCommand&&) = delete;
- TypedCommand& operator=(TypedCommand&&) = delete;
- void Execute(vk::CommandBuffer cmdbuf) const override {
- command(cmdbuf);
- }
- private:
- T command;
- };
- class CommandChunk final {
- public:
- void ExecuteAll(vk::CommandBuffer cmdbuf);
- template <typename T>
- bool Record(T& command) {
- using FuncType = TypedCommand<T>;
- static_assert(sizeof(FuncType) < sizeof(data), "Lambda is too large");
- command_offset = Common::AlignUp(command_offset, alignof(FuncType));
- if (command_offset > sizeof(data) - sizeof(FuncType)) {
- return false;
- }
- Command* const current_last = last;
- last = new (data.data() + command_offset) FuncType(std::move(command));
- if (current_last) {
- current_last->SetNext(last);
- } else {
- first = last;
- }
- command_offset += sizeof(FuncType);
- return true;
- }
- void MarkSubmit() {
- submit = true;
- }
- bool Empty() const {
- return command_offset == 0;
- }
- bool HasSubmit() const {
- return submit;
- }
- private:
- Command* first = nullptr;
- Command* last = nullptr;
- size_t command_offset = 0;
- bool submit = false;
- alignas(std::max_align_t) std::array<u8, 0x8000> data{};
- };
- struct State {
- VkRenderPass renderpass = nullptr;
- VkFramebuffer framebuffer = nullptr;
- VkExtent2D render_area = {0, 0};
- GraphicsPipeline* graphics_pipeline = nullptr;
- bool is_rescaling = false;
- bool rescaling_defined = false;
- };
- void WorkerThread(std::stop_token stop_token);
- void AllocateWorkerCommandBuffer();
- u64 SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore);
- void AllocateNewContext();
- void EndPendingOperations();
- void EndRenderPass();
- void AcquireNewChunk();
- const Device& device;
- StateTracker& state_tracker;
- std::unique_ptr<MasterSemaphore> master_semaphore;
- std::unique_ptr<CommandPool> command_pool;
- VideoCommon::QueryCacheBase<QueryCacheParams>* query_cache = nullptr;
- vk::CommandBuffer current_cmdbuf;
- std::unique_ptr<CommandChunk> chunk;
- std::function<void()> on_submit;
- State state;
- u32 num_renderpass_images = 0;
- std::array<VkImage, 9> renderpass_images{};
- std::array<VkImageSubresourceRange, 9> renderpass_image_ranges{};
- std::queue<std::unique_ptr<CommandChunk>> work_queue;
- std::vector<std::unique_ptr<CommandChunk>> chunk_reserve;
- std::mutex execution_mutex;
- std::mutex reserve_mutex;
- std::mutex queue_mutex;
- std::condition_variable_any event_cv;
- std::jthread worker_thread;
- };
- } // namespace Vulkan
|