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:
parent
1e5b37c94f
commit
3bad321d2b
4 changed files with 159 additions and 54 deletions
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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;
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Reference in a new issue