aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorriperiperi <rhy3756547@hotmail.com>2021-08-29 20:22:13 +0100
committerGitHub <noreply@github.com>2021-08-29 16:22:13 -0300
commit15e7fe3ac940a1768a25326e66683ad0f23127e0 (patch)
treee596124483034a1b6ab24823745d8c9700d11f57
parent54adc5f9fb65f4b03bc28da5899d2413a84f66c2 (diff)
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
-rw-r--r--Ryujinx.Graphics.Gpu/Image/Texture.cs32
-rw-r--r--Ryujinx.Graphics.Gpu/Image/TextureCache.cs7
-rw-r--r--Ryujinx.Graphics.Gpu/Image/TexturePool.cs5
-rw-r--r--Ryujinx.Graphics.Texture/Region.cs14
-rw-r--r--Ryujinx.Graphics.Texture/SizeInfo.cs19
5 files changed, 75 insertions, 2 deletions
diff --git a/Ryujinx.Graphics.Gpu/Image/Texture.cs b/Ryujinx.Graphics.Gpu/Image/Texture.cs
index 0a083ebc..c9c3c59a 100644
--- a/Ryujinx.Graphics.Gpu/Image/Texture.cs
+++ b/Ryujinx.Graphics.Gpu/Image/Texture.cs
@@ -9,6 +9,7 @@ using Ryujinx.Memory.Range;
using System;
using System.Collections.Generic;
using System.Diagnostics;
+using System.Linq;
namespace Ryujinx.Graphics.Gpu.Image
{
@@ -1296,6 +1297,37 @@ namespace Ryujinx.Graphics.Gpu.Image
}
/// <summary>
+ /// Determine if any of this texture's data overlaps with another.
+ /// </summary>
+ /// <param name="texture">The texture to check against</param>
+ /// <returns>True if any slice of the textures overlap, false otherwise</returns>
+ public bool DataOverlaps(Texture texture)
+ {
+ if (texture._sizeInfo.AllOffsets.Length == 1 && _sizeInfo.AllOffsets.Length == 1)
+ {
+ return Range.OverlapsWith(texture.Range);
+ }
+
+ MultiRange otherRange = texture.Range;
+
+ IEnumerable<MultiRange> regions = _sizeInfo.AllRegions().Select((region) => Range.GetSlice((ulong)region.Offset, (ulong)region.Size));
+ IEnumerable<MultiRange> otherRegions = texture._sizeInfo.AllRegions().Select((region) => otherRange.GetSlice((ulong)region.Offset, (ulong)region.Size));
+
+ foreach (MultiRange region in regions)
+ {
+ foreach (MultiRange otherRegion in otherRegions)
+ {
+ if (region.OverlapsWith(otherRegion))
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /// <summary>
/// Increments the texture reference count.
/// </summary>
public void IncrementReferenceCount()
diff --git a/Ryujinx.Graphics.Gpu/Image/TextureCache.cs b/Ryujinx.Graphics.Gpu/Image/TextureCache.cs
index 37682b65..44c974e8 100644
--- a/Ryujinx.Graphics.Gpu/Image/TextureCache.cs
+++ b/Ryujinx.Graphics.Gpu/Image/TextureCache.cs
@@ -636,6 +636,13 @@ namespace Ryujinx.Graphics.Gpu.Image
continue;
}
+ if (!texture.DataOverlaps(overlap))
+ {
+ // Allow textures to overlap if their data does not actually overlap.
+ // This typically happens when mip level subranges of a layered texture are used. (each texture fills the gaps of the others)
+ continue;
+ }
+
// The overlap texture is going to contain garbage data after we draw, or is generally incompatible.
// If the texture cannot be entirely contained in the new address space, and one of its view children is compatible with us,
// it must be flushed before removal, so that the data is not lost.
diff --git a/Ryujinx.Graphics.Gpu/Image/TexturePool.cs b/Ryujinx.Graphics.Gpu/Image/TexturePool.cs
index bcce443c..5b5c5ab0 100644
--- a/Ryujinx.Graphics.Gpu/Image/TexturePool.cs
+++ b/Ryujinx.Graphics.Gpu/Image/TexturePool.cs
@@ -85,8 +85,9 @@ namespace Ryujinx.Graphics.Gpu.Image
TextureDescriptor descriptor = GetDescriptor(id);
- int width = descriptor.UnpackWidth();
- int height = descriptor.UnpackHeight();
+ int baseLevel = descriptor.UnpackBaseLevel();
+ int width = Math.Max(1, descriptor.UnpackWidth() >> baseLevel);
+ int height = Math.Max(1, descriptor.UnpackHeight() >> baseLevel);
if (texture.Info.Width != width || texture.Info.Height != height)
{
diff --git a/Ryujinx.Graphics.Texture/Region.cs b/Ryujinx.Graphics.Texture/Region.cs
new file mode 100644
index 00000000..a60951e3
--- /dev/null
+++ b/Ryujinx.Graphics.Texture/Region.cs
@@ -0,0 +1,14 @@
+namespace Ryujinx.Graphics.Texture
+{
+ public struct Region
+ {
+ public int Offset { get; }
+ public int Size { get; }
+
+ public Region(int offset, int size)
+ {
+ Offset = offset;
+ Size = size;
+ }
+ }
+}
diff --git a/Ryujinx.Graphics.Texture/SizeInfo.cs b/Ryujinx.Graphics.Texture/SizeInfo.cs
index f518ee4b..880d677b 100644
--- a/Ryujinx.Graphics.Texture/SizeInfo.cs
+++ b/Ryujinx.Graphics.Texture/SizeInfo.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
namespace Ryujinx.Graphics.Texture
{
@@ -91,5 +92,23 @@ namespace Ryujinx.Graphics.Texture
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]);
+ }
+ }
+ }
}
} \ No newline at end of file