diff options
Diffstat (limited to 'src/video_core/buffer_cache')
| -rw-r--r-- | src/video_core/buffer_cache/buffer_block.h | 27 | ||||
| -rw-r--r-- | src/video_core/buffer_cache/buffer_cache.h | 272 |
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 | ||
| 16 | class BufferBlock { | 16 | class BufferBlock { |
| 17 | public: | 17 | public: |
| 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 | ||
| 55 | protected: | 55 | protected: |
| 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 | ||
| 61 | private: | 60 | private: |
| 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 | ||
| 31 | namespace VideoCommon { | 31 | namespace VideoCommon { |
| 32 | 32 | ||
| 33 | template <typename OwnerBuffer, typename BufferType, typename StreamBuffer> | 33 | template <typename Buffer, typename BufferType, typename StreamBuffer> |
| 34 | class BufferCache { | 34 | class 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 | |||
| 39 | public: | 43 | public: |
| 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 | ||
| 244 | protected: | 263 | protected: |
| 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 | ||
| 317 | private: | 324 | private: |
| 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; |