summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/video_core/CMakeLists.txt4
-rw-r--r--src/video_core/renderer_vulkan/vk_stream_buffer.cpp124
-rw-r--r--src/video_core/renderer_vulkan/vk_stream_buffer.h73
3 files changed, 200 insertions, 1 deletions
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 6036d6ed3..60529323e 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -111,7 +111,9 @@ if (ENABLE_VULKAN)
111 renderer_vulkan/vk_resource_manager.cpp 111 renderer_vulkan/vk_resource_manager.cpp
112 renderer_vulkan/vk_resource_manager.h 112 renderer_vulkan/vk_resource_manager.h
113 renderer_vulkan/vk_scheduler.cpp 113 renderer_vulkan/vk_scheduler.cpp
114 renderer_vulkan/vk_scheduler.h) 114 renderer_vulkan/vk_scheduler.h
115 renderer_vulkan/vk_stream_buffer.cpp
116 renderer_vulkan/vk_stream_buffer.h)
115 117
116 target_include_directories(video_core PRIVATE ../../externals/Vulkan-Headers/include) 118 target_include_directories(video_core PRIVATE ../../externals/Vulkan-Headers/include)
117 target_compile_definitions(video_core PRIVATE HAS_VULKAN) 119 target_compile_definitions(video_core PRIVATE HAS_VULKAN)
diff --git a/src/video_core/renderer_vulkan/vk_stream_buffer.cpp b/src/video_core/renderer_vulkan/vk_stream_buffer.cpp
new file mode 100644
index 000000000..1c5aefaec
--- /dev/null
+++ b/src/video_core/renderer_vulkan/vk_stream_buffer.cpp
@@ -0,0 +1,124 @@
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 <memory>
7#include <optional>
8#include <vector>
9
10#include "common/assert.h"
11#include "video_core/renderer_vulkan/declarations.h"
12#include "video_core/renderer_vulkan/vk_device.h"
13#include "video_core/renderer_vulkan/vk_memory_manager.h"
14#include "video_core/renderer_vulkan/vk_resource_manager.h"
15#include "video_core/renderer_vulkan/vk_scheduler.h"
16#include "video_core/renderer_vulkan/vk_stream_buffer.h"
17
18namespace Vulkan {
19
20constexpr u64 WATCHES_INITIAL_RESERVE = 0x4000;
21constexpr u64 WATCHES_RESERVE_CHUNK = 0x1000;
22
23VKStreamBuffer::VKStreamBuffer(const VKDevice& device, VKMemoryManager& memory_manager,
24 VKScheduler& scheduler, u64 size, vk::BufferUsageFlags usage,
25 vk::AccessFlags access, vk::PipelineStageFlags pipeline_stage)
26 : device{device}, scheduler{scheduler},
27 has_device_exclusive_memory{!memory_manager.IsMemoryUnified()},
28 buffer_size{size}, access{access}, pipeline_stage{pipeline_stage} {
29 CreateBuffers(memory_manager, usage);
30 ReserveWatches(WATCHES_INITIAL_RESERVE);
31}
32
33VKStreamBuffer::~VKStreamBuffer() = default;
34
35std::tuple<u8*, u64, vk::Buffer, bool> VKStreamBuffer::Reserve(u64 size, bool keep_in_host) {
36 ASSERT(size <= buffer_size);
37 mapped_size = size;
38
39 if (offset + size > buffer_size) {
40 // The buffer would overflow, save the amount of used buffers, signal an invalidation and
41 // reset the state.
42 invalidation_mark = used_watches;
43 used_watches = 0;
44 offset = 0;
45 }
46
47 use_device = has_device_exclusive_memory && !keep_in_host;
48
49 const vk::Buffer buffer = use_device ? *device_buffer : *mappable_buffer;
50 return {mapped_pointer + offset, offset, buffer, invalidation_mark.has_value()};
51}
52
53VKExecutionContext VKStreamBuffer::Send(VKExecutionContext exctx, u64 size) {
54 ASSERT_MSG(size <= mapped_size, "Reserved size is too small");
55
56 if (invalidation_mark) {
57 // TODO(Rodrigo): Find a better way to invalidate than waiting for all watches to finish.
58 exctx = scheduler.Flush();
59 std::for_each(watches.begin(), watches.begin() + *invalidation_mark,
60 [&](auto& resource) { resource->Wait(); });
61 invalidation_mark = std::nullopt;
62 }
63
64 // Only copy to VRAM when requested.
65 if (use_device) {
66 const auto& dld = device.GetDispatchLoader();
67 const u32 graphics_family = device.GetGraphicsFamily();
68 const auto cmdbuf = exctx.GetCommandBuffer();
69
70 // Buffers are mirrored, that's why the copy is done with the same offset on both buffers.
71 const vk::BufferCopy copy_region(offset, offset, size);
72 cmdbuf.copyBuffer(*mappable_buffer, *device_buffer, {copy_region}, dld);
73
74 // Protect the buffer from GPU usage until the copy has finished.
75 const vk::BufferMemoryBarrier barrier(vk::AccessFlagBits::eTransferWrite, access,
76 graphics_family, graphics_family, *device_buffer,
77 offset, size);
78 cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, pipeline_stage, {}, {},
79 {barrier}, {}, dld);
80 }
81
82 if (used_watches + 1 >= watches.size()) {
83 // Ensure that there are enough watches.
84 ReserveWatches(WATCHES_RESERVE_CHUNK);
85 }
86 // Add a watch for this allocation.
87 watches[used_watches++]->Watch(exctx.GetFence());
88
89 offset += size;
90
91 return exctx;
92}
93
94void VKStreamBuffer::CreateBuffers(VKMemoryManager& memory_manager, vk::BufferUsageFlags usage) {
95 vk::BufferUsageFlags mappable_usage = usage;
96 if (has_device_exclusive_memory) {
97 mappable_usage |= vk::BufferUsageFlagBits::eTransferSrc;
98 }
99 const vk::BufferCreateInfo buffer_ci({}, buffer_size, mappable_usage,
100 vk::SharingMode::eExclusive, 0, nullptr);
101
102 const auto dev = device.GetLogical();
103 const auto& dld = device.GetDispatchLoader();
104 mappable_buffer = dev.createBufferUnique(buffer_ci, nullptr, dld);
105 mappable_commit = memory_manager.Commit(*mappable_buffer, true);
106 mapped_pointer = mappable_commit->GetData();
107
108 if (has_device_exclusive_memory) {
109 const vk::BufferCreateInfo buffer_ci({}, buffer_size,
110 usage | vk::BufferUsageFlagBits::eTransferDst,
111 vk::SharingMode::eExclusive, 0, nullptr);
112 device_buffer = dev.createBufferUnique(buffer_ci, nullptr, dld);
113 device_commit = memory_manager.Commit(*device_buffer, false);
114 }
115}
116
117void VKStreamBuffer::ReserveWatches(std::size_t grow_size) {
118 const std::size_t previous_size = watches.size();
119 watches.resize(previous_size + grow_size);
120 std::generate(watches.begin() + previous_size, watches.end(),
121 []() { return std::make_unique<VKFenceWatch>(); });
122}
123
124} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_stream_buffer.h b/src/video_core/renderer_vulkan/vk_stream_buffer.h
new file mode 100644
index 000000000..8c00d383a
--- /dev/null
+++ b/src/video_core/renderer_vulkan/vk_stream_buffer.h
@@ -0,0 +1,73 @@
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 <memory>
8#include <optional>
9#include <tuple>
10#include <vector>
11
12#include "common/common_types.h"
13#include "video_core/renderer_vulkan/declarations.h"
14#include "video_core/renderer_vulkan/vk_memory_manager.h"
15
16namespace Vulkan {
17
18class VKDevice;
19class VKFence;
20class VKFenceWatch;
21class VKResourceManager;
22class VKScheduler;
23
24class VKStreamBuffer {
25public:
26 explicit VKStreamBuffer(const VKDevice& device, VKMemoryManager& memory_manager,
27 VKScheduler& scheduler, u64 size, vk::BufferUsageFlags usage,
28 vk::AccessFlags access, vk::PipelineStageFlags pipeline_stage);
29 ~VKStreamBuffer();
30
31 /**
32 * Reserves a region of memory from the stream buffer.
33 * @param size Size to reserve.
34 * @param keep_in_host Mapped buffer will be in host memory, skipping the copy to device local.
35 * @returns A tuple in the following order: Raw memory pointer (with offset added), buffer
36 * offset, Vulkan buffer handle, buffer has been invalited.
37 */
38 std::tuple<u8*, u64, vk::Buffer, bool> Reserve(u64 size, bool keep_in_host);
39
40 /// Ensures that "size" bytes of memory are available to the GPU, potentially recording a copy.
41 [[nodiscard]] VKExecutionContext Send(VKExecutionContext exctx, u64 size);
42
43private:
44 /// Creates Vulkan buffer handles committing the required the required memory.
45 void CreateBuffers(VKMemoryManager& memory_manager, vk::BufferUsageFlags usage);
46
47 /// Increases the amount of watches available.
48 void ReserveWatches(std::size_t grow_size);
49
50 const VKDevice& device; ///< Vulkan device manager.
51 VKScheduler& scheduler; ///< Command scheduler.
52 const u64 buffer_size; ///< Total size of the stream buffer.
53 const bool has_device_exclusive_memory; ///< True if the streaming buffer will use VRAM.
54 const vk::AccessFlags access; ///< Access usage of this stream buffer.
55 const vk::PipelineStageFlags pipeline_stage; ///< Pipeline usage of this stream buffer.
56
57 UniqueBuffer mappable_buffer; ///< Mapped buffer.
58 UniqueBuffer device_buffer; ///< Buffer exclusive to the GPU.
59 VKMemoryCommit mappable_commit; ///< Commit visible from the CPU.
60 VKMemoryCommit device_commit; ///< Commit stored in VRAM.
61 u8* mapped_pointer{}; ///< Pointer to the host visible commit
62
63 u64 offset{}; ///< Buffer iterator.
64 u64 mapped_size{}; ///< Size reserved for the current copy.
65 bool use_device{}; ///< True if the current uses VRAM.
66
67 std::vector<std::unique_ptr<VKFenceWatch>> watches; ///< Total watches
68 std::size_t used_watches{}; ///< Count of watches, reset on invalidation.
69 std::optional<std::size_t>
70 invalidation_mark{}; ///< Number of watches used in the current invalidation.
71};
72
73} // namespace Vulkan