diff --git a/Ryujinx.Graphics.Gpu/Image/TextureInfo.cs b/Ryujinx.Graphics.Gpu/Image/TextureInfo.cs
index 2bcafd727f..d27194b85b 100644
--- a/Ryujinx.Graphics.Gpu/Image/TextureInfo.cs
+++ b/Ryujinx.Graphics.Gpu/Image/TextureInfo.cs
@@ -1,4 +1,5 @@
 using Ryujinx.Graphics.GAL;
+using Ryujinx.Graphics.Texture;
 
 namespace Ryujinx.Graphics.Gpu.Image
 {
@@ -92,14 +93,17 @@ namespace Ryujinx.Graphics.Gpu.Image
         /// Texture swizzle for the red color channel.
         /// </summary>
         public SwizzleComponent SwizzleR { get; }
+
         /// <summary>
         /// Texture swizzle for the green color channel.
         /// </summary>
         public SwizzleComponent SwizzleG { get; }
+
         /// <summary>
         /// Texture swizzle for the blue color channel.
         /// </summary>
         public SwizzleComponent SwizzleB { get; }
+
         /// <summary>
         /// Texture swizzle for the alpha color channel.
         /// </summary>
@@ -176,7 +180,19 @@ namespace Ryujinx.Graphics.Gpu.Image
         /// <returns>Texture depth</returns>
         public int GetDepth()
         {
-            return Target == Target.Texture3D ? DepthOrLayers : 1;
+            return GetDepth(Target, DepthOrLayers);
+        }
+
+        /// <summary>
+        /// Gets the real texture depth.
+        /// Returns 1 for any target other than 3D textures.
+        /// </summary>
+        /// <param name="target">Texture target</param>
+        /// <param name="depthOrLayers">Texture depth if the texture is 3D, otherwise ignored</param>
+        /// <returns>Texture depth</returns>
+        public static int GetDepth(Target target, int depthOrLayers)
+        {
+            return target == Target.Texture3D ? depthOrLayers : 1;
         }
 
         /// <summary>
@@ -186,15 +202,27 @@ namespace Ryujinx.Graphics.Gpu.Image
         /// <returns>The number of texture layers</returns>
         public int GetLayers()
         {
-            if (Target == Target.Texture2DArray || Target == Target.Texture2DMultisampleArray)
+            return GetLayers(Target, DepthOrLayers);
+        }
+
+        /// <summary>
+        /// Gets the number of layers of the texture.
+        /// Returns 1 for non-array textures, 6 for cubemap textures, and layer faces for cubemap array textures.
+        /// </summary>
+        /// <param name="target">Texture target</param>
+        /// <param name="depthOrLayers">Texture layers if the is a array texture, ignored otherwise</param>
+        /// <returns>The number of texture layers</returns>
+        public static int GetLayers(Target target, int depthOrLayers)
+        {
+            if (target == Target.Texture2DArray || target == Target.Texture2DMultisampleArray)
             {
-                return DepthOrLayers;
+                return depthOrLayers;
             }
-            else if (Target == Target.CubemapArray)
+            else if (target == Target.CubemapArray)
             {
-                return DepthOrLayers * 6;
+                return depthOrLayers * 6;
             }
-            else if (Target == Target.Cubemap)
+            else if (target == Target.Cubemap)
             {
                 return 6;
             }
@@ -203,5 +231,41 @@ namespace Ryujinx.Graphics.Gpu.Image
                 return 1;
             }
         }
