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_block.h27
-rw-r--r--src/video_core/buffer_cache/buffer_cache.h272
2 files changed, 149 insertions, 150 deletions
diff --git a/src/video_core/buffer_cache/buffer_block.h b/src/video_core/buffer_cache/buffer_block.h
index e35ee0b67..e64170e66 100644
--- a/src/video_core/buffer_cache/buffer_block.h
+++ b/src/video_core/buffer_cache/buffer_block.h
@@ -15,48 +15,47 @@ namespace VideoCommon {
15 15
16class BufferBlock { 16class BufferBlock {
17public: 17public:
18 bool Overlaps(const VAddr start, const VAddr end) const { 18 bool Overlaps(VAddr start, VAddr end) const {
19 return (cpu_addr < end) && (cpu_addr_end > start); 19 return (cpu_addr < end) && (cpu_addr_end > start);
20 } 20 }
21 21
22 bool IsInside(const VAddr other_start, const VAddr other_end) const { 22 bool IsInside(VAddr other_start, VAddr other_end) const {
23 return cpu_addr <= other_start && other_end <= cpu_addr_end; 23 return cpu_addr <= other_start && other_end <= cpu_addr_end;
24 } 24 }
25 25
26 std::size_t GetOffset(const VAddr in_addr) { 26 std::size_t Offset(VAddr in_addr) const {
27 return static_cast<std::size_t>(in_addr - cpu_addr); 27 return static_cast<std::size_t>(in_addr - cpu_addr);
28 } 28 }
29 29
30 VAddr GetCpuAddr() const { 30 VAddr CpuAddr() const {
31 return cpu_addr; 31 return cpu_addr;
32 } 32 }
33 33
34 VAddr GetCpuAddrEnd() const { 34 VAddr CpuAddrEnd() const {
35 return cpu_addr_end; 35 return cpu_addr_end;
36 } 36 }
37 37
38 void SetCpuAddr(const VAddr new_addr) { 38 void SetCpuAddr(VAddr new_addr) {
39 cpu_addr = new_addr; 39 cpu_addr = new_addr;
40 cpu_addr_end = new_addr + size; 40 cpu_addr_end = new_addr + size;
41 } 41 }
42 42
43 std::size_t GetSize() const { 43 std::size_t Size() const {
44 return size; 44 return size;
45 } 45 }
46 46
47 void SetEpoch(u64 new_epoch) { 47 u64 Epoch() const {
48 epoch = new_epoch; 48 return epoch;
49 } 49 }
50 50
51 u64 GetEpoch() { 51 void SetEpoch(u64 new_epoch) {
52 return epoch; 52 epoch = new_epoch;
53 } 53 }
54 54
55protected: 55protected:
56 explicit BufferBlock(VAddr cpu_addr, const std::size_t size) : size{size} { 56 explicit BufferBlock(VAddr cpu_addr_, std::size_t size_) : size{size_} {
57 SetCpuAddr(cpu_addr); 57 SetCpuAddr(cpu_addr_);
58 } 58 }
59 ~BufferBlock() = default;
60 59
61private: 60private:
62 VAddr cpu_addr{}; 61 VAddr cpu_addr{};
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h
index d9a4a1b4d..cf8bdd021 100644
--- a/src/video_core/buffer_cache/buffer_cache.h
+++ b/src/video_core/buffer_cache/buffer_cache.h
@@ -30,23 +30,31 @@
30 30
31namespace VideoCommon { 31namespace VideoCommon {
32 32
33template <typename OwnerBuffer, typename BufferType, typename StreamBuffer> 33template <typename Buffer, typename BufferType, typename StreamBuffer>
34class BufferCache { 34class BufferCache {
35 using IntervalSet = boost::icl::interval_set<VAddr>; 35 using IntervalSet = boost::icl::interval_set<VAddr>;
36 using IntervalType = typename IntervalSet::interval_type; 36 using IntervalType = typename IntervalSet::interval_type;
37 using VectorMapInterval = boost::container::small_vector<MapInterval*, 1>; 37 using VectorMapInterval = boost::container::small_vector<MapInterval*, 1>;
38 38
39 static constexpr u64 WRITE_PAGE_BIT = 11;
40 static constexpr u64 BLOCK_PAGE_BITS = 21;
41 static constexpr u64 BLOCK_PAGE_SIZE = 1ULL << BLOCK_PAGE_BITS;
42
39public: 43public:
40 using BufferInfo = std::pair<BufferType, u64>; 44 struct BufferInfo {
45 BufferType handle;
46 u64 offset;
47 u64 address;
48 };
41 49
42 BufferInfo UploadMemory(GPUVAddr gpu_addr, std::size_t size, std::size_t alignment = 4, 50 BufferInfo UploadMemory(GPUVAddr gpu_addr, std::size_t size, std::size_t alignment = 4,
43 bool is_written = false, bool use_fast_cbuf = false) { 51 bool is_written = false, bool use_fast_cbuf = false) {
44 std::lock_guard lock{mutex}; 52 std::lock_guard lock{mutex};
45 53
46 const auto& memory_manager = system.GPU().MemoryManager(); 54 auto& memory_manager = system.GPU().MemoryManager();
47 const std::optional<VAddr> cpu_addr_opt = memory_manager.GpuToCpuAddress(gpu_addr); 55 const std::optional<VAddr> cpu_addr_opt = memory_manager.GpuToCpuAddress(gpu_addr);
48 if (!cpu_addr_opt) { 56 if (!cpu_addr_opt) {
49 return {GetEmptyBuffer(size), 0}; 57 return GetEmptyBuffer(size);
50 } 58 }
51 const VAddr cpu_addr = *cpu_addr_opt; 59 const VAddr cpu_addr = *cpu_addr_opt;
52 60
@@ -55,33 +63,36 @@ public:
55 constexpr std::size_t max_stream_size = 0x800; 63 constexpr std::size_t max_stream_size = 0x800;
56 if (use_fast_cbuf || size < max_stream_size) { 64 if (use_fast_cbuf || size < max_stream_size) {
57 if (!is_written && !IsRegionWritten(cpu_addr, cpu_addr + size - 1)) { 65 if (!is_written && !IsRegionWritten(cpu_addr, cpu_addr + size - 1)) {
58 auto& memory_manager = system.GPU().MemoryManager(); 66 const bool is_granular = memory_manager.IsGranularRange(gpu_addr, size);
59 if (use_fast_cbuf) { 67 if (use_fast_cbuf) {
60 if (memory_manager.IsGranularRange(gpu_addr, size)) { 68 u8* dest;
61 const auto host_ptr = memory_manager.GetPointer(gpu_addr); 69 if (is_granular) {
62 return ConstBufferUpload(host_ptr, size); 70 dest = memory_manager.GetPointer(gpu_addr);
63 } else { 71 } else {
64 staging_buffer.resize(size); 72 staging_buffer.resize(size);
65 memory_manager.ReadBlockUnsafe(gpu_addr, staging_buffer.data(), size); 73 dest = staging_buffer.data();
66 return ConstBufferUpload(staging_buffer.data(), size); 74 memory_manager.ReadBlockUnsafe(gpu_addr, dest, size);
67 } 75 }
76 return ConstBufferUpload(dest, size);
77 }
78 if (is_granular) {
79 u8* const host_ptr = memory_manager.GetPointer(gpu_addr);
80 return StreamBufferUpload(size, alignment, [host_ptr, size](u8* dest) {
81 std::memcpy(dest, host_ptr, size);
82 });
68 } else { 83 } else {
69 if (memory_manager.IsGranularRange(gpu_addr, size)) { 84 return StreamBufferUpload(
70 const auto host_ptr = memory_manager.GetPointer(gpu_addr); 85 size, alignment, [&memory_manager, gpu_addr, size](u8* dest) {
71 return StreamBufferUpload(host_ptr, size, alignment); 86 memory_manager.ReadBlockUnsafe(gpu_addr, dest, size);
72 } else { 87 });
73 staging_buffer.resize(size);
74 memory_manager.ReadBlockUnsafe(gpu_addr, staging_buffer.data(), size);
75 return StreamBufferUpload(staging_buffer.data(), size, alignment);
76 }
77 } 88 }
78 } 89 }
79 } 90 }
80 91
81 OwnerBuffer block = GetBlock(cpu_addr, size); 92 Buffer* const block = GetBlock(cpu_addr, size);
82 MapInterval* const map = MapAddress(block, gpu_addr, cpu_addr, size); 93 MapInterval* const map = MapAddress(block, gpu_addr, cpu_addr, size);
83 if (!map) { 94 if (!map) {
84 return {GetEmptyBuffer(size), 0}; 95 return GetEmptyBuffer(size);
85 } 96 }
86 if (is_written) { 97 if (is_written) {
87 map->MarkAsModified(true, GetModifiedTicks()); 98 map->MarkAsModified(true, GetModifiedTicks());
@@ -94,41 +105,49 @@ public:
94 } 105 }
95 } 106 }
96 107
97 return {ToHandle(block), static_cast<u64>(block->GetOffset(cpu_addr))}; 108 return BufferInfo{block->Handle(), block->Offset(cpu_addr), block->Address()};
98 } 109 }
99 110
100 /// Uploads from a host memory. Returns the OpenGL buffer where it's located and its offset. 111 /// Uploads from a host memory. Returns the OpenGL buffer where it's located and its offset.
101 BufferInfo UploadHostMemory(const void* raw_pointer, std::size_t size, 112 BufferInfo UploadHostMemory(const void* raw_pointer, std::size_t size,
102 std::size_t alignment = 4) { 113 std::size_t alignment = 4) {
103 std::lock_guard lock{mutex}; 114 std::lock_guard lock{mutex};
104 return StreamBufferUpload(raw_pointer, size, alignment); 115 return StreamBufferUpload(size, alignment, [raw_pointer, size](u8* dest) {
116 std::memcpy(dest, raw_pointer, size);
117 });
105 } 118 }
106 119
107 void Map(std::size_t max_size) { 120 /// Prepares the buffer cache for data uploading
121 /// @param max_size Maximum number of bytes that will be uploaded
122 /// @return True when a stream buffer invalidation was required, false otherwise
123 bool Map(std::size_t max_size) {
108 std::lock_guard lock{mutex}; 124 std::lock_guard lock{mutex};
109 125
126 bool invalidated;
110 std::tie(buffer_ptr, buffer_offset_base, invalidated) = stream_buffer->Map(max_size, 4); 127 std::tie(buffer_ptr, buffer_offset_base, invalidated) = stream_buffer->Map(max_size, 4);
111 buffer_offset = buffer_offset_base; 128 buffer_offset = buffer_offset_base;
129
130 return invalidated;
112 } 131 }
113 132
114 /// Finishes the upload stream, returns true on bindings invalidation. 133 /// Finishes the upload stream
115 bool Unmap() { 134 void Unmap() {
116 std::lock_guard lock{mutex}; 135 std::lock_guard lock{mutex};
117
118 stream_buffer->Unmap(buffer_offset - buffer_offset_base); 136 stream_buffer->Unmap(buffer_offset - buffer_offset_base);
119 return std::exchange(invalidated, false);
120 } 137 }
121 138
139 /// Function called at the end of each frame, inteded for deferred operations
122 void TickFrame() { 140 void TickFrame() {
123 ++epoch; 141 ++epoch;
142
124 while (!pending_destruction.empty()) { 143 while (!pending_destruction.empty()) {
125 // Delay at least 4 frames before destruction. 144 // Delay at least 4 frames before destruction.
126 // This is due to triple buffering happening on some drivers. 145 // This is due to triple buffering happening on some drivers.
127 static constexpr u64 epochs_to_destroy = 5; 146 static constexpr u64 epochs_to_destroy = 5;
128 if (pending_destruction.front()->GetEpoch() + epochs_to_destroy > epoch) { 147 if (pending_destruction.front()->Epoch() + epochs_to_destroy > epoch) {
129 break; 148 break;
130 } 149 }
131 pending_destruction.pop_front(); 150 pending_destruction.pop();
132 } 151 }
133 } 152 }
134 153
@@ -239,28 +258,16 @@ public:
239 committed_flushes.pop_front(); 258 committed_flushes.pop_front();
240 } 259 }
241 260
242 virtual BufferType GetEmptyBuffer(std::size_t size) = 0; 261 virtual BufferInfo GetEmptyBuffer(std::size_t size) = 0;
243 262
244protected: 263protected:
245 explicit BufferCache(VideoCore::RasterizerInterface& rasterizer, Core::System& system, 264 explicit BufferCache(VideoCore::RasterizerInterface& rasterizer, Core::System& system,
246 std::unique_ptr<StreamBuffer> stream_buffer) 265 std::unique_ptr<StreamBuffer> stream_buffer)
247 : rasterizer{rasterizer}, system{system}, stream_buffer{std::move(stream_buffer)}, 266 : rasterizer{rasterizer}, system{system}, stream_buffer{std::move(stream_buffer)} {}
248 stream_buffer_handle{this->stream_buffer->GetHandle()} {}
249 267
250 ~BufferCache() = default; 268 ~BufferCache() = default;
251 269
252 virtual BufferType ToHandle(const OwnerBuffer& storage) = 0; 270 virtual std::shared_ptr<Buffer> CreateBlock(VAddr cpu_addr, std::size_t size) = 0;
253
254 virtual OwnerBuffer CreateBlock(VAddr cpu_addr, std::size_t size) = 0;
255
256 virtual void UploadBlockData(const OwnerBuffer& buffer, std::size_t offset, std::size_t size,
257 const u8* data) = 0;
258
259 virtual void DownloadBlockData(const OwnerBuffer& buffer, std::size_t offset, std::size_t size,
260 u8* data) = 0;
261
262 virtual void CopyBlock(const OwnerBuffer& src, const OwnerBuffer& dst, std::size_t src_offset,
263 std::size_t dst_offset, std::size_t size) = 0;
264 271
265 virtual BufferInfo ConstBufferUpload(const void* raw_pointer, std::size_t size) { 272 virtual BufferInfo ConstBufferUpload(const void* raw_pointer, std::size_t size) {
266 return {}; 273 return {};
@@ -315,7 +322,7 @@ protected:
315 } 322 }
316 323
317private: 324private:
318 MapInterval* MapAddress(const OwnerBuffer& block, GPUVAddr gpu_addr, VAddr cpu_addr, 325 MapInterval* MapAddress(const Buffer* block, GPUVAddr gpu_addr, VAddr cpu_addr,
319 std::size_t size) { 326 std::size_t size) {
320 const VectorMapInterval overlaps = GetMapsInRange(cpu_addr, size); 327 const VectorMapInterval overlaps = GetMapsInRange(cpu_addr, size);
321 if (overlaps.empty()) { 328 if (overlaps.empty()) {
@@ -323,11 +330,11 @@ private:
323 const VAddr cpu_addr_end = cpu_addr + size; 330 const VAddr cpu_addr_end = cpu_addr + size;
324 if (memory_manager.IsGranularRange(gpu_addr, size)) { 331 if (memory_manager.IsGranularRange(gpu_addr, size)) {
325 u8* host_ptr = memory_manager.GetPointer(gpu_addr); 332 u8* host_ptr = memory_manager.GetPointer(gpu_addr);
326 UploadBlockData(block, block->GetOffset(cpu_addr), size, host_ptr); 333 block->Upload(block->Offset(cpu_addr), size, host_ptr);
327 } else { 334 } else {
328 staging_buffer.resize(size); 335 staging_buffer.resize(size);
329 memory_manager.ReadBlockUnsafe(gpu_addr, staging_buffer.data(), size); 336 memory_manager.ReadBlockUnsafe(gpu_addr, staging_buffer.data(), size);
330 UploadBlockData(block, block->GetOffset(cpu_addr), size, staging_buffer.data()); 337 block->Upload(block->Offset(cpu_addr), size, staging_buffer.data());
331 } 338 }
332 return Register(MapInterval(cpu_addr, cpu_addr_end, gpu_addr)); 339 return Register(MapInterval(cpu_addr, cpu_addr_end, gpu_addr));
333 } 340 }
@@ -370,7 +377,7 @@ private:
370 return map; 377 return map;
371 } 378 }
372 379
373 void UpdateBlock(const OwnerBuffer& block, VAddr start, VAddr end, 380 void UpdateBlock(const Buffer* block, VAddr start, VAddr end,
374 const VectorMapInterval& overlaps) { 381 const VectorMapInterval& overlaps) {
375 const IntervalType base_interval{start, end}; 382 const IntervalType base_interval{start, end};
376 IntervalSet interval_set{}; 383 IntervalSet interval_set{};
@@ -380,13 +387,13 @@ private:
380 interval_set.subtract(subtract); 387 interval_set.subtract(subtract);
381 } 388 }
382 for (auto& interval : interval_set) { 389 for (auto& interval : interval_set) {
383 std::size_t size = interval.upper() - interval.lower(); 390 const std::size_t size = interval.upper() - interval.lower();
384 if (size > 0) { 391 if (size == 0) {
385 staging_buffer.resize(size); 392 continue;
386 system.Memory().ReadBlockUnsafe(interval.lower(), staging_buffer.data(), size);
387 UploadBlockData(block, block->GetOffset(interval.lower()), size,
388 staging_buffer.data());
389 } 393 }
394 staging_buffer.resize(size);
395 system.Memory().ReadBlockUnsafe(interval.lower(), staging_buffer.data(), size);
396 block->Upload(block->Offset(interval.lower()), size, staging_buffer.data());
390 } 397 }
391 } 398 }
392 399
@@ -416,23 +423,27 @@ private:
416 } 423 }
417 424
418 void FlushMap(MapInterval* map) { 425 void FlushMap(MapInterval* map) {
426 const auto it = blocks.find(map->start >> BLOCK_PAGE_BITS);
427 ASSERT_OR_EXECUTE(it != blocks.end(), return;);
428
429 std::shared_ptr<Buffer> block = it->second;
430
419 const std::size_t size = map->end - map->start; 431 const std::size_t size = map->end - map->start;
420 OwnerBuffer block = blocks[map->start >> block_page_bits];
421 staging_buffer.resize(size); 432 staging_buffer.resize(size);
422 DownloadBlockData(block, block->GetOffset(map->start), size, staging_buffer.data()); 433 block->Download(block->Offset(map->start), size, staging_buffer.data());
423 system.Memory().WriteBlockUnsafe(map->start, staging_buffer.data(), size); 434 system.Memory().WriteBlockUnsafe(map->start, staging_buffer.data(), size);
424 map->MarkAsModified(false, 0); 435 map->MarkAsModified(false, 0);
425 } 436 }
426 437
427 BufferInfo StreamBufferUpload(const void* raw_pointer, std::size_t size, 438 template <typename Callable>
428 std::size_t alignment) { 439 BufferInfo StreamBufferUpload(std::size_t size, std::size_t alignment, Callable&& callable) {
429 AlignBuffer(alignment); 440 AlignBuffer(alignment);
430 const std::size_t uploaded_offset = buffer_offset; 441 const std::size_t uploaded_offset = buffer_offset;
431 std::memcpy(buffer_ptr, raw_pointer, size); 442 callable(buffer_ptr);
432 443
433 buffer_ptr += size; 444 buffer_ptr += size;
434 buffer_offset += size; 445 buffer_offset += size;
435 return {stream_buffer_handle, uploaded_offset}; 446 return BufferInfo{stream_buffer->Handle(), uploaded_offset, stream_buffer->Address()};
436 } 447 }
437 448
438 void AlignBuffer(std::size_t alignment) { 449 void AlignBuffer(std::size_t alignment) {
@@ -442,97 +453,89 @@ private:
442 buffer_offset = offset_aligned; 453 buffer_offset = offset_aligned;
443 } 454 }
444 455
445 OwnerBuffer EnlargeBlock(OwnerBuffer buffer) { 456 std::shared_ptr<Buffer> EnlargeBlock(std::shared_ptr<Buffer> buffer) {
446 const std::size_t old_size = buffer->GetSize(); 457 const std::size_t old_size = buffer->Size();
447 const std::size_t new_size = old_size + block_page_size; 458 const std::size_t new_size = old_size + BLOCK_PAGE_SIZE;
448 const VAddr cpu_addr = buffer->GetCpuAddr(); 459 const VAddr cpu_addr = buffer->CpuAddr();
449 OwnerBuffer new_buffer = CreateBlock(cpu_addr, new_size); 460 std::shared_ptr<Buffer> new_buffer = CreateBlock(cpu_addr, new_size);
450 CopyBlock(buffer, new_buffer, 0, 0, old_size); 461 new_buffer->CopyFrom(*buffer, 0, 0, old_size);
451 buffer->SetEpoch(epoch); 462 QueueDestruction(std::move(buffer));
452 pending_destruction.push_back(buffer); 463
453 const VAddr cpu_addr_end = cpu_addr + new_size - 1; 464 const VAddr cpu_addr_end = cpu_addr + new_size - 1;
454 u64 page_start = cpu_addr >> block_page_bits; 465 const u64 page_end = cpu_addr_end >> BLOCK_PAGE_BITS;
455 const u64 page_end = cpu_addr_end >> block_page_bits; 466 for (u64 page_start = cpu_addr >> BLOCK_PAGE_BITS; page_start <= page_end; ++page_start) {
456 while (page_start <= page_end) { 467 blocks.insert_or_assign(page_start, new_buffer);
457 blocks[page_start] = new_buffer;
458 ++page_start;
459 } 468 }
469
460 return new_buffer; 470 return new_buffer;
461 } 471 }
462 472
463 OwnerBuffer MergeBlocks(OwnerBuffer first, OwnerBuffer second) { 473 std::shared_ptr<Buffer> MergeBlocks(std::shared_ptr<Buffer> first,
464 const std::size_t size_1 = first->GetSize(); 474 std::shared_ptr<Buffer> second) {
465 const std::size_t size_2 = second->GetSize(); 475 const std::size_t size_1 = first->Size();
466 const VAddr first_addr = first->GetCpuAddr(); 476 const std::size_t size_2 = second->Size();
467 const VAddr second_addr = second->GetCpuAddr(); 477 const VAddr first_addr = first->CpuAddr();
478 const VAddr second_addr = second->CpuAddr();
468 const VAddr new_addr = std::min(first_addr, second_addr); 479 const VAddr new_addr = std::min(first_addr, second_addr);
469 const std::size_t new_size = size_1 + size_2; 480 const std::size_t new_size = size_1 + size_2;
470 OwnerBuffer new_buffer = CreateBlock(new_addr, new_size); 481
471 CopyBlock(first, new_buffer, 0, new_buffer->GetOffset(first_addr), size_1); 482 std::shared_ptr<Buffer> new_buffer = CreateBlock(new_addr, new_size);
472 CopyBlock(second, new_buffer, 0, new_buffer->GetOffset(second_addr), size_2); 483 new_buffer->CopyFrom(*first, 0, new_buffer->Offset(first_addr), size_1);
473 first->SetEpoch(epoch); 484 new_buffer->CopyFrom(*second, 0, new_buffer->Offset(second_addr), size_2);
474 second->SetEpoch(epoch); 485 QueueDestruction(std::move(first));
475 pending_destruction.push_back(first); 486 QueueDestruction(std::move(second));
476 pending_destruction.push_back(second); 487
477 const VAddr cpu_addr_end = new_addr + new_size - 1; 488 const VAddr cpu_addr_end = new_addr + new_size - 1;
478 u64 page_start = new_addr >> block_page_bits; 489 const u64 page_end = cpu_addr_end >> BLOCK_PAGE_BITS;
479 const u64 page_end = cpu_addr_end >> block_page_bits; 490 for (u64 page_start = new_addr >> BLOCK_PAGE_BITS; page_start <= page_end; ++page_start) {
480 while (page_start <= page_end) { 491 blocks.insert_or_assign(page_start, new_buffer);
481 blocks[page_start] = new_buffer;
482 ++page_start;
483 } 492 }
484 return new_buffer; 493 return new_buffer;
485 } 494 }
486 495
487 OwnerBuffer GetBlock(const VAddr cpu_addr, const std::size_t size) { 496 Buffer* GetBlock(VAddr cpu_addr, std::size_t size) {
488 OwnerBuffer found; 497 std::shared_ptr<Buffer> found;
498
489 const VAddr cpu_addr_end = cpu_addr + size - 1; 499 const VAddr cpu_addr_end = cpu_addr + size - 1;
490 u64 page_start = cpu_addr >> block_page_bits; 500 const u64 page_end = cpu_addr_end >> BLOCK_PAGE_BITS;
491 const u64 page_end = cpu_addr_end >> block_page_bits; 501 for (u64 page_start = cpu_addr >> BLOCK_PAGE_BITS; page_start <= page_end; ++page_start) {
492 while (page_start <= page_end) {
493 auto it = blocks.find(page_start); 502 auto it = blocks.find(page_start);
494 if (it == blocks.end()) { 503 if (it == blocks.end()) {
495 if (found) { 504 if (found) {
496 found = EnlargeBlock(found); 505 found = EnlargeBlock(found);
497 } else { 506 continue;
498 const VAddr start_addr = (page_start << block_page_bits);
499 found = CreateBlock(start_addr, block_page_size);
500 blocks[page_start] = found;
501 }
502 } else {
503 if (found) {
504 if (found == it->second) {
505 ++page_start;
506 continue;
507 }
508 found = MergeBlocks(found, it->second);
509 } else {
510 found = it->second;
511 } 507 }
508 const VAddr start_addr = page_start << BLOCK_PAGE_BITS;
509 found = CreateBlock(start_addr, BLOCK_PAGE_SIZE);
510 blocks.insert_or_assign(page_start, found);
511 continue;
512 }
513 if (!found) {
514 found = it->second;
515 continue;
516 }
517 if (found != it->second) {
518 found = MergeBlocks(std::move(found), it->second);
512 } 519 }
513 ++page_start;
514 } 520 }
515 return found; 521 return found.get();
516 } 522 }
517 523
518 void MarkRegionAsWritten(const VAddr start, const VAddr end) { 524 void MarkRegionAsWritten(VAddr start, VAddr end) {
519 u64 page_start = start >> write_page_bit; 525 const u64 page_end = end >> WRITE_PAGE_BIT;
520 const u64 page_end = end >> write_page_bit; 526 for (u64 page_start = start >> WRITE_PAGE_BIT; page_start <= page_end; ++page_start) {
521 while (page_start <= page_end) {
522 auto it = written_pages.find(page_start); 527 auto it = written_pages.find(page_start);
523 if (it != written_pages.end()) { 528 if (it != written_pages.end()) {
524 it->second = it->second + 1; 529 it->second = it->second + 1;
525 } else { 530 } else {
526 written_pages[page_start] = 1; 531 written_pages.insert_or_assign(page_start, 1);
527 } 532 }
528 ++page_start;
529 } 533 }
530 } 534 }
531 535
532 void UnmarkRegionAsWritten(const VAddr start, const VAddr end) { 536 void UnmarkRegionAsWritten(VAddr start, VAddr end) {
533 u64 page_start = start >> write_page_bit; 537 const u64 page_end = end >> WRITE_PAGE_BIT;
534 const u64 page_end = end >> write_page_bit; 538 for (u64 page_start = start >> WRITE_PAGE_BIT; page_start <= page_end; ++page_start) {
535 while (page_start <= page_end) {
536 auto it = written_pages.find(page_start); 539 auto it = written_pages.find(page_start);
537 if (it != written_pages.end()) { 540 if (it != written_pages.end()) {
538 if (it->second > 1) { 541 if (it->second > 1) {
@@ -541,22 +544,24 @@ private:
541 written_pages.erase(it); 544 written_pages.erase(it);
542 } 545 }
543 } 546 }
544 ++page_start;
545 } 547 }
546 } 548 }
547 549
548 bool IsRegionWritten(const VAddr start, const VAddr end) const { 550 bool IsRegionWritten(VAddr start, VAddr end) const {
549 u64 page_start = start >> write_page_bit; 551 const u64 page_end = end >> WRITE_PAGE_BIT;
550 const u64 page_end = end >> write_page_bit; 552 for (u64 page_start = start >> WRITE_PAGE_BIT; page_start <= page_end; ++page_start) {
551 while (page_start <= page_end) {
552 if (written_pages.count(page_start) > 0) { 553 if (written_pages.count(page_start) > 0) {
553 return true; 554 return true;
554 } 555 }
555 ++page_start;
556 } 556 }
557 return false; 557 return false;
558 } 558 }
559 559
560 void QueueDestruction(std::shared_ptr<Buffer> buffer) {
561 buffer->SetEpoch(epoch);
562 pending_destruction.push(std::move(buffer));
563 }
564
560 void MarkForAsyncFlush(MapInterval* map) { 565 void MarkForAsyncFlush(MapInterval* map) {
561 if (!uncommitted_flushes) { 566 if (!uncommitted_flushes) {
562 uncommitted_flushes = std::make_shared<std::unordered_set<MapInterval*>>(); 567 uncommitted_flushes = std::make_shared<std::unordered_set<MapInterval*>>();
@@ -568,9 +573,7 @@ private:
568 Core::System& system; 573 Core::System& system;
569 574
570 std::unique_ptr<StreamBuffer> stream_buffer; 575 std::unique_ptr<StreamBuffer> stream_buffer;
571 BufferType stream_buffer_handle{}; 576 BufferType stream_buffer_handle;
572
573 bool invalidated = false;
574 577
575 u8* buffer_ptr = nullptr; 578 u8* buffer_ptr = nullptr;
576 u64 buffer_offset = 0; 579 u64 buffer_offset = 0;
@@ -580,18 +583,15 @@ private:
580 boost::intrusive::set<MapInterval, boost::intrusive::compare<MapIntervalCompare>> 583 boost::intrusive::set<MapInterval, boost::intrusive::compare<MapIntervalCompare>>
581 mapped_addresses; 584 mapped_addresses;
582 585
583 static constexpr u64 write_page_bit = 11;
584 std::unordered_map<u64, u32> written_pages; 586 std::unordered_map<u64, u32> written_pages;
587 std::unordered_map<u64, std::shared_ptr<Buffer>> blocks;
585 588
586 static constexpr u64 block_page_bits = 21; 589 std::queue<std::shared_ptr<Buffer>> pending_destruction;
587 static constexpr u64 block_page_size = 1ULL << block_page_bits;
588 std::unordered_map<u64, OwnerBuffer> blocks;
589
590 std::list<OwnerBuffer> pending_destruction;
591 u64 epoch = 0; 590 u64 epoch = 0;
592 u64 modified_ticks = 0; 591 u64 modified_ticks = 0;
593 592
594 std::vector<u8> staging_buffer; 593 std::vector<u8> staging_buffer;
594
595 std::list<MapInterval*> marked_for_unregister; 595 std::list<MapInterval*> marked_for_unregister;
596 596
597 std::shared_ptr<std::unordered_set<MapInterval*>> uncommitted_flushes; 597 std::shared_ptr<std::unordered_set<MapInterval*>> uncommitted_flushes;