Fix mipmap base level being ignored for sampled textures and images (#1911)

* Fix mipmap base level being ignored for sampled textures and images

* Fix layer size and max level for textures

* Missing XML doc + reorder comments
This commit is contained in:
gdkchan 2021-01-15 15:14:00 -03:00 committed by GitHub
parent 1e5b37c94f
commit 3bad321d2b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 159 additions and 54 deletions

View file

@ -1,4 +1,5 @@
using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Texture;
namespace Ryujinx.Graphics.Gpu.Image namespace Ryujinx.Graphics.Gpu.Image
{ {
@ -92,14 +93,17 @@ namespace Ryujinx.Graphics.Gpu.Image
/// Texture swizzle for the red color channel. /// Texture swizzle for the red color channel.
/// </summary> /// </summary>
public SwizzleComponent SwizzleR { get; } public SwizzleComponent SwizzleR { get; }
/// <summary> /// <summary>
/// Texture swizzle for the green color channel. /// Texture swizzle for the green color channel.
/// </summary> /// </summary>
public SwizzleComponent SwizzleG { get; } public SwizzleComponent SwizzleG { get; }
/// <summary> /// <summary>
/// Texture swizzle for the blue color channel. /// Texture swizzle for the blue color channel.
/// </summary> /// </summary>
public SwizzleComponent SwizzleB { get; } public SwizzleComponent SwizzleB { get; }
/// <summary> /// <summary>
/// Texture swizzle for the alpha color channel. /// Texture swizzle for the alpha color channel.
/// </summary> /// </summary>
@ -176,7 +180,19 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <returns>Texture depth</returns> /// <returns>Texture depth</returns>
public int GetDepth() 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> /// <summary>
@ -186,15 +202,27 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <returns>The number of texture layers</returns> /// <returns>The number of texture layers</returns>
public int GetLayers() 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; return 6;
} }
@ -203,5 +231,41 @@ namespace Ryujinx.Graphics.Gpu.Image
return 1; 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);
}
}
} }
} }

View file

@ -514,7 +514,7 @@ namespace Ryujinx.Graphics.Gpu.Image
flags |= TextureSearchFlags.WithUpscale; flags |= TextureSearchFlags.WithUpscale;
} }
Texture texture = FindOrCreateTexture(info, flags, sizeHint); Texture texture = FindOrCreateTexture(info, flags, 0, sizeHint);
texture.SynchronizeMemory(); texture.SynchronizeMemory();
@ -598,7 +598,9 @@ namespace Ryujinx.Graphics.Gpu.Image
target, target,
formatInfo); 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(); texture.SynchronizeMemory();
@ -648,7 +650,7 @@ namespace Ryujinx.Graphics.Gpu.Image
target, target,
formatInfo); formatInfo);
Texture texture = FindOrCreateTexture(info, TextureSearchFlags.WithUpscale, sizeHint); Texture texture = FindOrCreateTexture(info, TextureSearchFlags.WithUpscale, dsState.LayerSize * 4, sizeHint);
texture.SynchronizeMemory(); texture.SynchronizeMemory();
@ -660,9 +662,10 @@ namespace Ryujinx.Graphics.Gpu.Image
/// </summary> /// </summary>
/// <param name="info">Texture information of the texture to be found or created</param> /// <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="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> /// <param name="sizeHint">A hint indicating the minimum used size for the texture</param>
/// <returns>The texture</returns> /// <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; bool isSamplerTexture = (flags & TextureSearchFlags.ForSampler) != 0;
@ -722,34 +725,7 @@ namespace Ryujinx.Graphics.Gpu.Image
} }
// Calculate texture sizes, used to find all overlapping textures. // Calculate texture sizes, used to find all overlapping textures.
SizeInfo sizeInfo; SizeInfo sizeInfo = info.CalculateSizeInfo(layerSize);
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);
}
// Find view compatible matches. // Find view compatible matches.
ulong size = (ulong)sizeInfo.TotalSize; ulong size = (ulong)sizeInfo.TotalSize;

View file

