diff options
Diffstat (limited to 'src/Ryujinx.Graphics.Gpu/Image/TextureGroupHandle.cs')
-rw-r--r-- | src/Ryujinx.Graphics.Gpu/Image/TextureGroupHandle.cs | 120 |
1 files changed, 111 insertions, 9 deletions
diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureGroupHandle.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureGroupHandle.cs index ebb4e9ae..9f66744b 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureGroupHandle.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureGroupHandle.cs @@ -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. @@ -43,6 +52,12 @@ namespace Ryujinx.Graphics.Gpu.Image 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> public int Offset { get; } @@ -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> @@ -160,6 +181,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> /// <param name="offset">The offset of the view in the group</param> @@ -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> @@ -297,14 +364,40 @@ namespace Ryujinx.Graphics.Gpu.Image } /// <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> |