aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs')
-rw-r--r--src/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs110
1 files changed, 98 insertions, 12 deletions
diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs
index 234e7e8c..14ab5d1e 100644
--- a/src/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs
+++ b/src/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs
@@ -58,6 +58,12 @@ namespace Ryujinx.Graphics.Gpu.Image
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>
public bool HasIncompatibleOverlaps => _incompatibleOverlaps.Count > 0;
@@ -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;
@@ -566,6 +587,58 @@ namespace Ryujinx.Graphics.Gpu.Image
}
/// <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>
/// <param name="texture">The texture that has been modified</param>
@@ -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,14 +1655,25 @@ 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>
public void Dispose()
@@ -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);
+ }
}
}
}