diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index 5ca67c4133cd29d949f66650d9d51575e54f161b..fd334a1462d0103ba16797aab69e5d001dd81546 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -63,7 +63,7 @@ VkViewport GetViewportState(const Device& device, const Maxwell& regs, size_t in
     const auto conv = [scale](float value) {
         float new_value = value * scale;
         if (scale < 1.0f) {
-            bool sign = std::signbit(new_value);
+            const bool sign = std::signbit(value);
             new_value = std::round(std::abs(new_value));
             new_value = sign ? -new_value : new_value;
         }
@@ -96,21 +96,22 @@ VkViewport GetViewportState(const Device& device, const Maxwell& regs, size_t in
 VkRect2D GetScissorState(const Maxwell& regs, size_t index, u32 up_scale = 1, u32 down_shift = 0) {
     const auto& src = regs.scissor_test[index];
     VkRect2D scissor;
-    const auto scale_up = [&](u32 value) -> u32 {
+    const auto scale_up = [&](s32 value) -> s32 {
         if (value == 0) {
             return 0U;
         }
-        const u32 upset = value * up_scale;
-        u32 acumm = 0;
+        const s32 upset = value * up_scale;
+        s32 acumm = 0;
         if ((up_scale >> down_shift) == 0) {
-            acumm = upset & 0x1;
+            acumm = upset % 2;
         }
-        const u32 converted_value = (value * up_scale) >> down_shift;
-        return std::max<u32>(converted_value + acumm, 1U);
+        const s32 converted_value = (value * up_scale) >> down_shift;
+        return value < 0 ? std::min<s32>(converted_value - acumm, -1)
+                         : std::max<s32>(converted_value + acumm, 1);
     };
     if (src.enable) {
-        scissor.offset.x = static_cast<s32>(scale_up(src.min_x));
-        scissor.offset.y = static_cast<s32>(scale_up(src.min_y));
+        scissor.offset.x = scale_up(static_cast<s32>(src.min_x));
+        scissor.offset.y = scale_up(static_cast<s32>(src.min_y));
         scissor.extent.width = scale_up(src.max_x - src.min_x);
         scissor.extent.height = scale_up(src.max_y - src.min_y);
     } else {
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index 13914dc8b17967986abe78d161c30a53fda4badf..a32c11d04918627ddbde5b255d7938f2476590cb 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -1037,8 +1037,11 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA
         if (overlap.info.num_samples != new_image.info.num_samples) {
             LOG_WARNING(HW_GPU, "Copying between images with different samples is not implemented");
         } else {
+            const auto& resolution = Settings::values.resolution_info;
             const SubresourceBase base = new_image.TryFindBase(overlap.gpu_addr).value();
-            auto copies = MakeShrinkImageCopies(new_info, overlap.info, base);
+            const u32 up_scale = can_rescale ? resolution.up_scale : 1;
+            const u32 down_shift = can_rescale ? resolution.down_shift : 0;
+            auto copies = MakeShrinkImageCopies(new_info, overlap.info, base, up_scale, down_shift);
             runtime.CopyImage(new_image, overlap, std::move(copies));
         }
         if (True(overlap.flags & ImageFlagBits::Tracked)) {
@@ -1659,19 +1662,35 @@ void TextureCache<P>::SynchronizeAliases(ImageId image_id) {
         const ImageBase& rhs_image = slot_images[rhs->id];
         return lhs_image.modification_tick < rhs_image.modification_tick;
     });
+    const auto& resolution = Settings::values.resolution_info;
     for (const AliasedImage* const aliased : aliased_images) {
-        if (any_rescaled) {
-            Image& aliased_image = slot_images[aliased->id];
-            if (can_rescale) {
-                ScaleUp(aliased_image);
-            } else {
-                ScaleDown(aliased_image);
-                if (any_blacklisted) {
-                    aliased_image.flags |= ImageFlagBits::Blacklisted;
-                }
+        if (!resolution.active | !any_rescaled) {
+            CopyImage(image_id, aliased->id, aliased->copies);
+            continue;
+        }
+        Image& aliased_image = slot_images[aliased->id];
+        if (!can_rescale) {
+            ScaleDown(aliased_image);
+            if (any_blacklisted) {
+                aliased_image.flags |= ImageFlagBits::Blacklisted;
+            }
+            CopyImage(image_id, aliased->id, aliased->copies);
+            continue;
+        }
+        ScaleUp(aliased_image);
+
+        const bool both_2d{image.info.type == ImageType::e2D &&
+                           aliased_image.info.type == ImageType::e2D};
+        auto copies = aliased->copies;
+        for (auto copy : copies) {
+            copy.extent.width = std::max<u32>(
+                (copy.extent.width * resolution.up_scale) >> resolution.down_shift, 1);
+            if (both_2d) {
+                copy.extent.height = std::max<u32>(
+                    (copy.extent.height * resolution.up_scale) >> resolution.down_shift, 1);
             }
         }
-        CopyImage(image_id, aliased->id, aliased->copies);
+        CopyImage(image_id, aliased->id, copies);
     }
 }
 
diff --git a/src/video_core/texture_cache/util.cpp b/src/video_core/texture_cache/util.cpp
index 59cf2f5618eb9bb0e9feb771ee0ae9382714bda9..9922aa0cc18581278c91e8765dd2809b29a4b0e1 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 {
 }
 
 std::vector<ImageCopy> MakeShrinkImageCopies(const ImageInfo& dst, const ImageInfo& src,
-                                             SubresourceBase base) {
+                                             SubresourceBase base, u32 up_scale, u32 down_shift) {
     ASSERT(dst.resources.levels >= src.resources.levels);
     ASSERT(dst.num_samples == src.num_samples);
 
@@ -732,7 +732,7 @@ std::vector<ImageCopy> MakeShrinkImageCopies(const ImageInfo& dst, const ImageIn
         ASSERT(src.type == ImageType::e3D);
         ASSERT(src.resources.levels == 1);
     }
-
+    const bool both_2d{src.type == ImageType::e2D && dst.type == ImageType::e2D};
     std::vector<ImageCopy> copies;
     copies.reserve(src.resources.levels);
     for (s32 level = 0; level < src.resources.levels; ++level) {
@@ -762,6 +762,10 @@ std::vector<ImageCopy> MakeShrinkImageCopies(const ImageInfo& dst, const ImageIn
         if (is_dst_3d) {
             copy.extent.depth = src.size.depth;
         }
+        copy.extent.width = std::max<u32>((copy.extent.width * up_scale) >> down_shift, 1);
+        if (both_2d) {
+            copy.extent.height = std::max<u32>((copy.extent.height * up_scale) >> down_shift, 1);
+        }
     }
     return copies;
 }
diff --git a/src/video_core/texture_cache/util.h b/src/video_core/texture_cache/util.h
index 766502908f210db8ae5d7fdbe0e4f28d1644152c..7af52de2e1a4aa2403465f2242cf9f46435ebec2 100644
--- a/src/video_core/texture_cache/util.h
+++ b/src/video_core/texture_cache/util.h
@@ -55,7 +55,8 @@ struct OverlapResult {
 
 [[nodiscard]] std::vector<ImageCopy> MakeShrinkImageCopies(const ImageInfo& dst,
                                                            const ImageInfo& src,
-                                                           SubresourceBase base);
+                                                           SubresourceBase base, u32 up_scale = 1,
+                                                           u32 down_shift = 0);
 
 [[nodiscard]] bool IsValidEntry(const Tegra::MemoryManager& gpu_memory, const TICEntry& config);