diff options
author | gdkchan <gab.dark.100@gmail.com> | 2020-03-20 00:17:11 -0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-03-20 14:17:11 +1100 |
commit | 8e649841582242a2f4826e9429fd926316e6f2cb (patch) | |
tree | 703d8968c3c03e431cc1582a0cbaccf10049e5b3 /Ryujinx.Graphics.Gpu/Image/Texture.cs | |
parent | 32d3f3f69024835d375401222b9abfbd32db37a5 (diff) |
Support partial invalidation on texture access (#1000)
* Support partial invalidation on texture access
* Fix typo
* PR feedback
* Fix modified size clamping
Diffstat (limited to 'Ryujinx.Graphics.Gpu/Image/Texture.cs')
-rw-r--r-- | Ryujinx.Graphics.Gpu/Image/Texture.cs | 76 |
1 files changed, 70 insertions, 6 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> |