diff options
| author | 2019-05-13 22:59:18 -0400 | |
|---|---|---|
| committer | 2019-06-20 21:36:12 -0300 | |
| commit | 6162cb922e67c6c529fb17a91da726fdf3444a50 (patch) | |
| tree | e37c3badd8c18cd426c551f712c01c5a15dfab83 /src/video_core/texture_cache | |
| parent | texture_cache: Try to Reconstruct Surface on bigger than overlap. (diff) | |
| download | yuzu-6162cb922e67c6c529fb17a91da726fdf3444a50.tar.gz yuzu-6162cb922e67c6c529fb17a91da726fdf3444a50.tar.xz yuzu-6162cb922e67c6c529fb17a91da726fdf3444a50.zip | |
texture_cache: Document the most important methods.
Diffstat (limited to 'src/video_core/texture_cache')
| -rw-r--r-- | src/video_core/texture_cache/texture_cache.h | 95 |
1 files changed, 87 insertions, 8 deletions
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 38b56475f..04e9528b8 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h | |||
| @@ -64,6 +64,10 @@ public: | |||
| 64 | } | 64 | } |
| 65 | } | 65 | } |
| 66 | 66 | ||
| 67 | /** | ||
| 68 | * `Guard` guarantees that rendertargets don't unregister themselves if the | ||
| 69 | * collide. Protection is currently only done on 3D slices. | ||
| 70 | **/ | ||
| 67 | void Guard(bool new_guard) { | 71 | void Guard(bool new_guard) { |
| 68 | guard_cache = new_guard; | 72 | guard_cache = new_guard; |
| 69 | } | 73 | } |
| @@ -293,6 +297,14 @@ private: | |||
| 293 | BufferCopy = 3, | 297 | BufferCopy = 3, |
| 294 | }; | 298 | }; |
| 295 | 299 | ||
| 300 | /** | ||
| 301 | * `PickStrategy` takes care of selecting a proper strategy to deal with a texture recycle. | ||
| 302 | * @param overlaps, the overlapping surfaces registered in the cache. | ||
| 303 | * @param params, the paremeters on the new surface. | ||
| 304 | * @param gpu_addr, the starting address of the new surface. | ||
| 305 | * @param untopological, tells the recycler that the texture has no way to match the overlaps | ||
| 306 | * due to topological reasons. | ||
| 307 | **/ | ||
| 296 | RecycleStrategy PickStrategy(std::vector<TSurface>& overlaps, const SurfaceParams& params, | 308 | RecycleStrategy PickStrategy(std::vector<TSurface>& overlaps, const SurfaceParams& params, |
| 297 | const GPUVAddr gpu_addr, const bool untopological) { | 309 | const GPUVAddr gpu_addr, const bool untopological) { |
| 298 | if (Settings::values.use_accurate_gpu_emulation) { | 310 | if (Settings::values.use_accurate_gpu_emulation) { |
| @@ -315,6 +327,18 @@ private: | |||
| 315 | return RecycleStrategy::Ignore; | 327 | return RecycleStrategy::Ignore; |
| 316 | } | 328 | } |
| 317 | 329 | ||
| 330 | /** | ||
| 331 | * `RecycleSurface` es a method we use to decide what to do with textures we can't resolve in | ||
| 332 | *the cache It has 2 implemented strategies: Ignore and Flush. Ignore just unregisters all the | ||
| 333 | *overlaps and loads the new texture. Flush, flushes all the overlaps into memory and loads the | ||
| 334 | *new surface from that data. | ||
| 335 | * @param overlaps, the overlapping surfaces registered in the cache. | ||
| 336 | * @param params, the paremeters on the new surface. | ||
| 337 | * @param gpu_addr, the starting address of the new surface. | ||
| 338 | * @param preserve_contents, tells if the new surface should be loaded from meory or left blank | ||
| 339 | * @param untopological, tells the recycler that the texture has no way to match the overlaps | ||
| 340 | * due to topological reasons. | ||
| 341 | **/ | ||
| 318 | std::pair<TSurface, TView> RecycleSurface(std::vector<TSurface>& overlaps, | 342 | std::pair<TSurface, TView> RecycleSurface(std::vector<TSurface>& overlaps, |
| 319 | const SurfaceParams& params, const GPUVAddr gpu_addr, | 343 | const SurfaceParams& params, const GPUVAddr gpu_addr, |
| 320 | const bool preserve_contents, | 344 | const bool preserve_contents, |
| @@ -343,6 +367,12 @@ private: | |||
| 343 | } | 367 | } |
| 344 | } | 368 | } |
| 345 | 369 | ||
| 370 | /** | ||
| 371 | * `RebuildSurface` this method takes a single surface and recreates into another that | ||
| 372 | * may differ in format, target or width alingment. | ||
| 373 | * @param current_surface, the registered surface in the cache which we want to convert. | ||
| 374 | * @param params, the new surface params which we'll use to recreate the surface. | ||
| 375 | **/ | ||
| 346 | std::pair<TSurface, TView> RebuildSurface(TSurface current_surface, | 376 | std::pair<TSurface, TView> RebuildSurface(TSurface current_surface, |
| 347 | const SurfaceParams& params) { | 377 | const SurfaceParams& params) { |
| 348 | const auto gpu_addr = current_surface->GetGpuAddr(); | 378 | const auto gpu_addr = current_surface->GetGpuAddr(); |
| @@ -357,6 +387,14 @@ private: | |||
| 357 | return {new_surface, new_surface->GetMainView()}; | 387 | return {new_surface, new_surface->GetMainView()}; |
| 358 | } | 388 | } |
| 359 | 389 | ||
| 390 | /** | ||
| 391 | * `ManageStructuralMatch` this method takes a single surface and checks with the new surface's | ||
| 392 | * params if it's an exact match, we return the main view of the registered surface. If it's | ||
| 393 | * formats don't match, we rebuild the surface. We call this last method a `Mirage`. If formats | ||
| 394 | * match but the targets don't, we create an overview View of the registered surface. | ||
| 395 | * @param current_surface, the registered surface in the cache which we want to convert. | ||
| 396 | * @param params, the new surface params which we want to check. | ||
| 397 | **/ | ||
| 360 | std::pair<TSurface, TView> ManageStructuralMatch(TSurface current_surface, | 398 | std::pair<TSurface, TView> ManageStructuralMatch(TSurface current_surface, |
| 361 | const SurfaceParams& params) { | 399 | const SurfaceParams& params) { |
| 362 | const bool is_mirage = !current_surface->MatchFormat(params.pixel_format); | 400 | const bool is_mirage = !current_surface->MatchFormat(params.pixel_format); |
| @@ -370,10 +408,18 @@ private: | |||
| 370 | return {current_surface, current_surface->EmplaceOverview(params)}; | 408 | return {current_surface, current_surface->EmplaceOverview(params)}; |
| 371 | } | 409 | } |
| 372 | 410 | ||
| 373 | std::optional<std::pair<TSurface, TView>> ReconstructSurface(std::vector<TSurface>& overlaps, | 411 | /** |
| 374 | const SurfaceParams& params, | 412 | * `TryReconstructSurface` unlike `RebuildSurface` where we know the registered surface |
| 375 | const GPUVAddr gpu_addr, | 413 | * matches the candidate in some way, we got no guarantess here. We try to see if the overlaps |
| 376 | const u8* host_ptr) { | 414 | * are sublayers/mipmaps of the new surface, if they all match we end up recreating a surface |
| 415 | * for them, else we return nothing. | ||
| 416 | * @param overlaps, the overlapping surfaces registered in the cache. | ||
| 417 | * @param params, the paremeters on the new surface. | ||
| 418 | * @param gpu_addr, the starting address of the new surface. | ||
| 419 | **/ | ||
| 420 | std::optional<std::pair<TSurface, TView>> TryReconstructSurface(std::vector<TSurface>& overlaps, | ||
| 421 | const SurfaceParams& params, | ||
| 422 | const GPUVAddr gpu_addr) { | ||
| 377 | if (params.target == SurfaceTarget::Texture3D) { | 423 | if (params.target == SurfaceTarget::Texture3D) { |
| 378 | return {}; | 424 | return {}; |
| 379 | } | 425 | } |
| @@ -412,12 +458,30 @@ private: | |||
| 412 | return {{new_surface, new_surface->GetMainView()}}; | 458 | return {{new_surface, new_surface->GetMainView()}}; |
| 413 | } | 459 | } |
| 414 | 460 | ||
| 461 | /** | ||
| 462 | * `GetSurface` gets the starting address and parameters of a candidate surface and tries | ||
| 463 | * to find a matching surface within the cache. This is done in 3 big steps. The first is to | ||
| 464 | * check the 1st Level Cache in order to find an exact match, if we fail, we move to step 2. | ||
| 465 | * Step 2 is checking if there are any overlaps at all, if none, we just load the texture from | ||
| 466 | * memory else we move to step 3. Step 3 consists on figuring the relationship between the | ||
| 467 | * candidate texture and the overlaps. We divide the scenarios depending if there's 1 or many | ||
| 468 | * overlaps. If there's many, we just try to reconstruct a new surface out of them based on the | ||
| 469 | * candidate's parameters, if we fail, we recycle. When there's only 1 overlap then we have to | ||
| 470 | * check if the candidate is a view (layer/mipmap) of the overlap or if the registered surface | ||
| 471 | * is a mipmap/layer of the candidate. In this last case we reconstruct a new surface. | ||
| 472 | * @param gpu_addr, the starting address of the candidate surface. | ||
| 473 | * @param params, the paremeters on the candidate surface. | ||
| 474 | * @param preserve_contents, tells if the new surface should be loaded from meory or left blank. | ||
| 475 | **/ | ||
| 415 | std::pair<TSurface, TView> GetSurface(const GPUVAddr gpu_addr, const SurfaceParams& params, | 476 | std::pair<TSurface, TView> GetSurface(const GPUVAddr gpu_addr, const SurfaceParams& params, |
| 416 | bool preserve_contents) { | 477 | bool preserve_contents) { |
| 417 | 478 | ||
| 418 | const auto host_ptr{memory_manager->GetPointer(gpu_addr)}; | 479 | const auto host_ptr{memory_manager->GetPointer(gpu_addr)}; |
| 419 | const auto cache_addr{ToCacheAddr(host_ptr)}; | 480 | const auto cache_addr{ToCacheAddr(host_ptr)}; |
| 420 | 481 | ||
| 482 | // Step 1 | ||
| 483 | // Check Level 1 Cache for a fast structural match. If candidate surface | ||
| 484 | // matches at certain level we are pretty much done. | ||
| 421 | if (l1_cache.count(cache_addr) > 0) { | 485 | if (l1_cache.count(cache_addr) > 0) { |
| 422 | TSurface current_surface = l1_cache[cache_addr]; | 486 | TSurface current_surface = l1_cache[cache_addr]; |
| 423 | if (!current_surface->MatchesTopology(params)) { | 487 | if (!current_surface->MatchesTopology(params)) { |
| @@ -437,31 +501,43 @@ private: | |||
| 437 | } | 501 | } |
| 438 | } | 502 | } |
| 439 | 503 | ||
| 504 | // Step 2 | ||
| 505 | // Obtain all possible overlaps in the memory region | ||
| 440 | const std::size_t candidate_size = params.GetGuestSizeInBytes(); | 506 | const std::size_t candidate_size = params.GetGuestSizeInBytes(); |
| 441 | auto overlaps{GetSurfacesInRegion(cache_addr, candidate_size)}; | 507 | auto overlaps{GetSurfacesInRegion(cache_addr, candidate_size)}; |
| 442 | 508 | ||
| 509 | // If none are found, we are done. we just load the surface and create it. | ||
| 443 | if (overlaps.empty()) { | 510 | if (overlaps.empty()) { |
| 444 | return InitializeSurface(gpu_addr, params, preserve_contents); | 511 | return InitializeSurface(gpu_addr, params, preserve_contents); |
| 445 | } | 512 | } |
| 446 | 513 | ||
| 514 | // Step 3 | ||
| 515 | // Now we need to figure the relationship between the texture and its overlaps | ||
| 516 | // we do a topological test to ensure we can find some relationship. If it fails | ||
| 517 | // inmediatly recycle the texture | ||
| 447 | for (auto surface : overlaps) { | 518 | for (auto surface : overlaps) { |
| 448 | if (!surface->MatchesTopology(params)) { | 519 | if (!surface->MatchesTopology(params)) { |
| 449 | return RecycleSurface(overlaps, params, gpu_addr, preserve_contents, true); | 520 | return RecycleSurface(overlaps, params, gpu_addr, preserve_contents, true); |
| 450 | } | 521 | } |
| 451 | } | 522 | } |
| 452 | 523 | ||
| 524 | // Split cases between 1 overlap or many. | ||
| 453 | if (overlaps.size() == 1) { | 525 | if (overlaps.size() == 1) { |
| 454 | TSurface current_surface = overlaps[0]; | 526 | TSurface current_surface = overlaps[0]; |
| 527 | // First check if the surface is within the overlap. If not, it means | ||
| 528 | // two things either the candidate surface is a supertexture of the overlap | ||
| 529 | // or they don't match in any known way. | ||
| 455 | if (!current_surface->IsInside(gpu_addr, gpu_addr + candidate_size)) { | 530 | if (!current_surface->IsInside(gpu_addr, gpu_addr + candidate_size)) { |
| 456 | if (current_surface->GetGpuAddr() == gpu_addr) { | 531 | if (current_surface->GetGpuAddr() == gpu_addr) { |
| 457 | std::optional<std::pair<TSurface, TView>> view = | 532 | std::optional<std::pair<TSurface, TView>> view = |
| 458 | ReconstructSurface(overlaps, params, gpu_addr, host_ptr); | 533 | TryReconstructSurface(overlaps, params, gpu_addr); |
| 459 | if (view.has_value()) { | 534 | if (view.has_value()) { |
| 460 | return *view; | 535 | return *view; |
| 461 | } | 536 | } |
| 462 | } | 537 | } |
| 463 | return RecycleSurface(overlaps, params, gpu_addr, preserve_contents, false); | 538 | return RecycleSurface(overlaps, params, gpu_addr, preserve_contents, false); |
| 464 | } | 539 | } |
| 540 | // Now we check if the candidate is a mipmap/layer of the overlap | ||
| 465 | std::optional<TView> view = | 541 | std::optional<TView> view = |
| 466 | current_surface->EmplaceView(params, gpu_addr, candidate_size); | 542 | current_surface->EmplaceView(params, gpu_addr, candidate_size); |
| 467 | if (view.has_value()) { | 543 | if (view.has_value()) { |
| @@ -472,15 +548,18 @@ private: | |||
| 472 | } | 548 | } |
| 473 | return {current_surface, *view}; | 549 | return {current_surface, *view}; |
| 474 | } | 550 | } |
| 475 | return RecycleSurface(overlaps, params, gpu_addr, preserve_contents, false); | ||
| 476 | } else { | 551 | } else { |
| 552 | // If there are many overlaps, odds are they are subtextures of the candidate | ||
| 553 | // surface. We try to construct a new surface based on the candidate parameters, | ||
| 554 | // using the overlaps. If a single overlap fails, this will fail. | ||
| 477 | std::optional<std::pair<TSurface, TView>> view = | 555 | std::optional<std::pair<TSurface, TView>> view = |
| 478 | ReconstructSurface(overlaps, params, gpu_addr, host_ptr); | 556 | TryReconstructSurface(overlaps, params, gpu_addr); |
| 479 | if (view.has_value()) { | 557 | if (view.has_value()) { |
| 480 | return *view; | 558 | return *view; |
| 481 | } | 559 | } |
| 482 | return RecycleSurface(overlaps, params, gpu_addr, preserve_contents, false); | ||
| 483 | } | 560 | } |
| 561 | // We failed all the tests, recycle the overlaps into a new texture. | ||
| 562 | return RecycleSurface(overlaps, params, gpu_addr, preserve_contents, false); | ||
| 484 | } | 563 | } |
| 485 | 564 | ||
| 486 | std::pair<TSurface, TView> InitializeSurface(GPUVAddr gpu_addr, const SurfaceParams& params, | 565 | std::pair<TSurface, TView> InitializeSurface(GPUVAddr gpu_addr, const SurfaceParams& params, |