mirror of
https://git.743378673.xyz/MeloNX/MeloNX.git
synced 2025-08-02 08:07:09 +02:00
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
|
@ -1,5 +1,6 @@
|
|||
using Ryujinx.Graphics.Device;
|
||||
using Ryujinx.Graphics.Gpu.Engine.MME;
|
||||
using Ryujinx.Graphics.Gpu.Synchronization;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
@ -59,7 +60,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
|
|||
if (_createSyncPending)
|
||||
{
|
||||
_createSyncPending = false;
|
||||
_context.CreateHostSyncIfNeeded(false, false);
|
||||
_context.CreateHostSyncIfNeeded(HostSyncFlags.None);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -157,7 +158,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
|
|||
}
|
||||
else if (operation == SyncpointbOperation.Incr)
|
||||
{
|
||||
_context.CreateHostSyncIfNeeded(true, true);
|
||||
_context.CreateHostSyncIfNeeded(HostSyncFlags.StrictSyncpoint);
|
||||
_context.Synchronization.IncrementSyncpoint(syncpointId);
|
||||
}
|
||||
|
||||
|
@ -184,7 +185,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
|
|||
{
|
||||
_context.Renderer.Pipeline.CommandBufferBarrier();
|
||||
|
||||
_context.CreateHostSyncIfNeeded(false, true);
|
||||
_context.CreateHostSyncIfNeeded(HostSyncFlags.Strict);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -3,6 +3,7 @@ using Ryujinx.Graphics.GAL;
|
|||
using Ryujinx.Graphics.Gpu.Engine.GPFifo;
|
||||
using Ryujinx.Graphics.Gpu.Engine.InlineToMemory;
|
||||
using Ryujinx.Graphics.Gpu.Engine.Threed.Blender;
|
||||
using Ryujinx.Graphics.Gpu.Synchronization;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
@ -254,7 +255,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||
uint syncpointId = (uint)argument & 0xFFFF;
|
||||
|
||||
_context.AdvanceSequence();
|
||||
_context.CreateHostSyncIfNeeded(true, true);
|
||||
_context.CreateHostSyncIfNeeded(HostSyncFlags.StrictSyncpoint);
|
||||
_context.Renderer.UpdateCounters(); // Poll the query counters, the game may want an updated result.
|
||||
_context.Synchronization.IncrementSyncpoint(syncpointId);
|
||||
}
|
||||
|
|
|
@ -60,14 +60,14 @@ namespace Ryujinx.Graphics.Gpu
|
|||
/// If there are more than 0 items when this happens, a host sync object will be generated for the given <see cref="SyncNumber"/>,
|
||||
/// and the SyncNumber will be incremented.
|
||||
/// </summary>
|
||||
internal List<Action> SyncActions { get; }
|
||||
internal List<ISyncActionHandler> SyncActions { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Actions to be performed when a CPU waiting syncpoint is triggered.
|
||||
/// If there are more than 0 items when this happens, a host sync object will be generated for the given <see cref="SyncNumber"/>,
|
||||
/// and the SyncNumber will be incremented.
|
||||
/// </summary>
|
||||
internal List<Action> SyncpointActions { get; }
|
||||
internal List<ISyncActionHandler> SyncpointActions { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Buffer migrations that are currently in-flight. These are checked whenever sync is created to determine if buffer migration
|
||||
|
@ -114,8 +114,8 @@ namespace Ryujinx.Graphics.Gpu
|
|||
|
||||
HostInitalized = new ManualResetEvent(false);
|
||||
|
||||
SyncActions = new List<Action>();
|
||||
SyncpointActions = new List<Action>();
|
||||
SyncActions = new List<ISyncActionHandler>();
|
||||
SyncpointActions = new List<ISyncActionHandler>();
|
||||
BufferMigrations = new List<BufferMigration>();
|
||||
|
||||
DeferredActions = new Queue<Action>();
|
||||
|
@ -296,9 +296,9 @@ namespace Ryujinx.Graphics.Gpu
|
|||
/// Registers an action to be performed the next time a syncpoint is incremented.
|
||||
/// This will also ensure a host sync object is created, and <see cref="SyncNumber"/> is incremented.
|
||||
/// </summary>
|
||||
/// <param name="action">The action to be performed on sync object creation</param>
|
||||
/// <param name="action">The resource with action to be performed on sync object creation</param>
|
||||
/// <param name="syncpointOnly">True if the sync action should only run when syncpoints are incremented</param>
|
||||
public void RegisterSyncAction(Action action, bool syncpointOnly = false)
|
||||
internal void RegisterSyncAction(ISyncActionHandler action, bool syncpointOnly = false)
|
||||
{
|
||||
if (syncpointOnly)
|
||||
{
|
||||
|
@ -315,10 +315,13 @@ namespace Ryujinx.Graphics.Gpu
|
|||
/// Creates a host sync object if there are any pending sync actions. The actions will then be called.
|
||||
/// If no actions are present, a host sync object is not created.
|
||||
/// </summary>
|
||||
/// <param name="syncpoint">True if host sync is being created by a syncpoint</param>
|
||||
/// <param name="strict">True if the sync should signal as soon as possible</param>
|
||||
public void CreateHostSyncIfNeeded(bool syncpoint, bool strict)
|
||||
/// <param name="flags">Modifiers for how host sync should be created</param>
|
||||
internal void CreateHostSyncIfNeeded(HostSyncFlags flags)
|
||||
{
|
||||
bool syncpoint = flags.HasFlag(HostSyncFlags.Syncpoint);
|
||||
bool strict = flags.HasFlag(HostSyncFlags.Strict);
|
||||
bool force = flags.HasFlag(HostSyncFlags.Force);
|
||||
|
||||
if (BufferMigrations.Count > 0)
|
||||
{
|
||||
ulong currentSyncNumber = Renderer.GetCurrentSync();
|
||||
|
@ -336,24 +339,17 @@ namespace Ryujinx.Graphics.Gpu
|
|||
}
|
||||
}
|
||||
|
||||
if (_pendingSync || (syncpoint && SyncpointActions.Count > 0))
|
||||
if (force || _pendingSync || (syncpoint && SyncpointActions.Count > 0))
|
||||
{
|
||||
Renderer.CreateSync(SyncNumber, strict);
|
||||
|
||||
SyncActions.ForEach(action => action.SyncPreAction(syncpoint));
|
||||
SyncpointActions.ForEach(action => action.SyncPreAction(syncpoint));
|
||||
|
||||
SyncNumber++;
|
||||
|
||||
foreach (Action action in SyncActions)
|
||||
{
|
||||
action();
|
||||
}
|
||||
|
||||
foreach (Action action in SyncpointActions)
|
||||
{
|
||||
action();
|
||||
}
|
||||
|
||||
SyncActions.Clear();
|
||||
SyncpointActions.Clear();
|
||||
SyncActions.RemoveAll(action => action.SyncAction(syncpoint));
|
||||
SyncpointActions.RemoveAll(action => action.SyncAction(syncpoint));
|
||||
}
|
||||
|
||||
_pendingSync = false;
|
||||
|
|
|
@ -1423,7 +1423,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
_scaledSetScore = Math.Max(0, _scaledSetScore - 1);
|
||||
}
|
||||
|
||||
if (_modifiedStale || Group.HasCopyDependencies)
|
||||
if (_modifiedStale || Group.HasCopyDependencies || Group.HasFlushBuffer)
|
||||
{
|
||||
_modifiedStale = false;
|
||||
Group.SignalModifying(this, bound);
|
||||
|
@ -1685,6 +1685,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
|
||||
if (Group.Storage == this)
|
||||
{
|
||||
Group.Unmapped();
|
||||
|
||||
Group.ClearModified(unmapRange);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using Ryujinx.Cpu.Tracking;
|
||||
using Ryujinx.Graphics.Gpu.Synchronization;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
@ -13,8 +14,14 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
/// Also tracks copy dependencies for the handle - references to other handles that must be kept
|
||||
/// in sync with this one before use.
|
||||
/// </summary>
|
||||
class TextureGroupHandle : IDisposable
|
||||
class TextureGroupHandle : ISyncActionHandler, IDisposable
|
||||
{
|
||||
private const int FlushBalanceIncrement = 6;
|
||||
private const int FlushBalanceWriteCost = 1;
|
||||
private const int FlushBalanceThreshold = 7;
|
||||
private const int FlushBalanceMax = 60;
|
||||
private const int FlushBalanceMin = -10;
|
||||
|
||||
private TextureGroup _group;
|
||||
private int _bindCount;
|
||||
private int _firstLevel;
|
||||
|
@ -26,6 +33,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
/// The sync number last registered.
|
||||
/// </summary>
|
||||
private ulong _registeredSync;
|
||||
private ulong _registeredBufferSync = ulong.MaxValue;
|
||||
private ulong _registeredBufferGuestSync = ulong.MaxValue;
|
||||
|
||||
/// <summary>
|
||||
/// The sync number when the texture was last modified by GPU.
|
||||
|
@ -42,6 +51,12 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
/// </summary>
|
||||
private bool _syncActionRegistered;
|
||||
|
||||
/// <summary>
|
||||
/// Determines the balance of synced writes to flushes.
|
||||
/// Used to determine if the texture should always write data to a persistent buffer for flush.
|
||||
/// </summary>
|
||||
private int _flushBalance;
|
||||
|
||||
/// <summary>
|
||||
/// The byte offset from the start of the storage of this handle.
|
||||
/// </summary>
|
||||
|
@ -132,6 +147,12 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
}
|
||||
|
||||
Handles = handles;
|
||||
|
||||
if (group.Storage.Info.IsLinear)
|
||||
{
|
||||
// Linear textures are presumed to be used for readback initially.
|
||||
_flushBalance = FlushBalanceThreshold + FlushBalanceIncrement;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -159,6 +180,35 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine if the next sync will copy into the flush buffer.
|
||||
/// </summary>
|
||||
/// <returns>True if it will copy, false otherwise</returns>
|
||||
private bool NextSyncCopies()
|
||||
{
|
||||
return _flushBalance - FlushBalanceWriteCost > FlushBalanceThreshold;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Alters the flush balance by the given value. Should increase significantly with each sync, decrease with each write.
|
||||
/// A flush balance higher than the threshold will cause a texture to repeatedly copy to a flush buffer on each use.
|
||||
/// </summary>
|
||||
/// <param name="modifier">Value to add to the existing flush balance</param>
|
||||
/// <returns>True if the new balance is over the threshold, false otherwise</returns>
|
||||
private bool ModifyFlushBalance(int modifier)
|
||||
{
|
||||
int result;
|
||||
int existingBalance;
|
||||
do
|
||||
{
|
||||
existingBalance = _flushBalance;
|
||||
result = Math.Max(FlushBalanceMin, Math.Min(FlushBalanceMax, existingBalance + modifier));
|
||||
}
|
||||
while (Interlocked.CompareExchange(ref _flushBalance, result, existingBalance) != existingBalance);
|
||||
|
||||
return result > FlushBalanceThreshold;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a single texture view as an overlap if its range overlaps.
|
||||
/// </summary>
|
||||
|
@ -204,7 +254,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
if (!_syncActionRegistered)
|
||||
{
|
||||
_modifiedSync = context.SyncNumber;
|
||||
context.RegisterSyncAction(SyncAction, true);
|
||||
context.RegisterSyncAction(this, true);
|
||||
_syncActionRegistered = true;
|
||||
}
|
||||
|
||||
|
@ -241,6 +291,13 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
{
|
||||
SignalModified(context);
|
||||
|
||||
if (!bound && _syncActionRegistered && NextSyncCopies())
|
||||
{
|
||||
// On unbind, textures that flush often should immediately create sync so their result can be obtained as soon as possible.
|
||||
|
||||
context.CreateHostSyncIfNeeded(HostSyncFlags.Force);
|
||||
}
|
||||
|
||||
// Note: Bind count currently resets to 0 on inherit for safety, as the handle <-> view relationship can change.
|
||||
_bindCount = Math.Max(0, _bindCount + (bound ? 1 : -1));
|
||||
}
|
||||
|
@ -266,25 +323,35 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
/// removing the modified flag if it was reached, or leaving it set if it has not yet been created.
|
||||
/// </summary>
|
||||
/// <param name="context">The GPU context used to wait for sync</param>
|
||||
public void Sync(GpuContext context)
|
||||
/// <returns>True if the texture data can be read from the flush buffer</returns>
|
||||
public bool Sync(GpuContext context)
|
||||
{
|
||||
ulong registeredSync = _registeredSync;
|
||||
long diff = (long)(context.SyncNumber - registeredSync);
|
||||
// Currently assumes the calling thread is a guest thread.
|
||||
|
||||
bool inBuffer = _registeredBufferGuestSync != ulong.MaxValue;
|
||||
ulong sync = inBuffer ? _registeredBufferGuestSync : _registeredSync;
|
||||
|
||||
long diff = (long)(context.SyncNumber - sync);
|
||||
|
||||
ModifyFlushBalance(FlushBalanceIncrement);
|
||||
|
||||
if (diff > 0)
|
||||
{
|
||||
context.Renderer.WaitSync(registeredSync);
|
||||
context.Renderer.WaitSync(sync);
|
||||
|
||||
if ((long)(_modifiedSync - registeredSync) > 0)
|
||||
if ((long)(_modifiedSync - sync) > 0)
|
||||
{
|
||||
// Flush the data in a previous state. Do not remove the modified flag - it will be removed to ignore following writes.
|
||||
return;
|
||||
return inBuffer;
|
||||
}
|
||||
|
||||
Modified = false;
|
||||
|
||||
return inBuffer;
|
||||
}
|
||||
|
||||
// If the difference is <= 0, no data is not ready yet. Flush any data we can without waiting or removing modified flag.
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -296,15 +363,41 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
Interlocked.Exchange(ref _actionRegistered, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Action to perform before a sync number is registered after modification.
|
||||
/// This action will copy the texture data to the flush buffer if this texture
|
||||
/// flushes often enough, which is determined by the flush balance.
|
||||
/// </summary>
|
||||
/// <inheritdoc/>
|
||||
public void SyncPreAction(bool syncpoint)
|
||||
{
|
||||
if (syncpoint || NextSyncCopies())
|
||||
{
|
||||
if (ModifyFlushBalance(0) && _registeredBufferSync != _modifiedSync)
|
||||
{
|
||||
_group.FlushIntoBuffer(this);
|
||||
_registeredBufferSync = _modifiedSync;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Action to perform when a sync number is registered after modification.
|
||||
/// This action will register a read tracking action on the memory tracking handle so that a flush from CPU can happen.
|
||||
/// </summary>
|
||||
private void SyncAction()
|
||||
/// <inheritdoc/>
|
||||
public bool SyncAction(bool syncpoint)
|
||||
{
|
||||
// The storage will need to signal modified again to update the sync number in future.
|
||||
_group.Storage.SignalModifiedDirty();
|
||||
|
||||
bool lastInBuffer = _registeredBufferSync == _modifiedSync;
|
||||
|
||||
if (!lastInBuffer)
|
||||
{
|
||||
_registeredBufferSync = ulong.MaxValue;
|
||||
}
|
||||
|
||||
lock (Overlaps)
|
||||
{
|
||||
foreach (Texture texture in Overlaps)
|
||||
|
@ -314,6 +407,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
}
|
||||
|
||||
// Register region tracking for CPU? (again)
|
||||
|
||||
_registeredSync = _modifiedSync;
|
||||
_syncActionRegistered = false;
|
||||
|
||||
|
@ -321,6 +415,14 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
{
|
||||
_group.RegisterAction(this);
|
||||
}
|
||||
|
||||
if (syncpoint)
|
||||
{
|
||||
_registeredBufferGuestSync = _registeredBufferSync;
|
||||
}
|
||||
|
||||
// If the last modification is in the buffer, keep this sync action alive until it sees a syncpoint.
|
||||
return syncpoint || !lastInBuffer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using Ryujinx.Cpu.Tracking;
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.Gpu.Synchronization;
|
||||
using Ryujinx.Memory.Range;
|
||||
using Ryujinx.Memory.Tracking;
|
||||
using System;
|
||||
|
@ -11,7 +12,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||
/// <summary>
|
||||
/// Buffer, used to store vertex and index data, uniform and storage buffers, and others.
|
||||
/// </summary>
|
||||
class Buffer : IRange, IDisposable
|
||||
class Buffer : IRange, ISyncActionHandler, IDisposable
|
||||
{
|
||||
private const ulong GranularBufferThreshold = 4096;
|
||||
|
||||
|
@ -248,7 +249,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||
|
||||
if (!_syncActionRegistered)
|
||||
{
|
||||
_context.RegisterSyncAction(SyncAction);
|
||||
_context.RegisterSyncAction(this);
|
||||
_syncActionRegistered = true;
|
||||
}
|
||||
}
|
||||
|
@ -267,7 +268,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||
/// Action to be performed when a syncpoint is reached after modification.
|
||||
/// This will register read/write tracking to flush the buffer from GPU when its memory is used.
|
||||
/// </summary>
|
||||
private void SyncAction()
|
||||
/// <inheritdoc/>
|
||||
public bool SyncAction(bool syncpoint)
|
||||
{
|
||||
_syncActionRegistered = false;
|
||||
|
||||
|
@ -284,6 +286,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||
_memoryTracking.RegisterAction(_externalFlushDelegate);
|
||||
SynchronizeMemory(Address, Size);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -296,7 +300,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||
{
|
||||
if (from._syncActionRegistered && !_syncActionRegistered)
|
||||
{
|
||||
_context.RegisterSyncAction(SyncAction);
|
||||
_context.RegisterSyncAction(this);
|
||||
_syncActionRegistered = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ using Ryujinx.Memory.Tracking;
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Memory
|
||||
|
@ -82,6 +83,34 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a host pointer for a given range of application memory.
|
||||
/// If the memory region is not a single contiguous block, this method returns 0.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Getting a host pointer is unsafe. It should be considered invalid immediately if the GPU memory is unmapped.
|
||||
/// </remarks>
|
||||
/// <param name="range">Ranges of physical memory where the target data is located</param>
|
||||
/// <returns>Pointer to the range of memory</returns>
|
||||
public nint GetHostPointer(MultiRange range)
|
||||
{
|
||||
if (range.Count == 1)
|
||||
{
|
||||
var singleRange = range.GetSubRange(0);
|
||||
if (singleRange.Address != MemoryManager.PteUnmapped)
|
||||
{
|
||||
var regions = _cpuMemory.GetHostRegions(singleRange.Address, singleRange.Size);
|
||||
|
||||
if (regions != null && regions.Count() == 1)
|
||||
{
|
||||
return (nint)regions.First().Address;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a span of data from the application process.
|
||||
/// </summary>
|
||||
|
|
30
src/Ryujinx.Graphics.Gpu/Synchronization/HostSyncFlags.cs
Normal file
30
src/Ryujinx.Graphics.Gpu/Synchronization/HostSyncFlags.cs
Normal file
|
@ -0,0 +1,30 @@
|
|||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Synchronization
|
||||
{
|
||||
/// <summary>
|
||||
/// Modifier flags for creating host sync.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
internal enum HostSyncFlags
|
||||
{
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Present if host sync is being created by a syncpoint.
|
||||
/// </summary>
|
||||
Syncpoint = 1 << 0,
|
||||
|
||||
/// <summary>
|
||||
/// Present if the sync should signal as soon as possible.
|
||||
/// </summary>
|
||||
Strict = 1 << 1,
|
||||
|
||||
/// <summary>
|
||||
/// Present will force the sync to be created, even if no actions are eligible.
|
||||
/// </summary>
|
||||
Force = 1 << 2,
|
||||
|
||||
StrictSyncpoint = Strict | Syncpoint
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
namespace Ryujinx.Graphics.Gpu.Synchronization
|
||||
{
|
||||
/// <summary>
|
||||
/// This interface indicates that a class can be registered for a sync action.
|
||||
/// </summary>
|
||||
interface ISyncActionHandler
|
||||
{
|
||||
/// <summary>
|
||||
/// Action to be performed when some synchronizing action is reached after modification.
|
||||
/// Generally used to register read/write tracking to flush resources from GPU when their memory is used.
|
||||
/// </summary>
|
||||
/// <param name="syncpoint">True if the action is a guest syncpoint</param>
|
||||
/// <returns>True if the action is to be removed, false otherwise</returns>
|
||||
bool SyncAction(bool syncpoint);
|
||||
|
||||
/// <summary>
|
||||
/// Action to be performed immediately before sync is created.
|
||||
/// </summary>
|
||||
/// <param name="syncpoint">True if the action is a guest syncpoint</param>
|
||||
void SyncPreAction(bool syncpoint) { }
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue