From f546fb35edd87f7837e74ee03e29b89bcb1559ea Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Fri, 22 Feb 2019 01:27:25 -0300
Subject: [PATCH] vk_scheduler: Implement a scheduler

The scheduler abstracts command buffer and fence management with an
interface that's able to do OpenGL-like operations on Vulkan command
buffers.

It returns by value a command buffer and fence that have to be used for
subsequent operations until Flush or Finish is executed, after that the
current execution context (the pair of command buffers and fences) gets
invalidated a new one must be fetched. Thankfully validation layers will
quickly detect if this is skipped throwing an error due to modifications
to a sent command buffer.
---
 src/video_core/CMakeLists.txt                 |  4 +-
 .../renderer_vulkan/vk_scheduler.cpp          | 60 ++++++++++++++++
 src/video_core/renderer_vulkan/vk_scheduler.h | 69 +++++++++++++++++++
 3 files changed, 132 insertions(+), 1 deletion(-)
 create mode 100644 src/video_core/renderer_vulkan/vk_scheduler.cpp
 create mode 100644 src/video_core/renderer_vulkan/vk_scheduler.h

diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 56f789e0b..6036d6ed3 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -109,7 +109,9 @@ if (ENABLE_VULKAN)
         renderer_vulkan/vk_memory_manager.cpp
         renderer_vulkan/vk_memory_manager.h
         renderer_vulkan/vk_resource_manager.cpp
-        renderer_vulkan/vk_resource_manager.h)
+        renderer_vulkan/vk_resource_manager.h
+        renderer_vulkan/vk_scheduler.cpp
+        renderer_vulkan/vk_scheduler.h)
 
     target_include_directories(video_core PRIVATE ../../externals/Vulkan-Headers/include)
     target_compile_definitions(video_core PRIVATE HAS_VULKAN)
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp
new file mode 100644
index 000000000..f1fea1871
--- /dev/null
+++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp
@@ -0,0 +1,60 @@
+// Copyright 2019 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/assert.h"
+#include "common/logging/log.h"
+#include "video_core/renderer_vulkan/declarations.h"
+#include "video_core/renderer_vulkan/vk_device.h"
+#include "video_core/renderer_vulkan/vk_resource_manager.h"
+#include "video_core/renderer_vulkan/vk_scheduler.h"
+
+namespace Vulkan {
+
+VKScheduler::VKScheduler(const VKDevice& device, VKResourceManager& resource_manager)
+    : device{device}, resource_manager{resource_manager} {
+    next_fence = &resource_manager.CommitFence();
+    AllocateNewContext();
+}
+
+VKScheduler::~VKScheduler() = default;
+
+VKExecutionContext VKScheduler::GetExecutionContext() const {
+    return VKExecutionContext(current_fence, current_cmdbuf);
+}
+
+VKExecutionContext VKScheduler::Flush(vk::Semaphore semaphore) {
+    SubmitExecution(semaphore);
+    current_fence->Release();
+    AllocateNewContext();
+    return GetExecutionContext();
+}
+
+VKExecutionContext VKScheduler::Finish(vk::Semaphore semaphore) {
+    SubmitExecution(semaphore);
+    current_fence->Wait();
+    current_fence->Release();
+    AllocateNewContext();
+    return GetExecutionContext();
+}
+
+void VKScheduler::SubmitExecution(vk::Semaphore semaphore) {
+    const auto& dld = device.GetDispatchLoader();
+    current_cmdbuf.end(dld);
+
+    const auto queue = device.GetGraphicsQueue();
+    const vk::SubmitInfo submit_info(0, nullptr, nullptr, 1, &current_cmdbuf, semaphore ? 1u : 0u,
+                                     &semaphore);
+    queue.submit({submit_info}, *current_fence, dld);
+}
+
+void VKScheduler::AllocateNewContext() {
+    current_fence = next_fence;
+    current_cmdbuf = resource_manager.CommitCommandBuffer(*current_fence);
+    next_fence = &resource_manager.CommitFence();
+
+    const auto& dld = device.GetDispatchLoader();
+    current_cmdbuf.begin({vk::CommandBufferUsageFlagBits::eOneTimeSubmit}, dld);
+}
+
+} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h
new file mode 100644
index 000000000..cfaf5376f
--- /dev/null
+++ b/src/video_core/renderer_vulkan/vk_scheduler.h
@@ -0,0 +1,69 @@
+// Copyright 2019 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_types.h"
+#include "video_core/renderer_vulkan/declarations.h"
+
+namespace Vulkan {
+
+class VKDevice;
+class VKExecutionContext;
+class VKFence;
+class VKResourceManager;
+
+/// The scheduler abstracts command buffer and fence management with an interface that's able to do
+/// OpenGL-like operations on Vulkan command buffers.
+class VKScheduler {
+public:
+    explicit VKScheduler(const VKDevice& device, VKResourceManager& resource_manager);
+    ~VKScheduler();
+
+    /// Gets the current execution context.
+    [[nodiscard]] VKExecutionContext GetExecutionContext() const;
+
+    /// Sends the current execution context to the GPU. It invalidates the current execution context
+    /// and returns a new one.
+    VKExecutionContext Flush(vk::Semaphore semaphore = nullptr);
+
+    /// Sends the current execution context to the GPU and waits for it to complete. It invalidates
+    /// the current execution context and returns a new one.
+    VKExecutionContext Finish(vk::Semaphore semaphore = nullptr);
+
+private:
+    void SubmitExecution(vk::Semaphore semaphore);
+
+    void AllocateNewContext();
+
+    const VKDevice& device;
+    VKResourceManager& resource_manager;
+    vk::CommandBuffer current_cmdbuf;
+    VKFence* current_fence = nullptr;
+    VKFence* next_fence = nullptr;
+};
+
+class VKExecutionContext {
+    friend class VKScheduler;
+
+public:
+    VKExecutionContext() = default;
+
+    VKFence& GetFence() const {
+        return *fence;
+    }
+
+    vk::CommandBuffer GetCommandBuffer() const {
+        return cmdbuf;
+    }
+
+private:
+    explicit VKExecutionContext(VKFence* fence, vk::CommandBuffer cmdbuf)
+        : fence{fence}, cmdbuf{cmdbuf} {}
+
+    VKFence* fence{};
+    vk::CommandBuffer cmdbuf;
+};
+
+} // namespace Vulkan