summaryrefslogtreecommitdiffstats
path: root/src/video_core/texture_cache
diff options
context:
space:
mode:
authorFernando Sahmkow <fsahmkow27@gmail.com>2019-05-14 04:59:18 +0200
committerReinUsesLisp <reinuseslisp@airmail.cc>2019-06-21 02:36:12 +0200
commit6162cb922e67c6c529fb17a91da726fdf3444a50 (patch)
treee37c3badd8c18cd426c551f712c01c5a15dfab83 /src/video_core/texture_cache
parenttexture_cache: Try to Reconstruct Surface on bigger than overlap. (diff)
downloadyuzu-6162cb922e67c6c529fb17a91da726fdf3444a50.tar
yuzu-6162cb922e67c6c529fb17a91da726fdf3444a50.tar.gz
yuzu-6162cb922e67c6c529fb17a91da726fdf3444a50.tar.bz2
yuzu-6162cb922e67c6c529fb17a91da726fdf3444a50.tar.lz
yuzu-6162cb922e67c6c529fb17a91da726fdf3444a50.tar.xz
yuzu-6162cb922e67c6c529fb17a91da726fdf3444a50.tar.zst
yuzu-6162cb922e67c6c529fb17a91da726fdf3444a50.zip
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:
}
}
+ /**
+ * `Guard` guarantees that rendertargets don't unregister themselves if the
+ * collide. Protection is currently only done on 3D slices.
+ **/
void Guard(bool new_guard) {
guard_cache = new_guard;
}
@@ -293,6 +297,14 @@ private:
BufferCopy = 3,
};
+ /**
+ * `PickStrategy` takes care of selecting a proper strategy to deal with a texture recycle.
+ * @param overlaps, the overlapping surfaces registered in the cache.
+ * @param params, the paremeters on the new surface.
+ * @param gpu_addr, the starting address of the new surface.
+ * @param untopological, tells the recycler that the texture has no way to match the overlaps
+ * due to topological reasons.
+ **/
RecycleStrategy PickStrategy(std::vector<TSurface>& overlaps, const SurfaceParams& params,
const GPUVAddr gpu_addr, const bool untopological) {
if (Settings::values.use_accurate_gpu_emulation) {
@@ -315,6 +327,18 @@ private:
return RecycleStrategy::Ignore;
}
+ /**
+ * `RecycleSurface` es a method we use to decide what to do with textures we can't resolve in
+ *the cache It has 2 implemented strategies: Ignore and Flush. Ignore just unregisters all the
+ *overlaps and loads the new texture. Flush, flushes all the overlaps into memory and loads the
+ *new surface from that data.
+ * @param overlaps, the overlapping surfaces registered in the cache.
+ * @param params, the paremeters on the new surface.
+ * @param gpu_addr, the starting address of the new surface.
+ * @param preserve_contents, tells if the new surface should be loaded from meory or left blank
+ * @param untopological, tells the recycler that the texture has no way to match the overlaps
+ * due to topological reasons.
+ **/
std::pair<TSurface, TView> RecycleSurface(std::vector<TSurface>& overlaps,
const SurfaceParams& params, const GPUVAddr gpu_addr,
const bool preserve_contents,
@@ -343,6 +367,12 @@ private:
}
}
+ /**
+ * `RebuildSurface` this method takes a single surface and recreates into another that
+ * may differ in format, target or width alingment.
+ * @param current_surface, the registered surface in the cache which we want to convert.
+ * @param params, the new surface params which we'll use to recreate the surface.
+ **/
std::pair<TSurface, TView> RebuildSurface(TSurface current_surface,
const SurfaceParams& params) {
const auto gpu_addr = current_surface->GetGpuAddr();
@@ -357,6 +387,14 @@ private:
return {new_surface, new_surface->GetMainView()};
}
+ /**
+ * `ManageStructuralMatch` this method takes a single surface and checks with the new surface's
+ * params if it's an exact match, we return the main view of the registered surface. If it's
+ * formats don't match, we rebuild the surface. We call this last method a `Mirage`. If formats
+ * match but the targets don't, we create an overview View of the registered surface.
+ * @param current_surface, the registered surface in the cache which we want to convert.
+ * @param params, the new surface params which we want to check.
+ **/
std::pair<TSurface, TView> ManageStructuralMatch(TSurface current_surface,
const SurfaceParams& params) {
const bool is_mirage = !current_surface->MatchFormat(params.pixel_format);
@@ -370,10 +408,18 @@ private:
return {current_surface, current_surface->EmplaceOverview(params)};
}
- std::optional<std::pair<TSurface, TView>> ReconstructSurface(std::vector<TSurface>& overlaps,
- const SurfaceParams& params,
- const GPUVAddr gpu_addr,
- const u8* host_ptr) {
+ /**
+ * `TryReconstructSurface` unlike `RebuildSurface` where we know the registered surface
+ * matches the candidate in some way, we got no guarantess here. We try to see if the overlaps
+ * are sublayers/mipmaps of the new surface, if they all match we end up recreating a surface
+ * for them, else we return nothing.
+ * @param overlaps, the overlapping surfaces registered in the cache.
+ * @param params, the paremeters on the new surface.
+ * @param gpu_addr, the starting address of the new surface.
+ **/
+ std::optional<std::pair<TSurface, TView>> TryReconstructSurface(std::vector<TSurface>& overlaps,
+ const SurfaceParams& params,
+ const GPUVAddr gpu_addr) {
if (params.target == SurfaceTarget::Texture3D) {
return {};
}
@@ -412,12 +458,30 @@ private:
return {{new_surface, new_surface->GetMainView()}};
}
+ /**
+ * `GetSurface` gets the starting address and parameters of a candidate surface and tries
+ * to find a matching surface within the cache. This is done in 3 big steps. The first is to
+ * check the 1st Level Cache in order to find an exact match, if we fail, we move to step 2.
+ * Step 2 is checking if there are any overlaps at all, if none, we just load the texture from
+ * memory else we move to step 3. Step 3 consists on figuring the relationship between the
+ * candidate texture and the overlaps. We divide the scenarios depending if there's 1 or many
+ * overlaps. If there's many, we just try to reconstruct a new surface out of them based on the
+ * candidate's parameters, if we fail, we recycle. When there's only 1 overlap then we have to
+ * check if the candidate is a view (layer/mipmap) of the overlap or if the registered surface
+ * is a mipmap/layer of the candidate. In this last case we reconstruct a new surface.
+ * @param gpu_addr, the starting address of the candidate surface.
+ * @param params, the paremeters on the candidate surface.
+ * @param preserve_contents, tells if the new surface should be loaded from meory or left blank.
+ **/
std::pair<TSurface, TView> GetSurface(const GPUVAddr gpu_addr, const SurfaceParams& params,
bool preserve_contents) {
const auto host_ptr{memory_manager->GetPointer(gpu_addr)};
const auto cache_addr{ToCacheAddr(host_ptr)};
+ // Step 1
+ // Check Level 1 Cache for a fast structural match. If candidate surface
+ // matches at certain level we are pretty much done.
if (l1_cache.count(cache_addr) > 0) {
TSurface current_surface = l1_cache[cache_addr];
if (!current_surface->MatchesTopology(params)) {
@@ -437,31 +501,43 @@ private:
}
}
+ // Step 2
+ // Obtain all possible overlaps in the memory region
const std::size_t candidate_size = params.GetGuestSizeInBytes();
auto overlaps{GetSurfacesInRegion(cache_addr, candidate_size)};
+ // If none are found, we are done. we just load the surface and create it.
if (overlaps.empty()) {
return InitializeSurface(gpu_addr, params, preserve_contents);
}
+ // Step 3
+ // Now we need to figure the relationship between the texture and its overlaps
+ // we do a topological test to ensure we can find some relationship. If it fails
+ // inmediatly recycle the texture
for (auto surface : overlaps) {
if (!surface->MatchesTopology(params)) {
return RecycleSurface(overlaps, params, gpu_addr, preserve_contents, true);
}
}
+ // Split cases between 1 overlap or many.
if (overlaps.size() == 1) {
TSurface current_surface = overlaps[0];
+ // First check if the surface is within the overlap. If not, it means
+ // two things either the candidate surface is a supertexture of the overlap
+ // or they don't match in any known way.
if (!current_surface->IsInside(gpu_addr, gpu_addr + candidate_size)) {
if (current_surface->GetGpuAddr() == gpu_addr) {
std::optional<std::pair<TSurface, TView>> view =
- ReconstructSurface(overlaps, params, gpu_addr, host_ptr);
+ TryReconstructSurface(overlaps, params, gpu_addr);
if (view.has_value()) {
return *view;
}
}
return RecycleSurface(overlaps, params, gpu_addr, preserve_contents, false);
}
+ // Now we check if the candidate is a mipmap/layer of the overlap
std::optional<TView> view =
current_surface->EmplaceView(params, gpu_addr, candidate_size);
if (view.has_value()) {
@@ -472,15 +548,18 @@ private:
}
return {current_surface, *view};
}
- return RecycleSurface(overlaps, params, gpu_addr, preserve_contents, false);
} else {
+ // If there are many overlaps, odds are they are subtextures of the candidate
+ // surface. We try to construct a new surface based on the candidate parameters,
+ // using the overlaps. If a single overlap fails, this will fail.
std::optional<std::pair<TSurface, TView>> view =
- ReconstructSurface(overlaps, params, gpu_addr, host_ptr);
+ TryReconstructSurface(overlaps, params, gpu_addr);
if (view.has_value()) {
return *view;
}
- return RecycleSurface(overlaps, params, gpu_addr, preserve_contents, false);
}
+ // We failed all the tests, recycle the overlaps into a new texture.
+ return RecycleSurface(overlaps, params, gpu_addr, preserve_contents, false);
}
std::pair<TSurface, TView> InitializeSurface(GPUVAddr gpu_addr, const SurfaceParams& params,