diff options
Diffstat (limited to 'src/video_core/texture_cache/texture_cache.h')
-rw-r--r-- | src/video_core/texture_cache/texture_cache.h | 220 |
1 files changed, 180 insertions, 40 deletions
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 6a92b22d3..f4c015635 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -29,6 +29,7 @@ #include "video_core/rasterizer_interface.h" #include "video_core/surface.h" #include "video_core/texture_cache/copy_params.h" +#include "video_core/texture_cache/format_lookup_table.h" #include "video_core/texture_cache/surface_base.h" #include "video_core/texture_cache/surface_params.h" #include "video_core/texture_cache/surface_view.h" @@ -94,10 +95,16 @@ public: std::lock_guard lock{mutex}; const auto gpu_addr{tic.Address()}; if (!gpu_addr) { - return {}; + return GetNullSurface(SurfaceParams::ExpectedTarget(entry)); } - const auto params{SurfaceParams::CreateForTexture(tic, entry)}; - const auto [surface, view] = GetSurface(gpu_addr, params, true, false); + + const auto host_ptr{system.GPU().MemoryManager().GetPointer(gpu_addr)}; + const auto cache_addr{ToCacheAddr(host_ptr)}; + if (!cache_addr) { + return GetNullSurface(SurfaceParams::ExpectedTarget(entry)); + } + const auto params{SurfaceParams::CreateForTexture(format_lookup_table, tic, entry)}; + const auto [surface, view] = GetSurface(gpu_addr, cache_addr, params, true, false); if (guard_samplers) { sampled_textures.push_back(surface); } @@ -109,10 +116,15 @@ public: std::lock_guard lock{mutex}; const auto gpu_addr{tic.Address()}; if (!gpu_addr) { - return {}; + return GetNullSurface(SurfaceParams::ExpectedTarget(entry)); + } + const auto host_ptr{system.GPU().MemoryManager().GetPointer(gpu_addr)}; + const auto cache_addr{ToCacheAddr(host_ptr)}; + if (!cache_addr) { + return GetNullSurface(SurfaceParams::ExpectedTarget(entry)); } - const auto params{SurfaceParams::CreateForImage(tic, entry)}; - const auto [surface, view] = GetSurface(gpu_addr, params, true, false); + const auto params{SurfaceParams::CreateForImage(format_lookup_table, tic, entry)}; + const auto [surface, view] = GetSurface(gpu_addr, cache_addr, params, true, false); if (guard_samplers) { sampled_textures.push_back(surface); } @@ -142,11 +154,17 @@ public: SetEmptyDepthBuffer(); return {}; } + const auto host_ptr{system.GPU().MemoryManager().GetPointer(gpu_addr)}; + const auto cache_addr{ToCacheAddr(host_ptr)}; + if (!cache_addr) { + SetEmptyDepthBuffer(); + return {}; + } const auto depth_params{SurfaceParams::CreateForDepthBuffer( system, regs.zeta_width, regs.zeta_height, regs.zeta.format, regs.zeta.memory_layout.block_width, regs.zeta.memory_layout.block_height, regs.zeta.memory_layout.block_depth, regs.zeta.memory_layout.type)}; - auto surface_view = GetSurface(gpu_addr, depth_params, preserve_contents, true); + auto surface_view = GetSurface(gpu_addr, cache_addr, depth_params, preserve_contents, true); if (depth_buffer.target) depth_buffer.target->MarkAsRenderTarget(false, NO_RT); depth_buffer.target = surface_view.first; @@ -179,8 +197,16 @@ public: return {}; } - auto surface_view = GetSurface(gpu_addr, SurfaceParams::CreateForFramebuffer(system, index), - preserve_contents, true); + const auto host_ptr{system.GPU().MemoryManager().GetPointer(gpu_addr)}; + const auto cache_addr{ToCacheAddr(host_ptr)}; + if (!cache_addr) { + SetEmptyColorBuffer(index); + return {}; + } + + auto surface_view = + GetSurface(gpu_addr, cache_addr, SurfaceParams::CreateForFramebuffer(system, index), + preserve_contents, true); if (render_targets[index].target) render_targets[index].target->MarkAsRenderTarget(false, NO_RT); render_targets[index].target = surface_view.first; @@ -229,8 +255,14 @@ public: const GPUVAddr src_gpu_addr = src_config.Address(); const GPUVAddr dst_gpu_addr = dst_config.Address(); DeduceBestBlit(src_params, dst_params, src_gpu_addr, dst_gpu_addr); - std::pair<TSurface, TView> dst_surface = GetSurface(dst_gpu_addr, dst_params, true, false); - std::pair<TSurface, TView> src_surface = GetSurface(src_gpu_addr, src_params, true, false); + const auto dst_host_ptr{system.GPU().MemoryManager().GetPointer(dst_gpu_addr)}; + const auto dst_cache_addr{ToCacheAddr(dst_host_ptr)}; + const auto src_host_ptr{system.GPU().MemoryManager().GetPointer(src_gpu_addr)}; + const auto src_cache_addr{ToCacheAddr(src_host_ptr)}; + std::pair<TSurface, TView> dst_surface = + GetSurface(dst_gpu_addr, dst_cache_addr, dst_params, true, false); + std::pair<TSurface, TView> src_surface = + GetSurface(src_gpu_addr, src_cache_addr, src_params, true, false); ImageBlit(src_surface.second, dst_surface.second, copy_config); dst_surface.first->MarkAsModified(true, Tick()); } @@ -346,13 +378,6 @@ protected: return new_surface; } - std::pair<TSurface, TView> GetFermiSurface( - const Tegra::Engines::Fermi2D::Regs::Surface& config) { - SurfaceParams params = SurfaceParams::CreateForFermiCopySurface(config); - const GPUVAddr gpu_addr = config.Address(); - return GetSurface(gpu_addr, params, true, false); - } - Core::System& system; private: @@ -485,15 +510,13 @@ private: GetSiblingFormat(cr_params.pixel_format) == params.pixel_format) { SurfaceParams new_params = params; new_params.pixel_format = cr_params.pixel_format; - new_params.component_type = cr_params.component_type; new_params.type = cr_params.type; new_surface = GetUncachedSurface(gpu_addr, new_params); } else { new_surface = GetUncachedSurface(gpu_addr, params); } const auto& final_params = new_surface->GetSurfaceParams(); - if (cr_params.type != final_params.type || - (cr_params.component_type != final_params.component_type)) { + if (cr_params.type != final_params.type) { BufferCopy(current_surface, new_surface); } else { std::vector<CopyParams> bricks = current_surface->BreakDown(final_params); @@ -593,6 +616,86 @@ private: } /** + * Takes care of managing 3D textures and its slices. Does HLE methods for reconstructing the 3D + * textures within the GPU if possible. Falls back to LLE when it isn't possible to use any of + * the HLE methods. + * + * @param overlaps The overlapping surfaces registered in the cache. + * @param params The parameters on the new surface. + * @param gpu_addr The starting address of the new surface. + * @param cache_addr The starting address of the new surface on physical memory. + * @param preserve_contents Indicates that the new surface should be loaded from memory or + * left blank. + */ + std::optional<std::pair<TSurface, TView>> Manage3DSurfaces(std::vector<TSurface>& overlaps, + const SurfaceParams& params, + const GPUVAddr gpu_addr, + const CacheAddr cache_addr, + bool preserve_contents) { + if (params.target == SurfaceTarget::Texture3D) { + bool failed = false; + if (params.num_levels > 1) { + // We can't handle mipmaps in 3D textures yet, better fallback to LLE approach + return std::nullopt; + } + TSurface new_surface = GetUncachedSurface(gpu_addr, params); + bool modified = false; + for (auto& surface : overlaps) { + const SurfaceParams& src_params = surface->GetSurfaceParams(); + if (src_params.target != SurfaceTarget::Texture2D) { + failed = true; + break; + } + if (src_params.height != params.height) { + failed = true; + break; + } + if (src_params.block_depth != params.block_depth || + src_params.block_height != params.block_height) { + failed = true; + break; + } + const u32 offset = static_cast<u32>(surface->GetCacheAddr() - cache_addr); + const auto [x, y, z] = params.GetBlockOffsetXYZ(offset); + modified |= surface->IsModified(); + const CopyParams copy_params(0, 0, 0, 0, 0, z, 0, 0, params.width, params.height, + 1); + ImageCopy(surface, new_surface, copy_params); + } + if (failed) { + return std::nullopt; + } + for (const auto& surface : overlaps) { + Unregister(surface); + } + new_surface->MarkAsModified(modified, Tick()); + Register(new_surface); + auto view = new_surface->GetMainView(); + return {{std::move(new_surface), view}}; + } else { + for (const auto& surface : overlaps) { + if (!surface->MatchTarget(params.target)) { + if (overlaps.size() == 1 && surface->GetCacheAddr() == cache_addr) { + if (Settings::values.use_accurate_gpu_emulation) { + return std::nullopt; + } + Unregister(surface); + return InitializeSurface(gpu_addr, params, preserve_contents); + } + return std::nullopt; + } + if (surface->GetCacheAddr() != cache_addr) { + continue; + } + if (surface->MatchesStructure(params) == MatchStructureResult::FullMatch) { + return {{surface, surface->GetMainView()}}; + } + } + return InitializeSurface(gpu_addr, params, preserve_contents); + } + } + + /** * 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: * @@ -615,22 +718,9 @@ private: * left blank. * @param is_render Whether or not the surface is a render target. **/ - std::pair<TSurface, TView> GetSurface(const GPUVAddr gpu_addr, const SurfaceParams& params, - bool preserve_contents, bool is_render) { - const auto host_ptr{system.GPU().MemoryManager().GetPointer(gpu_addr)}; - const auto cache_addr{ToCacheAddr(host_ptr)}; - - // Step 0: guarantee a valid surface - if (!cache_addr) { - // Return a null surface if it's invalid - SurfaceParams new_params = params; - new_params.width = 1; - new_params.height = 1; - new_params.depth = 1; - new_params.block_height = 0; - new_params.block_depth = 0; - return InitializeSurface(gpu_addr, new_params, false); - } + std::pair<TSurface, TView> GetSurface(const GPUVAddr gpu_addr, const CacheAddr cache_addr, + const SurfaceParams& params, bool preserve_contents, + bool is_render) { // Step 1 // Check Level 1 Cache for a fast structural match. If candidate surface @@ -677,6 +767,15 @@ private: } } + // Check if it's a 3D texture + if (params.block_depth > 0) { + auto surface = + Manage3DSurfaces(overlaps, params, gpu_addr, cache_addr, preserve_contents); + if (surface) { + return *surface; + } + } + // Split cases between 1 overlap or many. if (overlaps.size() == 1) { TSurface current_surface = overlaps[0]; @@ -795,6 +894,41 @@ private: } /** + * Gets a null surface based on a target texture. + * @param target The target of the null surface. + */ + TView GetNullSurface(SurfaceTarget target) { + const u32 i_target = static_cast<u32>(target); + if (const auto it = invalid_cache.find(i_target); it != invalid_cache.end()) { + return it->second->GetMainView(); + } + SurfaceParams params{}; + params.target = target; + params.is_tiled = false; + params.srgb_conversion = false; + params.is_layered = false; + params.block_width = 0; + params.block_height = 0; + params.block_depth = 0; + params.tile_width_spacing = 1; + params.width = 1; + params.height = 1; + params.depth = 1; + params.pitch = 4; + params.num_levels = 1; + params.emulated_levels = 1; + params.pixel_format = VideoCore::Surface::PixelFormat::RGBA16F; + params.type = VideoCore::Surface::SurfaceType::ColorTexture; + auto surface = CreateSurface(0ULL, params); + invalid_memory.clear(); + invalid_memory.resize(surface->GetHostSizeInBytes(), 0U); + surface->UploadTexture(invalid_memory); + surface->MarkAsModified(false, Tick()); + invalid_cache.emplace(i_target, surface); + return surface->GetMainView(); + } + + /** * Gets the a source and destination starting address and parameters, * and tries to deduce if they are supposed to be depth textures. If so, their * parameters are modified and fixed into so. @@ -835,12 +969,11 @@ private: } } - const auto inherit_format = ([](SurfaceParams& to, TSurface from) { + const auto inherit_format = [](SurfaceParams& to, TSurface from) { const SurfaceParams& params = from->GetSurfaceParams(); to.pixel_format = params.pixel_format; - to.component_type = params.component_type; to.type = params.type; - }); + }; // Now we got the cases where one or both is Depth and the other is not known if (!incomplete_src) { inherit_format(src_params, deduced_src.surface); @@ -956,6 +1089,8 @@ private: VideoCore::RasterizerInterface& rasterizer; + FormatLookupTable format_lookup_table; + u64 ticks{}; // Guards the cache for protection conflicts. @@ -991,6 +1126,11 @@ private: std::vector<TSurface> sampled_textures; + /// This cache stores null surfaces in order to be used as a placeholder + /// for invalid texture calls. + std::unordered_map<u32, TSurface> invalid_cache; + std::vector<u8> invalid_memory; + StagingCache staging_cache; std::recursive_mutex mutex; }; |