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.
///
public SwizzleComponent SwizzleR { get; }
+
///
/// Texture swizzle for the green color channel.
///
public SwizzleComponent SwizzleG { get; }
+
///
/// Texture swizzle for the blue color channel.
///
public SwizzleComponent SwizzleB { get; }
+
///
/// Texture swizzle for the alpha color channel.
///
@@ -176,7 +180,19 @@ namespace Ryujinx.Graphics.Gpu.Image
/// Texture depth
public int GetDepth()
{
- return Target == Target.Texture3D ? DepthOrLayers : 1;
+ return GetDepth(Target, DepthOrLayers);
+ }
+
+ ///
+ /// Gets the real texture depth.
+ /// Returns 1 for any target other than 3D textures.
+ ///
+ /// Texture target
+ /// Texture depth if the texture is 3D, otherwise ignored
+ /// Texture depth
+ public static int GetDepth(Target target, int depthOrLayers)
+ {
+ return target == Target.Texture3D ? depthOrLayers : 1;
}
///
@@ -186,15 +202,27 @@ namespace Ryujinx.Graphics.Gpu.Image
/// The number of texture layers
public int GetLayers()
{
- if (Target == Target.Texture2DArray || Target == Target.Texture2DMultisampleArray)
+ return GetLayers(Target, DepthOrLayers);
+ }
+
+ ///
+ /// 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.
+ ///
+ /// Texture target
+ /// Texture layers if the is a array texture, ignored otherwise
+ /// The number of texture layers
+ 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;
}
}
+
+ ///
+ /// Calculates the size information from the texture information.
+ ///
+ /// Optional size of each texture layer in bytes
+ /// Texture size information
+ 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
///
/// Texture information of the texture to be found or created
/// The texture search flags, defines texture comparison rules
+ /// Size in bytes of a single texture layer
/// A hint indicating the minimum used size for the texture
/// The texture
- 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.
///
/// The texture descriptor
+ /// Layer size for textures using a sub-range of mipmap levels, otherwise 0
/// The texture information
- 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);
}