diff options
| author | 2021-11-16 18:52:11 -0800 | |
|---|---|---|
| committer | 2021-11-16 18:52:11 -0800 | |
| commit | 71313509f75aeafe425e531824d1faa9e7c0a40b (patch) | |
| tree | cb1df371d288677fcede6a3409eb079e0d278163 /src/video_core/texture_cache | |
| parent | Merge pull request #7347 from lioncash/catch (diff) | |
| parent | TextureCache: Fix Automatic Anisotropic. (diff) | |
| download | yuzu-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.cpp | 10 | ||||
| -rw-r--r-- | src/video_core/texture_cache/image_base.h | 15 | ||||
| -rw-r--r-- | src/video_core/texture_cache/image_info.cpp | 22 | ||||
| -rw-r--r-- | src/video_core/texture_cache/image_info.h | 4 | ||||
| -rw-r--r-- | src/video_core/texture_cache/image_view_base.cpp | 13 | ||||
| -rw-r--r-- | src/video_core/texture_cache/image_view_base.h | 4 | ||||
| -rw-r--r-- | src/video_core/texture_cache/texture_cache.h | 495 | ||||
| -rw-r--r-- | src/video_core/texture_cache/texture_cache_base.h | 43 | ||||
| -rw-r--r-- | src/video_core/texture_cache/types.h | 7 | ||||
| -rw-r--r-- | src/video_core/texture_cache/util.cpp | 12 | ||||
| -rw-r--r-- | src/video_core/texture_cache/util.h | 3 |
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 { | |||
| 60 | ImageBase::ImageBase(const ImageInfo& info_, GPUVAddr gpu_addr_, VAddr cpu_addr_) | 60 | ImageBase::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 | ||
| 72 | ImageBase::ImageBase(const NullImageParams&) {} | ||
| 73 | |||
| 72 | ImageMapView::ImageMapView(GPUVAddr gpu_addr_, VAddr cpu_addr_, size_t size_, ImageId image_id_) | 74 | ImageMapView::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 | }; |
| 37 | DECLARE_ENUM_FLAG_OPERATORS(ImageFlagBits) | 42 | DECLARE_ENUM_FLAG_OPERATORS(ImageFlagBits) |
| 38 | 43 | ||
| @@ -43,8 +48,11 @@ struct AliasedImage { | |||
| 43 | ImageId id; | 48 | ImageId id; |
| 44 | }; | 49 | }; |
| 45 | 50 | ||
| 51 | struct NullImageParams {}; | ||
| 52 | |||
| 46 | struct ImageBase { | 53 | struct 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 { | |||
| 16 | using Tegra::Texture::TextureType; | 16 | using Tegra::Texture::TextureType; |
| 17 | using Tegra::Texture::TICEntry; | 17 | using Tegra::Texture::TICEntry; |
| 18 | using VideoCore::Surface::PixelFormat; | 18 | using VideoCore::Surface::PixelFormat; |
| 19 | using VideoCore::Surface::SurfaceType; | ||
| 19 | 20 | ||
| 20 | ImageInfo::ImageInfo(const TICEntry& config) noexcept { | 21 | ImageInfo::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 | ||
| 101 | ImageInfo::ImageInfo(const Tegra::Engines::Maxwell3D::Regs& regs, size_t index) noexcept { | 111 | ImageInfo::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 { | |||
| 161 | ImageInfo::ImageInfo(const Tegra::Engines::Fermi2D::Surface& config) noexcept { | 178 | ImageInfo::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; | |||
| 15 | using VideoCore::Surface::PixelFormat; | 15 | using VideoCore::Surface::PixelFormat; |
| 16 | 16 | ||
| 17 | struct ImageInfo { | 17 | struct 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 | ||
| 39 | ImageViewBase::ImageViewBase(const ImageInfo& info, const ImageViewInfo& view_info) | 39 | ImageViewBase::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 | ||
| 48 | ImageViewBase::ImageViewBase(const NullImageParams&) {} | 49 | ImageViewBase::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; | |||
| 15 | struct ImageViewInfo; | 15 | struct ImageViewInfo; |
| 16 | struct ImageInfo; | 16 | struct ImageInfo; |
| 17 | 17 | ||
| 18 | struct NullImageParams {}; | 18 | struct NullImageViewParams {}; |
| 19 | 19 | ||
| 20 | enum class ImageViewFlagBits : u16 { | 20 | enum 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 | ||
| 124 | template <class P> | 127 | template <class P> |
| 125 | void TextureCache<P>::FillGraphicsImageViews(std::span<const u32> indices, | 128 | template <bool has_blacklists> |
| 126 | std::span<ImageViewId> image_view_ids) { | 129 | void 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 | ||
| 130 | template <class P> | 133 | template <class P> |
| 131 | void TextureCache<P>::FillComputeImageViews(std::span<const u32> indices, | 134 | void 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 | ||
| 136 | template <class P> | 138 | template <class P> |
| @@ -190,6 +192,102 @@ void TextureCache<P>::SynchronizeComputeDescriptors() { | |||
| 190 | } | 192 | } |
| 191 | 193 | ||
| 192 | template <class P> | 194 | template <class P> |
| 195 | bool 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 | |||
| 290 | template <class P> | ||
| 193 | void TextureCache<P>::UpdateRenderTargets(bool is_clear) { | 291 | void 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 | ||
| 243 | template <class P> | 341 | template <class P> |
| 342 | template <bool has_blacklists> | ||
| 244 | void TextureCache<P>::FillImageViews(DescriptorTable<TICEntry>& table, | 343 | void 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 | ||
| 257 | template <class P> | 366 | template <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 | ||
| 489 | template <class P> | 639 | template <class P> |
| 640 | bool TextureCache<P>::IsRescaling() const noexcept { | ||
| 641 | return is_rescaling; | ||
| 642 | } | ||
| 643 | |||
| 644 | template <class P> | ||
| 645 | bool 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 | |||
| 653 | template <class P> | ||
| 490 | bool TextureCache<P>::IsRegionGpuModified(VAddr addr, size_t size) { | 654 | bool 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 | ||
| 626 | template <class P> | 790 | template <class P> |
| 791 | bool 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 | |||
| 817 | template <class P> | ||
| 818 | void 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 | |||
| 852 | template <class P> | ||
| 853 | u64 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 | |||
| 865 | template <class P> | ||
| 866 | bool 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 | |||
| 879 | template <class P> | ||
| 880 | bool 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 | |||
| 889 | template <class P> | ||
| 627 | ImageId TextureCache<P>::InsertImage(const ImageInfo& info, GPUVAddr gpu_addr, | 890 | ImageId 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 | ||
| 1215 | template <class P> | 1515 | template <class P> |
| 1216 | void TextureCache<P>::DeleteImage(ImageId image_id) { | 1516 | void 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 { | |||
| 1322 | template <class P> | 1639 | template <class P> |
| 1323 | void TextureCache<P>::SynchronizeAliases(ImageId image_id) { | 1640 | void 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 | ||
| 1379 | template <class P> | 1730 | template <class P> |
| 1380 | void TextureCache<P>::CopyImage(ImageId dst_id, ImageId src_id, std::span<const ImageCopy> copies) { | 1731 | void 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; | |||
| 39 | using VideoCore::Surface::PixelFormatFromRenderTargetFormat; | 40 | using VideoCore::Surface::PixelFormatFromRenderTargetFormat; |
| 40 | using namespace Common::Literals; | 41 | using namespace Common::Literals; |
| 41 | 42 | ||
| 43 | struct ImageViewInOut { | ||
| 44 | u32 index{}; | ||
| 45 | bool blacklist{}; | ||
| 46 | ImageViewId id{}; | ||
| 47 | }; | ||
| 48 | |||
| 42 | template <class P> | 49 | template <class P> |
| 43 | class TextureCache { | 50 | class 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 | ||
| 165 | private: | 176 | private: |
| @@ -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; | |||
| 22 | using SamplerId = SlotId; | 22 | using SamplerId = SlotId; |
| 23 | using FramebufferId = SlotId; | 23 | using FramebufferId = SlotId; |
| 24 | 24 | ||
| 25 | /// Fake image ID for null image views | ||
| 26 | constexpr ImageId NULL_IMAGE_ID{0}; | ||
| 27 | /// Image view ID for null descriptors | ||
| 28 | constexpr ImageViewId NULL_IMAGE_VIEW_ID{0}; | ||
| 29 | /// Sampler ID for bugged sampler ids | ||
| 30 | constexpr SamplerId NULL_SAMPLER_ID{0}; | ||
| 31 | |||
| 25 | enum class ImageType : u32 { | 32 | enum 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 | ||
| 725 | std::vector<ImageCopy> MakeShrinkImageCopies(const ImageInfo& dst, const ImageInfo& src, | 725 | std::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 | ||