diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 64cff27a4..470fbceda 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -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 ) diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp index 3e2a1f53c..ca007b797 100644 --- a/src/video_core/renderer_opengl/gl_texture_cache.cpp +++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp @@ -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" diff --git a/src/video_core/renderer_opengl/gl_texture_cache.h b/src/video_core/renderer_opengl/gl_texture_cache.h index 0a69be233..3c15b37bd 100644 --- a/src/video_core/renderer_opengl/gl_texture_cache.h +++ b/src/video_core/renderer_opengl/gl_texture_cache.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 { diff --git a/src/video_core/texture_cache.h b/src/video_core/texture_cache.h deleted file mode 100644 index 90c72cb15..000000000 --- a/src/video_core/texture_cache.h +++ /dev/null @@ -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 -#include -#include -#include -#include -#include - -#include -#include - -#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> 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 { - std::size_t operator()(const VideoCommon::SurfaceParams& k) const noexcept { - return k.Hash(); - } -}; - -template <> -struct hash { - 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 staging_buffer; -}; - -template -class SurfaceBase : public SurfaceBaseImpl { - static_assert(std::is_trivially_copyable_v); - -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(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 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> view_offset_map; - - bool is_modified{}; - u64 modification_tick{}; - std::unordered_map> views; -}; - -template -class TextureCache { - static_assert(std::is_trivially_copyable_v); - - using ResultType = std::tuple; - using IntervalMap = boost::icl::interval_map>>; - 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 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>& overlaps) = 0; - - virtual std::shared_ptr CreateSurface(const SurfaceParams& params) = 0; - - void Register(std::shared_ptr 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 surface) { - registered_surfaces.subtract({GetSurfaceInterval(surface), {surface}}); - rasterizer.UpdatePagesCachedCount(surface->GetCpuAddr(), surface->GetSizeInBytes(), -1); - surface->Unregister(); - } - - std::shared_ptr 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& surface) { - surface->LoadBuffer(); - exctx = surface->UploadTexture(exctx); - surface->MarkAsModified(false); - return exctx; - } - - TExecutionContext FlushSurface(TExecutionContext exctx, - const std::shared_ptr& surface) { - if (!surface->IsModified()) { - return exctx; - } - exctx = surface->DownloadTexture(exctx); - surface->FlushBuffer(); - return exctx; - } - - std::vector> GetSurfacesInRegion(CacheAddr cache_addr, - std::size_t size) const { - if (size == 0) { - return {}; - } - const IntervalType interval{cache_addr, cache_addr + size}; - - std::vector> 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 surface) { - surface_reserve[params].push_back(std::move(surface)); - } - - std::shared_ptr 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 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>> surface_reserve; -}; - -struct DummyExecutionContext {}; - -template -class TextureCacheContextless : protected TextureCache { - using Base = TextureCache; - -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 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{system, rasterizer} {} - - virtual TView* TryFastGetSurfaceView( - GPUVAddr gpu_addr, VAddr cpu_addr, u8* host_ptr, const SurfaceParams& params, - bool preserve_contents, const std::vector>& overlaps) = 0; - -private: - std::tuple TryFastGetSurfaceView( - DummyExecutionContext, GPUVAddr gpu_addr, VAddr cpu_addr, u8* host_ptr, - const SurfaceParams& params, bool preserve_contents, - const std::vector>& overlaps) { - return {TryFastGetSurfaceView(gpu_addr, cpu_addr, host_ptr, params, preserve_contents, - overlaps), - {}}; - } - - TView* RemoveContext(std::tuple return_value) { - const auto [view, exctx] = return_value; - return view; - } -}; - -template -class SurfaceBaseContextless : public SurfaceBase { -public: - DummyExecutionContext DownloadTexture(DummyExecutionContext) { - DownloadTextureImpl(); - return {}; - } - - DummyExecutionContext UploadTexture(DummyExecutionContext) { - UploadTextureImpl(); - return {}; - } - -protected: - explicit SurfaceBaseContextless(TTextureCache& texture_cache, const SurfaceParams& params) - : SurfaceBase{texture_cache, params} {} - - virtual void DownloadTextureImpl() = 0; - - virtual void UploadTextureImpl() = 0; -}; - -} // namespace VideoCommon diff --git a/src/video_core/texture_cache/surface_base.cpp b/src/video_core/texture_cache/surface_base.cpp new file mode 100644 index 000000000..8680485b4 --- /dev/null +++ b/src/video_core/texture_cache/surface_base.cpp @@ -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(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 diff --git a/src/video_core/texture_cache/surface_base.h b/src/video_core/texture_cache/surface_base.h new file mode 100644 index 000000000..d0142a9e6 --- /dev/null +++ b/src/video_core/texture_cache/surface_base.h @@ -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 + +#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 staging_buffer; +}; + +template +class SurfaceBase : public SurfaceBaseImpl { + static_assert(std::is_trivially_copyable_v); + +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(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 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> view_offset_map; + + std::unordered_map> views; + + bool is_modified{}; + u64 modification_tick{}; +}; + +} // namespace VideoCommon diff --git a/src/video_core/texture_cache.cpp b/src/video_core/texture_cache/surface_params.cpp similarity index 76% rename from src/video_core/texture_cache.cpp rename to src/video_core/texture_cache/surface_params.cpp index 146e8ed9b..d1f8c53d5 100644 --- a/src/video_core/texture_cache.cpp +++ b/src/video_core/texture_cache/surface_params.cpp @@ -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 + #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(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( - Common::CityHash64(reinterpret_cast(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 diff --git a/src/video_core/texture_cache/surface_params.h b/src/video_core/texture_cache/surface_params.h new file mode 100644 index 000000000..77dc0ba66 --- /dev/null +++ b/src/video_core/texture_cache/surface_params.h @@ -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 + +#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> 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 { + std::size_t operator()(const VideoCommon::SurfaceParams& k) const noexcept { + return k.Hash(); + } +}; + +} // namespace std diff --git a/src/video_core/texture_cache/surface_view.cpp b/src/video_core/texture_cache/surface_view.cpp new file mode 100644 index 000000000..5f4cdbb1c --- /dev/null +++ b/src/video_core/texture_cache/surface_view.cpp @@ -0,0 +1,23 @@ +// Copyright 2019 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include + +#include "common/common_types.h" +#include "video_core/texture_cache/surface_view.h" + +namespace VideoCommon { + +std::size_t ViewKey::Hash() const { + return static_cast(base_layer) ^ static_cast(num_layers << 16) ^ + (static_cast(base_level) << 32) ^ + (static_cast(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 diff --git a/src/video_core/texture_cache/surface_view.h b/src/video_core/texture_cache/surface_view.h new file mode 100644 index 000000000..e73d8f6ae --- /dev/null +++ b/src/video_core/texture_cache/surface_view.h @@ -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 + +#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 { + std::size_t operator()(const VideoCommon::ViewKey& k) const noexcept { + return k.Hash(); + } +}; + +} // namespace std diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h new file mode 100644 index 000000000..fb43fa65e --- /dev/null +++ b/src/video_core/texture_cache/texture_cache.h @@ -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 +#include +#include +#include +#include +#include + +#include +#include + +#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 +class TextureCache { + static_assert(std::is_trivially_copyable_v); + + using ResultType = std::tuple; + using IntervalMap = boost::icl::interval_map>>; + 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 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>& overlaps) = 0; + + virtual std::shared_ptr CreateSurface(const SurfaceParams& params) = 0; + + void Register(std::shared_ptr 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 surface) { + registered_surfaces.subtract({GetSurfaceInterval(surface), {surface}}); + rasterizer.UpdatePagesCachedCount(surface->GetCpuAddr(), surface->GetSizeInBytes(), -1); + surface->Unregister(); + } + + std::shared_ptr 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& surface) { + surface->LoadBuffer(); + exctx = surface->UploadTexture(exctx); + surface->MarkAsModified(false); + return exctx; + } + + TExecutionContext FlushSurface(TExecutionContext exctx, + const std::shared_ptr& surface) { + if (!surface->IsModified()) { + return exctx; + } + exctx = surface->DownloadTexture(exctx); + surface->FlushBuffer(); + return exctx; + } + + std::vector> GetSurfacesInRegion(CacheAddr cache_addr, + std::size_t size) const { + if (size == 0) { + return {}; + } + const IntervalType interval{cache_addr, cache_addr + size}; + + std::vector> 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 surface) { + surface_reserve[params].push_back(std::move(surface)); + } + + std::shared_ptr 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 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>> surface_reserve; +}; + +} // namespace VideoCommon diff --git a/src/video_core/texture_cache/texture_cache_contextless.h b/src/video_core/texture_cache/texture_cache_contextless.h new file mode 100644 index 000000000..cd35a9fd4 --- /dev/null +++ b/src/video_core/texture_cache/texture_cache_contextless.h @@ -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 +class TextureCacheContextless : protected TextureCache { + using Base = TextureCache; + +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 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{system, rasterizer} {} + + virtual TView* TryFastGetSurfaceView( + GPUVAddr gpu_addr, VAddr cpu_addr, u8* host_ptr, const SurfaceParams& params, + bool preserve_contents, const std::vector>& overlaps) = 0; + +private: + std::tuple TryFastGetSurfaceView( + DummyExecutionContext, GPUVAddr gpu_addr, VAddr cpu_addr, u8* host_ptr, + const SurfaceParams& params, bool preserve_contents, + const std::vector>& overlaps) { + return {TryFastGetSurfaceView(gpu_addr, cpu_addr, host_ptr, params, preserve_contents, + overlaps), + {}}; + } + + TView* RemoveContext(std::tuple return_value) { + const auto [view, exctx] = return_value; + return view; + } +}; + +template +class SurfaceBaseContextless : public SurfaceBase { +public: + DummyExecutionContext DownloadTexture(DummyExecutionContext) { + DownloadTextureImpl(); + return {}; + } + + DummyExecutionContext UploadTexture(DummyExecutionContext) { + UploadTextureImpl(); + return {}; + } + +protected: + explicit SurfaceBaseContextless(TTextureCache& texture_cache, const SurfaceParams& params) + : SurfaceBase{texture_cache, params} {} + + virtual void DownloadTextureImpl() = 0; + + virtual void UploadTextureImpl() = 0; +}; + +} // namespace VideoCommon