From 8704c939136e88876d65fc670bce98d8250a6588 Mon Sep 17 00:00:00 2001
From: Fernando Sahmkow <fsahmkow27@gmail.com>
Date: Tue, 20 Jul 2021 22:51:25 +0200
Subject: [PATCH] TextureCache: Fix rescaling of ImageCopies

---
 .../renderer_vulkan/vk_texture_cache.cpp      | 40 +++++++++++------
 src/video_core/texture_cache/texture_cache.h  | 43 +++++++++++++++++--
 .../texture_cache/texture_cache_base.h        |  2 +-
 3 files changed, 67 insertions(+), 18 deletions(-)

diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
index 5fd190825..54236e87f 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -136,6 +136,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
     if (info.type == ImageType::e3D) {
         flags |= VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT;
     }
+    const auto scale_up = [&](u32 value) { return std::max<u32>((value * up) >> down, 1U); };
     const auto [samples_x, samples_y] = VideoCommon::SamplesLog2(info.num_samples);
     const bool is_2d = info.type == ImageType::e2D;
     return VkImageCreateInfo{
@@ -145,8 +146,8 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
         .imageType = ConvertImageType(info.type),
         .format = format_info.format,
         .extent{
-            .width = ((info.size.width * up) >> down) >> samples_x,
-            .height = (is_2d ? ((info.size.height * up) >> down) : info.size.height) >> samples_y,
+            .width = scale_up(info.size.width) >> samples_x,
+            .height = (is_2d ? scale_up(info.size.height) : info.size.height) >> samples_y,
             .depth = info.size.depth,
         },
         .mipLevels = static_cast<u32>(info.resources.levels),
@@ -1078,12 +1079,35 @@ bool Image::ScaleUp(bool save_as_backup) {
     MemoryCommit new_commit(
         runtime->memory_allocator.Commit(rescaled_image, MemoryUsage::DeviceLocal));
 
+    SCOPE_EXIT({
+        if (save_as_backup) {
+            backup_image = std::move(image);
+            backup_commit = std::move(commit);
+            has_backup = true;
+        } else {
+            runtime->prescaled_images.Push(std::move(image));
+            runtime->prescaled_commits.Push(std::move(commit));
+        }
+        image = std::move(rescaled_image);
+        commit = std::move(new_commit);
+    });
+
+    const PixelFormat format = StorageFormat(info.format);
+    const auto format_info =
+        MaxwellToVK::SurfaceFormat(runtime->device, FormatType::Optimal, false, format);
+    const auto similar = runtime->device.GetSupportedFormat(
+        format_info.format, (VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT),
+        FormatType::Optimal);
+
+    if (similar != format_info.format) {
+        return true;
+    }
     if (aspect_mask == 0) {
         aspect_mask = ImageAspectMask(info.format);
     }
 
     const auto scale_up = [&](u32 value) {
-        return (value * resolution.up_scale) >> resolution.down_shift;
+        return std::max<u32>((value * resolution.up_scale) >> resolution.down_shift, 1U);
     };
 
     const bool is_2d = info.type == ImageType::e2D;
@@ -1130,16 +1154,6 @@ bool Image::ScaleUp(bool save_as_backup) {
         vkRegions.push_back(blit);
     }
     BlitScale(*scheduler, *image, *rescaled_image, vkRegions, aspect_mask);
-    if (save_as_backup) {
-        backup_image = std::move(image);
-        backup_commit = std::move(commit);
-        has_backup = true;
-    } else {
-        runtime->prescaled_images.Push(std::move(image));
-        runtime->prescaled_commits.Push(std::move(commit));
-    }
-    image = std::move(rescaled_image);
-    commit = std::move(new_commit);
     return true;
 }
 
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index 4e5031acc..df697cdeb 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -929,8 +929,8 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA
             LOG_WARNING(HW_GPU, "Copying between images with different samples is not implemented");
         } else {
             const SubresourceBase base = new_image.TryFindBase(overlap.gpu_addr).value();
-            const auto copies = MakeShrinkImageCopies(new_info, overlap.info, base);
-            runtime.CopyImage(new_image, overlap, copies);
+            auto copies = MakeShrinkImageCopies(new_info, overlap.info, base);
+            runtime.CopyImage(new_image, overlap, std::move(copies));
         }
         if (True(overlap.flags & ImageFlagBits::Tracked)) {
             UntrackImage(overlap, overlap_id);
@@ -1569,9 +1569,33 @@ void TextureCache<P>::PrepareImageView(ImageViewId image_view_id, bool is_modifi
 }
 
 template <class P>
-void TextureCache<P>::CopyImage(ImageId dst_id, ImageId src_id, std::span<const ImageCopy> copies) {
+void TextureCache<P>::CopyImage(ImageId dst_id, ImageId src_id, std::vector<ImageCopy> copies) {
     Image& dst = slot_images[dst_id];
     Image& src = slot_images[src_id];
+    const bool is_rescaled = True(src.flags & ImageFlagBits::Rescaled);
+    if (is_rescaled) {
+        ASSERT(True(dst.flags & ImageFlagBits::Rescaled));
+        const bool both_2d{src.info.type == ImageType::e2D && dst.info.type == ImageType::e2D};
+        const auto& resolution = Settings::values.resolution_info;
+        const auto scale_up = [&](u32 value) -> u32 {
+            if (value == 0) {
+                return 0U;
+            }
+            return std::max<u32>((value * resolution.up_scale) >> resolution.down_shift, 1U);
+        };
+        for (auto& copy : copies) {
+            copy.src_offset.x = scale_up(copy.src_offset.x);
+
+            copy.dst_offset.x = scale_up(copy.dst_offset.x);
+
+            copy.extent.width = scale_up(copy.extent.width);
+            if (both_2d) {
+                copy.src_offset.y = scale_up(copy.src_offset.y);
+                copy.dst_offset.y = scale_up(copy.dst_offset.y);
+                copy.extent.height = scale_up(copy.extent.height);
+            }
+        }
+    }
     const auto dst_format_type = GetFormatType(dst.info.format);
     const auto src_format_type = GetFormatType(src.info.format);
     if (src_format_type == dst_format_type) {
@@ -1639,10 +1663,21 @@ std::pair<FramebufferId, ImageViewId> TextureCache<P>::RenderTargetFromImage(
     ImageId image_id, const ImageViewInfo& view_info) {
     const ImageViewId view_id = FindOrEmplaceImageView(image_id, view_info);
     const ImageBase& image = slot_images[image_id];
+    const bool is_rescaled = True(image.flags & ImageFlagBits::Rescaled);
     const bool is_color = GetFormatType(image.info.format) == SurfaceType::ColorTexture;
     const ImageViewId color_view_id = is_color ? view_id : ImageViewId{};
     const ImageViewId depth_view_id = is_color ? ImageViewId{} : view_id;
-    const Extent3D extent = MipSize(image.info.size, view_info.range.base.level);
+    Extent3D extent = MipSize(image.info.size, view_info.range.base.level);
+    if (is_rescaled) {
+        const auto& resolution = Settings::values.resolution_info;
+        const auto scale_up = [&](u32 value) {
+            return std::max<u32>((value * resolution.up_scale) >> resolution.down_shift, 1U);
+        };
+        extent.width = scale_up(extent.width);
+        if (image.info.type == ImageType::e2D) {
+            extent.height = scale_up(extent.height);
+        }
+    }
     const u32 num_samples = image.info.num_samples;
     const auto [samples_x, samples_y] = SamplesLog2(num_samples);
     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 1f51fcee8..deddf0d30 100644
--- a/src/video_core/texture_cache/texture_cache_base.h
+++ b/src/video_core/texture_cache/texture_cache_base.h
@@ -316,7 +316,7 @@ private:
     void PrepareImageView(ImageViewId image_view_id, bool is_modification, bool invalidate);
 
     /// Execute copies from one image to the other, even if they are incompatible
-    void CopyImage(ImageId dst_id, ImageId src_id, std::span<const ImageCopy> copies);
+    void CopyImage(ImageId dst_id, ImageId src_id, std::vector<ImageCopy> copies);
 
     /// Bind an image view as render target, downloading resources preemtively if needed
     void BindRenderTarget(ImageViewId* old_id, ImageViewId new_id);