summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar ReinUsesLisp2019-06-16 20:00:48 -0300
committerGravatar ReinUsesLisp2019-07-06 00:37:55 -0300
commit32c0212b24bc933607b84eb8ab1d55e07db30afe (patch)
treed4b73e903254ea740fe3cacf50618aff12c82d11 /src
parentgl_buffer_cache: Remove global system getters (diff)
downloadyuzu-32c0212b24bc933607b84eb8ab1d55e07db30afe.tar.gz
yuzu-32c0212b24bc933607b84eb8ab1d55e07db30afe.tar.xz
yuzu-32c0212b24bc933607b84eb8ab1d55e07db30afe.zip
buffer_cache: Implement a generic buffer cache
Implements a templated class with a similar approach to our current generic texture cache. It is designed to be compatible with Vulkan and OpenGL,
Diffstat (limited to 'src')
-rw-r--r--src/video_core/CMakeLists.txt1
-rw-r--r--src/video_core/buffer_cache.h300
2 files changed, 301 insertions, 0 deletions
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 7aefd4035..8753383b8 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -1,4 +1,5 @@
1add_library(video_core STATIC 1add_library(video_core STATIC
2 buffer_cache.h
2 dma_pusher.cpp 3 dma_pusher.cpp
3 dma_pusher.h 4 dma_pusher.h
4 debug_utils/debug_utils.cpp 5 debug_utils/debug_utils.cpp
diff --git a/src/video_core/buffer_cache.h b/src/video_core/buffer_cache.h
new file mode 100644
index 000000000..eb0ec45c2
--- /dev/null
+++ b/src/video_core/buffer_cache.h
@@ -0,0 +1,300 @@
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 <array>
8#include <memory>
9#include <mutex>
10#include <unordered_map>
11#include <unordered_set>
12#include <utility>
13#include <vector>
14
15#include "common/alignment.h"
16#include "common/common_types.h"
17#include "core/core.h"
18#include "video_core/memory_manager.h"
19#include "video_core/rasterizer_cache.h"
20
21namespace VideoCore {
22class RasterizerInterface;
23}
24
25namespace VideoCommon {
26
27template <typename BufferStorageType>
28class CachedBuffer final : public RasterizerCacheObject {
29public:
30 explicit CachedBuffer(VAddr cpu_addr, u8* host_ptr)
31 : RasterizerCacheObject{host_ptr}, host_ptr{host_ptr}, cpu_addr{cpu_addr} {}
32 ~CachedBuffer() override = default;
33
34 VAddr GetCpuAddr() const override {
35 return cpu_addr;
36 }
37
38 std::size_t GetSizeInBytes() const override {
39 return size;
40 }
41
42 u8* GetWritableHostPtr() const {
43 return host_ptr;
44 }
45
46 std::size_t GetSize() const {
47 return size;
48 }
49
50 std::size_t GetCapacity() const {
51 return capacity;
52 }
53
54 bool IsInternalized() const {
55 return is_internal;
56 }
57
58 const BufferStorageType& GetBuffer() const {
59 return buffer;
60 }
61
62 void SetSize(std::size_t new_size) {
63 size = new_size;
64 }
65
66 void SetInternalState(bool is_internal_) {
67 is_internal = is_internal_;
68 }
69
70 BufferStorageType ExchangeBuffer(BufferStorageType buffer_, std::size_t new_capacity) {
71 capacity = new_capacity;
72 std::swap(buffer, buffer_);
73 return buffer_;
74 }
75
76private:
77 u8* host_ptr{};
78 VAddr cpu_addr{};
79 std::size_t size{};
80 std::size_t capacity{};
81 bool is_internal{};
82 BufferStorageType buffer;
83};
84
85template <typename BufferStorageType, typename BufferType, typename StreamBuffer>
86class BufferCache : public RasterizerCache<std::shared_ptr<CachedBuffer<BufferStorageType>>> {
87public:
88 using Buffer = std::shared_ptr<CachedBuffer<BufferStorageType>>;
89 using BufferInfo = std::pair<const BufferType*, u64>;
90
91 explicit BufferCache(VideoCore::RasterizerInterface& rasterizer, Core::System& system,
92 std::unique_ptr<StreamBuffer> stream_buffer)
93 : RasterizerCache<Buffer>{rasterizer}, system{system},
94 stream_buffer{std::move(stream_buffer)}, stream_buffer_handle{
95 this->stream_buffer->GetHandle()} {}
96 ~BufferCache() = default;
97
98 void Unregister(const Buffer& entry) override {
99 std::lock_guard lock{RasterizerCache<Buffer>::mutex};
100 if (entry->IsInternalized()) {
101 internalized_entries.erase(entry->GetCacheAddr());
102 }
103 ReserveBuffer(entry);
104 RasterizerCache<Buffer>::Unregister(entry);
105 }
106
107 void TickFrame() {
108 marked_for_destruction_index =
109 (marked_for_destruction_index + 1) % marked_for_destruction_ring_buffer.size();
110 MarkedForDestruction().clear();
111 }
112
113 [[nodiscard]] BufferInfo UploadMemory(GPUVAddr gpu_addr, std::size_t size,
114 std::size_t alignment = 4, bool internalize = false,
115 bool is_written = false) {
116 std::lock_guard lock{RasterizerCache<Buffer>::mutex};
117
118 auto& memory_manager = system.GPU().MemoryManager();
119 const auto host_ptr = memory_manager.GetPointer(gpu_addr);
120 if (!host_ptr) {
121 return {GetEmptyBuffer(size), 0};
122 }
123 const auto cache_addr = ToCacheAddr(host_ptr);
124
125 // Cache management is a big overhead, so only cache entries with a given size.
126 // TODO: Figure out which size is the best for given games.
127 constexpr std::size_t max_stream_size = 0x800;
128 if (!internalize && size < max_stream_size &&
129 internalized_entries.find(cache_addr) == internalized_entries.end()) {
130 return StreamBufferUpload(host_ptr, size, alignment);
131 }
132
133 auto entry = RasterizerCache<Buffer>::TryGet(cache_addr);
134 if (!entry) {
135 return FixedBufferUpload(gpu_addr, host_ptr, size, internalize, is_written);
136 }
137
138 if (entry->GetSize() < size) {
139 IncreaseBufferSize(entry, size);
140 }
141 if (is_written) {
142 entry->MarkAsModified(true, *this);
143 }
144 return {ToHandle(entry->GetBuffer()), 0};
145 }
146
147 /// Uploads from a host memory. Returns the OpenGL buffer where it's located and its offset.
148 [[nodiscard]] BufferInfo UploadHostMemory(const void* raw_pointer, std::size_t size,
149 std::size_t alignment = 4) {
150 std::lock_guard lock{RasterizerCache<Buffer>::mutex};
151 return StreamBufferUpload(raw_pointer, size, alignment);
152 }
153
154 void Map(std::size_t max_size) {
155 std::tie(buffer_ptr, buffer_offset_base, invalidated) = stream_buffer->Map(max_size, 4);
156 buffer_offset = buffer_offset_base;
157 }
158
159 /// Finishes the upload stream, returns true on bindings invalidation.
160 bool Unmap() {
161 stream_buffer->Unmap(buffer_offset - buffer_offset_base);
162 return std::exchange(invalidated, false);
163 }
164
165protected:
166 void FlushObjectInner(const Buffer& entry) override {
167 DownloadBufferData(entry->GetBuffer(), 0, entry->GetSize(), entry->GetWritableHostPtr());
168 }
169
170 virtual BufferStorageType CreateBuffer(std::size_t size) = 0;
171
172 virtual const BufferType* ToHandle(const BufferStorageType& storage) = 0;
173
174 virtual const BufferType* GetEmptyBuffer(std::size_t size) = 0;
175
176 virtual void UploadBufferData(const BufferStorageType& buffer, std::size_t offset,
177 std::size_t size, const u8* data) = 0;
178
179 virtual void DownloadBufferData(const BufferStorageType& buffer, std::size_t offset,
180 std::size_t size, u8* data) = 0;
181
182 virtual void CopyBufferData(const BufferStorageType& src, const BufferStorageType& dst,
183 std::size_t src_offset, std::size_t dst_offset,
184 std::size_t size) = 0;
185
186private:
187 BufferInfo StreamBufferUpload(const void* raw_pointer, std::size_t size,
188 std::size_t alignment) {
189 AlignBuffer(alignment);
190 const std::size_t uploaded_offset = buffer_offset;
191 std::memcpy(buffer_ptr, raw_pointer, size);
192
193 buffer_ptr += size;
194 buffer_offset += size;
195 return {&stream_buffer_handle, uploaded_offset};
196 }
197
198 BufferInfo FixedBufferUpload(GPUVAddr gpu_addr, u8* host_ptr, std::size_t size,
199 bool internalize, bool is_written) {
200 auto& memory_manager = Core::System::GetInstance().GPU().MemoryManager();
201 const auto cpu_addr = memory_manager.GpuToCpuAddress(gpu_addr);
202 ASSERT(cpu_addr);
203
204 auto entry = GetUncachedBuffer(*cpu_addr, host_ptr);
205 entry->SetSize(size);
206 entry->SetInternalState(internalize);
207 RasterizerCache<Buffer>::Register(entry);
208
209 if (internalize) {
210 internalized_entries.emplace(ToCacheAddr(host_ptr));
211 }
212 if (is_written) {
213 entry->MarkAsModified(true, *this);
214 }
215
216 if (entry->GetCapacity() < size) {
217 MarkedForDestruction().push_back(entry->ExchangeBuffer(CreateBuffer(size), size));
218 }
219
220 UploadBufferData(entry->GetBuffer(), 0, size, host_ptr);
221 return {ToHandle(entry->GetBuffer()), 0};
222 }
223
224 void IncreaseBufferSize(Buffer& entry, std::size_t new_size) {
225 const std::size_t old_size = entry->GetSize();
226 if (entry->GetCapacity() < new_size) {
227 const auto& old_buffer = entry->GetBuffer();
228 auto new_buffer = CreateBuffer(new_size);
229
230 // Copy bits from the old buffer to the new buffer.
231 CopyBufferData(old_buffer, new_buffer, 0, 0, old_size);
232 MarkedForDestruction().push_back(
233 entry->ExchangeBuffer(std::move(new_buffer), new_size));
234
235 // This buffer could have been used
236 invalidated = true;
237 }
238 // Upload the new bits.
239 const std::size_t size_diff = new_size - old_size;
240 UploadBufferData(entry->GetBuffer(), old_size, size_diff, entry->GetHostPtr() + old_size);
241
242 // Update entry's size in the object and in the cache.
243 Unregister(entry);
244
245 entry->SetSize(new_size);
246 RasterizerCache<Buffer>::Register(entry);
247 }
248
249 Buffer GetUncachedBuffer(VAddr cpu_addr, u8* host_ptr) {
250 if (auto entry = TryGetReservedBuffer(host_ptr)) {
251 return entry;
252 }
253 return std::make_shared<Buffer::element_type>(cpu_addr, host_ptr);
254 }
255
256 Buffer TryGetReservedBuffer(u8* host_ptr) {
257 const auto it = buffer_reserve.find(ToCacheAddr(host_ptr));
258 if (it == buffer_reserve.end()) {
259 return {};
260 }
261 auto& reserve = it->second;
262 auto entry = reserve.back();
263 reserve.pop_back();
264 return entry;
265 }
266
267 void ReserveBuffer(Buffer entry) {
268 buffer_reserve[entry->GetCacheAddr()].push_back(std::move(entry));
269 }
270
271 void AlignBuffer(std::size_t alignment) {
272 // Align the offset, not the mapped pointer
273 const std::size_t offset_aligned = Common::AlignUp(buffer_offset, alignment);
274 buffer_ptr += offset_aligned - buffer_offset;
275 buffer_offset = offset_aligned;
276 }
277
278 std::vector<BufferStorageType>& MarkedForDestruction() {
279 return marked_for_destruction_ring_buffer[marked_for_destruction_index];
280 }
281
282 Core::System& system;
283
284 std::unique_ptr<StreamBuffer> stream_buffer;
285 BufferType stream_buffer_handle{};
286
287 bool invalidated = false;
288
289 u8* buffer_ptr = nullptr;
290 u64 buffer_offset = 0;
291 u64 buffer_offset_base = 0;
292
293 std::size_t marked_for_destruction_index = 0;
294 std::array<std::vector<BufferStorageType>, 4> marked_for_destruction_ring_buffer;
295
296 std::unordered_set<CacheAddr> internalized_entries;
297 std::unordered_map<CacheAddr, std::vector<Buffer>> buffer_reserve;
298};
299
300} // namespace VideoCommon