summaryrefslogtreecommitdiff
path: root/src/video_core/buffer_cache
diff options
context:
space:
mode:
Diffstat (limited to 'src/video_core/buffer_cache')
-rw-r--r--src/video_core/buffer_cache/buffer_cache.h227
-rw-r--r--src/video_core/buffer_cache/map_interval.cpp33
-rw-r--r--src/video_core/buffer_cache/map_interval.h133
3 files changed, 210 insertions, 183 deletions
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h
index 56e570994..d9a4a1b4d 100644
--- a/src/video_core/buffer_cache/buffer_cache.h
+++ b/src/video_core/buffer_cache/buffer_cache.h
@@ -12,11 +12,12 @@
12#include <utility> 12#include <utility>
13#include <vector> 13#include <vector>
14 14
15#include <boost/icl/interval_map.hpp> 15#include <boost/container/small_vector.hpp>
16#include <boost/icl/interval_set.hpp> 16#include <boost/icl/interval_set.hpp>
17#include <boost/range/iterator_range.hpp> 17#include <boost/intrusive/set.hpp>
18 18
19#include "common/alignment.h" 19#include "common/alignment.h"
20#include "common/assert.h"
20#include "common/common_types.h" 21#include "common/common_types.h"
21#include "common/logging/log.h" 22#include "common/logging/log.h"
22#include "core/core.h" 23#include "core/core.h"
@@ -29,10 +30,12 @@
29 30
30namespace VideoCommon { 31namespace VideoCommon {
31 32
32using MapInterval = std::shared_ptr<MapIntervalBase>;
33
34template <typename OwnerBuffer, typename BufferType, typename StreamBuffer> 33template <typename OwnerBuffer, typename BufferType, typename StreamBuffer>
35class BufferCache { 34class BufferCache {
35 using IntervalSet = boost::icl::interval_set<VAddr>;
36 using IntervalType = typename IntervalSet::interval_type;
37 using VectorMapInterval = boost::container::small_vector<MapInterval*, 1>;
38
36public: 39public:
37 using BufferInfo = std::pair<BufferType, u64>; 40 using BufferInfo = std::pair<BufferType, u64>;
38 41
@@ -40,14 +43,12 @@ public:
40 bool is_written = false, bool use_fast_cbuf = false) { 43 bool is_written = false, bool use_fast_cbuf = false) {
41 std::lock_guard lock{mutex}; 44 std::lock_guard lock{mutex};
42 45
43 const std::optional<VAddr> cpu_addr_opt = 46 const auto& memory_manager = system.GPU().MemoryManager();
44 system.GPU().MemoryManager().GpuToCpuAddress(gpu_addr); 47 const std::optional<VAddr> cpu_addr_opt = memory_manager.GpuToCpuAddress(gpu_addr);
45
46 if (!cpu_addr_opt) { 48 if (!cpu_addr_opt) {
47 return {GetEmptyBuffer(size), 0}; 49 return {GetEmptyBuffer(size), 0};
48 } 50 }
49 51 const VAddr cpu_addr = *cpu_addr_opt;
50 VAddr cpu_addr = *cpu_addr_opt;
51 52
52 // Cache management is a big overhead, so only cache entries with a given size. 53 // Cache management is a big overhead, so only cache entries with a given size.
53 // TODO: Figure out which size is the best for given games. 54 // TODO: Figure out which size is the best for given games.
@@ -77,16 +78,19 @@ public:
77 } 78 }
78 } 79 }
79 80
80 auto block = GetBlock(cpu_addr, size); 81 OwnerBuffer block = GetBlock(cpu_addr, size);
81 auto map = MapAddress(block, gpu_addr, cpu_addr, size); 82 MapInterval* const map = MapAddress(block, gpu_addr, cpu_addr, size);
83 if (!map) {
84 return {GetEmptyBuffer(size), 0};
85 }
82 if (is_written) { 86 if (is_written) {
83 map->MarkAsModified(true, GetModifiedTicks()); 87 map->MarkAsModified(true, GetModifiedTicks());
84 if (Settings::IsGPULevelHigh() && Settings::values.use_asynchronous_gpu_emulation) { 88 if (Settings::IsGPULevelHigh() && Settings::values.use_asynchronous_gpu_emulation) {
85 MarkForAsyncFlush(map); 89 MarkForAsyncFlush(map);
86 } 90 }
87 if (!map->IsWritten()) { 91 if (!map->is_written) {
88 map->MarkAsWritten(true); 92 map->is_written = true;
89 MarkRegionAsWritten(map->GetStart(), map->GetEnd() - 1); 93 MarkRegionAsWritten(map->start, map->end - 1);
90 } 94 }
91 } 95 }
92 96
@@ -132,12 +136,11 @@ public:
132 void FlushRegion(VAddr addr, std::size_t size) { 136 void FlushRegion(VAddr addr, std::size_t size) {
133 std::lock_guard lock{mutex}; 137 std::lock_guard lock{mutex};
134 138
135 std::vector<MapInterval> objects = GetMapsInRange(addr, size); 139 VectorMapInterval objects = GetMapsInRange(addr, size);
136 std::sort(objects.begin(), objects.end(), [](const MapInterval& a, const MapInterval& b) { 140 std::sort(objects.begin(), objects.end(),
137 return a->GetModificationTick() < b->GetModificationTick(); 141 [](MapInterval* lhs, MapInterval* rhs) { return lhs->ticks < rhs->ticks; });
138 }); 142 for (MapInterval* object : objects) {
139 for (auto& object : objects) { 143 if (object->is_modified && object->is_registered) {
140 if (object->IsModified() && object->IsRegistered()) {
141 mutex.unlock(); 144 mutex.unlock();
142 FlushMap(object); 145 FlushMap(object);
143 mutex.lock(); 146 mutex.lock();
@@ -148,9 +151,9 @@ public:
148 bool MustFlushRegion(VAddr addr, std::size_t size) { 151 bool MustFlushRegion(VAddr addr, std::size_t size) {
149 std::lock_guard lock{mutex}; 152 std::lock_guard lock{mutex};
150 153
151 const std::vector<MapInterval> objects = GetMapsInRange(addr, size); 154 const VectorMapInterval objects = GetMapsInRange(addr, size);
152 return std::any_of(objects.cbegin(), objects.cend(), [](const MapInterval& map) { 155 return std::any_of(objects.cbegin(), objects.cend(), [](const MapInterval* map) {
153 return map->IsModified() && map->IsRegistered(); 156 return map->is_modified && map->is_registered;
154 }); 157 });
155 } 158 }
156 159
@@ -158,9 +161,8 @@ public:
158 void InvalidateRegion(VAddr addr, u64 size) { 161 void InvalidateRegion(VAddr addr, u64 size) {
159 std::lock_guard lock{mutex}; 162 std::lock_guard lock{mutex};
160 163
161 std::vector<MapInterval> objects = GetMapsInRange(addr, size); 164 for (auto& object : GetMapsInRange(addr, size)) {
162 for (auto& object : objects) { 165 if (object->is_registered) {
163 if (object->IsRegistered()) {
164 Unregister(object); 166 Unregister(object);
165 } 167 }
166 } 168 }
@@ -169,10 +171,10 @@ public:
169 void OnCPUWrite(VAddr addr, std::size_t size) { 171 void OnCPUWrite(VAddr addr, std::size_t size) {
170 std::lock_guard lock{mutex}; 172 std::lock_guard lock{mutex};
171 173
172 for (const auto& object : GetMapsInRange(addr, size)) { 174 for (MapInterval* object : GetMapsInRange(addr, size)) {
173 if (object->IsMemoryMarked() && object->IsRegistered()) { 175 if (object->is_memory_marked && object->is_registered) {
174 UnmarkMemory(object); 176 UnmarkMemory(object);
175 object->SetSyncPending(true); 177 object->is_sync_pending = true;
176 marked_for_unregister.emplace_back(object); 178 marked_for_unregister.emplace_back(object);
177 } 179 }
178 } 180 }
@@ -181,9 +183,9 @@ public:
181 void SyncGuestHost() { 183 void SyncGuestHost() {
182 std::lock_guard lock{mutex}; 184 std::lock_guard lock{mutex};
183 185
184 for (const auto& object : marked_for_unregister) { 186 for (auto& object : marked_for_unregister) {
185 if (object->IsRegistered()) { 187 if (object->is_registered) {
186 object->SetSyncPending(false); 188 object->is_sync_pending = false;
187 Unregister(object); 189 Unregister(object);
188 } 190 }
189 } 191 }
@@ -192,9 +194,9 @@ public:
192 194
193 void CommitAsyncFlushes() { 195 void CommitAsyncFlushes() {
194 if (uncommitted_flushes) { 196 if (uncommitted_flushes) {
195 auto commit_list = std::make_shared<std::list<MapInterval>>(); 197 auto commit_list = std::make_shared<std::list<MapInterval*>>();
196 for (auto& map : *uncommitted_flushes) { 198 for (MapInterval* map : *uncommitted_flushes) {
197 if (map->IsRegistered() && map->IsModified()) { 199 if (map->is_registered && map->is_modified) {
198 // TODO(Blinkhawk): Implement backend asynchronous flushing 200 // TODO(Blinkhawk): Implement backend asynchronous flushing
199 // AsyncFlushMap(map) 201 // AsyncFlushMap(map)
200 commit_list->push_back(map); 202 commit_list->push_back(map);
@@ -228,8 +230,8 @@ public:
228 committed_flushes.pop_front(); 230 committed_flushes.pop_front();
229 return; 231 return;
230 } 232 }
231 for (MapInterval& map : *flush_list) { 233 for (MapInterval* map : *flush_list) {
232 if (map->IsRegistered()) { 234 if (map->is_registered) {
233 // TODO(Blinkhawk): Replace this for reading the asynchronous flush 235 // TODO(Blinkhawk): Replace this for reading the asynchronous flush
234 FlushMap(map); 236 FlushMap(map);
235 } 237 }
@@ -265,61 +267,60 @@ protected:
265 } 267 }
266 268
267 /// Register an object into the cache 269 /// Register an object into the cache
268 void Register(const MapInterval& new_map, bool inherit_written = false) { 270 MapInterval* Register(MapInterval new_map, bool inherit_written = false) {
269 const VAddr cpu_addr = new_map->GetStart(); 271 const VAddr cpu_addr = new_map.start;
270 if (!cpu_addr) { 272 if (!cpu_addr) {
271 LOG_CRITICAL(HW_GPU, "Failed to register buffer with unmapped gpu_address 0x{:016x}", 273 LOG_CRITICAL(HW_GPU, "Failed to register buffer with unmapped gpu_address 0x{:016x}",
272 new_map->GetGpuAddress()); 274 new_map.gpu_addr);
273 return; 275 return nullptr;
274 } 276 }
275 const std::size_t size = new_map->GetEnd() - new_map->GetStart(); 277 const std::size_t size = new_map.end - new_map.start;
276 new_map->MarkAsRegistered(true); 278 new_map.is_registered = true;
277 const IntervalType interval{new_map->GetStart(), new_map->GetEnd()};
278 mapped_addresses.insert({interval, new_map});
279 rasterizer.UpdatePagesCachedCount(cpu_addr, size, 1); 279 rasterizer.UpdatePagesCachedCount(cpu_addr, size, 1);
280 new_map->SetMemoryMarked(true); 280 new_map.is_memory_marked = true;
281 if (inherit_written) { 281 if (inherit_written) {
282 MarkRegionAsWritten(new_map->GetStart(), new_map->GetEnd() - 1); 282 MarkRegionAsWritten(new_map.start, new_map.end - 1);
283 new_map->MarkAsWritten(true); 283 new_map.is_written = true;
284 } 284 }
285 MapInterval* const storage = mapped_addresses_allocator.Allocate();
286 *storage = new_map;
287 mapped_addresses.insert(*storage);
288 return storage;
285 } 289 }
286 290
287 void UnmarkMemory(const MapInterval& map) { 291 void UnmarkMemory(MapInterval* map) {
288 if (!map->IsMemoryMarked()) { 292 if (!map->is_memory_marked) {
289 return; 293 return;
290 } 294 }
291 const std::size_t size = map->GetEnd() - map->GetStart(); 295 const std::size_t size = map->end - map->start;
292 rasterizer.UpdatePagesCachedCount(map->GetStart(), size, -1); 296 rasterizer.UpdatePagesCachedCount(map->start, size, -1);
293 map->SetMemoryMarked(false); 297 map->is_memory_marked = false;
294 } 298 }
295 299
296 /// Unregisters an object from the cache 300 /// Unregisters an object from the cache
297 void Unregister(const MapInterval& map) { 301 void Unregister(MapInterval* map) {
298 UnmarkMemory(map); 302 UnmarkMemory(map);
299 map->MarkAsRegistered(false); 303 map->is_registered = false;
300 if (map->IsSyncPending()) { 304 if (map->is_sync_pending) {
305 map->is_sync_pending = false;
301 marked_for_unregister.remove(map); 306 marked_for_unregister.remove(map);
302 map->SetSyncPending(false);
303 } 307 }
304 if (map->IsWritten()) { 308 if (map->is_written) {
305 UnmarkRegionAsWritten(map->GetStart(), map->GetEnd() - 1); 309 UnmarkRegionAsWritten(map->start, map->end - 1);
306 } 310 }
307 const IntervalType delete_interval{map->GetStart(), map->GetEnd()}; 311 const auto it = mapped_addresses.find(*map);
308 mapped_addresses.erase(delete_interval); 312 ASSERT(it != mapped_addresses.end());
313 mapped_addresses.erase(it);
314 mapped_addresses_allocator.Release(map);
309 } 315 }
310 316
311private: 317private:
312 MapInterval CreateMap(const VAddr start, const VAddr end, const GPUVAddr gpu_addr) { 318 MapInterval* MapAddress(const OwnerBuffer& block, GPUVAddr gpu_addr, VAddr cpu_addr,
313 return std::make_shared<MapIntervalBase>(start, end, gpu_addr); 319 std::size_t size) {
314 } 320 const VectorMapInterval overlaps = GetMapsInRange(cpu_addr, size);
315
316 MapInterval MapAddress(const OwnerBuffer& block, const GPUVAddr gpu_addr, const VAddr cpu_addr,
317 const std::size_t size) {
318 std::vector<MapInterval> overlaps = GetMapsInRange(cpu_addr, size);
319 if (overlaps.empty()) { 321 if (overlaps.empty()) {
320 auto& memory_manager = system.GPU().MemoryManager(); 322 auto& memory_manager = system.GPU().MemoryManager();
321 const VAddr cpu_addr_end = cpu_addr + size; 323 const VAddr cpu_addr_end = cpu_addr + size;
322 MapInterval new_map = CreateMap(cpu_addr, cpu_addr_end, gpu_addr);
323 if (memory_manager.IsGranularRange(gpu_addr, size)) { 324 if (memory_manager.IsGranularRange(gpu_addr, size)) {
324 u8* host_ptr = memory_manager.GetPointer(gpu_addr); 325 u8* host_ptr = memory_manager.GetPointer(gpu_addr);
325 UploadBlockData(block, block->GetOffset(cpu_addr), size, host_ptr); 326 UploadBlockData(block, block->GetOffset(cpu_addr), size, host_ptr);
@@ -328,13 +329,12 @@ private:
328 memory_manager.ReadBlockUnsafe(gpu_addr, staging_buffer.data(), size); 329 memory_manager.ReadBlockUnsafe(gpu_addr, staging_buffer.data(), size);
329 UploadBlockData(block, block->GetOffset(cpu_addr), size, staging_buffer.data()); 330 UploadBlockData(block, block->GetOffset(cpu_addr), size, staging_buffer.data());
330 } 331 }
331 Register(new_map); 332 return Register(MapInterval(cpu_addr, cpu_addr_end, gpu_addr));
332 return new_map;
333 } 333 }
334 334
335 const VAddr cpu_addr_end = cpu_addr + size; 335 const VAddr cpu_addr_end = cpu_addr + size;
336 if (overlaps.size() == 1) { 336 if (overlaps.size() == 1) {
337 MapInterval& current_map = overlaps[0]; 337 MapInterval* const current_map = overlaps[0];
338 if (current_map->IsInside(cpu_addr, cpu_addr_end)) { 338 if (current_map->IsInside(cpu_addr, cpu_addr_end)) {
339 return current_map; 339 return current_map;
340 } 340 }
@@ -344,35 +344,39 @@ private:
344 bool write_inheritance = false; 344 bool write_inheritance = false;
345 bool modified_inheritance = false; 345 bool modified_inheritance = false;
346 // Calculate new buffer parameters 346 // Calculate new buffer parameters
347 for (auto& overlap : overlaps) { 347 for (MapInterval* overlap : overlaps) {
348 new_start = std::min(overlap->GetStart(), new_start); 348 new_start = std::min(overlap->start, new_start);
349 new_end = std::max(overlap->GetEnd(), new_end); 349 new_end = std::max(overlap->end, new_end);
350 write_inheritance |= overlap->IsWritten(); 350 write_inheritance |= overlap->is_written;
351 modified_inheritance |= overlap->IsModified(); 351 modified_inheritance |= overlap->is_modified;
352 } 352 }
353 GPUVAddr new_gpu_addr = gpu_addr + new_start - cpu_addr; 353 GPUVAddr new_gpu_addr = gpu_addr + new_start - cpu_addr;
354 for (auto& overlap : overlaps) { 354 for (auto& overlap : overlaps) {
355 Unregister(overlap); 355 Unregister(overlap);
356 } 356 }
357 UpdateBlock(block, new_start, new_end, overlaps); 357 UpdateBlock(block, new_start, new_end, overlaps);
358 MapInterval new_map = CreateMap(new_start, new_end, new_gpu_addr); 358
359 const MapInterval new_map{new_start, new_end, new_gpu_addr};
360 MapInterval* const map = Register(new_map, write_inheritance);
361 if (!map) {
362 return nullptr;
363 }
359 if (modified_inheritance) { 364 if (modified_inheritance) {
360 new_map->MarkAsModified(true, GetModifiedTicks()); 365 map->MarkAsModified(true, GetModifiedTicks());
361 if (Settings::IsGPULevelHigh() && Settings::values.use_asynchronous_gpu_emulation) { 366 if (Settings::IsGPULevelHigh() && Settings::values.use_asynchronous_gpu_emulation) {
362 MarkForAsyncFlush(new_map); 367 MarkForAsyncFlush(map);
363 } 368 }
364 } 369 }
365 Register(new_map, write_inheritance); 370 return map;
366 return new_map;
367 } 371 }
368 372
369 void UpdateBlock(const OwnerBuffer& block, VAddr start, VAddr end, 373 void UpdateBlock(const OwnerBuffer& block, VAddr start, VAddr end,
370 std::vector<MapInterval>& overlaps) { 374 const VectorMapInterval& overlaps) {
371 const IntervalType base_interval{start, end}; 375 const IntervalType base_interval{start, end};
372 IntervalSet interval_set{}; 376 IntervalSet interval_set{};
373 interval_set.add(base_interval); 377 interval_set.add(base_interval);
374 for (auto& overlap : overlaps) { 378 for (auto& overlap : overlaps) {
375 const IntervalType subtract{overlap->GetStart(), overlap->GetEnd()}; 379 const IntervalType subtract{overlap->start, overlap->end};
376 interval_set.subtract(subtract); 380 interval_set.subtract(subtract);
377 } 381 }
378 for (auto& interval : interval_set) { 382 for (auto& interval : interval_set) {
@@ -386,18 +390,24 @@ private:
386 } 390 }
387 } 391 }
388 392
389 std::vector<MapInterval> GetMapsInRange(VAddr addr, std::size_t size) { 393 VectorMapInterval GetMapsInRange(VAddr addr, std::size_t size) {
394 VectorMapInterval result;
390 if (size == 0) { 395 if (size == 0) {
391 return {}; 396 return result;
392 } 397 }
393 398
394 std::vector<MapInterval> objects{}; 399 const VAddr addr_end = addr + size;
395 const IntervalType interval{addr, addr + size}; 400 auto it = mapped_addresses.lower_bound(addr);
396 for (auto& pair : boost::make_iterator_range(mapped_addresses.equal_range(interval))) { 401 if (it != mapped_addresses.begin()) {
397 objects.push_back(pair.second); 402 --it;
398 } 403 }
399 404 while (it != mapped_addresses.end() && it->start < addr_end) {
400 return objects; 405 if (it->Overlaps(addr, addr_end)) {
406 result.push_back(&*it);
407 }
408 ++it;
409 }
410 return result;
401 } 411 }
402 412
403 /// Returns a ticks counter used for tracking when cached objects were last modified 413 /// Returns a ticks counter used for tracking when cached objects were last modified
@@ -405,12 +415,12 @@ private:
405 return ++modified_ticks; 415 return ++modified_ticks;
406 } 416 }
407 417
408 void FlushMap(MapInterval map) { 418 void FlushMap(MapInterval* map) {
409 std::size_t size = map->GetEnd() - map->GetStart(); 419 const std::size_t size = map->end - map->start;
410 OwnerBuffer block = blocks[map->GetStart() >> block_page_bits]; 420 OwnerBuffer block = blocks[map->start >> block_page_bits];
411 staging_buffer.resize(size); 421 staging_buffer.resize(size);
412 DownloadBlockData(block, block->GetOffset(map->GetStart()), size, staging_buffer.data()); 422 DownloadBlockData(block, block->GetOffset(map->start), size, staging_buffer.data());
413 system.Memory().WriteBlockUnsafe(map->GetStart(), staging_buffer.data(), size); 423 system.Memory().WriteBlockUnsafe(map->start, staging_buffer.data(), size);
414 map->MarkAsModified(false, 0); 424 map->MarkAsModified(false, 0);
415 } 425 }
416 426
@@ -515,7 +525,7 @@ private:
515 } else { 525 } else {
516 written_pages[page_start] = 1; 526 written_pages[page_start] = 1;
517 } 527 }
518 page_start++; 528 ++page_start;
519 } 529 }
520 } 530 }
521 531
@@ -531,7 +541,7 @@ private:
531 written_pages.erase(it); 541 written_pages.erase(it);
532 } 542 }
533 } 543 }
534 page_start++; 544 ++page_start;
535 } 545 }
536 } 546 }
537 547
@@ -542,14 +552,14 @@ private:
542 if (written_pages.count(page_start) > 0) { 552 if (written_pages.count(page_start) > 0) {
543 return true; 553 return true;
544 } 554 }
545 page_start++; 555 ++page_start;
546 } 556 }
547 return false; 557 return false;
548 } 558 }
549 559
550 void MarkForAsyncFlush(MapInterval& map) { 560 void MarkForAsyncFlush(MapInterval* map) {
551 if (!uncommitted_flushes) { 561 if (!uncommitted_flushes) {
552 uncommitted_flushes = std::make_shared<std::unordered_set<MapInterval>>(); 562 uncommitted_flushes = std::make_shared<std::unordered_set<MapInterval*>>();
553 } 563 }
554 uncommitted_flushes->insert(map); 564 uncommitted_flushes->insert(map);
555 } 565 }
@@ -566,10 +576,9 @@ private:
566 u64 buffer_offset = 0; 576 u64 buffer_offset = 0;
567 u64 buffer_offset_base = 0; 577 u64 buffer_offset_base = 0;
568 578
569 using IntervalSet = boost::icl::interval_set<VAddr>; 579 MapIntervalAllocator mapped_addresses_allocator;
570 using IntervalCache = boost::icl::interval_map<VAddr, MapInterval>; 580 boost::intrusive::set<MapInterval, boost::intrusive::compare<MapIntervalCompare>>
571 using IntervalType = typename IntervalCache::interval_type; 581 mapped_addresses;
572 IntervalCache mapped_addresses;
573 582
574 static constexpr u64 write_page_bit = 11; 583 static constexpr u64 write_page_bit = 11;
575 std::unordered_map<u64, u32> written_pages; 584 std::unordered_map<u64, u32> written_pages;
@@ -583,10 +592,10 @@ private:
583 u64 modified_ticks = 0; 592 u64 modified_ticks = 0;
584 593
585 std::vector<u8> staging_buffer; 594 std::vector<u8> staging_buffer;
586 std::list<MapInterval> marked_for_unregister; 595 std::list<MapInterval*> marked_for_unregister;
587 596
588 std::shared_ptr<std::unordered_set<MapInterval>> uncommitted_flushes{}; 597 std::shared_ptr<std::unordered_set<MapInterval*>> uncommitted_flushes;
589 std::list<std::shared_ptr<std::list<MapInterval>>> committed_flushes; 598 std::list<std::shared_ptr<std::list<MapInterval*>>> committed_flushes;
590 599
591 std::recursive_mutex mutex; 600 std::recursive_mutex mutex;
592}; 601};
diff --git a/src/video_core/buffer_cache/map_interval.cpp b/src/video_core/buffer_cache/map_interval.cpp
new file mode 100644
index 000000000..62587e18a
--- /dev/null
+++ b/src/video_core/buffer_cache/map_interval.cpp
@@ -0,0 +1,33 @@
1// Copyright 2020 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 <array>
7#include <cstddef>
8#include <memory>
9
10#include "video_core/buffer_cache/map_interval.h"
11
12namespace VideoCommon {
13
14MapIntervalAllocator::MapIntervalAllocator() {
15 FillFreeList(first_chunk);
16}
17
18MapIntervalAllocator::~MapIntervalAllocator() = default;
19
20void MapIntervalAllocator::AllocateNewChunk() {
21 *new_chunk = std::make_unique<Chunk>();
22 FillFreeList(**new_chunk);
23 new_chunk = &(*new_chunk)->next;
24}
25
26void MapIntervalAllocator::FillFreeList(Chunk& chunk) {
27 const std::size_t old_size = free_list.size();
28 free_list.resize(old_size + chunk.data.size());
29 std::transform(chunk.data.rbegin(), chunk.data.rend(), free_list.begin() + old_size,
30 [](MapInterval& interval) { return &interval; });
31}
32
33} // namespace VideoCommon
diff --git a/src/video_core/buffer_cache/map_interval.h b/src/video_core/buffer_cache/map_interval.h
index 29d8b26f3..fe0bcd1d8 100644
--- a/src/video_core/buffer_cache/map_interval.h
+++ b/src/video_core/buffer_cache/map_interval.h
@@ -4,104 +4,89 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <array>
8#include <cstddef>
9#include <memory>
10#include <vector>
11
12#include <boost/intrusive/set_hook.hpp>
13
7#include "common/common_types.h" 14#include "common/common_types.h"
8#include "video_core/gpu.h" 15#include "video_core/gpu.h"
9 16
10namespace VideoCommon { 17namespace VideoCommon {
11 18
12class MapIntervalBase { 19struct MapInterval : public boost::intrusive::set_base_hook<boost::intrusive::optimize_size<true>> {
13public: 20 MapInterval() = default;
14 MapIntervalBase(const VAddr start, const VAddr end, const GPUVAddr gpu_addr)
15 : start{start}, end{end}, gpu_addr{gpu_addr} {}
16
17 void SetCpuAddress(VAddr new_cpu_addr) {
18 cpu_addr = new_cpu_addr;
19 }
20
21 VAddr GetCpuAddress() const {
22 return cpu_addr;
23 }
24
25 GPUVAddr GetGpuAddress() const {
26 return gpu_addr;
27 }
28
29 bool IsInside(const VAddr other_start, const VAddr other_end) const {
30 return (start <= other_start && other_end <= end);
31 }
32
33 bool operator==(const MapIntervalBase& rhs) const {
34 return std::tie(start, end) == std::tie(rhs.start, rhs.end);
35 }
36
37 bool operator!=(const MapIntervalBase& rhs) const {
38 return !operator==(rhs);
39 }
40 21
41 void MarkAsRegistered(const bool registered) { 22 /*implicit*/ MapInterval(VAddr start_) noexcept : start{start_} {}
42 is_registered = registered;
43 }
44 23
45 bool IsRegistered() const { 24 explicit MapInterval(VAddr start_, VAddr end_, GPUVAddr gpu_addr_) noexcept
46 return is_registered; 25 : start{start_}, end{end_}, gpu_addr{gpu_addr_} {}
47 }
48 26
49 void SetMemoryMarked(bool is_memory_marked_) { 27 bool IsInside(VAddr other_start, VAddr other_end) const noexcept {
50 is_memory_marked = is_memory_marked_; 28 return start <= other_start && other_end <= end;
51 } 29 }
52 30
53 bool IsMemoryMarked() const { 31 bool Overlaps(VAddr other_start, VAddr other_end) const noexcept {
54 return is_memory_marked; 32 return start < other_end && other_start < end;
55 } 33 }
56 34
57 void SetSyncPending(bool is_sync_pending_) { 35 void MarkAsModified(bool is_modified_, u64 ticks_) noexcept {
58 is_sync_pending = is_sync_pending_; 36 is_modified = is_modified_;
59 } 37 ticks = ticks_;
38 }
39
40 boost::intrusive::set_member_hook<> member_hook_;
41 VAddr start = 0;
42 VAddr end = 0;
43 GPUVAddr gpu_addr = 0;
44 u64 ticks = 0;
45 bool is_written = false;
46 bool is_modified = false;
47 bool is_registered = false;
48 bool is_memory_marked = false;
49 bool is_sync_pending = false;
50};
60 51
61 bool IsSyncPending() const { 52struct MapIntervalCompare {
62 return is_sync_pending; 53 constexpr bool operator()(const MapInterval& lhs, const MapInterval& rhs) const noexcept {
54 return lhs.start < rhs.start;
63 } 55 }
56};
64 57
65 VAddr GetStart() const { 58class MapIntervalAllocator {
66 return start; 59public:
67 } 60 MapIntervalAllocator();
61 ~MapIntervalAllocator();
68 62
69 VAddr GetEnd() const { 63 MapInterval* Allocate() {
70 return end; 64 if (free_list.empty()) {
65 AllocateNewChunk();
66 }
67 MapInterval* const interval = free_list.back();
68 free_list.pop_back();
69 return interval;
71 } 70 }
72 71
73 void MarkAsModified(const bool is_modified_, const u64 tick) { 72 void Release(MapInterval* interval) {
74 is_modified = is_modified_; 73 free_list.push_back(interval);
75 ticks = tick;
76 } 74 }
77 75
78 bool IsModified() const { 76private:
79 return is_modified; 77 struct Chunk {
80 } 78 std::unique_ptr<Chunk> next;
79 std::array<MapInterval, 0x8000> data;
80 };
81 81
82 u64 GetModificationTick() const { 82 void AllocateNewChunk();
83 return ticks;
84 }
85 83
86 void MarkAsWritten(const bool is_written_) { 84 void FillFreeList(Chunk& chunk);
87 is_written = is_written_;
88 }
89 85
90 bool IsWritten() const { 86 std::vector<MapInterval*> free_list;
91 return is_written; 87 std::unique_ptr<Chunk>* new_chunk = &first_chunk.next;
92 }
93 88
94private: 89 Chunk first_chunk;
95 VAddr start;
96 VAddr end;
97 GPUVAddr gpu_addr;
98 VAddr cpu_addr{};
99 bool is_written{};
100 bool is_modified{};
101 bool is_registered{};
102 bool is_memory_marked{};
103 bool is_sync_pending{};
104 u64 ticks{};
105}; 90};
106 91
107} // namespace VideoCommon 92} // namespace VideoCommon