From f565b0e5a6bebc09381aabb046e9b0b6285b7d10 Mon Sep 17 00:00:00 2001
From: gdkchan <gab.dark.100@gmail.com>
Date: Sat, 23 Jan 2021 09:38:00 -0300
Subject: [PATCH] Match texture if the physical range is the same (#1934)

* Match texture if the physical range is the same

* XML docs and comments
---
 Ryujinx.Graphics.Gpu/Image/TextureManager.cs | 26 +++++++++++----
 Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs | 33 ++++++++++++++++++++
 2 files changed, 53 insertions(+), 6 deletions(-)

diff --git a/Ryujinx.Graphics.Gpu/Image/TextureManager.cs b/Ryujinx.Graphics.Gpu/Image/TextureManager.cs
index 30137d06..2646a75b 100644
--- a/Ryujinx.Graphics.Gpu/Image/TextureManager.cs
+++ b/Ryujinx.Graphics.Gpu/Image/TextureManager.cs
@@ -685,14 +685,28 @@ namespace Ryujinx.Graphics.Gpu.Image
             {
                 Texture overlap = _textureOverlaps[index];
 
-                bool rangeMatches = range != null ? overlap.Range.Equals(range.Value) : overlap.Info.GpuAddress == info.GpuAddress;
-                if (!rangeMatches)
-                {
-                    continue;
-                }
-
                 TextureMatchQuality matchQuality = overlap.IsExactMatch(info, flags);
 
+                if (matchQuality != TextureMatchQuality.NoMatch)
+                {
+                    // If the parameters match, we need to make sure the texture is mapped to the same memory regions.
+
+                    // If a range of memory was supplied, just check if the ranges match.
+                    if (range != null && !overlap.Range.Equals(range.Value))
+                    {
+                        continue;
+                    }
+
+                    // If no range was supplied, we can check if the GPU virtual address match. If they do,
+                    // we know the textures are located at the same memory region.
+                    // If they don't, it may still be mapped to the same physical region, so we
+                    // do a more expensive check to tell if they are mapped into the same physical regions.
+                    if (overlap.Info.GpuAddress != info.GpuAddress && !_context.MemoryManager.CompareRange(overlap.Range, info.GpuAddress))
+                    {
+                        continue;
+                    }
+                }
+
                 if (matchQuality == TextureMatchQuality.Perfect)
                 {
                     texture = overlap;
diff --git a/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs b/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs
index 7021cd20..5776836c 100644
--- a/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs
+++ b/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs
@@ -343,6 +343,39 @@ namespace Ryujinx.Graphics.Gpu.Memory
             return new MultiRange(regions.ToArray());
         }
 
+        /// <summary>
+        /// Checks if a given GPU virtual memory range is mapped to the same physical regions
+        /// as the specified physical memory multi-range.
+        /// </summary>
+        /// <param name="range">Physical memory multi-range</param>
+        /// <param name="va">GPU virtual memory address</param>
+        /// <returns>True if the virtual memory region is mapped into the specified physical one, false otherwise</returns>
+        public bool CompareRange(MultiRange range, ulong va)
+        {
+            va &= ~PageMask;
+
+            for (int i = 0; i < range.Count; i++)
+            {
+                MemoryRange currentRange = range.GetSubRange(i);
+
+                ulong address = currentRange.Address & ~PageMask;
+                ulong endAddress = (currentRange.EndAddress + PageMask) & ~PageMask;
+
+                while (address < endAddress)
+                {
+                    if (Translate(va) != address)
+                    {
+                        return false;
+                    }
+
+                    va += PageSize;
+                    address += PageSize;
+                }
+            }
+
+            return true;
+        }
+
         /// <summary>
         /// Validates a GPU virtual address.
         /// </summary>