mirror of
				https://git.zaroz.cloud/nintendo-back-up/yuzu/yuzu-mainline.git
				synced 2025-03-21 01:53:15 +00:00 
			
		
		
		
	texture_cache: Document the most important methods.
This commit is contained in:
		
							parent
							
								
									4530511ee4
								
							
						
					
					
						commit
						6162cb922e
					
				@ -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) {
 | 
					    void Guard(bool new_guard) {
 | 
				
			||||||
        guard_cache = new_guard;
 | 
					        guard_cache = new_guard;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -293,6 +297,14 @@ private:
 | 
				
			|||||||
        BufferCopy = 3,
 | 
					        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,
 | 
					    RecycleStrategy PickStrategy(std::vector<TSurface>& overlaps, const SurfaceParams& params,
 | 
				
			||||||
                                 const GPUVAddr gpu_addr, const bool untopological) {
 | 
					                                 const GPUVAddr gpu_addr, const bool untopological) {
 | 
				
			||||||
        if (Settings::values.use_accurate_gpu_emulation) {
 | 
					        if (Settings::values.use_accurate_gpu_emulation) {
 | 
				
			||||||
@ -315,6 +327,18 @@ private:
 | 
				
			|||||||
        return RecycleStrategy::Ignore;
 | 
					        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,
 | 
					    std::pair<TSurface, TView> RecycleSurface(std::vector<TSurface>& overlaps,
 | 
				
			||||||
                                              const SurfaceParams& params, const GPUVAddr gpu_addr,
 | 
					                                              const SurfaceParams& params, const GPUVAddr gpu_addr,
 | 
				
			||||||
                                              const bool preserve_contents,
 | 
					                                              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,
 | 
					    std::pair<TSurface, TView> RebuildSurface(TSurface current_surface,
 | 
				
			||||||
                                              const SurfaceParams& params) {
 | 
					                                              const SurfaceParams& params) {
 | 
				
			||||||
        const auto gpu_addr = current_surface->GetGpuAddr();
 | 
					        const auto gpu_addr = current_surface->GetGpuAddr();
 | 
				
			||||||
@ -357,6 +387,14 @@ private:
 | 
				
			|||||||
        return {new_surface, new_surface->GetMainView()};
 | 
					        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,
 | 
					    std::pair<TSurface, TView> ManageStructuralMatch(TSurface current_surface,
 | 
				
			||||||
                                                     const SurfaceParams& params) {
 | 
					                                                     const SurfaceParams& params) {
 | 
				
			||||||
        const bool is_mirage = !current_surface->MatchFormat(params.pixel_format);
 | 
					        const bool is_mirage = !current_surface->MatchFormat(params.pixel_format);
 | 
				
			||||||
@ -370,10 +408,18 @@ private:
 | 
				
			|||||||
        return {current_surface, current_surface->EmplaceOverview(params)};
 | 
					        return {current_surface, current_surface->EmplaceOverview(params)};
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    std::optional<std::pair<TSurface, TView>> ReconstructSurface(std::vector<TSurface>& overlaps,
 | 
					    /**
 | 
				
			||||||
                                                                 const SurfaceParams& params,
 | 
					     * `TryReconstructSurface` unlike `RebuildSurface` where we know the registered surface
 | 
				
			||||||
                                                                 const GPUVAddr gpu_addr,
 | 
					     * matches the candidate in some way, we got no guarantess here. We try to see if the overlaps
 | 
				
			||||||
                                                                 const u8* host_ptr) {
 | 
					     * 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) {
 | 
					        if (params.target == SurfaceTarget::Texture3D) {
 | 
				
			||||||
            return {};
 | 
					            return {};
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -412,12 +458,30 @@ private:
 | 
				
			|||||||
        return {{new_surface, new_surface->GetMainView()}};
 | 
					        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,
 | 
					    std::pair<TSurface, TView> GetSurface(const GPUVAddr gpu_addr, const SurfaceParams& params,
 | 
				
			||||||
                                          bool preserve_contents) {
 | 
					                                          bool preserve_contents) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const auto host_ptr{memory_manager->GetPointer(gpu_addr)};
 | 
					        const auto host_ptr{memory_manager->GetPointer(gpu_addr)};
 | 
				
			||||||
        const auto cache_addr{ToCacheAddr(host_ptr)};
 | 
					        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) {
 | 
					        if (l1_cache.count(cache_addr) > 0) {
 | 
				
			||||||
            TSurface current_surface = l1_cache[cache_addr];
 | 
					            TSurface current_surface = l1_cache[cache_addr];
 | 
				
			||||||
            if (!current_surface->MatchesTopology(params)) {
 | 
					            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();
 | 
					        const std::size_t candidate_size = params.GetGuestSizeInBytes();
 | 
				
			||||||
        auto overlaps{GetSurfacesInRegion(cache_addr, candidate_size)};
 | 
					        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()) {
 | 
					        if (overlaps.empty()) {
 | 
				
			||||||
            return InitializeSurface(gpu_addr, params, preserve_contents);
 | 
					            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) {
 | 
					        for (auto surface : overlaps) {
 | 
				
			||||||
            if (!surface->MatchesTopology(params)) {
 | 
					            if (!surface->MatchesTopology(params)) {
 | 
				
			||||||
                return RecycleSurface(overlaps, params, gpu_addr, preserve_contents, true);
 | 
					                return RecycleSurface(overlaps, params, gpu_addr, preserve_contents, true);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Split cases between 1 overlap or many.
 | 
				
			||||||
        if (overlaps.size() == 1) {
 | 
					        if (overlaps.size() == 1) {
 | 
				
			||||||
            TSurface current_surface = overlaps[0];
 | 
					            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->IsInside(gpu_addr, gpu_addr + candidate_size)) {
 | 
				
			||||||
                if (current_surface->GetGpuAddr() == gpu_addr) {
 | 
					                if (current_surface->GetGpuAddr() == gpu_addr) {
 | 
				
			||||||
                    std::optional<std::pair<TSurface, TView>> view =
 | 
					                    std::optional<std::pair<TSurface, TView>> view =
 | 
				
			||||||
                        ReconstructSurface(overlaps, params, gpu_addr, host_ptr);
 | 
					                        TryReconstructSurface(overlaps, params, gpu_addr);
 | 
				
			||||||
                    if (view.has_value()) {
 | 
					                    if (view.has_value()) {
 | 
				
			||||||
                        return *view;
 | 
					                        return *view;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                return RecycleSurface(overlaps, params, gpu_addr, preserve_contents, false);
 | 
					                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 =
 | 
					            std::optional<TView> view =
 | 
				
			||||||
                current_surface->EmplaceView(params, gpu_addr, candidate_size);
 | 
					                current_surface->EmplaceView(params, gpu_addr, candidate_size);
 | 
				
			||||||
            if (view.has_value()) {
 | 
					            if (view.has_value()) {
 | 
				
			||||||
@ -472,15 +548,18 @@ private:
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
                return {current_surface, *view};
 | 
					                return {current_surface, *view};
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            return RecycleSurface(overlaps, params, gpu_addr, preserve_contents, false);
 | 
					 | 
				
			||||||
        } else {
 | 
					        } 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 =
 | 
					            std::optional<std::pair<TSurface, TView>> view =
 | 
				
			||||||
                ReconstructSurface(overlaps, params, gpu_addr, host_ptr);
 | 
					                TryReconstructSurface(overlaps, params, gpu_addr);
 | 
				
			||||||
            if (view.has_value()) {
 | 
					            if (view.has_value()) {
 | 
				
			||||||
                return *view;
 | 
					                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,
 | 
					    std::pair<TSurface, TView> InitializeSurface(GPUVAddr gpu_addr, const SurfaceParams& params,
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user