From 34d19f381cd496ec5e6d4fb13b45d47c141b7a63 Mon Sep 17 00:00:00 2001
From: gdkchan <gab.dark.100@gmail.com>
Date: Sat, 25 Apr 2020 10:40:20 -0300
Subject: [PATCH] Fix texture level offset/size calculation when sparse tile
 width is > 1 (#1142)

* Fix texture level offset/size calculation when sparse tile width is > 1

* Sparse tile width affects layer size alignment aswell
---
 Ryujinx.Graphics.Gpu/Image/Texture.cs       | 11 +---
 Ryujinx.Graphics.Texture/LayoutConverter.cs | 34 +++++++---
 Ryujinx.Graphics.Texture/SizeCalculator.cs  | 73 +++++++++++++++------
 3 files changed, 81 insertions(+), 37 deletions(-)

diff --git a/Ryujinx.Graphics.Gpu/Image/Texture.cs b/Ryujinx.Graphics.Gpu/Image/Texture.cs
index 957c3465..d02c3665 100644
--- a/Ryujinx.Graphics.Gpu/Image/Texture.cs
+++ b/Ryujinx.Graphics.Gpu/Image/Texture.cs
@@ -870,13 +870,6 @@ namespace Ryujinx.Graphics.Gpu.Image
             {
                 int depth = Math.Max(1, info.GetDepth() >> level);
 
-                (int gobBlocksInY, int gobBlocksInZ) = SizeCalculator.GetMipGobBlockSizes(
-                    height,
-                    depth,
-                    info.FormatInfo.BlockHeight,
-                    info.GobBlocksInY,
-                    info.GobBlocksInZ);
-
                 return SizeCalculator.GetBlockLinearAlignedSize(
                     width,
                     height,
@@ -884,8 +877,8 @@ namespace Ryujinx.Graphics.Gpu.Image
                     info.FormatInfo.BlockWidth,
                     info.FormatInfo.BlockHeight,
                     info.FormatInfo.BytesPerPixel,
-                    gobBlocksInY,
-                    gobBlocksInZ,
+                    info.GobBlocksInY,
+                    info.GobBlocksInZ,
                     info.GobBlocksInTileX);
             }
         }
diff --git a/Ryujinx.Graphics.Texture/LayoutConverter.cs b/Ryujinx.Graphics.Texture/LayoutConverter.cs
index 2c3d641b..ce2b37b5 100644
--- a/Ryujinx.Graphics.Texture/LayoutConverter.cs
+++ b/Ryujinx.Graphics.Texture/LayoutConverter.cs
@@ -38,11 +38,12 @@ namespace Ryujinx.Graphics.Texture
 
             int outOffs = 0;
 
-            int wAlignment = gobBlocksInTileX * (GobStride / bytesPerPixel);
-
             int mipGobBlocksInY = gobBlocksInY;
             int mipGobBlocksInZ = gobBlocksInZ;
 
