Jelajahi Sumber

Out of bound blit (#6531)

* Fix out of bound blit error

* Fix code read

* Fix ci error

Co-authored-by: Feng Chen <chen.feng@gloritysolutions.com>
Feng Chen 5 tahun lalu
induk
melakukan
c7ad195fd3

+ 20 - 2
src/video_core/engines/fermi_2d.cpp

@@ -7,6 +7,10 @@
 #include "video_core/engines/fermi_2d.h"
 #include "video_core/memory_manager.h"
 #include "video_core/rasterizer_interface.h"
+#include "video_core/surface.h"
+
+using VideoCore::Surface::BytesPerBlock;
+using VideoCore::Surface::PixelFormatFromRenderTargetFormat;
 
 namespace Tegra::Engines {
 
@@ -49,7 +53,7 @@ void Fermi2D::Blit() {
     UNIMPLEMENTED_IF_MSG(regs.clip_enable != 0, "Clipped blit enabled");
 
     const auto& args = regs.pixels_from_memory;
-    const Config config{
+    Config config{
         .operation = regs.operation,
         .filter = args.sample_mode.filter,
         .dst_x0 = args.dst_x0,
@@ -61,7 +65,21 @@ void Fermi2D::Blit() {
         .src_x1 = static_cast<s32>((args.du_dx * args.dst_width + args.src_x0) >> 32),
         .src_y1 = static_cast<s32>((args.dv_dy * args.dst_height + args.src_y0) >> 32),
     };
-    if (!rasterizer->AccelerateSurfaceCopy(regs.src, regs.dst, config)) {
+    Surface src = regs.src;
+    const auto bytes_per_pixel = BytesPerBlock(PixelFormatFromRenderTargetFormat(src.format));
+    const auto need_align_to_pitch =
+        src.linear == Tegra::Engines::Fermi2D::MemoryLayout::Pitch &&
+        static_cast<s32>(src.width) == config.src_x1 &&
+        config.src_x1 > static_cast<s32>(src.pitch / bytes_per_pixel) && config.src_x0 > 0;
+    if (need_align_to_pitch) {
+        auto address = src.Address() + config.src_x0 * bytes_per_pixel;
+        src.addr_upper = static_cast<u32>(address >> 32);
+        src.addr_lower = static_cast<u32>(address);
+        src.width -= config.src_x0;
+        config.src_x1 -= config.src_x0;
+        config.src_x0 = 0;
+    }
+    if (!rasterizer->AccelerateSurfaceCopy(src, regs.dst, config)) {
         UNIMPLEMENTED();
     }
 }

+ 15 - 56
src/video_core/texture_cache/texture_cache.h

@@ -159,9 +159,7 @@ public:
     /// Blit an image with the given parameters
     void BlitImage(const Tegra::Engines::Fermi2D::Surface& dst,
                    const Tegra::Engines::Fermi2D::Surface& src,
-                   const Tegra::Engines::Fermi2D::Config& copy,
-                   std::optional<Region2D> src_region_override = {},
-                   std::optional<Region2D> dst_region_override = {});
+                   const Tegra::Engines::Fermi2D::Config& copy);
 
     /// Invalidate the contents of the color buffer index
     /// These contents become unspecified, the cache can assume aggressive optimizations.
@@ -760,9 +758,7 @@ void TextureCache<P>::UnmapGPUMemory(GPUVAddr gpu_addr, size_t size) {
 template <class P>
 void TextureCache<P>::BlitImage(const Tegra::Engines::Fermi2D::Surface& dst,
                                 const Tegra::Engines::Fermi2D::Surface& src,
-                                const Tegra::Engines::Fermi2D::Config& copy,
-                                std::optional<Region2D> src_override,
-                                std::optional<Region2D> dst_override) {
+                                const Tegra::Engines::Fermi2D::Config& copy) {
     const BlitImages images = GetBlitImages(dst, src);
     const ImageId dst_id = images.dst_id;
     const ImageId src_id = images.src_id;
@@ -773,47 +769,25 @@ void TextureCache<P>::BlitImage(const Tegra::Engines::Fermi2D::Surface& dst,
     const ImageBase& src_image = slot_images[src_id];
 
     // TODO: Deduplicate
-    const std::optional dst_base = dst_image.TryFindBase(dst.Address());
-    const SubresourceRange dst_range{.base = dst_base.value(), .extent = {1, 1}};
-    const ImageViewInfo dst_view_info(ImageViewType::e2D, images.dst_format, dst_range);
-    const auto [dst_framebuffer_id, dst_view_id] = RenderTargetFromImage(dst_id, dst_view_info);
-    const auto [src_samples_x, src_samples_y] = SamplesLog2(src_image.info.num_samples);
-
-    // out of bounds texture blit checking
-    const bool use_override = src_override.has_value();
-    const s32 src_x0 = copy.src_x0 >> src_samples_x;
-    s32 src_x1 = use_override ? src_override->end.x : copy.src_x1 >> src_samples_x;
-    const s32 src_y0 = copy.src_y0 >> src_samples_y;
-    const s32 src_y1 = copy.src_y1 >> src_samples_y;
-
-    const auto src_width = static_cast<s32>(src_image.info.size.width);
-    const bool width_oob = src_x1 > src_width;
-    const auto width_diff = width_oob ? src_x1 - src_width : 0;
-    if (width_oob) {
-        src_x1 = src_width;
-    }
-
-    const Region2D src_dimensions{
-        Offset2D{.x = src_x0, .y = src_y0},
-        Offset2D{.x = src_x1, .y = src_y1},
-    };
-    const auto src_region = use_override ? *src_override : src_dimensions;
-
     const std::optional src_base = src_image.TryFindBase(src.Address());
     const SubresourceRange src_range{.base = src_base.value(), .extent = {1, 1}};
     const ImageViewInfo src_view_info(ImageViewType::e2D, images.src_format, src_range);
     const auto [src_framebuffer_id, src_view_id] = RenderTargetFromImage(src_id, src_view_info);
-    const auto [dst_samples_x, dst_samples_y] = SamplesLog2(dst_image.info.num_samples);
+    const auto [src_samples_x, src_samples_y] = SamplesLog2(src_image.info.num_samples);
+    const Region2D src_region{
+        Offset2D{.x = copy.src_x0 >> src_samples_x, .y = copy.src_y0 >> src_samples_y},
+        Offset2D{.x = copy.src_x1 >> src_samples_x, .y = copy.src_y1 >> src_samples_y},
+    };
 
-    const s32 dst_x0 = copy.dst_x0 >> dst_samples_x;
-    const s32 dst_x1 = copy.dst_x1 >> dst_samples_x;
-    const s32 dst_y0 = copy.dst_y0 >> dst_samples_y;
-    const s32 dst_y1 = copy.dst_y1 >> dst_samples_y;
-    const Region2D dst_dimensions{
-        Offset2D{.x = dst_x0, .y = dst_y0},
-        Offset2D{.x = dst_x1 - width_diff, .y = dst_y1},
+    const std::optional dst_base = dst_image.TryFindBase(dst.Address());
+    const SubresourceRange dst_range{.base = dst_base.value(), .extent = {1, 1}};
+    const ImageViewInfo dst_view_info(ImageViewType::e2D, images.dst_format, dst_range);
+    const auto [dst_framebuffer_id, dst_view_id] = RenderTargetFromImage(dst_id, dst_view_info);
+    const auto [dst_samples_x, dst_samples_y] = SamplesLog2(dst_image.info.num_samples);
+    const Region2D dst_region{
+        Offset2D{.x = copy.dst_x0 >> dst_samples_x, .y = copy.dst_y0 >> dst_samples_y},
+        Offset2D{.x = copy.dst_x1 >> dst_samples_x, .y = copy.dst_y1 >> dst_samples_y},
     };
-    const auto dst_region = use_override ? *dst_override : dst_dimensions;
 
     // Always call this after src_framebuffer_id was queried, as the address might be invalidated.
     Framebuffer* const dst_framebuffer = &slot_framebuffers[dst_framebuffer_id];
@@ -830,21 +804,6 @@ void TextureCache<P>::BlitImage(const Tegra::Engines::Fermi2D::Surface& dst,
         runtime.BlitImage(dst_framebuffer, dst_view, src_view, dst_region, src_region, copy.filter,
                           copy.operation);
     }
-
-    if (width_oob) {
-        // Continue copy of the oob region of the texture on the next row
-        auto oob_src = src;
-        oob_src.height++;
-        const Region2D src_region_override{
-            Offset2D{.x = 0, .y = src_y0 + 1},
-            Offset2D{.x = width_diff, .y = src_y1 + 1},
-        };
-        const Region2D dst_region_override{
-            Offset2D{.x = dst_x1 - width_diff, .y = dst_y0},
-            Offset2D{.x = dst_x1, .y = dst_y1},
-        };
-        BlitImage(dst, oob_src, copy, src_region_override, dst_region_override);
-    }
 }
 
 template <class P>