GPU: Pre-emptively flush textures that are flushed often (to imported memory when available) (#4711)
* WIP texture pre-flush Improve performance of TextureView GetData to buffer Fix copy/sync ordering Fix minor bug Make this actually work WIP host mapping stuff * Fix usage flags * message * Cleanup 1 * Fix rebase * Fix * Improve pre-flush rules * Fix pre-flush * A lot of cleanup * Use the host memory bits * Select the correct memory type * Cleanup TextureGroupHandle * Missing comment * Remove debugging logs * Revert BufferHandle _value access modifier * One interrupt action at a time. * Support D32S8 to D24S8 conversion, safeguards * Interrupt cannot happen in sync handle's lock Waitable needs to be checked twice now, but this should stop it from deadlocking. * Remove unused using * Address some feedback * Address feedback * Address more feedback * Address more feedback * Improve sync rules Should allow for faster sync in some cases.
This commit is contained in:
parent
36f10df775
commit
e18d258fa0
40 changed files with 1328 additions and 79 deletions
|
@ -57,6 +57,12 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
/// </summary>
|
||||
public bool HasCopyDependencies { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if the texture group has a pre-emptive flush buffer.
|
||||
/// When one is present, the group must always be notified on unbind.
|
||||
/// </summary>
|
||||
public bool HasFlushBuffer => _flushBuffer != BufferHandle.Null;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if this texture has any incompatible overlaps alive.
|
||||
/// </summary>
|
||||
|
@ -89,6 +95,10 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
private bool _incompatibleOverlapsDirty = true;
|
||||
private bool _flushIncompatibleOverlaps;
|
||||
|
||||
private BufferHandle _flushBuffer;
|
||||
private bool _flushBufferImported;
|
||||
private bool _flushBufferInvalid;
|
||||
|
||||
/// <summary>
|
||||
/// Create a new texture group.
|
||||
/// </summary>
|
||||
|
@ -464,8 +474,9 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
/// </remarks>
|
||||
/// <param name="tracked">True if writing the texture data is tracked, false otherwise</param>
|
||||
/// <param name="sliceIndex">The index of the slice to flush</param>
|
||||
/// <param name="inBuffer">Whether the flushed texture data is up to date in the flush buffer</param>
|
||||
/// <param name="texture">The specific host texture to flush. Defaults to the storage texture</param>
|
||||
private void FlushTextureDataSliceToGuest(bool tracked, int sliceIndex, ITexture texture = null)
|
||||
private void FlushTextureDataSliceToGuest(bool tracked, int sliceIndex, bool inBuffer, ITexture texture = null)
|
||||
{
|
||||
(int layer, int level) = GetLayerLevelForView(sliceIndex);
|
||||
|
||||
|
@ -475,7 +486,16 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
|
||||
using WritableRegion region = _physicalMemory.GetWritableRegion(Storage.Range.Slice((ulong)offset, (ulong)size), tracked);
|
||||
|
||||
Storage.GetTextureDataSliceFromGpu(region.Memory.Span, layer, level, tracked, texture);
|
||||
if (inBuffer)
|
||||
{
|
||||
using PinnedSpan<byte> data = _context.Renderer.GetBufferData(_flushBuffer, offset, size);
|
||||
|
||||
Storage.ConvertFromHostCompatibleFormat(region.Memory.Span, data.Get(), level, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
Storage.GetTextureDataSliceFromGpu(region.Memory.Span, layer, level, tracked, texture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -484,12 +504,13 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
/// <param name="tracked">True if writing the texture data is tracked, false otherwise</param>
|
||||
/// <param name="sliceStart">The first slice to flush</param>
|
||||
/// <param name="sliceEnd">The slice to finish flushing on (exclusive)</param>
|
||||
/// <param name="inBuffer">Whether the flushed texture data is up to date in the flush buffer</param>
|
||||
/// <param name="texture">The specific host texture to flush. Defaults to the storage texture</param>
|
||||
private void FlushSliceRange(bool tracked, int sliceStart, int sliceEnd, ITexture texture = null)
|
||||
private void FlushSliceRange(bool tracked, int sliceStart, int sliceEnd, bool inBuffer, ITexture texture = null)
|
||||
{
|
||||
for (int i = sliceStart; i < sliceEnd; i++)
|
||||
{
|
||||
FlushTextureDataSliceToGuest(tracked, i, texture);
|
||||
FlushTextureDataSliceToGuest(tracked, i, inBuffer, texture);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -520,7 +541,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
{
|
||||
if (endSlice > startSlice)
|
||||
{
|
||||
FlushSliceRange(tracked, startSlice, endSlice);
|
||||
FlushSliceRange(tracked, startSlice, endSlice, false);
|
||||
flushed = true;
|
||||
}
|
||||
|
||||
|
@ -553,7 +574,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
}
|
||||
else
|
||||
{
|
||||
FlushSliceRange(tracked, startSlice, endSlice);
|
||||
FlushSliceRange(tracked, startSlice, endSlice, false);
|
||||
}
|
||||
|
||||
flushed = true;
|
||||
|
@ -565,6 +586,58 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
return flushed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flush the texture data into a persistently mapped buffer.
|
||||
/// If the buffer does not exist, this method will create it.
|
||||
/// </summary>
|
||||
/// <param name="handle">Handle of the texture group to flush slices of</param>
|
||||
public void FlushIntoBuffer(TextureGroupHandle handle)
|
||||
{
|
||||
// Ensure that the buffer exists.
|
||||
|
||||
if (_flushBufferInvalid && _flushBuffer != BufferHandle.Null)
|
||||
{
|
||||
_flushBufferInvalid = false;
|
||||
_context.Renderer.DeleteBuffer(_flushBuffer);
|
||||
_flushBuffer = BufferHandle.Null;
|
||||
}
|
||||
|
||||
if (_flushBuffer == BufferHandle.Null)
|
||||
{
|
||||
if (!TextureCompatibility.CanTextureFlush(Storage.Info, _context.Capabilities))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool canImport = Storage.Info.IsLinear && Storage.Info.Stride >= Storage.Info.Width * Storage.Info.FormatInfo.BytesPerPixel;
|
||||
|
||||
var hostPointer = canImport ? _physicalMemory.GetHostPointer(Storage.Range) : 0;
|
||||
|
||||
if (hostPointer != 0 && _context.Renderer.PrepareHostMapping(hostPointer, Storage.Size))
|
||||
{
|
||||
_flushBuffer = _context.Renderer.CreateBuffer(hostPointer, (int)Storage.Size);
|
||||
_flushBufferImported = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
_flushBuffer = _context.Renderer.CreateBuffer((int)Storage.Size, BufferAccess.FlushPersistent);
|
||||
_flushBufferImported = false;
|
||||
}
|
||||
|
||||
Storage.BlacklistScale();
|
||||
}
|
||||
|
||||
int sliceStart = handle.BaseSlice;
|
||||
int sliceEnd = sliceStart + handle.SliceCount;
|
||||
|
||||
for (int i = sliceStart; i < sliceEnd; i++)
|
||||
{
|
||||
(int layer, int level) = GetLayerLevelForView(i);
|
||||
|
||||
Storage.GetFlushTexture().CopyTo(new BufferRange(_flushBuffer, _allOffsets[i], _sliceSizes[level]), layer, level, _flushBufferImported ? Storage.Info.Stride : 0);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears competing modified flags for all incompatible ranges, if they have possibly been modified.
|
||||
/// </summary>
|
||||
|
@ -1570,10 +1643,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
|
||||
_context.Renderer.BackgroundContextAction(() =>
|
||||
{
|
||||
if (!isGpuThread)
|
||||
{
|
||||
handle.Sync(_context);
|
||||
}
|
||||
bool inBuffer = !isGpuThread && handle.Sync(_context);
|
||||
|
||||
Storage.SignalModifiedDirty();
|
||||
|
||||
|
@ -1585,13 +1655,24 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
}
|
||||
}
|
||||
|
||||
if (TextureCompatibility.CanTextureFlush(Storage.Info, _context.Capabilities))
|
||||
if (TextureCompatibility.CanTextureFlush(Storage.Info, _context.Capabilities) && !(inBuffer && _flushBufferImported))
|
||||
{
|
||||
FlushSliceRange(false, handle.BaseSlice, handle.BaseSlice + handle.SliceCount, Storage.GetFlushTexture());
|
||||
FlushSliceRange(false, handle.BaseSlice, handle.BaseSlice + handle.SliceCount, inBuffer, Storage.GetFlushTexture());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called if any part of the storage texture is unmapped.
|
||||
/// </summary>
|
||||
public void Unmapped()
|
||||
{
|
||||
if (_flushBufferImported)
|
||||
{
|
||||
_flushBufferInvalid = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispose this texture group, disposing all related memory tracking handles.
|
||||
/// </summary>
|
||||
|
@ -1606,6 +1687,11 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
{
|
||||
incompatible.Group._incompatibleOverlaps.RemoveAll(overlap => overlap.Group == this);
|
||||
}
|
||||
|
||||
if (_flushBuffer != BufferHandle.Null)
|
||||
{
|
||||
_context.Renderer.DeleteBuffer(_flushBuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue