forked from Mirror/Ryujinx
Support partial invalidation on texture access (#1000)
* Support partial invalidation on texture access * Fix typo * PR feedback * Fix modified size clamping
This commit is contained in:
parent
32d3f3f690
commit
8e64984158
2 changed files with 113 additions and 13 deletions
|
@ -302,15 +302,68 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
|
|
||||||
_sequenceNumber = _context.SequenceNumber;
|
_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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ReadOnlySpan<byte> data = _context.PhysicalMemory.GetSpan(Address, Size);
|
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)
|
if (Info.IsLinear)
|
||||||
{
|
{
|
||||||
data = LayoutConverter.ConvertLinearStridedToLinear(
|
data = LayoutConverter.ConvertLinearStridedToLinear(
|
||||||
|
@ -360,9 +413,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
data = decoded;
|
data = decoded;
|
||||||
}
|
}
|
||||||
|
|
||||||
HostTexture.SetData(data);
|
return data;
|
||||||
|
|
||||||
_hasData = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -374,6 +425,19 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
/// This may cause data corruption if the memory is already being used for something else on the CPU side.
|
/// This may cause data corruption if the memory is already being used for something else on the CPU side.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void Flush()
|
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();
|
Span<byte> data = HostTexture.GetData();
|
||||||
|
|
||||||
|
@ -406,7 +470,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
data);
|
data);
|
||||||
}
|
}
|
||||||
|
|
||||||
_context.PhysicalMemory.Write(Address, data);
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -37,6 +37,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
private readonly AutoDeleteCache _cache;
|
private readonly AutoDeleteCache _cache;
|
||||||
|
|
||||||
private readonly HashSet<Texture> _modified;
|
private readonly HashSet<Texture> _modified;
|
||||||
|
private readonly HashSet<Texture> _modifiedLinear;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Constructs a new instance of the texture manager.
|
/// Constructs a new instance of the texture manager.
|
||||||
|
@ -62,6 +63,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
_cache = new AutoDeleteCache();
|
_cache = new AutoDeleteCache();
|
||||||
|
|
||||||
_modified = new HashSet<Texture>(new ReferenceEqualityComparer<Texture>());
|
_modified = new HashSet<Texture>(new ReferenceEqualityComparer<Texture>());
|
||||||
|
_modifiedLinear = new HashSet<Texture>(new ReferenceEqualityComparer<Texture>());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -519,6 +521,11 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
|
|
||||||
texture = overlap.CreateView(info, sizeInfo, firstLayer, firstLevel);
|
texture = overlap.CreateView(info, sizeInfo, firstLayer, firstLevel);
|
||||||
|
|
||||||
|
if (IsTextureModified(overlap))
|
||||||
|
{
|
||||||
|
CacheTextureModified(texture);
|
||||||
|
}
|
||||||
|
|
||||||
// The size only matters (and is only really reliable) when the
|
// The size only matters (and is only really reliable) when the
|
||||||
// texture is used on a sampler, because otherwise the size will be
|
// texture is used on a sampler, because otherwise the size will be
|
||||||
// aligned.
|
// aligned.
|
||||||
|
@ -554,6 +561,13 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
|
|
||||||
overlap.HostTexture.CopyTo(newView, 0, 0);
|
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);
|
overlap.ReplaceView(texture, overlapInfo, newView);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -574,6 +588,11 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
out int firstLevel))
|
out int firstLevel))
|
||||||
{
|
{
|
||||||
overlap.HostTexture.CopyTo(texture.HostTexture, firstLayer, firstLevel);
|
overlap.HostTexture.CopyTo(texture.HostTexture, firstLayer, firstLevel);
|
||||||
|
|
||||||
|
if (IsTextureModified(overlap))
|
||||||
|
{
|
||||||
|
CacheTextureModified(texture);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -595,6 +614,16 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
return texture;
|
return texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <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>
|
/// <summary>
|
||||||
/// Signaled when a cache texture is modified, and adds it to a set to be enumerated when flushing textures.
|
/// Signaled when a cache texture is modified, and adds it to a set to be enumerated when flushing textures.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -602,6 +631,11 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
private void CacheTextureModified(Texture texture)
|
private void CacheTextureModified(Texture texture)
|
||||||
{
|
{
|
||||||
_modified.Add(texture);
|
_modified.Add(texture);
|
||||||
|
|
||||||
|
if (texture.Info.IsLinear)
|
||||||
|
{
|
||||||
|
_modifiedLinear.Add(texture);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -611,6 +645,11 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
private void CacheTextureDisposed(Texture texture)
|
private void CacheTextureDisposed(Texture texture)
|
||||||
{
|
{
|
||||||
_modified.Remove(texture);
|
_modified.Remove(texture);
|
||||||
|
|
||||||
|
if (texture.Info.IsLinear)
|
||||||
|
{
|
||||||
|
_modifiedLinear.Remove(texture);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -747,14 +786,12 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void Flush()
|
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>
|
/// <summary>
|
||||||
|
@ -771,7 +808,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
texture.Flush();
|
texture.Flush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_modified.Clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
Loading…
Reference in a new issue