+
+        /// <summary>
+        /// Calculates the size information from the texture information.
+        /// </summary>
+        /// <param name="layerSize">Optional size of each texture layer in bytes</param>
+        /// <returns>Texture size information</returns>
+        public SizeInfo CalculateSizeInfo(int layerSize = 0)
+        {
+            if (Target == Target.TextureBuffer)
+            {
+                return new SizeInfo(Width * FormatInfo.BytesPerPixel);
+            }
+            else if (IsLinear)
+            {
+                return SizeCalculator.GetLinearTextureSize(
+                    Stride,
+                    Height,
+                    FormatInfo.BlockHeight);
+            }
+            else
+            {
+                return SizeCalculator.GetBlockLinearTextureSize(
+                    Width,
+                    Height,
+                    GetDepth(),
+                    Levels,
+                    GetLayers(),
+                    FormatInfo.BlockWidth,
+                    FormatInfo.BlockHeight,
+                    FormatInfo.BytesPerPixel,
+                    GobBlocksInY,
+                    GobBlocksInZ,
+                    GobBlocksInTileX,
+                    layerSize);
+            }
+        }
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Image/TextureManager.cs b/Ryujinx.Graphics.Gpu/Image/TextureManager.cs
index 993218ce09..5bad3952d4 100644
--- a/Ryujinx.Graphics.Gpu/Image/TextureManager.cs
+++ b/Ryujinx.Graphics.Gpu/Image/TextureManager.cs
@@ -514,7 +514,7 @@ namespace Ryujinx.Graphics.Gpu.Image
                 flags |= TextureSearchFlags.WithUpscale;
             }
 
-            Texture texture = FindOrCreateTexture(info, flags, sizeHint);
+            Texture texture = FindOrCreateTexture(info, flags, 0, sizeHint);
 
             texture.SynchronizeMemory();
 
@@ -598,7 +598,9 @@ namespace Ryujinx.Graphics.Gpu.Image
                 target,
                 formatInfo);
 
-            Texture texture = FindOrCreateTexture(info, TextureSearchFlags.WithUpscale, sizeHint);
+            int layerSize = !isLinear ? colorState.LayerSize * 4 : 0;
+
+            Texture texture = FindOrCreateTexture(info, TextureSearchFlags.WithUpscale, layerSize, sizeHint);
 
             texture.SynchronizeMemory();
 
@@ -648,7 +650,7 @@ namespace Ryujinx.Graphics.Gpu.Image
                 target,
                 formatInfo);
 
-            Texture texture = FindOrCreateTexture(info, TextureSearchFlags.WithUpscale, sizeHint);
+            Texture texture = FindOrCreateTexture(info, TextureSearchFlags.WithUpscale, dsState.LayerSize * 4, sizeHint);
 
             texture.SynchronizeMemory();
 
@@ -660,9 +662,10 @@ namespace Ryujinx.Graphics.Gpu.Image
         /// </summary>
         /// <param name="info">Texture information of the texture to be found or created</param>
         /// <param name="flags">The texture search flags, defines texture comparison rules</param>
+        /// <param name="layerSize">Size in bytes of a single texture layer</param>
         /// <param name="sizeHint">A hint indicating the minimum used size for the texture</param>
         /// <returns>The texture</returns>
-        public Texture FindOrCreateTexture(TextureInfo info, TextureSearchFlags flags = TextureSearchFlags.None, Size? sizeHint = null)
+        public Texture FindOrCreateTexture(TextureInfo info, TextureSearchFlags flags = TextureSearchFlags.None, int layerSize = 0, Size? sizeHint = null)
         {
             bool isSamplerTexture = (flags & TextureSearchFlags.ForSampler) != 0;
 
@@ -722,34 +725,7 @@ namespace Ryujinx.Graphics.Gpu.Image
             }
 
             // Calculate texture sizes, used to find all overlapping textures.
-            SizeInfo sizeInfo;
-
-            if (info.Target == Target.TextureBuffer)
-            {
-                sizeInfo = new SizeInfo(info.Width * info.FormatInfo.BytesPerPixel);
-            }
-            else if (info.IsLinear)
-            {
-                sizeInfo = SizeCalculator.GetLinearTextureSize(
-                    info.Stride,
-                    info.Height,
-                    info.FormatInfo.BlockHeight);
-            }
-            else
-            {
-                sizeInfo = SizeCalculator.GetBlockLinearTextureSize(
-                    info.Width,
-                    info.Height,
-                    info.GetDepth(),
-                    info.Levels,
-                    info.GetLayers(),
-                    info.FormatInfo.BlockWidth,
-                    info.FormatInfo.BlockHeight,
-                    info.FormatInfo.BytesPerPixel,
-                    info.GobBlocksInY,
-                    info.GobBlocksInZ,
-                    info.GobBlocksInTileX);
-            }
+            SizeInfo sizeInfo = info.CalculateSizeInfo(layerSize);
 
             // Find view compatible matches.
             ulong size = (ulong)sizeInfo.TotalSize;
