summaryrefslogtreecommitdiffstats
path: root/src/video_core/texture_cache
diff options
context:
space:
mode:
authorReinUsesLisp <reinuseslisp@airmail.cc>2019-04-24 21:35:54 +0200
committerReinUsesLisp <reinuseslisp@airmail.cc>2019-06-21 02:36:11 +0200
commit1b4503c571d3b961efe74fa7e35d5fa14941ec09 (patch)
treefb732f50d8bd2855f3fa7657c7c6d36c58134d89 /src/video_core/texture_cache
parenttexture_cache: Move staging buffer into a generic implementation (diff)
downloadyuzu-1b4503c571d3b961efe74fa7e35d5fa14941ec09.tar
yuzu-1b4503c571d3b961efe74fa7e35d5fa14941ec09.tar.gz
yuzu-1b4503c571d3b961efe74fa7e35d5fa14941ec09.tar.bz2
yuzu-1b4503c571d3b961efe74fa7e35d5fa14941ec09.tar.lz
yuzu-1b4503c571d3b961efe74fa7e35d5fa14941ec09.tar.xz
yuzu-1b4503c571d3b961efe74fa7e35d5fa14941ec09.tar.zst
yuzu-1b4503c571d3b961efe74fa7e35d5fa14941ec09.zip
Diffstat (limited to 'src/video_core/texture_cache')
-rw-r--r--src/video_core/texture_cache/surface_base.cpp118
-rw-r--r--src/video_core/texture_cache/surface_base.h172
-rw-r--r--src/video_core/texture_cache/surface_params.cpp412
-rw-r--r--src/video_core/texture_cache/surface_params.h229
-rw-r--r--src/video_core/texture_cache/surface_view.cpp23
-rw-r--r--src/video_core/texture_cache/surface_view.h35
-rw-r--r--src/video_core/texture_cache/texture_cache.h282
-rw-r--r--src/video_core/texture_cache/texture_cache_contextless.h93
8 files changed, 1364 insertions, 0 deletions
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<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
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 <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
diff --git a/src/video_core/texture_cache/surface_params.cpp b/src/video_core/texture_cache/surface_params.cpp
new file mode 100644
index 000000000..d1f8c53d5
--- /dev/null
+++ b/src/video_core/texture_cache/surface_params.cpp
@@ -0,0 +1,412 @@
+// Copyright 2019 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <map>
+
+#include "common/cityhash.h"
+#include "common/alignment.h"
+#include "core/core.h"
+#include "video_core/surface.h"
+#include "video_core/texture_cache/surface_params.h"
+#include "video_core/textures/decoders.h"
+
+namespace VideoCommon {
+
+using VideoCore::Surface::ComponentTypeFromDepthFormat;
+using VideoCore::Surface::ComponentTypeFromRenderTarget;
+using VideoCore::Surface::ComponentTypeFromTexture;
+using VideoCore::Surface::PixelFormatFromDepthFormat;
+using VideoCore::Surface::PixelFormatFromRenderTargetFormat;
+using VideoCore::Surface::PixelFormatFromTextureFormat;
+using VideoCore::Surface::SurfaceTarget;
+using VideoCore::Surface::SurfaceTargetFromTextureType;
+
+namespace {
+constexpr u32 GetMipmapSize(bool uncompressed, u32 mip_size, u32 tile) {
+ return uncompressed ? mip_size : std::max(1U, (mip_size + tile - 1) / tile);
+}
+} // Anonymous namespace
+
+SurfaceParams SurfaceParams::CreateForTexture(Core::System& system,
+ const Tegra::Texture::FullTextureInfo& config) {
+ SurfaceParams params;
+ params.is_tiled = config.tic.IsTiled();
+ params.srgb_conversion = config.tic.IsSrgbConversionEnabled();
+ params.block_width = params.is_tiled ? config.tic.BlockWidth() : 0,
+ params.block_height = params.is_tiled ? config.tic.BlockHeight() : 0,
+ params.block_depth = params.is_tiled ? config.tic.BlockDepth() : 0,
+ params.tile_width_spacing = params.is_tiled ? (1 << config.tic.tile_width_spacing.Value()) : 1;
+ params.pixel_format = PixelFormatFromTextureFormat(config.tic.format, config.tic.r_type.Value(),
+ params.srgb_conversion);
+ params.component_type = ComponentTypeFromTexture(config.tic.r_type.Value());
+ params.type = GetFormatType(params.pixel_format);
+ params.target = SurfaceTargetFromTextureType(config.tic.texture_type);
+ params.width = Common::AlignUp(config.tic.Width(), GetCompressionFactor(params.pixel_format));
+ params.height = Common::AlignUp(config.tic.Height(), GetCompressionFactor(params.pixel_format));
+ params.depth = config.tic.Depth();
+ if (params.target == SurfaceTarget::TextureCubemap ||
+ params.target == SurfaceTarget::TextureCubeArray) {
+ params.depth *= 6;
+ }
+ params.pitch = params.is_tiled ? 0 : config.tic.Pitch();
+ params.unaligned_height = config.tic.Height();
+ params.num_levels = config.tic.max_mip_level + 1;
+
+ params.CalculateCachedValues();
+ return params;
+}
+
+SurfaceParams 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) {
+ SurfaceParams params;
+ params.is_tiled = type == Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear;
+ params.srgb_conversion = false;
+ params.block_width = 1 << std::min(block_width, 5U);
+ params.block_height = 1 << std::min(block_height, 5U);
+ params.block_depth = 1 << std::min(block_depth, 5U);
+ params.tile_width_spacing = 1;
+ params.pixel_format = PixelFormatFromDepthFormat(format);
+ params.component_type = ComponentTypeFromDepthFormat(format);
+ params.type = GetFormatType(params.pixel_format);
+ params.width = zeta_width;
+ params.height = zeta_height;
+ params.unaligned_height = zeta_height;
+ params.target = SurfaceTarget::Texture2D;
+ params.depth = 1;
+ params.num_levels = 1;
+
+ params.CalculateCachedValues();
+ return params;
+}
+
+SurfaceParams SurfaceParams::CreateForFramebuffer(Core::System& system, std::size_t index) {
+ const auto& config{system.GPU().Maxwell3D().regs.rt[index]};
+ SurfaceParams params;
+ params.is_tiled =
+ config.memory_layout.type == Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear;
+ params.srgb_conversion = config.format == Tegra::RenderTargetFormat::BGRA8_SRGB ||
+ config.format == Tegra::RenderTargetFormat::RGBA8_SRGB;
+ params.block_width = 1 << config.memory_layout.block_width;
+ params.block_height = 1 << config.memory_layout.block_height;
+ params.block_depth = 1 << config.memory_layout.block_depth;
+ params.tile_width_spacing = 1;
+ params.pixel_format = PixelFormatFromRenderTargetFormat(config.format);
+ params.component_type = ComponentTypeFromRenderTarget(config.format);
+ params.type = GetFormatType(params.pixel_format);
+ if (params.is_tiled) {
+ params.width = config.width;
+ } else {
+ const u32 bpp = GetFormatBpp(params.pixel_format) / CHAR_BIT;
+ params.pitch = config.width;
+ params.width = params.pitch / bpp;
+ }
+ params.height = config.height;
+ params.depth = 1;
+ params.unaligned_height = config.height;
+ params.target = SurfaceTarget::Texture2D;
+ params.num_levels = 1;
+
+ params.CalculateCachedValues();
+ return params;
+}
+
+SurfaceParams SurfaceParams::CreateForFermiCopySurface(
+ const Tegra::Engines::Fermi2D::Regs::Surface& config) {
+ SurfaceParams params{};
+ params.is_tiled = !config.linear;
+ params.srgb_conversion = config.format == Tegra::RenderTargetFormat::BGRA8_SRGB ||
+ config.format == Tegra::RenderTargetFormat::RGBA8_SRGB;
+ params.block_width = params.is_tiled ? std::min(config.BlockWidth(), 32U) : 0,
+ params.block_height = params.is_tiled ? std::min(config.BlockHeight(), 32U) : 0,
+ params.block_depth = params.is_tiled ? std::min(config.BlockDepth(), 32U) : 0,
+ params.tile_width_spacing = 1;
+ params.pixel_format = PixelFormatFromRenderTargetFormat(config.format);
+ params.component_type = ComponentTypeFromRenderTarget(config.format);
+ params.type = GetFormatType(params.pixel_format);
+ params.width = config.width;
+ params.height = config.height;
+ params.unaligned_height = config.height;
+ // TODO(Rodrigo): Try to guess the surface target from depth and layer parameters
+ params.target = SurfaceTarget::Texture2D;
+ params.depth = 1;
+ params.num_levels = 1;
+
+ params.CalculateCachedValues();
+ return params;
+}
+
+u32 SurfaceParams::GetMipWidth(u32 level) const {
+ return std::max(1U, width >> level);
+}
+
+u32 SurfaceParams::GetMipHeight(u32 level) const {
+ return std::max(1U, height >> level);
+}
+
+u32 SurfaceParams::GetMipDepth(u32 level) const {
+ return IsLayered() ? depth : std::max(1U, depth >> level);
+}
+
+bool SurfaceParams::IsLayered() const {
+ switch (target) {
+ case SurfaceTarget::Texture1DArray:
+ case SurfaceTarget::Texture2DArray:
+ case SurfaceTarget::TextureCubemap:
+ case SurfaceTarget::TextureCubeArray:
+ return true;
+ default:
+ return false;
+ }
+}
+
+u32 SurfaceParams::GetMipBlockHeight(u32 level) const {
+ // Auto block resizing algorithm from:
+ // https://cgit.freedesktop.org/mesa/mesa/tree/src/gallium/drivers/nouveau/nv50/nv50_miptree.c
+ if (level == 0) {
+ return this->block_height;
+ }
+
+ const u32 height{GetMipHeight(level)};
+ const u32 default_block_height{GetDefaultBlockHeight()};
+ const u32 blocks_in_y{(height + default_block_height - 1) / default_block_height};
+ u32 block_height = 16;
+ while (block_height > 1 && blocks_in_y <= block_height * 4) {
+ block_height >>= 1;
+ }
+ return block_height;
+}
+
+u32 SurfaceParams::GetMipBlockDepth(u32 level) const {
+ if (level == 0) {
+ return this->block_depth;
+ }
+ if (IsLayered()) {
+ return 1;
+ }
+
+ const u32 depth{GetMipDepth(level)};
+ u32 block_depth = 32;
+ while (block_depth > 1 && depth * 2 <= block_depth) {
+ block_depth >>= 1;
+ }
+
+ if (block_depth == 32 && GetMipBlockHeight(level) >= 4) {
+ return 16;
+ }
+
+ return block_depth;
+}
+
+std::size_t SurfaceParams::GetGuestMipmapLevelOffset(u32 level) const {
+ std::size_t offset = 0;
+ for (u32 i = 0; i < level; i++) {
+ offset += GetInnerMipmapMemorySize(i, false, false);
+ }
+ return offset;
+}
+
+std::size_t SurfaceParams::GetHostMipmapLevelOffset(u32 level) const {
+ std::size_t offset = 0;
+ for (u32 i = 0; i < level; i++) {
+ offset += GetInnerMipmapMemorySize(i, true, false) * GetNumLayers();
+ }
+ return offset;
+}
+
+std::size_t SurfaceParams::GetHostMipmapSize(u32 level) const {
+ return GetInnerMipmapMemorySize(level, true, false) * GetNumLayers();
+}
+
+std::size_t SurfaceParams::GetGuestLayerSize() const {
+ return GetLayerSize(false, false);
+}
+
+std::size_t SurfaceParams::GetLayerSize(bool as_host_size, bool uncompressed) const {
+ std::size_t size = 0;
+ for (u32 level = 0; level < num_levels; ++level) {
+ size += GetInnerMipmapMemorySize(level, as_host_size, uncompressed);
+ }
+ if (is_tiled && (IsLayered() || target == SurfaceTarget::Texture3D)) {
+ return Common::AlignUp(size, Tegra::Texture::GetGOBSize() * block_height * block_depth);
+ }
+ return size;
+}
+
+std::size_t SurfaceParams::GetHostLayerSize(u32 level) const {
+ ASSERT(target != SurfaceTarget::Texture3D);
+ return GetInnerMipmapMemorySize(level, true, false);
+}
+
+u32 SurfaceParams::GetDefaultBlockWidth() const {
+ return VideoCore::Surface::GetDefaultBlockWidth(pixel_format);
+}
+
+u32 SurfaceParams::GetDefaultBlockHeight() const {
+ return VideoCore::Surface::GetDefaultBlockHeight(pixel_format);
+}
+
+u32 SurfaceParams::GetBitsPerPixel() const {
+ return VideoCore::Surface::GetFormatBpp(pixel_format);
+}
+
+u32 SurfaceParams::GetBytesPerPixel() const {
+ return VideoCore::Surface::GetBytesPerPixel(pixel_format);
+}
+
+bool SurfaceParams::IsFamiliar(const SurfaceParams& view_params) const {
+ if (std::tie(is_tiled, tile_width_spacing, pixel_format, component_type, type) !=
+ std::tie(view_params.is_tiled, view_params.tile_width_spacing, view_params.pixel_format,
+ view_params.component_type, view_params.type)) {
+ return false;
+ }
+
+ const SurfaceTarget view_target{view_params.target};
+ if (view_target == target) {
+ return true;
+ }
+
+ switch (target) {
+ case SurfaceTarget::Texture1D:
+ case SurfaceTarget::Texture2D:
+ case SurfaceTarget::Texture3D:
+ return false;
+ case SurfaceTarget::Texture1DArray:
+ return view_target == SurfaceTarget::Texture1D;
+ case SurfaceTarget::Texture2DArray:
+ return view_target == SurfaceTarget::Texture2D;
+ case SurfaceTarget::TextureCubemap:
+ return view_target == SurfaceTarget::Texture2D ||
+ view_target == SurfaceTarget::Texture2DArray;
+ case SurfaceTarget::TextureCubeArray:
+ return view_target == SurfaceTarget::Texture2D ||
+ view_target == SurfaceTarget::Texture2DArray ||
+ view_target == SurfaceTarget::TextureCubemap;
+ default:
+ UNIMPLEMENTED_MSG("Unimplemented texture family={}", static_cast<u32>(target));
+ return false;
+ }
+}
+
+bool SurfaceParams::IsPixelFormatZeta() const {
+ return pixel_format >= VideoCore::Surface::PixelFormat::MaxColorFormat &&
+ pixel_format < VideoCore::Surface::PixelFormat::MaxDepthStencilFormat;
+}
+
+void SurfaceParams::CalculateCachedValues() {
+ switch (target) {
+ case SurfaceTarget::Texture1D:
+ case SurfaceTarget::Texture2D:
+ case SurfaceTarget::Texture3D:
+ num_layers = 1;
+ break;
+ case SurfaceTarget::Texture1DArray:
+ case SurfaceTarget::Texture2DArray:
+ case SurfaceTarget::TextureCubemap:
+ case SurfaceTarget::TextureCubeArray:
+ num_layers = depth;
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ guest_size_in_bytes = GetInnerMemorySize(false, false, false);
+
+ if (IsPixelFormatASTC(pixel_format)) {
+ // ASTC is uncompressed in software, in emulated as RGBA8
+ host_size_in_bytes = static_cast<std::size_t>(width) * static_cast<std::size_t>(height) *
+ static_cast<std::size_t>(depth) * 4ULL;
+ } else {
+ host_size_in_bytes = GetInnerMemorySize(true, false, false);
+ }
+}
+
+std::size_t SurfaceParams::GetInnerMipmapMemorySize(u32 level, bool as_host_size,
+ bool uncompressed) const {
+ const bool tiled{as_host_size ? false : is_tiled};
+ const u32 width{GetMipmapSize(uncompressed, GetMipWidth(level), GetDefaultBlockWidth())};
+ const u32 height{GetMipmapSize(uncompressed, GetMipHeight(level), GetDefaultBlockHeight())};
+ const u32 depth{target == SurfaceTarget::Texture3D ? GetMipDepth(level) : 1U};
+ return Tegra::Texture::CalculateSize(tiled, GetBytesPerPixel(), width, height, depth,
+ GetMipBlockHeight(level), GetMipBlockDepth(level));
+}
+
+std::size_t SurfaceParams::GetInnerMemorySize(bool as_host_size, bool layer_only,
+ bool uncompressed) const {
+ return GetLayerSize(as_host_size, uncompressed) * (layer_only ? 1U : num_layers);
+}
+
+std::map<u64, std::pair<u32, u32>> SurfaceParams::CreateViewOffsetMap() const {
+ std::map<u64, std::pair<u32, u32>> view_offset_map;
+ switch (target) {
+ case SurfaceTarget::Texture1D:
+ case SurfaceTarget::Texture2D:
+ case SurfaceTarget::Texture3D: {
+ // TODO(Rodrigo): Add layer iterations for 3D textures
+ constexpr u32 layer = 0;
+ for (u32 level = 0; level < num_levels; ++level) {
+ const std::size_t offset{GetGuestMipmapLevelOffset(level)};
+ view_offset_map.insert({offset, {layer, level}});
+ }
+ break;
+ }
+ case SurfaceTarget::Texture1DArray:
+ case SurfaceTarget::Texture2DArray:
+ case SurfaceTarget::TextureCubemap:
+ case SurfaceTarget::TextureCubeArray: {
+ const std::size_t layer_size{GetGuestLayerSize()};
+ for (u32 level = 0; level < num_levels; ++level) {
+ const std::size_t level_offset{GetGuestMipmapLevelOffset(level)};
+ for (u32 layer = 0; layer < num_layers; ++layer) {
+ const auto layer_offset{static_cast<std::size_t>(layer_size * layer)};
+ const std::size_t offset{level_offset + layer_offset};
+ view_offset_map.insert({offset, {layer, level}});
+ }
+ }
+ break;
+ }
+ default:
+ UNIMPLEMENTED_MSG("Unimplemented surface target {}", static_cast<u32>(target));
+ }
+ return view_offset_map;
+}
+
+bool SurfaceParams::IsViewValid(const SurfaceParams& view_params, u32 layer, u32 level) const {
+ return IsDimensionValid(view_params, level) && IsDepthValid(view_params, level) &&
+ IsInBounds(view_params, layer, level);
+}
+
+bool SurfaceParams::IsDimensionValid(const SurfaceParams& view_params, u32 level) const {
+ return view_params.width == GetMipWidth(level) && view_params.height == GetMipHeight(level);
+}
+
+bool SurfaceParams::IsDepthValid(const SurfaceParams& view_params, u32 level) const {
+ if (view_params.target != SurfaceTarget::Texture3D) {
+ return true;
+ }
+ return view_params.depth == GetMipDepth(level);
+}
+
+bool SurfaceParams::IsInBounds(const SurfaceParams& view_params, u32 layer, u32 level) const {
+ return layer + view_params.num_layers <= num_layers &&
+ level + view_params.num_levels <= num_levels;
+}
+
+std::size_t HasheableSurfaceParams::Hash() const {
+ return static_cast<std::size_t>(
+ Common::CityHash64(reinterpret_cast<const char*>(this), sizeof(*this)));
+}
+
+bool HasheableSurfaceParams::operator==(const HasheableSurfaceParams& rhs) const {
+ return std::tie(is_tiled, block_width, block_height, block_depth, tile_width_spacing, width,
+ height, depth, pitch, unaligned_height, num_levels, pixel_format,
+ component_type, type, target) ==
+ std::tie(rhs.is_tiled, rhs.block_width, rhs.block_height, rhs.block_depth,
+ rhs.tile_width_spacing, rhs.width, rhs.height, rhs.depth, rhs.pitch,
+ rhs.unaligned_height, rhs.num_levels, rhs.pixel_format, rhs.component_type,
+ rhs.type, rhs.target);
+}
+
+} // 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 <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
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 <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
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 <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
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 <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
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 <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