From 898f63e44cae743a2bb192524e79035ac5afc80f Mon Sep 17 00:00:00 2001 From: xam <87-xam@users.noreply.git.ryujinx.app> Date: Sun, 19 Oct 2025 23:22:04 +0200 Subject: [PATCH 1/3] Input: HLE: NpadManager: ignore handheld inputs when docked fixes ghost inputs in games like pokemon when handheld controller is set in docked mode. it is now possible to keep handheld set and switch between modes with no issue. --- src/Ryujinx.Input/HLE/NpadManager.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Ryujinx.Input/HLE/NpadManager.cs b/src/Ryujinx.Input/HLE/NpadManager.cs index 866504128..7b64fbfb8 100644 --- a/src/Ryujinx.Input/HLE/NpadManager.cs +++ b/src/Ryujinx.Input/HLE/NpadManager.cs @@ -224,6 +224,10 @@ namespace Ryujinx.Input.HLE foreach (InputConfig inputConfig in _inputConfig) { + // ignore handheld inputs if docked + if (_device.System.State.DockedMode && inputConfig.PlayerIndex == Common.Configuration.Hid.PlayerIndex.Handheld) + continue; + GamepadInput inputState = default; (SixAxisInput, SixAxisInput) motionState = default; From c3155fcadb4b1ec0322bf93b7d27621198af7d75 Mon Sep 17 00:00:00 2001 From: LotP <22-lotp@users.noreply.git.ryujinx.app> Date: Sat, 6 Dec 2025 17:19:19 -0600 Subject: [PATCH 2/3] Memory Changes 3.2 (ryubing/ryujinx!234) See merge request ryubing/ryujinx!234 --- .../Memory/MemoryStreamManager.cs | 21 +- src/Ryujinx.Graphics.GAL/IPipeline.cs | 2 +- .../Commands/SetRenderTargetsCommand.cs | 14 +- .../Multithreading/ThreadedPipeline.cs | 6 +- .../Multithreading/ThreadedRenderer.cs | 2 +- src/Ryujinx.Graphics.Gpu/Memory/Buffer.cs | 33 +-- .../Memory/BufferBackingState.cs | 6 +- .../Memory/BufferCache.cs | 88 ++------ .../Memory/BufferModifiedRangeList.cs | 167 ++++++++------- .../Memory/VirtualRangeCache.cs | 19 +- src/Ryujinx.Graphics.OpenGL/Pipeline.cs | 2 +- .../FramebufferParams.cs | 32 ++- .../MultiFenceHolder.cs | 8 +- src/Ryujinx.Graphics.Vulkan/PipelineBase.cs | 12 +- src/Ryujinx.Graphics.Vulkan/SyncManager.cs | 5 +- src/Ryujinx.HLE/HOS/Ipc/IpcMessage.cs | 5 +- .../HOS/Kernel/Ipc/KBufferDescriptorTable.cs | 9 + .../HOS/Kernel/Ipc/KClientSession.cs | 5 +- .../HOS/Kernel/Ipc/KServerSession.cs | 12 ++ .../HOS/Kernel/Ipc/KSessionRequest.cs | 16 +- .../HOS/Kernel/Threading/KAddressArbiter.cs | 148 +++++++------ .../HOS/Kernel/Threading/KThread.cs | 2 - src/Ryujinx.HLE/HOS/Services/IpcService.cs | 11 +- .../HOS/Services/Nv/INvDrvServices.cs | 85 ++++---- src/Ryujinx.HLE/HOS/Services/ServerBase.cs | 6 +- .../Sdk/OsTypes/Impl/MultiWaitImpl.cs | 29 ++- src/Ryujinx.Input/HLE/NpadController.cs | 2 - src/Ryujinx.Input/HLE/NpadManager.cs | 23 +- src/Ryujinx.Input/IKeyboard.cs | 12 +- .../Range/INonOverlappingRange.cs | 4 +- src/Ryujinx.Memory/Range/IRange.cs | 4 +- .../Range/NonOverlappingRangeList.cs | 198 +++++------------- src/Ryujinx.Memory/Range/RangeList.cs | 139 ++---------- src/Ryujinx.Memory/Range/RangeListBase.cs | 82 ++------ src/Ryujinx.Memory/Tracking/AbstractRegion.cs | 13 +- src/Ryujinx.Memory/Tracking/MemoryTracking.cs | 14 +- src/Ryujinx.Memory/Tracking/VirtualRegion.cs | 4 +- 37 files changed, 563 insertions(+), 677 deletions(-) diff --git a/src/Ryujinx.Common/Memory/MemoryStreamManager.cs b/src/Ryujinx.Common/Memory/MemoryStreamManager.cs index 834210e07..88f5956e9 100644 --- a/src/Ryujinx.Common/Memory/MemoryStreamManager.cs +++ b/src/Ryujinx.Common/Memory/MemoryStreamManager.cs @@ -7,6 +7,9 @@ namespace Ryujinx.Common.Memory { private static readonly RecyclableMemoryStreamManager _shared = new(); + private static readonly ObjectPool _streamPool = + new(() => new RecyclableMemoryStream(_shared, Guid.NewGuid(), null, 0)); + /// /// We don't expose the RecyclableMemoryStreamManager directly because version 2.x /// returns them as MemoryStream. This Shared class is here to a) offer only the GetStream() versions we use @@ -19,7 +22,12 @@ namespace Ryujinx.Common.Memory /// /// A RecyclableMemoryStream public static RecyclableMemoryStream GetStream() - => new(_shared); + { + RecyclableMemoryStream stream = _streamPool.Allocate(); + stream.SetLength(0); + + return stream; + } /// /// Retrieve a new MemoryStream object with the contents copied from the provided @@ -55,7 +63,8 @@ namespace Ryujinx.Common.Memory RecyclableMemoryStream stream = null; try { - stream = new RecyclableMemoryStream(_shared, id, tag, buffer.Length); + stream = _streamPool.Allocate(); + stream.SetLength(0); stream.Write(buffer); stream.Position = 0; return stream; @@ -83,7 +92,8 @@ namespace Ryujinx.Common.Memory RecyclableMemoryStream stream = null; try { - stream = new RecyclableMemoryStream(_shared, id, tag, count); + stream = _streamPool.Allocate(); + stream.SetLength(0); stream.Write(buffer, offset, count); stream.Position = 0; return stream; @@ -94,6 +104,11 @@ namespace Ryujinx.Common.Memory throw; } } + + public static void ReleaseStream(RecyclableMemoryStream stream) + { + _streamPool.Release(stream); + } } } } diff --git a/src/Ryujinx.Graphics.GAL/IPipeline.cs b/src/Ryujinx.Graphics.GAL/IPipeline.cs index b8409a573..da20da870 100644 --- a/src/Ryujinx.Graphics.GAL/IPipeline.cs +++ b/src/Ryujinx.Graphics.GAL/IPipeline.cs @@ -82,7 +82,7 @@ namespace Ryujinx.Graphics.GAL void SetRasterizerDiscard(bool discard); void SetRenderTargetColorMasks(ReadOnlySpan componentMask); - void SetRenderTargets(ITexture[] colors, ITexture depthStencil); + void SetRenderTargets(Span colors, ITexture depthStencil); void SetScissors(ReadOnlySpan> regions); diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRenderTargetsCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRenderTargetsCommand.cs index ca7c8c8c2..2641ae528 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRenderTargetsCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetRenderTargetsCommand.cs @@ -1,5 +1,6 @@ using Ryujinx.Graphics.GAL.Multithreading.Model; using Ryujinx.Graphics.GAL.Multithreading.Resources; +using System; using System.Buffers; namespace Ryujinx.Graphics.GAL.Multithreading.Commands @@ -8,11 +9,13 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands { public static readonly ArrayPool ArrayPool = ArrayPool.Create(512, 50); public readonly CommandType CommandType => CommandType.SetRenderTargets; + private int _colorsCount; private TableRef _colors; private TableRef _depthStencil; - public void Set(TableRef colors, TableRef depthStencil) + public void Set(int colorsCount, TableRef colors, TableRef depthStencil) { + _colorsCount = colorsCount; _colors = colors; _depthStencil = depthStencil; } @@ -20,16 +23,15 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands public static void Run(ref SetRenderTargetsCommand command, ThreadedRenderer threaded, IRenderer renderer) { ITexture[] colors = command._colors.Get(threaded); - ITexture[] colorsCopy = ArrayPool.Rent(colors.Length); + Span colorsSpan = colors.AsSpan(0, command._colorsCount); - for (int i = 0; i < colors.Length; i++) + for (int i = 0; i < colorsSpan.Length; i++) { - colorsCopy[i] = ((ThreadedTexture)colors[i])?.Base; + colorsSpan[i] = ((ThreadedTexture)colorsSpan[i])?.Base; } - renderer.Pipeline.SetRenderTargets(colorsCopy, command._depthStencil.GetAs(threaded)?.Base); + renderer.Pipeline.SetRenderTargets(colorsSpan, command._depthStencil.GetAs(threaded)?.Base); - ArrayPool.Return(colorsCopy); ArrayPool.Return(colors); } } diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs b/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs index ea3fd1e11..6873574b7 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs @@ -267,12 +267,12 @@ namespace Ryujinx.Graphics.GAL.Multithreading _renderer.QueueCommand(); } - public unsafe void SetRenderTargets(ITexture[] colors, ITexture depthStencil) + public unsafe void SetRenderTargets(Span colors, ITexture depthStencil) { ITexture[] colorsCopy = SetRenderTargetsCommand.ArrayPool.Rent(colors.Length); - colors.CopyTo(colorsCopy, 0); + colors.CopyTo(colorsCopy.AsSpan()); - _renderer.New()->Set(Ref(colorsCopy), Ref(depthStencil)); + _renderer.New()->Set(colors.Length, Ref(colorsCopy), Ref(depthStencil)); _renderer.QueueCommand(); } diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs b/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs index 3c179da36..66ac31ab4 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading /// public class ThreadedRenderer : IRenderer { - private const int SpanPoolBytes = 4 * 1024 * 1024; + private const int SpanPoolBytes = 8 * 1024 * 1024; private const int MaxRefsPerCommand = 2; private const int QueueCount = 10000; diff --git a/src/Ryujinx.Graphics.Gpu/Memory/Buffer.cs b/src/Ryujinx.Graphics.Gpu/Memory/Buffer.cs index 277a30689..f04576c2a 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/Buffer.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/Buffer.cs @@ -15,7 +15,7 @@ namespace Ryujinx.Graphics.Gpu.Memory /// /// Buffer, used to store vertex and index data, uniform and storage buffers, and others. /// - class Buffer : INonOverlappingRange, ISyncActionHandler, IDisposable + class Buffer : INonOverlappingRange, ISyncActionHandler, IDisposable { private const ulong GranularBufferThreshold = 4096; @@ -41,6 +41,9 @@ namespace Ryujinx.Graphics.Gpu.Memory /// End address of the buffer in guest memory. /// public ulong EndAddress => Address + Size; + + public Buffer Next { get; set; } + public Buffer Previous { get; set; } /// /// Increments when the buffer is (partially) unmapped or disposed. @@ -87,6 +90,7 @@ namespace Ryujinx.Graphics.Gpu.Memory private readonly bool _useGranular; private bool _syncActionRegistered; + private bool _bufferInherited; private int _referenceCount = 1; @@ -113,7 +117,7 @@ namespace Ryujinx.Graphics.Gpu.Memory ulong size, BufferStage stage, bool sparseCompatible, - RangeItem[] baseBuffers) + Buffer[] baseBuffers) { _context = context; _physicalMemory = physicalMemory; @@ -134,15 +138,15 @@ namespace Ryujinx.Graphics.Gpu.Memory if (baseBuffers.Length != 0) { baseHandles = new List(); - foreach (RangeItem item in baseBuffers) + foreach (Buffer item in baseBuffers) { - if (item.Value._useGranular) + if (item._useGranular) { - baseHandles.AddRange(item.Value._memoryTrackingGranular.Handles); + baseHandles.AddRange(item._memoryTrackingGranular.Handles); } else { - baseHandles.Add(item.Value._memoryTracking); + baseHandles.Add(item._memoryTracking); } } } @@ -247,14 +251,14 @@ namespace Ryujinx.Graphics.Gpu.Memory /// Checks if a given range overlaps with the buffer. /// /// Start address of the range - /// Size in bytes of the range + /// End address of the range /// True if the range overlaps, false otherwise - public bool OverlapsWith(ulong address, ulong size) + public bool OverlapsWith(ulong address, ulong endAddress) { - return Address < address + size && address < EndAddress; + return Address < endAddress && address < EndAddress; } - public INonOverlappingRange Split(ulong splitAddress) + public INonOverlappingRange Split(ulong splitAddress) { throw new NotImplementedException(); } @@ -426,10 +430,13 @@ namespace Ryujinx.Graphics.Gpu.Memory { _syncActionRegistered = false; + if (_bufferInherited) + { + return true; + } + if (_useGranular) { - - _modifiedRanges?.GetRanges(Address, Size, _syncRangeAction); } else @@ -453,6 +460,8 @@ namespace Ryujinx.Graphics.Gpu.Memory /// The buffer to inherit from public void InheritModifiedRanges(Buffer from) { + from._bufferInherited = true; + if (from._modifiedRanges is { HasRanges: true }) { if (from._syncActionRegistered && !_syncActionRegistered) diff --git a/src/Ryujinx.Graphics.Gpu/Memory/BufferBackingState.cs b/src/Ryujinx.Graphics.Gpu/Memory/BufferBackingState.cs index a81e7e98f..e674eb1d7 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/BufferBackingState.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/BufferBackingState.cs @@ -56,7 +56,7 @@ namespace Ryujinx.Graphics.Gpu.Memory /// Parent buffer /// Initial buffer stage /// Buffers to inherit state from - public BufferBackingState(GpuContext context, Buffer parent, BufferStage stage, RangeItem[] baseBuffers) + public BufferBackingState(GpuContext context, Buffer parent, BufferStage stage, Buffer[] baseBuffers) { _size = (int)parent.Size; _systemMemoryType = context.Capabilities.MemoryType; @@ -102,9 +102,9 @@ namespace Ryujinx.Graphics.Gpu.Memory if (baseBuffers.Length != 0) { - foreach (RangeItem item in baseBuffers) + foreach (Buffer item in baseBuffers) { - CombineState(item.Value.BackingState); + CombineState(item.BackingState); } } } diff --git a/src/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs b/src/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs index 0d623ff95..83869ed02 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs @@ -79,16 +79,13 @@ namespace Ryujinx.Graphics.Gpu.Memory for (int index = 0; index < range.Count; index++) { MemoryRange subRange = range.GetSubRange(index); - - _buffers.Lock.EnterReadLock(); - Span> overlaps = _buffers.FindOverlapsAsSpan(subRange.Address, subRange.Size); + + ReadOnlySpan overlaps = _buffers.FindOverlapsAsSpan(subRange.Address, subRange.Size); for (int i = 0; i < overlaps.Length; i++) { - overlaps[i].Value.Unmapped(subRange.Address, subRange.Size); + overlaps[i].Unmapped(subRange.Address, subRange.Size); } - - _buffers.Lock.ExitReadLock(); } } @@ -328,7 +325,7 @@ namespace Ryujinx.Graphics.Gpu.Memory ulong alignedEndAddress = (endAddress + alignmentMask) & ~alignmentMask; ulong alignedSize = alignedEndAddress - alignedAddress; - Buffer buffer = _buffers.FindOverlap(alignedAddress, alignedSize).Value; + Buffer buffer = _buffers.FindOverlap(alignedAddress, alignedSize); BufferRange bufferRange = buffer.GetRange(alignedAddress, alignedSize, false); alignedSubRanges[i] = new MemoryRange(alignedAddress, alignedSize); @@ -395,7 +392,7 @@ namespace Ryujinx.Graphics.Gpu.Memory if (subRange.Address != MemoryManager.PteUnmapped) { - Buffer buffer = _buffers.FindOverlap(subRange.Address, subRange.Size).Value; + Buffer buffer = _buffers.FindOverlap(subRange.Address, subRange.Size); virtualBuffer.AddPhysicalDependency(buffer, subRange.Address, dstOffset, subRange.Size); physicalBuffers.Add(buffer); @@ -487,10 +484,7 @@ namespace Ryujinx.Graphics.Gpu.Memory /// The type of usage that created the buffer private void CreateBufferAligned(ulong address, ulong size, BufferStage stage) { - Buffer newBuffer = null; - - _buffers.Lock.EnterWriteLock(); - Span> overlaps = _buffers.FindOverlapsAsSpan(address, size); + ReadOnlySpan overlaps = _buffers.FindOverlapsAsSpan(address, size); if (overlaps.Length != 0) { @@ -521,7 +515,7 @@ namespace Ryujinx.Graphics.Gpu.Memory { // Try to grow the buffer by 1.5x of its current size. // This improves performance in the cases where the buffer is resized often by small amounts. - ulong existingSize = overlaps[0].Value.Size; + ulong existingSize = overlaps[0].Size; ulong growthSize = (existingSize + Math.Min(existingSize >> 1, MaxDynamicGrowthSize)) & ~BufferAlignmentMask; size = Math.Max(size, growthSize); @@ -535,39 +529,22 @@ namespace Ryujinx.Graphics.Gpu.Memory for (int i = 0; i < overlaps.Length; i++) { - anySparseCompatible |= overlaps[i].Value.SparseCompatible; + anySparseCompatible |= overlaps[i].SparseCompatible; } - RangeItem[] overlapsArray = overlaps.ToArray(); + Buffer[] overlapsArray = overlaps.ToArray(); _buffers.RemoveRange(overlaps[0], overlaps[^1]); - _buffers.Lock.ExitWriteLock(); - ulong newSize = endAddress - address; - newBuffer = CreateBufferAligned(address, newSize, stage, anySparseCompatible, overlapsArray); - } - else - { - _buffers.Lock.ExitWriteLock(); + _buffers.Add(CreateBufferAligned(address, newSize, stage, anySparseCompatible, overlapsArray)); } } else { - _buffers.Lock.ExitWriteLock(); - // No overlap, just create a new buffer. - newBuffer = new(_context, _physicalMemory, address, size, stage, sparseCompatible: false, []); - } - - if (newBuffer is not null) - { - _buffers.Lock.EnterWriteLock(); - - _buffers.Add(newBuffer); - - _buffers.Lock.ExitWriteLock(); + _buffers.Add(new(_context, _physicalMemory, address, size, stage, sparseCompatible: false, [])); } } @@ -583,10 +560,8 @@ namespace Ryujinx.Graphics.Gpu.Memory private void CreateBufferAligned(ulong address, ulong size, BufferStage stage, ulong alignment) { bool sparseAligned = alignment >= SparseBufferAlignmentSize; - Buffer newBuffer = null; - _buffers.Lock.EnterWriteLock(); - Span> overlaps = _buffers.FindOverlapsAsSpan(address, size); + ReadOnlySpan overlaps = _buffers.FindOverlapsAsSpan(address, size); if (overlaps.Length != 0) { @@ -598,7 +573,7 @@ namespace Ryujinx.Graphics.Gpu.Memory if (overlaps[0].Address > address || overlaps[0].EndAddress < endAddress || (overlaps[0].Address & (alignment - 1)) != 0 || - (!overlaps[0].Value.SparseCompatible && sparseAligned)) + (!overlaps[0].SparseCompatible && sparseAligned)) { // We need to make sure the new buffer is properly aligned. // However, after the range is aligned, it is possible that it @@ -622,35 +597,18 @@ namespace Ryujinx.Graphics.Gpu.Memory ulong newSize = endAddress - address; - RangeItem[] overlapsArray = overlaps.ToArray(); + Buffer[] overlapsArray = overlaps.ToArray(); _buffers.RemoveRange(overlaps[0], overlaps[^1]); - _buffers.Lock.ExitWriteLock(); - - newBuffer = CreateBufferAligned(address, newSize, stage, sparseAligned, overlapsArray); - } - else - { - _buffers.Lock.ExitWriteLock(); + _buffers.Add(CreateBufferAligned(address, newSize, stage, sparseAligned, overlapsArray)); } } else { - _buffers.Lock.ExitWriteLock(); - // No overlap, just create a new buffer. - newBuffer = new(_context, _physicalMemory, address, size, stage, sparseAligned, []); - } - - if (newBuffer is not null) - { - _buffers.Lock.EnterWriteLock(); - - _buffers.Add(newBuffer); - - _buffers.Lock.ExitWriteLock(); - } + _buffers.Add(new(_context, _physicalMemory, address, size, stage, sparseAligned, [])); + } } /// @@ -663,13 +621,13 @@ namespace Ryujinx.Graphics.Gpu.Memory /// The type of usage that created the buffer /// Indicates if the buffer can be used in a sparse buffer mapping /// Buffers overlapping the range - private Buffer CreateBufferAligned(ulong address, ulong size, BufferStage stage, bool sparseCompatible, RangeItem[] overlaps) + private Buffer CreateBufferAligned(ulong address, ulong size, BufferStage stage, bool sparseCompatible, Buffer[] overlaps) { Buffer newBuffer = new(_context, _physicalMemory, address, size, stage, sparseCompatible, overlaps); for (int index = 0; index < overlaps.Length; index++) { - Buffer buffer = overlaps[index].Value; + Buffer buffer = overlaps[index]; int dstOffset = (int)(buffer.Address - newBuffer.Address); @@ -897,7 +855,7 @@ namespace Ryujinx.Graphics.Gpu.Memory { MemoryRange subRange = range.GetSubRange(i); - Buffer subBuffer = _buffers.FindOverlap(subRange.Address, subRange.Size).Value; + Buffer subBuffer = _buffers.FindOverlap(subRange.Address, subRange.Size); subBuffer.SynchronizeMemory(subRange.Address, subRange.Size); @@ -945,7 +903,7 @@ namespace Ryujinx.Graphics.Gpu.Memory if (size != 0) { - buffer = _buffers.FindOverlap(address, size).Value; + buffer = _buffers.FindOverlap(address, size); buffer.CopyFromDependantVirtualBuffers(); buffer.SynchronizeMemory(address, size); @@ -957,7 +915,7 @@ namespace Ryujinx.Graphics.Gpu.Memory } else { - buffer = _buffers.FindOverlapFast(address, 1).Value; + buffer = _buffers.FindOverlapFast(address, 1); } return buffer; @@ -995,7 +953,7 @@ namespace Ryujinx.Graphics.Gpu.Memory { if (size != 0) { - Buffer buffer = _buffers.FindOverlap(address, size).Value; + Buffer buffer = _buffers.FindOverlap(address, size); if (copyBackVirtual) { diff --git a/src/Ryujinx.Graphics.Gpu/Memory/BufferModifiedRangeList.cs b/src/Ryujinx.Graphics.Gpu/Memory/BufferModifiedRangeList.cs index 9c50eaf2f..aef04abc6 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/BufferModifiedRangeList.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/BufferModifiedRangeList.cs @@ -8,7 +8,7 @@ namespace Ryujinx.Graphics.Gpu.Memory /// /// A range within a buffer that has been modified by the GPU. /// - class BufferModifiedRange : INonOverlappingRange + class BufferModifiedRange : INonOverlappingRange { /// /// Start address of the range in guest memory. @@ -24,6 +24,9 @@ namespace Ryujinx.Graphics.Gpu.Memory /// End address of the range in guest memory. /// public ulong EndAddress => Address + Size; + + public BufferModifiedRange Next { get; set; } + public BufferModifiedRange Previous { get; set; } /// /// The GPU sync number at the time of the last modification. @@ -54,14 +57,14 @@ namespace Ryujinx.Graphics.Gpu.Memory /// Checks if a given range overlaps with the modified range. /// /// Start address of the range - /// Size in bytes of the range + /// End address of the range /// True if the range overlaps, false otherwise - public bool OverlapsWith(ulong address, ulong size) + public bool OverlapsWith(ulong address, ulong endAddress) { - return Address < address + size && address < EndAddress; + return Address < endAddress && address < EndAddress; } - public INonOverlappingRange Split(ulong splitAddress) + public INonOverlappingRange Split(ulong splitAddress) { throw new NotImplementedException(); } @@ -119,11 +122,11 @@ namespace Ryujinx.Graphics.Gpu.Memory // Slices a given region using the modified regions in the list. Calls the action for the new slices. Lock.EnterReadLock(); - Span> overlaps = FindOverlapsAsSpan(address, size); + ReadOnlySpan overlaps = FindOverlapsAsSpan(address, size); for (int i = 0; i < overlaps.Length; i++) { - BufferModifiedRange overlap = overlaps[i].Value; + BufferModifiedRange overlap = overlaps[i]; if (overlap.Address > address) { @@ -157,7 +160,7 @@ namespace Ryujinx.Graphics.Gpu.Memory ulong syncNumber = _context.SyncNumber; // We may overlap with some existing modified regions. They must be cut into by the new entry. Lock.EnterWriteLock(); - (RangeItem first, RangeItem last) = FindOverlapsAsNodes(address, size); + (BufferModifiedRange first, BufferModifiedRange last) = FindOverlapsAsNodes(address, size); if (first is null) { @@ -170,34 +173,39 @@ namespace Ryujinx.Graphics.Gpu.Memory { if (first.Address == address && first.EndAddress == endAddress) { - first.Value.SyncNumber = syncNumber; - first.Value.Parent = this; + first.SyncNumber = syncNumber; + first.Parent = this; Lock.ExitWriteLock(); return; } if (first.Address < address) { - first.Value.Size = address - first.Address; - Update(first); - if (first.EndAddress > endAddress) { Add(new BufferModifiedRange(endAddress, first.EndAddress - endAddress, - first.Value.SyncNumber, first.Value.Parent)); + first.SyncNumber, first.Parent)); } + + first.Size = address - first.Address; } else { if (first.EndAddress > endAddress) { - first.Value.Size = first.EndAddress - endAddress; - first.Value.Address = endAddress; - Update(first); + first.Size = first.EndAddress - endAddress; + first.Address = endAddress; } else { - Remove(first.Value); + first.Address = address; + first.Size = size; + first.SyncNumber = syncNumber; + first.Parent = this; + + Lock.ExitWriteLock(); + + return; } } @@ -207,38 +215,39 @@ namespace Ryujinx.Graphics.Gpu.Memory return; } - BufferModifiedRange buffPre = null; - BufferModifiedRange buffPost = null; - bool extendsPost = false; - bool extendsPre = false; - if (first.Address < address) { - buffPre = new BufferModifiedRange(first.Address, address - first.Address, - first.Value.SyncNumber, first.Value.Parent); - extendsPre = true; + first.Size = address - first.Address; + first = first.Next; } if (last.EndAddress > endAddress) { - buffPost = new BufferModifiedRange(endAddress, last.EndAddress - endAddress, - last.Value.SyncNumber, last.Value.Parent); - extendsPost = true; + last.Size = last.EndAddress - endAddress; + last.Address = endAddress; + last = last.Previous; } - RemoveRange(first, last); - - if (extendsPre) + if (first.Address < last.Address) { - Add(buffPre); + RemoveRange(first.Next, last); + first.Address = address; + first.Size = size; + first.SyncNumber = syncNumber; + first.Parent = this; } - - if (extendsPost) + else if (first.Address == last.Address) { - Add(buffPost); + first.Address = address; + first.Size = size; + first.SyncNumber = syncNumber; + first.Parent = this; } - - Add(new BufferModifiedRange(address, size, syncNumber, this)); + else + { + Add(new BufferModifiedRange(address, size, syncNumber, this)); + } + Lock.ExitWriteLock(); } @@ -252,11 +261,11 @@ namespace Ryujinx.Graphics.Gpu.Memory public void GetRangesAtSync(ulong address, ulong size, ulong syncNumber, Action rangeAction) { Lock.EnterReadLock(); - Span> overlaps = FindOverlapsAsSpan(address, size); + ReadOnlySpan overlaps = FindOverlapsAsSpan(address, size); for (int i = 0; i < overlaps.Length; i++) { - BufferModifiedRange overlap = overlaps[i].Value; + BufferModifiedRange overlap = overlaps[i]; if (overlap.SyncNumber == syncNumber) { @@ -277,18 +286,18 @@ namespace Ryujinx.Graphics.Gpu.Memory { // We use the non-span method here because keeping the lock will cause a deadlock. Lock.EnterReadLock(); - RangeItem[] overlaps = FindOverlapsAsArray(address, size, out int length); + BufferModifiedRange[] overlaps = FindOverlapsAsArray(address, size, out int length); Lock.ExitReadLock(); if (length != 0) { for (int i = 0; i < length; i++) { - BufferModifiedRange overlap = overlaps[i].Value; + BufferModifiedRange overlap = overlaps[i]; rangeAction(overlap.Address, overlap.Size); } - ArrayPool>.Shared.Return(overlaps); + ArrayPool.Shared.Return(overlaps); } } @@ -301,7 +310,7 @@ namespace Ryujinx.Graphics.Gpu.Memory public bool HasRange(ulong address, ulong size) { Lock.EnterReadLock(); - RangeItem first = FindOverlapFast(address, size); + BufferModifiedRange first = FindOverlapFast(address, size); bool result = first is not null; Lock.ExitReadLock(); return result; @@ -336,7 +345,7 @@ namespace Ryujinx.Graphics.Gpu.Memory /// The start address of the flush range /// The end address of the flush range private void RemoveRangesAndFlush( - RangeItem[] overlaps, + BufferModifiedRange[] overlaps, int rangeCount, long highestDiff, ulong currentSync, @@ -349,7 +358,7 @@ namespace Ryujinx.Graphics.Gpu.Memory for (int i = 0; i < rangeCount; i++) { - BufferModifiedRange overlap = overlaps[i].Value; + BufferModifiedRange overlap = overlaps[i]; long diff = (long)(overlap.SyncNumber - currentSync); @@ -358,7 +367,14 @@ namespace Ryujinx.Graphics.Gpu.Memory ulong clampAddress = Math.Max(address, overlap.Address); ulong clampEnd = Math.Min(endAddress, overlap.EndAddress); - ClearPart(overlap, clampAddress, clampEnd); + if (i == 0 || i == rangeCount - 1) + { + ClearPart(overlap, clampAddress, clampEnd); + } + else + { + Remove(overlap); + } RangeActionWithMigration(clampAddress, clampEnd - clampAddress, waitSync, _flushAction); } @@ -398,7 +414,7 @@ namespace Ryujinx.Graphics.Gpu.Memory Lock.EnterWriteLock(); // We use the non-span method here because the array is partially modified by the code, which would invalidate a span. - RangeItem[] overlaps = FindOverlapsAsArray(address, size, out int rangeCount); + BufferModifiedRange[] overlaps = FindOverlapsAsArray(address, size, out int rangeCount); if (rangeCount == 0) { @@ -414,7 +430,7 @@ namespace Ryujinx.Graphics.Gpu.Memory for (int i = 0; i < rangeCount; i++) { - BufferModifiedRange overlap = overlaps![i].Value; + BufferModifiedRange overlap = overlaps![i]; long diff = (long)(overlap.SyncNumber - currentSync); @@ -436,7 +452,7 @@ namespace Ryujinx.Graphics.Gpu.Memory RemoveRangesAndFlush(overlaps, rangeCount, highestDiff, currentSync, address, endAddress); - ArrayPool>.Shared.Return(overlaps!); + ArrayPool.Shared.Return(overlaps!); Lock.ExitWriteLock(); } @@ -452,7 +468,9 @@ namespace Ryujinx.Graphics.Gpu.Memory public void InheritRanges(BufferModifiedRangeList ranges, Action registerRangeAction) { ranges.Lock.EnterReadLock(); - BufferModifiedRange[] inheritRanges = ranges.ToArray(); + int rangesCount = ranges.Count; + BufferModifiedRange[] inheritRanges = ArrayPool.Shared.Rent(ranges.Count); + ranges.Items.AsSpan(0, ranges.Count).CopyTo(inheritRanges); ranges.Lock.ExitReadLock(); // Copy over the migration from the previous range list @@ -478,22 +496,26 @@ namespace Ryujinx.Graphics.Gpu.Memory ranges._migrationTarget = this; Lock.EnterWriteLock(); - - foreach (BufferModifiedRange range in inheritRanges) + + for (int i = 0; i < rangesCount; i++) { + BufferModifiedRange range = inheritRanges[i]; Add(range); } Lock.ExitWriteLock(); ulong currentSync = _context.SyncNumber; - foreach (BufferModifiedRange range in inheritRanges) + for (int i = 0; i < rangesCount; i++) { + BufferModifiedRange range = inheritRanges[i]; if (range.SyncNumber != currentSync) { registerRangeAction(range.Address, range.Size); } } + + ArrayPool.Shared.Return(inheritRanges); } /// @@ -534,18 +556,25 @@ namespace Ryujinx.Graphics.Gpu.Memory private void ClearPart(BufferModifiedRange overlap, ulong address, ulong endAddress) { - Remove(overlap); - // If the overlap extends outside of the clear range, make sure those parts still exist. if (overlap.Address < address) { - Add(new BufferModifiedRange(overlap.Address, address - overlap.Address, overlap.SyncNumber, overlap.Parent)); + if (overlap.EndAddress > endAddress) + { + Add(new BufferModifiedRange(endAddress, overlap.EndAddress - endAddress, overlap.SyncNumber, overlap.Parent)); + } + + overlap.Size = address - overlap.Address; } - - if (overlap.EndAddress > endAddress) + else if (overlap.EndAddress > endAddress) { - Add(new BufferModifiedRange(endAddress, overlap.EndAddress - endAddress, overlap.SyncNumber, overlap.Parent)); + overlap.Size = overlap.EndAddress - endAddress; + overlap.Address = endAddress; + } + else + { + Remove(overlap); } } @@ -558,7 +587,7 @@ namespace Ryujinx.Graphics.Gpu.Memory { ulong endAddress = address + size; Lock.EnterWriteLock(); - (RangeItem first, RangeItem last) = FindOverlapsAsNodes(address, size); + (BufferModifiedRange first, BufferModifiedRange last) = FindOverlapsAsNodes(address, size); if (first is null) { @@ -570,26 +599,24 @@ namespace Ryujinx.Graphics.Gpu.Memory { if (first.Address < address) { - first.Value.Size = address - first.Address; - Update(first); + first.Size = address - first.Address; if (first.EndAddress > endAddress) { Add(new BufferModifiedRange(endAddress, first.EndAddress - endAddress, - first.Value.SyncNumber, first.Value.Parent)); + first.SyncNumber, first.Parent)); } } else { if (first.EndAddress > endAddress) { - first.Value.Size = first.EndAddress - endAddress; - first.Value.Address = endAddress; - Update(first); + first.Size = first.EndAddress - endAddress; + first.Address = endAddress; } else { - Remove(first.Value); + Remove(first); } } @@ -605,14 +632,14 @@ namespace Ryujinx.Graphics.Gpu.Memory if (first.Address < address) { buffPre = new BufferModifiedRange(first.Address, address - first.Address, - first.Value.SyncNumber, first.Value.Parent); + first.SyncNumber, first.Parent); extendsPre = true; } if (last.EndAddress > endAddress) { buffPost = new BufferModifiedRange(endAddress, last.EndAddress - endAddress, - last.Value.SyncNumber, last.Value.Parent); + last.SyncNumber, last.Parent); extendsPost = true; } diff --git a/src/Ryujinx.Graphics.Gpu/Memory/VirtualRangeCache.cs b/src/Ryujinx.Graphics.Gpu/Memory/VirtualRangeCache.cs index 1d44ee65f..0339d8617 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/VirtualRangeCache.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/VirtualRangeCache.cs @@ -15,7 +15,7 @@ namespace Ryujinx.Graphics.Gpu.Memory /// /// Represents a GPU virtual memory range. /// - private class VirtualRange : INonOverlappingRange + private class VirtualRange : INonOverlappingRange { /// /// GPU virtual address where the range starts. @@ -32,6 +32,9 @@ namespace Ryujinx.Graphics.Gpu.Memory /// public ulong EndAddress => Address + Size; + public VirtualRange Next { get; set; } + public VirtualRange Previous { get; set; } + /// /// Physical regions where the GPU virtual region is mapped. /// @@ -54,14 +57,14 @@ namespace Ryujinx.Graphics.Gpu.Memory /// Checks if a given range overlaps with the buffer. /// /// Start address of the range - /// Size in bytes of the range + /// End address of the range /// True if the range overlaps, false otherwise - public bool OverlapsWith(ulong address, ulong size) + public bool OverlapsWith(ulong address, ulong endAddress) { - return Address < address + size && address < EndAddress; + return Address < endAddress && address < EndAddress; } - public INonOverlappingRange Split(ulong splitAddress) + public INonOverlappingRange Split(ulong splitAddress) { throw new NotImplementedException(); } @@ -122,7 +125,7 @@ namespace Ryujinx.Graphics.Gpu.Memory ulong originalVa = gpuVa; _virtualRanges.Lock.EnterWriteLock(); - (RangeItem first, RangeItem last) = _virtualRanges.FindOverlapsAsNodes(gpuVa, size); + (VirtualRange first, VirtualRange last) = _virtualRanges.FindOverlapsAsNodes(gpuVa, size); if (first is not null) { @@ -147,8 +150,8 @@ namespace Ryujinx.Graphics.Gpu.Memory } else { - found = first.Value.Range.Count == 1 || IsSparseAligned(first.Value.Range); - range = first.Value.Range.Slice(gpuVa - first.Address, size); + found = first.Range.Count == 1 || IsSparseAligned(first.Range); + range = first.Range.Slice(gpuVa - first.Address, size); } } else diff --git a/src/Ryujinx.Graphics.OpenGL/Pipeline.cs b/src/Ryujinx.Graphics.OpenGL/Pipeline.cs index c8ca02140..e58e6f2b9 100644 --- a/src/Ryujinx.Graphics.OpenGL/Pipeline.cs +++ b/src/Ryujinx.Graphics.OpenGL/Pipeline.cs @@ -1166,7 +1166,7 @@ namespace Ryujinx.Graphics.OpenGL } } - public void SetRenderTargets(ITexture[] colors, ITexture depthStencil) + public void SetRenderTargets(Span colors, ITexture depthStencil) { EnsureFramebuffer(); diff --git a/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs b/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs index 919c45b9d..6d03fcd0d 100644 --- a/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs +++ b/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs @@ -71,17 +71,31 @@ namespace Ryujinx.Graphics.Vulkan HasDepthStencil = isDepthStencil; } - public FramebufferParams(Device device, ITexture[] colors, ITexture depthStencil) + public FramebufferParams(Device device, ReadOnlySpan colors, ITexture depthStencil) { _device = device; - int colorsCount = colors.Count(IsValidTextureView); + int colorsCount = 0; + _colorsCanonical = new TextureView[colors.Length]; + + for (int i = 0; i < colors.Length; i++) + { + ITexture color = colors[i]; + if (color is TextureView { Valid: true } view) + { + colorsCount++; + _colorsCanonical[i] = view; + } + else + { + _colorsCanonical[i] = null; + } + } int count = colorsCount + (IsValidTextureView(depthStencil) ? 1 : 0); _attachments = new Auto[count]; _colors = new TextureView[colorsCount]; - _colorsCanonical = colors.Select(color => color is TextureView view && view.Valid ? view : null).ToArray(); AttachmentSamples = new uint[count]; AttachmentFormats = new VkFormat[count]; @@ -165,9 +179,17 @@ namespace Ryujinx.Graphics.Vulkan _totalCount = colors.Length; } - public FramebufferParams Update(ITexture[] colors, ITexture depthStencil) + public FramebufferParams Update(ReadOnlySpan colors, ITexture depthStencil) { - int colorsCount = colors.Count(IsValidTextureView); + int colorsCount = 0; + + foreach (ITexture color in colors) + { + if (IsValidTextureView(color)) + { + colorsCount++; + } + } int count = colorsCount + (IsValidTextureView(depthStencil) ? 1 : 0); diff --git a/src/Ryujinx.Graphics.Vulkan/MultiFenceHolder.cs b/src/Ryujinx.Graphics.Vulkan/MultiFenceHolder.cs index 40ad7716d..b226ce1f3 100644 --- a/src/Ryujinx.Graphics.Vulkan/MultiFenceHolder.cs +++ b/src/Ryujinx.Graphics.Vulkan/MultiFenceHolder.cs @@ -1,7 +1,7 @@ +using Ryujinx.Common; using Ryujinx.Common.Memory; using Silk.NET.Vulkan; using System; -using System.Buffers; namespace Ryujinx.Graphics.Vulkan { @@ -10,6 +10,8 @@ namespace Ryujinx.Graphics.Vulkan /// class MultiFenceHolder { + public static readonly ObjectPool FencePool = new(() => new FenceHolder[CommandBufferPool.MaxCommandBuffers]); + private const int BufferUsageTrackingGranularity = 4096; public FenceHolder[] Fences { get; } @@ -20,7 +22,7 @@ namespace Ryujinx.Graphics.Vulkan /// public MultiFenceHolder() { - Fences = ArrayPool.Shared.Rent(CommandBufferPool.MaxCommandBuffers); + Fences = FencePool.Allocate(); } /// @@ -29,7 +31,7 @@ namespace Ryujinx.Graphics.Vulkan /// Size of the buffer public MultiFenceHolder(int size) { - Fences = ArrayPool.Shared.Rent(CommandBufferPool.MaxCommandBuffers); + Fences = FencePool.Allocate(); _bufferUsageBitmap = new BufferUsageBitmap(size, BufferUsageTrackingGranularity); } diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs index f2f68378f..0172b5b56 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -1035,7 +1035,7 @@ namespace Ryujinx.Graphics.Vulkan } } - private void SetRenderTargetsInternal(ITexture[] colors, ITexture depthStencil, bool filterWriteMasked) + private void SetRenderTargetsInternal(Span colors, ITexture depthStencil, bool filterWriteMasked) { CreateFramebuffer(colors, depthStencil, filterWriteMasked); CreateRenderPass(); @@ -1043,7 +1043,7 @@ namespace Ryujinx.Graphics.Vulkan SignalAttachmentChange(); } - public void SetRenderTargets(ITexture[] colors, ITexture depthStencil) + public void SetRenderTargets(Span colors, ITexture depthStencil) { _framebufferUsingColorWriteMask = false; SetRenderTargetsInternal(colors, depthStencil, Gd.IsTBDR); @@ -1389,7 +1389,7 @@ namespace Ryujinx.Graphics.Vulkan _currentPipelineHandle = 0; } - private void CreateFramebuffer(ITexture[] colors, ITexture depthStencil, bool filterWriteMasked) + private void CreateFramebuffer(Span colors, ITexture depthStencil, bool filterWriteMasked) { if (filterWriteMasked) { @@ -1399,7 +1399,7 @@ namespace Ryujinx.Graphics.Vulkan // Just try to remove duplicate attachments. // Save a copy of the array to rebind when mask changes. - void MaskOut() + void MaskOut(ReadOnlySpan colors) { if (!_framebufferUsingColorWriteMask) { @@ -1436,12 +1436,12 @@ namespace Ryujinx.Graphics.Vulkan if (vkBlend.ColorWriteMask == 0) { colors[i] = null; - MaskOut(); + MaskOut(colors); } else if (vkBlend2.ColorWriteMask == 0) { colors[j] = null; - MaskOut(); + MaskOut(colors); } } } diff --git a/src/Ryujinx.Graphics.Vulkan/SyncManager.cs b/src/Ryujinx.Graphics.Vulkan/SyncManager.cs index 149759906..15759b0de 100644 --- a/src/Ryujinx.Graphics.Vulkan/SyncManager.cs +++ b/src/Ryujinx.Graphics.Vulkan/SyncManager.cs @@ -1,6 +1,6 @@ using Ryujinx.Common.Logging; using Silk.NET.Vulkan; -using System.Buffers; +using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; @@ -193,7 +193,8 @@ namespace Ryujinx.Graphics.Vulkan { _firstHandle = first.ID + 1; _handles.RemoveAt(0); - ArrayPool.Shared.Return(first.Waitable.Fences); + Array.Clear(first.Waitable.Fences); + MultiFenceHolder.FencePool.Release(first.Waitable.Fences); first.Waitable = null; } } diff --git a/src/Ryujinx.HLE/HOS/Ipc/IpcMessage.cs b/src/Ryujinx.HLE/HOS/Ipc/IpcMessage.cs index 7df2b778f..13a93db39 100644 --- a/src/Ryujinx.HLE/HOS/Ipc/IpcMessage.cs +++ b/src/Ryujinx.HLE/HOS/Ipc/IpcMessage.cs @@ -2,6 +2,7 @@ using Microsoft.IO; using Ryujinx.Common; using Ryujinx.Common.Memory; using System; +using System.Buffers; using System.Collections.Generic; using System.Diagnostics; using System.IO; @@ -37,7 +38,7 @@ namespace Ryujinx.HLE.HOS.Ipc public IpcMessage(ReadOnlySpan data, long cmdPtr) { - using RecyclableMemoryStream ms = MemoryStreamManager.Shared.GetStream(data); + RecyclableMemoryStream ms = MemoryStreamManager.Shared.GetStream(data); BinaryReader reader = new(ms); @@ -123,6 +124,8 @@ namespace Ryujinx.HLE.HOS.Ipc } ObjectIds = []; + + MemoryStreamManager.Shared.ReleaseStream(ms); } public RecyclableMemoryStream GetStream(long cmdPtr, ulong recvListAddr) diff --git a/src/Ryujinx.HLE/HOS/Kernel/Ipc/KBufferDescriptorTable.cs b/src/Ryujinx.HLE/HOS/Kernel/Ipc/KBufferDescriptorTable.cs index 373899b7b..d22cfb469 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Ipc/KBufferDescriptorTable.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Ipc/KBufferDescriptorTable.cs @@ -20,6 +20,15 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc _exchangeBufferDescriptors = new List(MaxInternalBuffersCount); } + public KBufferDescriptorTable Clear() + { + _sendBufferDescriptors.Clear(); + _receiveBufferDescriptors.Clear(); + _exchangeBufferDescriptors.Clear(); + + return this; + } + public Result AddSendBuffer(ulong src, ulong dst, ulong size, MemoryState state) { return Add(_sendBufferDescriptors, src, dst, size, state); diff --git a/src/Ryujinx.HLE/HOS/Kernel/Ipc/KClientSession.cs b/src/Ryujinx.HLE/HOS/Kernel/Ipc/KClientSession.cs index 385f09020..d83e14ba3 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Ipc/KClientSession.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Ipc/KClientSession.cs @@ -1,3 +1,4 @@ +using Ryujinx.Common; using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Process; using Ryujinx.HLE.HOS.Kernel.Threading; @@ -32,7 +33,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc { KThread currentThread = KernelStatic.GetCurrentThread(); - KSessionRequest request = new(currentThread, customCmdBuffAddr, customCmdBuffSize); + KSessionRequest request = _parent.ServerSession.RequestPool.Allocate().Set(currentThread, customCmdBuffAddr, customCmdBuffSize); KernelContext.CriticalSection.Enter(); @@ -55,7 +56,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc { KThread currentThread = KernelStatic.GetCurrentThread(); - KSessionRequest request = new(currentThread, customCmdBuffAddr, customCmdBuffSize, asyncEvent); + KSessionRequest request = _parent.ServerSession.RequestPool.Allocate().Set(currentThread, customCmdBuffAddr, customCmdBuffSize, asyncEvent); KernelContext.CriticalSection.Enter(); diff --git a/src/Ryujinx.HLE/HOS/Kernel/Ipc/KServerSession.cs b/src/Ryujinx.HLE/HOS/Kernel/Ipc/KServerSession.cs index edc3d819e..f2c22c9f3 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Ipc/KServerSession.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Ipc/KServerSession.cs @@ -10,6 +10,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc { class KServerSession : KSynchronizationObject { + public readonly ObjectPool RequestPool = new(() => new KSessionRequest()); + private static readonly MemoryState[] _ipcMemoryStates = [ MemoryState.IpcBuffer3, @@ -274,6 +276,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc KernelContext.CriticalSection.Leave(); WakeClientThread(request, clientResult); + + RequestPool.Release(request); } if (clientHeader.ReceiveListType < 2 && @@ -627,6 +631,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc CloseAllHandles(clientMsg, serverHeader, clientProcess); FinishRequest(request, clientResult); + + RequestPool.Release(request); } if (clientHeader.ReceiveListType < 2 && @@ -865,6 +871,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc // Unmap buffers from server. FinishRequest(request, clientResult); + + RequestPool.Release(request); return serverResult; } @@ -1098,6 +1106,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc foreach (KSessionRequest request in IterateWithRemovalOfAllRequests()) { FinishRequest(request, KernelResult.PortRemoteClosed); + + RequestPool.Release(request); } } @@ -1117,6 +1127,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc { SendResultToAsyncRequestClient(request, KernelResult.PortRemoteClosed); } + + RequestPool.Release(request); } WakeServerThreads(KernelResult.PortRemoteClosed); diff --git a/src/Ryujinx.HLE/HOS/Kernel/Ipc/KSessionRequest.cs b/src/Ryujinx.HLE/HOS/Kernel/Ipc/KSessionRequest.cs index bc3eef71e..69a0d3a02 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Ipc/KSessionRequest.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Ipc/KSessionRequest.cs @@ -5,18 +5,18 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc { class KSessionRequest { - public KBufferDescriptorTable BufferDescriptorTable { get; } + public KBufferDescriptorTable BufferDescriptorTable { get; private set; } - public KThread ClientThread { get; } + public KThread ClientThread { get; private set; } public KProcess ServerProcess { get; set; } - public KWritableEvent AsyncEvent { get; } + public KWritableEvent AsyncEvent { get; private set; } - public ulong CustomCmdBuffAddr { get; } - public ulong CustomCmdBuffSize { get; } + public ulong CustomCmdBuffAddr { get; private set; } + public ulong CustomCmdBuffSize { get; private set; } - public KSessionRequest( + public KSessionRequest Set( KThread clientThread, ulong customCmdBuffAddr, ulong customCmdBuffSize, @@ -27,7 +27,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc CustomCmdBuffSize = customCmdBuffSize; AsyncEvent = asyncEvent; - BufferDescriptorTable = new KBufferDescriptorTable(); + BufferDescriptorTable = BufferDescriptorTable?.Clear() ?? new KBufferDescriptorTable(); + + return this; } } } diff --git a/src/Ryujinx.HLE/HOS/Kernel/Threading/KAddressArbiter.cs b/src/Ryujinx.HLE/HOS/Kernel/Threading/KAddressArbiter.cs index 278a9b2ff..3e13b917a 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Threading/KAddressArbiter.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Threading/KAddressArbiter.cs @@ -1,10 +1,8 @@ -using Ryujinx.Common; using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Process; using Ryujinx.Horizon.Common; using System; using System.Collections.Generic; -using System.Linq; using System.Threading; namespace Ryujinx.HLE.HOS.Kernel.Threading @@ -12,12 +10,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading class KAddressArbiter { private const int HasListenersMask = 0x40000000; - private static readonly ObjectPool _threadArrayPool = new(() => []); private readonly KernelContext _context; - private readonly List _condVarThreads; - private readonly List _arbiterThreads; + private readonly Dictionary> _condVarThreads; + private readonly Dictionary> _arbiterThreads; + private readonly ByDynamicPriority _byDynamicPriority; public KAddressArbiter(KernelContext context) { @@ -25,6 +23,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading _condVarThreads = []; _arbiterThreads = []; + _byDynamicPriority = new ByDynamicPriority(); } public Result ArbitrateLock(int ownerHandle, ulong mutexAddress, int requesterHandle) @@ -140,9 +139,23 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading currentThread.MutexAddress = mutexAddress; currentThread.ThreadHandleForUserMutex = threadHandle; - currentThread.CondVarAddress = condVarAddress; - _condVarThreads.Add(currentThread); + if (_condVarThreads.TryGetValue(condVarAddress, out List threads)) + { + int i = 0; + + if (threads.Count > 0) + { + i = threads.BinarySearch(currentThread, _byDynamicPriority); + if (i < 0) i = ~i; + } + + threads.Insert(i, currentThread); + } + else + { + _condVarThreads.Add(condVarAddress, [currentThread]); + } if (timeout != 0) { @@ -165,7 +178,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading currentThread.MutexOwner?.RemoveMutexWaiter(currentThread); - _condVarThreads.Remove(currentThread); + _condVarThreads[condVarAddress].Remove(currentThread); _context.CriticalSection.Leave(); @@ -200,13 +213,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading { _context.CriticalSection.Enter(); - static bool SignalProcessWideKeyPredicate(KThread thread, ulong address) + int validThreads = 0; + _condVarThreads.TryGetValue(address, out List threads); + + if (threads is not null && threads.Count > 0) { - return thread.CondVarAddress == address; + validThreads = WakeThreads(threads, count, TryAcquireMutex); } - - int validThreads = WakeThreads(_condVarThreads, count, TryAcquireMutex, SignalProcessWideKeyPredicate, address); - + if (validThreads == 0) { KernelTransfer.KernelToUser(address, 0); @@ -315,9 +329,24 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading currentThread.MutexAddress = address; currentThread.WaitingInArbitration = true; + + if (_arbiterThreads.TryGetValue(address, out List threads)) + { + int i = 0; - _arbiterThreads.Add(currentThread); - + if (threads.Count > 0) + { + i = threads.BinarySearch(currentThread, _byDynamicPriority); + if (i < 0) i = ~i; + } + + threads.Insert(i, currentThread); + } + else + { + _arbiterThreads.Add(address, [currentThread]); + } + currentThread.Reschedule(ThreadSchedState.Paused); if (timeout > 0) @@ -336,7 +365,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading if (currentThread.WaitingInArbitration) { - _arbiterThreads.Remove(currentThread); + _arbiterThreads[address].Remove(currentThread); currentThread.WaitingInArbitration = false; } @@ -392,9 +421,24 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading currentThread.MutexAddress = address; currentThread.WaitingInArbitration = true; + + if (_arbiterThreads.TryGetValue(address, out List threads)) + { + int i = 0; - _arbiterThreads.Add(currentThread); - + if (threads.Count > 0) + { + i = threads.BinarySearch(currentThread, _byDynamicPriority); + if (i < 0) i = ~i; + } + + threads.Insert(i, currentThread); + } + else + { + _arbiterThreads.Add(address, [currentThread]); + } + currentThread.Reschedule(ThreadSchedState.Paused); if (timeout > 0) @@ -413,7 +457,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading if (currentThread.WaitingInArbitration) { - _arbiterThreads.Remove(currentThread); + _arbiterThreads[address].Remove(currentThread); currentThread.WaitingInArbitration = false; } @@ -485,16 +529,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading // The value is decremented if the number of threads waiting is less // or equal to the Count of threads to be signaled, or Count is zero // or negative. It is incremented if there are no threads waiting. - int waitingCount = 0; - - foreach (KThread thread in _arbiterThreads) - { - if (thread.MutexAddress == address && - ++waitingCount >= count) - { - break; - } - } + int waitingCount = _arbiterThreads[address].Count; if (waitingCount > 0) { @@ -561,55 +596,38 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading thread.WaitingInArbitration = false; } - static bool ArbiterThreadPredecate(KThread thread, ulong address) - { - return thread.MutexAddress == address; - } + _arbiterThreads.TryGetValue(address, out List threads); - WakeThreads(_arbiterThreads, count, RemoveArbiterThread, ArbiterThreadPredecate, address); + if (threads is not null && threads.Count > 0) + { + WakeThreads(threads, count, RemoveArbiterThread); + } } private static int WakeThreads( List threads, int count, - Action removeCallback, - Func predicate, - ulong address = 0) + Action removeCallback) { - KThread[] candidates = _threadArrayPool.Allocate(); - if (candidates.Length < threads.Count) - { - Array.Resize(ref candidates, threads.Count); - } - - int validCount = 0; - - for (int i = 0; i < threads.Count; i++) - { - if (predicate(threads[i], address)) - { - candidates[validCount++] = threads[i]; - } - } - - Span candidatesSpan = candidates.AsSpan(..validCount); - - candidatesSpan.Sort((x, y) => (x.DynamicPriority.CompareTo(y.DynamicPriority))); + int validCount = count > 0 ? Math.Min(count, threads.Count) : threads.Count; - if (count > 0) - { - candidatesSpan = candidatesSpan[..Math.Min(count, candidatesSpan.Length)]; - } - - foreach (KThread thread in candidatesSpan) + for (int i = 0; i < validCount; i++) { + KThread thread = threads[i]; removeCallback(thread); - threads.Remove(thread); } - _threadArrayPool.Release(candidates); - + threads.RemoveRange(0, validCount); + return validCount; } + + private class ByDynamicPriority : IComparer + { + public int Compare(KThread x, KThread y) + { + return x!.DynamicPriority.CompareTo(y!.DynamicPriority); + } + } } } diff --git a/src/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs b/src/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs index 64cd4d595..aaa4ccd99 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs @@ -61,8 +61,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading public KSynchronizationObject SignaledObj { get; set; } - public ulong CondVarAddress { get; set; } - private ulong _entrypoint; private ThreadStart _customThreadStart; private bool _forcedUnschedulable; diff --git a/src/Ryujinx.HLE/HOS/Services/IpcService.cs b/src/Ryujinx.HLE/HOS/Services/IpcService.cs index 4c354ebc6..c7dee64fb 100644 --- a/src/Ryujinx.HLE/HOS/Services/IpcService.cs +++ b/src/Ryujinx.HLE/HOS/Services/IpcService.cs @@ -23,6 +23,9 @@ namespace Ryujinx.HLE.HOS.Services private int _selfId; private bool _isDomain; + // cache array so we don't recreate it all the time + private object[] _parameters = [null]; + public IpcService(ServerBase server = null, bool registerTipc = false) { Stopwatch sw = Stopwatch.StartNew(); @@ -146,7 +149,9 @@ namespace Ryujinx.HLE.HOS.Services { Logger.Trace?.Print(LogClass.KernelIpc, $"{service.GetType().Name}: {processRequest.Name}"); - result = (ResultCode)processRequest.Invoke(service, [context]); + _parameters[0] = context; + + result = (ResultCode)processRequest.Invoke(service, _parameters); } else { @@ -196,7 +201,9 @@ namespace Ryujinx.HLE.HOS.Services { Logger.Debug?.Print(LogClass.KernelIpc, $"{GetType().Name}: {processRequest.Name}"); - result = (ResultCode)processRequest.Invoke(this, [context]); + _parameters[0] = context; + + result = (ResultCode)processRequest.Invoke(this, _parameters); } else { diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs b/src/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs index 598c7e6e2..ed14b3e15 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs @@ -59,6 +59,10 @@ namespace Ryujinx.HLE.HOS.Services.Nv // TODO: This should call set:sys::GetDebugModeFlag private readonly bool _debugModeEnabled = false; + + private byte[] _ioctl2Buffer = []; + private byte[] _ioctlArgumentBuffer = []; + private byte[] _ioctl3Buffer = []; public INvDrvServices(ServiceCtx context) : base(context.Device.System.NvDrvServer) { @@ -128,27 +132,38 @@ namespace Ryujinx.HLE.HOS.Services.Nv if (!context.Memory.TryReadUnsafe(inputDataPosition, (int)inputDataSize, out arguments)) { - arguments = new byte[inputDataSize]; + if (_ioctlArgumentBuffer.Length < (int)inputDataSize) + { + Array.Resize(ref _ioctlArgumentBuffer, (int)inputDataSize); + } + + arguments = _ioctlArgumentBuffer.AsSpan(0, (int)inputDataSize); + context.Memory.Read(inputDataPosition, arguments); } - else - { - arguments = arguments.ToArray(); - } } else if (isWrite) { - byte[] outputData = new byte[outputDataSize]; - - arguments = new Span(outputData); + if (_ioctlArgumentBuffer.Length < (int)outputDataSize) + { + Array.Resize(ref _ioctlArgumentBuffer, (int)outputDataSize); + } + + arguments = _ioctlArgumentBuffer.AsSpan(0, (int)outputDataSize); } else { - byte[] temp = new byte[inputDataSize]; + if (!context.Memory.TryReadUnsafe(inputDataPosition, (int)inputDataSize, out arguments)) + { + if (_ioctlArgumentBuffer.Length < (int)inputDataSize) + { + Array.Resize(ref _ioctlArgumentBuffer, (int)inputDataSize); + } + + arguments = _ioctlArgumentBuffer.AsSpan(0, (int)inputDataSize); - context.Memory.Read(inputDataPosition, temp); - - arguments = new Span(temp); + context.Memory.Read(inputDataPosition, arguments); + } } return NvResult.Success; @@ -270,7 +285,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv if ((ioctlCommand.DirectionValue & NvIoctl.Direction.Write) != 0) { - context.Memory.Write(context.Request.GetBufferType0x22(0).Position, arguments.ToArray()); + context.Memory.Write(context.Request.GetBufferType0x22(0).Position, arguments); } } } @@ -474,13 +489,15 @@ namespace Ryujinx.HLE.HOS.Services.Nv errorCode = GetIoctlArgument(context, ioctlCommand, out Span arguments); - byte[] inlineInBuffer = null; - if (!context.Memory.TryReadUnsafe(inlineInBufferPosition, (int)inlineInBufferSize, out Span inlineInBufferSpan)) { - inlineInBuffer = _byteArrayPool.Rent((int)inlineInBufferSize); - inlineInBufferSpan = inlineInBuffer; - context.Memory.Read(inlineInBufferPosition, inlineInBufferSpan[..(int)inlineInBufferSize]); + if (_ioctl2Buffer.Length < (int)inlineInBufferSize) + { + Array.Resize(ref _ioctl2Buffer, (int)inlineInBufferSize); + } + + inlineInBufferSpan = _ioctl2Buffer.AsSpan(0, (int)inlineInBufferSize); + context.Memory.Read(inlineInBufferPosition, inlineInBufferSpan); } if (errorCode == NvResult.Success) @@ -489,7 +506,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv if (errorCode == NvResult.Success) { - NvInternalResult internalResult = deviceFile.Ioctl2(ioctlCommand, arguments, inlineInBufferSpan[..(int)inlineInBufferSize]); + NvInternalResult internalResult = deviceFile.Ioctl2(ioctlCommand, arguments, inlineInBufferSpan); if (internalResult == NvInternalResult.NotImplemented) { @@ -500,15 +517,10 @@ namespace Ryujinx.HLE.HOS.Services.Nv if ((ioctlCommand.DirectionValue & NvIoctl.Direction.Write) != 0) { - context.Memory.Write(context.Request.GetBufferType0x22(0).Position, arguments.ToArray()); + context.Memory.Write(context.Request.GetBufferType0x22(0).Position, arguments); } } } - - if (inlineInBuffer is not null) - { - _byteArrayPool.Return(inlineInBuffer); - } } context.ResponseData.Write((uint)errorCode); @@ -531,13 +543,15 @@ namespace Ryujinx.HLE.HOS.Services.Nv errorCode = GetIoctlArgument(context, ioctlCommand, out Span arguments); - byte[] inlineOutBuffer = null; - if (!context.Memory.TryReadUnsafe(inlineOutBufferPosition, (int)inlineOutBufferSize, out Span inlineOutBufferSpan)) { - inlineOutBuffer = _byteArrayPool.Rent((int)inlineOutBufferSize); - inlineOutBufferSpan = inlineOutBuffer; - context.Memory.Read(inlineOutBufferPosition, inlineOutBufferSpan[..(int)inlineOutBufferSize]); + if (_ioctl3Buffer.Length < (int)inlineOutBufferSize) + { + Array.Resize(ref _ioctl3Buffer, (int)inlineOutBufferSize); + } + + inlineOutBufferSpan = _ioctl3Buffer.AsSpan(0, (int)inlineOutBufferSize); + context.Memory.Read(inlineOutBufferPosition, inlineOutBufferSpan); } if (errorCode == NvResult.Success) @@ -546,7 +560,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv if (errorCode == NvResult.Success) { - NvInternalResult internalResult = deviceFile.Ioctl3(ioctlCommand, arguments, inlineOutBufferSpan[..(int)inlineOutBufferSize]); + NvInternalResult internalResult = deviceFile.Ioctl3(ioctlCommand, arguments, inlineOutBufferSpan); if (internalResult == NvInternalResult.NotImplemented) { @@ -557,16 +571,11 @@ namespace Ryujinx.HLE.HOS.Services.Nv if ((ioctlCommand.DirectionValue & NvIoctl.Direction.Write) != 0) { - context.Memory.Write(context.Request.GetBufferType0x22(0).Position, arguments.ToArray()); - context.Memory.Write(inlineOutBufferPosition, inlineOutBufferSpan[..(int)inlineOutBufferSize].ToArray()); + context.Memory.Write(context.Request.GetBufferType0x22(0).Position, arguments); + context.Memory.Write(inlineOutBufferPosition, inlineOutBufferSpan); } } } - - if (inlineOutBuffer is not null) - { - _byteArrayPool.Return(inlineOutBuffer); - } } context.ResponseData.Write((uint)errorCode); diff --git a/src/Ryujinx.HLE/HOS/Services/ServerBase.cs b/src/Ryujinx.HLE/HOS/Services/ServerBase.cs index dcc01bf38..a54dc637e 100644 --- a/src/Ryujinx.HLE/HOS/Services/ServerBase.cs +++ b/src/Ryujinx.HLE/HOS/Services/ServerBase.cs @@ -454,8 +454,9 @@ namespace Ryujinx.HLE.HOS.Services response.RawData = _responseDataStream.ToArray(); - using RecyclableMemoryStream responseStream = response.GetStreamTipc(); + RecyclableMemoryStream responseStream = response.GetStreamTipc(); _selfProcess.CpuMemory.Write(_selfThread.TlsAddress, responseStream.GetReadOnlySequence()); + MemoryStreamManager.Shared.ReleaseStream(responseStream); } else { @@ -464,8 +465,9 @@ namespace Ryujinx.HLE.HOS.Services if (!isTipcCommunication) { - using RecyclableMemoryStream responseStream = response.GetStream((long)_selfThread.TlsAddress, recvListAddr | ((ulong)PointerBufferSize << 48)); + RecyclableMemoryStream responseStream = response.GetStream((long)_selfThread.TlsAddress, recvListAddr | ((ulong)PointerBufferSize << 48)); _selfProcess.CpuMemory.Write(_selfThread.TlsAddress, responseStream.GetReadOnlySequence()); + MemoryStreamManager.Shared.ReleaseStream(responseStream); } return shouldReply; diff --git a/src/Ryujinx.Horizon/Sdk/OsTypes/Impl/MultiWaitImpl.cs b/src/Ryujinx.Horizon/Sdk/OsTypes/Impl/MultiWaitImpl.cs index d723e5322..412337b6b 100644 --- a/src/Ryujinx.Horizon/Sdk/OsTypes/Impl/MultiWaitImpl.cs +++ b/src/Ryujinx.Horizon/Sdk/OsTypes/Impl/MultiWaitImpl.cs @@ -19,6 +19,9 @@ namespace Ryujinx.Horizon.Sdk.OsTypes.Impl private int _waitingThreadHandle; private MultiWaitHolderBase _signaledHolder; + + ObjectPool _objectHandlePool = new(() => new int[64]); + ObjectPool _objectPool = new(() => new MultiWaitHolderBase[64]); public long CurrentTime { get; private set; } @@ -76,11 +79,15 @@ namespace Ryujinx.Horizon.Sdk.OsTypes.Impl private MultiWaitHolderBase WaitAnyHandleImpl(bool infinite, long timeout) { - Span objectHandles = new int[64]; + int[] objectHandles = _objectHandlePool.Allocate(); + Span objectHandlesSpan = objectHandles; + objectHandlesSpan.Clear(); - Span objects = new MultiWaitHolderBase[64]; + MultiWaitHolderBase[] objects = _objectPool.Allocate(); + Span objectsSpan = objects; + objectsSpan.Clear(); - int count = FillObjectsArray(objectHandles, objects); + int count = FillObjectsArray(objectHandlesSpan, objectsSpan); long endTime = infinite ? long.MaxValue : PerformanceCounter.ElapsedMilliseconds * 1000000; @@ -98,7 +105,7 @@ namespace Ryujinx.Horizon.Sdk.OsTypes.Impl } else { - index = WaitSynchronization(objectHandles[..count], minTimeout); + index = WaitSynchronization(objectHandlesSpan[..count], minTimeout); DebugUtil.Assert(index != WaitInvalid); } @@ -116,12 +123,18 @@ namespace Ryujinx.Horizon.Sdk.OsTypes.Impl { _signaledHolder = minTimeoutObject; + _objectHandlePool.Release(objectHandles); + _objectPool.Release(objects); + return _signaledHolder; } } } else { + _objectHandlePool.Release(objectHandles); + _objectPool.Release(objects); + return null; } @@ -131,6 +144,9 @@ namespace Ryujinx.Horizon.Sdk.OsTypes.Impl { if (_signaledHolder != null) { + _objectHandlePool.Release(objectHandles); + _objectPool.Release(objects); + return _signaledHolder; } } @@ -139,8 +155,11 @@ namespace Ryujinx.Horizon.Sdk.OsTypes.Impl default: lock (_lock) { - _signaledHolder = objects[index]; + _signaledHolder = objectsSpan[index]; + _objectHandlePool.Release(objectHandles); + _objectPool.Release(objects); + return _signaledHolder; } } diff --git a/src/Ryujinx.Input/HLE/NpadController.cs b/src/Ryujinx.Input/HLE/NpadController.cs index dd8907a4b..84f9e89ab 100644 --- a/src/Ryujinx.Input/HLE/NpadController.cs +++ b/src/Ryujinx.Input/HLE/NpadController.cs @@ -532,8 +532,6 @@ namespace Ryujinx.Input.HLE hidKeyboard.Modifier |= value << entry.Target; } - - ArrayPool.Shared.Return(keyboardState.KeysState); return hidKeyboard; diff --git a/src/Ryujinx.Input/HLE/NpadManager.cs b/src/Ryujinx.Input/HLE/NpadManager.cs index 866504128..f2936aa72 100644 --- a/src/Ryujinx.Input/HLE/NpadManager.cs +++ b/src/Ryujinx.Input/HLE/NpadManager.cs @@ -20,7 +20,6 @@ namespace Ryujinx.Input.HLE { public class NpadManager : IDisposable { - private static readonly ObjectPool> _hleMotionStatesPool = new (() => new List(NpadDevices.MaxControllers)); private readonly CemuHookClient _cemuHookClient; private readonly Lock _lock = new(); @@ -40,6 +39,9 @@ namespace Ryujinx.Input.HLE private bool _enableKeyboard; private bool _enableMouse; private Switch _device; + + private readonly List _hleInputStates = []; + private readonly List _hleMotionStates = new(NpadDevices.MaxControllers); public NpadManager(IGamepadDriver keyboardDriver, IGamepadDriver gamepadDriver, IGamepadDriver mouseDriver) { @@ -217,8 +219,8 @@ namespace Ryujinx.Input.HLE { lock (_lock) { - List hleInputStates = []; - List hleMotionStates = _hleMotionStatesPool.Allocate(); + _hleInputStates.Clear(); + _hleMotionStates.Clear(); KeyboardInput? hleKeyboardInput = null; @@ -260,14 +262,14 @@ namespace Ryujinx.Input.HLE inputState.PlayerId = playerIndex; motionState.Item1.PlayerId = playerIndex; - hleInputStates.Add(inputState); - hleMotionStates.Add(motionState.Item1); + _hleInputStates.Add(inputState); + _hleMotionStates.Add(motionState.Item1); if (isJoyconPair && !motionState.Item2.Equals(default)) { motionState.Item2.PlayerId = playerIndex; - hleMotionStates.Add(motionState.Item2); + _hleMotionStates.Add(motionState.Item2); } } @@ -276,8 +278,8 @@ namespace Ryujinx.Input.HLE hleKeyboardInput = NpadController.GetHLEKeyboardInput(_keyboardDriver); } - _device.Hid.Npads.Update(hleInputStates); - _device.Hid.Npads.UpdateSixAxis(hleMotionStates); + _device.Hid.Npads.Update(_hleInputStates); + _device.Hid.Npads.UpdateSixAxis(_hleMotionStates); if (hleKeyboardInput.HasValue) { @@ -328,10 +330,7 @@ namespace Ryujinx.Input.HLE _device.Hid.Mouse.Update(0, 0); } - _device.TamperMachine.UpdateInput(hleInputStates); - - hleMotionStates.Clear(); - _hleMotionStatesPool.Release(hleMotionStates); + _device.TamperMachine.UpdateInput(_hleInputStates); } } diff --git a/src/Ryujinx.Input/IKeyboard.cs b/src/Ryujinx.Input/IKeyboard.cs index 7fecaaa5d..c51d5aea3 100644 --- a/src/Ryujinx.Input/IKeyboard.cs +++ b/src/Ryujinx.Input/IKeyboard.cs @@ -8,6 +8,8 @@ namespace Ryujinx.Input /// public interface IKeyboard : IGamepad { + private static bool[] _keyState; + /// /// Check if a given key is pressed on the keyboard. /// @@ -29,15 +31,17 @@ namespace Ryujinx.Input [MethodImpl(MethodImplOptions.AggressiveInlining)] static KeyboardStateSnapshot GetStateSnapshot(IKeyboard keyboard) { + if (_keyState is null) + { + _keyState = new bool[(int)Key.Count]; + } - bool[] keysState = ArrayPool.Shared.Rent((int)Key.Count); - for (Key key = 0; key < Key.Count; key++) { - keysState[(int)key] = keyboard.IsPressed(key); + _keyState[(int)key] = keyboard.IsPressed(key); } - return new KeyboardStateSnapshot(keysState); + return new KeyboardStateSnapshot(_keyState); } } } diff --git a/src/Ryujinx.Memory/Range/INonOverlappingRange.cs b/src/Ryujinx.Memory/Range/INonOverlappingRange.cs index c6a0197d4..09311a830 100644 --- a/src/Ryujinx.Memory/Range/INonOverlappingRange.cs +++ b/src/Ryujinx.Memory/Range/INonOverlappingRange.cs @@ -3,7 +3,7 @@ namespace Ryujinx.Memory.Range /// /// Range of memory that can be split in two. /// - public interface INonOverlappingRange : IRange + public interface INonOverlappingRange : IRangeListRange where T : class, IRangeListRange { /// /// Split this region into two, around the specified address. @@ -11,6 +11,6 @@ namespace Ryujinx.Memory.Range /// /// Address to split the region around /// The second part of the split region, with start address at the given split. - public INonOverlappingRange Split(ulong splitAddress); + public INonOverlappingRange Split(ulong splitAddress); } } diff --git a/src/Ryujinx.Memory/Range/IRange.cs b/src/Ryujinx.Memory/Range/IRange.cs index c85e21d1d..c8f50a921 100644 --- a/src/Ryujinx.Memory/Range/IRange.cs +++ b/src/Ryujinx.Memory/Range/IRange.cs @@ -24,8 +24,8 @@ namespace Ryujinx.Memory.Range /// Check if this range overlaps with another. /// /// Base address - /// Size of the range + /// EndAddress of the range /// True if overlapping, false otherwise - bool OverlapsWith(ulong address, ulong size); + bool OverlapsWith(ulong address, ulong endAddress); } } diff --git a/src/Ryujinx.Memory/Range/NonOverlappingRangeList.cs b/src/Ryujinx.Memory/Range/NonOverlappingRangeList.cs index 7560d2ae7..1ef51644c 100644 --- a/src/Ryujinx.Memory/Range/NonOverlappingRangeList.cs +++ b/src/Ryujinx.Memory/Range/NonOverlappingRangeList.cs @@ -11,7 +11,7 @@ namespace Ryujinx.Memory.Range /// A range list that assumes ranges are non-overlapping, with list items that can be split in two to avoid overlaps. /// /// Type of the range. - public unsafe class NonOverlappingRangeList : RangeListBase where T : class, INonOverlappingRange + public class NonOverlappingRangeList : RangeListBase where T : class, INonOverlappingRange { public readonly ReaderWriterLockSlim Lock = new(); @@ -32,83 +32,18 @@ namespace Ryujinx.Memory.Range /// The item to be added public override void Add(T item) { + Debug.Assert(item.Address != item.EndAddress); + int index = BinarySearch(item.Address); if (index < 0) { index = ~index; } - - RangeItem rangeItem = _rangeItemPool.Allocate().Set(item); - - Insert(index, rangeItem); - } - - /// - /// Updates an item's end address on the list. Address must be the same. - /// - /// The item to be updated - /// True if the item was located and updated, false otherwise - protected override bool Update(T item) - { - int index = BinarySearch(item.Address); - - if (index >= 0 && Items[index].Value.Equals(item)) - { - RangeItem rangeItem = new(item) { Previous = Items[index].Previous, Next = Items[index].Next }; - - if (index > 0) - { - Items[index - 1].Next = rangeItem; - } - - if (index < Count - 1) - { - Items[index + 1].Previous = rangeItem; - } - - Items[index] = rangeItem; - - return true; - } - - return false; - } - - /// - /// Updates an item's end address on the list. Address must be the same. - /// - /// The RangeItem to be updated - /// True if the item was located and updated, false otherwise - protected override bool Update(RangeItem item) - { - int index = BinarySearch(item.Address); - - RangeItem rangeItem = new(item.Value) { Previous = item.Previous, Next = item.Next }; - - if (index > 0) - { - Items[index - 1].Next = rangeItem; - } - - if (index < Count - 1) - { - Items[index + 1].Previous = rangeItem; - } - - Items[index] = rangeItem; - - return true; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void Insert(int index, RangeItem item) - { - Debug.Assert(item.Address != item.EndAddress); if (Count + 1 > Items.Length) { - Array.Resize(ref Items, Items.Length + BackingGrowthSize); + Array.Resize(ref Items, (int)(Items.Length * 1.5)); } if (index >= Count) @@ -145,8 +80,6 @@ namespace Ryujinx.Memory.Range [MethodImpl(MethodImplOptions.AggressiveInlining)] private void RemoveAt(int index) { - _rangeItemPool.Release(Items[index]); - if (index < Count - 1) { Items[index + 1].Previous = index > 0 ? Items[index - 1] : null; @@ -173,7 +106,7 @@ namespace Ryujinx.Memory.Range { int index = BinarySearch(item.Address); - if (index >= 0 && Items[index].Value.Equals(item)) + if (index >= 0 && Items[index] == item) { RemoveAt(index); @@ -188,7 +121,7 @@ namespace Ryujinx.Memory.Range /// /// The first item in the range of items to be removed /// The last item in the range of items to be removed - public override void RemoveRange(RangeItem startItem, RangeItem endItem) + public override void RemoveRange(T startItem, T endItem) { if (startItem is null) { @@ -197,7 +130,7 @@ namespace Ryujinx.Memory.Range if (startItem == endItem) { - Remove(startItem.Value); + Remove(startItem); return; } @@ -229,42 +162,45 @@ namespace Ryujinx.Memory.Range /// Size of the range public void RemoveRange(ulong address, ulong size) { - int startIndex = BinarySearchLeftEdge(address, address + size); + (int startIndex, int endIndex) = BinarySearchEdges(address, address + size); if (startIndex < 0) { return; } - int endIndex = startIndex; - - while (Items[endIndex] is not null && Items[endIndex].Address < address + size) + if (startIndex == endIndex - 1) { - if (endIndex == Count - 1) - { - break; - } - - endIndex++; + RemoveAt(startIndex); + return; } - if (endIndex < Count - 1) + RemoveRangeInternal(startIndex, endIndex); + } + + /// + /// Removes a range of items from the item list + /// + /// Start index of the range + /// End index of the range (exclusive) + private void RemoveRangeInternal(int index, int endIndex) + { + if (endIndex < Count) { - Items[endIndex + 1].Previous = startIndex > 0 ? Items[startIndex - 1] : null; + Items[endIndex].Previous = index > 0 ? Items[index - 1] : null; } - if (startIndex > 0) + if (index > 0) { - Items[startIndex - 1].Next = endIndex < Count - 1 ? Items[endIndex + 1] : null; + Items[index - 1].Next = endIndex < Count ? Items[endIndex] : null; } - - if (endIndex < Count - 1) + if (endIndex < Count) { - Array.Copy(Items, endIndex + 1, Items, startIndex, Count - endIndex - 1); + Array.Copy(Items, endIndex, Items, index, Count - endIndex); } - Count -= endIndex - startIndex + 1; + Count -= endIndex - index; } /// @@ -296,8 +232,8 @@ namespace Ryujinx.Memory.Range // So we need to return both the split 0-1 and 1-2 ranges. Lock.EnterWriteLock(); - (RangeItem first, RangeItem last) = FindOverlapsAsNodes(address, size); - list = new List(); + (T first, T last) = FindOverlapsAsNodes(address, size); + list = []; if (first is null) { @@ -311,42 +247,41 @@ namespace Ryujinx.Memory.Range ulong lastAddress = address; ulong endAddress = address + size; - RangeItem current = first; + T current = first; while (last is not null && current is not null && current.Address < endAddress) { - T region = current.Value; - if (first == last && region.Address == address && region.Size == size) + if (first == last && current.Address == address && current.Size == size) { // Exact match, no splitting required. - list.Add(region); + list.Add(current); Lock.ExitWriteLock(); return; } - if (lastAddress < region.Address) + if (lastAddress < current.Address) { // There is a gap between this region and the last. We need to fill it. - T fillRegion = factory(lastAddress, region.Address - lastAddress); + T fillRegion = factory(lastAddress, current.Address - lastAddress); list.Add(fillRegion); Add(fillRegion); } - if (region.Address < address) + if (current.Address < address) { // Split the region around our base address and take the high half. - region = Split(region, address); + current = Split(current, address); } - if (region.EndAddress > address + size) + if (current.EndAddress > address + size) { // Split the region around our end address and take the low half. - Split(region, address + size); + Split(current, address + size); } - list.Add(region); - lastAddress = region.EndAddress; + list.Add(current); + lastAddress = current.EndAddress; current = current.Next; } @@ -374,7 +309,6 @@ namespace Ryujinx.Memory.Range private T Split(T region, ulong splitAddress) { T newRegion = (T)region.Split(splitAddress); - Update(region); Add(newRegion); return newRegion; } @@ -386,16 +320,11 @@ namespace Ryujinx.Memory.Range /// Size in bytes of the range /// The leftmost overlapping item, or null if none is found [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override RangeItem FindOverlap(ulong address, ulong size) + public override T FindOverlap(ulong address, ulong size) { int index = BinarySearchLeftEdge(address, address + size); - if (index < 0) - { - return null; - } - - return Items[index]; + return index < 0 ? null : Items[index]; } /// @@ -405,16 +334,11 @@ namespace Ryujinx.Memory.Range /// Size in bytes of the range /// The overlapping item, or null if none is found [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override RangeItem FindOverlapFast(ulong address, ulong size) + public override T FindOverlapFast(ulong address, ulong size) { int index = BinarySearch(address, address + size); - if (index < 0) - { - return null; - } - - return Items[index]; + return index < 0 ? null : Items[index]; } /// @@ -424,23 +348,18 @@ namespace Ryujinx.Memory.Range /// Size in bytes of the range /// The first and last overlapping items, or null if none are found [MethodImpl(MethodImplOptions.AggressiveInlining)] - public (RangeItem, RangeItem) FindOverlapsAsNodes(ulong address, ulong size) + public (T, T) FindOverlapsAsNodes(ulong address, ulong size) { (int index, int endIndex) = BinarySearchEdges(address, address + size); - if (index < 0) - { - return (null, null); - } - - return (Items[index], Items[endIndex - 1]); + return index < 0 ? (null, null) : (Items[index], Items[endIndex - 1]); } - public RangeItem[] FindOverlapsAsArray(ulong address, ulong size, out int length) + public T[] FindOverlapsAsArray(ulong address, ulong size, out int length) { (int index, int endIndex) = BinarySearchEdges(address, address + size); - RangeItem[] result; + T[] result; if (index < 0) { @@ -449,29 +368,20 @@ namespace Ryujinx.Memory.Range } else { - result = ArrayPool>.Shared.Rent(endIndex - index); + result = ArrayPool.Shared.Rent(endIndex - index); length = endIndex - index; - Array.Copy(Items, index, result, 0, endIndex - index); + Items.AsSpan(index, endIndex - index).CopyTo(result); } return result; } - public Span> FindOverlapsAsSpan(ulong address, ulong size) + public ReadOnlySpan FindOverlapsAsSpan(ulong address, ulong size) { (int index, int endIndex) = BinarySearchEdges(address, address + size); - Span> result; - - if (index < 0) - { - result = []; - } - else - { - result = Items.AsSpan().Slice(index, endIndex - index); - } + ReadOnlySpan result = index < 0 ? [] : Items.AsSpan(index, endIndex - index); return result; } @@ -480,7 +390,7 @@ namespace Ryujinx.Memory.Range { for (int i = 0; i < Count; i++) { - yield return Items[i].Value; + yield return Items[i]; } } } diff --git a/src/Ryujinx.Memory/Range/RangeList.cs b/src/Ryujinx.Memory/Range/RangeList.cs index 63025f1e8..e7ea55a94 100644 --- a/src/Ryujinx.Memory/Range/RangeList.cs +++ b/src/Ryujinx.Memory/Range/RangeList.cs @@ -14,14 +14,14 @@ namespace Ryujinx.Memory.Range /// startIndex is inclusive. /// endIndex is exclusive. /// - public readonly struct OverlapResult where T : IRange + public readonly struct OverlapResult where T : class, IRangeListRange { public readonly int StartIndex = -1; public readonly int EndIndex = -1; - public readonly RangeItem QuickResult; + public readonly T QuickResult; public int Count => EndIndex - StartIndex; - public OverlapResult(int startIndex, int endIndex, RangeItem quickResult = null) + public OverlapResult(int startIndex, int endIndex, T quickResult = null) { this.StartIndex = startIndex; this.EndIndex = endIndex; @@ -33,7 +33,7 @@ namespace Ryujinx.Memory.Range /// Sorted list of ranges that supports binary search. /// /// Type of the range. - public class RangeList : RangeListBase where T : IRange + public class RangeList : RangeListBase where T : class, IRangeListRange { public readonly ReaderWriterLockSlim Lock = new(); @@ -61,104 +61,6 @@ namespace Ryujinx.Memory.Range index = ~index; } - Insert(index, new RangeItem(item)); - } - - /// - /// Updates an item's end address on the list. Address must be the same. - /// - /// The item to be updated - /// True if the item was located and updated, false otherwise - protected override bool Update(T item) - { - int index = BinarySearch(item.Address); - - if (index >= 0) - { - while (index < Count) - { - if (Items[index].Value.Equals(item)) - { - RangeItem rangeItem = new(item) { Previous = Items[index].Previous, Next = Items[index].Next }; - - if (index > 0) - { - Items[index - 1].Next = rangeItem; - } - - if (index < Count - 1) - { - Items[index + 1].Previous = rangeItem; - } - - Items[index] = rangeItem; - - return true; - } - - if (Items[index].Address > item.Address) - { - break; - } - - index++; - } - } - - return false; - } - - /// - /// Updates an item's end address on the list. Address must be the same. - /// - /// The RangeItem to be updated - /// True if the item was located and updated, false otherwise - protected override bool Update(RangeItem item) - { - int index = BinarySearch(item.Address); - - if (index >= 0) - { - while (index < Count) - { - if (Items[index].Equals(item)) - { - RangeItem rangeItem = new(item.Value) { Previous = item.Previous, Next = item.Next }; - - if (index > 0) - { - Items[index - 1].Next = rangeItem; - } - - if (index < Count - 1) - { - Items[index + 1].Previous = rangeItem; - } - - Items[index] = rangeItem; - - return true; - } - - if (Items[index].Address > item.Address) - { - break; - } - - index++; - } - } - - return false; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void Insert(int index, RangeItem item) - { - Debug.Assert(item.Address != item.EndAddress); - - Debug.Assert(item.Address % 32 == 0); - if (Count + 1 > Items.Length) { Array.Resize(ref Items, Items.Length + BackingGrowthSize); @@ -220,7 +122,7 @@ namespace Ryujinx.Memory.Range /// The first item in the range of items to be removed /// The last item in the range of items to be removed [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override void RemoveRange(RangeItem startItem, RangeItem endItem) + public override void RemoveRange(T startItem, T endItem) { if (startItem is null) { @@ -229,30 +131,29 @@ namespace Ryujinx.Memory.Range if (startItem == endItem) { - Remove(startItem.Value); + Remove(startItem); return; } - int startIndex = BinarySearch(startItem.Address); - int endIndex = BinarySearch(endItem.Address); + (int index, int endIndex) = BinarySearchEdges(startItem.Address, endItem.EndAddress); - if (endIndex < Count - 1) + if (endIndex < Count) { - Items[endIndex + 1].Previous = startIndex > 0 ? Items[startIndex - 1] : null; + Items[endIndex].Previous = index > 0 ? Items[index - 1] : null; } - if (startIndex > 0) + if (index > 0) { - Items[startIndex - 1].Next = endIndex < Count - 1 ? Items[endIndex + 1] : null; + Items[index - 1].Next = endIndex < Count ? Items[endIndex] : null; } - if (endIndex < Count - 1) + if (endIndex < Count) { - Array.Copy(Items, endIndex + 1, Items, startIndex, Count - endIndex - 1); + Array.Copy(Items, endIndex, Items, index, Count - endIndex); } - Count -= endIndex - startIndex + 1; + Count -= endIndex - index; } /// @@ -268,7 +169,7 @@ namespace Ryujinx.Memory.Range { while (index < Count) { - if (Items[index].Value.Equals(item)) + if (Items[index] == item) { RemoveAt(index); @@ -298,7 +199,7 @@ namespace Ryujinx.Memory.Range /// Size in bytes of the range /// The overlapping item, or the default value for the type if none found [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override RangeItem FindOverlap(ulong address, ulong size) + public override T FindOverlap(ulong address, ulong size) { int index = BinarySearchLeftEdge(address, address + size); @@ -321,7 +222,7 @@ namespace Ryujinx.Memory.Range /// Size in bytes of the range /// The overlapping item, or the default value for the type if none found [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override RangeItem FindOverlapFast(ulong address, ulong size) + public override T FindOverlapFast(ulong address, ulong size) { int index = BinarySearch(address, address + size); @@ -340,7 +241,7 @@ namespace Ryujinx.Memory.Range /// Size in bytes of the range /// Output array where matches will be written. It is automatically resized to fit the results /// Range information of overlapping items found - private OverlapResult FindOverlaps(ulong address, ulong size, ref RangeItem[] output) + private OverlapResult FindOverlaps(ulong address, ulong size, ref T[] output) { int outputCount = 0; @@ -353,7 +254,7 @@ namespace Ryujinx.Memory.Range for (int i = startIndex; i < Count; i++) { - ref RangeItem item = ref Items[i]; + T item = Items[i]; if (item.Address >= endAddress) { @@ -398,7 +299,7 @@ namespace Ryujinx.Memory.Range { for (int i = 0; i < Count; i++) { - yield return Items[i].Value; + yield return Items[i]; } } } diff --git a/src/Ryujinx.Memory/Range/RangeListBase.cs b/src/Ryujinx.Memory/Range/RangeListBase.cs index 01fe1b0dc..9f0284253 100644 --- a/src/Ryujinx.Memory/Range/RangeListBase.cs +++ b/src/Ryujinx.Memory/Range/RangeListBase.cs @@ -1,56 +1,22 @@ using Ryujinx.Common; +using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; namespace Ryujinx.Memory.Range { - public class RangeItem where TValue : IRange + public interface IRangeListRange : IRange where TValue : class, IRangeListRange { - public RangeItem Next; - public RangeItem Previous; - - public ulong Address; - public ulong EndAddress; - - public TValue Value; - - public RangeItem() - { - - } - - public RangeItem(TValue value) - { - Address = value.Address; - EndAddress = value.Address + value.Size; - Value = value; - } - - public RangeItem Set(TValue value) - { - Next = null; - Previous = null; - Address = value.Address; - EndAddress = value.Address + value.Size; - Value = value; - - return this; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool OverlapsWith(ulong address, ulong endAddress) - { - return Address < endAddress && address < EndAddress; - } + public TValue Next { get; set; } + public TValue Previous { get; set; } } - public unsafe abstract class RangeListBase : IEnumerable where T : IRange + public unsafe abstract class RangeListBase : IEnumerable where T : class, IRangeListRange { - protected static readonly ObjectPool> _rangeItemPool = new(() => new RangeItem()); private const int BackingInitialSize = 1024; - protected RangeItem[] Items; + protected T[] Items; protected readonly int BackingGrowthSize; public int Count { get; protected set; } @@ -62,32 +28,18 @@ namespace Ryujinx.Memory.Range protected RangeListBase(int backingInitialSize = BackingInitialSize) { BackingGrowthSize = backingInitialSize; - Items = new RangeItem[backingInitialSize]; + Items = new T[backingInitialSize]; } public abstract void Add(T item); - - /// - /// Updates an item's end address on the list. Address must be the same. - /// - /// The item to be updated - /// True if the item was located and updated, false otherwise - protected abstract bool Update(T item); - - /// - /// Updates an item's end address on the list. Address must be the same. - /// - /// The RangeItem to be updated - /// True if the item was located and updated, false otherwise - protected abstract bool Update(RangeItem item); public abstract bool Remove(T item); - public abstract void RemoveRange(RangeItem startItem, RangeItem endItem); + public abstract void RemoveRange(T startItem, T endItem); - public abstract RangeItem FindOverlap(ulong address, ulong size); + public abstract T FindOverlap(ulong address, ulong size); - public abstract RangeItem FindOverlapFast(ulong address, ulong size); + public abstract T FindOverlapFast(ulong address, ulong size); /// /// Performs binary search on the internal list of items. @@ -106,7 +58,7 @@ namespace Ryujinx.Memory.Range int middle = left + (range >> 1); - ref RangeItem item = ref Items[middle]; + T item = Items[middle]; if (item.Address == address) { @@ -144,7 +96,7 @@ namespace Ryujinx.Memory.Range int middle = left + (range >> 1); - ref RangeItem item = ref Items[middle]; + T item = Items[middle]; if (item.OverlapsWith(address, endAddress)) { @@ -185,7 +137,7 @@ namespace Ryujinx.Memory.Range int middle = left + (range >> 1); - ref RangeItem item = ref Items[middle]; + T item = Items[middle]; bool match = item.OverlapsWith(address, endAddress); @@ -237,7 +189,7 @@ namespace Ryujinx.Memory.Range int middle = right - (range >> 1); - ref RangeItem item = ref Items[middle]; + T item = Items[middle]; bool match = item.OverlapsWith(address, endAddress); @@ -282,7 +234,7 @@ namespace Ryujinx.Memory.Range if (Count == 1) { - ref RangeItem item = ref Items[0]; + T item = Items[0]; if (item.OverlapsWith(address, endAddress)) { @@ -312,7 +264,7 @@ namespace Ryujinx.Memory.Range int middle = left + (range >> 1); - ref RangeItem item = ref Items[middle]; + T item = Items[middle]; bool match = item.OverlapsWith(address, endAddress); @@ -369,7 +321,7 @@ namespace Ryujinx.Memory.Range int middle = right - (range >> 1); - ref RangeItem item = ref Items[middle]; + T item = Items[middle]; bool match = item.OverlapsWith(address, endAddress); diff --git a/src/Ryujinx.Memory/Tracking/AbstractRegion.cs b/src/Ryujinx.Memory/Tracking/AbstractRegion.cs index 7226fe954..09703a253 100644 --- a/src/Ryujinx.Memory/Tracking/AbstractRegion.cs +++ b/src/Ryujinx.Memory/Tracking/AbstractRegion.cs @@ -5,7 +5,7 @@ namespace Ryujinx.Memory.Tracking /// /// A region of memory. /// - abstract class AbstractRegion : INonOverlappingRange + abstract class AbstractRegion : INonOverlappingRange where T : class, INonOverlappingRange { /// /// Base address. @@ -21,6 +21,9 @@ namespace Ryujinx.Memory.Tracking /// End address. /// public ulong EndAddress => Address + Size; + + public T Next { get; set; } + public T Previous { get; set; } /// /// Create a new region. @@ -37,11 +40,11 @@ namespace Ryujinx.Memory.Tracking /// Check if this range overlaps with another. /// /// Base address - /// Size of the range + /// End address /// True if overlapping, false otherwise - public bool OverlapsWith(ulong address, ulong size) + public bool OverlapsWith(ulong address, ulong endAddress) { - return Address < address + size && address < EndAddress; + return Address < endAddress && address < EndAddress; } /// @@ -68,6 +71,6 @@ namespace Ryujinx.Memory.Tracking /// /// Address to split the region around /// The second part of the split region, with start address at the given split. - public abstract INonOverlappingRange Split(ulong splitAddress); + public abstract INonOverlappingRange Split(ulong splitAddress); } } diff --git a/src/Ryujinx.Memory/Tracking/MemoryTracking.cs b/src/Ryujinx.Memory/Tracking/MemoryTracking.cs index b4b06da8c..0160791e8 100644 --- a/src/Ryujinx.Memory/Tracking/MemoryTracking.cs +++ b/src/Ryujinx.Memory/Tracking/MemoryTracking.cs @@ -81,10 +81,10 @@ namespace Ryujinx.Memory.Tracking { NonOverlappingRangeList regions = type == 0 ? _virtualRegions : _guestVirtualRegions; regions.Lock.EnterReadLock(); - Span> overlaps = regions.FindOverlapsAsSpan(va, size); + ReadOnlySpan overlaps = regions.FindOverlapsAsSpan(va, size); for (int i = 0; i < overlaps.Length; i++) { - VirtualRegion region = overlaps[i].Value; + VirtualRegion region = overlaps[i]; // If the region has been fully remapped, signal that it has been mapped again. bool remapped = _memoryManager.IsRangeMapped(region.Address, region.Size); @@ -117,11 +117,11 @@ namespace Ryujinx.Memory.Tracking { NonOverlappingRangeList regions = type == 0 ? _virtualRegions : _guestVirtualRegions; regions.Lock.EnterReadLock(); - Span> overlaps = regions.FindOverlapsAsSpan(va, size); + ReadOnlySpan overlaps = regions.FindOverlapsAsSpan(va, size); for (int i = 0; i < overlaps.Length; i++) { - overlaps[i].Value.SignalMappingChanged(false); + overlaps[i].SignalMappingChanged(false); } regions.Lock.ExitReadLock(); } @@ -301,7 +301,7 @@ namespace Ryujinx.Memory.Tracking // We use the non-span method here because keeping the lock will cause a deadlock. regions.Lock.EnterReadLock(); - RangeItem[] overlaps = regions.FindOverlapsAsArray(address, size, out int length); + VirtualRegion[] overlaps = regions.FindOverlapsAsArray(address, size, out int length); regions.Lock.ExitReadLock(); if (length == 0 && !precise) @@ -327,7 +327,7 @@ namespace Ryujinx.Memory.Tracking for (int i = 0; i < length; i++) { - VirtualRegion region = overlaps[i].Value; + VirtualRegion region = overlaps[i]; if (precise) { @@ -341,7 +341,7 @@ namespace Ryujinx.Memory.Tracking if (length != 0) { - ArrayPool>.Shared.Return(overlaps); + ArrayPool.Shared.Return(overlaps); } } } diff --git a/src/Ryujinx.Memory/Tracking/VirtualRegion.cs b/src/Ryujinx.Memory/Tracking/VirtualRegion.cs index b86631db2..e95754c7a 100644 --- a/src/Ryujinx.Memory/Tracking/VirtualRegion.cs +++ b/src/Ryujinx.Memory/Tracking/VirtualRegion.cs @@ -6,7 +6,7 @@ namespace Ryujinx.Memory.Tracking /// /// A region of virtual memory. /// - class VirtualRegion : AbstractRegion + class VirtualRegion : AbstractRegion { public List Handles = []; @@ -137,7 +137,7 @@ namespace Ryujinx.Memory.Tracking } } - public override INonOverlappingRange Split(ulong splitAddress) + public override INonOverlappingRange Split(ulong splitAddress) { VirtualRegion newRegion = new(_tracking, splitAddress, EndAddress - splitAddress, Guest, _lastPermission); Size = splitAddress - Address; From 3a593b60845d894ece9e86b967a07e1390949fff Mon Sep 17 00:00:00 2001 From: LotP <22-lotp@users.noreply.git.ryujinx.app> Date: Sat, 6 Dec 2025 20:16:43 -0600 Subject: [PATCH 3/3] Fix kaddressarbiter crash (ryubing/ryujinx!235) See merge request ryubing/ryujinx!235 --- .gitignore | 1 + src/Ryujinx.HLE/HOS/Kernel/Threading/KAddressArbiter.cs | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 0c46c50c0..6f887e638 100644 --- a/.gitignore +++ b/.gitignore @@ -100,6 +100,7 @@ DocProject/Help/html # Click-Once directory publish/ +RyubingMaintainerTools/ # Publish Web Output *.Publish.xml diff --git a/src/Ryujinx.HLE/HOS/Kernel/Threading/KAddressArbiter.cs b/src/Ryujinx.HLE/HOS/Kernel/Threading/KAddressArbiter.cs index 3e13b917a..c9ac86fc9 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Threading/KAddressArbiter.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Threading/KAddressArbiter.cs @@ -529,7 +529,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading // The value is decremented if the number of threads waiting is less // or equal to the Count of threads to be signaled, or Count is zero // or negative. It is incremented if there are no threads waiting. - int waitingCount = _arbiterThreads[address].Count; + int waitingCount = 0; + + if (_arbiterThreads.TryGetValue(address, out List threads)) + { + waitingCount = threads.Count; + } + if (waitingCount > 0) {