summaryrefslogtreecommitdiff
path: root/src/video_core/texture_cache
diff options
context:
space:
mode:
authorGravatar bunnei2021-11-16 18:52:11 -0800
committerGravatar GitHub2021-11-16 18:52:11 -0800
commit71313509f75aeafe425e531824d1faa9e7c0a40b (patch)
treecb1df371d288677fcede6a3409eb079e0d278163 /src/video_core/texture_cache
parentMerge pull request #7347 from lioncash/catch (diff)
parentTextureCache: Fix Automatic Anisotropic. (diff)
downloadyuzu-71313509f75aeafe425e531824d1faa9e7c0a40b.tar.gz
yuzu-71313509f75aeafe425e531824d1faa9e7c0a40b.tar.xz
yuzu-71313509f75aeafe425e531824d1faa9e7c0a40b.zip
Merge pull request #7219 from FernandoS27/aristotles-right-testicle
Project A.R.T. Advanced Rendering Techniques
Diffstat (limited to 'src/video_core/texture_cache')
-rw-r--r--src/video_core/texture_cache/image_base.cpp10
-rw-r--r--src/video_core/texture_cache/image_base.h15
-rw-r--r--src/video_core/texture_cache/image_info.cpp22
-rw-r--r--src/video_core/texture_cache/image_info.h4
-rw-r--r--src/video_core/texture_cache/image_view_base.cpp13
-rw-r--r--src/video_core/texture_cache/image_view_base.h4
-rw-r--r--src/video_core/texture_cache/texture_cache.h495
-rw-r--r--src/video_core/texture_cache/texture_cache_base.h43
-rw-r--r--src/video_core/texture_cache/types.h7
-rw-r--r--src/video_core/texture_cache/util.cpp12
-rw-r--r--src/video_core/texture_cache/util.h3
11 files changed, 539 insertions, 89 deletions
diff --git a/src/video_core/texture_cache/image_base.cpp b/src/video_core/texture_cache/image_base.cpp
index 6052d148a..3db2fdf34 100644
--- a/src/video_core/texture_cache/image_base.cpp
+++ b/src/video_core/texture_cache/image_base.cpp
@@ -60,15 +60,17 @@ namespace {
60ImageBase::ImageBase(const ImageInfo& info_, GPUVAddr gpu_addr_, VAddr cpu_addr_) 60ImageBase::ImageBase(const ImageInfo& info_, GPUVAddr gpu_addr_, VAddr cpu_addr_)
61 : info{info_}, guest_size_bytes{CalculateGuestSizeInBytes(info)}, 61 : info{info_}, guest_size_bytes{CalculateGuestSizeInBytes(info)},
62 unswizzled_size_bytes{CalculateUnswizzledSizeBytes(info)}, 62 unswizzled_size_bytes{CalculateUnswizzledSizeBytes(info)},
63 converted_size_bytes{CalculateConvertedSizeBytes(info)}, gpu_addr{gpu_addr_}, 63 converted_size_bytes{CalculateConvertedSizeBytes(info)}, scale_rating{}, scale_tick{},
64 cpu_addr{cpu_addr_}, cpu_addr_end{cpu_addr + guest_size_bytes}, 64 has_scaled{}, gpu_addr{gpu_addr_}, cpu_addr{cpu_addr_},
65 mip_level_offsets{CalculateMipLevelOffsets(info)} { 65 cpu_addr_end{cpu_addr + guest_size_bytes}, mip_level_offsets{CalculateMipLevelOffsets(info)} {
66 if (info.type == ImageType::e3D) { 66 if (info.type == ImageType::e3D) {
67 slice_offsets = CalculateSliceOffsets(info); 67 slice_offsets = CalculateSliceOffsets(info);
68 slice_subresources = CalculateSliceSubresources(info); 68 slice_subresources = CalculateSliceSubresources(info);
69 } 69 }
70} 70}
71 71
72ImageBase::ImageBase(const NullImageParams&) {}
73
72ImageMapView::ImageMapView(GPUVAddr gpu_addr_, VAddr cpu_addr_, size_t size_, ImageId image_id_) 74ImageMapView::ImageMapView(GPUVAddr gpu_addr_, VAddr cpu_addr_, size_t size_, ImageId image_id_)
73 : gpu_addr{gpu_addr_}, cpu_addr{cpu_addr_}, size{size_}, image_id{image_id_} {} 75 : gpu_addr{gpu_addr_}, cpu_addr{cpu_addr_}, size{size_}, image_id{image_id_} {}
74 76
@@ -254,6 +256,8 @@ void AddImageAlias(ImageBase& lhs, ImageBase& rhs, ImageId lhs_id, ImageId rhs_i
254 } 256 }
255 lhs.aliased_images.push_back(std::move(lhs_alias)); 257 lhs.aliased_images.push_back(std::move(lhs_alias));
256 rhs.aliased_images.push_back(std::move(rhs_alias)); 258 rhs.aliased_images.push_back(std::move(rhs_alias));
259 lhs.flags &= ~ImageFlagBits::IsRescalable;
260 rhs.flags &= ~ImageFlagBits::IsRescalable;
257} 261}
258 262
259} // namespace VideoCommon 263} // namespace VideoCommon
diff --git a/src/video_core/texture_cache/image_base.h b/src/video_core/texture_cache/image_base.h
index 0c17a791b..89c111c00 100644
--- a/src/video_core/texture_cache/image_base.h
+++ b/src/video_core/texture_cache/image_base.h
@@ -33,6 +33,11 @@ enum class ImageFlagBits : u32 {
33 ///< garbage collection priority 33 ///< garbage collection priority
34 Alias = 1 << 11, ///< This image has aliases and has priority on garbage 34 Alias = 1 << 11, ///< This image has aliases and has priority on garbage
35 ///< collection 35 ///< collection
36
37 // Rescaler
38 Rescaled = 1 << 12,
39 CheckingRescalable = 1 << 13,
40 IsRescalable = 1 << 14,
36}; 41};
37DECLARE_ENUM_FLAG_OPERATORS(ImageFlagBits) 42DECLARE_ENUM_FLAG_OPERATORS(ImageFlagBits)
38 43
@@ -43,8 +48,11 @@ struct AliasedImage {
43 ImageId id; 48 ImageId id;
44}; 49};
45 50
51struct NullImageParams {};
52
46struct ImageBase { 53struct ImageBase {
47 explicit ImageBase(const ImageInfo& info, GPUVAddr gpu_addr, VAddr cpu_addr); 54 explicit ImageBase(const ImageInfo& info, GPUVAddr gpu_addr, VAddr cpu_addr);
55 explicit ImageBase(const NullImageParams&);
48 56
49 [[nodiscard]] std::optional<SubresourceBase> TryFindBase(GPUVAddr other_addr) const noexcept; 57 [[nodiscard]] std::optional<SubresourceBase> TryFindBase(GPUVAddr other_addr) const noexcept;
50 58
@@ -68,11 +76,18 @@ struct ImageBase {
68 void CheckBadOverlapState(); 76 void CheckBadOverlapState();
69 void CheckAliasState(); 77 void CheckAliasState();
70 78
79 bool HasScaled() const {
80 return has_scaled;
81 }
82
71 ImageInfo info; 83 ImageInfo info;
72 84
73 u32 guest_size_bytes = 0; 85 u32 guest_size_bytes = 0;
74 u32 unswizzled_size_bytes = 0; 86 u32 unswizzled_size_bytes = 0;
75 u32 converted_size_bytes = 0; 87 u32 converted_size_bytes = 0;
88 u32 scale_rating = 0;
89 u64 scale_tick = 0;
90 bool has_scaled = false;
76 ImageFlagBits flags = ImageFlagBits::CpuModified; 91 ImageFlagBits flags = ImageFlagBits::CpuModified;
77 92
78 GPUVAddr gpu_addr = 0; 93 GPUVAddr gpu_addr = 0;
diff --git a/src/video_core/texture_cache/image_info.cpp b/src/video_core/texture_cache/image_info.cpp
index 64fd7010a..afb94082b 100644
--- a/src/video_core/texture_cache/image_info.cpp
+++ b/src/video_core/texture_cache/image_info.cpp
@@ -16,6 +16,7 @@ namespace VideoCommon {
16using Tegra::Texture::TextureType; 16using Tegra::Texture::TextureType;
17using Tegra::Texture::TICEntry; 17using Tegra::Texture::TICEntry;
18using VideoCore::Surface::PixelFormat; 18using VideoCore::Surface::PixelFormat;
19using VideoCore::Surface::SurfaceType;
19 20
20ImageInfo::ImageInfo(const TICEntry& config) noexcept { 21ImageInfo::ImageInfo(const TICEntry& config) noexcept {
21 format = PixelFormatFromTextureInfo(config.format, config.r_type, config.g_type, config.b_type, 22 format = PixelFormatFromTextureInfo(config.format, config.r_type, config.g_type, config.b_type,
@@ -31,6 +32,7 @@ ImageInfo::ImageInfo(const TICEntry& config) noexcept {
31 .depth = config.block_depth, 32 .depth = config.block_depth,
32 }; 33 };
33 } 34 }
35 rescaleable = false;
34 tile_width_spacing = config.tile_width_spacing; 36 tile_width_spacing = config.tile_width_spacing;
35 if (config.texture_type != TextureType::Texture2D && 37 if (config.texture_type != TextureType::Texture2D &&
36 config.texture_type != TextureType::Texture2DNoMipmap) { 38 config.texture_type != TextureType::Texture2DNoMipmap) {
@@ -41,6 +43,7 @@ ImageInfo::ImageInfo(const TICEntry& config) noexcept {
41 ASSERT(config.BaseLayer() == 0); 43 ASSERT(config.BaseLayer() == 0);
42 type = ImageType::e1D; 44 type = ImageType::e1D;
43 size.width = config.Width(); 45 size.width = config.Width();
46 resources.layers = 1;
44 break; 47 break;
45 case TextureType::Texture1DArray: 48 case TextureType::Texture1DArray:
46 UNIMPLEMENTED_IF(config.BaseLayer() != 0); 49 UNIMPLEMENTED_IF(config.BaseLayer() != 0);
@@ -52,12 +55,14 @@ ImageInfo::ImageInfo(const TICEntry& config) noexcept {
52 case TextureType::Texture2DNoMipmap: 55 case TextureType::Texture2DNoMipmap:
53 ASSERT(config.Depth() == 1); 56 ASSERT(config.Depth() == 1);
54 type = config.IsPitchLinear() ? ImageType::Linear : ImageType::e2D; 57 type = config.IsPitchLinear() ? ImageType::Linear : ImageType::e2D;
58 rescaleable = !config.IsPitchLinear();
55 size.width = config.Width(); 59 size.width = config.Width();
56 size.height = config.Height(); 60 size.height = config.Height();
57 resources.layers = config.BaseLayer() + 1; 61 resources.layers = config.BaseLayer() + 1;
58 break; 62 break;
59 case TextureType::Texture2DArray: 63 case TextureType::Texture2DArray:
60 type = ImageType::e2D; 64 type = ImageType::e2D;
65 rescaleable = true;
61 size.width = config.Width(); 66 size.width = config.Width();
62 size.height = config.Height(); 67 size.height = config.Height();
63 resources.layers = config.BaseLayer() + config.Depth(); 68 resources.layers = config.BaseLayer() + config.Depth();
@@ -82,10 +87,12 @@ ImageInfo::ImageInfo(const TICEntry& config) noexcept {
82 size.width = config.Width(); 87 size.width = config.Width();
83 size.height = config.Height(); 88 size.height = config.Height();
84 size.depth = config.Depth(); 89 size.depth = config.Depth();
90 resources.layers = 1;
85 break; 91 break;
86 case TextureType::Texture1DBuffer: 92 case TextureType::Texture1DBuffer:
87 type = ImageType::Buffer; 93 type = ImageType::Buffer;
88 size.width = config.Width(); 94 size.width = config.Width();
95 resources.layers = 1;
89 break; 96 break;
90 default: 97 default:
91 UNREACHABLE_MSG("Invalid texture_type={}", static_cast<int>(config.texture_type.Value())); 98 UNREACHABLE_MSG("Invalid texture_type={}", static_cast<int>(config.texture_type.Value()));
@@ -95,12 +102,16 @@ ImageInfo::ImageInfo(const TICEntry& config) noexcept {
95 // FIXME: Call this without passing *this 102 // FIXME: Call this without passing *this
96 layer_stride = CalculateLayerStride(*this); 103 layer_stride = CalculateLayerStride(*this);
97 maybe_unaligned_layer_stride = CalculateLayerSize(*this); 104 maybe_unaligned_layer_stride = CalculateLayerSize(*this);
105 rescaleable &= (block.depth == 0) && resources.levels == 1;
106 rescaleable &= size.height > 256 || GetFormatType(format) != SurfaceType::ColorTexture;
107 downscaleable = size.height > 512;
98 } 108 }
99} 109}
100 110
101ImageInfo::ImageInfo(const Tegra::Engines::Maxwell3D::Regs& regs, size_t index) noexcept { 111ImageInfo::ImageInfo(const Tegra::Engines::Maxwell3D::Regs& regs, size_t index) noexcept {
102 const auto& rt = regs.rt[index]; 112 const auto& rt = regs.rt[index];
103 format = VideoCore::Surface::PixelFormatFromRenderTargetFormat(rt.format); 113 format = VideoCore::Surface::PixelFormatFromRenderTargetFormat(rt.format);
114 rescaleable = false;
104 if (rt.tile_mode.is_pitch_linear) { 115 if (rt.tile_mode.is_pitch_linear) {
105 ASSERT(rt.tile_mode.is_3d == 0); 116 ASSERT(rt.tile_mode.is_3d == 0);
106 type = ImageType::Linear; 117 type = ImageType::Linear;
@@ -126,6 +137,9 @@ ImageInfo::ImageInfo(const Tegra::Engines::Maxwell3D::Regs& regs, size_t index)
126 type = ImageType::e3D; 137 type = ImageType::e3D;
127 size.depth = rt.depth; 138 size.depth = rt.depth;
128 } else { 139 } else {
140 rescaleable = block.depth == 0;
141 rescaleable &= size.height > 256;
142 downscaleable = size.height > 512;
129 type = ImageType::e2D; 143 type = ImageType::e2D;
130 resources.layers = rt.depth; 144 resources.layers = rt.depth;
131 } 145 }
@@ -135,6 +149,7 @@ ImageInfo::ImageInfo(const Tegra::Engines::Maxwell3D::Regs& regs) noexcept {
135 format = VideoCore::Surface::PixelFormatFromDepthFormat(regs.zeta.format); 149 format = VideoCore::Surface::PixelFormatFromDepthFormat(regs.zeta.format);
136 size.width = regs.zeta_width; 150 size.width = regs.zeta_width;
137 size.height = regs.zeta_height; 151 size.height = regs.zeta_height;
152 rescaleable = false;
138 resources.levels = 1; 153 resources.levels = 1;
139 layer_stride = regs.zeta.layer_stride * 4; 154 layer_stride = regs.zeta.layer_stride * 4;
140 maybe_unaligned_layer_stride = layer_stride; 155 maybe_unaligned_layer_stride = layer_stride;
@@ -153,6 +168,8 @@ ImageInfo::ImageInfo(const Tegra::Engines::Maxwell3D::Regs& regs) noexcept {
153 type = ImageType::e3D; 168 type = ImageType::e3D;
154 size.depth = regs.zeta_depth; 169 size.depth = regs.zeta_depth;
155 } else { 170 } else {
171 rescaleable = block.depth == 0;
172 downscaleable = size.height > 512;
156 type = ImageType::e2D; 173 type = ImageType::e2D;
157 resources.layers = regs.zeta_depth; 174 resources.layers = regs.zeta_depth;
158 } 175 }
@@ -161,6 +178,7 @@ ImageInfo::ImageInfo(const Tegra::Engines::Maxwell3D::Regs& regs) noexcept {
161ImageInfo::ImageInfo(const Tegra::Engines::Fermi2D::Surface& config) noexcept { 178ImageInfo::ImageInfo(const Tegra::Engines::Fermi2D::Surface& config) noexcept {
162 UNIMPLEMENTED_IF_MSG(config.layer != 0, "Surface layer is not zero"); 179 UNIMPLEMENTED_IF_MSG(config.layer != 0, "Surface layer is not zero");
163 format = VideoCore::Surface::PixelFormatFromRenderTargetFormat(config.format); 180 format = VideoCore::Surface::PixelFormatFromRenderTargetFormat(config.format);
181 rescaleable = false;
164 if (config.linear == Tegra::Engines::Fermi2D::MemoryLayout::Pitch) { 182 if (config.linear == Tegra::Engines::Fermi2D::MemoryLayout::Pitch) {
165 type = ImageType::Linear; 183 type = ImageType::Linear;
166 size = Extent3D{ 184 size = Extent3D{
@@ -171,6 +189,7 @@ ImageInfo::ImageInfo(const Tegra::Engines::Fermi2D::Surface& config) noexcept {
171 pitch = config.pitch; 189 pitch = config.pitch;
172 } else { 190 } else {
173 type = config.block_depth > 0 ? ImageType::e3D : ImageType::e2D; 191 type = config.block_depth > 0 ? ImageType::e3D : ImageType::e2D;
192
174 block = Extent3D{ 193 block = Extent3D{
175 .width = config.block_width, 194 .width = config.block_width,
176 .height = config.block_height, 195 .height = config.block_height,
@@ -183,6 +202,9 @@ ImageInfo::ImageInfo(const Tegra::Engines::Fermi2D::Surface& config) noexcept {
183 .height = config.height, 202 .height = config.height,
184 .depth = 1, 203 .depth = 1,
185 }; 204 };
205 rescaleable = block.depth == 0;
206 rescaleable &= size.height > 256;
207 downscaleable = size.height > 512;
186 } 208 }
187} 209}
188 210
diff --git a/src/video_core/texture_cache/image_info.h b/src/video_core/texture_cache/image_info.h
index 5049fc36e..5932dcaba 100644
--- a/src/video_core/texture_cache/image_info.h
+++ b/src/video_core/texture_cache/image_info.h
@@ -15,7 +15,7 @@ using Tegra::Texture::TICEntry;
15using VideoCore::Surface::PixelFormat; 15using VideoCore::Surface::PixelFormat;
16 16
17struct ImageInfo { 17struct ImageInfo {
18 explicit ImageInfo() = default; 18 ImageInfo() = default;
19 explicit ImageInfo(const TICEntry& config) noexcept; 19 explicit ImageInfo(const TICEntry& config) noexcept;
20 explicit ImageInfo(const Tegra::Engines::Maxwell3D::Regs& regs, size_t index) noexcept; 20 explicit ImageInfo(const Tegra::Engines::Maxwell3D::Regs& regs, size_t index) noexcept;
21 explicit ImageInfo(const Tegra::Engines::Maxwell3D::Regs& regs) noexcept; 21 explicit ImageInfo(const Tegra::Engines::Maxwell3D::Regs& regs) noexcept;
@@ -33,6 +33,8 @@ struct ImageInfo {
33 u32 maybe_unaligned_layer_stride = 0; 33 u32 maybe_unaligned_layer_stride = 0;
34 u32 num_samples = 1; 34 u32 num_samples = 1;
35 u32 tile_width_spacing = 0; 35 u32 tile_width_spacing = 0;
36 bool rescaleable = false;
37 bool downscaleable = false;
36}; 38};
37 39
38} // namespace VideoCommon 40} // namespace VideoCommon
diff --git a/src/video_core/texture_cache/image_view_base.cpp b/src/video_core/texture_cache/image_view_base.cpp
index 450becbeb..c7b4fc231 100644
--- a/src/video_core/texture_cache/image_view_base.cpp
+++ b/src/video_core/texture_cache/image_view_base.cpp
@@ -37,14 +37,15 @@ ImageViewBase::ImageViewBase(const ImageViewInfo& info, const ImageInfo& image_i
37} 37}
38 38
39ImageViewBase::ImageViewBase(const ImageInfo& info, const ImageViewInfo& view_info) 39ImageViewBase::ImageViewBase(const ImageInfo& info, const ImageViewInfo& view_info)
40 : format{info.format}, type{ImageViewType::Buffer}, size{ 40 : image_id{NULL_IMAGE_ID}, format{info.format}, type{ImageViewType::Buffer},
41 .width = info.size.width, 41 size{
42 .height = 1, 42 .width = info.size.width,
43 .depth = 1, 43 .height = 1,
44 } { 44 .depth = 1,
45 } {
45 ASSERT_MSG(view_info.type == ImageViewType::Buffer, "Expected texture buffer"); 46 ASSERT_MSG(view_info.type == ImageViewType::Buffer, "Expected texture buffer");
46} 47}
47 48
48ImageViewBase::ImageViewBase(const NullImageParams&) {} 49ImageViewBase::ImageViewBase(const NullImageViewParams&) : image_id{NULL_IMAGE_ID} {}
49 50
50} // namespace VideoCommon 51} // namespace VideoCommon
diff --git a/src/video_core/texture_cache/image_view_base.h b/src/video_core/texture_cache/image_view_base.h
index 903f715c5..9c24c5359 100644
--- a/src/video_core/texture_cache/image_view_base.h
+++ b/src/video_core/texture_cache/image_view_base.h
@@ -15,7 +15,7 @@ using VideoCore::Surface::PixelFormat;
15struct ImageViewInfo; 15struct ImageViewInfo;
16struct ImageInfo; 16struct ImageInfo;
17 17
18struct NullImageParams {}; 18struct NullImageViewParams {};
19 19
20enum class ImageViewFlagBits : u16 { 20enum class ImageViewFlagBits : u16 {
21 PreemtiveDownload = 1 << 0, 21 PreemtiveDownload = 1 << 0,
@@ -28,7 +28,7 @@ struct ImageViewBase {
28 explicit ImageViewBase(const ImageViewInfo& info, const ImageInfo& image_info, 28 explicit ImageViewBase(const ImageViewInfo& info, const ImageInfo& image_info,
29 ImageId image_id); 29 ImageId image_id);
30 explicit ImageViewBase(const ImageInfo& info, const ImageViewInfo& view_info); 30 explicit ImageViewBase(const ImageInfo& info, const ImageViewInfo& view_info);
31 explicit ImageViewBase(const NullImageParams&); 31 explicit ImageViewBase(const NullImageViewParams&);
32 32
33 [[nodiscard]] bool IsBuffer() const noexcept { 33 [[nodiscard]] bool IsBuffer() const noexcept {
34 return type == ImageViewType::Buffer; 34 return type == ImageViewType::Buffer;
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index f70c1f764..4d2874bf2 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -7,6 +7,7 @@
7#include <unordered_set> 7#include <unordered_set>
8 8
9#include "common/alignment.h" 9#include "common/alignment.h"
10#include "common/settings.h"
10#include "video_core/dirty_flags.h" 11#include "video_core/dirty_flags.h"
11#include "video_core/engines/kepler_compute.h" 12#include "video_core/engines/kepler_compute.h"
12#include "video_core/texture_cache/image_view_base.h" 13#include "video_core/texture_cache/image_view_base.h"
@@ -44,21 +45,22 @@ TextureCache<P>::TextureCache(Runtime& runtime_, VideoCore::RasterizerInterface&
44 45
45 // Make sure the first index is reserved for the null resources 46 // Make sure the first index is reserved for the null resources
46 // This way the null resource becomes a compile time constant 47 // This way the null resource becomes a compile time constant
47 void(slot_image_views.insert(runtime, NullImageParams{})); 48 void(slot_images.insert(NullImageParams{}));
49 void(slot_image_views.insert(runtime, NullImageViewParams{}));
48 void(slot_samplers.insert(runtime, sampler_descriptor)); 50 void(slot_samplers.insert(runtime, sampler_descriptor));
49 51
50 if constexpr (HAS_DEVICE_MEMORY_INFO) { 52 if constexpr (HAS_DEVICE_MEMORY_INFO) {
51 const auto device_memory = runtime.GetDeviceLocalMemory(); 53 const auto device_memory = runtime.GetDeviceLocalMemory();
52 const u64 possible_expected_memory = (device_memory * 3) / 10; 54 const u64 possible_expected_memory = (device_memory * 4) / 10;
53 const u64 possible_critical_memory = (device_memory * 6) / 10; 55 const u64 possible_critical_memory = (device_memory * 7) / 10;
54 expected_memory = std::max(possible_expected_memory, DEFAULT_EXPECTED_MEMORY); 56 expected_memory = std::max(possible_expected_memory, DEFAULT_EXPECTED_MEMORY - 256_MiB);
55 critical_memory = std::max(possible_critical_memory, DEFAULT_CRITICAL_MEMORY); 57 critical_memory = std::max(possible_critical_memory, DEFAULT_CRITICAL_MEMORY - 512_MiB);
56 minimum_memory = 0; 58 minimum_memory = 0;
57 } else { 59 } else {
58 // on OGL we can be more conservatives as the driver takes care. 60 // On OpenGL we can be more conservatives as the driver takes care.
59 expected_memory = DEFAULT_EXPECTED_MEMORY + 512_MiB; 61 expected_memory = DEFAULT_EXPECTED_MEMORY + 512_MiB;
60 critical_memory = DEFAULT_CRITICAL_MEMORY + 1_GiB; 62 critical_memory = DEFAULT_CRITICAL_MEMORY + 1_GiB;
61 minimum_memory = expected_memory; 63 minimum_memory = 0;
62 } 64 }
63} 65}
64 66
@@ -67,7 +69,7 @@ void TextureCache<P>::RunGarbageCollector() {
67 const bool high_priority_mode = total_used_memory >= expected_memory; 69 const bool high_priority_mode = total_used_memory >= expected_memory;
68 const bool aggressive_mode = total_used_memory >= critical_memory; 70 const bool aggressive_mode = total_used_memory >= critical_memory;
69 const u64 ticks_to_destroy = aggressive_mode ? 10ULL : high_priority_mode ? 25ULL : 100ULL; 71 const u64 ticks_to_destroy = aggressive_mode ? 10ULL : high_priority_mode ? 25ULL : 100ULL;
70 size_t num_iterations = aggressive_mode ? 10000 : (high_priority_mode ? 100 : 5); 72 size_t num_iterations = aggressive_mode ? 300 : (high_priority_mode ? 50 : 10);
71 const auto clean_up = [this, &num_iterations, high_priority_mode](ImageId image_id) { 73 const auto clean_up = [this, &num_iterations, high_priority_mode](ImageId image_id) {
72 if (num_iterations == 0) { 74 if (num_iterations == 0) {
73 return true; 75 return true;
@@ -89,7 +91,7 @@ void TextureCache<P>::RunGarbageCollector() {
89 UntrackImage(image, image_id); 91 UntrackImage(image, image_id);
90 } 92 }
91 UnregisterImage(image_id); 93 UnregisterImage(image_id);
92 DeleteImage(image_id); 94 DeleteImage(image_id, image.scale_tick > frame_tick + 5);
93 return false; 95 return false;
94 }; 96 };
95 lru_cache.ForEachItemBelow(frame_tick - ticks_to_destroy, clean_up); 97 lru_cache.ForEachItemBelow(frame_tick - ticks_to_destroy, clean_up);
@@ -103,6 +105,7 @@ void TextureCache<P>::TickFrame() {
103 sentenced_images.Tick(); 105 sentenced_images.Tick();
104 sentenced_framebuffers.Tick(); 106 sentenced_framebuffers.Tick();
105 sentenced_image_view.Tick(); 107 sentenced_image_view.Tick();
108 runtime.TickFrame();
106 ++frame_tick; 109 ++frame_tick;
107} 110}
108 111
@@ -122,15 +125,14 @@ void TextureCache<P>::MarkModification(ImageId id) noexcept {
122} 125}
123 126
124template <class P> 127template <class P>
125void TextureCache<P>::FillGraphicsImageViews(std::span<const u32> indices, 128template <bool has_blacklists>
126 std::span<ImageViewId> image_view_ids) { 129void TextureCache<P>::FillGraphicsImageViews(std::span<ImageViewInOut> views) {
127 FillImageViews(graphics_image_table, graphics_image_view_ids, indices, image_view_ids); 130 FillImageViews<has_blacklists>(graphics_image_table, graphics_image_view_ids, views);
128} 131}
129 132
130template <class P> 133template <class P>
131void TextureCache<P>::FillComputeImageViews(std::span<const u32> indices, 134void TextureCache<P>::FillComputeImageViews(std::span<ImageViewInOut> views) {
132 std::span<ImageViewId> image_view_ids) { 135 FillImageViews<true>(compute_image_table, compute_image_view_ids, views);
133 FillImageViews(compute_image_table, compute_image_view_ids, indices, image_view_ids);
134} 136}
135 137
136template <class P> 138template <class P>
@@ -190,6 +192,102 @@ void TextureCache<P>::SynchronizeComputeDescriptors() {
190} 192}
191 193
192template <class P> 194template <class P>
195bool TextureCache<P>::RescaleRenderTargets(bool is_clear) {
196 auto& flags = maxwell3d.dirty.flags;
197 u32 scale_rating = 0;
198 bool rescaled = false;
199 std::array<ImageId, NUM_RT> tmp_color_images{};
200 ImageId tmp_depth_image{};
201 do {
202 flags[Dirty::RenderTargets] = false;
203
204 has_deleted_images = false;
205 // Render target control is used on all render targets, so force look ups when this one is
206 // up
207 const bool force = flags[Dirty::RenderTargetControl];
208 flags[Dirty::RenderTargetControl] = false;
209
210 scale_rating = 0;
211 bool any_rescaled = false;
212 bool can_rescale = true;
213 const auto check_rescale = [&](ImageViewId view_id, ImageId& id_save) {
214 if (view_id != NULL_IMAGE_VIEW_ID && view_id != ImageViewId{}) {
215 const auto& view = slot_image_views[view_id];
216 const auto image_id = view.image_id;
217 id_save = image_id;
218 auto& image = slot_images[image_id];
219 can_rescale &= ImageCanRescale(image);
220 any_rescaled |= True(image.flags & ImageFlagBits::Rescaled) ||
221 GetFormatType(image.info.format) != SurfaceType::ColorTexture;
222 scale_rating = std::max<u32>(scale_rating, image.scale_tick <= frame_tick
223 ? image.scale_rating + 1U
224 : image.scale_rating);
225 } else {
226 id_save = CORRUPT_ID;
227 }
228 };
229 for (size_t index = 0; index < NUM_RT; ++index) {
230 ImageViewId& color_buffer_id = render_targets.color_buffer_ids[index];
231 if (flags[Dirty::ColorBuffer0 + index] || force) {
232 flags[Dirty::ColorBuffer0 + index] = false;
233 BindRenderTarget(&color_buffer_id, FindColorBuffer(index, is_clear));
234 }
235 check_rescale(color_buffer_id, tmp_color_images[index]);
236 }
237 if (flags[Dirty::ZetaBuffer] || force) {
238 flags[Dirty::ZetaBuffer] = false;
239 BindRenderTarget(&render_targets.depth_buffer_id, FindDepthBuffer(is_clear));
240 }
241 check_rescale(render_targets.depth_buffer_id, tmp_depth_image);
242
243 if (can_rescale) {
244 rescaled = any_rescaled || scale_rating >= 2;
245 const auto scale_up = [this](ImageId image_id) {
246 if (image_id != CORRUPT_ID) {
247 Image& image = slot_images[image_id];
248 ScaleUp(image);
249 }
250 };
251 if (rescaled) {
252 for (size_t index = 0; index < NUM_RT; ++index) {
253 scale_up(tmp_color_images[index]);
254 }
255 scale_up(tmp_depth_image);
256 scale_rating = 2;
257 }
258 } else {
259 rescaled = false;
260 const auto scale_down = [this](ImageId image_id) {
261 if (image_id != CORRUPT_ID) {
262 Image& image = slot_images[image_id];
263 ScaleDown(image);
264 }
265 };
266 for (size_t index = 0; index < NUM_RT; ++index) {
267 scale_down(tmp_color_images[index]);
268 }
269 scale_down(tmp_depth_image);
270 scale_rating = 1;
271 }
272 } while (has_deleted_images);
273 const auto set_rating = [this, scale_rating](ImageId image_id) {
274 if (image_id != CORRUPT_ID) {
275 Image& image = slot_images[image_id];
276 image.scale_rating = scale_rating;
277 if (image.scale_tick <= frame_tick) {
278 image.scale_tick = frame_tick + 1;
279 }
280 }
281 };
282 for (size_t index = 0; index < NUM_RT; ++index) {
283 set_rating(tmp_color_images[index]);
284 }
285 set_rating(tmp_depth_image);
286
287 return rescaled;
288}
289
290template <class P>
193void TextureCache<P>::UpdateRenderTargets(bool is_clear) { 291void TextureCache<P>::UpdateRenderTargets(bool is_clear) {
194 using namespace VideoCommon::Dirty; 292 using namespace VideoCommon::Dirty;
195 auto& flags = maxwell3d.dirty.flags; 293 auto& flags = maxwell3d.dirty.flags;
@@ -202,24 +300,18 @@ void TextureCache<P>::UpdateRenderTargets(bool is_clear) {
202 PrepareImageView(depth_buffer_id, true, is_clear && IsFullClear(depth_buffer_id)); 300 PrepareImageView(depth_buffer_id, true, is_clear && IsFullClear(depth_buffer_id));
203 return; 301 return;
204 } 302 }
205 flags[Dirty::RenderTargets] = false;
206 303
207 // Render target control is used on all render targets, so force look ups when this one is up 304 const bool rescaled = RescaleRenderTargets(is_clear);
208 const bool force = flags[Dirty::RenderTargetControl]; 305 if (is_rescaling != rescaled) {
209 flags[Dirty::RenderTargetControl] = false; 306 flags[Dirty::RescaleViewports] = true;
307 flags[Dirty::RescaleScissors] = true;
308 is_rescaling = rescaled;
309 }
210 310
211 for (size_t index = 0; index < NUM_RT; ++index) { 311 for (size_t index = 0; index < NUM_RT; ++index) {
212 ImageViewId& color_buffer_id = render_targets.color_buffer_ids[index]; 312 ImageViewId& color_buffer_id = render_targets.color_buffer_ids[index];
213 if (flags[Dirty::ColorBuffer0 + index] || force) {
214 flags[Dirty::ColorBuffer0 + index] = false;
215 BindRenderTarget(&color_buffer_id, FindColorBuffer(index, is_clear));
216 }
217 PrepareImageView(color_buffer_id, true, is_clear && IsFullClear(color_buffer_id)); 313 PrepareImageView(color_buffer_id, true, is_clear && IsFullClear(color_buffer_id));
218 } 314 }
219 if (flags[Dirty::ZetaBuffer] || force) {
220 flags[Dirty::ZetaBuffer] = false;
221 BindRenderTarget(&render_targets.depth_buffer_id, FindDepthBuffer(is_clear));
222 }
223 const ImageViewId depth_buffer_id = render_targets.depth_buffer_id; 315 const ImageViewId depth_buffer_id = render_targets.depth_buffer_id;
224 316
225 PrepareImageView(depth_buffer_id, true, is_clear && IsFullClear(depth_buffer_id)); 317 PrepareImageView(depth_buffer_id, true, is_clear && IsFullClear(depth_buffer_id));
@@ -227,9 +319,15 @@ void TextureCache<P>::UpdateRenderTargets(bool is_clear) {
227 for (size_t index = 0; index < NUM_RT; ++index) { 319 for (size_t index = 0; index < NUM_RT; ++index) {
228 render_targets.draw_buffers[index] = static_cast<u8>(maxwell3d.regs.rt_control.Map(index)); 320 render_targets.draw_buffers[index] = static_cast<u8>(maxwell3d.regs.rt_control.Map(index));
229 } 321 }
322 u32 up_scale = 1;
323 u32 down_shift = 0;
324 if (is_rescaling) {
325 up_scale = Settings::values.resolution_info.up_scale;
326 down_shift = Settings::values.resolution_info.down_shift;
327 }
230 render_targets.size = Extent2D{ 328 render_targets.size = Extent2D{
231 maxwell3d.regs.render_area.width, 329 (maxwell3d.regs.render_area.width * up_scale) >> down_shift,
232 maxwell3d.regs.render_area.height, 330 (maxwell3d.regs.render_area.height * up_scale) >> down_shift,
233 }; 331 };
234 332
235 flags[Dirty::DepthBiasGlobal] = true; 333 flags[Dirty::DepthBiasGlobal] = true;
@@ -241,17 +339,28 @@ typename P::Framebuffer* TextureCache<P>::GetFramebuffer() {
241} 339}
242 340
243template <class P> 341template <class P>
342template <bool has_blacklists>
244void TextureCache<P>::FillImageViews(DescriptorTable<TICEntry>& table, 343void TextureCache<P>::FillImageViews(DescriptorTable<TICEntry>& table,
245 std::span<ImageViewId> cached_image_view_ids, 344 std::span<ImageViewId> cached_image_view_ids,
246 std::span<const u32> indices, 345 std::span<ImageViewInOut> views) {
247 std::span<ImageViewId> image_view_ids) { 346 bool has_blacklisted;
248 ASSERT(indices.size() <= image_view_ids.size());
249 do { 347 do {
250 has_deleted_images = false; 348 has_deleted_images = false;
251 std::ranges::transform(indices, image_view_ids.begin(), [&](u32 index) { 349 if constexpr (has_blacklists) {
252 return VisitImageView(table, cached_image_view_ids, index); 350 has_blacklisted = false;
253 }); 351 }
254 } while (has_deleted_images); 352 for (ImageViewInOut& view : views) {
353 view.id = VisitImageView(table, cached_image_view_ids, view.index);
354 if constexpr (has_blacklists) {
355 if (view.blacklist && view.id != NULL_IMAGE_VIEW_ID) {
356 const ImageViewBase& image_view{slot_image_views[view.id]};
357 auto& image = slot_images[image_view.image_id];
358 has_blacklisted |= ScaleDown(image);
359 image.scale_rating = 0;
360 }
361 }
362 }
363 } while (has_deleted_images || (has_blacklists && has_blacklisted));
255} 364}
256 365
257template <class P> 366template <class P>
@@ -369,8 +478,43 @@ void TextureCache<P>::BlitImage(const Tegra::Engines::Fermi2D::Surface& dst,
369 PrepareImage(src_id, false, false); 478 PrepareImage(src_id, false, false);
370 PrepareImage(dst_id, true, false); 479 PrepareImage(dst_id, true, false);
371 480
372 ImageBase& dst_image = slot_images[dst_id]; 481 Image& dst_image = slot_images[dst_id];
373 const ImageBase& src_image = slot_images[src_id]; 482 Image& src_image = slot_images[src_id];
483 bool is_src_rescaled = True(src_image.flags & ImageFlagBits::Rescaled);
484 bool is_dst_rescaled = True(dst_image.flags & ImageFlagBits::Rescaled);
485
486 const bool is_resolve = src_image.info.num_samples != 1 && dst_image.info.num_samples == 1;
487 if (is_src_rescaled != is_dst_rescaled) {
488 if (ImageCanRescale(src_image)) {
489 ScaleUp(src_image);
490 is_src_rescaled = True(src_image.flags & ImageFlagBits::Rescaled);
491 if (is_resolve) {
492 dst_image.info.rescaleable = true;
493 for (const auto& alias : dst_image.aliased_images) {
494 Image& other_image = slot_images[alias.id];
495 other_image.info.rescaleable = true;
496 }
497 }
498 }
499 if (ImageCanRescale(dst_image)) {
500 ScaleUp(dst_image);
501 is_dst_rescaled = True(dst_image.flags & ImageFlagBits::Rescaled);
502 }
503 }
504 if (is_resolve && (is_src_rescaled != is_dst_rescaled)) {
505 // A resolve requires both images to be the same dimensions. Resize down if needed.
506 ScaleDown(src_image);
507 ScaleDown(dst_image);
508 is_src_rescaled = True(src_image.flags & ImageFlagBits::Rescaled);
509 is_dst_rescaled = True(dst_image.flags & ImageFlagBits::Rescaled);
510 }
511 const auto& resolution = Settings::values.resolution_info;
512 const auto scale_region = [&](Region2D& region) {
513 region.start.x = resolution.ScaleUp(region.start.x);
514 region.start.y = resolution.ScaleUp(region.start.y);
515 region.end.x = resolution.ScaleUp(region.end.x);
516 region.end.y = resolution.ScaleUp(region.end.y);
517 };
374 518
375 // TODO: Deduplicate 519 // TODO: Deduplicate
376 const std::optional src_base = src_image.TryFindBase(src.Address()); 520 const std::optional src_base = src_image.TryFindBase(src.Address());
@@ -378,20 +522,26 @@ void TextureCache<P>::BlitImage(const Tegra::Engines::Fermi2D::Surface& dst,
378 const ImageViewInfo src_view_info(ImageViewType::e2D, images.src_format, src_range); 522 const ImageViewInfo src_view_info(ImageViewType::e2D, images.src_format, src_range);
379 const auto [src_framebuffer_id, src_view_id] = RenderTargetFromImage(src_id, src_view_info); 523 const auto [src_framebuffer_id, src_view_id] = RenderTargetFromImage(src_id, src_view_info);
380 const auto [src_samples_x, src_samples_y] = SamplesLog2(src_image.info.num_samples); 524 const auto [src_samples_x, src_samples_y] = SamplesLog2(src_image.info.num_samples);
381 const Region2D src_region{ 525 Region2D src_region{
382 Offset2D{.x = copy.src_x0 >> src_samples_x, .y = copy.src_y0 >> src_samples_y}, 526 Offset2D{.x = copy.src_x0 >> src_samples_x, .y = copy.src_y0 >> src_samples_y},
383 Offset2D{.x = copy.src_x1 >> src_samples_x, .y = copy.src_y1 >> src_samples_y}, 527 Offset2D{.x = copy.src_x1 >> src_samples_x, .y = copy.src_y1 >> src_samples_y},
384 }; 528 };
529 if (is_src_rescaled) {
530 scale_region(src_region);
531 }
385 532
386 const std::optional dst_base = dst_image.TryFindBase(dst.Address()); 533 const std::optional dst_base = dst_image.TryFindBase(dst.Address());
387 const SubresourceRange dst_range{.base = dst_base.value(), .extent = {1, 1}}; 534 const SubresourceRange dst_range{.base = dst_base.value(), .extent = {1, 1}};
388 const ImageViewInfo dst_view_info(ImageViewType::e2D, images.dst_format, dst_range); 535 const ImageViewInfo dst_view_info(ImageViewType::e2D, images.dst_format, dst_range);
389 const auto [dst_framebuffer_id, dst_view_id] = RenderTargetFromImage(dst_id, dst_view_info); 536 const auto [dst_framebuffer_id, dst_view_id] = RenderTargetFromImage(dst_id, dst_view_info);
390 const auto [dst_samples_x, dst_samples_y] = SamplesLog2(dst_image.info.num_samples); 537 const auto [dst_samples_x, dst_samples_y] = SamplesLog2(dst_image.info.num_samples);
391 const Region2D dst_region{ 538 Region2D dst_region{
392 Offset2D{.x = copy.dst_x0 >> dst_samples_x, .y = copy.dst_y0 >> dst_samples_y}, 539 Offset2D{.x = copy.dst_x0 >> dst_samples_x, .y = copy.dst_y0 >> dst_samples_y},
393 Offset2D{.x = copy.dst_x1 >> dst_samples_x, .y = copy.dst_y1 >> dst_samples_y}, 540 Offset2D{.x = copy.dst_x1 >> dst_samples_x, .y = copy.dst_y1 >> dst_samples_y},
394 }; 541 };
542 if (is_dst_rescaled) {
543 scale_region(dst_region);
544 }
395 545
396 // Always call this after src_framebuffer_id was queried, as the address might be invalidated. 546 // Always call this after src_framebuffer_id was queried, as the address might be invalidated.
397 Framebuffer* const dst_framebuffer = &slot_framebuffers[dst_framebuffer_id]; 547 Framebuffer* const dst_framebuffer = &slot_framebuffers[dst_framebuffer_id];
@@ -487,6 +637,20 @@ void TextureCache<P>::PopAsyncFlushes() {
487} 637}
488 638
489template <class P> 639template <class P>
640bool TextureCache<P>::IsRescaling() const noexcept {
641 return is_rescaling;
642}
643
644template <class P>
645bool TextureCache<P>::IsRescaling(const ImageViewBase& image_view) const noexcept {
646 if (image_view.type == ImageViewType::Buffer) {
647 return false;
648 }
649 const ImageBase& image = slot_images[image_view.image_id];
650 return True(image.flags & ImageFlagBits::Rescaled);
651}
652
653template <class P>
490bool TextureCache<P>::IsRegionGpuModified(VAddr addr, size_t size) { 654bool TextureCache<P>::IsRegionGpuModified(VAddr addr, size_t size) {
491 bool is_modified = false; 655 bool is_modified = false;
492 ForEachImageInRegion(addr, size, [&is_modified](ImageId, ImageBase& image) { 656 ForEachImageInRegion(addr, size, [&is_modified](ImageId, ImageBase& image) {
@@ -624,6 +788,105 @@ ImageId TextureCache<P>::FindImage(const ImageInfo& info, GPUVAddr gpu_addr,
624} 788}
625 789
626template <class P> 790template <class P>
791bool TextureCache<P>::ImageCanRescale(ImageBase& image) {
792 if (!image.info.rescaleable) {
793 return false;
794 }
795 if (Settings::values.resolution_info.downscale && !image.info.downscaleable) {
796 return false;
797 }
798 if (True(image.flags & (ImageFlagBits::Rescaled | ImageFlagBits::CheckingRescalable))) {
799 return true;
800 }
801 if (True(image.flags & ImageFlagBits::IsRescalable)) {
802 return true;
803 }
804 image.flags |= ImageFlagBits::CheckingRescalable;
805 for (const auto& alias : image.aliased_images) {
806 Image& other_image = slot_images[alias.id];
807 if (!ImageCanRescale(other_image)) {
808 image.flags &= ~ImageFlagBits::CheckingRescalable;
809 return false;
810 }
811 }
812 image.flags &= ~ImageFlagBits::CheckingRescalable;
813 image.flags |= ImageFlagBits::IsRescalable;
814 return true;
815}
816
817template <class P>
818void TextureCache<P>::InvalidateScale(Image& image) {
819 if (image.scale_tick <= frame_tick) {
820 image.scale_tick = frame_tick + 1;
821 }
822 const std::span<const ImageViewId> image_view_ids = image.image_view_ids;
823 auto& dirty = maxwell3d.dirty.flags;
824 dirty[Dirty::RenderTargets] = true;
825 dirty[Dirty::ZetaBuffer] = true;
826 for (size_t rt = 0; rt < NUM_RT; ++rt) {
827 dirty[Dirty::ColorBuffer0 + rt] = true;
828 }
829 for (const ImageViewId image_view_id : image_view_ids) {
830 std::ranges::replace(render_targets.color_buffer_ids, image_view_id, ImageViewId{});
831 if (render_targets.depth_buffer_id == image_view_id) {
832 render_targets.depth_buffer_id = ImageViewId{};
833 }
834 }
835 RemoveImageViewReferences(image_view_ids);
836 RemoveFramebuffers(image_view_ids);
837 for (const ImageViewId image_view_id : image_view_ids) {
838 sentenced_image_view.Push(std::move(slot_image_views[image_view_id]));
839 slot_image_views.erase(image_view_id);
840 }
841 image.image_view_ids.clear();
842 image.image_view_infos.clear();
843 if constexpr (ENABLE_VALIDATION) {
844 std::ranges::fill(graphics_image_view_ids, CORRUPT_ID);
845 std::ranges::fill(compute_image_view_ids, CORRUPT_ID);
846 }
847 graphics_image_table.Invalidate();
848 compute_image_table.Invalidate();
849 has_deleted_images = true;
850}
851
852template <class P>
853u64 TextureCache<P>::GetScaledImageSizeBytes(ImageBase& image) {
854 const u64 scale_up = static_cast<u64>(Settings::values.resolution_info.up_scale *
855 Settings::values.resolution_info.up_scale);
856 const u64 down_shift = static_cast<u64>(Settings::values.resolution_info.down_shift +
857 Settings::values.resolution_info.down_shift);
858 const u64 image_size_bytes =
859 static_cast<u64>(std::max(image.guest_size_bytes, image.unswizzled_size_bytes));
860 const u64 tentative_size = (image_size_bytes * scale_up) >> down_shift;
861 const u64 fitted_size = Common::AlignUp(tentative_size, 1024);
862 return fitted_size;
863}
864
865template <class P>
866bool TextureCache<P>::ScaleUp(Image& image) {
867 const bool has_copy = image.HasScaled();
868 const bool rescaled = image.ScaleUp();
869 if (!rescaled) {
870 return false;
871 }
872 if (!has_copy) {
873 total_used_memory += GetScaledImageSizeBytes(image);
874 }
875 InvalidateScale(image);
876 return true;
877}
878
879template <class P>
880bool TextureCache<P>::ScaleDown(Image& image) {
881 const bool rescaled = image.ScaleDown();
882 if (!rescaled) {
883 return false;
884 }
885 InvalidateScale(image);
886 return true;
887}
888
889template <class P>
627ImageId TextureCache<P>::InsertImage(const ImageInfo& info, GPUVAddr gpu_addr, 890ImageId TextureCache<P>::InsertImage(const ImageInfo& info, GPUVAddr gpu_addr,
628 RelaxedOptions options) { 891 RelaxedOptions options) {
629 std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr); 892 std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr);
@@ -660,12 +923,18 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA
660 std::vector<ImageId> right_aliased_ids; 923 std::vector<ImageId> right_aliased_ids;
661 std::unordered_set<ImageId> ignore_textures; 924 std::unordered_set<ImageId> ignore_textures;
662 std::vector<ImageId> bad_overlap_ids; 925 std::vector<ImageId> bad_overlap_ids;
926 std::vector<ImageId> all_siblings;
927 const bool this_is_linear = info.type == ImageType::Linear;
663 const auto region_check = [&](ImageId overlap_id, ImageBase& overlap) { 928 const auto region_check = [&](ImageId overlap_id, ImageBase& overlap) {
664 if (True(overlap.flags & ImageFlagBits::Remapped)) { 929 if (True(overlap.flags & ImageFlagBits::Remapped)) {
665 ignore_textures.insert(overlap_id); 930 ignore_textures.insert(overlap_id);
666 return; 931 return;
667 } 932 }
668 if (info.type == ImageType::Linear) { 933 const bool overlap_is_linear = overlap.info.type == ImageType::Linear;
934 if (this_is_linear != overlap_is_linear) {
935 return;
936 }
937 if (this_is_linear && overlap_is_linear) {
669 if (info.pitch == overlap.info.pitch && gpu_addr == overlap.gpu_addr) { 938 if (info.pitch == overlap.info.pitch && gpu_addr == overlap.gpu_addr) {
670 // Alias linear images with the same pitch 939 // Alias linear images with the same pitch
671 left_aliased_ids.push_back(overlap_id); 940 left_aliased_ids.push_back(overlap_id);
@@ -681,6 +950,7 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA
681 cpu_addr = solution->cpu_addr; 950 cpu_addr = solution->cpu_addr;
682 new_info.resources = solution->resources; 951 new_info.resources = solution->resources;
683 overlap_ids.push_back(overlap_id); 952 overlap_ids.push_back(overlap_id);
953 all_siblings.push_back(overlap_id);
684 return; 954 return;
685 } 955 }
686 static constexpr auto options = RelaxedOptions::Size | RelaxedOptions::Format; 956 static constexpr auto options = RelaxedOptions::Size | RelaxedOptions::Format;
@@ -688,10 +958,12 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA
688 if (IsSubresource(new_info, overlap, gpu_addr, options, broken_views, native_bgr)) { 958 if (IsSubresource(new_info, overlap, gpu_addr, options, broken_views, native_bgr)) {
689 left_aliased_ids.push_back(overlap_id); 959 left_aliased_ids.push_back(overlap_id);
690 overlap.flags |= ImageFlagBits::Alias; 960 overlap.flags |= ImageFlagBits::Alias;
961 all_siblings.push_back(overlap_id);
691 } else if (IsSubresource(overlap.info, new_image_base, overlap.gpu_addr, options, 962 } else if (IsSubresource(overlap.info, new_image_base, overlap.gpu_addr, options,
692 broken_views, native_bgr)) { 963 broken_views, native_bgr)) {
693 right_aliased_ids.push_back(overlap_id); 964 right_aliased_ids.push_back(overlap_id);
694 overlap.flags |= ImageFlagBits::Alias; 965 overlap.flags |= ImageFlagBits::Alias;
966 all_siblings.push_back(overlap_id);
695 } else { 967 } else {
696 bad_overlap_ids.push_back(overlap_id); 968 bad_overlap_ids.push_back(overlap_id);
697 overlap.flags |= ImageFlagBits::BadOverlap; 969 overlap.flags |= ImageFlagBits::BadOverlap;
@@ -709,6 +981,32 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA
709 } 981 }
710 }; 982 };
711 ForEachSparseImageInRegion(gpu_addr, size_bytes, region_check_gpu); 983 ForEachSparseImageInRegion(gpu_addr, size_bytes, region_check_gpu);
984
985 bool can_rescale = info.rescaleable;
986 bool any_rescaled = false;
987 for (const ImageId sibling_id : all_siblings) {
988 if (!can_rescale) {
989 break;
990 }
991 Image& sibling = slot_images[sibling_id];
992 can_rescale &= ImageCanRescale(sibling);
993 any_rescaled |= True(sibling.flags & ImageFlagBits::Rescaled);
994 }
995
996 can_rescale &= any_rescaled;
997
998 if (can_rescale) {
999 for (const ImageId sibling_id : all_siblings) {
1000 Image& sibling = slot_images[sibling_id];
1001 ScaleUp(sibling);
1002 }
1003 } else {
1004 for (const ImageId sibling_id : all_siblings) {
1005 Image& sibling = slot_images[sibling_id];
1006 ScaleDown(sibling);
1007 }
1008 }
1009
712 const ImageId new_image_id = slot_images.insert(runtime, new_info, gpu_addr, cpu_addr); 1010 const ImageId new_image_id = slot_images.insert(runtime, new_info, gpu_addr, cpu_addr);
713 Image& new_image = slot_images[new_image_id]; 1011 Image& new_image = slot_images[new_image_id];
714 1012
@@ -731,14 +1029,23 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA
731 // TODO: Only upload what we need 1029 // TODO: Only upload what we need
732 RefreshContents(new_image, new_image_id); 1030 RefreshContents(new_image, new_image_id);
733 1031
1032 if (can_rescale) {
1033 ScaleUp(new_image);
1034 } else {
1035 ScaleDown(new_image);
1036 }
1037
734 for (const ImageId overlap_id : overlap_ids) { 1038 for (const ImageId overlap_id : overlap_ids) {
735 Image& overlap = slot_images[overlap_id]; 1039 Image& overlap = slot_images[overlap_id];
736 if (overlap.info.num_samples != new_image.info.num_samples) { 1040 if (overlap.info.num_samples != new_image.info.num_samples) {
737 LOG_WARNING(HW_GPU, "Copying between images with different samples is not implemented"); 1041 LOG_WARNING(HW_GPU, "Copying between images with different samples is not implemented");
738 } else { 1042 } else {
1043 const auto& resolution = Settings::values.resolution_info;
739 const SubresourceBase base = new_image.TryFindBase(overlap.gpu_addr).value(); 1044 const SubresourceBase base = new_image.TryFindBase(overlap.gpu_addr).value();
740 const auto copies = MakeShrinkImageCopies(new_info, overlap.info, base); 1045 const u32 up_scale = can_rescale ? resolution.up_scale : 1;
741 runtime.CopyImage(new_image, overlap, copies); 1046 const u32 down_shift = can_rescale ? resolution.down_shift : 0;
1047 auto copies = MakeShrinkImageCopies(new_info, overlap.info, base, up_scale, down_shift);
1048 runtime.CopyImage(new_image, overlap, std::move(copies));
742 } 1049 }
743 if (True(overlap.flags & ImageFlagBits::Tracked)) { 1050 if (True(overlap.flags & ImageFlagBits::Tracked)) {
744 UntrackImage(overlap, overlap_id); 1051 UntrackImage(overlap, overlap_id);
@@ -1083,13 +1390,6 @@ void TextureCache<P>::UnregisterImage(ImageId image_id) {
1083 "Trying to unregister an already registered image"); 1390 "Trying to unregister an already registered image");
1084 image.flags &= ~ImageFlagBits::Registered; 1391 image.flags &= ~ImageFlagBits::Registered;
1085 image.flags &= ~ImageFlagBits::BadOverlap; 1392 image.flags &= ~ImageFlagBits::BadOverlap;
1086 u64 tentative_size = std::max(image.guest_size_bytes, image.unswizzled_size_bytes);
1087 if ((IsPixelFormatASTC(image.info.format) &&
1088 True(image.flags & ImageFlagBits::AcceleratedUpload)) ||
1089 True(image.flags & ImageFlagBits::Converted)) {
1090 tentative_size = EstimatedDecompressedSize(tentative_size, image.info.format);
1091 }
1092 total_used_memory -= Common::AlignUp(tentative_size, 1024);
1093 lru_cache.Free(image.lru_index); 1393 lru_cache.Free(image.lru_index);
1094 const auto& clear_page_table = 1394 const auto& clear_page_table =
1095 [this, image_id]( 1395 [this, image_id](
@@ -1213,8 +1513,18 @@ void TextureCache<P>::UntrackImage(ImageBase& image, ImageId image_id) {
1213} 1513}
1214 1514
1215template <class P> 1515template <class P>
1216void TextureCache<P>::DeleteImage(ImageId image_id) { 1516void TextureCache<P>::DeleteImage(ImageId image_id, bool immediate_delete) {
1217 ImageBase& image = slot_images[image_id]; 1517 ImageBase& image = slot_images[image_id];
1518 if (image.HasScaled()) {
1519 total_used_memory -= GetScaledImageSizeBytes(image);
1520 }
1521 u64 tentative_size = std::max(image.guest_size_bytes, image.unswizzled_size_bytes);
1522 if ((IsPixelFormatASTC(image.info.format) &&
1523 True(image.flags & ImageFlagBits::AcceleratedUpload)) ||
1524 True(image.flags & ImageFlagBits::Converted)) {
1525 tentative_size = EstimatedDecompressedSize(tentative_size, image.info.format);
1526 }
1527 total_used_memory -= Common::AlignUp(tentative_size, 1024);
1218 const GPUVAddr gpu_addr = image.gpu_addr; 1528 const GPUVAddr gpu_addr = image.gpu_addr;
1219 const auto alloc_it = image_allocs_table.find(gpu_addr); 1529 const auto alloc_it = image_allocs_table.find(gpu_addr);
1220 if (alloc_it == image_allocs_table.end()) { 1530 if (alloc_it == image_allocs_table.end()) {
@@ -1269,10 +1579,14 @@ void TextureCache<P>::DeleteImage(ImageId image_id) {
1269 num_removed_overlaps); 1579 num_removed_overlaps);
1270 } 1580 }
1271 for (const ImageViewId image_view_id : image_view_ids) { 1581 for (const ImageViewId image_view_id : image_view_ids) {
1272 sentenced_image_view.Push(std::move(slot_image_views[image_view_id])); 1582 if (!immediate_delete) {
1583 sentenced_image_view.Push(std::move(slot_image_views[image_view_id]));
1584 }
1273 slot_image_views.erase(image_view_id); 1585 slot_image_views.erase(image_view_id);
1274 } 1586 }
1275 sentenced_images.Push(std::move(slot_images[image_id])); 1587 if (!immediate_delete) {
1588 sentenced_images.Push(std::move(slot_images[image_id]));
1589 }
1276 slot_images.erase(image_id); 1590 slot_images.erase(image_id);
1277 1591
1278 alloc_images.erase(alloc_image_it); 1592 alloc_images.erase(alloc_image_it);
@@ -1306,6 +1620,9 @@ void TextureCache<P>::RemoveFramebuffers(std::span<const ImageViewId> removed_vi
1306 auto it = framebuffers.begin(); 1620 auto it = framebuffers.begin();
1307 while (it != framebuffers.end()) { 1621 while (it != framebuffers.end()) {
1308 if (it->first.Contains(removed_views)) { 1622 if (it->first.Contains(removed_views)) {
1623 auto framebuffer_id = it->second;
1624 ASSERT(framebuffer_id);
1625 sentenced_framebuffers.Push(std::move(slot_framebuffers[framebuffer_id]));
1309 it = framebuffers.erase(it); 1626 it = framebuffers.erase(it);
1310 } else { 1627 } else {
1311 ++it; 1628 ++it;
@@ -1322,26 +1639,60 @@ void TextureCache<P>::MarkModification(ImageBase& image) noexcept {
1322template <class P> 1639template <class P>
1323void TextureCache<P>::SynchronizeAliases(ImageId image_id) { 1640void TextureCache<P>::SynchronizeAliases(ImageId image_id) {
1324 boost::container::small_vector<const AliasedImage*, 1> aliased_images; 1641 boost::container::small_vector<const AliasedImage*, 1> aliased_images;
1325 ImageBase& image = slot_images[image_id]; 1642 Image& image = slot_images[image_id];
1643 bool any_rescaled = True(image.flags & ImageFlagBits::Rescaled);
1326 u64 most_recent_tick = image.modification_tick; 1644 u64 most_recent_tick = image.modification_tick;
1327 for (const AliasedImage& aliased : image.aliased_images) { 1645 for (const AliasedImage& aliased : image.aliased_images) {
1328 ImageBase& aliased_image = slot_images[aliased.id]; 1646 ImageBase& aliased_image = slot_images[aliased.id];
1329 if (image.modification_tick < aliased_image.modification_tick) { 1647 if (image.modification_tick < aliased_image.modification_tick) {
1330 most_recent_tick = std::max(most_recent_tick, aliased_image.modification_tick); 1648 most_recent_tick = std::max(most_recent_tick, aliased_image.modification_tick);
1331 aliased_images.push_back(&aliased); 1649 aliased_images.push_back(&aliased);
1650 any_rescaled |= True(aliased_image.flags & ImageFlagBits::Rescaled);
1332 } 1651 }
1333 } 1652 }
1334 if (aliased_images.empty()) { 1653 if (aliased_images.empty()) {
1335 return; 1654 return;
1336 } 1655 }
1656 const bool can_rescale = ImageCanRescale(image);
1657 if (any_rescaled) {
1658 if (can_rescale) {
1659 ScaleUp(image);
1660 } else {
1661 ScaleDown(image);
1662 }
1663 }
1337 image.modification_tick = most_recent_tick; 1664 image.modification_tick = most_recent_tick;
1338 std::ranges::sort(aliased_images, [this](const AliasedImage* lhs, const AliasedImage* rhs) { 1665 std::ranges::sort(aliased_images, [this](const AliasedImage* lhs, const AliasedImage* rhs) {
1339 const ImageBase& lhs_image = slot_images[lhs->id]; 1666 const ImageBase& lhs_image = slot_images[lhs->id];
1340 const ImageBase& rhs_image = slot_images[rhs->id]; 1667 const ImageBase& rhs_image = slot_images[rhs->id];
1341 return lhs_image.modification_tick < rhs_image.modification_tick; 1668 return lhs_image.modification_tick < rhs_image.modification_tick;
1342 }); 1669 });
1670 const auto& resolution = Settings::values.resolution_info;
1343 for (const AliasedImage* const aliased : aliased_images) { 1671 for (const AliasedImage* const aliased : aliased_images) {
1344 CopyImage(image_id, aliased->id, aliased->copies); 1672 if (!resolution.active | !any_rescaled) {
1673 CopyImage(image_id, aliased->id, aliased->copies);
1674 continue;
1675 }
1676 Image& aliased_image = slot_images[aliased->id];
1677 if (!can_rescale) {
1678 ScaleDown(aliased_image);
1679 CopyImage(image_id, aliased->id, aliased->copies);
1680 continue;
1681 }
1682 ScaleUp(aliased_image);
1683
1684 const bool both_2d{image.info.type == ImageType::e2D &&
1685 aliased_image.info.type == ImageType::e2D};
1686 auto copies = aliased->copies;
1687 for (auto copy : copies) {
1688 copy.extent.width = std::max<u32>(
1689 (copy.extent.width * resolution.up_scale) >> resolution.down_shift, 1);
1690 if (both_2d) {
1691 copy.extent.height = std::max<u32>(
1692 (copy.extent.height * resolution.up_scale) >> resolution.down_shift, 1);
1693 }
1694 }
1695 CopyImage(image_id, aliased->id, copies);
1345 } 1696 }
1346} 1697}
1347 1698
@@ -1377,9 +1728,25 @@ void TextureCache<P>::PrepareImageView(ImageViewId image_view_id, bool is_modifi
1377} 1728}
1378 1729
1379template <class P> 1730template <class P>
1380void TextureCache<P>::CopyImage(ImageId dst_id, ImageId src_id, std::span<const ImageCopy> copies) { 1731void TextureCache<P>::CopyImage(ImageId dst_id, ImageId src_id, std::vector<ImageCopy> copies) {
1381 Image& dst = slot_images[dst_id]; 1732 Image& dst = slot_images[dst_id];
1382 Image& src = slot_images[src_id]; 1733 Image& src = slot_images[src_id];
1734 const bool is_rescaled = True(src.flags & ImageFlagBits::Rescaled);
1735 if (is_rescaled) {
1736 ASSERT(True(dst.flags & ImageFlagBits::Rescaled));
1737 const bool both_2d{src.info.type == ImageType::e2D && dst.info.type == ImageType::e2D};
1738 const auto& resolution = Settings::values.resolution_info;
1739 for (auto& copy : copies) {
1740 copy.src_offset.x = resolution.ScaleUp(copy.src_offset.x);
1741 copy.dst_offset.x = resolution.ScaleUp(copy.dst_offset.x);
1742 copy.extent.width = resolution.ScaleUp(copy.extent.width);
1743 if (both_2d) {
1744 copy.src_offset.y = resolution.ScaleUp(copy.src_offset.y);
1745 copy.dst_offset.y = resolution.ScaleUp(copy.dst_offset.y);
1746 copy.extent.height = resolution.ScaleUp(copy.extent.height);
1747 }
1748 }
1749 }
1383 const auto dst_format_type = GetFormatType(dst.info.format); 1750 const auto dst_format_type = GetFormatType(dst.info.format);
1384 const auto src_format_type = GetFormatType(src.info.format); 1751 const auto src_format_type = GetFormatType(src.info.format);
1385 if (src_format_type == dst_format_type) { 1752 if (src_format_type == dst_format_type) {
@@ -1424,7 +1791,7 @@ void TextureCache<P>::CopyImage(ImageId dst_id, ImageId src_id, std::span<const
1424 }; 1791 };
1425 UNIMPLEMENTED_IF(copy.extent != expected_size); 1792 UNIMPLEMENTED_IF(copy.extent != expected_size);
1426 1793
1427 runtime.ConvertImage(dst_framebuffer, dst_view, src_view); 1794 runtime.ConvertImage(dst_framebuffer, dst_view, src_view, is_rescaled);
1428 } 1795 }
1429} 1796}
1430 1797
@@ -1433,8 +1800,8 @@ void TextureCache<P>::BindRenderTarget(ImageViewId* old_id, ImageViewId new_id)
1433 if (*old_id == new_id) { 1800 if (*old_id == new_id) {
1434 return; 1801 return;
1435 } 1802 }
1436 if (*old_id) { 1803 if (new_id) {
1437 const ImageViewBase& old_view = slot_image_views[*old_id]; 1804 const ImageViewBase& old_view = slot_image_views[new_id];
1438 if (True(old_view.flags & ImageViewFlagBits::PreemtiveDownload)) { 1805 if (True(old_view.flags & ImageViewFlagBits::PreemtiveDownload)) {
1439 uncommitted_downloads.push_back(old_view.image_id); 1806 uncommitted_downloads.push_back(old_view.image_id);
1440 } 1807 }
@@ -1447,10 +1814,18 @@ std::pair<FramebufferId, ImageViewId> TextureCache<P>::RenderTargetFromImage(
1447 ImageId image_id, const ImageViewInfo& view_info) { 1814 ImageId image_id, const ImageViewInfo& view_info) {
1448 const ImageViewId view_id = FindOrEmplaceImageView(image_id, view_info); 1815 const ImageViewId view_id = FindOrEmplaceImageView(image_id, view_info);
1449 const ImageBase& image = slot_images[image_id]; 1816 const ImageBase& image = slot_images[image_id];
1817 const bool is_rescaled = True(image.flags & ImageFlagBits::Rescaled);
1450 const bool is_color = GetFormatType(image.info.format) == SurfaceType::ColorTexture; 1818 const bool is_color = GetFormatType(image.info.format) == SurfaceType::ColorTexture;
1451 const ImageViewId color_view_id = is_color ? view_id : ImageViewId{}; 1819 const ImageViewId color_view_id = is_color ? view_id : ImageViewId{};
1452 const ImageViewId depth_view_id = is_color ? ImageViewId{} : view_id; 1820 const ImageViewId depth_view_id = is_color ? ImageViewId{} : view_id;
1453 const Extent3D extent = MipSize(image.info.size, view_info.range.base.level); 1821 Extent3D extent = MipSize(image.info.size, view_info.range.base.level);
1822 if (is_rescaled) {
1823 const auto& resolution = Settings::values.resolution_info;
1824 extent.width = resolution.ScaleUp(extent.width);
1825 if (image.info.type == ImageType::e2D) {
1826 extent.height = resolution.ScaleUp(extent.height);
1827 }
1828 }
1454 const u32 num_samples = image.info.num_samples; 1829 const u32 num_samples = image.info.num_samples;
1455 const auto [samples_x, samples_y] = SamplesLog2(num_samples); 1830 const auto [samples_x, samples_y] = SamplesLog2(num_samples);
1456 const FramebufferId framebuffer_id = GetFramebufferId(RenderTargets{ 1831 const FramebufferId framebuffer_id = GetFramebufferId(RenderTargets{
diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h
index 2d1893c1c..643ad811c 100644
--- a/src/video_core/texture_cache/texture_cache_base.h
+++ b/src/video_core/texture_cache/texture_cache_base.h
@@ -21,6 +21,7 @@
21#include "video_core/texture_cache/descriptor_table.h" 21#include "video_core/texture_cache/descriptor_table.h"
22#include "video_core/texture_cache/image_base.h" 22#include "video_core/texture_cache/image_base.h"
23#include "video_core/texture_cache/image_info.h" 23#include "video_core/texture_cache/image_info.h"
24#include "video_core/texture_cache/image_view_base.h"
24#include "video_core/texture_cache/image_view_info.h" 25#include "video_core/texture_cache/image_view_info.h"
25#include "video_core/texture_cache/render_targets.h" 26#include "video_core/texture_cache/render_targets.h"
26#include "video_core/texture_cache/slot_vector.h" 27#include "video_core/texture_cache/slot_vector.h"
@@ -39,6 +40,12 @@ using VideoCore::Surface::PixelFormatFromDepthFormat;
39using VideoCore::Surface::PixelFormatFromRenderTargetFormat; 40using VideoCore::Surface::PixelFormatFromRenderTargetFormat;
40using namespace Common::Literals; 41using namespace Common::Literals;
41 42
43struct ImageViewInOut {
44 u32 index{};
45 bool blacklist{};
46 ImageViewId id{};
47};
48
42template <class P> 49template <class P>
43class TextureCache { 50class TextureCache {
44 /// Address shift for caching images into a hash table 51 /// Address shift for caching images into a hash table
@@ -53,11 +60,6 @@ class TextureCache {
53 /// True when the API can provide info about the memory of the device. 60 /// True when the API can provide info about the memory of the device.
54 static constexpr bool HAS_DEVICE_MEMORY_INFO = P::HAS_DEVICE_MEMORY_INFO; 61 static constexpr bool HAS_DEVICE_MEMORY_INFO = P::HAS_DEVICE_MEMORY_INFO;
55 62
56 /// Image view ID for null descriptors
57 static constexpr ImageViewId NULL_IMAGE_VIEW_ID{0};
58 /// Sampler ID for bugged sampler ids
59 static constexpr SamplerId NULL_SAMPLER_ID{0};
60
61 static constexpr u64 DEFAULT_EXPECTED_MEMORY = 1_GiB; 63 static constexpr u64 DEFAULT_EXPECTED_MEMORY = 1_GiB;
62 static constexpr u64 DEFAULT_CRITICAL_MEMORY = 2_GiB; 64 static constexpr u64 DEFAULT_CRITICAL_MEMORY = 2_GiB;
63 65
@@ -99,11 +101,11 @@ public:
99 void MarkModification(ImageId id) noexcept; 101 void MarkModification(ImageId id) noexcept;
100 102
101 /// Fill image_view_ids with the graphics images in indices 103 /// Fill image_view_ids with the graphics images in indices
102 void FillGraphicsImageViews(std::span<const u32> indices, 104 template <bool has_blacklists>
103 std::span<ImageViewId> image_view_ids); 105 void FillGraphicsImageViews(std::span<ImageViewInOut> views);
104 106
105 /// Fill image_view_ids with the compute images in indices 107 /// Fill image_view_ids with the compute images in indices
106 void FillComputeImageViews(std::span<const u32> indices, std::span<ImageViewId> image_view_ids); 108 void FillComputeImageViews(std::span<ImageViewInOut> views);
107 109
108 /// Get the sampler from the graphics descriptor table in the specified index 110 /// Get the sampler from the graphics descriptor table in the specified index
109 Sampler* GetGraphicsSampler(u32 index); 111 Sampler* GetGraphicsSampler(u32 index);
@@ -117,6 +119,11 @@ public:
117 /// Refresh the state for compute image view and sampler descriptors 119 /// Refresh the state for compute image view and sampler descriptors
118 void SynchronizeComputeDescriptors(); 120 void SynchronizeComputeDescriptors();
119 121
122 /// Updates the Render Targets if they can be rescaled
123 /// @param is_clear True when the render targets are being used for clears
124 /// @retval True if the Render Targets have been rescaled.
125 bool RescaleRenderTargets(bool is_clear);
126
120 /// Update bound render targets and upload memory if necessary 127 /// Update bound render targets and upload memory if necessary
121 /// @param is_clear True when the render targets are being used for clears 128 /// @param is_clear True when the render targets are being used for clears
122 void UpdateRenderTargets(bool is_clear); 129 void UpdateRenderTargets(bool is_clear);
@@ -160,6 +167,10 @@ public:
160 /// Return true when a CPU region is modified from the GPU 167 /// Return true when a CPU region is modified from the GPU
161 [[nodiscard]] bool IsRegionGpuModified(VAddr addr, size_t size); 168 [[nodiscard]] bool IsRegionGpuModified(VAddr addr, size_t size);
162 169
170 [[nodiscard]] bool IsRescaling() const noexcept;
171
172 [[nodiscard]] bool IsRescaling(const ImageViewBase& image_view) const noexcept;
173
163 std::mutex mutex; 174 std::mutex mutex;
164 175
165private: 176private:
@@ -198,9 +209,10 @@ private:
198 void RunGarbageCollector(); 209 void RunGarbageCollector();
199 210
200 /// Fills image_view_ids in the image views in indices 211 /// Fills image_view_ids in the image views in indices
212 template <bool has_blacklists>
201 void FillImageViews(DescriptorTable<TICEntry>& table, 213 void FillImageViews(DescriptorTable<TICEntry>& table,
202 std::span<ImageViewId> cached_image_view_ids, std::span<const u32> indices, 214 std::span<ImageViewId> cached_image_view_ids,
203 std::span<ImageViewId> image_view_ids); 215 std::span<ImageViewInOut> views);
204 216
205 /// Find or create an image view in the guest descriptor table 217 /// Find or create an image view in the guest descriptor table
206 ImageViewId VisitImageView(DescriptorTable<TICEntry>& table, 218 ImageViewId VisitImageView(DescriptorTable<TICEntry>& table,
@@ -285,7 +297,7 @@ private:
285 void UntrackImage(ImageBase& image, ImageId image_id); 297 void UntrackImage(ImageBase& image, ImageId image_id);
286 298
287 /// Delete image from the cache 299 /// Delete image from the cache
288 void DeleteImage(ImageId image); 300 void DeleteImage(ImageId image, bool immediate_delete = false);
289 301
290 /// Remove image views references from the cache 302 /// Remove image views references from the cache
291 void RemoveImageViewReferences(std::span<const ImageViewId> removed_views); 303 void RemoveImageViewReferences(std::span<const ImageViewId> removed_views);
@@ -306,7 +318,7 @@ private:
306 void PrepareImageView(ImageViewId image_view_id, bool is_modification, bool invalidate); 318 void PrepareImageView(ImageViewId image_view_id, bool is_modification, bool invalidate);
307 319
308 /// Execute copies from one image to the other, even if they are incompatible 320 /// Execute copies from one image to the other, even if they are incompatible
309 void CopyImage(ImageId dst_id, ImageId src_id, std::span<const ImageCopy> copies); 321 void CopyImage(ImageId dst_id, ImageId src_id, std::vector<ImageCopy> copies);
310 322
311 /// Bind an image view as render target, downloading resources preemtively if needed 323 /// Bind an image view as render target, downloading resources preemtively if needed
312 void BindRenderTarget(ImageViewId* old_id, ImageViewId new_id); 324 void BindRenderTarget(ImageViewId* old_id, ImageViewId new_id);
@@ -318,6 +330,12 @@ private:
318 /// Returns true if the current clear parameters clear the whole image of a given image view 330 /// Returns true if the current clear parameters clear the whole image of a given image view
319 [[nodiscard]] bool IsFullClear(ImageViewId id); 331 [[nodiscard]] bool IsFullClear(ImageViewId id);
320 332
333 bool ImageCanRescale(ImageBase& image);
334 void InvalidateScale(Image& image);
335 bool ScaleUp(Image& image);
336 bool ScaleDown(Image& image);
337 u64 GetScaledImageSizeBytes(ImageBase& image);
338
321 Runtime& runtime; 339 Runtime& runtime;
322 VideoCore::RasterizerInterface& rasterizer; 340 VideoCore::RasterizerInterface& rasterizer;
323 Tegra::Engines::Maxwell3D& maxwell3d; 341 Tegra::Engines::Maxwell3D& maxwell3d;
@@ -349,6 +367,7 @@ private:
349 VAddr virtual_invalid_space{}; 367 VAddr virtual_invalid_space{};
350 368
351 bool has_deleted_images = false; 369 bool has_deleted_images = false;
370 bool is_rescaling = false;
352 u64 total_used_memory = 0; 371 u64 total_used_memory = 0;
353 u64 minimum_memory; 372 u64 minimum_memory;
354 u64 expected_memory; 373 u64 expected_memory;
diff --git a/src/video_core/texture_cache/types.h b/src/video_core/texture_cache/types.h
index 47a11cb2f..5c274abdf 100644
--- a/src/video_core/texture_cache/types.h
+++ b/src/video_core/texture_cache/types.h
@@ -22,6 +22,13 @@ using ImageAllocId = SlotId;
22using SamplerId = SlotId; 22using SamplerId = SlotId;
23using FramebufferId = SlotId; 23using FramebufferId = SlotId;
24 24
25/// Fake image ID for null image views
26constexpr ImageId NULL_IMAGE_ID{0};
27/// Image view ID for null descriptors
28constexpr ImageViewId NULL_IMAGE_VIEW_ID{0};
29/// Sampler ID for bugged sampler ids
30constexpr SamplerId NULL_SAMPLER_ID{0};
31
25enum class ImageType : u32 { 32enum class ImageType : u32 {
26 e1D, 33 e1D,
27 e2D, 34 e2D,
diff --git a/src/video_core/texture_cache/util.cpp b/src/video_core/texture_cache/util.cpp
index 59cf2f561..ddc9fb13a 100644
--- a/src/video_core/texture_cache/util.cpp
+++ b/src/video_core/texture_cache/util.cpp
@@ -723,7 +723,7 @@ ImageViewType RenderTargetImageViewType(const ImageInfo& info) noexcept {
723} 723}
724 724
725std::vector<ImageCopy> MakeShrinkImageCopies(const ImageInfo& dst, const ImageInfo& src, 725std::vector<ImageCopy> MakeShrinkImageCopies(const ImageInfo& dst, const ImageInfo& src,
726 SubresourceBase base) { 726 SubresourceBase base, u32 up_scale, u32 down_shift) {
727 ASSERT(dst.resources.levels >= src.resources.levels); 727 ASSERT(dst.resources.levels >= src.resources.levels);
728 ASSERT(dst.num_samples == src.num_samples); 728 ASSERT(dst.num_samples == src.num_samples);
729 729
@@ -732,7 +732,7 @@ std::vector<ImageCopy> MakeShrinkImageCopies(const ImageInfo& dst, const ImageIn
732 ASSERT(src.type == ImageType::e3D); 732 ASSERT(src.type == ImageType::e3D);
733 ASSERT(src.resources.levels == 1); 733 ASSERT(src.resources.levels == 1);
734 } 734 }
735 735 const bool both_2d{src.type == ImageType::e2D && dst.type == ImageType::e2D};
736 std::vector<ImageCopy> copies; 736 std::vector<ImageCopy> copies;
737 copies.reserve(src.resources.levels); 737 copies.reserve(src.resources.levels);
738 for (s32 level = 0; level < src.resources.levels; ++level) { 738 for (s32 level = 0; level < src.resources.levels; ++level) {
@@ -762,6 +762,10 @@ std::vector<ImageCopy> MakeShrinkImageCopies(const ImageInfo& dst, const ImageIn
762 if (is_dst_3d) { 762 if (is_dst_3d) {
763 copy.extent.depth = src.size.depth; 763 copy.extent.depth = src.size.depth;
764 } 764 }
765 copy.extent.width = std::max<u32>((copy.extent.width * up_scale) >> down_shift, 1);
766 if (both_2d) {
767 copy.extent.height = std::max<u32>((copy.extent.height * up_scale) >> down_shift, 1);
768 }
765 } 769 }
766 return copies; 770 return copies;
767} 771}
@@ -1153,10 +1157,10 @@ void DeduceBlitImages(ImageInfo& dst_info, ImageInfo& src_info, const ImageBase*
1153 if (dst && GetFormatType(dst->info.format) != SurfaceType::ColorTexture) { 1157 if (dst && GetFormatType(dst->info.format) != SurfaceType::ColorTexture) {
1154 dst_info.format = dst->info.format; 1158 dst_info.format = dst->info.format;
1155 } 1159 }
1156 if (!dst && src && GetFormatType(src->info.format) != SurfaceType::ColorTexture) { 1160 if (src && GetFormatType(src->info.format) != SurfaceType::ColorTexture) {
1157 dst_info.format = src->info.format; 1161 dst_info.format = src->info.format;
1158 } 1162 }
1159 if (!src && dst && GetFormatType(dst->info.format) != SurfaceType::ColorTexture) { 1163 if (dst && GetFormatType(dst->info.format) != SurfaceType::ColorTexture) {
1160 src_info.format = dst->info.format; 1164 src_info.format = dst->info.format;
1161 } 1165 }
1162} 1166}
diff --git a/src/video_core/texture_cache/util.h b/src/video_core/texture_cache/util.h
index 766502908..7af52de2e 100644
--- a/src/video_core/texture_cache/util.h
+++ b/src/video_core/texture_cache/util.h
@@ -55,7 +55,8 @@ struct OverlapResult {
55 55
56[[nodiscard]] std::vector<ImageCopy> MakeShrinkImageCopies(const ImageInfo& dst, 56[[nodiscard]] std::vector<ImageCopy> MakeShrinkImageCopies(const ImageInfo& dst,
57 const ImageInfo& src, 57 const ImageInfo& src,
58 SubresourceBase base); 58 SubresourceBase base, u32 up_scale = 1,
59 u32 down_shift = 0);
59 60
60[[nodiscard]] bool IsValidEntry(const Tegra::MemoryManager& gpu_memory, const TICEntry& config); 61[[nodiscard]] bool IsValidEntry(const Tegra::MemoryManager& gpu_memory, const TICEntry& config);
61 62