aboutsummaryrefslogtreecommitdiff
path: root/src/video_core/texture_cache
diff options
context:
space:
mode:
Diffstat (limited to 'src/video_core/texture_cache')
-rw-r--r--src/video_core/texture_cache/format_lookup_table.cpp208
-rw-r--r--src/video_core/texture_cache/format_lookup_table.h51
-rw-r--r--src/video_core/texture_cache/surface_base.cpp1
-rw-r--r--src/video_core/texture_cache/surface_base.h5
-rw-r--r--src/video_core/texture_cache/surface_params.cpp32
-rw-r--r--src/video_core/texture_cache/surface_params.h9
-rw-r--r--src/video_core/texture_cache/texture_cache.h281
7 files changed, 504 insertions, 83 deletions
diff --git a/src/video_core/texture_cache/format_lookup_table.cpp b/src/video_core/texture_cache/format_lookup_table.cpp
new file mode 100644
index 000000000..271e67533
--- /dev/null
+++ b/src/video_core/texture_cache/format_lookup_table.cpp
@@ -0,0 +1,208 @@
+// Copyright 2019 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <array>
+#include "common/common_types.h"
+#include "common/logging/log.h"
+#include "video_core/texture_cache/format_lookup_table.h"
+
+namespace VideoCommon {
+
+using Tegra::Texture::ComponentType;
+using Tegra::Texture::TextureFormat;
+using VideoCore::Surface::PixelFormat;
+
+namespace {
+
+constexpr auto SNORM = ComponentType::SNORM;
+constexpr auto UNORM = ComponentType::UNORM;
+constexpr auto SINT = ComponentType::SINT;
+constexpr auto UINT = ComponentType::UINT;
+constexpr auto SNORM_FORCE_FP16 = ComponentType::SNORM_FORCE_FP16;
+constexpr auto UNORM_FORCE_FP16 = ComponentType::UNORM_FORCE_FP16;
+constexpr auto FLOAT = ComponentType::FLOAT;
+constexpr bool C = false; // Normal color
+constexpr bool S = true; // Srgb
+
+struct Table {
+ constexpr Table(TextureFormat texture_format, bool is_srgb, ComponentType red_component,
+ ComponentType green_component, ComponentType blue_component,
+ ComponentType alpha_component, PixelFormat pixel_format)
+ : texture_format{texture_format}, pixel_format{pixel_format}, red_component{red_component},
+ green_component{green_component}, blue_component{blue_component},
+ alpha_component{alpha_component}, is_srgb{is_srgb} {}
+
+ TextureFormat texture_format;
+ PixelFormat pixel_format;
+ ComponentType red_component;
+ ComponentType green_component;
+ ComponentType blue_component;
+ ComponentType alpha_component;
+ bool is_srgb;
+};
+constexpr std::array<Table, 74> DefinitionTable = {{
+ {TextureFormat::A8R8G8B8, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::ABGR8U},
+ {TextureFormat::A8R8G8B8, C, SNORM, SNORM, SNORM, SNORM, PixelFormat::ABGR8S},
+ {TextureFormat::A8R8G8B8, C, UINT, UINT, UINT, UINT, PixelFormat::ABGR8UI},
+ {TextureFormat::A8R8G8B8, S, UNORM, UNORM, UNORM, UNORM, PixelFormat::RGBA8_SRGB},
+
+ {TextureFormat::B5G6R5, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::B5G6R5U},
+
+ {TextureFormat::A2B10G10R10, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::A2B10G10R10U},
+
+ {TextureFormat::A1B5G5R5, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::A1B5G5R5U},
+
+ {TextureFormat::A4B4G4R4, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::R4G4B4A4U},
+
+ {TextureFormat::R8, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::R8U},
+ {TextureFormat::R8, C, UINT, UINT, UINT, UINT, PixelFormat::R8UI},
+
+ {TextureFormat::G8R8, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::RG8U},
+ {TextureFormat::G8R8, C, SNORM, SNORM, SNORM, SNORM, PixelFormat::RG8S},
+
+ {TextureFormat::R16_G16_B16_A16, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::RGBA16U},
+ {TextureFormat::R16_G16_B16_A16, C, FLOAT, FLOAT, FLOAT, FLOAT, PixelFormat::RGBA16F},
+ {TextureFormat::R16_G16_B16_A16, C, UINT, UINT, UINT, UINT, PixelFormat::RGBA16UI},
+
+ {TextureFormat::R16_G16, C, FLOAT, FLOAT, FLOAT, FLOAT, PixelFormat::RG16F},
+ {TextureFormat::R16_G16, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::RG16},
+ {TextureFormat::R16_G16, C, SNORM, SNORM, SNORM, SNORM, PixelFormat::RG16S},
+ {TextureFormat::R16_G16, C, UINT, UINT, UINT, UINT, PixelFormat::RG16UI},
+ {TextureFormat::R16_G16, C, SINT, SINT, SINT, SINT, PixelFormat::RG16I},
+
+ {TextureFormat::R16, C, FLOAT, FLOAT, FLOAT, FLOAT, PixelFormat::R16F},
+ {TextureFormat::R16, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::R16U},
+ {TextureFormat::R16, C, SNORM, SNORM, SNORM, SNORM, PixelFormat::R16S},
+ {TextureFormat::R16, C, UINT, UINT, UINT, UINT, PixelFormat::R16UI},
+ {TextureFormat::R16, C, SINT, SINT, SINT, SINT, PixelFormat::R16I},
+
+ {TextureFormat::BF10GF11RF11, C, FLOAT, FLOAT, FLOAT, FLOAT, PixelFormat::R11FG11FB10F},
+
+ {TextureFormat::R32_G32_B32_A32, C, FLOAT, FLOAT, FLOAT, FLOAT, PixelFormat::RGBA32F},
+ {TextureFormat::R32_G32_B32_A32, C, UINT, UINT, UINT, UINT, PixelFormat::RGBA32UI},
+
+ {TextureFormat::R32_G32_B32, C, FLOAT, FLOAT, FLOAT, FLOAT, PixelFormat::RGB32F},
+
+ {TextureFormat::R32_G32, C, FLOAT, FLOAT, FLOAT, FLOAT, PixelFormat::RG32F},
+ {TextureFormat::R32_G32, C, UINT, UINT, UINT, UINT, PixelFormat::RG32UI},
+
+ {TextureFormat::R32, C, FLOAT, FLOAT, FLOAT, FLOAT, PixelFormat::R32F},
+ {TextureFormat::R32, C, UINT, UINT, UINT, UINT, PixelFormat::R32UI},
+
+ {TextureFormat::E5B9G9R9_SHAREDEXP, C, FLOAT, FLOAT, FLOAT, FLOAT, PixelFormat::E5B9G9R9F},
+
+ {TextureFormat::ZF32, C, FLOAT, FLOAT, FLOAT, FLOAT, PixelFormat::Z32F},
+ {TextureFormat::Z16, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::Z16},
+ {TextureFormat::S8Z24, C, UINT, UNORM, UNORM, UNORM, PixelFormat::S8Z24},
+ {TextureFormat::ZF32_X24S8, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::Z32FS8},
+
+ {TextureFormat::DXT1, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::DXT1},
+ {TextureFormat::DXT1, S, UNORM, UNORM, UNORM, UNORM, PixelFormat::DXT1_SRGB},
+
+ {TextureFormat::DXT23, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::DXT23},
+ {TextureFormat::DXT23, S, UNORM, UNORM, UNORM, UNORM, PixelFormat::DXT23_SRGB},
+
+ {TextureFormat::DXT45, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::DXT45},
+ {TextureFormat::DXT45, S, UNORM, UNORM, UNORM, UNORM, PixelFormat::DXT45_SRGB},
+
+ // TODO: Use a different pixel format for SNORM
+ {TextureFormat::DXN1, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::DXN1},
+ {TextureFormat::DXN1, C, SNORM, SNORM, SNORM, SNORM, PixelFormat::DXN1},
+
+ {TextureFormat::DXN2, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::DXN2UNORM},
+ {TextureFormat::DXN2, C, SNORM, SNORM, SNORM, SNORM, PixelFormat::DXN2SNORM},
+
+ {TextureFormat::BC7U, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::BC7U},
+ {TextureFormat::BC7U, S, UNORM, UNORM, UNORM, UNORM, PixelFormat::BC7U_SRGB},
+
+ {TextureFormat::BC6H_SF16, C, FLOAT, FLOAT, FLOAT, FLOAT, PixelFormat::BC6H_SF16},
+ {TextureFormat::BC6H_UF16, C, FLOAT, FLOAT, FLOAT, FLOAT, PixelFormat::BC6H_UF16},
+
+ {TextureFormat::ASTC_2D_4X4, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::ASTC_2D_4X4},
+ {TextureFormat::ASTC_2D_4X4, S, UNORM, UNORM, UNORM, UNORM, PixelFormat::ASTC_2D_4X4_SRGB},
+
+ {TextureFormat::ASTC_2D_5X4, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::ASTC_2D_5X4},
+ {TextureFormat::ASTC_2D_5X4, S, UNORM, UNORM, UNORM, UNORM, PixelFormat::ASTC_2D_5X4_SRGB},
+
+ {TextureFormat::ASTC_2D_5X5, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::ASTC_2D_5X5},
+ {TextureFormat::ASTC_2D_5X5, S, UNORM, UNORM, UNORM, UNORM, PixelFormat::ASTC_2D_5X5_SRGB},
+
+ {TextureFormat::ASTC_2D_8X8, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::ASTC_2D_8X8},
+ {TextureFormat::ASTC_2D_8X8, S, UNORM, UNORM, UNORM, UNORM, PixelFormat::ASTC_2D_8X8_SRGB},
+
+ {TextureFormat::ASTC_2D_8X5, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::ASTC_2D_8X5},
+ {TextureFormat::ASTC_2D_8X5, S, UNORM, UNORM, UNORM, UNORM, PixelFormat::ASTC_2D_8X5_SRGB},
+
+ {TextureFormat::ASTC_2D_10X8, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::ASTC_2D_10X8},
+ {TextureFormat::ASTC_2D_10X8, S, UNORM, UNORM, UNORM, UNORM, PixelFormat::ASTC_2D_10X8_SRGB},
+
+ {TextureFormat::ASTC_2D_6X6, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::ASTC_2D_6X6},
+ {TextureFormat::ASTC_2D_6X6, S, UNORM, UNORM, UNORM, UNORM, PixelFormat::ASTC_2D_6X6_SRGB},
+
+ {TextureFormat::ASTC_2D_10X10, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::ASTC_2D_10X10},
+ {TextureFormat::ASTC_2D_10X10, S, UNORM, UNORM, UNORM, UNORM, PixelFormat::ASTC_2D_10X10_SRGB},
+
+ {TextureFormat::ASTC_2D_12X12, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::ASTC_2D_12X12},
+ {TextureFormat::ASTC_2D_12X12, S, UNORM, UNORM, UNORM, UNORM, PixelFormat::ASTC_2D_12X12_SRGB},
+
+ {TextureFormat::ASTC_2D_8X6, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::ASTC_2D_8X6},
+ {TextureFormat::ASTC_2D_8X6, S, UNORM, UNORM, UNORM, UNORM, PixelFormat::ASTC_2D_8X6_SRGB},
+
+ {TextureFormat::ASTC_2D_6X5, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::ASTC_2D_6X5},
+ {TextureFormat::ASTC_2D_6X5, S, UNORM, UNORM, UNORM, UNORM, PixelFormat::ASTC_2D_6X5_SRGB},
+}};
+
+} // Anonymous namespace
+
+FormatLookupTable::FormatLookupTable() {
+ table.fill(static_cast<u8>(PixelFormat::Invalid));
+
+ for (const auto& entry : DefinitionTable) {
+ table[CalculateIndex(entry.texture_format, entry.is_srgb != 0, entry.red_component,
+ entry.green_component, entry.blue_component, entry.alpha_component)] =
+ static_cast<u8>(entry.pixel_format);
+ }
+}
+
+PixelFormat FormatLookupTable::GetPixelFormat(TextureFormat format, bool is_srgb,
+ ComponentType red_component,
+ ComponentType green_component,
+ ComponentType blue_component,
+ ComponentType alpha_component) const noexcept {
+ const auto pixel_format = static_cast<PixelFormat>(table[CalculateIndex(
+ format, is_srgb, red_component, green_component, blue_component, alpha_component)]);
+ // [[likely]]
+ if (pixel_format != PixelFormat::Invalid) {
+ return pixel_format;
+ }
+ UNIMPLEMENTED_MSG("texture format={} srgb={} components={{{} {} {} {}}}",
+ static_cast<int>(format), is_srgb, static_cast<int>(red_component),
+ static_cast<int>(green_component), static_cast<int>(blue_component),
+ static_cast<int>(alpha_component));
+ return PixelFormat::ABGR8U;
+}
+
+void FormatLookupTable::Set(TextureFormat format, bool is_srgb, ComponentType red_component,
+ ComponentType green_component, ComponentType blue_component,
+ ComponentType alpha_component, PixelFormat pixel_format) {}
+
+std::size_t FormatLookupTable::CalculateIndex(TextureFormat format, bool is_srgb,
+ ComponentType red_component,
+ ComponentType green_component,
+ ComponentType blue_component,
+ ComponentType alpha_component) noexcept {
+ const auto format_index = static_cast<std::size_t>(format);
+ const auto red_index = static_cast<std::size_t>(red_component);
+ const auto green_index = static_cast<std::size_t>(red_component);
+ const auto blue_index = static_cast<std::size_t>(red_component);
+ const auto alpha_index = static_cast<std::size_t>(red_component);
+ const std::size_t srgb_index = is_srgb ? 1 : 0;
+
+ return format_index * PerFormat +
+ srgb_index * PerComponent * PerComponent * PerComponent * PerComponent +
+ alpha_index * PerComponent * PerComponent * PerComponent +
+ blue_index * PerComponent * PerComponent + green_index * PerComponent + red_index;
+}
+
+} // namespace VideoCommon
diff --git a/src/video_core/texture_cache/format_lookup_table.h b/src/video_core/texture_cache/format_lookup_table.h
new file mode 100644
index 000000000..aa77e0a5a
--- /dev/null
+++ b/src/video_core/texture_cache/format_lookup_table.h
@@ -0,0 +1,51 @@
+// Copyright 2019 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+#include <limits>
+#include "video_core/surface.h"
+#include "video_core/textures/texture.h"
+
+namespace VideoCommon {
+
+class FormatLookupTable {
+public:
+ explicit FormatLookupTable();
+
+ VideoCore::Surface::PixelFormat GetPixelFormat(
+ Tegra::Texture::TextureFormat format, bool is_srgb,
+ Tegra::Texture::ComponentType red_component, Tegra::Texture::ComponentType green_component,
+ Tegra::Texture::ComponentType blue_component,
+ Tegra::Texture::ComponentType alpha_component) const noexcept;
+
+private:
+ static_assert(VideoCore::Surface::MaxPixelFormat <= std::numeric_limits<u8>::max());
+
+ static constexpr std::size_t NumTextureFormats = 128;
+
+ static constexpr std::size_t PerComponent = 8;
+ static constexpr std::size_t PerComponents2 = PerComponent * PerComponent;
+ static constexpr std::size_t PerComponents3 = PerComponents2 * PerComponent;
+ static constexpr std::size_t PerComponents4 = PerComponents3 * PerComponent;
+ static constexpr std::size_t PerFormat = PerComponents4 * 2;
+
+ static std::size_t CalculateIndex(Tegra::Texture::TextureFormat format, bool is_srgb,
+ Tegra::Texture::ComponentType red_component,
+ Tegra::Texture::ComponentType green_component,
+ Tegra::Texture::ComponentType blue_component,
+ Tegra::Texture::ComponentType alpha_component) noexcept;
+
+ void Set(Tegra::Texture::TextureFormat format, bool is_srgb,
+ Tegra::Texture::ComponentType red_component,
+ Tegra::Texture::ComponentType green_component,
+ Tegra::Texture::ComponentType blue_component,
+ Tegra::Texture::ComponentType alpha_component,
+ VideoCore::Surface::PixelFormat pixel_format);
+
+ std::array<u8, NumTextureFormats * PerFormat> table;
+};
+
+} // namespace VideoCommon
diff --git a/src/video_core/texture_cache/surface_base.cpp b/src/video_core/texture_cache/surface_base.cpp
index 683c49207..829268b4c 100644
--- a/src/video_core/texture_cache/surface_base.cpp
+++ b/src/video_core/texture_cache/surface_base.cpp
@@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include "common/algorithm.h"
#include "common/assert.h"
#include "common/common_types.h"
#include "common/microprofile.h"
diff --git a/src/video_core/texture_cache/surface_base.h b/src/video_core/texture_cache/surface_base.h
index 5e497e49f..1bed82898 100644
--- a/src/video_core/texture_cache/surface_base.h
+++ b/src/video_core/texture_cache/surface_base.h
@@ -4,12 +4,11 @@
#pragma once
-#include <algorithm>
+#include <optional>
+#include <tuple>
#include <unordered_map>
#include <vector>
-#include "common/assert.h"
-#include "common/binary_find.h"
#include "common/common_types.h"
#include "video_core/gpu.h"
#include "video_core/morton.h"
diff --git a/src/video_core/texture_cache/surface_params.cpp b/src/video_core/texture_cache/surface_params.cpp
index 1e4d3fb79..858e17e08 100644
--- a/src/video_core/texture_cache/surface_params.cpp
+++ b/src/video_core/texture_cache/surface_params.cpp
@@ -2,24 +2,23 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <map>
+#include <algorithm>
+#include <string>
+#include <tuple>
#include "common/alignment.h"
#include "common/bit_util.h"
#include "core/core.h"
#include "video_core/engines/shader_bytecode.h"
#include "video_core/surface.h"
+#include "video_core/texture_cache/format_lookup_table.h"
#include "video_core/texture_cache/surface_params.h"
namespace VideoCommon {
-using VideoCore::Surface::ComponentTypeFromDepthFormat;
-using VideoCore::Surface::ComponentTypeFromRenderTarget;
-using VideoCore::Surface::ComponentTypeFromTexture;
using VideoCore::Surface::PixelFormat;
using VideoCore::Surface::PixelFormatFromDepthFormat;
using VideoCore::Surface::PixelFormatFromRenderTargetFormat;
-using VideoCore::Surface::PixelFormatFromTextureFormat;
using VideoCore::Surface::SurfaceTarget;
using VideoCore::Surface::SurfaceTargetFromTextureType;
using VideoCore::Surface::SurfaceType;
@@ -69,7 +68,8 @@ constexpr u32 GetMipmapSize(bool uncompressed, u32 mip_size, u32 tile) {
} // Anonymous namespace
-SurfaceParams SurfaceParams::CreateForTexture(const Tegra::Texture::TICEntry& tic,
+SurfaceParams SurfaceParams::CreateForTexture(const FormatLookupTable& lookup_table,
+ const Tegra::Texture::TICEntry& tic,
const VideoCommon::Shader::Sampler& entry) {
SurfaceParams params;
params.is_tiled = tic.IsTiled();
@@ -78,8 +78,8 @@ SurfaceParams SurfaceParams::CreateForTexture(const Tegra::Texture::TICEntry& ti
params.block_height = params.is_tiled ? tic.BlockHeight() : 0,
params.block_depth = params.is_tiled ? tic.BlockDepth() : 0,
params.tile_width_spacing = params.is_tiled ? (1 << tic.tile_width_spacing.Value()) : 1;
- params.pixel_format =
- PixelFormatFromTextureFormat(tic.format, tic.r_type.Value(), params.srgb_conversion);
+ params.pixel_format = lookup_table.GetPixelFormat(
+ tic.format, params.srgb_conversion, tic.r_type, tic.g_type, tic.b_type, tic.a_type);
params.type = GetFormatType(params.pixel_format);
if (entry.IsShadow() && params.type == SurfaceType::ColorTexture) {
switch (params.pixel_format) {
@@ -99,7 +99,6 @@ SurfaceParams SurfaceParams::CreateForTexture(const Tegra::Texture::TICEntry& ti
}
params.type = GetFormatType(params.pixel_format);
}
- params.component_type = ComponentTypeFromTexture(tic.r_type.Value());
params.type = GetFormatType(params.pixel_format);
// TODO: on 1DBuffer we should use the tic info.
if (tic.IsBuffer()) {
@@ -128,7 +127,8 @@ SurfaceParams SurfaceParams::CreateForTexture(const Tegra::Texture::TICEntry& ti
return params;
}
-SurfaceParams SurfaceParams::CreateForImage(const Tegra::Texture::TICEntry& tic,
+SurfaceParams SurfaceParams::CreateForImage(const FormatLookupTable& lookup_table,
+ const Tegra::Texture::TICEntry& tic,
const VideoCommon::Shader::Image& entry) {
SurfaceParams params;
params.is_tiled = tic.IsTiled();
@@ -137,10 +137,9 @@ SurfaceParams SurfaceParams::CreateForImage(const Tegra::Texture::TICEntry& tic,
params.block_height = params.is_tiled ? tic.BlockHeight() : 0,
params.block_depth = params.is_tiled ? tic.BlockDepth() : 0,
params.tile_width_spacing = params.is_tiled ? (1 << tic.tile_width_spacing.Value()) : 1;
- params.pixel_format =
- PixelFormatFromTextureFormat(tic.format, tic.r_type.Value(), params.srgb_conversion);
+ params.pixel_format = lookup_table.GetPixelFormat(
+ tic.format, params.srgb_conversion, tic.r_type, tic.g_type, tic.b_type, tic.a_type);
params.type = GetFormatType(params.pixel_format);
- params.component_type = ComponentTypeFromTexture(tic.r_type.Value());
params.type = GetFormatType(params.pixel_format);
params.target = ImageTypeToSurfaceTarget(entry.GetType());
// TODO: on 1DBuffer we should use the tic info.
@@ -181,7 +180,6 @@ SurfaceParams SurfaceParams::CreateForDepthBuffer(
params.block_depth = 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;
@@ -206,7 +204,6 @@ SurfaceParams SurfaceParams::CreateForFramebuffer(Core::System& system, std::siz
params.block_depth = 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.pitch = 0;
@@ -236,7 +233,6 @@ SurfaceParams SurfaceParams::CreateForFermiCopySurface(
params.block_depth = params.is_tiled ? std::min(config.BlockDepth(), 5U) : 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;
@@ -355,10 +351,10 @@ std::size_t SurfaceParams::GetInnerMipmapMemorySize(u32 level, bool as_host_size
bool SurfaceParams::operator==(const SurfaceParams& rhs) const {
return std::tie(is_tiled, block_width, block_height, block_depth, tile_width_spacing, width,
- height, depth, pitch, num_levels, pixel_format, component_type, type, target) ==
+ height, depth, pitch, num_levels, pixel_format, 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.num_levels, rhs.pixel_format, rhs.component_type, rhs.type, rhs.target);
+ rhs.num_levels, rhs.pixel_format, rhs.type, rhs.target);
}
std::string SurfaceParams::TargetName() const {
diff --git a/src/video_core/texture_cache/surface_params.h b/src/video_core/texture_cache/surface_params.h
index c58e7f8a4..709aa0dc2 100644
--- a/src/video_core/texture_cache/surface_params.h
+++ b/src/video_core/texture_cache/surface_params.h
@@ -16,16 +16,20 @@
namespace VideoCommon {
+class FormatLookupTable;
+
using VideoCore::Surface::SurfaceCompression;
class SurfaceParams {
public:
/// Creates SurfaceCachedParams from a texture configuration.
- static SurfaceParams CreateForTexture(const Tegra::Texture::TICEntry& tic,
+ static SurfaceParams CreateForTexture(const FormatLookupTable& lookup_table,
+ const Tegra::Texture::TICEntry& tic,
const VideoCommon::Shader::Sampler& entry);
/// Creates SurfaceCachedParams from an image configuration.
- static SurfaceParams CreateForImage(const Tegra::Texture::TICEntry& tic,
+ static SurfaceParams CreateForImage(const FormatLookupTable& lookup_table,
+ const Tegra::Texture::TICEntry& tic,
const VideoCommon::Shader::Image& entry);
/// Creates SurfaceCachedParams for a depth buffer configuration.
@@ -248,7 +252,6 @@ public:
u32 num_levels;
u32 emulated_levels;
VideoCore::Surface::PixelFormat pixel_format;
- VideoCore::Surface::ComponentType component_type;
VideoCore::Surface::SurfaceType type;
VideoCore::Surface::SurfaceTarget target;
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index 877c6635d..41309ebea 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -29,6 +29,7 @@
#include "video_core/rasterizer_interface.h"
#include "video_core/surface.h"
#include "video_core/texture_cache/copy_params.h"
+#include "video_core/texture_cache/format_lookup_table.h"
#include "video_core/texture_cache/surface_base.h"
#include "video_core/texture_cache/surface_params.h"
#include "video_core/texture_cache/surface_view.h"
@@ -62,10 +63,10 @@ public:
}
}
- /***
- * `Guard` guarantees that rendertargets don't unregister themselves if the
+ /**
+ * Guarantees that rendertargets don't unregister themselves if the
* collide. Protection is currently only done on 3D slices.
- ***/
+ */
void GuardRenderTargets(bool new_guard) {
guard_render_targets = new_guard;
}
@@ -96,7 +97,7 @@ public:
if (!gpu_addr) {
return {};
}
- const auto params{SurfaceParams::CreateForTexture(tic, entry)};
+ const auto params{SurfaceParams::CreateForTexture(format_lookup_table, tic, entry)};
const auto [surface, view] = GetSurface(gpu_addr, params, true, false);
if (guard_samplers) {
sampled_textures.push_back(surface);
@@ -111,7 +112,7 @@ public:
if (!gpu_addr) {
return {};
}
- const auto params{SurfaceParams::CreateForImage(tic, entry)};
+ const auto params{SurfaceParams::CreateForImage(format_lookup_table, tic, entry)};
const auto [surface, view] = GetSurface(gpu_addr, params, true, false);
if (guard_samplers) {
sampled_textures.push_back(surface);
@@ -224,8 +225,13 @@ public:
const Tegra::Engines::Fermi2D::Regs::Surface& dst_config,
const Tegra::Engines::Fermi2D::Config& copy_config) {
std::lock_guard lock{mutex};
- std::pair<TSurface, TView> dst_surface = GetFermiSurface(dst_config);
- std::pair<TSurface, TView> src_surface = GetFermiSurface(src_config);
+ SurfaceParams src_params = SurfaceParams::CreateForFermiCopySurface(src_config);
+ SurfaceParams dst_params = SurfaceParams::CreateForFermiCopySurface(dst_config);
+ const GPUVAddr src_gpu_addr = src_config.Address();
+ const GPUVAddr dst_gpu_addr = dst_config.Address();
+ DeduceBestBlit(src_params, dst_params, src_gpu_addr, dst_gpu_addr);
+ std::pair<TSurface, TView> dst_surface = GetSurface(dst_gpu_addr, dst_params, true, false);
+ std::pair<TSurface, TView> src_surface = GetSurface(src_gpu_addr, src_params, true, false);
ImageBlit(src_surface.second, dst_surface.second, copy_config);
dst_surface.first->MarkAsModified(true, Tick());
}
@@ -282,7 +288,7 @@ protected:
const Tegra::Engines::Fermi2D::Config& copy_config) = 0;
// Depending on the backend, a buffer copy can be slow as it means deoptimizing the texture
- // and reading it from a sepparate buffer.
+ // and reading it from a separate buffer.
virtual void BufferCopy(TSurface& src_surface, TSurface& dst_surface) = 0;
void ManageRenderTargetUnregister(TSurface& surface) {
@@ -357,13 +363,37 @@ private:
BufferCopy = 3,
};
+ enum class DeductionType : u32 {
+ DeductionComplete,
+ DeductionIncomplete,
+ DeductionFailed,
+ };
+
+ struct Deduction {
+ DeductionType type{DeductionType::DeductionFailed};
+ TSurface surface{};
+
+ bool Failed() const {
+ return type == DeductionType::DeductionFailed;
+ }
+
+ bool Incomplete() const {
+ return type == DeductionType::DeductionIncomplete;
+ }
+
+ bool IsDepth() const {
+ return surface->GetSurfaceParams().IsPixelFormatZeta();
+ }
+ };
+
/**
- * `PickStrategy` takes care of selecting a proper strategy to deal with a texture recycle.
- * @param overlaps, the overlapping surfaces registered in the cache.
- * @param params, the paremeters on the new surface.
- * @param gpu_addr, the starting address of the new surface.
- * @param untopological, tells the recycler that the texture has no way to match the overlaps
- * due to topological reasons.
+ * Takes care of selecting a proper strategy to deal with a texture recycle.
+ *
+ * @param overlaps The overlapping surfaces registered in the cache.
+ * @param params The parameters on the new surface.
+ * @param gpu_addr The starting address of the new surface.
+ * @param untopological Indicates to the recycler that the texture has no way
+ * to match the overlaps due to topological reasons.
**/
RecycleStrategy PickStrategy(std::vector<TSurface>& overlaps, const SurfaceParams& params,
const GPUVAddr gpu_addr, const MatchTopologyResult untopological) {
@@ -374,7 +404,7 @@ private:
if (params.block_depth > 1 || params.target == SurfaceTarget::Texture3D) {
return RecycleStrategy::Flush;
}
- for (auto s : overlaps) {
+ for (const auto& s : overlaps) {
const auto& s_params = s->GetSurfaceParams();
if (s_params.block_depth > 1 || s_params.target == SurfaceTarget::Texture3D) {
return RecycleStrategy::Flush;
@@ -391,16 +421,19 @@ private:
}
/**
- * `RecycleSurface` es a method we use to decide what to do with textures we can't resolve in
- *the cache It has 2 implemented strategies: Ignore and Flush. Ignore just unregisters all the
- *overlaps and loads the new texture. Flush, flushes all the overlaps into memory and loads the
- *new surface from that data.
- * @param overlaps, the overlapping surfaces registered in the cache.
- * @param params, the paremeters on the new surface.
- * @param gpu_addr, the starting address of the new surface.
- * @param preserve_contents, tells if the new surface should be loaded from meory or left blank
- * @param untopological, tells the recycler that the texture has no way to match the overlaps
- * due to topological reasons.
+ * Used to decide what to do with textures we can't resolve in the cache It has 2 implemented
+ * strategies: Ignore and Flush.
+ *
+ * - Ignore: Just unregisters all the overlaps and loads the new texture.
+ * - Flush: Flushes all the overlaps into memory and loads the new surface from that data.
+ *
+ * @param overlaps The overlapping surfaces registered in the cache.
+ * @param params The parameters for the new surface.
+ * @param gpu_addr The starting address of the new surface.
+ * @param preserve_contents Indicates that the new surface should be loaded from memory or left
+ * blank.
+ * @param untopological Indicates to the recycler that the texture has no way to match the
+ * overlaps due to topological reasons.
**/
std::pair<TSurface, TView> RecycleSurface(std::vector<TSurface>& overlaps,
const SurfaceParams& params, const GPUVAddr gpu_addr,
@@ -437,10 +470,12 @@ private:
}
/**
- * `RebuildSurface` this method takes a single surface and recreates into another that
- * may differ in format, target or width alingment.
- * @param current_surface, the registered surface in the cache which we want to convert.
- * @param params, the new surface params which we'll use to recreate the surface.
+ * Takes a single surface and recreates into another that may differ in
+ * format, target or width alignment.
+ *
+ * @param current_surface The registered surface in the cache which we want to convert.
+ * @param params The new surface params which we'll use to recreate the surface.
+ * @param is_render Whether or not the surface is a render target.
**/
std::pair<TSurface, TView> RebuildSurface(TSurface current_surface, const SurfaceParams& params,
bool is_render) {
@@ -451,15 +486,13 @@ private:
GetSiblingFormat(cr_params.pixel_format) == params.pixel_format) {
SurfaceParams new_params = params;
new_params.pixel_format = cr_params.pixel_format;
- new_params.component_type = cr_params.component_type;
new_params.type = cr_params.type;
new_surface = GetUncachedSurface(gpu_addr, new_params);
} else {
new_surface = GetUncachedSurface(gpu_addr, params);
}
const auto& final_params = new_surface->GetSurfaceParams();
- if (cr_params.type != final_params.type ||
- (cr_params.component_type != final_params.component_type)) {
+ if (cr_params.type != final_params.type) {
BufferCopy(current_surface, new_surface);
} else {
std::vector<CopyParams> bricks = current_surface->BreakDown(final_params);
@@ -474,12 +507,14 @@ private:
}
/**
- * `ManageStructuralMatch` this method takes a single surface and checks with the new surface's
- * params if it's an exact match, we return the main view of the registered surface. If it's
- * formats don't match, we rebuild the surface. We call this last method a `Mirage`. If formats
+ * Takes a single surface and checks with the new surface's params if it's an exact
+ * match, we return the main view of the registered surface. If its formats don't
+ * match, we rebuild the surface. We call this last method a `Mirage`. If formats
* match but the targets don't, we create an overview View of the registered surface.
- * @param current_surface, the registered surface in the cache which we want to convert.
- * @param params, the new surface params which we want to check.
+ *
+ * @param current_surface The registered surface in the cache which we want to convert.
+ * @param params The new surface params which we want to check.
+ * @param is_render Whether or not the surface is a render target.
**/
std::pair<TSurface, TView> ManageStructuralMatch(TSurface current_surface,
const SurfaceParams& params, bool is_render) {
@@ -501,13 +536,14 @@ private:
}
/**
- * `TryReconstructSurface` unlike `RebuildSurface` where we know the registered surface
- * matches the candidate in some way, we got no guarantess here. We try to see if the overlaps
- * are sublayers/mipmaps of the new surface, if they all match we end up recreating a surface
- * for them, else we return nothing.
- * @param overlaps, the overlapping surfaces registered in the cache.
- * @param params, the paremeters on the new surface.
- * @param gpu_addr, the starting address of the new surface.
+ * Unlike RebuildSurface where we know whether or not registered surfaces match the candidate
+ * in some way, we have no guarantees here. We try to see if the overlaps are sublayers/mipmaps
+ * of the new surface, if they all match we end up recreating a surface for them,
+ * else we return nothing.
+ *
+ * @param overlaps The overlapping surfaces registered in the cache.
+ * @param params The parameters on the new surface.
+ * @param gpu_addr The starting address of the new surface.
**/
std::optional<std::pair<TSurface, TView>> TryReconstructSurface(std::vector<TSurface>& overlaps,
const SurfaceParams& params,
@@ -547,7 +583,7 @@ private:
} else if (Settings::values.use_accurate_gpu_emulation && passed_tests != overlaps.size()) {
return {};
}
- for (auto surface : overlaps) {
+ for (const auto& surface : overlaps) {
Unregister(surface);
}
new_surface->MarkAsModified(modified, Tick());
@@ -556,19 +592,27 @@ private:
}
/**
- * `GetSurface` gets the starting address and parameters of a candidate surface and tries
- * to find a matching surface within the cache. This is done in 3 big steps. The first is to
- * check the 1st Level Cache in order to find an exact match, if we fail, we move to step 2.
- * Step 2 is checking if there are any overlaps at all, if none, we just load the texture from
- * memory else we move to step 3. Step 3 consists on figuring the relationship between the
- * candidate texture and the overlaps. We divide the scenarios depending if there's 1 or many
- * overlaps. If there's many, we just try to reconstruct a new surface out of them based on the
- * candidate's parameters, if we fail, we recycle. When there's only 1 overlap then we have to
- * check if the candidate is a view (layer/mipmap) of the overlap or if the registered surface
- * is a mipmap/layer of the candidate. In this last case we reconstruct a new surface.
- * @param gpu_addr, the starting address of the candidate surface.
- * @param params, the paremeters on the candidate surface.
- * @param preserve_contents, tells if the new surface should be loaded from meory or left blank.
+ * Gets the starting address and parameters of a candidate surface and tries
+ * to find a matching surface within the cache. This is done in 3 big steps:
+ *
+ * 1. Check the 1st Level Cache in order to find an exact match, if we fail, we move to step 2.
+ *
+ * 2. Check if there are any overlaps at all, if there are none, we just load the texture from
+ * memory else we move to step 3.
+ *
+ * 3. Consists of figuring out the relationship between the candidate texture and the
+ * overlaps. We divide the scenarios depending if there's 1 or many overlaps. If
+ * there's many, we just try to reconstruct a new surface out of them based on the
+ * candidate's parameters, if we fail, we recycle. When there's only 1 overlap then we
+ * have to check if the candidate is a view (layer/mipmap) of the overlap or if the
+ * registered surface is a mipmap/layer of the candidate. In this last case we reconstruct
+ * a new surface.
+ *
+ * @param gpu_addr The starting address of the candidate surface.
+ * @param params The parameters on the candidate surface.
+ * @param preserve_contents Indicates that the new surface should be loaded from memory or
+ * left blank.
+ * @param is_render Whether or not the surface is a render target.
**/
std::pair<TSurface, TView> GetSurface(const GPUVAddr gpu_addr, const SurfaceParams& params,
bool preserve_contents, bool is_render) {
@@ -623,7 +667,7 @@ private:
// Step 3
// Now we need to figure the relationship between the texture and its overlaps
// we do a topological test to ensure we can find some relationship. If it fails
- // inmediatly recycle the texture
+ // immediately recycle the texture
for (const auto& surface : overlaps) {
const auto topological_result = surface->MatchesTopology(params);
if (topological_result != MatchTopologyResult::FullMatch) {
@@ -691,6 +735,123 @@ private:
MatchTopologyResult::FullMatch);
}
+ /**
+ * Gets the starting address and parameters of a candidate surface and tries to find a
+ * matching surface within the cache that's similar to it. If there are many textures
+ * or the texture found if entirely incompatible, it will fail. If no texture is found, the
+ * blit will be unsuccessful.
+ *
+ * @param gpu_addr The starting address of the candidate surface.
+ * @param params The parameters on the candidate surface.
+ **/
+ Deduction DeduceSurface(const GPUVAddr gpu_addr, const SurfaceParams& params) {
+ const auto host_ptr{system.GPU().MemoryManager().GetPointer(gpu_addr)};
+ const auto cache_addr{ToCacheAddr(host_ptr)};
+
+ if (!cache_addr) {
+ Deduction result{};
+ result.type = DeductionType::DeductionFailed;
+ return result;
+ }
+
+ if (const auto iter = l1_cache.find(cache_addr); iter != l1_cache.end()) {
+ TSurface& current_surface = iter->second;
+ const auto topological_result = current_surface->MatchesTopology(params);
+ if (topological_result != MatchTopologyResult::FullMatch) {
+ Deduction result{};
+ result.type = DeductionType::DeductionFailed;
+ return result;
+ }
+ const auto struct_result = current_surface->MatchesStructure(params);
+ if (struct_result != MatchStructureResult::None &&
+ current_surface->MatchTarget(params.target)) {
+ Deduction result{};
+ result.type = DeductionType::DeductionComplete;
+ result.surface = current_surface;
+ return result;
+ }
+ }
+
+ const std::size_t candidate_size = params.GetGuestSizeInBytes();
+ auto overlaps{GetSurfacesInRegion(cache_addr, candidate_size)};
+
+ if (overlaps.empty()) {
+ Deduction result{};
+ result.type = DeductionType::DeductionIncomplete;
+ return result;
+ }
+
+ if (overlaps.size() > 1) {
+ Deduction result{};
+ result.type = DeductionType::DeductionFailed;
+ return result;
+ } else {
+ Deduction result{};
+ result.type = DeductionType::DeductionComplete;
+ result.surface = overlaps[0];
+ return result;
+ }
+ }
+
+ /**
+ * Gets the a source and destination starting address and parameters,
+ * and tries to deduce if they are supposed to be depth textures. If so, their
+ * parameters are modified and fixed into so.
+ *
+ * @param src_params The parameters of the candidate surface.
+ * @param dst_params The parameters of the destination surface.
+ * @param src_gpu_addr The starting address of the candidate surface.
+ * @param dst_gpu_addr The starting address of the destination surface.
+ **/
+ void DeduceBestBlit(SurfaceParams& src_params, SurfaceParams& dst_params,
+ const GPUVAddr src_gpu_addr, const GPUVAddr dst_gpu_addr) {
+ auto deduced_src = DeduceSurface(src_gpu_addr, src_params);
+ auto deduced_dst = DeduceSurface(src_gpu_addr, src_params);
+ if (deduced_src.Failed() || deduced_dst.Failed()) {
+ return;
+ }
+
+ const bool incomplete_src = deduced_src.Incomplete();
+ const bool incomplete_dst = deduced_dst.Incomplete();
+
+ if (incomplete_src && incomplete_dst) {
+ return;
+ }
+
+ const bool any_incomplete = incomplete_src || incomplete_dst;
+
+ if (!any_incomplete) {
+ if (!(deduced_src.IsDepth() && deduced_dst.IsDepth())) {
+ return;
+ }
+ } else {
+ if (incomplete_src && !(deduced_dst.IsDepth())) {
+ return;
+ }
+
+ if (incomplete_dst && !(deduced_src.IsDepth())) {
+ return;
+ }
+ }
+
+ const auto inherit_format = [](SurfaceParams& to, TSurface from) {
+ const SurfaceParams& params = from->GetSurfaceParams();
+ to.pixel_format = params.pixel_format;
+ to.type = params.type;
+ };
+ // Now we got the cases where one or both is Depth and the other is not known
+ if (!incomplete_src) {
+ inherit_format(src_params, deduced_src.surface);
+ } else {
+ inherit_format(src_params, deduced_dst.surface);
+ }
+ if (!incomplete_dst) {
+ inherit_format(dst_params, deduced_dst.surface);
+ } else {
+ inherit_format(dst_params, deduced_src.surface);
+ }
+ }
+
std::pair<TSurface, TView> InitializeSurface(GPUVAddr gpu_addr, const SurfaceParams& params,
bool preserve_contents) {
auto new_surface{GetUncachedSurface(gpu_addr, params)};
@@ -793,6 +954,8 @@ private:
VideoCore::RasterizerInterface& rasterizer;
+ FormatLookupTable format_lookup_table;
+
u64 ticks{};
// Guards the cache for protection conflicts.