diff --git a/Ryujinx.Graphics.Gpu/Image/TexturePool.cs b/Ryujinx.Graphics.Gpu/Image/TexturePool.cs
index 9c7e849b82..333ebaedc0 100644
--- a/Ryujinx.Graphics.Gpu/Image/TexturePool.cs
+++ b/Ryujinx.Graphics.Gpu/Image/TexturePool.cs
@@ -1,6 +1,8 @@
 using Ryujinx.Common.Logging;
 using Ryujinx.Graphics.GAL;
 using Ryujinx.Graphics.Gpu.Memory;
+using Ryujinx.Graphics.Texture;
+using System;
 using System.Collections.Generic;
 
 namespace Ryujinx.Graphics.Gpu.Image
@@ -50,7 +52,7 @@ namespace Ryujinx.Graphics.Gpu.Image
             {
                 TextureDescriptor descriptor = GetDescriptor(id);
 
-                TextureInfo info = GetInfo(descriptor);
+                TextureInfo info = GetInfo(descriptor, out int layerSize);
 
                 // Bad address. We can't add a texture with a invalid address
                 // to the cache.
@@ -59,7 +61,7 @@ namespace Ryujinx.Graphics.Gpu.Image
                     return null;
                 }
 
-                texture = Context.Methods.TextureManager.FindOrCreateTexture(info, TextureSearchFlags.ForSampler);
+                texture = Context.Methods.TextureManager.FindOrCreateTexture(info, TextureSearchFlags.ForSampler, layerSize);
 
                 texture.IncrementReferenceCount();
 
@@ -121,7 +123,7 @@ namespace Ryujinx.Graphics.Gpu.Image
 
                     // If the descriptors are the same, the texture is the same,
                     // we don't need to remove as it was not modified. Just continue.
-                    if (texture.IsExactMatch(GetInfo(descriptor), TextureSearchFlags.Strict) != TextureMatchQuality.NoMatch)
+                    if (texture.IsExactMatch(GetInfo(descriptor, out _), TextureSearchFlags.Strict) != TextureMatchQuality.NoMatch)
                     {
                         continue;
                     }
@@ -137,10 +139,12 @@ namespace Ryujinx.Graphics.Gpu.Image
         /// Gets texture information from a texture descriptor.
         /// </summary>
         /// <param name="descriptor">The texture descriptor</param>
+        /// <param name="layerSize">Layer size for textures using a sub-range of mipmap levels, otherwise 0</param>
         /// <returns>The texture information</returns>
