aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorgdkchan <gab.dark.100@gmail.com>2020-03-20 00:17:11 -0300
committerGitHub <noreply@github.com>2020-03-20 14:17:11 +1100
commit8e649841582242a2f4826e9429fd926316e6f2cb (patch)
tree703d8968c3c03e431cc1582a0cbaccf10049e5b3
parent32d3f3f69024835d375401222b9abfbd32db37a5 (diff)
Support partial invalidation on texture access (#1000)
* Support partial invalidation on texture access * Fix typo * PR feedback * Fix modified size clamping
-rw-r--r--Ryujinx.Graphics.Gpu/Image/Texture.cs76
-rw-r--r--Ryujinx.Graphics.Gpu/Image/TextureManager.cs50
2 files changed, 113 insertions, 13 deletions
diff --git a/Ryujinx.Graphics.Gpu/Image/Texture.cs b/Ryujinx.Graphics.Gpu/Image/Texture.cs
index 7511bdfa..ba7dce7b 100644
--- a/Ryujinx.Graphics.Gpu/Image/Texture.cs
+++ b/Ryujinx.Graphics.Gpu/Image/Texture.cs
@@ -302,15 +302,68 @@ namespace Ryujinx.Graphics.Gpu.Image
_sequenceNumber = _context.SequenceNumber;
- bool modified = _context.PhysicalMemory.GetModifiedRanges(Address, Size, ResourceName.Texture).Length != 0;
+ (ulong, ulong)[] modifiedRanges = _context.PhysicalMemory.GetModifiedRanges(Address, Size, ResourceName.Texture);
- if (!modified && _hasData)
+ if (modifiedRanges.Length == 0 && _hasData)
{
return;
}
ReadOnlySpan<byte> data = _context.PhysicalMemory.GetSpan(Address, Size);
+ // If the texture was modified by the host GPU, we do partial invalidation
+ // of the texture by getting GPU data and merging in the pages of memory
+ // that were modified.
+ // Note that if ASTC is not supported by the GPU we can't read it back since
+ // it will use a different format. Since applications shouldn't be writing
+ // ASTC textures from the GPU anyway, ignoring it should be safe.
+ if (_context.Methods.TextureManager.IsTextureModified(this) && !Info.FormatInfo.Format.IsAstc())
+ {
+ Span<byte> gpuData = GetTextureDataFromGpu();
+
+ ulong endAddress = Address + Size;
+
+ for (int i = 0; i < modifiedRanges.Length; i++)
+ {
+ (ulong modifiedAddress, ulong modifiedSize) = modifiedRanges[i];
+
+ ulong endModifiedAddress = modifiedAddress + modifiedSize;
+
+ if (modifiedAddress < Address)
+ {
+ modifiedAddress = Address;
+ }
+
+ if (endModifiedAddress > endAddress)
+ {
+ endModifiedAddress = endAddress;
+ }
+
+ modifiedSize = endModifiedAddress - modifiedAddress;
+
+ int offset = (int)(modifiedAddress - Address);
+ int length = (int)modifiedSize;
+
+ data.Slice(offset, length).CopyTo(gpuData.Slice(offset, length));
+ }
+
+ data = gpuData;
+ }
+
+ data = ConvertToHostCompatibleFormat(data);
+
+ HostTexture.SetData(data);
+
+ _hasData = true;
+ }
+
+ /// <summary>
+ /// Converts texture data to a format and layout that is supported by the host GPU.
+ /// </summary>
+ /// <param name="data">Data to be converted</param>
+ /// <returns>Converted data</returns>
+ private ReadOnlySpan<byte> ConvertToHostCompatibleFormat(ReadOnlySpan<byte> data)
+ {
if (Info.IsLinear)
{
data = LayoutConverter.ConvertLinearStridedToLinear(
@@ -360,9 +413,7 @@ namespace Ryujinx.Graphics.Gpu.Image
data = decoded;
}
- HostTexture.SetData(data);
-
- _hasData = true;
+ return data;
}
/// <summary>
@@ -375,6 +426,19 @@ namespace Ryujinx.Graphics.Gpu.Image
/// </summary>
public void Flush()
{
+ _context.PhysicalMemory.Write(Address, GetTextureDataFromGpu());
+ }
+
+ /// <summary>
+ /// Gets data from the host GPU.
+ /// </summary>
+ /// <remarks>
+ /// This method should be used to retrieve data that was modified by the host GPU.
+ /// This is not cheap, avoid doing that unless strictly needed.
+ /// </remarks>
+ /// <returns>Host texture data</returns>
+ private Span<byte> GetTextureDataFromGpu()
+ {
Span<byte> data = HostTexture.GetData();
if (Info.IsLinear)
@@ -406,7 +470,7 @@ namespace Ryujinx.Graphics.Gpu.Image
data);
}
- _context.PhysicalMemory.Write(Address, data);
+ return data;
}
/// <summary>
diff --git a/Ryujinx.Graphics.Gpu/Image/TextureManager.cs b/Ryujinx.Graphics.Gpu/Image/TextureManager.cs
index 87fb4161..27cbc6ff 100644
--- a/Ryujinx.Graphics.Gpu/Image/TextureManager.cs
+++ b/Ryujinx.Graphics.Gpu/Image/TextureManager.cs
@@ -37,6 +37,7 @@ namespace Ryujinx.Graphics.Gpu.Image
private readonly AutoDeleteCache _cache;
private readonly HashSet<Texture> _modified;
+ private readonly HashSet<Texture> _modifiedLinear;
/// <summary>
/// Constructs a new instance of the texture manager.
@@ -62,6 +63,7 @@ namespace Ryujinx.Graphics.Gpu.Image
_cache = new AutoDeleteCache();
_modified = new HashSet<Texture>(new ReferenceEqualityComparer<Texture>());
+ _modifiedLinear = new HashSet<Texture>(new ReferenceEqualityComparer<Texture>());
}
/// <summary>
@@ -519,6 +521,11 @@ namespace Ryujinx.Graphics.Gpu.Image
texture = overlap.CreateView(info, sizeInfo, firstLayer, firstLevel);
+ if (IsTextureModified(overlap))
+ {
+ CacheTextureModified(texture);
+ }
+
// The size only matters (and is only really reliable) when the
// texture is used on a sampler, because otherwise the size will be
// aligned.
@@ -554,6 +561,13 @@ namespace Ryujinx.Graphics.Gpu.Image
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))
+ {
+ CacheTextureModified(texture);
+ }
+
overlap.ReplaceView(texture, overlapInfo, newView);
}
}
@@ -574,6 +588,11 @@ namespace Ryujinx.Graphics.Gpu.Image
out int firstLevel))
{
overlap.HostTexture.CopyTo(texture.HostTexture, firstLayer, firstLevel);
+
+ if (IsTextureModified(overlap))
+ {
+ CacheTextureModified(texture);
+ }
}
}
}
@@ -596,12 +615,27 @@ namespace Ryujinx.Graphics.Gpu.Image
}
/// <summary>
+ /// Checks if a texture was modified by the host GPU.
+ /// </summary>
+ /// <param name="texture">Texture to be checked</param>
+ /// <returns>True if the texture was modified by the host GPU, false otherwise</returns>
+ public bool IsTextureModified(Texture texture)
+ {
+ return _modified.Contains(texture);
+ }
+
+ /// <summary>
/// Signaled when a cache texture is modified, and adds it to a set to be enumerated when flushing textures.
/// </summary>
/// <param name="texture">The texture that was modified.</param>
private void CacheTextureModified(Texture texture)
{
_modified.Add(texture);
+
+ if (texture.Info.IsLinear)
+ {
+ _modifiedLinear.Add(texture);
+ }
}
/// <summary>
@@ -611,6 +645,11 @@ namespace Ryujinx.Graphics.Gpu.Image
private void CacheTextureDisposed(Texture texture)
{
_modified.Remove(texture);
+
+ if (texture.Info.IsLinear)
+ {
+ _modifiedLinear.Remove(texture);
+ }
}
/// <summary>
@@ -747,14 +786,12 @@ namespace Ryujinx.Graphics.Gpu.Image
/// </summary>
public void Flush()
{
- foreach (Texture texture in _modified)
+ foreach (Texture texture in _modifiedLinear)
{
- if (texture.Info.IsLinear)
- {
- texture.Flush();
- }
+ texture.Flush();
}
- _modified.Clear();
+
+ _modifiedLinear.Clear();
}
/// <summary>
@@ -771,7 +808,6 @@ namespace Ryujinx.Graphics.Gpu.Image
texture.Flush();
}
}
- _modified.Clear();
}
/// <summary>