From 61b1ce252f11e8f8e31080faee60d0a9d99cb67f Mon Sep 17 00:00:00 2001
From: gdkchan <gab.dark.100@gmail.com>
Date: Fri, 10 Feb 2023 11:47:59 -0300
Subject: [PATCH] Allow partially mapped textures with unmapped start (#4394)

---
 Ryujinx.Graphics.Gpu/Image/TextureCache.cs   |  7 +++++
 Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs | 32 ++++++++++++++++++++
 2 files changed, 39 insertions(+)

diff --git a/Ryujinx.Graphics.Gpu/Image/TextureCache.cs b/Ryujinx.Graphics.Gpu/Image/TextureCache.cs
index 27bec786f0..f18de60751 100644
--- a/Ryujinx.Graphics.Gpu/Image/TextureCache.cs
+++ b/Ryujinx.Graphics.Gpu/Image/TextureCache.cs
@@ -474,6 +474,13 @@ namespace Ryujinx.Graphics.Gpu.Image
             {
                 address = memoryManager.Translate(info.GpuAddress);
 
+                // If the start address is unmapped, let's try to find a page of memory that is mapped.
+                if (address == MemoryManager.PteUnmapped)
+                {
+                    address = memoryManager.TranslateFirstMapped(info.GpuAddress, (ulong)info.CalculateSizeInfo(layerSize).TotalSize);
+                }
+
+                // If address is still invalid, the texture is fully unmapped, so it has no data, just return null.
                 if (address == MemoryManager.PteUnmapped)
                 {
                     return null;
diff --git a/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs b/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs
index 0ac6160d95..b0f7e7992f 100644
--- a/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs
+++ b/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs
@@ -583,6 +583,38 @@ namespace Ryujinx.Graphics.Gpu.Memory
             return UnpackPaFromPte(pte) + (va & PageMask);
         }
 
+        /// <summary>
+        /// Translates a GPU virtual address to a CPU virtual address on the first mapped page of memory
+        /// on the specified region.
+        /// If no page is mapped on the specified region, <see cref="PteUnmapped"/> is returned.
+        /// </summary>
+        /// <param name="va">GPU virtual address to be translated</param>
+        /// <param name="size">Size of the range to be translated</param>
+        /// <returns>CPU virtual address, or <see cref="PteUnmapped"/> if unmapped</returns>
+        public ulong TranslateFirstMapped(ulong va, ulong size)
+        {
+            if (!ValidateAddress(va))
+            {
+                return PteUnmapped;
+            }
+
+            ulong endVa = va + size;
+
+            ulong pte = GetPte(va);
+
+            for (; va < endVa && pte == PteUnmapped; va += PageSize - (va & PageMask))
+            {
+                pte = GetPte(va);
+            }
+
+            if (pte == PteUnmapped)
+            {
+                return PteUnmapped;
+            }
+
+            return UnpackPaFromPte(pte) + (va & PageMask);
+        }
+
         /// <summary>
         /// Gets the kind of a given memory page.
         /// This might indicate the type of resource that can be allocated on the page, and also texture tiling.