summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Fernando Sahmkow2019-07-19 10:50:40 -0400
committerGravatar FernandoS272019-08-21 12:14:22 -0400
commit862bec001b7ada13ba0e97f95d6ad108ae8a8d0c (patch)
treed366f7768d0acd4cbe1514a57b9fddcfb4c79eaa /src
parentMerge pull request #2748 from FernandoS27/align-memory (diff)
downloadyuzu-862bec001b7ada13ba0e97f95d6ad108ae8a8d0c.tar.gz
yuzu-862bec001b7ada13ba0e97f95d6ad108ae8a8d0c.tar.xz
yuzu-862bec001b7ada13ba0e97f95d6ad108ae8a8d0c.zip
Video_Core: Implement a new Buffer Cache
Diffstat (limited to 'src')
-rw-r--r--src/video_core/CMakeLists.txt4
-rw-r--r--src/video_core/buffer_cache.h299
-rw-r--r--src/video_core/buffer_cache/buffer_block.h78
-rw-r--r--src/video_core/buffer_cache/buffer_cache.h372
-rw-r--r--src/video_core/buffer_cache/map_interval.h48
-rw-r--r--src/video_core/gpu.h4
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.cpp47
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.h33
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp2
9 files changed, 560 insertions, 327 deletions
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 7c18c27b3..e73ecb2fe 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -1,5 +1,7 @@
1add_library(video_core STATIC 1add_library(video_core STATIC
2 buffer_cache.h 2 buffer_cache/buffer_block.h
3 buffer_cache/buffer_cache.h
4 buffer_cache/map_interval.h
3 dma_pusher.cpp 5 dma_pusher.cpp
4 dma_pusher.h 6 dma_pusher.h
5 debug_utils/debug_utils.cpp 7 debug_utils/debug_utils.cpp
diff --git a/src/video_core/buffer_cache.h b/src/video_core/buffer_cache.h
deleted file mode 100644
index 6f868b8b4..000000000
--- a/src/video_core/buffer_cache.h
+++ /dev/null
@@ -1,299 +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 <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 BufferInfo UploadMemory(GPUVAddr gpu_addr, std::size_t size, std::size_t alignment = 4,
114 bool internalize = false, bool is_written = false) {
115 std::lock_guard lock{RasterizerCache<Buffer>::mutex};
116
117 auto& memory_manager = system.GPU().MemoryManager();
118 const auto host_ptr = memory_manager.GetPointer(gpu_addr);
119 if (!host_ptr) {
120 return {GetEmptyBuffer(size), 0};
121 }
122 const auto cache_addr = ToCacheAddr(host_ptr);
123
124 // Cache management is a big overhead, so only cache entries with a given size.
125 // TODO: Figure out which size is the best for given games.
126 constexpr std::size_t max_stream_size = 0x800;
127 if (!internalize && size < max_stream_size &&
128 internalized_entries.find(cache_addr) == internalized_entries.end()) {
129 return StreamBufferUpload(host_ptr, size, alignment);
130 }
131
132 auto entry = RasterizerCache<Buffer>::TryGet(cache_addr);
133 if (!entry) {
134 return FixedBufferUpload(gpu_addr, host_ptr, size, internalize, is_written);
135 }
136
137 if (entry->GetSize() < size) {
138 IncreaseBufferSize(entry, size);
139 }
140 if (is_written) {
141 entry->MarkAsModified(true, *this);
142 }
143 return {ToHandle(entry->GetBuffer()), 0};
144 }
145
146 /// Uploads from a host memory. Returns the OpenGL buffer where it's located and its offset.
147 BufferInfo UploadHostMemory(const void* raw_pointer, std::size_t size,
148 std::size_t alignment = 4) {
149 std::lock_guard lock{RasterizerCache<Buffer>::mutex};
150 return StreamBufferUpload(raw_pointer, size, alignment);
151 }
152
153 void Map(std::size_t max_size) {
154 std::tie(buffer_ptr, buffer_offset_base, invalidated) = stream_buffer->Map(max_size, 4);
155 buffer_offset = buffer_offset_base;
156 }
157
158 /// Finishes the upload stream, returns true on bindings invalidation.
159 bool Unmap() {
160 stream_buffer->Unmap(buffer_offset - buffer_offset_base);
161 return std::exchange(invalidated, false);
162 }
163
164 virtual const BufferType* GetEmptyBuffer(std::size_t size) = 0;
165
166protected:
167 void FlushObjectInner(const Buffer& entry) override {
168 DownloadBufferData(entry->GetBuffer(), 0, entry->GetSize(), entry->GetWritableHostPtr());
169 }
170
171 virtual BufferStorageType CreateBuffer(std::size_t size) = 0;
172
173 virtual const BufferType* ToHandle(const BufferStorageType& storage) = 0;
174
175 virtual void UploadBufferData(const BufferStorageType& buffer, std::size_t offset,
176 std::size_t size, const u8* data) = 0;
177
178 virtual void DownloadBufferData(const BufferStorageType& buffer, std::size_t offset,
179 std::size_t size, u8* data) = 0;
180
181 virtual void CopyBufferData(const BufferStorageType& src, const BufferStorageType& dst,
182 std::size_t src_offset, std::size_t dst_offset,
183 std::size_t size) = 0;
184
185private:
186 BufferInfo StreamBufferUpload(const void* raw_pointer, std::size_t size,
187 std::size_t alignment) {
188 AlignBuffer(alignment);
189 const std::size_t uploaded_offset = buffer_offset;
190 std::memcpy(buffer_ptr, raw_pointer, size);
191
192 buffer_ptr += size;
193 buffer_offset += size;
194 return {&stream_buffer_handle, uploaded_offset};
195 }
196
197 BufferInfo FixedBufferUpload(GPUVAddr gpu_addr, u8* host_ptr, std::size_t size,
198 bool internalize, bool is_written) {
199 auto& memory_manager = Core::System::GetInstance().GPU().MemoryManager();
200 const auto cpu_addr = memory_manager.GpuToCpuAddress(gpu_addr);
201 ASSERT(cpu_addr);
202
203 auto entry = GetUncachedBuffer(*cpu_addr, host_ptr);
204 entry->SetSize(size);
205 entry->SetInternalState(internalize);
206 RasterizerCache<Buffer>::Register(entry);
207
208 if (internalize) {
209 internalized_entries.emplace(ToCacheAddr(host_ptr));
210 }
211 if (is_written) {
212 entry->MarkAsModified(true, *this);
213 }
214
215 if (entry->GetCapacity() < size) {
216 MarkedForDestruction().push_back(entry->ExchangeBuffer(CreateBuffer(size), size));
217 }
218
219 UploadBufferData(entry->GetBuffer(), 0, size, host_ptr);
220 return {ToHandle(entry->GetBuffer()), 0};
221 }
222
223 void IncreaseBufferSize(Buffer& entry, std::size_t new_size) {
224 const std::size_t old_size = entry->GetSize();
225 if (entry->GetCapacity() < new_size) {
226 const auto& old_buffer = entry->GetBuffer();
227 auto new_buffer = CreateBuffer(new_size);
228
229 // Copy bits from the old buffer to the new buffer.
230 CopyBufferData(old_buffer, new_buffer, 0, 0, old_size);
231 MarkedForDestruction().push_back(
232 entry->ExchangeBuffer(std::move(new_buffer), new_size));
233
234 // This buffer could have been used
235 invalidated = true;
236 }
237 // Upload the new bits.
238 const std::size_t size_diff = new_size - old_size;
239 UploadBufferData(entry->GetBuffer(), old_size, size_diff, entry->GetHostPtr() + old_size);
240
241 // Update entry's size in the object and in the cache.
242 Unregister(entry);
243
244 entry->SetSize(new_size);
245 RasterizerCache<Buffer>::Register(entry);
246 }
247
248 Buffer GetUncachedBuffer(VAddr cpu_addr, u8* host_ptr) {
249 if (auto entry = TryGetReservedBuffer(host_ptr)) {
250 return entry;
251 }
252 return std::make_shared<CachedBuffer<BufferStorageType>>(cpu_addr, host_ptr);
253 }
254
255 Buffer TryGetReservedBuffer(u8* host_ptr) {
256 const auto it = buffer_reserve.find(ToCacheAddr(host_ptr));
257 if (it == buffer_reserve.end()) {
258 return {};
259 }
260 auto& reserve = it->second;
261 auto entry = reserve.back();
262 reserve.pop_back();
263 return entry;
264 }
265
266 void ReserveBuffer(Buffer entry) {
267 buffer_reserve[entry->GetCacheAddr()].push_back(std::move(entry));
268 }
269
270 void AlignBuffer(std::size_t alignment) {
271 // Align the offset, not the mapped pointer
272 const std::size_t offset_aligned = Common::AlignUp(buffer_offset, alignment);
273 buffer_ptr += offset_aligned - buffer_offset;
274 buffer_offset = offset_aligned;
275 }
276
277 std::vector<BufferStorageType>& MarkedForDestruction() {
278 return marked_for_destruction_ring_buffer[marked_for_destruction_index];
279 }
280
281 Core::System& system;
282
283 std::unique_ptr<StreamBuffer> stream_buffer;
284 BufferType stream_buffer_handle{};
285
286 bool invalidated = false;
287
288 u8* buffer_ptr = nullptr;
289 u64 buffer_offset = 0;
290 u64 buffer_offset_base = 0;
291
292 std::size_t marked_for_destruction_index = 0;
293 std::array<std::vector<BufferStorageType>, 4> marked_for_destruction_ring_buffer;
294
295 std::unordered_set<CacheAddr> internalized_entries;
296 std::unordered_map<CacheAddr, std::vector<Buffer>> buffer_reserve;
297};
298
299} // namespace VideoCommon
diff --git a/src/video_core/buffer_cache/buffer_block.h b/src/video_core/buffer_cache/buffer_block.h
new file mode 100644
index 000000000..2c739a586
--- /dev/null
+++ b/src/video_core/buffer_cache/buffer_block.h
@@ -0,0 +1,78 @@
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 <unordered_set>
8#include <utility>
9
10#include "common/alignment.h"
11#include "common/common_types.h"
12#include "video_core/gpu.h"
13
14namespace VideoCommon {
15
16class BufferBlock {
17public:
18 bool Overlaps(const CacheAddr start, const CacheAddr end) const {
19 return (cache_addr < end) && (cache_addr_end > start);
20 }
21
22 bool IsInside(const CacheAddr other_start, const CacheAddr other_end) {
23 return (cache_addr <= other_start && other_end <= cache_addr_end);
24 }
25
26 u8* GetWritableHostPtr() const {
27 return FromCacheAddr(cache_addr);
28 }
29
30 u8* GetWritableHostPtr(std::size_t offset) const {
31 return FromCacheAddr(cache_addr + offset);
32 }
33
34 std::size_t GetOffset(const CacheAddr in_addr) {
35 return static_cast<std::size_t>(in_addr - cache_addr);
36 }
37
38 CacheAddr GetCacheAddr() const {
39 return cache_addr;
40 }
41
42 CacheAddr GetCacheAddrEnd() const {
43 return cache_addr_end;
44 }
45
46 void SetCacheAddr(const CacheAddr new_addr) {
47 cache_addr = new_addr;
48 cache_addr_end = new_addr + size;
49 }
50
51 std::size_t GetSize() const {
52 return size;
53 }
54
55 void SetEpoch(u64 new_epoch) {
56 epoch = new_epoch;
57 }
58
59 u64 GetEpoch() {
60 return epoch;
61 }
62
63protected:
64 explicit BufferBlock(CacheAddr cache_addr,const std::size_t size)
65 : size{size} {
66 SetCacheAddr(cache_addr);
67 }
68 ~BufferBlock() = default;
69
70private:
71 CacheAddr cache_addr{};
72 CacheAddr cache_addr_end{};
73 u64 pages{};
74 std::size_t size{};
75 u64 epoch{};
76};
77
78} // namespace VideoCommon
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h
new file mode 100644
index 000000000..6c467eb80
--- /dev/null
+++ b/src/video_core/buffer_cache/buffer_cache.h
@@ -0,0 +1,372 @@
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/buffer_cache/map_interval.h"
19#include "video_core/buffer_cache/buffer_block.h"
20#include "video_core/memory_manager.h"
21
22namespace VideoCore {
23class RasterizerInterface;
24}
25
26namespace VideoCommon {
27
28template <typename TBuffer, typename TBufferType, typename StreamBuffer>
29class BufferCache {
30public:
31 using BufferInfo = std::pair<const TBufferType*, u64>;
32
33 BufferInfo UploadMemory(GPUVAddr gpu_addr, std::size_t size, std::size_t alignment = 4,
34 bool is_written = false) {
35 std::lock_guard lock{mutex};
36
37 auto& memory_manager = system.GPU().MemoryManager();
38 const auto host_ptr = memory_manager.GetPointer(gpu_addr);
39 if (!host_ptr) {
40 return {GetEmptyBuffer(size), 0};
41 }
42 const auto cache_addr = ToCacheAddr(host_ptr);
43
44 auto block = GetBlock(cache_addr, size);
45 MapAddress(block, gpu_addr, cache_addr, size, is_written);
46
47 const u64 offset = static_cast<u64>(block->GetOffset(cache_addr));
48
49 return {ToHandle(block), offset};
50 }
51
52 /// Uploads from a host memory. Returns the OpenGL buffer where it's located and its offset.
53 BufferInfo UploadHostMemory(const void* raw_pointer, std::size_t size,
54 std::size_t alignment = 4) {
55 std::lock_guard lock{mutex};
56 return StreamBufferUpload(raw_pointer, size, alignment);
57 }
58
59 void Map(std::size_t max_size) {
60 std::tie(buffer_ptr, buffer_offset_base, invalidated) = stream_buffer->Map(max_size, 4);
61 buffer_offset = buffer_offset_base;
62 }
63
64 /// Finishes the upload stream, returns true on bindings invalidation.
65 bool Unmap() {
66 stream_buffer->Unmap(buffer_offset - buffer_offset_base);
67 return std::exchange(invalidated, false);
68 }
69
70 void TickFrame() {
71 ++epoch;
72 while (!pending_destruction.empty()) {
73 if (pending_destruction.front()->GetEpoch() + 1 > epoch) {
74 break;
75 }
76 pending_destruction.pop_front();
77 }
78 }
79
80 /// Write any cached resources overlapping the specified region back to memory
81 void FlushRegion(CacheAddr addr, std::size_t size) {
82 std::lock_guard lock{mutex};
83
84 // TODO
85 }
86
87 /// Mark the specified region as being invalidated
88 void InvalidateRegion(CacheAddr addr, u64 size) {
89 std::lock_guard lock{mutex};
90
91 std::vector<MapInterval> objects = GetMapsInRange(addr, size);
92 for (auto& object : objects) {
93 Unregister(object);
94 }
95 }
96
97 virtual const TBufferType* GetEmptyBuffer(std::size_t size) = 0;
98
99protected:
100 explicit BufferCache(VideoCore::RasterizerInterface& rasterizer, Core::System& system,
101 std::unique_ptr<StreamBuffer> stream_buffer)
102 : rasterizer{rasterizer}, system{system}, stream_buffer{std::move(stream_buffer)},
103 stream_buffer_handle{this->stream_buffer->GetHandle()} {}
104
105 ~BufferCache() = default;
106
107 virtual const TBufferType* ToHandle(const TBuffer& storage) = 0;
108
109 virtual void WriteBarrier() = 0;
110
111 virtual TBuffer CreateBlock(CacheAddr cache_addr, std::size_t size) = 0;
112
113 virtual void UploadBlockData(const TBuffer& buffer, std::size_t offset, std::size_t size,
114 const u8* data) = 0;
115
116 virtual void DownloadBlockData(const TBuffer& buffer, std::size_t offset, std::size_t size,
117 u8* data) = 0;
118
119 virtual void CopyBlock(const TBuffer& src, const TBuffer& dst, std::size_t src_offset,
120 std::size_t dst_offset, std::size_t size) = 0;
121
122 /// Register an object into the cache
123 void Register(const MapInterval& new_interval, const GPUVAddr gpu_addr) {
124 const CacheAddr cache_ptr = new_interval.start;
125 const std::size_t size = new_interval.end - new_interval.start;
126 const std::optional<VAddr> cpu_addr =
127 system.GPU().MemoryManager().GpuToCpuAddress(gpu_addr);
128 if (!cache_ptr || !cpu_addr) {
129 LOG_CRITICAL(HW_GPU, "Failed to register buffer with unmapped gpu_address 0x{:016x}",
130 gpu_addr);
131 return;
132 }
133 const IntervalType interval{new_interval.start, new_interval.end};
134 mapped_addresses.insert(interval);
135 map_storage[new_interval] = MapInfo{gpu_addr, *cpu_addr};
136
137 rasterizer.UpdatePagesCachedCount(*cpu_addr, size, 1);
138 }
139
140 /// Unregisters an object from the cache
141 void Unregister(const MapInterval& interval) {
142 const MapInfo info = map_storage[interval];
143 const std::size_t size = interval.end - interval.start;
144 rasterizer.UpdatePagesCachedCount(info.cpu_addr, size, -1);
145 const IntervalType delete_interval{interval.start, interval.end};
146 mapped_addresses.erase(delete_interval);
147 map_storage.erase(interval);
148 }
149
150private:
151 void MapAddress(const TBuffer& block, const GPUVAddr gpu_addr, const CacheAddr cache_addr,
152 const std::size_t size, bool is_written) {
153
154 std::vector<MapInterval> overlaps = GetMapsInRange(cache_addr, size);
155 if (overlaps.empty()) {
156 const CacheAddr cache_addr_end = cache_addr + size;
157 MapInterval new_interval{cache_addr, cache_addr_end};
158 if (!is_written) {
159 u8* host_ptr = FromCacheAddr(cache_addr);
160 UploadBlockData(block, block->GetOffset(cache_addr), size, host_ptr);
161 }
162 Register(new_interval, gpu_addr);
163 return;
164 }
165
166 if (overlaps.size() == 1) {
167 MapInterval current_map = overlaps[0];
168 const CacheAddr cache_addr_end = cache_addr + size;
169 if (current_map.IsInside(cache_addr, cache_addr_end)) {
170 return;
171 }
172 const CacheAddr new_start = std::min(cache_addr, current_map.start);
173 const CacheAddr new_end = std::max(cache_addr_end, current_map.end);
174 const GPUVAddr new_gpu_addr = gpu_addr + new_start - cache_addr;
175 const std::size_t new_size = static_cast<std::size_t>(new_end - new_start);
176 MapInterval new_interval{new_start, new_end};
177 const std::size_t offset = current_map.start - new_start;
178 const std::size_t size = current_map.end - current_map.start;
179 // Upload the remaining data
180 if (!is_written) {
181 u8* host_ptr = FromCacheAddr(new_start);
182 if (new_start == cache_addr && new_end == cache_addr_end) {
183 std::size_t first_size = current_map.start - new_start;
184 if (first_size > 0) {
185 UploadBlockData(block, block->GetOffset(new_start), first_size, host_ptr);
186 }
187
188 std::size_t second_size = new_end - current_map.end;
189 if (second_size > 0) {
190 u8* host_ptr2 = FromCacheAddr(current_map.end);
191 UploadBlockData(block, block->GetOffset(current_map.end), second_size,
192 host_ptr2);
193 }
194 } else {
195 if (new_start == cache_addr) {
196 std::size_t second_size = new_end - current_map.end;
197 if (second_size > 0) {
198 u8* host_ptr2 = FromCacheAddr(current_map.end);
199 UploadBlockData(block, block->GetOffset(current_map.end), second_size,
200 host_ptr2);
201 }
202 } else {
203 std::size_t first_size = current_map.start - new_start;
204 if (first_size > 0) {
205 UploadBlockData(block, block->GetOffset(new_start), first_size, host_ptr);
206 }
207 }
208 }
209 }
210 Unregister(current_map);
211 Register(new_interval, new_gpu_addr);
212 } else {
213 // Calculate new buffer parameters
214 GPUVAddr new_gpu_addr = gpu_addr;
215 CacheAddr start = cache_addr;
216 CacheAddr end = cache_addr + size;
217 for (auto& overlap : overlaps) {
218 start = std::min(overlap.start, start);
219 end = std::max(overlap.end, end);
220 }
221 new_gpu_addr = gpu_addr + start - cache_addr;
222 MapInterval new_interval{start, end};
223 for (auto& overlap : overlaps) {
224 Unregister(overlap);
225 }
226 std::size_t new_size = end - start;
227 if (!is_written) {
228 u8* host_ptr = FromCacheAddr(start);
229 UploadBlockData(block, block->GetOffset(start), new_size, host_ptr);
230 }
231 Register(new_interval, new_gpu_addr);
232 }
233 }
234
235 std::vector<MapInterval> GetMapsInRange(CacheAddr addr, std::size_t size) {
236 if (size == 0) {
237 return {};
238 }
239
240 std::vector<MapInterval> objects{};
241 const IntervalType interval{addr, addr + size};
242 for (auto& pair : boost::make_iterator_range(mapped_addresses.equal_range(interval))) {
243 objects.emplace_back(pair.lower(), pair.upper());
244 }
245
246 return objects;
247 }
248
249 /// Returns a ticks counter used for tracking when cached objects were last modified
250 u64 GetModifiedTicks() {
251 return ++modified_ticks;
252 }
253
254 BufferInfo StreamBufferUpload(const void* raw_pointer, std::size_t size,
255 std::size_t alignment) {
256 AlignBuffer(alignment);
257 const std::size_t uploaded_offset = buffer_offset;
258 std::memcpy(buffer_ptr, raw_pointer, size);
259
260 buffer_ptr += size;
261 buffer_offset += size;
262 return {&stream_buffer_handle, uploaded_offset};
263 }
264
265 void AlignBuffer(std::size_t alignment) {
266 // Align the offset, not the mapped pointer
267 const std::size_t offset_aligned = Common::AlignUp(buffer_offset, alignment);
268 buffer_ptr += offset_aligned - buffer_offset;
269 buffer_offset = offset_aligned;
270 }
271
272 TBuffer EnlargeBlock(TBuffer buffer) {
273 const std::size_t old_size = buffer->GetSize();
274 const std::size_t new_size = old_size + block_page_size;
275 const CacheAddr cache_addr = buffer->GetCacheAddr();
276 TBuffer new_buffer = CreateBlock(cache_addr, new_size);
277 CopyBlock(buffer, new_buffer, 0, 0, old_size);
278 buffer->SetEpoch(epoch);
279 pending_destruction.push_back(buffer);
280 const CacheAddr cache_addr_end = cache_addr + new_size - 1;
281 u64 page_start = cache_addr >> block_page_bits;
282 const u64 page_end = cache_addr_end >> block_page_bits;
283 while (page_start <= page_end) {
284 blocks[page_start] = new_buffer;
285 ++page_start;
286 }
287 return new_buffer;
288 }
289
290 TBuffer MergeBlocks(TBuffer first, TBuffer second) {
291 const std::size_t size_1 = first->GetSize();
292 const std::size_t size_2 = second->GetSize();
293 const CacheAddr first_addr = first->GetCacheAddr();
294 const CacheAddr second_addr = second->GetCacheAddr();
295 const CacheAddr new_addr = std::min(first_addr, second_addr);
296 const std::size_t new_size = size_1 + size_2;
297 TBuffer new_buffer = CreateBlock(new_addr, new_size);
298 CopyBlock(first, new_buffer, 0, new_buffer->GetOffset(first_addr), size_1);
299 CopyBlock(second, new_buffer, 0, new_buffer->GetOffset(second_addr), size_2);
300 first->SetEpoch(epoch);
301 second->SetEpoch(epoch);
302 pending_destruction.push_back(first);
303 pending_destruction.push_back(second);
304 const CacheAddr cache_addr_end = new_addr + new_size - 1;
305 u64 page_start = new_addr >> block_page_bits;
306 const u64 page_end = cache_addr_end >> block_page_bits;
307 while (page_start <= page_end) {
308 blocks[page_start] = new_buffer;
309 ++page_start;
310 }
311 return new_buffer;
312 }
313
314 TBuffer GetBlock(const CacheAddr cache_addr, const std::size_t size) {
315 TBuffer found{};
316 const CacheAddr cache_addr_end = cache_addr + size - 1;
317 u64 page_start = cache_addr >> block_page_bits;
318 const u64 page_end = cache_addr_end >> block_page_bits;
319 const u64 num_pages = page_end - page_start + 1;
320 while (page_start <= page_end) {
321 auto it = blocks.find(page_start);
322 if (it == blocks.end()) {
323 if (found) {
324 found = EnlargeBlock(found);
325 } else {
326 const CacheAddr start_addr = (page_start << block_page_bits);
327 found = CreateBlock(start_addr, block_page_size);
328 blocks[page_start] = found;
329 }
330 } else {
331 if (found) {
332 if (found == it->second) {
333 ++page_start;
334 continue;
335 }
336 found = MergeBlocks(found, it->second);
337 } else {
338 found = it->second;
339 }
340 }
341 ++page_start;
342 }
343 return found;
344 }
345
346 std::unique_ptr<StreamBuffer> stream_buffer;
347 TBufferType stream_buffer_handle{};
348
349 bool invalidated = false;
350
351 u8* buffer_ptr = nullptr;
352 u64 buffer_offset = 0;
353 u64 buffer_offset_base = 0;
354
355 using IntervalCache = boost::icl::interval_set<CacheAddr>;
356 using IntervalType = typename IntervalCache::interval_type;
357 IntervalCache mapped_addresses{};
358 std::unordered_map<MapInterval, MapInfo> map_storage;
359
360 static constexpr u64 block_page_bits{24};
361 static constexpr u64 block_page_size{1 << block_page_bits};
362 std::unordered_map<u64, TBuffer> blocks;
363
364 std::list<TBuffer> pending_destruction;
365 u64 epoch{};
366 u64 modified_ticks{};
367 VideoCore::RasterizerInterface& rasterizer;
368 Core::System& system;
369 std::recursive_mutex mutex;
370};
371
372} // namespace VideoCommon
diff --git a/src/video_core/buffer_cache/map_interval.h b/src/video_core/buffer_cache/map_interval.h
new file mode 100644
index 000000000..652a35dcd
--- /dev/null
+++ b/src/video_core/buffer_cache/map_interval.h
@@ -0,0 +1,48 @@
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 <boost/functional/hash.hpp>
8#include "common/common_types.h"
9#include "video_core/gpu.h"
10
11namespace VideoCommon {
12
13struct MapInterval {
14 MapInterval(const CacheAddr start, const CacheAddr end) : start{start}, end{end} {}
15 CacheAddr start;
16 CacheAddr end;
17 bool IsInside(const CacheAddr other_start, const CacheAddr other_end) {
18 return (start <= other_start && other_end <= end);
19 }
20
21 bool operator==(const MapInterval& rhs) const {
22 return std::tie(start, end) == std::tie(rhs.start, rhs.end);
23 }
24
25 bool operator!=(const MapInterval& rhs) const {
26 return !operator==(rhs);
27 }
28};
29
30struct MapInfo {
31 GPUVAddr gpu_addr;
32 VAddr cpu_addr;
33};
34
35} // namespace VideoCommon
36
37namespace std {
38
39template <>
40struct hash<VideoCommon::MapInterval> {
41 std::size_t operator()(const VideoCommon::MapInterval& k) const noexcept {
42 std::size_t a = std::hash<CacheAddr>()(k.start);
43 boost::hash_combine(a, std::hash<CacheAddr>()(k.end));
44 return a;
45 }
46};
47
48} // namespace std
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index 11857ff99..0baf2177c 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -19,6 +19,10 @@ inline CacheAddr ToCacheAddr(const void* host_ptr) {
19 return reinterpret_cast<CacheAddr>(host_ptr); 19 return reinterpret_cast<CacheAddr>(host_ptr);
20} 20}
21 21
22inline u8* FromCacheAddr(CacheAddr cache_addr) {
23 return reinterpret_cast<u8*>(cache_addr);
24}
25
22namespace Core { 26namespace Core {
23class System; 27class System;
24} 28}
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
index 2a9b523f5..a45d2771b 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
@@ -13,22 +13,31 @@
13 13
14namespace OpenGL { 14namespace OpenGL {
15 15
16CachedBufferBlock::CachedBufferBlock(CacheAddr cache_addr, const std::size_t size)
17 : VideoCommon::BufferBlock{cache_addr, size} {
18 gl_buffer.Create();
19 glNamedBufferData(gl_buffer.handle, static_cast<GLsizeiptr>(size), nullptr, GL_DYNAMIC_DRAW);
20}
21
22CachedBufferBlock::~CachedBufferBlock() = default;
23
16OGLBufferCache::OGLBufferCache(RasterizerOpenGL& rasterizer, Core::System& system, 24OGLBufferCache::OGLBufferCache(RasterizerOpenGL& rasterizer, Core::System& system,
17 std::size_t stream_size) 25 std::size_t stream_size)
18 : VideoCommon::BufferCache<OGLBuffer, GLuint, OGLStreamBuffer>{ 26 : VideoCommon::BufferCache<Buffer, GLuint, OGLStreamBuffer>{
19 rasterizer, system, std::make_unique<OGLStreamBuffer>(stream_size, true)} {} 27 rasterizer, system, std::make_unique<OGLStreamBuffer>(stream_size, true)} {}
20 28
21OGLBufferCache::~OGLBufferCache() = default; 29OGLBufferCache::~OGLBufferCache() = default;
22 30
23OGLBuffer OGLBufferCache::CreateBuffer(std::size_t size) { 31Buffer OGLBufferCache::CreateBlock(CacheAddr cache_addr, std::size_t size) {
24 OGLBuffer buffer; 32 return std::make_shared<CachedBufferBlock>(cache_addr, size);
25 buffer.Create(); 33}
26 glNamedBufferData(buffer.handle, static_cast<GLsizeiptr>(size), nullptr, GL_DYNAMIC_DRAW); 34
27 return buffer; 35void OGLBufferCache::WriteBarrier() {
36 glMemoryBarrier(GL_ALL_BARRIER_BITS);
28} 37}
29 38
30const GLuint* OGLBufferCache::ToHandle(const OGLBuffer& buffer) { 39const GLuint* OGLBufferCache::ToHandle(const Buffer& buffer) {
31 return &buffer.handle; 40 return buffer->GetHandle();
32} 41}
33 42
34const GLuint* OGLBufferCache::GetEmptyBuffer(std::size_t) { 43const GLuint* OGLBufferCache::GetEmptyBuffer(std::size_t) {
@@ -36,23 +45,23 @@ const GLuint* OGLBufferCache::GetEmptyBuffer(std::size_t) {
36 return &null_buffer; 45 return &null_buffer;
37} 46}
38 47
39void OGLBufferCache::UploadBufferData(const OGLBuffer& buffer, std::size_t offset, std::size_t size, 48void OGLBufferCache::UploadBlockData(const Buffer& buffer, std::size_t offset, std::size_t size,
40 const u8* data) { 49 const u8* data) {
41 glNamedBufferSubData(buffer.handle, static_cast<GLintptr>(offset), 50 glNamedBufferSubData(*buffer->GetHandle(), static_cast<GLintptr>(offset),
42 static_cast<GLsizeiptr>(size), data); 51 static_cast<GLsizeiptr>(size), data);
43} 52}
44 53
45void OGLBufferCache::DownloadBufferData(const OGLBuffer& buffer, std::size_t offset, 54void OGLBufferCache::DownloadBlockData(const Buffer& buffer, std::size_t offset, std::size_t size,
46 std::size_t size, u8* data) { 55 u8* data) {
47 glGetNamedBufferSubData(buffer.handle, static_cast<GLintptr>(offset), 56 glGetNamedBufferSubData(*buffer->GetHandle(), static_cast<GLintptr>(offset),
48 static_cast<GLsizeiptr>(size), data); 57 static_cast<GLsizeiptr>(size), data);
49} 58}
50 59
51void OGLBufferCache::CopyBufferData(const OGLBuffer& src, const OGLBuffer& dst, 60void OGLBufferCache::CopyBlock(const Buffer& src, const Buffer& dst, std::size_t src_offset,
52 std::size_t src_offset, std::size_t dst_offset, 61 std::size_t dst_offset, std::size_t size) {
53 std::size_t size) { 62 glCopyNamedBufferSubData(*src->GetHandle(), *dst->GetHandle(),
54 glCopyNamedBufferSubData(src.handle, dst.handle, static_cast<GLintptr>(src_offset), 63 static_cast<GLintptr>(src_offset), static_cast<GLintptr>(dst_offset),
55 static_cast<GLintptr>(dst_offset), static_cast<GLsizeiptr>(size)); 64 static_cast<GLsizeiptr>(size));
56} 65}
57 66
58} // namespace OpenGL 67} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.h b/src/video_core/renderer_opengl/gl_buffer_cache.h
index 8c8ac4038..fb93f22dd 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.h
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.h
@@ -7,7 +7,7 @@
7#include <memory> 7#include <memory>
8 8
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "video_core/buffer_cache.h" 10#include "video_core/buffer_cache/buffer_cache.h"
11#include "video_core/rasterizer_cache.h" 11#include "video_core/rasterizer_cache.h"
12#include "video_core/renderer_opengl/gl_resource_manager.h" 12#include "video_core/renderer_opengl/gl_resource_manager.h"
13#include "video_core/renderer_opengl/gl_stream_buffer.h" 13#include "video_core/renderer_opengl/gl_stream_buffer.h"
@@ -21,7 +21,24 @@ namespace OpenGL {
21class OGLStreamBuffer; 21class OGLStreamBuffer;
22class RasterizerOpenGL; 22class RasterizerOpenGL;
23 23
24class OGLBufferCache final : public VideoCommon::BufferCache<OGLBuffer, GLuint, OGLStreamBuffer> { 24class CachedBufferBlock;
25
26using Buffer = std::shared_ptr<CachedBufferBlock>;
27
28class CachedBufferBlock : public VideoCommon::BufferBlock {
29public:
30 explicit CachedBufferBlock(CacheAddr cache_addr, const std::size_t size);
31 ~CachedBufferBlock();
32
33 const GLuint* GetHandle() const {
34 return &gl_buffer.handle;
35 }
36
37private:
38 OGLBuffer gl_buffer{};
39};
40
41class OGLBufferCache final : public VideoCommon::BufferCache<Buffer, GLuint, OGLStreamBuffer> {
25public: 42public:
26 explicit OGLBufferCache(RasterizerOpenGL& rasterizer, Core::System& system, 43 explicit OGLBufferCache(RasterizerOpenGL& rasterizer, Core::System& system,
27 std::size_t stream_size); 44 std::size_t stream_size);
@@ -30,17 +47,19 @@ public:
30 const GLuint* GetEmptyBuffer(std::size_t) override; 47 const GLuint* GetEmptyBuffer(std::size_t) override;
31 48
32protected: 49protected:
33 OGLBuffer CreateBuffer(std::size_t size) override; 50 Buffer CreateBlock(CacheAddr cache_addr, std::size_t size) override;
51
52 void WriteBarrier() override;
34 53
35 const GLuint* ToHandle(const OGLBuffer& buffer) override; 54 const GLuint* ToHandle(const Buffer& buffer) override;
36 55
37 void UploadBufferData(const OGLBuffer& buffer, std::size_t offset, std::size_t size, 56 void UploadBlockData(const Buffer& buffer, std::size_t offset, std::size_t size,
38 const u8* data) override; 57 const u8* data) override;
39 58
40 void DownloadBufferData(const OGLBuffer& buffer, std::size_t offset, std::size_t size, 59 void DownloadBlockData(const Buffer& buffer, std::size_t offset, std::size_t size,
41 u8* data) override; 60 u8* data) override;
42 61
43 void CopyBufferData(const OGLBuffer& src, const OGLBuffer& dst, std::size_t src_offset, 62 void CopyBlock(const Buffer& src, const Buffer& dst, std::size_t src_offset,
44 std::size_t dst_offset, std::size_t size) override; 63 std::size_t dst_offset, std::size_t size) override;
45}; 64};
46 65
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 80cfda7e4..019583718 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -980,7 +980,7 @@ void RasterizerOpenGL::SetupGlobalMemory(const GLShader::GlobalMemoryEntry& entr
980 GPUVAddr gpu_addr, std::size_t size) { 980 GPUVAddr gpu_addr, std::size_t size) {
981 const auto alignment{device.GetShaderStorageBufferAlignment()}; 981 const auto alignment{device.GetShaderStorageBufferAlignment()};
982 const auto [ssbo, buffer_offset] = 982 const auto [ssbo, buffer_offset] =
983 buffer_cache.UploadMemory(gpu_addr, size, alignment, true, entry.IsWritten()); 983 buffer_cache.UploadMemory(gpu_addr, size, alignment, entry.IsWritten());
984 bind_ssbo_pushbuffer.Push(ssbo, buffer_offset, static_cast<GLsizeiptr>(size)); 984 bind_ssbo_pushbuffer.Push(ssbo, buffer_offset, static_cast<GLsizeiptr>(size));
985} 985}
986 986