Ryujinx/Ryujinx.Graphics.Texture/SizeInfo.cs
riperiperi 15e7fe3ac9
Avoid deleting textures when their data does not overlap. (#2601)
* Avoid deleting textures when their data does not overlap.

It's possible that while two textures start and end addresses indicate an overlap, that the actual data contained within them is sparse due to a layer stride. One such possibility is array slices of a cubemap at different mip levels - they overlap on a whole, but the actual texture data fills the gaps between each other's layers rather than actually overlapping.

This fixes issues with UE4 games having incorrect lighting (solid white screen or really dark shadows). There are still remaining issues with games that use the 3D texture prebaked lighting, such as THPS1+2.

This PR also fixes a bug with TexturePool's resized texture handling where the base level in the descriptor was not considered.

* AllRegions granularity for 3d textures is now by level rather than by slice.

* Address feedback
2021-08-29 16:22:13 -03:00

114 lines
2.9 KiB
C#

using System;
using System.Collections.Generic;
namespace Ryujinx.Graphics.Texture
{
public struct SizeInfo
{
private readonly int[] _mipOffsets;
private readonly int _levels;
private readonly int _depth;
private readonly bool _is3D;
public readonly int[] AllOffsets;
public readonly int[] SliceSizes;
public int LayerSize { get; }
public int TotalSize { get; }
public SizeInfo(int size)
{
_mipOffsets = new int[] { 0 };
AllOffsets = new int[] { 0 };
SliceSizes = new int[] { size };
_depth = 1;
_levels = 1;
LayerSize = size;
TotalSize = size;
_is3D = false;
}
internal SizeInfo(
int[] mipOffsets,
int[] allOffsets,
int[] sliceSizes,
int depth,
int levels,
int layerSize,
int totalSize,
bool is3D)
{
_mipOffsets = mipOffsets;
AllOffsets = allOffsets;
SliceSizes = sliceSizes;
_depth = depth;
_levels = levels;
LayerSize = layerSize;
TotalSize = totalSize;
_is3D = is3D;
}
public int GetMipOffset(int level)
{
if ((uint)level >= _mipOffsets.Length)
{
throw new ArgumentOutOfRangeException(nameof(level));
}
return _mipOffsets[level];
}
public bool FindView(int offset, out int firstLayer, out int firstLevel)
{
int index = Array.BinarySearch(AllOffsets, offset);
if (index < 0)
{
firstLayer = 0;
firstLevel = 0;
return false;
}
if (_is3D)
{
firstLayer = index;
firstLevel = 0;
int levelDepth = _depth;
while (firstLayer >= levelDepth)
{
firstLayer -= levelDepth;
firstLevel++;
levelDepth = Math.Max(levelDepth >> 1, 1);
}
}
else
{
firstLayer = index / _levels;
firstLevel = index - (firstLayer * _levels);
}
return true;
}
public IEnumerable<Region> AllRegions()
{
if (_is3D)
{
for (int i = 0; i < _mipOffsets.Length; i++)
{
yield return new Region(_mipOffsets[i], SliceSizes[i]);
}
}
else
{
for (int i = 0; i < AllOffsets.Length; i++)
{
yield return new Region(AllOffsets[i], SliceSizes[i % _levels]);
}
}
}
}
}