diff options
Diffstat (limited to 'src/video_core/buffer_cache')
| -rw-r--r-- | src/video_core/buffer_cache/buffer_cache.h | 227 | ||||
| -rw-r--r-- | src/video_core/buffer_cache/map_interval.cpp | 33 | ||||
| -rw-r--r-- | src/video_core/buffer_cache/map_interval.h | 133 |
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 | ||
| 30 | namespace VideoCommon { | 31 | namespace VideoCommon { |
| 31 | 32 | ||
| 32 | using MapInterval = std::shared_ptr<MapIntervalBase>; | ||
| 33 | |||
| 34 | template <typename OwnerBuffer, typename BufferType, typename StreamBuffer> | 33 | template <typename OwnerBuffer, typename BufferType, typename StreamBuffer> |
| 35 | class BufferCache { | 34 | class 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 | |||
| 36 | public: | 39 | public: |
| 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 | ||
| 311 | private: | 317 | private: |
| 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 | |||
| 12 | namespace VideoCommon { | ||
| 13 | |||
| 14 | MapIntervalAllocator::MapIntervalAllocator() { | ||
| 15 | FillFreeList(first_chunk); | ||
| 16 | } | ||
| 17 | |||
| 18 | MapIntervalAllocator::~MapIntervalAllocator() = default; | ||
| 19 | |||
| 20 | void MapIntervalAllocator::AllocateNewChunk() { | ||
| 21 | *new_chunk = std::make_unique<Chunk>(); | ||
| 22 | FillFreeList(**new_chunk); | ||
| 23 | new_chunk = &(*new_chunk)->next; | ||
| 24 | } | ||
| 25 | |||
| 26 | void 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 | ||
| 10 | namespace VideoCommon { | 17 | namespace VideoCommon { |
| 11 | 18 | ||
| 12 | class MapIntervalBase { | 19 | struct MapInterval : public boost::intrusive::set_base_hook<boost::intrusive::optimize_size<true>> { |
| 13 | public: | 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 { | 52 | struct 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 { | 58 | class MapIntervalAllocator { |
| 66 | return start; | 59 | public: |
| 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 { | 76 | private: |
| 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 | ||
| 94 | private: | 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 |