texture_cache: Split texture cache into different files
This commit is contained in:
		| @@ -109,6 +109,13 @@ add_library(video_core STATIC | ||||
|     shader/track.cpp | ||||
|     surface.cpp | ||||
|     surface.h | ||||
|     texture_cache/surface_base.cpp | ||||
|     texture_cache/surface_base.h | ||||
|     texture_cache/surface_params.cpp | ||||
|     texture_cache/surface_params.h | ||||
|     texture_cache/surface_view.cpp | ||||
|     texture_cache/surface_view.h | ||||
|     texture_cache/texture_cache.h | ||||
|     textures/astc.cpp | ||||
|     textures/astc.h | ||||
|     textures/convert.cpp | ||||
| @@ -116,8 +123,6 @@ add_library(video_core STATIC | ||||
|     textures/decoders.cpp | ||||
|     textures/decoders.h | ||||
|     textures/texture.h | ||||
|     texture_cache.cpp | ||||
|     texture_cache.h | ||||
|     video_core.cpp | ||||
|     video_core.h | ||||
| ) | ||||
|   | ||||
| @@ -9,7 +9,7 @@ | ||||
| #include "video_core/renderer_opengl/gl_resource_manager.h" | ||||
| #include "video_core/renderer_opengl/gl_texture_cache.h" | ||||
| #include "video_core/renderer_opengl/utils.h" | ||||
| #include "video_core/texture_cache.h" | ||||
| #include "video_core/texture_cache/texture_cache_contextless.h" | ||||
| #include "video_core/textures/convert.h" | ||||
| #include "video_core/textures/texture.h" | ||||
|  | ||||
|   | ||||
| @@ -13,7 +13,7 @@ | ||||
|  | ||||
| #include "common/common_types.h" | ||||
| #include "video_core/engines/shader_bytecode.h" | ||||
| #include "video_core/texture_cache.h" | ||||
| #include "video_core/texture_cache/texture_cache_contextless.h" | ||||
|  | ||||
| namespace OpenGL { | ||||
|  | ||||
|   | ||||
| @@ -1,750 +0,0 @@ | ||||
| // Copyright 2019 yuzu Emulator Project | ||||
| // Licensed under GPLv2 or any later version | ||||
| // Refer to the license.txt file included. | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <list> | ||||
| #include <memory> | ||||
| #include <set> | ||||
| #include <tuple> | ||||
| #include <type_traits> | ||||
| #include <unordered_map> | ||||
|  | ||||
| #include <boost/icl/interval_map.hpp> | ||||
| #include <boost/range/iterator_range.hpp> | ||||
|  | ||||
| #include "common/assert.h" | ||||
| #include "common/common_types.h" | ||||
| #include "core/memory.h" | ||||
| #include "video_core/engines/fermi_2d.h" | ||||
| #include "video_core/engines/maxwell_3d.h" | ||||
| #include "video_core/gpu.h" | ||||
| #include "video_core/memory_manager.h" | ||||
| #include "video_core/rasterizer_interface.h" | ||||
| #include "video_core/surface.h" | ||||
|  | ||||
| namespace Core { | ||||
| class System; | ||||
| } | ||||
|  | ||||
| namespace Tegra::Texture { | ||||
| struct FullTextureInfo; | ||||
| } | ||||
|  | ||||
| namespace VideoCore { | ||||
| class RasterizerInterface; | ||||
| } | ||||
|  | ||||
| namespace VideoCommon { | ||||
|  | ||||
| class HasheableSurfaceParams { | ||||
| public: | ||||
|     std::size_t Hash() const; | ||||
|  | ||||
|     bool operator==(const HasheableSurfaceParams& rhs) const; | ||||
|  | ||||
|     bool operator!=(const HasheableSurfaceParams& rhs) const { | ||||
|         return !operator==(rhs); | ||||
|     } | ||||
|  | ||||
| protected: | ||||
|     // Avoid creation outside of a managed environment. | ||||
|     HasheableSurfaceParams() = default; | ||||
|  | ||||
|     bool is_tiled; | ||||
|     bool srgb_conversion; | ||||
|     u32 block_width; | ||||
|     u32 block_height; | ||||
|     u32 block_depth; | ||||
|     u32 tile_width_spacing; | ||||
|     u32 width; | ||||
|     u32 height; | ||||
|     u32 depth; | ||||
|     u32 pitch; | ||||
|     u32 unaligned_height; | ||||
|     u32 num_levels; | ||||
|     VideoCore::Surface::PixelFormat pixel_format; | ||||
|     VideoCore::Surface::ComponentType component_type; | ||||
|     VideoCore::Surface::SurfaceType type; | ||||
|     VideoCore::Surface::SurfaceTarget target; | ||||
| }; | ||||
|  | ||||
| class SurfaceParams final : public HasheableSurfaceParams { | ||||
| public: | ||||
|     /// Creates SurfaceCachedParams from a texture configuration. | ||||
|     static SurfaceParams CreateForTexture(Core::System& system, | ||||
|                                           const Tegra::Texture::FullTextureInfo& config); | ||||
|  | ||||
|     /// Creates SurfaceCachedParams for a depth buffer configuration. | ||||
|     static SurfaceParams CreateForDepthBuffer( | ||||
|         Core::System& system, u32 zeta_width, u32 zeta_height, Tegra::DepthFormat format, | ||||
|         u32 block_width, u32 block_height, u32 block_depth, | ||||
|         Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout type); | ||||
|  | ||||
|     /// Creates SurfaceCachedParams from a framebuffer configuration. | ||||
|     static SurfaceParams CreateForFramebuffer(Core::System& system, std::size_t index); | ||||
|  | ||||
|     /// Creates SurfaceCachedParams from a Fermi2D surface configuration. | ||||
|     static SurfaceParams CreateForFermiCopySurface( | ||||
|         const Tegra::Engines::Fermi2D::Regs::Surface& config); | ||||
|  | ||||
|     bool IsTiled() const { | ||||
|         return is_tiled; | ||||
|     } | ||||
|  | ||||
|     bool GetSrgbConversion() const { | ||||
|         return srgb_conversion; | ||||
|     } | ||||
|  | ||||
|     u32 GetBlockWidth() const { | ||||
|         return block_width; | ||||
|     } | ||||
|  | ||||
|     u32 GetTileWidthSpacing() const { | ||||
|         return tile_width_spacing; | ||||
|     } | ||||
|  | ||||
|     u32 GetWidth() const { | ||||
|         return width; | ||||
|     } | ||||
|  | ||||
|     u32 GetHeight() const { | ||||
|         return height; | ||||
|     } | ||||
|  | ||||
|     u32 GetDepth() const { | ||||
|         return depth; | ||||
|     } | ||||
|  | ||||
|     u32 GetPitch() const { | ||||
|         return pitch; | ||||
|     } | ||||
|  | ||||
|     u32 GetNumLevels() const { | ||||
|         return num_levels; | ||||
|     } | ||||
|  | ||||
|     VideoCore::Surface::PixelFormat GetPixelFormat() const { | ||||
|         return pixel_format; | ||||
|     } | ||||
|  | ||||
|     VideoCore::Surface::ComponentType GetComponentType() const { | ||||
|         return component_type; | ||||
|     } | ||||
|  | ||||
|     VideoCore::Surface::SurfaceTarget GetTarget() const { | ||||
|         return target; | ||||
|     } | ||||
|  | ||||
|     VideoCore::Surface::SurfaceType GetType() const { | ||||
|         return type; | ||||
|     } | ||||
|  | ||||
|     std::size_t GetGuestSizeInBytes() const { | ||||
|         return guest_size_in_bytes; | ||||
|     } | ||||
|  | ||||
|     std::size_t GetHostSizeInBytes() const { | ||||
|         return host_size_in_bytes; | ||||
|     } | ||||
|  | ||||
|     u32 GetNumLayers() const { | ||||
|         return num_layers; | ||||
|     } | ||||
|  | ||||
|     /// Returns the width of a given mipmap level. | ||||
|     u32 GetMipWidth(u32 level) const; | ||||
|  | ||||
|     /// Returns the height of a given mipmap level. | ||||
|     u32 GetMipHeight(u32 level) const; | ||||
|  | ||||
|     /// Returns the depth of a given mipmap level. | ||||
|     u32 GetMipDepth(u32 level) const; | ||||
|  | ||||
|     /// Returns true if these parameters are from a layered surface. | ||||
|     bool IsLayered() const; | ||||
|  | ||||
|     /// Returns the block height of a given mipmap level. | ||||
|     u32 GetMipBlockHeight(u32 level) const; | ||||
|  | ||||
|     /// Returns the block depth of a given mipmap level. | ||||
|     u32 GetMipBlockDepth(u32 level) const; | ||||
|  | ||||
|     /// Returns the offset in bytes in guest memory of a given mipmap level. | ||||
|     std::size_t GetGuestMipmapLevelOffset(u32 level) const; | ||||
|  | ||||
|     /// Returns the offset in bytes in host memory (linear) of a given mipmap level. | ||||
|     std::size_t GetHostMipmapLevelOffset(u32 level) const; | ||||
|  | ||||
|     /// Returns the size in bytes in host memory (linear) of a given mipmap level. | ||||
|     std::size_t GetHostMipmapSize(u32 level) const; | ||||
|  | ||||
|     /// Returns the size of a layer in bytes in guest memory. | ||||
|     std::size_t GetGuestLayerSize() const; | ||||
|  | ||||
|     /// Returns the size of a layer in bytes in host memory for a given mipmap level. | ||||
|     std::size_t GetHostLayerSize(u32 level) const; | ||||
|  | ||||
|     /// Returns the default block width. | ||||
|     u32 GetDefaultBlockWidth() const; | ||||
|  | ||||
|     /// Returns the default block height. | ||||
|     u32 GetDefaultBlockHeight() const; | ||||
|  | ||||
|     /// Returns the bits per pixel. | ||||
|     u32 GetBitsPerPixel() const; | ||||
|  | ||||
|     /// Returns the bytes per pixel. | ||||
|     u32 GetBytesPerPixel() const; | ||||
|  | ||||
|     /// Returns true if another surface can be familiar with this. This is a loosely defined term | ||||
|     /// that reflects the possibility of these two surface parameters potentially being part of a | ||||
|     /// bigger superset. | ||||
|     bool IsFamiliar(const SurfaceParams& view_params) const; | ||||
|  | ||||
|     /// Returns true if the pixel format is a depth and/or stencil format. | ||||
|     bool IsPixelFormatZeta() const; | ||||
|  | ||||
|     /// Creates a map that redirects an address difference to a layer and mipmap level. | ||||
|     std::map<u64, std::pair<u32, u32>> CreateViewOffsetMap() const; | ||||
|  | ||||
|     /// Returns true if the passed surface view parameters is equal or a valid subset of this. | ||||
|     bool IsViewValid(const SurfaceParams& view_params, u32 layer, u32 level) const; | ||||
|  | ||||
| private: | ||||
|     /// Calculates values that can be deduced from HasheableSurfaceParams. | ||||
|     void CalculateCachedValues(); | ||||
|  | ||||
|     /// Returns the size of a given mipmap level inside a layer. | ||||
|     std::size_t GetInnerMipmapMemorySize(u32 level, bool as_host_size, bool uncompressed) const; | ||||
|  | ||||
|     /// Returns the size of all mipmap levels and aligns as needed. | ||||
|     std::size_t GetInnerMemorySize(bool as_host_size, bool layer_only, bool uncompressed) const; | ||||
|  | ||||
|     /// Returns the size of a layer | ||||
|     std::size_t GetLayerSize(bool as_host_size, bool uncompressed) const; | ||||
|  | ||||
|     /// Returns true if the passed view width and height match the size of this params in a given | ||||
|     /// mipmap level. | ||||
|     bool IsDimensionValid(const SurfaceParams& view_params, u32 level) const; | ||||
|  | ||||
|     /// Returns true if the passed view depth match the size of this params in a given mipmap level. | ||||
|     bool IsDepthValid(const SurfaceParams& view_params, u32 level) const; | ||||
|  | ||||
|     /// Returns true if the passed view layers and mipmap levels are in bounds. | ||||
|     bool IsInBounds(const SurfaceParams& view_params, u32 layer, u32 level) const; | ||||
|  | ||||
|     std::size_t guest_size_in_bytes; | ||||
|     std::size_t host_size_in_bytes; | ||||
|     u32 num_layers; | ||||
| }; | ||||
|  | ||||
| struct ViewKey { | ||||
|     std::size_t Hash() const; | ||||
|  | ||||
|     bool operator==(const ViewKey& rhs) const; | ||||
|  | ||||
|     u32 base_layer{}; | ||||
|     u32 num_layers{}; | ||||
|     u32 base_level{}; | ||||
|     u32 num_levels{}; | ||||
| }; | ||||
|  | ||||
| } // namespace VideoCommon | ||||
|  | ||||
| namespace std { | ||||
|  | ||||
| template <> | ||||
| struct hash<VideoCommon::SurfaceParams> { | ||||
|     std::size_t operator()(const VideoCommon::SurfaceParams& k) const noexcept { | ||||
|         return k.Hash(); | ||||
|     } | ||||
| }; | ||||
|  | ||||
| template <> | ||||
| struct hash<VideoCommon::ViewKey> { | ||||
|     std::size_t operator()(const VideoCommon::ViewKey& k) const noexcept { | ||||
|         return k.Hash(); | ||||
|     } | ||||
| }; | ||||
|  | ||||
| } // namespace std | ||||
|  | ||||
| namespace VideoCommon { | ||||
|  | ||||
| class SurfaceBaseImpl { | ||||
| public: | ||||
|     void LoadBuffer(); | ||||
|  | ||||
|     void FlushBuffer(); | ||||
|  | ||||
|     GPUVAddr GetGpuAddr() const { | ||||
|         ASSERT(is_registered); | ||||
|         return gpu_addr; | ||||
|     } | ||||
|  | ||||
|     VAddr GetCpuAddr() const { | ||||
|         ASSERT(is_registered); | ||||
|         return cpu_addr; | ||||
|     } | ||||
|  | ||||
|     u8* GetHostPtr() const { | ||||
|         ASSERT(is_registered); | ||||
|         return host_ptr; | ||||
|     } | ||||
|  | ||||
|     CacheAddr GetCacheAddr() const { | ||||
|         ASSERT(is_registered); | ||||
|         return cache_addr; | ||||
|     } | ||||
|  | ||||
|     const SurfaceParams& GetSurfaceParams() const { | ||||
|         return params; | ||||
|     } | ||||
|  | ||||
|     void Register(GPUVAddr gpu_addr_, VAddr cpu_addr_, u8* host_ptr_) { | ||||
|         ASSERT(!is_registered); | ||||
|         is_registered = true; | ||||
|         gpu_addr = gpu_addr_; | ||||
|         cpu_addr = cpu_addr_; | ||||
|         host_ptr = host_ptr_; | ||||
|         cache_addr = ToCacheAddr(host_ptr_); | ||||
|         DecorateSurfaceName(); | ||||
|     } | ||||
|  | ||||
|     void Unregister() { | ||||
|         ASSERT(is_registered); | ||||
|         is_registered = false; | ||||
|     } | ||||
|  | ||||
|     bool IsRegistered() const { | ||||
|         return is_registered; | ||||
|     } | ||||
|  | ||||
|     std::size_t GetSizeInBytes() const { | ||||
|         return params.GetGuestSizeInBytes(); | ||||
|     } | ||||
|  | ||||
|     u8* GetStagingBufferLevelData(u32 level) { | ||||
|         return staging_buffer.data() + params.GetHostMipmapLevelOffset(level); | ||||
|     } | ||||
|  | ||||
| protected: | ||||
|     explicit SurfaceBaseImpl(const SurfaceParams& params); | ||||
|     ~SurfaceBaseImpl(); // non-virtual is intended | ||||
|  | ||||
|     virtual void DecorateSurfaceName() = 0; | ||||
|  | ||||
|     const SurfaceParams params; | ||||
|  | ||||
| private: | ||||
|     GPUVAddr gpu_addr{}; | ||||
|     VAddr cpu_addr{}; | ||||
|     u8* host_ptr{}; | ||||
|     CacheAddr cache_addr{}; | ||||
|     bool is_registered{}; | ||||
|  | ||||
|     std::vector<u8> staging_buffer; | ||||
| }; | ||||
|  | ||||
| template <typename TTextureCache, typename TView, typename TExecutionContext> | ||||
| class SurfaceBase : public SurfaceBaseImpl { | ||||
|     static_assert(std::is_trivially_copyable_v<TExecutionContext>); | ||||
|  | ||||
| public: | ||||
|     virtual TExecutionContext UploadTexture(TExecutionContext exctx) = 0; | ||||
|  | ||||
|     virtual TExecutionContext DownloadTexture(TExecutionContext exctx) = 0; | ||||
|  | ||||
|     TView* TryGetView(GPUVAddr view_addr, const SurfaceParams& view_params) { | ||||
|         if (view_addr < GetGpuAddr() || !params.IsFamiliar(view_params)) { | ||||
|             // It can't be a view if it's in a prior address. | ||||
|             return {}; | ||||
|         } | ||||
|  | ||||
|         const auto relative_offset{static_cast<u64>(view_addr - GetGpuAddr())}; | ||||
|         const auto it{view_offset_map.find(relative_offset)}; | ||||
|         if (it == view_offset_map.end()) { | ||||
|             // Couldn't find an aligned view. | ||||
|             return {}; | ||||
|         } | ||||
|         const auto [layer, level] = it->second; | ||||
|  | ||||
|         if (!params.IsViewValid(view_params, layer, level)) { | ||||
|             return {}; | ||||
|         } | ||||
|  | ||||
|         return GetView(layer, view_params.GetNumLayers(), level, view_params.GetNumLevels()); | ||||
|     } | ||||
|  | ||||
|     void MarkAsModified(bool is_modified_) { | ||||
|         is_modified = is_modified_; | ||||
|         if (is_modified_) { | ||||
|             modification_tick = texture_cache.Tick(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     TView* GetView(GPUVAddr view_addr, const SurfaceParams& view_params) { | ||||
|         TView* view{TryGetView(view_addr, view_params)}; | ||||
|         ASSERT(view != nullptr); | ||||
|         return view; | ||||
|     } | ||||
|  | ||||
|     bool IsModified() const { | ||||
|         return is_modified; | ||||
|     } | ||||
|  | ||||
|     u64 GetModificationTick() const { | ||||
|         return modification_tick; | ||||
|     } | ||||
|  | ||||
| protected: | ||||
|     explicit SurfaceBase(TTextureCache& texture_cache, const SurfaceParams& params) | ||||
|         : SurfaceBaseImpl{params}, texture_cache{texture_cache}, | ||||
|           view_offset_map{params.CreateViewOffsetMap()} {} | ||||
|  | ||||
|     ~SurfaceBase() = default; | ||||
|  | ||||
|     virtual std::unique_ptr<TView> CreateView(const ViewKey& view_key) = 0; | ||||
|  | ||||
| private: | ||||
|     TView* GetView(u32 base_layer, u32 num_layers, u32 base_level, u32 num_levels) { | ||||
|         const ViewKey key{base_layer, num_layers, base_level, num_levels}; | ||||
|         const auto [entry, is_cache_miss] = views.try_emplace(key); | ||||
|         auto& view{entry->second}; | ||||
|         if (is_cache_miss) { | ||||
|             view = CreateView(key); | ||||
|         } | ||||
|         return view.get(); | ||||
|     } | ||||
|  | ||||
|     TTextureCache& texture_cache; | ||||
|     const std::map<u64, std::pair<u32, u32>> view_offset_map; | ||||
|  | ||||
|     bool is_modified{}; | ||||
|     u64 modification_tick{}; | ||||
|     std::unordered_map<ViewKey, std::unique_ptr<TView>> views; | ||||
| }; | ||||
|  | ||||
| template <typename TSurface, typename TView, typename TExecutionContext> | ||||
| class TextureCache { | ||||
|     static_assert(std::is_trivially_copyable_v<TExecutionContext>); | ||||
|  | ||||
|     using ResultType = std::tuple<TView*, TExecutionContext>; | ||||
|     using IntervalMap = boost::icl::interval_map<CacheAddr, std::set<std::shared_ptr<TSurface>>>; | ||||
|     using IntervalType = typename IntervalMap::interval_type; | ||||
|  | ||||
| public: | ||||
|     void InvalidateRegion(CacheAddr addr, std::size_t size) { | ||||
|         for (const auto& surface : GetSurfacesInRegion(addr, size)) { | ||||
|             if (!surface->IsRegistered()) { | ||||
|                 // Skip duplicates | ||||
|                 continue; | ||||
|             } | ||||
|             Unregister(surface); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     ResultType GetTextureSurface(TExecutionContext exctx, | ||||
|                                  const Tegra::Texture::FullTextureInfo& config) { | ||||
|         const auto gpu_addr{config.tic.Address()}; | ||||
|         if (!gpu_addr) { | ||||
|             return {{}, exctx}; | ||||
|         } | ||||
|         const auto params{SurfaceParams::CreateForTexture(system, config)}; | ||||
|         return GetSurfaceView(exctx, gpu_addr, params, true); | ||||
|     } | ||||
|  | ||||
|     ResultType GetDepthBufferSurface(TExecutionContext exctx, bool preserve_contents) { | ||||
|         const auto& regs{system.GPU().Maxwell3D().regs}; | ||||
|         const auto gpu_addr{regs.zeta.Address()}; | ||||
|         if (!gpu_addr || !regs.zeta_enable) { | ||||
|             return {{}, exctx}; | ||||
|         } | ||||
|         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)}; | ||||
|         return GetSurfaceView(exctx, gpu_addr, depth_params, preserve_contents); | ||||
|     } | ||||
|  | ||||
|     ResultType GetColorBufferSurface(TExecutionContext exctx, std::size_t index, | ||||
|                                      bool preserve_contents) { | ||||
|         ASSERT(index < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets); | ||||
|  | ||||
|         const auto& regs{system.GPU().Maxwell3D().regs}; | ||||
|         if (index >= regs.rt_control.count || regs.rt[index].Address() == 0 || | ||||
|             regs.rt[index].format == Tegra::RenderTargetFormat::NONE) { | ||||
|             return {{}, exctx}; | ||||
|         } | ||||
|  | ||||
|         auto& memory_manager{system.GPU().MemoryManager()}; | ||||
|         const auto& config{system.GPU().Maxwell3D().regs.rt[index]}; | ||||
|         const auto gpu_addr{config.Address() + | ||||
|                             config.base_layer * config.layer_stride * sizeof(u32)}; | ||||
|         if (!gpu_addr) { | ||||
|             return {{}, exctx}; | ||||
|         } | ||||
|  | ||||
|         return GetSurfaceView(exctx, gpu_addr, SurfaceParams::CreateForFramebuffer(system, index), | ||||
|                               preserve_contents); | ||||
|     } | ||||
|  | ||||
|     ResultType GetFermiSurface(TExecutionContext exctx, | ||||
|                                const Tegra::Engines::Fermi2D::Regs::Surface& config) { | ||||
|         return GetSurfaceView(exctx, config.Address(), | ||||
|                               SurfaceParams::CreateForFermiCopySurface(config), true); | ||||
|     } | ||||
|  | ||||
|     std::shared_ptr<TSurface> TryFindFramebufferSurface(const u8* host_ptr) const { | ||||
|         const auto it{registered_surfaces.find(ToCacheAddr(host_ptr))}; | ||||
|         return it != registered_surfaces.end() ? *it->second.begin() : nullptr; | ||||
|     } | ||||
|  | ||||
|     u64 Tick() { | ||||
|         return ++ticks; | ||||
|     } | ||||
|  | ||||
| protected: | ||||
|     TextureCache(Core::System& system, VideoCore::RasterizerInterface& rasterizer) | ||||
|         : system{system}, rasterizer{rasterizer} {} | ||||
|  | ||||
|     ~TextureCache() = default; | ||||
|  | ||||
|     virtual ResultType TryFastGetSurfaceView( | ||||
|         TExecutionContext exctx, GPUVAddr gpu_addr, VAddr cpu_addr, u8* host_ptr, | ||||
|         const SurfaceParams& params, bool preserve_contents, | ||||
|         const std::vector<std::shared_ptr<TSurface>>& overlaps) = 0; | ||||
|  | ||||
|     virtual std::shared_ptr<TSurface> CreateSurface(const SurfaceParams& params) = 0; | ||||
|  | ||||
|     void Register(std::shared_ptr<TSurface> surface, GPUVAddr gpu_addr, VAddr cpu_addr, | ||||
|                   u8* host_ptr) { | ||||
|         surface->Register(gpu_addr, cpu_addr, host_ptr); | ||||
|         registered_surfaces.add({GetSurfaceInterval(surface), {surface}}); | ||||
|         rasterizer.UpdatePagesCachedCount(surface->GetCpuAddr(), surface->GetSizeInBytes(), 1); | ||||
|     } | ||||
|  | ||||
|     void Unregister(std::shared_ptr<TSurface> surface) { | ||||
|         registered_surfaces.subtract({GetSurfaceInterval(surface), {surface}}); | ||||
|         rasterizer.UpdatePagesCachedCount(surface->GetCpuAddr(), surface->GetSizeInBytes(), -1); | ||||
|         surface->Unregister(); | ||||
|     } | ||||
|  | ||||
|     std::shared_ptr<TSurface> GetUncachedSurface(const SurfaceParams& params) { | ||||
|         if (const auto surface = TryGetReservedSurface(params); surface) | ||||
|             return surface; | ||||
|         // No reserved surface available, create a new one and reserve it | ||||
|         auto new_surface{CreateSurface(params)}; | ||||
|         ReserveSurface(params, new_surface); | ||||
|         return new_surface; | ||||
|     } | ||||
|  | ||||
|     Core::System& system; | ||||
|  | ||||
| private: | ||||
|     ResultType GetSurfaceView(TExecutionContext exctx, GPUVAddr gpu_addr, | ||||
|                               const SurfaceParams& params, bool preserve_contents) { | ||||
|         auto& memory_manager{system.GPU().MemoryManager()}; | ||||
|         const auto cpu_addr{memory_manager.GpuToCpuAddress(gpu_addr)}; | ||||
|         DEBUG_ASSERT(cpu_addr); | ||||
|  | ||||
|         const auto host_ptr{memory_manager.GetPointer(gpu_addr)}; | ||||
|         const auto cache_addr{ToCacheAddr(host_ptr)}; | ||||
|         auto overlaps{GetSurfacesInRegion(cache_addr, params.GetGuestSizeInBytes())}; | ||||
|         if (overlaps.empty()) { | ||||
|             return LoadSurfaceView(exctx, gpu_addr, *cpu_addr, host_ptr, params, preserve_contents); | ||||
|         } | ||||
|  | ||||
|         if (overlaps.size() == 1) { | ||||
|             if (TView* view = overlaps[0]->TryGetView(gpu_addr, params); view) { | ||||
|                 return {view, exctx}; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         TView* fast_view; | ||||
|         std::tie(fast_view, exctx) = TryFastGetSurfaceView(exctx, gpu_addr, *cpu_addr, host_ptr, | ||||
|                                                            params, preserve_contents, overlaps); | ||||
|  | ||||
|         if (!fast_view) { | ||||
|             std::sort(overlaps.begin(), overlaps.end(), [](const auto& lhs, const auto& rhs) { | ||||
|                 return lhs->GetModificationTick() < rhs->GetModificationTick(); | ||||
|             }); | ||||
|         } | ||||
|  | ||||
|         for (const auto& surface : overlaps) { | ||||
|             if (!fast_view) { | ||||
|                 // Flush even when we don't care about the contents, to preserve memory not | ||||
|                 // written by the new surface. | ||||
|                 exctx = FlushSurface(exctx, surface); | ||||
|             } | ||||
|             Unregister(surface); | ||||
|         } | ||||
|  | ||||
|         if (fast_view) { | ||||
|             return {fast_view, exctx}; | ||||
|         } | ||||
|  | ||||
|         return LoadSurfaceView(exctx, gpu_addr, *cpu_addr, host_ptr, params, preserve_contents); | ||||
|     } | ||||
|  | ||||
|     ResultType LoadSurfaceView(TExecutionContext exctx, GPUVAddr gpu_addr, VAddr cpu_addr, | ||||
|                                u8* host_ptr, const SurfaceParams& params, bool preserve_contents) { | ||||
|         const auto new_surface{GetUncachedSurface(params)}; | ||||
|         Register(new_surface, gpu_addr, cpu_addr, host_ptr); | ||||
|         if (preserve_contents) { | ||||
|             exctx = LoadSurface(exctx, new_surface); | ||||
|         } | ||||
|         return {new_surface->GetView(gpu_addr, params), exctx}; | ||||
|     } | ||||
|  | ||||
|     TExecutionContext LoadSurface(TExecutionContext exctx, | ||||
|                                   const std::shared_ptr<TSurface>& surface) { | ||||
|         surface->LoadBuffer(); | ||||
|         exctx = surface->UploadTexture(exctx); | ||||
|         surface->MarkAsModified(false); | ||||
|         return exctx; | ||||
|     } | ||||
|  | ||||
|     TExecutionContext FlushSurface(TExecutionContext exctx, | ||||
|                                    const std::shared_ptr<TSurface>& surface) { | ||||
|         if (!surface->IsModified()) { | ||||
|             return exctx; | ||||
|         } | ||||
|         exctx = surface->DownloadTexture(exctx); | ||||
|         surface->FlushBuffer(); | ||||
|         return exctx; | ||||
|     } | ||||
|  | ||||
|     std::vector<std::shared_ptr<TSurface>> GetSurfacesInRegion(CacheAddr cache_addr, | ||||
|                                                                std::size_t size) const { | ||||
|         if (size == 0) { | ||||
|             return {}; | ||||
|         } | ||||
|         const IntervalType interval{cache_addr, cache_addr + size}; | ||||
|  | ||||
|         std::vector<std::shared_ptr<TSurface>> surfaces; | ||||
|         for (auto& pair : boost::make_iterator_range(registered_surfaces.equal_range(interval))) { | ||||
|             surfaces.push_back(*pair.second.begin()); | ||||
|         } | ||||
|         return surfaces; | ||||
|     } | ||||
|  | ||||
|     void ReserveSurface(const SurfaceParams& params, std::shared_ptr<TSurface> surface) { | ||||
|         surface_reserve[params].push_back(std::move(surface)); | ||||
|     } | ||||
|  | ||||
|     std::shared_ptr<TSurface> TryGetReservedSurface(const SurfaceParams& params) { | ||||
|         auto search{surface_reserve.find(params)}; | ||||
|         if (search == surface_reserve.end()) { | ||||
|             return {}; | ||||
|         } | ||||
|         for (auto& surface : search->second) { | ||||
|             if (!surface->IsRegistered()) { | ||||
|                 return surface; | ||||
|             } | ||||
|         } | ||||
|         return {}; | ||||
|     } | ||||
|  | ||||
|     IntervalType GetSurfaceInterval(std::shared_ptr<TSurface> surface) const { | ||||
|         return IntervalType::right_open(surface->GetCacheAddr(), | ||||
|                                         surface->GetCacheAddr() + surface->GetSizeInBytes()); | ||||
|     } | ||||
|  | ||||
|     VideoCore::RasterizerInterface& rasterizer; | ||||
|  | ||||
|     u64 ticks{}; | ||||
|  | ||||
|     IntervalMap registered_surfaces; | ||||
|  | ||||
|     /// The surface reserve is a "backup" cache, this is where we put unique surfaces that have | ||||
|     /// previously been used. This is to prevent surfaces from being constantly created and | ||||
|     /// destroyed when used with different surface parameters. | ||||
|     std::unordered_map<SurfaceParams, std::list<std::shared_ptr<TSurface>>> surface_reserve; | ||||
| }; | ||||
|  | ||||
| struct DummyExecutionContext {}; | ||||
|  | ||||
| template <typename TSurface, typename TView> | ||||
| class TextureCacheContextless : protected TextureCache<TSurface, TView, DummyExecutionContext> { | ||||
|     using Base = TextureCache<TSurface, TView, DummyExecutionContext>; | ||||
|  | ||||
| public: | ||||
|     void InvalidateRegion(CacheAddr addr, std::size_t size) { | ||||
|         Base::InvalidateRegion(addr, size); | ||||
|     } | ||||
|  | ||||
|     TView* GetTextureSurface(const Tegra::Texture::FullTextureInfo& config) { | ||||
|         return RemoveContext(Base::GetTextureSurface({}, config)); | ||||
|     } | ||||
|  | ||||
|     TView* GetDepthBufferSurface(bool preserve_contents) { | ||||
|         return RemoveContext(Base::GetDepthBufferSurface({}, preserve_contents)); | ||||
|     } | ||||
|  | ||||
|     TView* GetColorBufferSurface(std::size_t index, bool preserve_contents) { | ||||
|         return RemoveContext(Base::GetColorBufferSurface({}, index, preserve_contents)); | ||||
|     } | ||||
|  | ||||
|     TView* GetFermiSurface(const Tegra::Engines::Fermi2D::Regs::Surface& config) { | ||||
|         return RemoveContext(Base::GetFermiSurface({}, config)); | ||||
|     } | ||||
|  | ||||
|     std::shared_ptr<TSurface> TryFindFramebufferSurface(const u8* host_ptr) const { | ||||
|         return Base::TryFindFramebufferSurface(host_ptr); | ||||
|     } | ||||
|  | ||||
|     u64 Tick() { | ||||
|         return Base::Tick(); | ||||
|     } | ||||
|  | ||||
| protected: | ||||
|     explicit TextureCacheContextless(Core::System& system, | ||||
|                                      VideoCore::RasterizerInterface& rasterizer) | ||||
|         : TextureCache<TSurface, TView, DummyExecutionContext>{system, rasterizer} {} | ||||
|  | ||||
|     virtual TView* TryFastGetSurfaceView( | ||||
|         GPUVAddr gpu_addr, VAddr cpu_addr, u8* host_ptr, const SurfaceParams& params, | ||||
|         bool preserve_contents, const std::vector<std::shared_ptr<TSurface>>& overlaps) = 0; | ||||
|  | ||||
| private: | ||||
|     std::tuple<TView*, DummyExecutionContext> TryFastGetSurfaceView( | ||||
|         DummyExecutionContext, GPUVAddr gpu_addr, VAddr cpu_addr, u8* host_ptr, | ||||
|         const SurfaceParams& params, bool preserve_contents, | ||||
|         const std::vector<std::shared_ptr<TSurface>>& overlaps) { | ||||
|         return {TryFastGetSurfaceView(gpu_addr, cpu_addr, host_ptr, params, preserve_contents, | ||||
|                                       overlaps), | ||||
|                 {}}; | ||||
|     } | ||||
|  | ||||
|     TView* RemoveContext(std::tuple<TView*, DummyExecutionContext> return_value) { | ||||
|         const auto [view, exctx] = return_value; | ||||
|         return view; | ||||
|     } | ||||
| }; | ||||
|  | ||||
| template <typename TTextureCache, typename TView> | ||||
| class SurfaceBaseContextless : public SurfaceBase<TTextureCache, TView, DummyExecutionContext> { | ||||
| public: | ||||
|     DummyExecutionContext DownloadTexture(DummyExecutionContext) { | ||||
|         DownloadTextureImpl(); | ||||
|         return {}; | ||||
|     } | ||||
|  | ||||
|     DummyExecutionContext UploadTexture(DummyExecutionContext) { | ||||
|         UploadTextureImpl(); | ||||
|         return {}; | ||||
|     } | ||||
|  | ||||
| protected: | ||||
|     explicit SurfaceBaseContextless(TTextureCache& texture_cache, const SurfaceParams& params) | ||||
|         : SurfaceBase<TTextureCache, TView, DummyExecutionContext>{texture_cache, params} {} | ||||
|  | ||||
|     virtual void DownloadTextureImpl() = 0; | ||||
|  | ||||
|     virtual void UploadTextureImpl() = 0; | ||||
| }; | ||||
|  | ||||
| } // namespace VideoCommon | ||||
							
								
								
									
										118
									
								
								src/video_core/texture_cache/surface_base.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								src/video_core/texture_cache/surface_base.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,118 @@ | ||||
| // Copyright 2019 yuzu Emulator Project | ||||
| // Licensed under GPLv2 or any later version | ||||
| // Refer to the license.txt file included. | ||||
|  | ||||
| #include "common/assert.h" | ||||
| #include "common/common_types.h" | ||||
| #include "video_core/morton.h" | ||||
| #include "video_core/texture_cache/surface_base.h" | ||||
| #include "video_core/texture_cache/surface_params.h" | ||||
| #include "video_core/textures/convert.h" | ||||
|  | ||||
| namespace VideoCommon { | ||||
|  | ||||
| using Tegra::Texture::ConvertFromGuestToHost; | ||||
| using VideoCore::MortonSwizzleMode; | ||||
|  | ||||
| namespace { | ||||
| void SwizzleFunc(MortonSwizzleMode mode, u8* memory, const SurfaceParams& params, u8* buffer, | ||||
|                  u32 level) { | ||||
|     const u32 width{params.GetMipWidth(level)}; | ||||
|     const u32 height{params.GetMipHeight(level)}; | ||||
|     const u32 block_height{params.GetMipBlockHeight(level)}; | ||||
|     const u32 block_depth{params.GetMipBlockDepth(level)}; | ||||
|  | ||||
|     std::size_t guest_offset{params.GetGuestMipmapLevelOffset(level)}; | ||||
|     if (params.IsLayered()) { | ||||
|         std::size_t host_offset{0}; | ||||
|         const std::size_t guest_stride = params.GetGuestLayerSize(); | ||||
|         const std::size_t host_stride = params.GetHostLayerSize(level); | ||||
|         for (u32 layer = 0; layer < params.GetNumLayers(); layer++) { | ||||
|             MortonSwizzle(mode, params.GetPixelFormat(), width, block_height, height, block_depth, | ||||
|                           1, params.GetTileWidthSpacing(), buffer + host_offset, | ||||
|                           memory + guest_offset); | ||||
|             guest_offset += guest_stride; | ||||
|             host_offset += host_stride; | ||||
|         } | ||||
|     } else { | ||||
|         MortonSwizzle(mode, params.GetPixelFormat(), width, block_height, height, block_depth, | ||||
|                       params.GetMipDepth(level), params.GetTileWidthSpacing(), buffer, | ||||
|                       memory + guest_offset); | ||||
|     } | ||||
| } | ||||
| } // Anonymous namespace | ||||
|  | ||||
| SurfaceBaseImpl::SurfaceBaseImpl(const SurfaceParams& params) : params{params} { | ||||
|     staging_buffer.resize(params.GetHostSizeInBytes()); | ||||
| } | ||||
|  | ||||
| SurfaceBaseImpl::~SurfaceBaseImpl() = default; | ||||
|  | ||||
| void SurfaceBaseImpl::LoadBuffer() { | ||||
|     if (params.IsTiled()) { | ||||
|         ASSERT_MSG(params.GetBlockWidth() == 1, "Block width is defined as {} on texture target {}", | ||||
|                    params.GetBlockWidth(), static_cast<u32>(params.GetTarget())); | ||||
|         for (u32 level = 0; level < params.GetNumLevels(); ++level) { | ||||
|             u8* const buffer{GetStagingBufferLevelData(level)}; | ||||
|             SwizzleFunc(MortonSwizzleMode::MortonToLinear, host_ptr, params, buffer, level); | ||||
|         } | ||||
|     } else { | ||||
|         ASSERT_MSG(params.GetNumLevels() == 1, "Linear mipmap loading is not implemented"); | ||||
|         const u32 bpp{GetFormatBpp(params.GetPixelFormat()) / CHAR_BIT}; | ||||
|         const u32 block_width{params.GetDefaultBlockWidth()}; | ||||
|         const u32 block_height{params.GetDefaultBlockHeight()}; | ||||
|         const u32 width{(params.GetWidth() + block_width - 1) / block_width}; | ||||
|         const u32 height{(params.GetHeight() + block_height - 1) / block_height}; | ||||
|         const u32 copy_size{width * bpp}; | ||||
|         if (params.GetPitch() == copy_size) { | ||||
|             std::memcpy(staging_buffer.data(), host_ptr, params.GetHostSizeInBytes()); | ||||
|         } else { | ||||
|             const u8* start{host_ptr}; | ||||
|             u8* write_to{staging_buffer.data()}; | ||||
|             for (u32 h = height; h > 0; --h) { | ||||
|                 std::memcpy(write_to, start, copy_size); | ||||
|                 start += params.GetPitch(); | ||||
|                 write_to += copy_size; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     for (u32 level = 0; level < params.GetNumLevels(); ++level) { | ||||
|         ConvertFromGuestToHost(GetStagingBufferLevelData(level), params.GetPixelFormat(), | ||||
|                                params.GetMipWidth(level), params.GetMipHeight(level), | ||||
|                                params.GetMipDepth(level), true, true); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void SurfaceBaseImpl::FlushBuffer() { | ||||
|     if (params.IsTiled()) { | ||||
|         ASSERT_MSG(params.GetBlockWidth() == 1, "Block width is defined as {}", | ||||
|                    params.GetBlockWidth()); | ||||
|         for (u32 level = 0; level < params.GetNumLevels(); ++level) { | ||||
|             u8* const buffer = GetStagingBufferLevelData(level); | ||||
|             SwizzleFunc(MortonSwizzleMode::LinearToMorton, GetHostPtr(), params, buffer, level); | ||||
|         } | ||||
|     } else { | ||||
|         UNIMPLEMENTED(); | ||||
|         /* | ||||
|         ASSERT(params.GetTarget() == SurfaceTarget::Texture2D); | ||||
|         ASSERT(params.GetNumLevels() == 1); | ||||
|  | ||||
|         const u32 bpp{params.GetFormatBpp() / 8}; | ||||
|         const u32 copy_size{params.GetWidth() * bpp}; | ||||
|         if (params.GetPitch() == copy_size) { | ||||
|             std::memcpy(host_ptr, staging_buffer.data(), GetSizeInBytes()); | ||||
|         } else { | ||||
|             u8* start{host_ptr}; | ||||
|             const u8* read_to{staging_buffer.data()}; | ||||
|             for (u32 h = params.GetHeight(); h > 0; --h) { | ||||
|                 std::memcpy(start, read_to, copy_size); | ||||
|                 start += params.GetPitch(); | ||||
|                 read_to += copy_size; | ||||
|             } | ||||
|         } | ||||
|         */ | ||||
|     } | ||||
| } | ||||
|  | ||||
| } // namespace VideoCommon | ||||
							
								
								
									
										172
									
								
								src/video_core/texture_cache/surface_base.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										172
									
								
								src/video_core/texture_cache/surface_base.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,172 @@ | ||||
| // Copyright 2019 yuzu Emulator Project | ||||
| // Licensed under GPLv2 or any later version | ||||
| // Refer to the license.txt file included. | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <unordered_map> | ||||
|  | ||||
| #include "common/assert.h" | ||||
| #include "common/common_types.h" | ||||
| #include "video_core/gpu.h" | ||||
| #include "video_core/texture_cache/surface_params.h" | ||||
| #include "video_core/texture_cache/surface_view.h" | ||||
|  | ||||
| namespace VideoCommon { | ||||
|  | ||||
| class SurfaceBaseImpl { | ||||
| public: | ||||
|     void LoadBuffer(); | ||||
|  | ||||
|     void FlushBuffer(); | ||||
|  | ||||
|     GPUVAddr GetGpuAddr() const { | ||||
|         ASSERT(is_registered); | ||||
|         return gpu_addr; | ||||
|     } | ||||
|  | ||||
|     VAddr GetCpuAddr() const { | ||||
|         ASSERT(is_registered); | ||||
|         return cpu_addr; | ||||
|     } | ||||
|  | ||||
|     u8* GetHostPtr() const { | ||||
|         ASSERT(is_registered); | ||||
|         return host_ptr; | ||||
|     } | ||||
|  | ||||
|     CacheAddr GetCacheAddr() const { | ||||
|         ASSERT(is_registered); | ||||
|         return cache_addr; | ||||
|     } | ||||
|  | ||||
|     const SurfaceParams& GetSurfaceParams() const { | ||||
|         return params; | ||||
|     } | ||||
|  | ||||
|     void Register(GPUVAddr gpu_addr_, VAddr cpu_addr_, u8* host_ptr_) { | ||||
|         ASSERT(!is_registered); | ||||
|         is_registered = true; | ||||
|         gpu_addr = gpu_addr_; | ||||
|         cpu_addr = cpu_addr_; | ||||
|         host_ptr = host_ptr_; | ||||
|         cache_addr = ToCacheAddr(host_ptr_); | ||||
|         DecorateSurfaceName(); | ||||
|     } | ||||
|  | ||||
|     void Unregister() { | ||||
|         ASSERT(is_registered); | ||||
|         is_registered = false; | ||||
|     } | ||||
|  | ||||
|     bool IsRegistered() const { | ||||
|         return is_registered; | ||||
|     } | ||||
|  | ||||
|     std::size_t GetSizeInBytes() const { | ||||
|         return params.GetGuestSizeInBytes(); | ||||
|     } | ||||
|  | ||||
|     u8* GetStagingBufferLevelData(u32 level) { | ||||
|         return staging_buffer.data() + params.GetHostMipmapLevelOffset(level); | ||||
|     } | ||||
|  | ||||
| protected: | ||||
|     explicit SurfaceBaseImpl(const SurfaceParams& params); | ||||
|     ~SurfaceBaseImpl(); // non-virtual is intended | ||||
|  | ||||
|     virtual void DecorateSurfaceName() = 0; | ||||
|  | ||||
|     const SurfaceParams params; | ||||
|  | ||||
| private: | ||||
|     GPUVAddr gpu_addr{}; | ||||
|     VAddr cpu_addr{}; | ||||
|     u8* host_ptr{}; | ||||
|     CacheAddr cache_addr{}; | ||||
|     bool is_registered{}; | ||||
|  | ||||
|     std::vector<u8> staging_buffer; | ||||
| }; | ||||
|  | ||||
| template <typename TTextureCache, typename TView, typename TExecutionContext> | ||||
| class SurfaceBase : public SurfaceBaseImpl { | ||||
|     static_assert(std::is_trivially_copyable_v<TExecutionContext>); | ||||
|  | ||||
| public: | ||||
|     virtual TExecutionContext UploadTexture(TExecutionContext exctx) = 0; | ||||
|  | ||||
|     virtual TExecutionContext DownloadTexture(TExecutionContext exctx) = 0; | ||||
|  | ||||
|     TView* TryGetView(GPUVAddr view_addr, const SurfaceParams& view_params) { | ||||
|         if (view_addr < GetGpuAddr() || !params.IsFamiliar(view_params)) { | ||||
|             // It can't be a view if it's in a prior address. | ||||
|             return {}; | ||||
|         } | ||||
|  | ||||
|         const auto relative_offset{static_cast<u64>(view_addr - GetGpuAddr())}; | ||||
|         const auto it{view_offset_map.find(relative_offset)}; | ||||
|         if (it == view_offset_map.end()) { | ||||
|             // Couldn't find an aligned view. | ||||
|             return {}; | ||||
|         } | ||||
|         const auto [layer, level] = it->second; | ||||
|  | ||||
|         if (!params.IsViewValid(view_params, layer, level)) { | ||||
|             return {}; | ||||
|         } | ||||
|  | ||||
|         return GetView(layer, view_params.GetNumLayers(), level, view_params.GetNumLevels()); | ||||
|     } | ||||
|  | ||||
|     void MarkAsModified(bool is_modified_) { | ||||
|         is_modified = is_modified_; | ||||
|         if (is_modified_) { | ||||
|             modification_tick = texture_cache.Tick(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     TView* GetView(GPUVAddr view_addr, const SurfaceParams& view_params) { | ||||
|         TView* view{TryGetView(view_addr, view_params)}; | ||||
|         ASSERT(view != nullptr); | ||||
|         return view; | ||||
|     } | ||||
|  | ||||
|     bool IsModified() const { | ||||
|         return is_modified; | ||||
|     } | ||||
|  | ||||
|     u64 GetModificationTick() const { | ||||
|         return modification_tick; | ||||
|     } | ||||
|  | ||||
| protected: | ||||
|     explicit SurfaceBase(TTextureCache& texture_cache, const SurfaceParams& params) | ||||
|         : SurfaceBaseImpl{params}, texture_cache{texture_cache}, | ||||
|           view_offset_map{params.CreateViewOffsetMap()} {} | ||||
|  | ||||
|     ~SurfaceBase() = default; | ||||
|  | ||||
|     virtual std::unique_ptr<TView> CreateView(const ViewKey& view_key) = 0; | ||||
|  | ||||
| private: | ||||
|     TView* GetView(u32 base_layer, u32 num_layers, u32 base_level, u32 num_levels) { | ||||
|         const ViewKey key{base_layer, num_layers, base_level, num_levels}; | ||||
|         const auto [entry, is_cache_miss] = views.try_emplace(key); | ||||
|         auto& view{entry->second}; | ||||
|         if (is_cache_miss) { | ||||
|             view = CreateView(key); | ||||
|         } | ||||
|         return view.get(); | ||||
|     } | ||||
|  | ||||
|     TTextureCache& texture_cache; | ||||
|     const std::map<u64, std::pair<u32, u32>> view_offset_map; | ||||
|  | ||||
|     std::unordered_map<ViewKey, std::unique_ptr<TView>> views; | ||||
|  | ||||
|     bool is_modified{}; | ||||
|     u64 modification_tick{}; | ||||
| }; | ||||
|  | ||||
| } // namespace VideoCommon | ||||
| @@ -2,22 +2,17 @@ | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include "common/alignment.h" | ||||
| #include "common/assert.h" | ||||
| #include <map> | ||||
| 
 | ||||
| #include "common/cityhash.h" | ||||
| #include "common/common_types.h" | ||||
| #include "common/alignment.h" | ||||
| #include "core/core.h" | ||||
| #include "video_core/morton.h" | ||||
| #include "video_core/surface.h" | ||||
| #include "video_core/texture_cache.h" | ||||
| #include "video_core/textures/convert.h" | ||||
| #include "video_core/texture_cache/surface_params.h" | ||||
| #include "video_core/textures/decoders.h" | ||||
| #include "video_core/textures/texture.h" | ||||
| 
 | ||||
| namespace VideoCommon { | ||||
| 
 | ||||
| using VideoCore::MortonSwizzleMode; | ||||
| 
 | ||||
| using VideoCore::Surface::ComponentTypeFromDepthFormat; | ||||
| using VideoCore::Surface::ComponentTypeFromRenderTarget; | ||||
| using VideoCore::Surface::ComponentTypeFromTexture; | ||||
| @@ -27,115 +22,12 @@ using VideoCore::Surface::PixelFormatFromTextureFormat; | ||||
| using VideoCore::Surface::SurfaceTarget; | ||||
| using VideoCore::Surface::SurfaceTargetFromTextureType; | ||||
| 
 | ||||
| using Tegra::Texture::ConvertFromGuestToHost; | ||||
| 
 | ||||
| namespace { | ||||
| 
 | ||||
| constexpr u32 GetMipmapSize(bool uncompressed, u32 mip_size, u32 tile) { | ||||
|     return uncompressed ? mip_size : std::max(1U, (mip_size + tile - 1) / tile); | ||||
| } | ||||
| 
 | ||||
| void SwizzleFunc(MortonSwizzleMode mode, u8* memory, const SurfaceParams& params, u8* buffer, | ||||
|                  u32 level) { | ||||
|     const u32 width{params.GetMipWidth(level)}; | ||||
|     const u32 height{params.GetMipHeight(level)}; | ||||
|     const u32 block_height{params.GetMipBlockHeight(level)}; | ||||
|     const u32 block_depth{params.GetMipBlockDepth(level)}; | ||||
| 
 | ||||
|     std::size_t guest_offset{params.GetGuestMipmapLevelOffset(level)}; | ||||
|     if (params.IsLayered()) { | ||||
|         std::size_t host_offset{0}; | ||||
|         const std::size_t guest_stride = params.GetGuestLayerSize(); | ||||
|         const std::size_t host_stride = params.GetHostLayerSize(level); | ||||
|         for (u32 layer = 0; layer < params.GetNumLayers(); layer++) { | ||||
|             MortonSwizzle(mode, params.GetPixelFormat(), width, block_height, height, block_depth, | ||||
|                           1, params.GetTileWidthSpacing(), buffer + host_offset, | ||||
|                           memory + guest_offset); | ||||
|             guest_offset += guest_stride; | ||||
|             host_offset += host_stride; | ||||
|         } | ||||
|     } else { | ||||
|         MortonSwizzle(mode, params.GetPixelFormat(), width, block_height, height, block_depth, | ||||
|                       params.GetMipDepth(level), params.GetTileWidthSpacing(), buffer, | ||||
|                       memory + guest_offset); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| } // Anonymous namespace
 | ||||
| 
 | ||||
| SurfaceBaseImpl::SurfaceBaseImpl(const SurfaceParams& params) : params{params} { | ||||
|     staging_buffer.resize(params.GetHostSizeInBytes()); | ||||
| } | ||||
| 
 | ||||
| SurfaceBaseImpl::~SurfaceBaseImpl() = default; | ||||
| 
 | ||||
| void SurfaceBaseImpl::LoadBuffer() { | ||||
|     if (params.IsTiled()) { | ||||
|         ASSERT_MSG(params.GetBlockWidth() == 1, "Block width is defined as {} on texture target {}", | ||||
|                    params.GetBlockWidth(), static_cast<u32>(params.GetTarget())); | ||||
|         for (u32 level = 0; level < params.GetNumLevels(); ++level) { | ||||
|             u8* const buffer{GetStagingBufferLevelData(level)}; | ||||
|             SwizzleFunc(MortonSwizzleMode::MortonToLinear, host_ptr, params, buffer, level); | ||||
|         } | ||||
|     } else { | ||||
|         ASSERT_MSG(params.GetNumLevels() == 1, "Linear mipmap loading is not implemented"); | ||||
|         const u32 bpp{GetFormatBpp(params.GetPixelFormat()) / CHAR_BIT}; | ||||
|         const u32 block_width{params.GetDefaultBlockWidth()}; | ||||
|         const u32 block_height{params.GetDefaultBlockHeight()}; | ||||
|         const u32 width{(params.GetWidth() + block_width - 1) / block_width}; | ||||
|         const u32 height{(params.GetHeight() + block_height - 1) / block_height}; | ||||
|         const u32 copy_size{width * bpp}; | ||||
|         if (params.GetPitch() == copy_size) { | ||||
|             std::memcpy(staging_buffer.data(), host_ptr, params.GetHostSizeInBytes()); | ||||
|         } else { | ||||
|             const u8* start{host_ptr}; | ||||
|             u8* write_to{staging_buffer.data()}; | ||||
|             for (u32 h = height; h > 0; --h) { | ||||
|                 std::memcpy(write_to, start, copy_size); | ||||
|                 start += params.GetPitch(); | ||||
|                 write_to += copy_size; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     for (u32 level = 0; level < params.GetNumLevels(); ++level) { | ||||
|         ConvertFromGuestToHost(GetStagingBufferLevelData(level), params.GetPixelFormat(), | ||||
|                                params.GetMipWidth(level), params.GetMipHeight(level), | ||||
|                                params.GetMipDepth(level), true, true); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void SurfaceBaseImpl::FlushBuffer() { | ||||
|     if (params.IsTiled()) { | ||||
|         ASSERT_MSG(params.GetBlockWidth() == 1, "Block width is defined as {}", | ||||
|                    params.GetBlockWidth()); | ||||
|         for (u32 level = 0; level < params.GetNumLevels(); ++level) { | ||||
|             u8* const buffer = GetStagingBufferLevelData(level); | ||||
|             SwizzleFunc(MortonSwizzleMode::LinearToMorton, GetHostPtr(), params, buffer, level); | ||||
|         } | ||||
|     } else { | ||||
|         UNIMPLEMENTED(); | ||||
|         /*
 | ||||
|         ASSERT(params.GetTarget() == SurfaceTarget::Texture2D); | ||||
|         ASSERT(params.GetNumLevels() == 1); | ||||
| 
 | ||||
|         const u32 bpp{params.GetFormatBpp() / 8}; | ||||
|         const u32 copy_size{params.GetWidth() * bpp}; | ||||
|         if (params.GetPitch() == copy_size) { | ||||
|             std::memcpy(host_ptr, staging_buffer.data(), GetSizeInBytes()); | ||||
|         } else { | ||||
|             u8* start{host_ptr}; | ||||
|             const u8* read_to{staging_buffer.data()}; | ||||
|             for (u32 h = params.GetHeight(); h > 0; --h) { | ||||
|                 std::memcpy(start, read_to, copy_size); | ||||
|                 start += params.GetPitch(); | ||||
|                 read_to += copy_size; | ||||
|             } | ||||
|         } | ||||
|         */ | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| SurfaceParams SurfaceParams::CreateForTexture(Core::System& system, | ||||
|                                               const Tegra::Texture::FullTextureInfo& config) { | ||||
|     SurfaceParams params; | ||||
| @@ -517,14 +409,4 @@ bool HasheableSurfaceParams::operator==(const HasheableSurfaceParams& rhs) const | ||||
|                     rhs.type, rhs.target); | ||||
| } | ||||
| 
 | ||||
| std::size_t ViewKey::Hash() const { | ||||
|     return static_cast<std::size_t>( | ||||
|         Common::CityHash64(reinterpret_cast<const char*>(this), sizeof(*this))); | ||||
| } | ||||
| 
 | ||||
| bool ViewKey::operator==(const ViewKey& rhs) const { | ||||
|     return std::tie(base_layer, num_layers, base_level, num_levels) == | ||||
|            std::tie(rhs.base_layer, rhs.num_layers, rhs.base_level, rhs.num_levels); | ||||
| } | ||||
| 
 | ||||
| } // namespace VideoCommon
 | ||||
							
								
								
									
										229
									
								
								src/video_core/texture_cache/surface_params.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										229
									
								
								src/video_core/texture_cache/surface_params.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,229 @@ | ||||
| // Copyright 2019 yuzu Emulator Project | ||||
| // Licensed under GPLv2 or any later version | ||||
| // Refer to the license.txt file included. | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <map> | ||||
|  | ||||
| #include "common/common_types.h" | ||||
| #include "video_core/engines/fermi_2d.h" | ||||
| #include "video_core/engines/maxwell_3d.h" | ||||
| #include "video_core/surface.h" | ||||
|  | ||||
| namespace VideoCommon { | ||||
|  | ||||
| class HasheableSurfaceParams { | ||||
| public: | ||||
|     std::size_t Hash() const; | ||||
|  | ||||
|     bool operator==(const HasheableSurfaceParams& rhs) const; | ||||
|  | ||||
|     bool operator!=(const HasheableSurfaceParams& rhs) const { | ||||
|         return !operator==(rhs); | ||||
|     } | ||||
|  | ||||
| protected: | ||||
|     // Avoid creation outside of a managed environment. | ||||
|     HasheableSurfaceParams() = default; | ||||
|  | ||||
|     bool is_tiled; | ||||
|     bool srgb_conversion; | ||||
|     u32 block_width; | ||||
|     u32 block_height; | ||||
|     u32 block_depth; | ||||
|     u32 tile_width_spacing; | ||||
|     u32 width; | ||||
|     u32 height; | ||||
|     u32 depth; | ||||
|     u32 pitch; | ||||
|     u32 unaligned_height; | ||||
|     u32 num_levels; | ||||
|     VideoCore::Surface::PixelFormat pixel_format; | ||||
|     VideoCore::Surface::ComponentType component_type; | ||||
|     VideoCore::Surface::SurfaceType type; | ||||
|     VideoCore::Surface::SurfaceTarget target; | ||||
| }; | ||||
|  | ||||
| class SurfaceParams final : public HasheableSurfaceParams { | ||||
| public: | ||||
|     /// Creates SurfaceCachedParams from a texture configuration. | ||||
|     static SurfaceParams CreateForTexture(Core::System& system, | ||||
|                                           const Tegra::Texture::FullTextureInfo& config); | ||||
|  | ||||
|     /// Creates SurfaceCachedParams for a depth buffer configuration. | ||||
|     static SurfaceParams CreateForDepthBuffer( | ||||
|         Core::System& system, u32 zeta_width, u32 zeta_height, Tegra::DepthFormat format, | ||||
|         u32 block_width, u32 block_height, u32 block_depth, | ||||
|         Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout type); | ||||
|  | ||||
|     /// Creates SurfaceCachedParams from a framebuffer configuration. | ||||
|     static SurfaceParams CreateForFramebuffer(Core::System& system, std::size_t index); | ||||
|  | ||||
|     /// Creates SurfaceCachedParams from a Fermi2D surface configuration. | ||||
|     static SurfaceParams CreateForFermiCopySurface( | ||||
|         const Tegra::Engines::Fermi2D::Regs::Surface& config); | ||||
|  | ||||
|     bool IsTiled() const { | ||||
|         return is_tiled; | ||||
|     } | ||||
|  | ||||
|     bool GetSrgbConversion() const { | ||||
|         return srgb_conversion; | ||||
|     } | ||||
|  | ||||
|     u32 GetBlockWidth() const { | ||||
|         return block_width; | ||||
|     } | ||||
|  | ||||
|     u32 GetTileWidthSpacing() const { | ||||
|         return tile_width_spacing; | ||||
|     } | ||||
|  | ||||
|     u32 GetWidth() const { | ||||
|         return width; | ||||
|     } | ||||
|  | ||||
|     u32 GetHeight() const { | ||||
|         return height; | ||||
|     } | ||||
|  | ||||
|     u32 GetDepth() const { | ||||
|         return depth; | ||||
|     } | ||||
|  | ||||
|     u32 GetPitch() const { | ||||
|         return pitch; | ||||
|     } | ||||
|  | ||||
|     u32 GetNumLevels() const { | ||||
|         return num_levels; | ||||
|     } | ||||
|  | ||||
|     VideoCore::Surface::PixelFormat GetPixelFormat() const { | ||||
|         return pixel_format; | ||||
|     } | ||||
|  | ||||
|     VideoCore::Surface::ComponentType GetComponentType() const { | ||||
|         return component_type; | ||||
|     } | ||||
|  | ||||
|     VideoCore::Surface::SurfaceTarget GetTarget() const { | ||||
|         return target; | ||||
|     } | ||||
|  | ||||
|     VideoCore::Surface::SurfaceType GetType() const { | ||||
|         return type; | ||||
|     } | ||||
|  | ||||
|     std::size_t GetGuestSizeInBytes() const { | ||||
|         return guest_size_in_bytes; | ||||
|     } | ||||
|  | ||||
|     std::size_t GetHostSizeInBytes() const { | ||||
|         return host_size_in_bytes; | ||||
|     } | ||||
|  | ||||
|     u32 GetNumLayers() const { | ||||
|         return num_layers; | ||||
|     } | ||||
|  | ||||
|     /// Returns the width of a given mipmap level. | ||||
|     u32 GetMipWidth(u32 level) const; | ||||
|  | ||||
|     /// Returns the height of a given mipmap level. | ||||
|     u32 GetMipHeight(u32 level) const; | ||||
|  | ||||
|     /// Returns the depth of a given mipmap level. | ||||
|     u32 GetMipDepth(u32 level) const; | ||||
|  | ||||
|     /// Returns true if these parameters are from a layered surface. | ||||
|     bool IsLayered() const; | ||||
|  | ||||
|     /// Returns the block height of a given mipmap level. | ||||
|     u32 GetMipBlockHeight(u32 level) const; | ||||
|  | ||||
|     /// Returns the block depth of a given mipmap level. | ||||
|     u32 GetMipBlockDepth(u32 level) const; | ||||
|  | ||||
|     /// Returns the offset in bytes in guest memory of a given mipmap level. | ||||
|     std::size_t GetGuestMipmapLevelOffset(u32 level) const; | ||||
|  | ||||
|     /// Returns the offset in bytes in host memory (linear) of a given mipmap level. | ||||
|     std::size_t GetHostMipmapLevelOffset(u32 level) const; | ||||
|  | ||||
|     /// Returns the size in bytes in host memory (linear) of a given mipmap level. | ||||
|     std::size_t GetHostMipmapSize(u32 level) const; | ||||
|  | ||||
|     /// Returns the size of a layer in bytes in guest memory. | ||||
|     std::size_t GetGuestLayerSize() const; | ||||
|  | ||||
|     /// Returns the size of a layer in bytes in host memory for a given mipmap level. | ||||
|     std::size_t GetHostLayerSize(u32 level) const; | ||||
|  | ||||
|     /// Returns the default block width. | ||||
|     u32 GetDefaultBlockWidth() const; | ||||
|  | ||||
|     /// Returns the default block height. | ||||
|     u32 GetDefaultBlockHeight() const; | ||||
|  | ||||
|     /// Returns the bits per pixel. | ||||
|     u32 GetBitsPerPixel() const; | ||||
|  | ||||
|     /// Returns the bytes per pixel. | ||||
|     u32 GetBytesPerPixel() const; | ||||
|  | ||||
|     /// Returns true if another surface can be familiar with this. This is a loosely defined term | ||||
|     /// that reflects the possibility of these two surface parameters potentially being part of a | ||||
|     /// bigger superset. | ||||
|     bool IsFamiliar(const SurfaceParams& view_params) const; | ||||
|  | ||||
|     /// Returns true if the pixel format is a depth and/or stencil format. | ||||
|     bool IsPixelFormatZeta() const; | ||||
|  | ||||
|     /// Creates a map that redirects an address difference to a layer and mipmap level. | ||||
|     std::map<u64, std::pair<u32, u32>> CreateViewOffsetMap() const; | ||||
|  | ||||
|     /// Returns true if the passed surface view parameters is equal or a valid subset of this. | ||||
|     bool IsViewValid(const SurfaceParams& view_params, u32 layer, u32 level) const; | ||||
|  | ||||
| private: | ||||
|     /// Calculates values that can be deduced from HasheableSurfaceParams. | ||||
|     void CalculateCachedValues(); | ||||
|  | ||||
|     /// Returns the size of a given mipmap level inside a layer. | ||||
|     std::size_t GetInnerMipmapMemorySize(u32 level, bool as_host_size, bool uncompressed) const; | ||||
|  | ||||
|     /// Returns the size of all mipmap levels and aligns as needed. | ||||
|     std::size_t GetInnerMemorySize(bool as_host_size, bool layer_only, bool uncompressed) const; | ||||
|  | ||||
|     /// Returns the size of a layer | ||||
|     std::size_t GetLayerSize(bool as_host_size, bool uncompressed) const; | ||||
|  | ||||
|     /// Returns true if the passed view width and height match the size of this params in a given | ||||
|     /// mipmap level. | ||||
|     bool IsDimensionValid(const SurfaceParams& view_params, u32 level) const; | ||||
|  | ||||
|     /// Returns true if the passed view depth match the size of this params in a given mipmap level. | ||||
|     bool IsDepthValid(const SurfaceParams& view_params, u32 level) const; | ||||
|  | ||||
|     /// Returns true if the passed view layers and mipmap levels are in bounds. | ||||
|     bool IsInBounds(const SurfaceParams& view_params, u32 layer, u32 level) const; | ||||
|  | ||||
|     std::size_t guest_size_in_bytes; | ||||
|     std::size_t host_size_in_bytes; | ||||
|     u32 num_layers; | ||||
| }; | ||||
|  | ||||
| } // namespace VideoCommon | ||||
|  | ||||
| namespace std { | ||||
|  | ||||
| template <> | ||||
| struct hash<VideoCommon::SurfaceParams> { | ||||
|     std::size_t operator()(const VideoCommon::SurfaceParams& k) const noexcept { | ||||
|         return k.Hash(); | ||||
|     } | ||||
| }; | ||||
|  | ||||
| } // namespace std | ||||
							
								
								
									
										23
									
								
								src/video_core/texture_cache/surface_view.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								src/video_core/texture_cache/surface_view.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| // Copyright 2019 yuzu Emulator Project | ||||
| // Licensed under GPLv2 or any later version | ||||
| // Refer to the license.txt file included. | ||||
|  | ||||
| #include <tuple> | ||||
|  | ||||
| #include "common/common_types.h" | ||||
| #include "video_core/texture_cache/surface_view.h" | ||||
|  | ||||
| namespace VideoCommon { | ||||
|  | ||||
| std::size_t ViewKey::Hash() const { | ||||
|     return static_cast<std::size_t>(base_layer) ^ static_cast<std::size_t>(num_layers << 16) ^ | ||||
|            (static_cast<std::size_t>(base_level) << 32) ^ | ||||
|            (static_cast<std::size_t>(num_levels) << 48); | ||||
| } | ||||
|  | ||||
| bool ViewKey::operator==(const ViewKey& rhs) const { | ||||
|     return std::tie(base_layer, num_layers, base_level, num_levels) == | ||||
|            std::tie(rhs.base_layer, rhs.num_layers, rhs.base_level, rhs.num_levels); | ||||
| } | ||||
|  | ||||
| } // namespace VideoCommon | ||||
							
								
								
									
										35
									
								
								src/video_core/texture_cache/surface_view.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								src/video_core/texture_cache/surface_view.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | ||||
| // Copyright 2019 yuzu Emulator Project | ||||
| // Licensed under GPLv2 or any later version | ||||
| // Refer to the license.txt file included. | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <functional> | ||||
|  | ||||
| #include "common/common_types.h" | ||||
|  | ||||
| namespace VideoCommon { | ||||
|  | ||||
| struct ViewKey { | ||||
|     std::size_t Hash() const; | ||||
|  | ||||
|     bool operator==(const ViewKey& rhs) const; | ||||
|  | ||||
|     u32 base_layer{}; | ||||
|     u32 num_layers{}; | ||||
|     u32 base_level{}; | ||||
|     u32 num_levels{}; | ||||
| }; | ||||
|  | ||||
| } // namespace VideoCommon | ||||
|  | ||||
| namespace std { | ||||
|  | ||||
| template <> | ||||
| struct hash<VideoCommon::ViewKey> { | ||||
|     std::size_t operator()(const VideoCommon::ViewKey& k) const noexcept { | ||||
|         return k.Hash(); | ||||
|     } | ||||
| }; | ||||
|  | ||||
| } // namespace std | ||||
							
								
								
									
										282
									
								
								src/video_core/texture_cache/texture_cache.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										282
									
								
								src/video_core/texture_cache/texture_cache.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,282 @@ | ||||
| // Copyright 2019 yuzu Emulator Project | ||||
| // Licensed under GPLv2 or any later version | ||||
| // Refer to the license.txt file included. | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <list> | ||||
| #include <memory> | ||||
| #include <set> | ||||
| #include <tuple> | ||||
| #include <type_traits> | ||||
| #include <unordered_map> | ||||
|  | ||||
| #include <boost/icl/interval_map.hpp> | ||||
| #include <boost/range/iterator_range.hpp> | ||||
|  | ||||
| #include "common/assert.h" | ||||
| #include "common/common_types.h" | ||||
| #include "core/memory.h" | ||||
| #include "video_core/engines/fermi_2d.h" | ||||
| #include "video_core/engines/maxwell_3d.h" | ||||
| #include "video_core/gpu.h" | ||||
| #include "video_core/memory_manager.h" | ||||
| #include "video_core/rasterizer_interface.h" | ||||
| #include "video_core/surface.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" | ||||
|  | ||||
| namespace Core { | ||||
| class System; | ||||
| } | ||||
|  | ||||
| namespace Tegra::Texture { | ||||
| struct FullTextureInfo; | ||||
| } | ||||
|  | ||||
| namespace VideoCore { | ||||
| class RasterizerInterface; | ||||
| } | ||||
|  | ||||
| namespace VideoCommon { | ||||
|  | ||||
| template <typename TSurface, typename TView, typename TExecutionContext> | ||||
| class TextureCache { | ||||
|     static_assert(std::is_trivially_copyable_v<TExecutionContext>); | ||||
|  | ||||
|     using ResultType = std::tuple<TView*, TExecutionContext>; | ||||
|     using IntervalMap = boost::icl::interval_map<CacheAddr, std::set<std::shared_ptr<TSurface>>>; | ||||
|     using IntervalType = typename IntervalMap::interval_type; | ||||
|  | ||||
| public: | ||||
|     void InvalidateRegion(CacheAddr addr, std::size_t size) { | ||||
|         for (const auto& surface : GetSurfacesInRegion(addr, size)) { | ||||
|             if (!surface->IsRegistered()) { | ||||
|                 // Skip duplicates | ||||
|                 continue; | ||||
|             } | ||||
|             Unregister(surface); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     ResultType GetTextureSurface(TExecutionContext exctx, | ||||
|                                  const Tegra::Texture::FullTextureInfo& config) { | ||||
|         const auto gpu_addr{config.tic.Address()}; | ||||
|         if (!gpu_addr) { | ||||
|             return {{}, exctx}; | ||||
|         } | ||||
|         const auto params{SurfaceParams::CreateForTexture(system, config)}; | ||||
|         return GetSurfaceView(exctx, gpu_addr, params, true); | ||||
|     } | ||||
|  | ||||
|     ResultType GetDepthBufferSurface(TExecutionContext exctx, bool preserve_contents) { | ||||
|         const auto& regs{system.GPU().Maxwell3D().regs}; | ||||
|         const auto gpu_addr{regs.zeta.Address()}; | ||||
|         if (!gpu_addr || !regs.zeta_enable) { | ||||
|             return {{}, exctx}; | ||||
|         } | ||||
|         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)}; | ||||
|         return GetSurfaceView(exctx, gpu_addr, depth_params, preserve_contents); | ||||
|     } | ||||
|  | ||||
|     ResultType GetColorBufferSurface(TExecutionContext exctx, std::size_t index, | ||||
|                                      bool preserve_contents) { | ||||
|         ASSERT(index < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets); | ||||
|  | ||||
|         const auto& regs{system.GPU().Maxwell3D().regs}; | ||||
|         if (index >= regs.rt_control.count || regs.rt[index].Address() == 0 || | ||||
|             regs.rt[index].format == Tegra::RenderTargetFormat::NONE) { | ||||
|             return {{}, exctx}; | ||||
|         } | ||||
|  | ||||
|         auto& memory_manager{system.GPU().MemoryManager()}; | ||||
|         const auto& config{system.GPU().Maxwell3D().regs.rt[index]}; | ||||
|         const auto gpu_addr{config.Address() + | ||||
|                             config.base_layer * config.layer_stride * sizeof(u32)}; | ||||
|         if (!gpu_addr) { | ||||
|             return {{}, exctx}; | ||||
|         } | ||||
|  | ||||
|         return GetSurfaceView(exctx, gpu_addr, SurfaceParams::CreateForFramebuffer(system, index), | ||||
|                               preserve_contents); | ||||
|     } | ||||
|  | ||||
|     ResultType GetFermiSurface(TExecutionContext exctx, | ||||
|                                const Tegra::Engines::Fermi2D::Regs::Surface& config) { | ||||
|         return GetSurfaceView(exctx, config.Address(), | ||||
|                               SurfaceParams::CreateForFermiCopySurface(config), true); | ||||
|     } | ||||
|  | ||||
|     std::shared_ptr<TSurface> TryFindFramebufferSurface(const u8* host_ptr) const { | ||||
|         const auto it{registered_surfaces.find(ToCacheAddr(host_ptr))}; | ||||
|         return it != registered_surfaces.end() ? *it->second.begin() : nullptr; | ||||
|     } | ||||
|  | ||||
|     u64 Tick() { | ||||
|         return ++ticks; | ||||
|     } | ||||
|  | ||||
| protected: | ||||
|     TextureCache(Core::System& system, VideoCore::RasterizerInterface& rasterizer) | ||||
|         : system{system}, rasterizer{rasterizer} {} | ||||
|  | ||||
|     ~TextureCache() = default; | ||||
|  | ||||
|     virtual ResultType TryFastGetSurfaceView( | ||||
|         TExecutionContext exctx, GPUVAddr gpu_addr, VAddr cpu_addr, u8* host_ptr, | ||||
|         const SurfaceParams& params, bool preserve_contents, | ||||
|         const std::vector<std::shared_ptr<TSurface>>& overlaps) = 0; | ||||
|  | ||||
|     virtual std::shared_ptr<TSurface> CreateSurface(const SurfaceParams& params) = 0; | ||||
|  | ||||
|     void Register(std::shared_ptr<TSurface> surface, GPUVAddr gpu_addr, VAddr cpu_addr, | ||||
|                   u8* host_ptr) { | ||||
|         surface->Register(gpu_addr, cpu_addr, host_ptr); | ||||
|         registered_surfaces.add({GetSurfaceInterval(surface), {surface}}); | ||||
|         rasterizer.UpdatePagesCachedCount(surface->GetCpuAddr(), surface->GetSizeInBytes(), 1); | ||||
|     } | ||||
|  | ||||
|     void Unregister(std::shared_ptr<TSurface> surface) { | ||||
|         registered_surfaces.subtract({GetSurfaceInterval(surface), {surface}}); | ||||
|         rasterizer.UpdatePagesCachedCount(surface->GetCpuAddr(), surface->GetSizeInBytes(), -1); | ||||
|         surface->Unregister(); | ||||
|     } | ||||
|  | ||||
|     std::shared_ptr<TSurface> GetUncachedSurface(const SurfaceParams& params) { | ||||
|         if (const auto surface = TryGetReservedSurface(params); surface) | ||||
|             return surface; | ||||
|         // No reserved surface available, create a new one and reserve it | ||||
|         auto new_surface{CreateSurface(params)}; | ||||
|         ReserveSurface(params, new_surface); | ||||
|         return new_surface; | ||||
|     } | ||||
|  | ||||
|     Core::System& system; | ||||
|  | ||||
| private: | ||||
|     ResultType GetSurfaceView(TExecutionContext exctx, GPUVAddr gpu_addr, | ||||
|                               const SurfaceParams& params, bool preserve_contents) { | ||||
|         auto& memory_manager{system.GPU().MemoryManager()}; | ||||
|         const auto cpu_addr{memory_manager.GpuToCpuAddress(gpu_addr)}; | ||||
|         DEBUG_ASSERT(cpu_addr); | ||||
|  | ||||
|         const auto host_ptr{memory_manager.GetPointer(gpu_addr)}; | ||||
|         const auto cache_addr{ToCacheAddr(host_ptr)}; | ||||
|         auto overlaps{GetSurfacesInRegion(cache_addr, params.GetGuestSizeInBytes())}; | ||||
|         if (overlaps.empty()) { | ||||
|             return LoadSurfaceView(exctx, gpu_addr, *cpu_addr, host_ptr, params, preserve_contents); | ||||
|         } | ||||
|  | ||||
|         if (overlaps.size() == 1) { | ||||
|             if (TView* view = overlaps[0]->TryGetView(gpu_addr, params); view) { | ||||
|                 return {view, exctx}; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         TView* fast_view; | ||||
|         std::tie(fast_view, exctx) = TryFastGetSurfaceView(exctx, gpu_addr, *cpu_addr, host_ptr, | ||||
|                                                            params, preserve_contents, overlaps); | ||||
|  | ||||
|         if (!fast_view) { | ||||
|             std::sort(overlaps.begin(), overlaps.end(), [](const auto& lhs, const auto& rhs) { | ||||
|                 return lhs->GetModificationTick() < rhs->GetModificationTick(); | ||||
|             }); | ||||
|         } | ||||
|  | ||||
|         for (const auto& surface : overlaps) { | ||||
|             if (!fast_view) { | ||||
|                 // Flush even when we don't care about the contents, to preserve memory not | ||||
|                 // written by the new surface. | ||||
|                 exctx = FlushSurface(exctx, surface); | ||||
|             } | ||||
|             Unregister(surface); | ||||
|         } | ||||
|  | ||||
|         if (fast_view) { | ||||
|             return {fast_view, exctx}; | ||||
|         } | ||||
|  | ||||
|         return LoadSurfaceView(exctx, gpu_addr, *cpu_addr, host_ptr, params, preserve_contents); | ||||
|     } | ||||
|  | ||||
|     ResultType LoadSurfaceView(TExecutionContext exctx, GPUVAddr gpu_addr, VAddr cpu_addr, | ||||
|                                u8* host_ptr, const SurfaceParams& params, bool preserve_contents) { | ||||
|         const auto new_surface{GetUncachedSurface(params)}; | ||||
|         Register(new_surface, gpu_addr, cpu_addr, host_ptr); | ||||
|         if (preserve_contents) { | ||||
|             exctx = LoadSurface(exctx, new_surface); | ||||
|         } | ||||
|         return {new_surface->GetView(gpu_addr, params), exctx}; | ||||
|     } | ||||
|  | ||||
|     TExecutionContext LoadSurface(TExecutionContext exctx, | ||||
|                                   const std::shared_ptr<TSurface>& surface) { | ||||
|         surface->LoadBuffer(); | ||||
|         exctx = surface->UploadTexture(exctx); | ||||
|         surface->MarkAsModified(false); | ||||
|         return exctx; | ||||
|     } | ||||
|  | ||||
|     TExecutionContext FlushSurface(TExecutionContext exctx, | ||||
|                                    const std::shared_ptr<TSurface>& surface) { | ||||
|         if (!surface->IsModified()) { | ||||
|             return exctx; | ||||
|         } | ||||
|         exctx = surface->DownloadTexture(exctx); | ||||
|         surface->FlushBuffer(); | ||||
|         return exctx; | ||||
|     } | ||||
|  | ||||
|     std::vector<std::shared_ptr<TSurface>> GetSurfacesInRegion(CacheAddr cache_addr, | ||||
|                                                                std::size_t size) const { | ||||
|         if (size == 0) { | ||||
|             return {}; | ||||
|         } | ||||
|         const IntervalType interval{cache_addr, cache_addr + size}; | ||||
|  | ||||
|         std::vector<std::shared_ptr<TSurface>> surfaces; | ||||
|         for (auto& pair : boost::make_iterator_range(registered_surfaces.equal_range(interval))) { | ||||
|             surfaces.push_back(*pair.second.begin()); | ||||
|         } | ||||
|         return surfaces; | ||||
|     } | ||||
|  | ||||
|     void ReserveSurface(const SurfaceParams& params, std::shared_ptr<TSurface> surface) { | ||||
|         surface_reserve[params].push_back(std::move(surface)); | ||||
|     } | ||||
|  | ||||
|     std::shared_ptr<TSurface> TryGetReservedSurface(const SurfaceParams& params) { | ||||
|         auto search{surface_reserve.find(params)}; | ||||
|         if (search == surface_reserve.end()) { | ||||
|             return {}; | ||||
|         } | ||||
|         for (auto& surface : search->second) { | ||||
|             if (!surface->IsRegistered()) { | ||||
|                 return surface; | ||||
|             } | ||||
|         } | ||||
|         return {}; | ||||
|     } | ||||
|  | ||||
|     IntervalType GetSurfaceInterval(std::shared_ptr<TSurface> surface) const { | ||||
|         return IntervalType::right_open(surface->GetCacheAddr(), | ||||
|                                         surface->GetCacheAddr() + surface->GetSizeInBytes()); | ||||
|     } | ||||
|  | ||||
|     VideoCore::RasterizerInterface& rasterizer; | ||||
|  | ||||
|     u64 ticks{}; | ||||
|  | ||||
|     IntervalMap registered_surfaces; | ||||
|  | ||||
|     /// The surface reserve is a "backup" cache, this is where we put unique surfaces that have | ||||
|     /// previously been used. This is to prevent surfaces from being constantly created and | ||||
|     /// destroyed when used with different surface parameters. | ||||
|     std::unordered_map<SurfaceParams, std::list<std::shared_ptr<TSurface>>> surface_reserve; | ||||
| }; | ||||
|  | ||||
| } // namespace VideoCommon | ||||
							
								
								
									
										93
									
								
								src/video_core/texture_cache/texture_cache_contextless.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								src/video_core/texture_cache/texture_cache_contextless.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,93 @@ | ||||
| // Copyright 2019 yuzu Emulator Project | ||||
| // Licensed under GPLv2 or any later version | ||||
| // Refer to the license.txt file included. | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "video_core/texture_cache/texture_cache.h" | ||||
|  | ||||
| namespace VideoCommon { | ||||
|  | ||||
| struct DummyExecutionContext {}; | ||||
|  | ||||
| template <typename TSurface, typename TView> | ||||
| class TextureCacheContextless : protected TextureCache<TSurface, TView, DummyExecutionContext> { | ||||
|     using Base = TextureCache<TSurface, TView, DummyExecutionContext>; | ||||
|  | ||||
| public: | ||||
|     void InvalidateRegion(CacheAddr addr, std::size_t size) { | ||||
|         Base::InvalidateRegion(addr, size); | ||||
|     } | ||||
|  | ||||
|     TView* GetTextureSurface(const Tegra::Texture::FullTextureInfo& config) { | ||||
|         return RemoveContext(Base::GetTextureSurface({}, config)); | ||||
|     } | ||||
|  | ||||
|     TView* GetDepthBufferSurface(bool preserve_contents) { | ||||
|         return RemoveContext(Base::GetDepthBufferSurface({}, preserve_contents)); | ||||
|     } | ||||
|  | ||||
|     TView* GetColorBufferSurface(std::size_t index, bool preserve_contents) { | ||||
|         return RemoveContext(Base::GetColorBufferSurface({}, index, preserve_contents)); | ||||
|     } | ||||
|  | ||||
|     TView* GetFermiSurface(const Tegra::Engines::Fermi2D::Regs::Surface& config) { | ||||
|         return RemoveContext(Base::GetFermiSurface({}, config)); | ||||
|     } | ||||
|  | ||||
|     std::shared_ptr<TSurface> TryFindFramebufferSurface(const u8* host_ptr) const { | ||||
|         return Base::TryFindFramebufferSurface(host_ptr); | ||||
|     } | ||||
|  | ||||
|     u64 Tick() { | ||||
|         return Base::Tick(); | ||||
|     } | ||||
|  | ||||
| protected: | ||||
|     explicit TextureCacheContextless(Core::System& system, | ||||
|                                      VideoCore::RasterizerInterface& rasterizer) | ||||
|         : TextureCache<TSurface, TView, DummyExecutionContext>{system, rasterizer} {} | ||||
|  | ||||
|     virtual TView* TryFastGetSurfaceView( | ||||
|         GPUVAddr gpu_addr, VAddr cpu_addr, u8* host_ptr, const SurfaceParams& params, | ||||
|         bool preserve_contents, const std::vector<std::shared_ptr<TSurface>>& overlaps) = 0; | ||||
|  | ||||
| private: | ||||
|     std::tuple<TView*, DummyExecutionContext> TryFastGetSurfaceView( | ||||
|         DummyExecutionContext, GPUVAddr gpu_addr, VAddr cpu_addr, u8* host_ptr, | ||||
|         const SurfaceParams& params, bool preserve_contents, | ||||
|         const std::vector<std::shared_ptr<TSurface>>& overlaps) { | ||||
|         return {TryFastGetSurfaceView(gpu_addr, cpu_addr, host_ptr, params, preserve_contents, | ||||
|                                       overlaps), | ||||
|                 {}}; | ||||
|     } | ||||
|  | ||||
|     TView* RemoveContext(std::tuple<TView*, DummyExecutionContext> return_value) { | ||||
|         const auto [view, exctx] = return_value; | ||||
|         return view; | ||||
|     } | ||||
| }; | ||||
|  | ||||
| template <typename TTextureCache, typename TView> | ||||
| class SurfaceBaseContextless : public SurfaceBase<TTextureCache, TView, DummyExecutionContext> { | ||||
| public: | ||||
|     DummyExecutionContext DownloadTexture(DummyExecutionContext) { | ||||
|         DownloadTextureImpl(); | ||||
|         return {}; | ||||
|     } | ||||
|  | ||||
|     DummyExecutionContext UploadTexture(DummyExecutionContext) { | ||||
|         UploadTextureImpl(); | ||||
|         return {}; | ||||
|     } | ||||
|  | ||||
| protected: | ||||
|     explicit SurfaceBaseContextless(TTextureCache& texture_cache, const SurfaceParams& params) | ||||
|         : SurfaceBase<TTextureCache, TView, DummyExecutionContext>{texture_cache, params} {} | ||||
|  | ||||
|     virtual void DownloadTextureImpl() = 0; | ||||
|  | ||||
|     virtual void UploadTextureImpl() = 0; | ||||
| }; | ||||
|  | ||||
| } // namespace VideoCommon | ||||
		Reference in New Issue
	
	Block a user
	 ReinUsesLisp
					ReinUsesLisp