-        private TextureInfo GetInfo(TextureDescriptor descriptor)
+        private TextureInfo GetInfo(TextureDescriptor descriptor, out int layerSize)
         {
             ulong address = Context.MemoryManager.Translate(descriptor.UnpackAddress());
+            bool addressIsValid = address != MemoryManager.PteUnmapped;
 
             int width         = descriptor.UnpackWidth();
             int height        = descriptor.UnpackHeight();
@@ -181,7 +185,7 @@ namespace Ryujinx.Graphics.Gpu.Image
 
             if (!FormatTable.TryGetTextureFormat(format, srgb, out FormatInfo formatInfo))
             {
-                if ((long)address > 0L && (int)format > 0)
+                if (addressIsValid && (int)format > 0)
                 {
                     Logger.Error?.Print(LogClass.Gpu, $"Invalid texture format 0x{format:X} (sRGB: {srgb}).");
                 }
@@ -194,6 +198,53 @@ namespace Ryujinx.Graphics.Gpu.Image
 
             int gobBlocksInTileX = descriptor.UnpackGobBlocksInTileX();
 
+            layerSize = 0;
+
+            int minLod = descriptor.UnpackBaseLevel();
+            int maxLod = descriptor.UnpackMaxLevelInclusive();
+
+            // Linear textures don't support mipmaps, so we don't handle this case here.
+            if ((minLod != 0 || maxLod + 1 != levels) && target != Target.TextureBuffer && !isLinear && addressIsValid)
+            {
+                int depth  = TextureInfo.GetDepth(target, depthOrLayers);
+                int layers = TextureInfo.GetLayers(target, depthOrLayers);
+
+                SizeInfo sizeInfo = SizeCalculator.GetBlockLinearTextureSize(
+                    width,
+                    height,
+                    depth,
+                    levels,
+                    layers,
+                    formatInfo.BlockWidth,
+                    formatInfo.BlockHeight,
+                    formatInfo.BytesPerPixel,
+                    gobBlocksInY,
+                    gobBlocksInZ,
+                    gobBlocksInTileX);
+
+                layerSize = sizeInfo.LayerSize;
+
+                if (minLod != 0)
+                {
+                    // If the base level is not zero, we additionally add the mip level offset
+                    // to the address, this allows the texture manager to find the base level from the
+                    // address if there is a overlapping texture on the cache that can contain the new texture.
+                    address += (ulong)sizeInfo.GetMipOffset(minLod);
+
+                    width  = Math.Max(1, width  >> minLod);
+                    height = Math.Max(1, height >> minLod);
+
+                    if (target == Target.Texture3D)
+                    {
+                        depthOrLayers = Math.Max(1, depthOrLayers >> minLod);
+                    }
+
+                    (gobBlocksInY, gobBlocksInZ) = SizeCalculator.GetMipGobBlockSizes(height, depth, formatInfo.BlockHeight, gobBlocksInY, gobBlocksInZ);
+                }
+
+                levels = (maxLod - minLod) + 1;
+            }
+
             SwizzleComponent swizzleR = descriptor.UnpackSwizzleR().Convert();
             SwizzleComponent swizzleG = descriptor.UnpackSwizzleG().Convert();
             SwizzleComponent swizzleB = descriptor.UnpackSwizzleB().Convert();
diff --git a/Ryujinx.Graphics.Texture/SizeCalculator.cs b/Ryujinx.Graphics.Texture/SizeCalculator.cs
index 9339ba12e8..2dc6086972 100644
--- a/Ryujinx.Graphics.Texture/SizeCalculator.cs
+++ b/Ryujinx.Graphics.Texture/SizeCalculator.cs
@@ -20,7 +20,8 @@ namespace Ryujinx.Graphics.Texture
             int bytesPerPixel,
             int gobBlocksInY,
             int gobBlocksInZ,
-            int gobBlocksInTileX)
+            int gobBlocksInTileX,
+            int gpuLayerSize = 0)
         {
             bool is3D = depth > 1;
 
@@ -94,14 +95,29 @@ namespace Ryujinx.Graphics.Texture
                 layerSize += totalBlocksOfGobsInZ * totalBlocksOfGobsInY * robSize;
             }
 
-            layerSize = AlignLayerSize(
-                layerSize,
-                height,
-                depth,
-                blockHeight,
-                gobBlocksInY,
-                gobBlocksInZ,
-                gobBlocksInTileX);
+            if (layers > 1)
+            {
+                layerSize = AlignLayerSize(
+                    layerSize,
+                    height,
+                    depth,
+                    blockHeight,
+                    gobBlocksInY,
+                    gobBlocksInZ,
+                    gobBlocksInTileX);
+            }
+
+            int totalSize;
+
+            if (layerSize < gpuLayerSize)
+            {
+                totalSize = (layers - 1) * gpuLayerSize + layerSize;
+                layerSize = gpuLayerSize;
+            }
+            else
+            {
+                totalSize = layerSize * layers;
+            }
 
             if (!is3D)
             {
@@ -117,8 +133,6 @@ namespace Ryujinx.Graphics.Texture
                 }
             }
 
-            int totalSize = layerSize * layers;
-
             return new SizeInfo(mipOffsets, allOffsets, levels, layerSize, totalSize);
         }