From f78bcb80485419466fde56812425a53e22705765 Mon Sep 17 00:00:00 2001
From: gdkchan <gab.dark.100@gmail.com>
Date: Wed, 3 Nov 2021 20:58:24 -0300
Subject: [PATCH] Clamp number of mipmap levels to avoid API errors due to
 invalid textures (#2808)

---
 Ryujinx.Graphics.GAL/TextureCreateInfo.cs     | 20 +++++++++++++++++++
 .../Image/TextureStorage.cs                   | 16 ++++++++-------
 Ryujinx.Graphics.OpenGL/Image/TextureView.cs  | 16 ++++++++++-----
 3 files changed, 40 insertions(+), 12 deletions(-)

diff --git a/Ryujinx.Graphics.GAL/TextureCreateInfo.cs b/Ryujinx.Graphics.GAL/TextureCreateInfo.cs
index eedf58a0..3ccfb700 100644
--- a/Ryujinx.Graphics.GAL/TextureCreateInfo.cs
+++ b/Ryujinx.Graphics.GAL/TextureCreateInfo.cs
@@ -1,5 +1,6 @@
 using Ryujinx.Common;
 using System;
+using System.Numerics;
 
 namespace Ryujinx.Graphics.GAL
 {
@@ -112,6 +113,25 @@ namespace Ryujinx.Graphics.GAL
             return 1;
         }
 
+        public readonly int GetLevelsClamped()
+        {
+            int maxSize = Width;
+
+            if (Target != Target.Texture1D &&
+                Target != Target.Texture1DArray)
+            {
+                maxSize = Math.Max(maxSize, Height);
+            }
+
+            if (Target == Target.Texture3D)
+            {
+                maxSize = Math.Max(maxSize, Depth);
+            }
+
+            int maxLevels = BitOperations.Log2((uint)maxSize) + 1;
+            return Math.Min(Levels, maxLevels);
+        }
+
         private static int GetLevelSize(int size, int level)
         {
             return Math.Max(1, size >> level);
diff --git a/Ryujinx.Graphics.OpenGL/Image/TextureStorage.cs b/Ryujinx.Graphics.OpenGL/Image/TextureStorage.cs
index ce4616e4..215446ec 100644
--- a/Ryujinx.Graphics.OpenGL/Image/TextureStorage.cs
+++ b/Ryujinx.Graphics.OpenGL/Image/TextureStorage.cs
@@ -50,12 +50,14 @@ namespace Ryujinx.Graphics.OpenGL.Image
                 internalFormat = (SizedInternalFormat)format.PixelInternalFormat;
             }
 
+            int levels = Info.GetLevelsClamped();
+
             switch (Info.Target)
             {
                 case Target.Texture1D:
                     GL.TexStorage1D(
                         TextureTarget1d.Texture1D,
-                        Info.Levels,
+                        levels,
                         internalFormat,
                         Info.Width);
                     break;
@@ -63,7 +65,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
                 case Target.Texture1DArray:
                     GL.TexStorage2D(
                         TextureTarget2d.Texture1DArray,
-                        Info.Levels,
+                        levels,
                         internalFormat,
                         Info.Width,
                         Info.Height);
@@ -72,7 +74,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
                 case Target.Texture2D:
                     GL.TexStorage2D(
                         TextureTarget2d.Texture2D,
-                        Info.Levels,
+                        levels,
                         internalFormat,
                         Info.Width,
                         Info.Height);
@@ -81,7 +83,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
                 case Target.Texture2DArray:
                     GL.TexStorage3D(
                         TextureTarget3d.Texture2DArray,
-                        Info.Levels,
+                        levels,
                         internalFormat,
                         Info.Width,
                         Info.Height,
@@ -112,7 +114,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
                 case Target.Texture3D:
                     GL.TexStorage3D(
                         TextureTarget3d.Texture3D,
-                        Info.Levels,
+                        levels,
                         internalFormat,
                         Info.Width,
                         Info.Height,
@@ -122,7 +124,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
                 case Target.Cubemap:
                     GL.TexStorage2D(
                         TextureTarget2d.TextureCubeMap,
-                        Info.Levels,
+                        levels,
                         internalFormat,
                         Info.Width,
                         Info.Height);
@@ -131,7 +133,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
                 case Target.CubemapArray:
                     GL.TexStorage3D(
                         (TextureTarget3d)All.TextureCubeMapArray,
-                        Info.Levels,
+                        levels,
                         internalFormat,
                         Info.Width,
                         Info.Height,
diff --git a/Ryujinx.Graphics.OpenGL/Image/TextureView.cs b/Ryujinx.Graphics.OpenGL/Image/TextureView.cs
index a70ab595..137f7d6e 100644
--- a/Ryujinx.Graphics.OpenGL/Image/TextureView.cs
+++ b/Ryujinx.Graphics.OpenGL/Image/TextureView.cs
@@ -48,13 +48,15 @@ namespace Ryujinx.Graphics.OpenGL.Image
                 pixelInternalFormat = format.PixelInternalFormat;
             }
 
+            int levels = Info.GetLevelsClamped();
+
             GL.TextureView(
                 Handle,
                 target,
                 _parent.Handle,
                 pixelInternalFormat,
                 FirstLevel,
-                Info.Levels,
+                levels,
                 FirstLayer,
                 Info.GetLayers());
 
@@ -81,7 +83,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
 
             GL.TexParameter(target, TextureParameterName.TextureSwizzleRgba, swizzleRgba);
 
-            int maxLevel = Info.Levels - 1;
+            int maxLevel = levels - 1;
 
             if (maxLevel < 0)
             {
@@ -122,8 +124,9 @@ namespace Ryujinx.Graphics.OpenGL.Image
         public unsafe ReadOnlySpan<byte> GetData()
         {
             int size = 0;
+            int levels = Info.GetLevelsClamped();
 
-            for (int level = 0; level < Info.Levels; level++)
+            for (int level = 0; level < levels; level++)
             {
                 size += Info.GetMipSize(level);
             }
@@ -227,7 +230,9 @@ namespace Ryujinx.Graphics.OpenGL.Image
                 faces = 6;
             }
 
-            for (int level = 0; level < Info.Levels; level++)
+            int levels = Info.GetLevelsClamped();
+
+            for (int level = 0; level < levels; level++)
             {
                 for (int face = 0; face < faces; face++)
                 {
@@ -465,10 +470,11 @@ namespace Ryujinx.Graphics.OpenGL.Image
             int width  = Info.Width;
             int height = Info.Height;
             int depth  = Info.Depth;
+            int levels = Info.GetLevelsClamped();
 
             int offset = 0;
 
-            for (int level = 0; level < Info.Levels; level++)
+            for (int level = 0; level < levels; level++)
             {
                 int mipSize = Info.GetMipSize(level);