diff options
| author | 2019-12-30 12:25:59 -0400 | |
|---|---|---|
| committer | 2019-12-30 12:25:59 -0400 | |
| commit | 287d5921cf3f159642361b7dcdf1b02355e3cc80 (patch) | |
| tree | 6ab5ad70b616d818464de68ac2498b3d770ef93c /src | |
| parent | Merge pull request #3250 from ReinUsesLisp/empty-fragment (diff) | |
| parent | vk_staging_buffer_pool: Initialize last epoch to zero (diff) | |
| download | yuzu-287d5921cf3f159642361b7dcdf1b02355e3cc80.tar.gz yuzu-287d5921cf3f159642361b7dcdf1b02355e3cc80.tar.xz yuzu-287d5921cf3f159642361b7dcdf1b02355e3cc80.zip | |
Merge pull request #3249 from ReinUsesLisp/vk-staging-buffer-pool
vk_staging_buffer_pool: Add a staging pool for temporary operations
Diffstat (limited to 'src')
| -rw-r--r-- | src/video_core/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp | 127 | ||||
| -rw-r--r-- | src/video_core/renderer_vulkan/vk_staging_buffer_pool.h | 83 |
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 | |||
| 17 | namespace Vulkan { | ||
| 18 | |||
| 19 | VKStagingBufferPool::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 | |||
| 23 | VKStagingBufferPool::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 | |||
| 29 | VKStagingBufferPool::StagingBuffer::~StagingBuffer() = default; | ||
| 30 | |||
| 31 | VKStagingBufferPool::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 | |||
| 39 | VKStagingBufferPool::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 | |||
| 44 | VKStagingBufferPool::~VKStagingBufferPool() = default; | ||
| 45 | |||
| 46 | VKBuffer& 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 | |||
| 53 | void 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 | |||
| 63 | VKBuffer* 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 | |||
| 73 | VKBuffer& 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 | |||
| 89 | VKStagingBufferPool::StagingBuffersCache& VKStagingBufferPool::GetCache(bool host_visible) { | ||
| 90 | return is_device_integrated || host_visible ? host_staging_buffers : device_staging_buffers; | ||
| 91 | } | ||
| 92 | |||
| 93 | void 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 | |||
| 101 | u64 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..02310375f --- /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 | |||
| 17 | namespace Vulkan { | ||
| 18 | |||
| 19 | class VKDevice; | ||
| 20 | class VKFenceWatch; | ||
| 21 | class VKScheduler; | ||
| 22 | |||
| 23 | struct VKBuffer final { | ||
| 24 | UniqueBuffer handle; | ||
| 25 | VKMemoryCommit commit; | ||
| 26 | }; | ||
| 27 | |||
| 28 | class VKStagingBufferPool final { | ||
| 29 | public: | ||
| 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 | |||
| 38 | private: | ||
| 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 = 0; | ||
| 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 | ||