summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar ReinUsesLisp2019-12-25 18:12:17 -0300
committerGravatar ReinUsesLisp2019-12-25 18:12:17 -0300
commit3813af2f3c4536e14950b95b8211788b9a6b243f (patch)
tree41b62899c9412fce205d5d6f059d88ceda3a4db0 /src
parentMerge pull request #3244 from ReinUsesLisp/vk-fps (diff)
downloadyuzu-3813af2f3c4536e14950b95b8211788b9a6b243f.tar.gz
yuzu-3813af2f3c4536e14950b95b8211788b9a6b243f.tar.xz
yuzu-3813af2f3c4536e14950b95b8211788b9a6b243f.zip
vk_staging_buffer_pool: Add a staging pool for temporary operations
The job of this abstraction is to provide staging buffers for temporary operations. Think of image uploads or buffer uploads to device memory. It automatically deletes unused buffers.
Diffstat (limited to 'src')
-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)
169 renderer_vulkan/vk_scheduler.h 169 renderer_vulkan/vk_scheduler.h
170 renderer_vulkan/vk_shader_decompiler.cpp 170 renderer_vulkan/vk_shader_decompiler.cpp
171 renderer_vulkan/vk_shader_decompiler.h 171 renderer_vulkan/vk_shader_decompiler.h
172 renderer_vulkan/vk_staging_buffer_pool.cpp
173 renderer_vulkan/vk_staging_buffer_pool.h
172 renderer_vulkan/vk_stream_buffer.cpp 174 renderer_vulkan/vk_stream_buffer.cpp
173 renderer_vulkan/vk_stream_buffer.h 175 renderer_vulkan/vk_stream_buffer.h
174 renderer_vulkan/vk_swapchain.cpp 176 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 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <unordered_map>
7#include <utility>
8#include <vector>
9
10#include "common/bit_util.h"
11#include "common/common_types.h"
12#include "video_core/renderer_vulkan/vk_device.h"
13#include "video_core/renderer_vulkan/vk_resource_manager.h"
14#include "video_core/renderer_vulkan/vk_scheduler.h"
15#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h"
16
17namespace Vulkan {
18
19VKStagingBufferPool::StagingBuffer::StagingBuffer(std::unique_ptr<VKBuffer> buffer, VKFence& fence,
20 u64 last_epoch)
21 : buffer{std::move(buffer)}, watch{fence}, last_epoch{last_epoch} {}
22
23VKStagingBufferPool::StagingBuffer::StagingBuffer(StagingBuffer&& rhs) noexcept {
24 buffer = std::move(rhs.buffer);
25 watch = std::move(rhs.watch);
26 last_epoch = rhs.last_epoch;
27}
28
29VKStagingBufferPool::StagingBuffer::~StagingBuffer() = default;
30
31VKStagingBufferPool::StagingBuffer& VKStagingBufferPool::StagingBuffer::operator=(
32 StagingBuffer&& rhs) noexcept {
33 buffer = std::move(rhs.buffer);
34 watch = std::move(rhs.watch);
35 last_epoch = rhs.last_epoch;
36 return *this;
37}
38
39VKStagingBufferPool::VKStagingBufferPool(const VKDevice& device, VKMemoryManager& memory_manager,
40 VKScheduler& scheduler)
41 : device{device}, memory_manager{memory_manager}, scheduler{scheduler},
42 is_device_integrated{device.IsIntegrated()} {}
43
44VKStagingBufferPool::~VKStagingBufferPool() = default;
45
46VKBuffer& VKStagingBufferPool::GetUnusedBuffer(std::size_t size, bool host_visible) {
47 if (const auto buffer = TryGetReservedBuffer(size, host_visible)) {
48 return *buffer;
49 }
50 return CreateStagingBuffer(size, host_visible);
51}
52
53void VKStagingBufferPool::TickFrame() {
54 ++epoch;
55 current_delete_level = (current_delete_level + 1) % NumLevels;
56
57 ReleaseCache(true);
58 if (!is_device_integrated) {
59 ReleaseCache(false);
60 }
61}
62
63VKBuffer* VKStagingBufferPool::TryGetReservedBuffer(std::size_t size, bool host_visible) {
64 for (auto& entry : GetCache(host_visible)[Common::Log2Ceil64(size)].entries) {
65 if (entry.watch.TryWatch(scheduler.GetFence())) {
66 entry.last_epoch = epoch;
67 return &*entry.buffer;
68 }
69 }
70 return nullptr;
71}
72
73VKBuffer& VKStagingBufferPool::CreateStagingBuffer(std::size_t size, bool host_visible) {
74 const auto usage =
75 vk::BufferUsageFlagBits::eTransferSrc | vk::BufferUsageFlagBits::eTransferDst |
76 vk::BufferUsageFlagBits::eStorageBuffer | vk::BufferUsageFlagBits::eIndexBuffer;
77 const u32 log2 = Common::Log2Ceil64(size);
78 const vk::BufferCreateInfo buffer_ci({}, 1ULL << log2, usage, vk::SharingMode::eExclusive, 0,
79 nullptr);
80 const auto dev = device.GetLogical();
81 auto buffer = std::make_unique<VKBuffer>();
82 buffer->handle = dev.createBufferUnique(buffer_ci, nullptr, device.GetDispatchLoader());
83 buffer->commit = memory_manager.Commit(*buffer->handle, host_visible);
84
85 auto& entries = GetCache(host_visible)[log2].entries;
86 return *entries.emplace_back(std::move(buffer), scheduler.GetFence(), epoch).buffer;
87}
88
89VKStagingBufferPool::StagingBuffersCache& VKStagingBufferPool::GetCache(bool host_visible) {
90 return is_device_integrated || host_visible ? host_staging_buffers : device_staging_buffers;
91}
92
93void VKStagingBufferPool::ReleaseCache(bool host_visible) {
94 auto& cache = GetCache(host_visible);
95 const u64 size = ReleaseLevel(cache, current_delete_level);
96 if (size == 0) {
97 return;
98 }
99}
100
101u64 VKStagingBufferPool::ReleaseLevel(StagingBuffersCache& cache, std::size_t log2) {
102 static constexpr u64 epochs_to_destroy = 180;
103 static constexpr std::size_t deletions_per_tick = 16;
104
105 auto& staging = cache[log2];
106 auto& entries = staging.entries;
107 const std::size_t old_size = entries.size();
108
109 const auto is_deleteable = [this](const auto& entry) {
110 return entry.last_epoch + epochs_to_destroy < epoch && !entry.watch.IsUsed();
111 };
112 const std::size_t begin_offset = staging.delete_index;
113 const std::size_t end_offset = std::min(begin_offset + deletions_per_tick, old_size);
114 const auto begin = std::begin(entries) + begin_offset;
115 const auto end = std::begin(entries) + end_offset;
116 entries.erase(std::remove_if(begin, end, is_deleteable), end);
117
118 const std::size_t new_size = entries.size();
119 staging.delete_index += deletions_per_tick;
120 if (staging.delete_index >= new_size) {
121 staging.delete_index = 0;
122 }
123
124 return (1ULL << log2) * (old_size - new_size);
125}
126
127} // 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 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <climits>
8#include <unordered_map>
9#include <utility>
10#include <vector>
11
12#include "common/common_types.h"
13
14#include "video_core/renderer_vulkan/declarations.h"
15#include "video_core/renderer_vulkan/vk_memory_manager.h"
16
17namespace Vulkan {
18
19class VKDevice;
20class VKFenceWatch;
21class VKScheduler;
22
23struct VKBuffer final {
24 UniqueBuffer handle;
25 VKMemoryCommit commit;
26};
27
28class VKStagingBufferPool final {
29public:
30 explicit VKStagingBufferPool(const VKDevice& device, VKMemoryManager& memory_manager,
31 VKScheduler& scheduler);
32 ~VKStagingBufferPool();
33
34 VKBuffer& GetUnusedBuffer(std::size_t size, bool host_visible);
35
36 void TickFrame();
37
38private:
39 struct StagingBuffer final {
40 explicit StagingBuffer(std::unique_ptr<VKBuffer> buffer, VKFence& fence, u64 last_epoch);
41 StagingBuffer(StagingBuffer&& rhs) noexcept;
42 StagingBuffer(const StagingBuffer&) = delete;
43 ~StagingBuffer();
44
45 StagingBuffer& operator=(StagingBuffer&& rhs) noexcept;
46
47 std::unique_ptr<VKBuffer> buffer;
48 VKFenceWatch watch;
49 u64 last_epoch;
50 };
51
52 struct StagingBuffers final {
53 std::vector<StagingBuffer> entries;
54 std::size_t delete_index = 0;
55 };
56
57 static constexpr std::size_t NumLevels = sizeof(std::size_t) * CHAR_BIT;
58 using StagingBuffersCache = std::array<StagingBuffers, NumLevels>;
59
60 VKBuffer* TryGetReservedBuffer(std::size_t size, bool host_visible);
61
62 VKBuffer& CreateStagingBuffer(std::size_t size, bool host_visible);
63
64 StagingBuffersCache& GetCache(bool host_visible);
65
66 void ReleaseCache(bool host_visible);
67
68 u64 ReleaseLevel(StagingBuffersCache& cache, std::size_t log2);
69
70 const VKDevice& device;
71 VKMemoryManager& memory_manager;
72 VKScheduler& scheduler;
73 const bool is_device_integrated;
74
75 StagingBuffersCache host_staging_buffers;
76 StagingBuffersCache device_staging_buffers;
77
78 u64 epoch = 0;
79
80 std::size_t current_delete_level = 0;
81};
82
83} // namespace Vulkan