aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Graphics.Gpu/Image/TextureManager.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Ryujinx.Graphics.Gpu/Image/TextureManager.cs')
-rw-r--r--Ryujinx.Graphics.Gpu/Image/TextureManager.cs188
1 files changed, 148 insertions, 40 deletions
diff --git a/Ryujinx.Graphics.Gpu/Image/TextureManager.cs b/Ryujinx.Graphics.Gpu/Image/TextureManager.cs
index cab76da1..0b1d38d1 100644
--- a/Ryujinx.Graphics.Gpu/Image/TextureManager.cs
+++ b/Ryujinx.Graphics.Gpu/Image/TextureManager.cs
@@ -14,6 +14,20 @@ namespace Ryujinx.Graphics.Gpu.Image
/// </summary>
class TextureManager : IDisposable
{
+ private struct OverlapInfo
+ {
+ public TextureViewCompatibility Compatibility { get; }
+ public int FirstLayer { get; }
+ public int FirstLevel { get; }
+
+ public OverlapInfo(TextureViewCompatibility compatibility, int firstLayer, int firstLevel)
+ {
+ Compatibility = compatibility;
+ FirstLayer = firstLayer;
+ FirstLevel = firstLevel;
+ }
+ }
+
private const int OverlapsBufferInitialCapacity = 10;
private const int OverlapsBufferMaxCapacity = 10000;
@@ -33,6 +47,7 @@ namespace Ryujinx.Graphics.Gpu.Image
private readonly RangeList<Texture> _textures;
private Texture[] _textureOverlaps;
+ private OverlapInfo[] _overlapInfo;
private readonly AutoDeleteCache _cache;
@@ -64,6 +79,7 @@ namespace Ryujinx.Graphics.Gpu.Image
_textures = new RangeList<Texture>();
_textureOverlaps = new Texture[OverlapsBufferInitialCapacity];
+ _overlapInfo = new OverlapInfo[OverlapsBufferInitialCapacity];
_cache = new AutoDeleteCache();
@@ -408,6 +424,27 @@ namespace Ryujinx.Graphics.Gpu.Image
}
/// <summary>
+ /// Handles removal of textures written to a memory region being unmapped.
+ /// </summary>
+ /// <param name="sender">Sender object</param>
+ /// <param name="e">Event arguments</param>
+ public void MemoryUnmappedHandler(object sender, UnmapEventArgs e)
+ {
+ Texture[] overlaps = new Texture[10];
+ int overlapCount;
+
+ lock (_textures)
+ {
+ overlapCount = _textures.FindOverlaps(_context.MemoryManager.Translate(e.Address), e.Size, ref overlaps);
+ }
+
+ for (int i = 0; i < overlapCount; i++)
+ {
+ overlaps[i].Unmapped();
+ }
+ }
+
+ /// <summary>
/// Tries to find an existing texture, or create a new one if not found.
/// </summary>
/// <param name="copyTexture">Copy texture to find or create</param>
@@ -618,8 +655,13 @@ namespace Ryujinx.Graphics.Gpu.Image
scaleMode = (flags & TextureSearchFlags.WithUpscale) != 0 ? TextureScaleMode.Scaled : TextureScaleMode.Eligible;
}
- // Try to find a perfect texture match, with the same address and parameters.
- int sameAddressOverlapsCount = _textures.FindOverlaps(info.Address, ref _textureOverlaps);
+ int sameAddressOverlapsCount;
+
+ lock (_textures)
+ {
+ // Try to find a perfect texture match, with the same address and parameters.
+ sameAddressOverlapsCount = _textures.FindOverlaps(info.Address, ref _textureOverlaps);
+ }
for (int index = 0; index < sameAddressOverlapsCount; index++)
{
@@ -681,8 +723,12 @@ namespace Ryujinx.Graphics.Gpu.Image
// Find view compatible matches.
ulong size = (ulong)sizeInfo.TotalSize;
+ int overlapsCount;
- int overlapsCount = _textures.FindOverlaps(info.Address, size, ref _textureOverlaps);
+ lock (_textures)
+ {
+ overlapsCount = _textures.FindOverlaps(info.Address, size, ref _textureOverlaps);
+ }
Texture texture = null;
@@ -690,7 +736,7 @@ namespace Ryujinx.Graphics.Gpu.Image
{
Texture overlap = _textureOverlaps[index];
- if (overlap.IsViewCompatible(info, size, out int firstLayer, out int firstLevel))
+ if (overlap.IsViewCompatible(info, size, out int firstLayer, out int firstLevel) == TextureViewCompatibility.Full)
{
if (!isSamplerTexture)
{
@@ -701,7 +747,7 @@ namespace Ryujinx.Graphics.Gpu.Image
if (IsTextureModified(overlap))
{
- CacheTextureModified(texture);
+ texture.SignalModified();
}
// The size only matters (and is only really reliable) when the
@@ -721,65 +767,114 @@ namespace Ryujinx.Graphics.Gpu.Image
{
texture = new Texture(_context, info, sizeInfo, scaleMode);
- // We need to synchronize before copying the old view data to the texture,
- // otherwise the copied data would be overwritten by a future synchronization.
- texture.SynchronizeMemory();
+ // Step 1: Find textures that are view compatible with the new texture.
+ // Any textures that are incompatible will contain garbage data, so they should be removed where possible.
+
+ int viewCompatible = 0;
+ bool setData = isSamplerTexture || overlapsCount == 0;
for (int index = 0; index < overlapsCount; index++)
{
Texture overlap = _textureOverlaps[index];
+ bool overlapInCache = overlap.CacheNode != null;
- if (texture.IsViewCompatible(overlap.Info, overlap.Size, out int firstLayer, out int firstLevel))
- {
- TextureInfo overlapInfo = AdjustSizes(texture, overlap.Info, firstLevel);
-
- TextureCreateInfo createInfo = GetCreateInfo(overlapInfo, _context.Capabilities);
+ TextureViewCompatibility compatibility = texture.IsViewCompatible(overlap.Info, overlap.Size, out int firstLayer, out int firstLevel);
- if (texture.ScaleFactor != overlap.ScaleFactor)
+ if (compatibility != TextureViewCompatibility.Incompatible)
+ {
+ if (_overlapInfo.Length != _textureOverlaps.Length)
{
- // A bit tricky, our new texture may need to contain an existing texture that is upscaled, but isn't itself.
- // In that case, we prefer the higher scale only if our format is render-target-like, otherwise we scale the view down before copy.
+ Array.Resize(ref _overlapInfo, _textureOverlaps.Length);
+ }
- texture.PropagateScale(overlap);
+ _overlapInfo[viewCompatible] = new OverlapInfo(compatibility, firstLayer, firstLevel);
+ _textureOverlaps[viewCompatible++] = overlap;
+ }
+ else if (overlapInCache || !setData)
+ {
+ if (info.GobBlocksInZ > 1 && info.GobBlocksInZ == overlap.Info.GobBlocksInZ)
+ {
+ // Allow overlapping slices of 3D textures. Could be improved in future by making sure the textures don't overlap.
+ continue;
}
- ITexture newView = texture.HostTexture.CreateView(createInfo, firstLayer, firstLevel);
+ // 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.
+
+ // If the texture was modified since its last use, then that data is probably meant to go into this texture.
+ // If the data has been modified by the CPU, then it also shouldn't be flushed.
+ bool modified = overlap.ConsumeModified();
- overlap.HostTexture.CopyTo(newView, 0, 0);
+ bool flush = overlapInCache && !modified && (overlap.Address < texture.Address || overlap.EndAddress > texture.EndAddress) && overlap.HasViewCompatibleChild(texture);
- // Inherit modification from overlapping texture, do that before replacing
- // the view since the replacement operation removes it from the list.
- if (IsTextureModified(overlap))
+ setData |= modified || flush;
+
+ if (overlapInCache)
{
- CacheTextureModified(texture);
+ _cache.Remove(overlap, flush);
}
+ }
+ }
+
+ // We need to synchronize before copying the old view data to the texture,
+ // otherwise the copied data would be overwritten by a future synchronization.
+ texture.InitializeData(false, setData);
+
+ for (int index = 0; index < viewCompatible; index++)
+ {
+ Texture overlap = _textureOverlaps[index];
+ OverlapInfo oInfo = _overlapInfo[index];
+
+ if (oInfo.Compatibility != TextureViewCompatibility.Full)
+ {
+ continue; // Copy only compatibilty.
+ }
+
+ TextureInfo overlapInfo = AdjustSizes(texture, overlap.Info, oInfo.FirstLevel);
+
+ TextureCreateInfo createInfo = GetCreateInfo(overlapInfo, _context.Capabilities);
+
+ if (texture.ScaleFactor != overlap.ScaleFactor)
+ {
+ // A bit tricky, our new texture may need to contain an existing texture that is upscaled, but isn't itself.
+ // In that case, we prefer the higher scale only if our format is render-target-like, otherwise we scale the view down before copy.
- overlap.ReplaceView(texture, overlapInfo, newView, firstLayer, firstLevel);
+ texture.PropagateScale(overlap);
}
+
+ ITexture newView = texture.HostTexture.CreateView(createInfo, oInfo.FirstLayer, oInfo.FirstLevel);
+
+ overlap.HostTexture.CopyTo(newView, 0, 0);
+
+ // Inherit modification from overlapping texture, do that before replacing
+ // the view since the replacement operation removes it from the list.
+ if (IsTextureModified(overlap))
+ {
+ texture.SignalModified();
+ }
+
+ overlap.ReplaceView(texture, overlapInfo, newView, oInfo.FirstLayer, oInfo.FirstLevel);
}
// If the texture is a 3D texture, we need to additionally copy any slice
// of the 3D texture to the newly created 3D texture.
if (info.Target == Target.Texture3D)
{
- for (int index = 0; index < overlapsCount; index++)
+ for (int index = 0; index < viewCompatible; index++)
{
Texture overlap = _textureOverlaps[index];
+ OverlapInfo oInfo = _overlapInfo[index];
- if (texture.IsViewCompatible(
- overlap.Info,
- overlap.Size,
- isCopy: true,
- out int firstLayer,
- out int firstLevel))
+ if (oInfo.Compatibility != TextureViewCompatibility.Incompatible)
{
overlap.BlacklistScale();
- overlap.HostTexture.CopyTo(texture.HostTexture, firstLayer, firstLevel);
+ overlap.HostTexture.CopyTo(texture.HostTexture, oInfo.FirstLayer, oInfo.FirstLevel);
if (IsTextureModified(overlap))
{
- CacheTextureModified(texture);
+ texture.SignalModified();
}
}
}
@@ -795,7 +890,10 @@ namespace Ryujinx.Graphics.Gpu.Image
texture.Disposed += CacheTextureDisposed;
}
- _textures.Add(texture);
+ lock (_textures)
+ {
+ _textures.Add(texture);
+ }
ShrinkOverlapsBufferIfNeeded();
@@ -818,6 +916,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="texture">The texture that was modified.</param>
private void CacheTextureModified(Texture texture)
{
+ texture.IsModified = true;
_modified.Add(texture);
if (texture.Info.IsLinear)
@@ -992,7 +1091,10 @@ namespace Ryujinx.Graphics.Gpu.Image
{
foreach (Texture texture in _modifiedLinear)
{
- texture.Flush();
+ if (texture.IsModified)
+ {
+ texture.Flush();
+ }
}
_modifiedLinear.Clear();
@@ -1007,7 +1109,7 @@ namespace Ryujinx.Graphics.Gpu.Image
{
foreach (Texture texture in _modified)
{
- if (texture.OverlapsWith(address, size))
+ if (texture.OverlapsWith(address, size) && texture.IsModified)
{
texture.Flush();
}
@@ -1024,7 +1126,10 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="texture">The texture to be removed</param>
public void RemoveTextureFromCache(Texture texture)
{
- _textures.Remove(texture);
+ lock (_textures)
+ {
+ _textures.Remove(texture);
+ }
}
/// <summary>
@@ -1033,10 +1138,13 @@ namespace Ryujinx.Graphics.Gpu.Image
/// </summary>
public void Dispose()
{
- foreach (Texture texture in _textures)
+ lock (_textures)
{
- _modified.Remove(texture);
- texture.Dispose();
+ foreach (Texture texture in _textures)
+ {
+ _modified.Remove(texture);
+ texture.Dispose();
+ }
}
}
}