forked from Mirror/Ryujinx
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.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
Reference in a new issue