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.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);
}
}
}
}

View file

@ -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;

View file

@ -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();

View file

@ -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);
}