summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/video_core/CMakeLists.txt2
-rw-r--r--src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp127
-rw-r--r--src/video_core/renderer_vulkan/vk_staging_buffer_pool.h83
3 files changed, 212 insertions, 0 deletions
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index e615b238e..fcedad3fa 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -169,6 +169,8 @@ if (ENABLE_VULKAN)
renderer_vulkan/vk_scheduler.h
renderer_vulkan/vk_shader_decompiler.cpp
renderer_vulkan/vk_shader_decompiler.h
+ renderer_vulkan/vk_staging_buffer_pool.cpp
+ renderer_vulkan/vk_staging_buffer_pool.h
renderer_vulkan/vk_stream_buffer.cpp
renderer_vulkan/vk_stream_buffer.h
renderer_vulkan/vk_swapchain.cpp
diff --git a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp
new file mode 100644
index 000000000..171d78afc
--- /dev/null
+++ b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp
@@ -0,0 +1,127 @@
+// Copyright 2019 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <algorithm>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+#include "common/bit_util.h"
+#include "common/common_types.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"
+#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h"
+
+namespace Vulkan {
+
+VKStagingBufferPool::StagingBuffer::StagingBuffer(std::unique_ptr<VKBuffer> buffer, VKFence& fence,
+ u64 last_epoch)
+ : buffer{std::move(buffer)}, watch{fence}, last_epoch{last_epoch} {}
+
+VKStagingBufferPool::StagingBuffer::StagingBuffer(StagingBuffer&& rhs) noexcept {
+ buffer = std::move(rhs.buffer);
+ watch = std::move(rhs.watch);
+ last_epoch = rhs.last_epoch;
+}
+
+VKStagingBufferPool::StagingBuffer::~StagingBuffer() = default;
+
+VKStagingBufferPool::StagingBuffer& VKStagingBufferPool::StagingBuffer::operator=(
+ StagingBuffer&& rhs) noexcept {
+ buffer = std::move(rhs.buffer);
+ watch = std::move(rhs.watch);
+ last_epoch = rhs.last_epoch;
+ return *this;
+}
+
+VKStagingBufferPool::VKStagingBufferPool(const VKDevice& device, VKMemoryManager& memory_manager,
+ VKScheduler& scheduler)
+ : device{device}, memory_manager{memory_manager}, scheduler{scheduler},
+ is_device_integrated{device.IsIntegrated()} {}
+
+VKStagingBufferPool::~VKStagingBufferPool() = default;
+
+VKBuffer& VKStagingBufferPool::GetUnusedBuffer(std::size_t size, bool host_visible) {
+ if (const auto buffer = TryGetReservedBuffer(size, host_visible)) {
+ return *buffer;
+ }
+ return CreateStagingBuffer(size, host_visible);
+}
+
+void VKStagingBufferPool::TickFrame() {
+ ++epoch;
+ current_delete_level = (current_delete_level + 1) % NumLevels;
+
+ ReleaseCache(true);
+ if (!is_device_integrated) {
+ ReleaseCache(false);
+ }
+}
+
+VKBuffer* VKStagingBufferPool::TryGetReservedBuffer(std::size_t size, bool host_visible) {
+ for (auto& entry : GetCache(host_visible)[Common::Log2Ceil64(size)].entries) {
+ if (entry.watch.TryWatch(scheduler.GetFence())) {
+ entry.last_epoch = epoch;
+ return &*entry.buffer;
+ }
+ }
+ return nullptr;
+}
+
+VKBuffer& VKStagingBufferPool::CreateStagingBuffer(std::size_t size, bool host_visible) {
+ const auto usage =
+ vk::BufferUsageFlagBits::eTransferSrc | vk::BufferUsageFlagBits::eTransferDst |
+ vk::BufferUsageFlagBits::eStorageBuffer | vk::BufferUsageFlagBits::eIndexBuffer;
+ const u32 log2 = Common::Log2Ceil64(size);
+ const vk::BufferCreateInfo buffer_ci({}, 1ULL << log2, usage, vk::SharingMode::eExclusive, 0,
+ nullptr);
+ const auto dev = device.GetLogical();
+ auto buffer = std::make_unique<VKBuffer>();
+ buffer->handle = dev.createBufferUnique(buffer_ci, nullptr, device.GetDispatchLoader());
+ buffer->commit = memory_manager.Commit(*buffer->handle, host_visible);
+
+ auto& entries = GetCache(host_visible)[log2].entries;
+ return *entries.emplace_back(std::move(buffer), scheduler.GetFence(), epoch).buffer;
+}
+
+VKStagingBufferPool::StagingBuffersCache& VKStagingBufferPool::GetCache(bool host_visible) {
+ return is_device_integrated || host_visible ? host_staging_buffers : device_staging_buffers;
+}
+
+void VKStagingBufferPool::ReleaseCache(bool host_visible) {
+ auto& cache = GetCache(host_visible);
+ const u64 size = ReleaseLevel(cache, current_delete_level);
+ if (size == 0) {
+ return;
+ }
+}
+
+u64 VKStagingBufferPool::ReleaseLevel(StagingBuffersCache& cache, std::size_t log2) {
+ static constexpr u64 epochs_to_destroy = 180;
+ static constexpr std::size_t deletions_per_tick = 16;
+
+ auto& staging = cache[log2];
+ auto& entries = staging.entries;
+ const std::size_t old_size = entries.size();
+
+ const auto is_deleteable = [this](const auto& entry) {
+ return entry.last_epoch + epochs_to_destroy < epoch && !entry.watch.IsUsed();
+ };
+ const std::size_t begin_offset = staging.delete_index;
+ const std::size_t end_offset = std::min(begin_offset + deletions_per_tick, old_size);
+ const auto begin = std::begin(entries) + begin_offset;
+ const auto end = std::begin(entries) + end_offset;
+ entries.erase(std::remove_if(begin, end, is_deleteable), end);
+
+ const std::size_t new_size = entries.size();
+ staging.delete_index += deletions_per_tick;
+ if (staging.delete_index >= new_size) {
+ staging.delete_index = 0;
+ }
+
+ return (1ULL << log2) * (old_size - new_size);
+}
+
+} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h
new file mode 100644
index 000000000..4530749f0
--- /dev/null
+++ b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h
@@ -0,0 +1,83 @@
+// Copyright 2019 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <climits>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+#include "common/common_types.h"
+
+#include "video_core/renderer_vulkan/declarations.h"
+#include "video_core/renderer_vulkan/vk_memory_manager.h"
+
+namespace Vulkan {
+
+class VKDevice;
+class VKFenceWatch;
+class VKScheduler;
+
+struct VKBuffer final {
+ UniqueBuffer handle;
+ VKMemoryCommit commit;
+};
+
+class VKStagingBufferPool final {
+public:
+ explicit VKStagingBufferPool(const VKDevice& device, VKMemoryManager& memory_manager,
+ VKScheduler& scheduler);
+ ~VKStagingBufferPool();
+
+ VKBuffer& GetUnusedBuffer(std::size_t size, bool host_visible);
+
+ void TickFrame();
+
+private:
+ struct StagingBuffer final {
+ explicit StagingBuffer(std::unique_ptr<VKBuffer> buffer, VKFence& fence, u64 last_epoch);
+ StagingBuffer(StagingBuffer&& rhs) noexcept;
+ StagingBuffer(const StagingBuffer&) = delete;
+ ~StagingBuffer();
+
+ StagingBuffer& operator=(StagingBuffer&& rhs) noexcept;
+
+ std::unique_ptr<VKBuffer> buffer;
+ VKFenceWatch watch;
+ u64 last_epoch;
+ };
+
+ struct StagingBuffers final {
+ std::vector<StagingBuffer> entries;
+ std::size_t delete_index = 0;
+ };
+
+ static constexpr std::size_t NumLevels = sizeof(std::size_t) * CHAR_BIT;
+ using StagingBuffersCache = std::array<StagingBuffers, NumLevels>;
+
+ VKBuffer* TryGetReservedBuffer(std::size_t size, bool host_visible);
+
+ VKBuffer& CreateStagingBuffer(std::size_t size, bool host_visible);
+
+ StagingBuffersCache& GetCache(bool host_visible);
+
+ void ReleaseCache(bool host_visible);
+
+ u64 ReleaseLevel(StagingBuffersCache& cache, std::size_t log2);
+
+ const VKDevice& device;
+ VKMemoryManager& memory_manager;
+ VKScheduler& scheduler;
+ const bool is_device_integrated;
+
+ StagingBuffersCache host_staging_buffers;
+ StagingBuffersCache device_staging_buffers;
+
+ u64 epoch = 0;
+
+ std::size_t current_delete_level = 0;
+};
+
+} // namespace Vulkan