summaryrefslogtreecommitdiff
path: root/src/video_core/texture_cache
diff options
context:
space:
mode:
Diffstat (limited to 'src/video_core/texture_cache')
-rw-r--r--src/video_core/texture_cache/texture_cache.h95
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,