+            int gobWidth  = (GobStride / bytesPerPixel) * gobBlocksInTileX;
+            int gobHeight = gobBlocksInY * GobHeight;
+
             for (int level = 0; level < levels; level++)
             {
                 int w = Math.Max(1, width  >> level);
@@ -66,8 +67,16 @@ namespace Ryujinx.Graphics.Texture
 
                 int xStart = strideTrunc / bytesPerPixel;
 
-                int stride   = BitUtils.AlignUp(w * bytesPerPixel, HostStrideAlignment);
-                int wAligned = BitUtils.AlignUp(w, wAlignment);
+                int stride = BitUtils.AlignUp(w * bytesPerPixel, HostStrideAlignment);
+
+                int alignment = gobWidth;
+
+                if (d < gobBlocksInZ || w <= gobWidth || h <= gobHeight)
+                {
+                    alignment = GobStride / bytesPerPixel;
+                }
+
+                int wAligned = BitUtils.AlignUp(w, alignment);
 
                 BlockLinearLayout layoutConverter = new BlockLinearLayout(
                     wAligned,
@@ -164,11 +173,12 @@ namespace Ryujinx.Graphics.Texture
 
             int inOffs = 0;
 
-            int wAlignment = gobBlocksInTileX * (GobStride / bytesPerPixel);
-
             int mipGobBlocksInY = gobBlocksInY;
             int mipGobBlocksInZ = gobBlocksInZ;
 
+            int gobWidth  = (GobStride / bytesPerPixel) * gobBlocksInTileX;
+            int gobHeight = gobBlocksInY * GobHeight;
+
             for (int level = 0; level < levels; level++)
             {
                 int w = Math.Max(1, width  >> level);
@@ -188,8 +198,16 @@ namespace Ryujinx.Graphics.Texture
                     mipGobBlocksInZ >>= 1;
                 }
 
-                int stride   = BitUtils.AlignUp(w * bytesPerPixel, HostStrideAlignment);
-                int wAligned = BitUtils.AlignUp(w, wAlignment);
+                int stride = BitUtils.AlignUp(w * bytesPerPixel, HostStrideAlignment);
+
+                int alignment = gobWidth;
+
+                if (d < gobBlocksInZ || w <= gobWidth || h <= gobHeight)
+                {
+                    alignment = GobStride / bytesPerPixel;
+                }
+
+                int wAligned = BitUtils.AlignUp(w, alignment);
 
                 BlockLinearLayout layoutConverter = new BlockLinearLayout(
                     wAligned,
diff --git a/Ryujinx.Graphics.Texture/SizeCalculator.cs b/Ryujinx.Graphics.Texture/SizeCalculator.cs
index 11385d28..b02b5a8d 100644
--- a/Ryujinx.Graphics.Texture/SizeCalculator.cs
+++ b/Ryujinx.Graphics.Texture/SizeCalculator.cs
@@ -32,6 +32,9 @@ namespace Ryujinx.Graphics.Texture
             int mipGobBlocksInY = gobBlocksInY;
             int mipGobBlocksInZ = gobBlocksInZ;
 
+            int gobWidth  = (GobStride / bytesPerPixel) * gobBlocksInTileX;
+            int gobHeight = gobBlocksInY * GobHeight;
+
             for (int level = 0; level < levels; level++)
             {
                 int w = Math.Max(1, width  >> level);
@@ -51,7 +54,16 @@ namespace Ryujinx.Graphics.Texture
                     mipGobBlocksInZ >>= 1;
                 }
 
-                int widthInGobs = BitUtils.AlignUp(BitUtils.DivRoundUp(w * bytesPerPixel, GobStride), gobBlocksInTileX);
+                int widthInGobs = BitUtils.DivRoundUp(w * bytesPerPixel, GobStride);
+
+                int alignment = gobBlocksInTileX;
+
+                if (d < gobBlocksInZ || w <= gobWidth || h <= gobHeight)
+                {
+                    alignment = 1;
+                }
+
+                widthInGobs = BitUtils.AlignUp(widthInGobs, alignment);
 
                 int totalBlocksOfGobsInZ = BitUtils.DivRoundUp(d, mipGobBlocksInZ);
                 int totalBlocksOfGobsInY = BitUtils.DivRoundUp(BitUtils.DivRoundUp(h, GobHeight), mipGobBlocksInY);
@@ -88,7 +100,8 @@ namespace Ryujinx.Graphics.Texture
                 depth,
                 blockHeight,
                 gobBlocksInY,
-                gobBlocksInZ);
+                gobBlocksInZ,
+                gobBlocksInTileX);
 
             if (!is3D)
             {
@@ -124,27 +137,37 @@ namespace Ryujinx.Graphics.Texture
             int depth,
             int blockHeight,
             int gobBlocksInY,
-            int gobBlocksInZ)
+            int gobBlocksInZ,
+            int gobBlocksInTileX)
         {
-            height = BitUtils.DivRoundUp(height, blockHeight);
-
-            while (height <= (gobBlocksInY >> 1) * GobHeight && gobBlocksInY != 1)
+            if (gobBlocksInTileX < 2)
             {
-                gobBlocksInY >>= 1;
+                height = BitUtils.DivRoundUp(height, blockHeight);
+
+                while (height <= (gobBlocksInY >> 1) * GobHeight && gobBlocksInY != 1)
+                {
+                    gobBlocksInY >>= 1;
+                }
+
+                while (depth <= (gobBlocksInZ >> 1) && gobBlocksInZ != 1)
+                {
+                    gobBlocksInZ >>= 1;
+                }
+
+                int blockOfGobsSize = gobBlocksInY * gobBlocksInZ * GobSize;
+
+                int sizeInBlockOfGobs = size / blockOfGobsSize;
+
+                if (size != sizeInBlockOfGobs * blockOfGobsSize)
+                {
+                    size = (sizeInBlockOfGobs + 1) * blockOfGobsSize;
+                }
             }
-
-            while (depth <= (gobBlocksInZ >> 1) && gobBlocksInZ != 1)
+            else
             {
-                gobBlocksInZ >>= 1;
-            }
+                int alignment = (gobBlocksInTileX * GobSize) * gobBlocksInY * gobBlocksInZ;
 
-            int blockOfGobsSize = gobBlocksInY * gobBlocksInZ * GobSize;
-
-            int sizeInBlockOfGobs = size / blockOfGobsSize;
-
-            if (size != sizeInBlockOfGobs * blockOfGobsSize)
-            {
-                size = (sizeInBlockOfGobs + 1) * blockOfGobsSize;
+                size = BitUtils.AlignUp(size, alignment);
             }
 
             return size;
@@ -164,12 +187,22 @@ namespace Ryujinx.Graphics.Texture
             width  = BitUtils.DivRoundUp(width,  blockWidth);
             height = BitUtils.DivRoundUp(height, blockHeight);
 
-            int gobWidth = gobBlocksInTileX * (GobStride / bytesPerPixel);
+            int gobWidth  = (GobStride / bytesPerPixel) * gobBlocksInTileX;
+            int gobHeight = gobBlocksInY * GobHeight;
+
+            int alignment = gobWidth;
+
+            if (depth < gobBlocksInZ || width <= gobWidth || height <= gobHeight)
+            {
+                alignment = GobStride / bytesPerPixel;
+            }
+
+            (gobBlocksInY, gobBlocksInZ) = GetMipGobBlockSizes(height, depth, blockHeight, gobBlocksInY, gobBlocksInZ);
 
             int blockOfGobsHeight = gobBlocksInY * GobHeight;
             int blockOfGobsDepth  = gobBlocksInZ;
 
-            width  = BitUtils.AlignUp(width,  gobWidth);
+            width  = BitUtils.AlignUp(width,  alignment);
             height = BitUtils.AlignUp(height, blockOfGobsHeight);
             depth  = BitUtils.AlignUp(depth,  blockOfGobsDepth);