diff options
Diffstat (limited to 'src/Ryujinx.Graphics.Gpu')
| -rw-r--r-- | src/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoClass.cs | 7 | ||||
| -rw-r--r-- | src/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs | 3 | ||||
| -rw-r--r-- | src/Ryujinx.Graphics.Gpu/GpuContext.cs | 40 | ||||
| -rw-r--r-- | src/Ryujinx.Graphics.Gpu/Image/Texture.cs | 4 | ||||
| -rw-r--r-- | src/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs | 110 | ||||
| -rw-r--r-- | src/Ryujinx.Graphics.Gpu/Image/TextureGroupHandle.cs | 120 | ||||
| -rw-r--r-- | src/Ryujinx.Graphics.Gpu/Memory/Buffer.cs | 12 | ||||
| -rw-r--r-- | src/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs | 29 | ||||
| -rw-r--r-- | src/Ryujinx.Graphics.Gpu/Synchronization/HostSyncFlags.cs | 30 | ||||
| -rw-r--r-- | src/Ryujinx.Graphics.Gpu/Synchronization/ISyncActionHandler.cs | 22 |
10 files changed, 325 insertions, 52 deletions
diff --git a/src/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoClass.cs b/src/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoClass.cs index e80d98a1..7a11c649 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoClass.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoClass.cs @@ -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> diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs index caeee18e..1386071c 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs @@ -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); } diff --git a/src/Ryujinx.Graphics.Gpu/GpuContext.cs b/src/Ryujinx.Graphics.Gpu/GpuContext.cs index 91758863..bab62b95 100644 --- a/src/Ryujinx.Graphics.Gpu/GpuContext.cs +++ b/src/Ryujinx.Graphics.Gpu/GpuContext.cs @@ -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); - SyncNumber++; - - foreach (Action action in SyncActions) - { - action(); - } + SyncActions.ForEach(action => action.SyncPreAction(syncpoint)); + SyncpointActions.ForEach(action => action.SyncPreAction(syncpoint)); - foreach (Action action in SyncpointActions) - { - action(); - } + SyncNumber++; - SyncActions.Clear(); - SyncpointActions.Clear(); + SyncActions.RemoveAll(action => action.SyncAction(syncpoint)); + SyncpointActions.RemoveAll(action => action.SyncAction(syncpoint)); } _pendingSync = false; diff --git a/src/Ryujinx.Graphics.Gpu/Image/Texture.cs b/src/Ryujinx.Graphics.Gpu/Image/Texture.cs index f0df55e6..3b257988 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/Texture.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/Texture.cs @@ -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); } } 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); + } } } } 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> diff --git a/src/Ryujinx.Graphics.Gpu/Memory/Buffer.cs b/src/Ryujinx.Graphics.Gpu/Memory/Buffer.cs index f267dfda..ef8c8074 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/Buffer.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/Buffer.cs @@ -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; } diff --git a/src/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs b/src/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs index bd33383e..b976667c 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs @@ -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 @@ -83,6 +84,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> /// <param name="address">Start address of the range</param> diff --git a/src/Ryujinx.Graphics.Gpu/Synchronization/HostSyncFlags.cs b/src/Ryujinx.Graphics.Gpu/Synchronization/HostSyncFlags.cs new file mode 100644 index 00000000..42666991 --- /dev/null +++ b/src/Ryujinx.Graphics.Gpu/Synchronization/HostSyncFlags.cs @@ -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 + } +} diff --git a/src/Ryujinx.Graphics.Gpu/Synchronization/ISyncActionHandler.cs b/src/Ryujinx.Graphics.Gpu/Synchronization/ISyncActionHandler.cs new file mode 100644 index 00000000..d470d2f0 --- /dev/null +++ b/src/Ryujinx.Graphics.Gpu/Synchronization/ISyncActionHandler.cs @@ -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) { } + } +} |
