summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/video_core/renderer_vulkan/vk_stream_buffer.cpp168
-rw-r--r--src/video_core/renderer_vulkan/vk_stream_buffer.h76
2 files changed, 0 insertions, 244 deletions
diff --git a/src/video_core/renderer_vulkan/vk_stream_buffer.cpp b/src/video_core/renderer_vulkan/vk_stream_buffer.cpp
deleted file mode 100644
index 7b4875d0e..000000000
--- a/src/video_core/renderer_vulkan/vk_stream_buffer.cpp
+++ /dev/null
@@ -1,168 +0,0 @@
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 <limits>
7#include <optional>
8#include <tuple>
9#include <vector>
10
11#include "common/alignment.h"
12#include "common/assert.h"
13#include "common/literals.h"
14#include "video_core/renderer_vulkan/vk_scheduler.h"
15#include "video_core/renderer_vulkan/vk_stream_buffer.h"
16#include "video_core/vulkan_common/vulkan_device.h"
17#include "video_core/vulkan_common/vulkan_wrapper.h"
18
19namespace Vulkan {
20
21namespace {
22
23using namespace Common::Literals;
24
25constexpr VkBufferUsageFlags BUFFER_USAGE =
26 VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT |
27 VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
28
29constexpr u64 WATCHES_INITIAL_RESERVE = 0x4000;
30constexpr u64 WATCHES_RESERVE_CHUNK = 0x1000;
31
32constexpr u64 PREFERRED_STREAM_BUFFER_SIZE = 256_MiB;
33
34/// Find a memory type with the passed requirements
35std::optional<u32> FindMemoryType(const VkPhysicalDeviceMemoryProperties& properties,
36 VkMemoryPropertyFlags wanted,
37 u32 filter = std::numeric_limits<u32>::max()) {
38 for (u32 i = 0; i < properties.memoryTypeCount; ++i) {
39 const auto flags = properties.memoryTypes[i].propertyFlags;
40 if ((flags & wanted) == wanted && (filter & (1U << i)) != 0) {
41 return i;
42 }
43 }
44 return std::nullopt;
45}
46
47/// Get the preferred host visible memory type.
48u32 GetMemoryType(const VkPhysicalDeviceMemoryProperties& properties,
49 u32 filter = std::numeric_limits<u32>::max()) {
50 // Prefer device local host visible allocations. Both AMD and Nvidia now provide one.
51 // Otherwise search for a host visible allocation.
52 static constexpr auto HOST_MEMORY =
53 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
54 static constexpr auto DYNAMIC_MEMORY = HOST_MEMORY | VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
55
56 std::optional preferred_type = FindMemoryType(properties, DYNAMIC_MEMORY);
57 if (!preferred_type) {
58 preferred_type = FindMemoryType(properties, HOST_MEMORY);
59 ASSERT_MSG(preferred_type, "No host visible and coherent memory type found");
60 }
61 return preferred_type.value_or(0);
62}
63
64} // Anonymous namespace
65
66VKStreamBuffer::VKStreamBuffer(const Device& device_, VKScheduler& scheduler_)
67 : device{device_}, scheduler{scheduler_} {
68 CreateBuffers();
69 ReserveWatches(current_watches, WATCHES_INITIAL_RESERVE);
70 ReserveWatches(previous_watches, WATCHES_INITIAL_RESERVE);
71}
72
73VKStreamBuffer::~VKStreamBuffer() = default;
74
75std::pair<u8*, u64> VKStreamBuffer::Map(u64 size, u64 alignment) {
76 ASSERT(size <= stream_buffer_size);
77 mapped_size = size;
78
79 if (alignment > 0) {
80 offset = Common::AlignUp(offset, alignment);
81 }
82
83 WaitPendingOperations(offset);
84
85 if (offset + size > stream_buffer_size) {
86 // The buffer would overflow, save the amount of used watches and reset the state.
87 invalidation_mark = current_watch_cursor;
88 current_watch_cursor = 0;
89 offset = 0;
90
91 // Swap watches and reset waiting cursors.
92 std::swap(previous_watches, current_watches);
93 wait_cursor = 0;
94 wait_bound = 0;
95
96 // Ensure that we don't wait for uncommitted fences.
97 scheduler.Flush();
98 }
99
100 return std::make_pair(memory.Map(offset, size), offset);
101}
102
103void VKStreamBuffer::Unmap(u64 size) {
104 ASSERT_MSG(size <= mapped_size, "Reserved size is too small");
105
106 memory.Unmap();
107
108 offset += size;
109
110 if (current_watch_cursor + 1 >= current_watches.size()) {
111 // Ensure that there are enough watches.
112 ReserveWatches(current_watches, WATCHES_RESERVE_CHUNK);
113 }
114 auto& watch = current_watches[current_watch_cursor++];
115 watch.upper_bound = offset;
116 watch.tick = scheduler.CurrentTick();
117}
118
119void VKStreamBuffer::CreateBuffers() {
120 const auto memory_properties = device.GetPhysical().GetMemoryProperties();
121 const u32 preferred_type = GetMemoryType(memory_properties);
122 const u32 preferred_heap = memory_properties.memoryTypes[preferred_type].heapIndex;
123
124 // Substract from the preferred heap size some bytes to avoid getting out of memory.
125 const VkDeviceSize heap_size = memory_properties.memoryHeaps[preferred_heap].size;
126 // As per DXVK's example, using `heap_size / 2`
127 const VkDeviceSize allocable_size = heap_size / 2;
128 buffer = device.GetLogical().CreateBuffer({
129 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
130 .pNext = nullptr,
131 .flags = 0,
132 .size = std::min(PREFERRED_STREAM_BUFFER_SIZE, allocable_size),
133 .usage = BUFFER_USAGE,
134 .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
135 .queueFamilyIndexCount = 0,
136 .pQueueFamilyIndices = nullptr,
137 });
138
139 const auto requirements = device.GetLogical().GetBufferMemoryRequirements(*buffer);
140 const u32 required_flags = requirements.memoryTypeBits;
141 stream_buffer_size = static_cast<u64>(requirements.size);
142
143 memory = device.GetLogical().AllocateMemory({
144 .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
145 .pNext = nullptr,
146 .allocationSize = requirements.size,
147 .memoryTypeIndex = GetMemoryType(memory_properties, required_flags),
148 });
149 buffer.BindMemory(*memory, 0);
150}
151
152void VKStreamBuffer::ReserveWatches(std::vector<Watch>& watches, std::size_t grow_size) {
153 watches.resize(watches.size() + grow_size);
154}
155
156void VKStreamBuffer::WaitPendingOperations(u64 requested_upper_bound) {
157 if (!invalidation_mark) {
158 return;
159 }
160 while (requested_upper_bound < wait_bound && wait_cursor < *invalidation_mark) {
161 auto& watch = previous_watches[wait_cursor];
162 wait_bound = watch.upper_bound;
163 scheduler.Wait(watch.tick);
164 ++wait_cursor;
165 }
166}
167
168} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_stream_buffer.h b/src/video_core/renderer_vulkan/vk_stream_buffer.h
deleted file mode 100644
index 2e9c8cb46..000000000
--- a/src/video_core/renderer_vulkan/vk_stream_buffer.h
+++ /dev/null
@@ -1,76 +0,0 @@
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 <optional>
8#include <utility>
9#include <vector>
10
11#include "common/common_types.h"
12#include "video_core/vulkan_common/vulkan_wrapper.h"
13
14namespace Vulkan {
15
16class Device;
17class VKFenceWatch;
18class VKScheduler;
19
20class VKStreamBuffer final {
21public:
22 explicit VKStreamBuffer(const Device& device, VKScheduler& scheduler);
23 ~VKStreamBuffer();
24
25 /**
26 * Reserves a region of memory from the stream buffer.
27 * @param size Size to reserve.
28 * @returns A pair of a raw memory pointer (with offset added), and the buffer offset
29 */
30 std::pair<u8*, u64> Map(u64 size, u64 alignment);
31
32 /// Ensures that "size" bytes of memory are available to the GPU, potentially recording a copy.
33 void Unmap(u64 size);
34
35 VkBuffer Handle() const noexcept {
36 return *buffer;
37 }
38
39 u64 Address() const noexcept {
40 return 0;
41 }
42
43private:
44 struct Watch {
45 u64 tick{};
46 u64 upper_bound{};
47 };
48
49 /// Creates Vulkan buffer handles committing the required the required memory.
50 void CreateBuffers();
51
52 /// Increases the amount of watches available.
53 void ReserveWatches(std::vector<Watch>& watches, std::size_t grow_size);
54
55 void WaitPendingOperations(u64 requested_upper_bound);
56
57 const Device& device; ///< Vulkan device manager.
58 VKScheduler& scheduler; ///< Command scheduler.
59
60 vk::Buffer buffer; ///< Mapped buffer.
61 vk::DeviceMemory memory; ///< Memory allocation.
62 u64 stream_buffer_size{}; ///< Stream buffer size.
63
64 u64 offset{}; ///< Buffer iterator.
65 u64 mapped_size{}; ///< Size reserved for the current copy.
66
67 std::vector<Watch> current_watches; ///< Watches recorded in the current iteration.
68 std::size_t current_watch_cursor{}; ///< Count of watches, reset on invalidation.
69 std::optional<std::size_t> invalidation_mark; ///< Number of watches used in the previous cycle.
70
71 std::vector<Watch> previous_watches; ///< Watches used in the previous iteration.
72 std::size_t wait_cursor{}; ///< Last watch being waited for completion.
73 u64 wait_bound{}; ///< Highest offset being watched for completion.
74};
75
76} // namespace Vulkan