@ -1,6 +1,8 @@
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Memory; using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Graphics.Texture;
using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace Ryujinx.Graphics.Gpu.Image namespace Ryujinx.Graphics.Gpu.Image
@ -50,7 +52,7 @@ namespace Ryujinx.Graphics.Gpu.Image
{ {
TextureDescriptor descriptor = GetDescriptor(id); 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 // Bad address. We can't add a texture with a invalid address
// to the cache. // to the cache.
@ -59,7 +61,7 @@ namespace Ryujinx.Graphics.Gpu.Image
return null; return null;
} }
texture = Context.Methods.TextureManager.FindOrCreateTexture(info, TextureSearchFlags.ForSampler); texture = Context.Methods.TextureManager.FindOrCreateTexture(info, TextureSearchFlags.ForSampler, layerSize);
texture.IncrementReferenceCount(); texture.IncrementReferenceCount();
@ -121,7 +123,7 @@ namespace Ryujinx.Graphics.Gpu.Image
// If the descriptors are the same, the texture is the same, // If the descriptors are the same, the texture is the same,
// we don't need to remove as it was not modified. Just continue. // 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; continue;
} }
@ -137,10 +139,12 @@ namespace Ryujinx.Graphics.Gpu.Image
/// Gets texture information from a texture descriptor. /// Gets texture information from a texture descriptor.
/// </summary> /// </summary>
/// <param name="descriptor">The texture descriptor</param> /// <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> /// <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()); ulong address = Context.MemoryManager.Translate(descriptor.UnpackAddress());
bool addressIsValid = address != MemoryManager.PteUnmapped;
int width = descriptor.UnpackWidth(); int width = descriptor.UnpackWidth();
int height = descriptor.UnpackHeight(); int height = descriptor.UnpackHeight();
@ -181,7 +185,7 @@ namespace Ryujinx.Graphics.Gpu.Image
if (!FormatTable.TryGetTextureFormat(format, srgb, out FormatInfo formatInfo)) 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})."); 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(); 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 swizzleR = descriptor.UnpackSwizzleR().Convert();
SwizzleComponent swizzleG = descriptor.UnpackSwizzleG().Convert(); SwizzleComponent swizzleG = descriptor.UnpackSwizzleG().Convert();
SwizzleComponent swizzleB = descriptor.UnpackSwizzleB().Convert(); SwizzleComponent swizzleB = descriptor.UnpackSwizzleB().Convert();

View file

@ -20,7 +20,8 @@ namespace Ryujinx.Graphics.Texture
int bytesPerPixel, int bytesPerPixel,
int gobBlocksInY, int gobBlocksInY,
int gobBlocksInZ, int gobBlocksInZ,
int gobBlocksInTileX) int gobBlocksInTileX,
int gpuLayerSize = 0)
{ {
bool is3D = depth > 1; bool is3D = depth > 1;
@ -94,14 +95,29 @@ namespace Ryujinx.Graphics.Texture
layerSize += totalBlocksOfGobsInZ * totalBlocksOfGobsInY * robSize; layerSize += totalBlocksOfGobsInZ * totalBlocksOfGobsInY * robSize;
} }
layerSize = AlignLayerSize( if (layers > 1)
layerSize, {
height, layerSize = AlignLayerSize(
depth, layerSize,
blockHeight, height,
gobBlocksInY, depth,
gobBlocksInZ, blockHeight,
gobBlocksInTileX); gobBlocksInY,
gobBlocksInZ,
gobBlocksInTileX);
}
int totalSize;
if (layerSize < gpuLayerSize)
{
totalSize = (layers - 1) * gpuLayerSize + layerSize;
layerSize = gpuLayerSize;
}
else
{
totalSize = layerSize * layers;
}
if (!is3D) if (!is3D)
{ {
@ -117,8 +133,6 @@ namespace Ryujinx.Graphics.Texture
} }
} }
int totalSize = layerSize * layers;
return new SizeInfo(mipOffsets, allOffsets, levels, layerSize, totalSize); return new SizeInfo(mipOffsets, allOffsets, levels, layerSize, totalSize);
} }