See merge request ryubing/ryujinx!202
This commit is contained in:
LotP 2025-10-30 20:55:58 -05:00
parent ab7aeee67b
commit 92b61f9d73
43 changed files with 686 additions and 315 deletions

View file

@ -1107,17 +1107,17 @@ namespace ARMeilleure.CodeGen.X86
} }
else else
{ {
if (flags.HasFlag(InstructionFlags.Prefix66)) if ((flags & InstructionFlags.Prefix66) != 0)
{ {
WriteByte(0x66); WriteByte(0x66);
} }
if (flags.HasFlag(InstructionFlags.PrefixF2)) if ((flags & InstructionFlags.PrefixF2) != 0f)
{ {
WriteByte(0xf2); WriteByte(0xf2);
} }
if (flags.HasFlag(InstructionFlags.PrefixF3)) if ((flags & InstructionFlags.PrefixF3) != 0f)
{ {
WriteByte(0xf3); WriteByte(0xf3);
} }

View file

@ -1,6 +1,7 @@
using Ryujinx.Audio.Integration; using Ryujinx.Audio.Integration;
using Ryujinx.Audio.Renderer.Server.Sink; using Ryujinx.Audio.Renderer.Server.Sink;
using System; using System;
using System.Buffers;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Text; using System.Text;
@ -35,7 +36,9 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
Enabled = true; Enabled = true;
NodeId = nodeId; NodeId = nodeId;
DeviceName = Encoding.ASCII.GetString(sink.Parameter.DeviceName).TrimEnd('\0'); // Unused and wasting time and memory, re-add if needed
// DeviceName = Encoding.ASCII.GetString(sink.Parameter.DeviceName).TrimEnd('\0');
SessionId = sessionId; SessionId = sessionId;
InputCount = sink.Parameter.InputCount; InputCount = sink.Parameter.InputCount;
InputBufferIndices = new ushort[InputCount]; InputBufferIndices = new ushort[InputCount];
@ -88,7 +91,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
inputCount = bufferCount; inputCount = bufferCount;
} }
short[] outputBuffer = new short[inputCount * SampleCount]; short[] outputBuffer = ArrayPool<short>.Shared.Rent((int)inputCount * SampleCount);
for (int i = 0; i < bufferCount; i++) for (int i = 0; i < bufferCount; i++)
{ {
@ -100,7 +103,9 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
} }
} }
device.AppendBuffer(outputBuffer, inputCount); device.AppendBuffer(outputBuffer.AsSpan(..((int)inputCount * SampleCount)), inputCount);
ArrayPool<short>.Shared.Return(outputBuffer);
} }
else else
{ {

View file

@ -188,6 +188,8 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
/// </summary> /// </summary>
public Span<bool> BiquadFilterNeedInitialization => SpanHelpers.AsSpan<BiquadFilterNeedInitializationArrayStruct, bool>(ref _biquadFilterNeedInitialization); public Span<bool> BiquadFilterNeedInitialization => SpanHelpers.AsSpan<BiquadFilterNeedInitializationArrayStruct, bool>(ref _biquadFilterNeedInitialization);
private static List<ErrorInfo> _waveBufferUpdaterErrorInfosList;
/// <summary> /// <summary>
/// Initialize the <see cref="VoiceInfo"/>. /// Initialize the <see cref="VoiceInfo"/>.
/// </summary> /// </summary>
@ -216,6 +218,8 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
DataSourceStateAddressInfo.Setup(0, 0); DataSourceStateAddressInfo.Setup(0, 0);
InitializeWaveBuffers(); InitializeWaveBuffers();
_waveBufferUpdaterErrorInfosList ??= [];
} }
/// <summary> /// <summary>
@ -587,14 +591,14 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
Span<WaveBuffer> waveBuffersSpan = WaveBuffers.AsSpan(); Span<WaveBuffer> waveBuffersSpan = WaveBuffers.AsSpan();
Span<WaveBufferInternal> pWaveBuffersSpan = parameter.WaveBuffers.AsSpan(); Span<WaveBufferInternal> pWaveBuffersSpan = parameter.WaveBuffers.AsSpan();
List<ErrorInfo> errorInfosList = []; _waveBufferUpdaterErrorInfosList.Clear();
for (int i = 0; i < Constants.VoiceWaveBufferCount; i++) for (int i = 0; i < Constants.VoiceWaveBufferCount; i++)
{ {
UpdateWaveBuffer(errorInfosList, ref waveBuffersSpan[i], ref pWaveBuffersSpan[i], parameter.SampleFormat, voiceState.IsWaveBufferValid[i], mapper, ref behaviourInfo); UpdateWaveBuffer(_waveBufferUpdaterErrorInfosList, ref waveBuffersSpan[i], ref pWaveBuffersSpan[i], parameter.SampleFormat, voiceState.IsWaveBufferValid[i], mapper, ref behaviourInfo);
} }
errorInfos = errorInfosList.ToArray(); errorInfos = _waveBufferUpdaterErrorInfosList.ToArray();
} }
/// <summary> /// <summary>
@ -628,14 +632,14 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
Span<WaveBuffer> waveBuffersSpan = WaveBuffers.AsSpan(); Span<WaveBuffer> waveBuffersSpan = WaveBuffers.AsSpan();
Span<WaveBufferInternal> pWaveBuffersSpan = parameter.WaveBuffers.AsSpan(); Span<WaveBufferInternal> pWaveBuffersSpan = parameter.WaveBuffers.AsSpan();
List<ErrorInfo> errorInfosList = []; _waveBufferUpdaterErrorInfosList.Clear();
for (int i = 0; i < Constants.VoiceWaveBufferCount; i++) for (int i = 0; i < Constants.VoiceWaveBufferCount; i++)
{ {
UpdateWaveBuffer(errorInfosList, ref waveBuffersSpan[i], ref pWaveBuffersSpan[i], parameter.SampleFormat, voiceState.IsWaveBufferValid[i], mapper, ref behaviourInfo); UpdateWaveBuffer(_waveBufferUpdaterErrorInfosList, ref waveBuffersSpan[i], ref pWaveBuffersSpan[i], parameter.SampleFormat, voiceState.IsWaveBufferValid[i], mapper, ref behaviourInfo);
} }
errorInfos = errorInfosList.ToArray(); errorInfos = _waveBufferUpdaterErrorInfosList.ToArray();
} }
/// <summary> /// <summary>

View file

@ -24,7 +24,10 @@ namespace Ryujinx.Common.Collections
/// <exception cref="ArgumentNullException"><paramref name="key"/> is null</exception> /// <exception cref="ArgumentNullException"><paramref name="key"/> is null</exception>
public int Get(TKey key, ref TValue[] overlaps) public int Get(TKey key, ref TValue[] overlaps)
{ {
ArgumentNullException.ThrowIfNull(key); if (!typeof(TKey).IsValueType)
{
ArgumentNullException.ThrowIfNull(key);
}
IntervalTreeNode<TKey, TValue> node = GetNode(key); IntervalTreeNode<TKey, TValue> node = GetNode(key);
@ -91,7 +94,10 @@ namespace Ryujinx.Common.Collections
/// <returns>Number of deleted values</returns> /// <returns>Number of deleted values</returns>
public int Remove(TKey key, TValue value) public int Remove(TKey key, TValue value)
{ {
ArgumentNullException.ThrowIfNull(key); if (!typeof(TKey).IsValueType)
{
ArgumentNullException.ThrowIfNull(key);
}
int removed = Delete(key, value); int removed = Delete(key, value);
@ -144,7 +150,10 @@ namespace Ryujinx.Common.Collections
/// <exception cref="ArgumentNullException"><paramref name="key"/> is null</exception> /// <exception cref="ArgumentNullException"><paramref name="key"/> is null</exception>
private IntervalTreeNode<TKey, TValue> GetNode(TKey key) private IntervalTreeNode<TKey, TValue> GetNode(TKey key)
{ {
ArgumentNullException.ThrowIfNull(key); if (!typeof(TKey).IsValueType)
{
ArgumentNullException.ThrowIfNull(key);
}
IntervalTreeNode<TKey, TValue> node = Root; IntervalTreeNode<TKey, TValue> node = Root;
while (node != null) while (node != null)

View file

@ -1,11 +1,12 @@
using Ryujinx.Graphics.GAL.Multithreading.Model; using Ryujinx.Graphics.GAL.Multithreading.Model;
using Ryujinx.Graphics.GAL.Multithreading.Resources; using Ryujinx.Graphics.GAL.Multithreading.Resources;
using System.Linq; using System.Buffers;
namespace Ryujinx.Graphics.GAL.Multithreading.Commands namespace Ryujinx.Graphics.GAL.Multithreading.Commands
{ {
struct SetRenderTargetsCommand : IGALCommand, IGALCommand<SetRenderTargetsCommand> struct SetRenderTargetsCommand : IGALCommand, IGALCommand<SetRenderTargetsCommand>
{ {
public static readonly ArrayPool<ITexture> ArrayPool = ArrayPool<ITexture>.Create(512, 50);
public readonly CommandType CommandType => CommandType.SetRenderTargets; public readonly CommandType CommandType => CommandType.SetRenderTargets;
private TableRef<ITexture[]> _colors; private TableRef<ITexture[]> _colors;
private TableRef<ITexture> _depthStencil; private TableRef<ITexture> _depthStencil;
@ -18,7 +19,18 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands
public static void Run(ref SetRenderTargetsCommand command, ThreadedRenderer threaded, IRenderer renderer) public static void Run(ref SetRenderTargetsCommand command, ThreadedRenderer threaded, IRenderer renderer)
{ {
renderer.Pipeline.SetRenderTargets(command._colors.Get(threaded).Select(color => ((ThreadedTexture)color)?.Base).ToArray(), command._depthStencil.GetAs<ThreadedTexture>(threaded)?.Base); ITexture[] colors = command._colors.Get(threaded);
ITexture[] colorsCopy = ArrayPool.Rent(colors.Length);
for (int i = 0; i < colors.Length; i++)
{
colorsCopy[i] = ((ThreadedTexture)colors[i])?.Base;
}
renderer.Pipeline.SetRenderTargets(colorsCopy, command._depthStencil.GetAs<ThreadedTexture>(threaded)?.Base);
ArrayPool.Return(colorsCopy);
ArrayPool.Return(colors);
} }
} }
} }

View file

@ -269,7 +269,10 @@ namespace Ryujinx.Graphics.GAL.Multithreading
public unsafe void SetRenderTargets(ITexture[] colors, ITexture depthStencil) public unsafe void SetRenderTargets(ITexture[] colors, ITexture depthStencil)
{ {
_renderer.New<SetRenderTargetsCommand>()->Set(Ref(colors.ToArray()), Ref(depthStencil)); ITexture[] colorsCopy = SetRenderTargetsCommand.ArrayPool.Rent(colors.Length);
colors.CopyTo(colorsCopy, 0);
_renderer.New<SetRenderTargetsCommand>()->Set(Ref(colorsCopy), Ref(depthStencil));
_renderer.QueueCommand(); _renderer.QueueCommand();
} }

View file

@ -451,7 +451,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
// TODO: Confirm behaviour on hardware. // TODO: Confirm behaviour on hardware.
// When this is active, the origin appears to be on the bottom. // When this is active, the origin appears to be on the bottom.
if (_state.State.YControl.HasFlag(YControl.NegateY)) if ((_state.State.YControl & YControl.NegateY) != 0)
{ {
dstY0 -= dstHeight; dstY0 -= dstHeight;
} }

View file

@ -646,7 +646,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
int width = scissor.X2 - x; int width = scissor.X2 - x;
int height = scissor.Y2 - y; int height = scissor.Y2 - y;
if (_state.State.YControl.HasFlag(YControl.NegateY)) if ((_state.State.YControl & YControl.NegateY) != 0)
{ {
ref ScreenScissorState screenScissor = ref _state.State.ScreenScissorState; ref ScreenScissorState screenScissor = ref _state.State.ScreenScissorState;
y = screenScissor.Height - height - y; y = screenScissor.Height - height - y;
@ -730,7 +730,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
FaceState face = _state.State.FaceState; FaceState face = _state.State.FaceState;
bool disableTransform = _state.State.ViewportTransformEnable == 0; bool disableTransform = _state.State.ViewportTransformEnable == 0;
bool yNegate = yControl.HasFlag(YControl.NegateY); bool yNegate = (yControl & YControl.NegateY) != 0;
UpdateFrontFace(yControl, face.FrontFace); UpdateFrontFace(yControl, face.FrontFace);
UpdateDepthMode(); UpdateDepthMode();
@ -1230,7 +1230,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// <param name="frontFace">Front face</param> /// <param name="frontFace">Front face</param>
private void UpdateFrontFace(YControl yControl, FrontFace frontFace) private void UpdateFrontFace(YControl yControl, FrontFace frontFace)
{ {
bool isUpperLeftOrigin = !yControl.HasFlag(YControl.TriangleRastFlip); bool isUpperLeftOrigin = (yControl & YControl.TriangleRastFlip) == 0;
if (isUpperLeftOrigin) if (isUpperLeftOrigin)
{ {
@ -1521,7 +1521,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
{ {
// Make sure we update the viewport size on the support buffer if it will be consumed on the new shader. // Make sure we update the viewport size on the support buffer if it will be consumed on the new shader.
if (!_fsReadsFragCoord && _state.State.YControl.HasFlag(YControl.NegateY)) if (!_fsReadsFragCoord && (_state.State.YControl & YControl.NegateY) != 0)
{ {
UpdateSupportBufferViewportSize(); UpdateSupportBufferViewportSize();
} }

View file

@ -381,9 +381,9 @@ namespace Ryujinx.Graphics.Gpu
/// <param name="flags">Modifiers for how host sync should be created</param> /// <param name="flags">Modifiers for how host sync should be created</param>
internal void CreateHostSyncIfNeeded(HostSyncFlags flags) internal void CreateHostSyncIfNeeded(HostSyncFlags flags)
{ {
bool syncpoint = flags.HasFlag(HostSyncFlags.Syncpoint); bool syncPoint = (flags & HostSyncFlags.Syncpoint) == HostSyncFlags.Syncpoint;
bool strict = flags.HasFlag(HostSyncFlags.Strict); bool strict = (flags & HostSyncFlags.Strict) == HostSyncFlags.Strict;
bool force = flags.HasFlag(HostSyncFlags.Force); bool force = (flags & HostSyncFlags.Force) == HostSyncFlags.Force;
if (BufferMigrations.Count > 0) if (BufferMigrations.Count > 0)
{ {
@ -402,24 +402,37 @@ namespace Ryujinx.Graphics.Gpu
} }
} }
if (force || _pendingSync || (syncpoint && SyncpointActions.Count > 0)) if (force || _pendingSync || (syncPoint && SyncpointActions.Count > 0))
{ {
foreach (ISyncActionHandler action in SyncActions) foreach (ISyncActionHandler action in SyncActions)
{ {
action.SyncPreAction(syncpoint); action.SyncPreAction(syncPoint);
} }
foreach (ISyncActionHandler action in SyncpointActions) foreach (ISyncActionHandler action in SyncpointActions)
{ {
action.SyncPreAction(syncpoint); action.SyncPreAction(syncPoint);
} }
Renderer.CreateSync(SyncNumber, strict); Renderer.CreateSync(SyncNumber, strict);
SyncNumber++; SyncNumber++;
SyncActions.RemoveAll(action => action.SyncAction(syncpoint)); for (int i = 0; i < SyncActions.Count; i++)
SyncpointActions.RemoveAll(action => action.SyncAction(syncpoint)); {
if (SyncActions[i].SyncAction(syncPoint))
{
SyncActions.RemoveAt(i--);
}
}
for (int i = 0; i < SyncpointActions.Count; i++)
{
if (SyncpointActions[i].SyncAction(syncPoint))
{
SyncpointActions.RemoveAt(i--);
}
}
} }
_pendingSync = false; _pendingSync = false;

View file

@ -1628,7 +1628,15 @@ namespace Ryujinx.Graphics.Gpu.Image
{ {
lock (_poolOwners) lock (_poolOwners)
{ {
int references = _poolOwners.RemoveAll(entry => entry.Pool == pool && entry.ID == id || id == -1); int references = 0;
for (int i = 0; i < _poolOwners.Count; i++)
{
if (_poolOwners[i].Pool == pool && _poolOwners[i].ID == id || id == -1)
{
_poolOwners.RemoveAt(i--);
references++;
}
}
if (references == 0) if (references == 0)
{ {

View file

@ -45,7 +45,11 @@ namespace Ryujinx.Graphics.Gpu.Image
/// </summary> /// </summary>
private const int GranularLayerThreshold = 8; private const int GranularLayerThreshold = 8;
private delegate void HandlesCallbackDelegate(int baseHandle, int regionCount, bool split = false); private delegate bool HandlesCallbackDelegate(int baseHandle, int regionCount, bool split = false, bool specialData = false);
private readonly HandlesCallbackDelegate _signalModifyingCallback;
private readonly HandlesCallbackDelegate _discardDataCallback;
private readonly HandlesCallbackDelegate _checkDirtyCallback;
/// <summary> /// <summary>
/// The storage texture associated with this group. /// The storage texture associated with this group.
@ -126,6 +130,10 @@ namespace Ryujinx.Graphics.Gpu.Image
_incompatibleOverlaps = incompatibleOverlaps; _incompatibleOverlaps = incompatibleOverlaps;
_flushIncompatibleOverlaps = TextureCompatibility.IsFormatHostIncompatible(storage.Info, context.Capabilities); _flushIncompatibleOverlaps = TextureCompatibility.IsFormatHostIncompatible(storage.Info, context.Capabilities);
_signalModifyingCallback = SignalModifyingCallback;
_discardDataCallback = DiscardDataCallback;
_checkDirtyCallback = CheckDirtyCallback;
} }
/// <summary> /// <summary>
@ -253,29 +261,33 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="consume">True to consume the dirty flags and reprotect, false to leave them as is</param> /// <param name="consume">True to consume the dirty flags and reprotect, false to leave them as is</param>
/// <returns>True if a flag was dirty, false otherwise</returns> /// <returns>True if a flag was dirty, false otherwise</returns>
public bool CheckDirty(Texture texture, bool consume) public bool CheckDirty(Texture texture, bool consume)
{
EvaluateRelevantHandles(texture, _checkDirtyCallback, out bool dirty, consume);
return dirty;
}
bool CheckDirtyCallback(int baseHandle, int regionCount, bool split, bool consume)
{ {
bool dirty = false; bool dirty = false;
EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) => for (int i = 0; i < regionCount; i++)
{ {
for (int i = 0; i < regionCount; i++) TextureGroupHandle group = _handles[baseHandle + i];
foreach (RegionHandle handle in group.Handles)
{ {
TextureGroupHandle group = _handles[baseHandle + i]; if (handle.Dirty)
foreach (RegionHandle handle in group.Handles)
{ {
if (handle.Dirty) if (consume)
{ {
if (consume) handle.Reprotect();
{
handle.Reprotect();
}
dirty = true;
} }
dirty = true;
} }
} }
}); }
return dirty; return dirty;
} }
@ -287,15 +299,19 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="texture">The texture being discarded</param> /// <param name="texture">The texture being discarded</param>
public void DiscardData(Texture texture) public void DiscardData(Texture texture)
{ {
EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) => EvaluateRelevantHandles(texture, _discardDataCallback, out _);
}
bool DiscardDataCallback(int baseHandle, int regionCount, bool split, bool bound)
{
for (int i = 0; i < regionCount; i++)
{ {
for (int i = 0; i < regionCount; i++) TextureGroupHandle group = _handles[baseHandle + i];
{
TextureGroupHandle group = _handles[baseHandle + i];
group.DiscardData(); group.DiscardData();
} }
});
return true;
} }
/// <summary> /// <summary>
@ -307,7 +323,7 @@ namespace Ryujinx.Graphics.Gpu.Image
{ {
FlushIncompatibleOverlapsIfNeeded(); FlushIncompatibleOverlapsIfNeeded();
EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) => EvaluateRelevantHandles(texture, (baseHandle, regionCount, split, bound) =>
{ {
bool dirty = false; bool dirty = false;
bool anyModified = false; bool anyModified = false;
@ -383,7 +399,9 @@ namespace Ryujinx.Graphics.Gpu.Image
texture.SynchronizeFull(); texture.SynchronizeFull();
} }
} }
});
return true;
}, out _);
} }
/// <summary> /// <summary>
@ -460,7 +478,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="texture">The texture to synchronize dependents of</param> /// <param name="texture">The texture to synchronize dependents of</param>
public void SynchronizeDependents(Texture texture) public void SynchronizeDependents(Texture texture)
{ {
EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) => EvaluateRelevantHandles(texture, (baseHandle, regionCount, split, bound) =>
{ {
for (int i = 0; i < regionCount; i++) for (int i = 0; i < regionCount; i++)
{ {
@ -468,7 +486,9 @@ namespace Ryujinx.Graphics.Gpu.Image
group.SynchronizeDependents(); group.SynchronizeDependents();
} }
});
return true;
}, out _);
} }
/// <summary> /// <summary>
@ -550,7 +570,7 @@ namespace Ryujinx.Graphics.Gpu.Image
tracked = tracked || ShouldFlushTriggerTracking(); tracked = tracked || ShouldFlushTriggerTracking();
bool flushed = false; bool flushed = false;
EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) => EvaluateRelevantHandles(texture, (baseHandle, regionCount, split, bound) =>
{ {
int startSlice = 0; int startSlice = 0;
int endSlice = 0; int endSlice = 0;
@ -604,7 +624,9 @@ namespace Ryujinx.Graphics.Gpu.Image
flushed = true; flushed = true;
} }
});
return true;
}, out _);
Storage.SignalModifiedDirty(); Storage.SignalModifiedDirty();
@ -693,7 +715,7 @@ namespace Ryujinx.Graphics.Gpu.Image
ClearIncompatibleOverlaps(texture); ClearIncompatibleOverlaps(texture);
EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) => EvaluateRelevantHandles(texture, (baseHandle, regionCount, split, bound) =>
{ {
for (int i = 0; i < regionCount; i++) for (int i = 0; i < regionCount; i++)
{ {
@ -701,7 +723,9 @@ namespace Ryujinx.Graphics.Gpu.Image
group.SignalModified(_context); group.SignalModified(_context);
} }
});
return true;
}, out _);
} }
/// <summary> /// <summary>
@ -714,16 +738,20 @@ namespace Ryujinx.Graphics.Gpu.Image
ModifiedSequence = _context.GetModifiedSequence(); ModifiedSequence = _context.GetModifiedSequence();
ClearIncompatibleOverlaps(texture); ClearIncompatibleOverlaps(texture);
EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) => EvaluateRelevantHandles(texture, _signalModifyingCallback, out _, bound);
}
bool SignalModifyingCallback(int baseHandle, int regionCount, bool split, bool bound)
{
for (int i = 0; i < regionCount; i++)
{ {
for (int i = 0; i < regionCount; i++) TextureGroupHandle group = _handles[baseHandle + i];
{
TextureGroupHandle group = _handles[baseHandle + i];
group.SignalModifying(bound, _context); group.SignalModifying(bound, _context);
} }
});
return true;
} }
/// <summary> /// <summary>
@ -767,16 +795,16 @@ namespace Ryujinx.Graphics.Gpu.Image
/// A function to be called with the base index of the range of handles for the given texture, and the number of handles it covers. /// A function to be called with the base index of the range of handles for the given texture, and the number of handles it covers.
/// This can be called for multiple disjoint ranges, if required. /// This can be called for multiple disjoint ranges, if required.
/// </param> /// </param>
private void EvaluateRelevantHandles(Texture texture, HandlesCallbackDelegate callback) private void EvaluateRelevantHandles(Texture texture, HandlesCallbackDelegate callback, out bool result, bool specialData = false)
{ {
if (texture == Storage || !(_hasMipViews || _hasLayerViews)) if (texture == Storage || !(_hasMipViews || _hasLayerViews))
{ {
callback(0, _handles.Length); result = callback(0, _handles.Length, specialData: specialData);
return; return;
} }
EvaluateRelevantHandles(texture.FirstLayer, texture.FirstLevel, texture.Info.GetSlices(), texture.Info.Levels, callback); EvaluateRelevantHandles(texture.FirstLayer, texture.FirstLevel, texture.Info.GetSlices(), texture.Info.Levels, callback, out result, specialData);
} }
/// <summary> /// <summary>
@ -791,11 +819,13 @@ namespace Ryujinx.Graphics.Gpu.Image
/// A function to be called with the base index of the range of handles for the given texture, and the number of handles it covers. /// A function to be called with the base index of the range of handles for the given texture, and the number of handles it covers.
/// This can be called for multiple disjoint ranges, if required. /// This can be called for multiple disjoint ranges, if required.
/// </param> /// </param>
private void EvaluateRelevantHandles(int firstLayer, int firstLevel, int slices, int levels, HandlesCallbackDelegate callback) private void EvaluateRelevantHandles(int firstLayer, int firstLevel, int slices, int levels, HandlesCallbackDelegate callback, out bool result, bool specialData = false)
{ {
int targetLayerHandles = _hasLayerViews ? slices : 1; int targetLayerHandles = _hasLayerViews ? slices : 1;
int targetLevelHandles = _hasMipViews ? levels : 1; int targetLevelHandles = _hasMipViews ? levels : 1;
result = false;
if (_isBuffer) if (_isBuffer)
{ {
return; return;
@ -808,7 +838,7 @@ namespace Ryujinx.Graphics.Gpu.Image
{ {
// When there are no layer views, the mips are at a consistent offset. // When there are no layer views, the mips are at a consistent offset.
callback(firstLevel, targetLevelHandles); result = callback(firstLevel, targetLevelHandles, specialData: specialData);
} }
else else
{ {
@ -822,7 +852,7 @@ namespace Ryujinx.Graphics.Gpu.Image
while (levels-- > 1) while (levels-- > 1)
{ {
callback(firstLayer + levelIndex, slices); result = callback(firstLayer + levelIndex, slices, specialData: specialData);
levelIndex += layerCount; levelIndex += layerCount;
layerCount = Math.Max(layerCount >> 1, 1); layerCount = Math.Max(layerCount >> 1, 1);
@ -839,7 +869,7 @@ namespace Ryujinx.Graphics.Gpu.Image
totalSize += layerCount; totalSize += layerCount;
} }
callback(firstLayer + levelIndex, totalSize); result = callback(firstLayer + levelIndex, totalSize, specialData: specialData);
} }
} }
} }
@ -856,12 +886,12 @@ namespace Ryujinx.Graphics.Gpu.Image
for (int i = 0; i < slices; i++) for (int i = 0; i < slices; i++)
{ {
callback(firstLevel + (firstLayer + i) * levelHandles, targetLevelHandles, true); result = callback(firstLevel + (firstLayer + i) * levelHandles, targetLevelHandles, true, specialData: specialData);
} }
} }
else else
{ {
callback(firstLevel + firstLayer * levelHandles, targetLevelHandles + (targetLayerHandles - 1) * levelHandles); result = callback(firstLevel + firstLayer * levelHandles, targetLevelHandles + (targetLayerHandles - 1) * levelHandles, specialData: specialData);
} }
} }
} }
@ -1439,8 +1469,16 @@ namespace Ryujinx.Graphics.Gpu.Image
List<(int BaseHandle, int RegionCount)> targetRange = []; List<(int BaseHandle, int RegionCount)> targetRange = [];
List<(int BaseHandle, int RegionCount)> otherRange = []; List<(int BaseHandle, int RegionCount)> otherRange = [];
EvaluateRelevantHandles(firstLayer, firstLevel, other.Info.GetSlices(), other.Info.Levels, (baseHandle, regionCount, split) => targetRange.Add((baseHandle, regionCount))); EvaluateRelevantHandles(firstLayer, firstLevel, other.Info.GetSlices(), other.Info.Levels, (baseHandle, regionCount, split, specialData) =>
otherGroup.EvaluateRelevantHandles(other, (baseHandle, regionCount, split) => otherRange.Add((baseHandle, regionCount))); {
targetRange.Add((baseHandle, regionCount));
return true;
}, out _);
otherGroup.EvaluateRelevantHandles(other, (baseHandle, regionCount, split, specialData) =>
{
otherRange.Add((baseHandle, regionCount));
return true;
}, out _);
int targetIndex = 0; int targetIndex = 0;
int otherIndex = 0; int otherIndex = 0;

View file

@ -93,6 +93,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
private ulong _dirtyStart = ulong.MaxValue; private ulong _dirtyStart = ulong.MaxValue;
private ulong _dirtyEnd = ulong.MaxValue; private ulong _dirtyEnd = ulong.MaxValue;
private readonly Action<ulong, ulong> _syncPreRangeAction;
private readonly Action<ulong, ulong> _syncRangeAction;
/// <summary> /// <summary>
/// Creates a new instance of the buffer. /// Creates a new instance of the buffer.
/// </summary> /// </summary>
@ -177,6 +180,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
_modifiedDelegate = RegionModified; _modifiedDelegate = RegionModified;
_virtualDependenciesLock = new ReaderWriterLockSlim(); _virtualDependenciesLock = new ReaderWriterLockSlim();
_syncPreRangeAction = SyncPreRangeAction;
_syncRangeAction = SyncRangeAction;
} }
/// <summary> /// <summary>
@ -401,13 +407,15 @@ namespace Ryujinx.Graphics.Gpu.Memory
if (_preFlush.ShouldCopy) if (_preFlush.ShouldCopy)
{ {
_modifiedRanges?.GetRangesAtSync(Address, Size, _context.SyncNumber, (address, size) => _modifiedRanges?.GetRangesAtSync(Address, Size, _context.SyncNumber, _syncPreRangeAction);
{
_preFlush.CopyModified(address, size);
});
} }
} }
} }
void SyncPreRangeAction(ulong address, ulong size)
{
_preFlush.CopyModified(address, size);
}
/// <summary> /// <summary>
/// Action to be performed when a syncpoint is reached after modification. /// Action to be performed when a syncpoint is reached after modification.
@ -420,11 +428,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
if (_useGranular) if (_useGranular)
{ {
_modifiedRanges?.GetRanges(Address, Size, (address, size) =>
{
_memoryTrackingGranular.RegisterAction(address, size, _externalFlushDelegate); _modifiedRanges?.GetRanges(Address, Size, _syncRangeAction);
SynchronizeMemory(address, size);
});
} }
else else
{ {
@ -434,6 +440,12 @@ namespace Ryujinx.Graphics.Gpu.Memory
return true; return true;
} }
void SyncRangeAction(ulong address, ulong size)
{
_memoryTrackingGranular.RegisterAction(address, size, _externalFlushDelegate);
SynchronizeMemory(address, size);
}
/// <summary> /// <summary>
/// Inherit modified and dirty ranges from another buffer. /// Inherit modified and dirty ranges from another buffer.

View file

@ -1,5 +1,6 @@
using Ryujinx.Memory.Range; using Ryujinx.Memory.Range;
using System; using System;
using System.Buffers;
using System.Linq; using System.Linq;
namespace Ryujinx.Graphics.Gpu.Memory namespace Ryujinx.Graphics.Gpu.Memory
@ -276,13 +277,18 @@ namespace Ryujinx.Graphics.Gpu.Memory
{ {
// We use the non-span method here because keeping the lock will cause a deadlock. // We use the non-span method here because keeping the lock will cause a deadlock.
Lock.EnterReadLock(); Lock.EnterReadLock();
RangeItem<BufferModifiedRange>[] overlaps = FindOverlapsAsArray(address, size); RangeItem<BufferModifiedRange>[] overlaps = FindOverlapsAsArray(address, size, out int length);
Lock.ExitReadLock(); Lock.ExitReadLock();
for (int i = 0; i < overlaps.Length; i++) if (length != 0)
{ {
BufferModifiedRange overlap = overlaps[i].Value; for (int i = 0; i < length; i++)
rangeAction(overlap.Address, overlap.Size); {
BufferModifiedRange overlap = overlaps[i].Value;
rangeAction(overlap.Address, overlap.Size);
}
ArrayPool<RangeItem<BufferModifiedRange>>.Shared.Return(overlaps);
} }
} }
@ -392,9 +398,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
Lock.EnterWriteLock(); Lock.EnterWriteLock();
// We use the non-span method here because the array is partially modified by the code, which would invalidate a span. // We use the non-span method here because the array is partially modified by the code, which would invalidate a span.
RangeItem<BufferModifiedRange>[] overlaps = FindOverlapsAsArray(address, size); RangeItem<BufferModifiedRange>[] overlaps = FindOverlapsAsArray(address, size, out int rangeCount);
int rangeCount = overlaps.Length;
if (rangeCount == 0) if (rangeCount == 0)
{ {
@ -410,7 +414,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
for (int i = 0; i < rangeCount; i++) for (int i = 0; i < rangeCount; i++)
{ {
BufferModifiedRange overlap = overlaps[i].Value; BufferModifiedRange overlap = overlaps![i].Value;
long diff = (long)(overlap.SyncNumber - currentSync); long diff = (long)(overlap.SyncNumber - currentSync);
@ -430,7 +434,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
// Wait for the syncpoint. // Wait for the syncpoint.
_context.Renderer.WaitSync(currentSync + (ulong)highestDiff); _context.Renderer.WaitSync(currentSync + (ulong)highestDiff);
RemoveRangesAndFlush(overlaps.ToArray(), rangeCount, highestDiff, currentSync, address, endAddress); RemoveRangesAndFlush(overlaps, rangeCount, highestDiff, currentSync, address, endAddress);
ArrayPool<RangeItem<BufferModifiedRange>>.Shared.Return(overlaps!);
Lock.ExitWriteLock(); Lock.ExitWriteLock();
} }

View file

@ -397,7 +397,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <returns>True if queried, false otherwise</returns> /// <returns>True if queried, false otherwise</returns>
public bool IsPrimitiveTopologyQueried() public bool IsPrimitiveTopologyQueried()
{ {
return _queriedState.HasFlag(QueriedStateFlags.PrimitiveTopology); return (_queriedState & QueriedStateFlags.PrimitiveTopology) == QueriedStateFlags.PrimitiveTopology;
} }
/// <summary> /// <summary>
@ -904,7 +904,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
specState.PipelineState = pipelineState; specState.PipelineState = pipelineState;
} }
if (specState._queriedState.HasFlag(QueriedStateFlags.TransformFeedback)) if ((specState._queriedState & QueriedStateFlags.TransformFeedback) == QueriedStateFlags.TransformFeedback)
{ {
ushort tfCount = 0; ushort tfCount = 0;
dataReader.Read(ref tfCount); dataReader.Read(ref tfCount);
@ -930,7 +930,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
specState._textureSpecialization[textureKey] = textureState; specState._textureSpecialization[textureKey] = textureState;
} }
if (specState._queriedState.HasFlag(QueriedStateFlags.TextureArrayFromBuffer)) if ((specState._queriedState & QueriedStateFlags.TextureArrayFromBuffer) == QueriedStateFlags.TextureArrayFromBuffer)
{ {
dataReader.Read(ref count); dataReader.Read(ref count);
@ -946,7 +946,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
} }
} }
if (specState._queriedState.HasFlag(QueriedStateFlags.TextureArrayFromPool)) if ((specState._queriedState & QueriedStateFlags.TextureArrayFromPool) == QueriedStateFlags.TextureArrayFromPool)
{ {
dataReader.Read(ref count); dataReader.Read(ref count);
@ -1006,7 +1006,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
dataWriter.WriteWithMagicAndSize(ref pipelineState, PgpsMagic); dataWriter.WriteWithMagicAndSize(ref pipelineState, PgpsMagic);
} }
if (_queriedState.HasFlag(QueriedStateFlags.TransformFeedback)) if ((_queriedState & QueriedStateFlags.TransformFeedback) == QueriedStateFlags.TransformFeedback)
{ {
ushort tfCount = (ushort)TransformFeedbackDescriptors.Length; ushort tfCount = (ushort)TransformFeedbackDescriptors.Length;
dataWriter.Write(ref tfCount); dataWriter.Write(ref tfCount);
@ -1029,7 +1029,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
dataWriter.WriteWithMagicAndSize(ref textureState.Value, TexsMagic); dataWriter.WriteWithMagicAndSize(ref textureState.Value, TexsMagic);
} }
if (_queriedState.HasFlag(QueriedStateFlags.TextureArrayFromBuffer)) if ((_queriedState & QueriedStateFlags.TextureArrayFromBuffer) == QueriedStateFlags.TextureArrayFromBuffer)
{ {
count = (ushort)_textureArrayFromBufferSpecialization.Count; count = (ushort)_textureArrayFromBufferSpecialization.Count;
dataWriter.Write(ref count); dataWriter.Write(ref count);
@ -1044,7 +1044,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
} }
} }
if (_queriedState.HasFlag(QueriedStateFlags.TextureArrayFromPool)) if ((_queriedState & QueriedStateFlags.TextureArrayFromPool) == QueriedStateFlags.TextureArrayFromPool)
{ {
count = (ushort)_textureArrayFromPoolSpecialization.Count; count = (ushort)_textureArrayFromPoolSpecialization.Count;
dataWriter.Write(ref count); dataWriter.Write(ref count);

View file

@ -58,6 +58,8 @@ namespace Ryujinx.Graphics.Vulkan
private Dictionary<ulong, StagingBufferReserved> _mirrors; private Dictionary<ulong, StagingBufferReserved> _mirrors;
private bool _useMirrors; private bool _useMirrors;
private Action _decrementReferenceCount;
public BufferHolder(VulkanRenderer gd, Device device, VkBuffer buffer, MemoryAllocation allocation, int size, BufferAllocationType type, BufferAllocationType currentType) public BufferHolder(VulkanRenderer gd, Device device, VkBuffer buffer, MemoryAllocation allocation, int size, BufferAllocationType type, BufferAllocationType currentType)
{ {
_gd = gd; _gd = gd;
@ -75,6 +77,8 @@ namespace Ryujinx.Graphics.Vulkan
_flushLock = new ReaderWriterLockSlim(); _flushLock = new ReaderWriterLockSlim();
_useMirrors = gd.IsTBDR; _useMirrors = gd.IsTBDR;
_decrementReferenceCount = _buffer.DecrementReferenceCount;
} }
public BufferHolder(VulkanRenderer gd, Device device, VkBuffer buffer, Auto<MemoryAllocation> allocation, int size, BufferAllocationType type, BufferAllocationType currentType, int offset) public BufferHolder(VulkanRenderer gd, Device device, VkBuffer buffer, Auto<MemoryAllocation> allocation, int size, BufferAllocationType type, BufferAllocationType currentType, int offset)
@ -444,7 +448,7 @@ namespace Ryujinx.Graphics.Vulkan
_flushLock.ExitReadLock(); _flushLock.ExitReadLock();
return PinnedSpan<byte>.UnsafeFromSpan(result, _buffer.DecrementReferenceCount); return PinnedSpan<byte>.UnsafeFromSpan(result, _decrementReferenceCount);
} }
BackgroundResource resource = _gd.BackgroundResources.Get(); BackgroundResource resource = _gd.BackgroundResources.Get();

View file

@ -1,6 +1,7 @@
using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL;
using Silk.NET.Vulkan; using Silk.NET.Vulkan;
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using Format = Ryujinx.Graphics.GAL.Format; using Format = Ryujinx.Graphics.GAL.Format;
using VkFormat = Silk.NET.Vulkan.Format; using VkFormat = Silk.NET.Vulkan.Format;
@ -10,26 +11,27 @@ namespace Ryujinx.Graphics.Vulkan
class FramebufferParams class FramebufferParams
{ {
private readonly Device _device; private readonly Device _device;
private readonly Auto<DisposableImageView>[] _attachments; private Auto<DisposableImageView>[] _attachments;
private readonly TextureView[] _colors; private TextureView[] _colors;
private readonly TextureView _depthStencil; private TextureView _depthStencil;
private readonly TextureView[] _colorsCanonical; private TextureView[] _colorsCanonical;
private readonly TextureView _baseAttachment; private TextureView _baseAttachment;
private readonly uint _validColorAttachments; private uint _validColorAttachments;
private int _totalCount;
public uint Width { get; } public uint Width { get; private set; }
public uint Height { get; } public uint Height { get; private set; }
public uint Layers { get; } public uint Layers { get; private set; }
public uint[] AttachmentSamples { get; } public uint[] AttachmentSamples { get; private set; }
public VkFormat[] AttachmentFormats { get; } public VkFormat[] AttachmentFormats { get; private set; }
public int[] AttachmentIndices { get; } public int[] AttachmentIndices { get; private set; }
public uint AttachmentIntegerFormatMask { get; } public uint AttachmentIntegerFormatMask { get; private set; }
public bool LogicOpsAllowed { get; } public bool LogicOpsAllowed { get; private set; }
public int AttachmentsCount { get; } public int AttachmentsCount { get; private set; }
public int MaxColorAttachmentIndex => AttachmentIndices.Length > 0 ? AttachmentIndices[^1] : -1; public int MaxColorAttachmentIndex => ColorAttachmentsCount > 0 ? AttachmentIndices[ColorAttachmentsCount - 1] : -1;
public bool HasDepthStencil { get; } public bool HasDepthStencil { get; private set; }
public int ColorAttachmentsCount => AttachmentsCount - (HasDepthStencil ? 1 : 0); public int ColorAttachmentsCount => AttachmentsCount - (HasDepthStencil ? 1 : 0);
public FramebufferParams(Device device, TextureView view, uint width, uint height) public FramebufferParams(Device device, TextureView view, uint width, uint height)
@ -50,7 +52,7 @@ namespace Ryujinx.Graphics.Vulkan
else else
{ {
_colors = [view]; _colors = [view];
_colorsCanonical = _colors; _colorsCanonical = [view];
} }
Width = width; Width = width;
@ -64,6 +66,7 @@ namespace Ryujinx.Graphics.Vulkan
LogicOpsAllowed = !format.IsFloatOrSrgb(); LogicOpsAllowed = !format.IsFloatOrSrgb();
AttachmentsCount = 1; AttachmentsCount = 1;
_totalCount = 1;
HasDepthStencil = isDepthStencil; HasDepthStencil = isDepthStencil;
} }
@ -133,7 +136,7 @@ namespace Ryujinx.Graphics.Vulkan
AttachmentIntegerFormatMask = attachmentIntegerFormatMask; AttachmentIntegerFormatMask = attachmentIntegerFormatMask;
LogicOpsAllowed = !allFormatsFloatOrSrgb; LogicOpsAllowed = !allFormatsFloatOrSrgb;
if (depthStencil is TextureView dsTexture && dsTexture.Valid) if (depthStencil is TextureView { Valid: true } dsTexture)
{ {
_attachments[count - 1] = dsTexture.GetImageViewForAttachment(); _attachments[count - 1] = dsTexture.GetImageViewForAttachment();
_depthStencil = dsTexture; _depthStencil = dsTexture;
@ -159,11 +162,151 @@ namespace Ryujinx.Graphics.Vulkan
Layers = layers; Layers = layers;
AttachmentsCount = count; AttachmentsCount = count;
_totalCount = colors.Length;
}
public FramebufferParams Update(ITexture[] colors, ITexture depthStencil)
{
int colorsCount = colors.Count(IsValidTextureView);
int count = colorsCount + (IsValidTextureView(depthStencil) ? 1 : 0);
Array.Clear(_attachments);
Array.Clear(_colors);
if (_attachments.Length < count)
{
Array.Resize(ref _attachments, count);
}
if (_colors.Length < colorsCount)
{
Array.Resize(ref _colors, colorsCount);
}
if (_colorsCanonical.Length < colors.Length)
{
Array.Resize(ref _colorsCanonical, colors.Length);
}
for (int i = 0; i < colors.Length; i++)
{
ITexture color = colors[i];
if (color is TextureView { Valid: true } view)
{
_colorsCanonical[i] = view;
}
else
{
_colorsCanonical[i] = null;
}
}
Array.Clear(AttachmentSamples);
Array.Clear(AttachmentFormats);
Array.Clear(AttachmentIndices);
if (AttachmentSamples.Length < count)
{
uint[] attachmentSamples = AttachmentSamples;
Array.Resize(ref attachmentSamples, count);
AttachmentSamples = attachmentSamples;
}
if (AttachmentFormats.Length < count)
{
VkFormat[] attachmentFormats = AttachmentFormats;
Array.Resize(ref attachmentFormats, count);
AttachmentFormats = attachmentFormats;
}
if (AttachmentIndices.Length < colorsCount)
{
int[] attachmentIndices = AttachmentIndices;
Array.Resize(ref attachmentIndices, colorsCount);
AttachmentIndices = attachmentIndices;
}
uint width = uint.MaxValue;
uint height = uint.MaxValue;
uint layers = uint.MaxValue;
int index = 0;
uint attachmentIntegerFormatMask = 0;
bool allFormatsFloatOrSrgb = colorsCount != 0;
_validColorAttachments = 0;
_baseAttachment = null;
for (int bindIndex = 0; bindIndex < colors.Length; bindIndex++)
{
TextureView texture = _colorsCanonical[bindIndex];
if (texture is not null)
{
_attachments[index] = texture.GetImageViewForAttachment();
_colors[index] = texture;
_validColorAttachments |= 1u << bindIndex;
_baseAttachment = texture;
AttachmentSamples[index] = (uint)texture.Info.Samples;
AttachmentFormats[index] = texture.VkFormat;
AttachmentIndices[index] = bindIndex;
Format format = texture.Info.Format;
if (format.IsInteger())
{
attachmentIntegerFormatMask |= 1u << bindIndex;
}
allFormatsFloatOrSrgb &= format.IsFloatOrSrgb();
width = Math.Min(width, (uint)texture.Width);
height = Math.Min(height, (uint)texture.Height);
layers = Math.Min(layers, (uint)texture.Layers);
if (++index >= colorsCount)
{
break;
}
}
}
AttachmentIntegerFormatMask = attachmentIntegerFormatMask;
LogicOpsAllowed = !allFormatsFloatOrSrgb;
_depthStencil = null;
HasDepthStencil = false;
if (depthStencil is TextureView { Valid: true } dsTexture)
{
_attachments[count - 1] = dsTexture.GetImageViewForAttachment();
_depthStencil = dsTexture;
_baseAttachment ??= dsTexture;
AttachmentSamples[count - 1] = (uint)dsTexture.Info.Samples;
AttachmentFormats[count - 1] = dsTexture.VkFormat;
width = Math.Min(width, (uint)dsTexture.Width);
height = Math.Min(height, (uint)dsTexture.Height);
layers = Math.Min(layers, (uint)dsTexture.Layers);
HasDepthStencil = true;
}
if (count == 0)
{
width = height = layers = 1;
}
Width = width;
Height = height;
Layers = layers;
AttachmentsCount = count;
_totalCount = colors.Length;
return this;
} }
public Auto<DisposableImageView> GetAttachment(int index) public Auto<DisposableImageView> GetAttachment(int index)
{ {
if ((uint)index >= _attachments.Length) if ((uint)index >= AttachmentsCount)
{ {
return null; return null;
} }
@ -183,7 +326,7 @@ namespace Ryujinx.Graphics.Vulkan
public ComponentType GetAttachmentComponentType(int index) public ComponentType GetAttachmentComponentType(int index)
{ {
if (_colors != null && (uint)index < _colors.Length) if (_colors != null && (uint)index < ColorAttachmentsCount)
{ {
Format format = _colors[index].Info.Format; Format format = _colors[index].Info.Format;
@ -218,7 +361,7 @@ namespace Ryujinx.Graphics.Vulkan
private static bool IsValidTextureView(ITexture texture) private static bool IsValidTextureView(ITexture texture)
{ {
return texture is TextureView view && view.Valid; return texture is TextureView { Valid: true };
} }
public ClearRect GetClearRect(Rectangle<int> scissor, int layer, int layerCount) public ClearRect GetClearRect(Rectangle<int> scissor, int layer, int layerCount)
@ -233,9 +376,9 @@ namespace Ryujinx.Graphics.Vulkan
public unsafe Auto<DisposableFramebuffer> Create(Vk api, CommandBufferScoped cbs, Auto<DisposableRenderPass> renderPass) public unsafe Auto<DisposableFramebuffer> Create(Vk api, CommandBufferScoped cbs, Auto<DisposableRenderPass> renderPass)
{ {
ImageView* attachments = stackalloc ImageView[_attachments.Length]; ImageView* attachments = stackalloc ImageView[AttachmentsCount];
for (int i = 0; i < _attachments.Length; i++) for (int i = 0; i < AttachmentsCount; i++)
{ {
attachments[i] = _attachments[i].Get(cbs).Value; attachments[i] = _attachments[i].Get(cbs).Value;
} }
@ -244,7 +387,7 @@ namespace Ryujinx.Graphics.Vulkan
{ {
SType = StructureType.FramebufferCreateInfo, SType = StructureType.FramebufferCreateInfo,
RenderPass = renderPass.Get(cbs).Value, RenderPass = renderPass.Get(cbs).Value,
AttachmentCount = (uint)_attachments.Length, AttachmentCount = (uint)AttachmentsCount,
PAttachments = attachments, PAttachments = attachments,
Width = Width, Width = Width,
Height = Height, Height = Height,
@ -252,14 +395,13 @@ namespace Ryujinx.Graphics.Vulkan
}; };
api.CreateFramebuffer(_device, in framebufferCreateInfo, null, out Framebuffer framebuffer).ThrowOnError(); api.CreateFramebuffer(_device, in framebufferCreateInfo, null, out Framebuffer framebuffer).ThrowOnError();
return new Auto<DisposableFramebuffer>(new DisposableFramebuffer(api, _device, framebuffer), null, _attachments); return new Auto<DisposableFramebuffer>(new DisposableFramebuffer(api, _device, framebuffer), null, _attachments[..AttachmentsCount]);
} }
public TextureView[] GetAttachmentViews() public TextureView[] GetAttachmentViews()
{ {
TextureView[] result = new TextureView[_attachments.Length]; TextureView[] result = new TextureView[AttachmentsCount];
_colors?.AsSpan(..ColorAttachmentsCount).CopyTo(result.AsSpan());
_colors?.CopyTo(result, 0);
if (_depthStencil != null) if (_depthStencil != null)
{ {
@ -278,8 +420,11 @@ namespace Ryujinx.Graphics.Vulkan
{ {
if (_colors != null) if (_colors != null)
{ {
foreach (TextureView color in _colors) int count = ColorAttachmentsCount;
for (int i = 0; i < count; i++)
{ {
TextureView color = _colors[i];
// If Clear or DontCare were used, this would need to be write bit. // If Clear or DontCare were used, this would need to be write bit.
color.Storage?.QueueLoadOpBarrier(cbs, false); color.Storage?.QueueLoadOpBarrier(cbs, false);
} }
@ -294,8 +439,11 @@ namespace Ryujinx.Graphics.Vulkan
{ {
if (_colors != null) if (_colors != null)
{ {
foreach (TextureView color in _colors) int count = ColorAttachmentsCount;
for (int i = 0; i < count; i++)
{ {
TextureView color = _colors[i];
color.Storage?.AddStoreOpUsage(false); color.Storage?.AddStoreOpUsage(false);
} }
} }
@ -307,7 +455,7 @@ namespace Ryujinx.Graphics.Vulkan
{ {
_depthStencil?.Storage.ClearBindings(); _depthStencil?.Storage.ClearBindings();
for (int i = 0; i < _colorsCanonical.Length; i++) for (int i = 0; i < _totalCount; i++)
{ {
_colorsCanonical[i]?.Storage.ClearBindings(); _colorsCanonical[i]?.Storage.ClearBindings();
} }
@ -317,7 +465,7 @@ namespace Ryujinx.Graphics.Vulkan
{ {
_depthStencil?.Storage.AddBinding(_depthStencil); _depthStencil?.Storage.AddBinding(_depthStencil);
for (int i = 0; i < _colorsCanonical.Length; i++) for (int i = 0; i < _totalCount; i++)
{ {
TextureView color = _colorsCanonical[i]; TextureView color = _colorsCanonical[i];
color?.Storage.AddBinding(color); color?.Storage.AddBinding(color);

View file

@ -1,6 +1,7 @@
using Ryujinx.Common.Memory; using Ryujinx.Common.Memory;
using Silk.NET.Vulkan; using Silk.NET.Vulkan;
using System; using System;
using System.Buffers;
namespace Ryujinx.Graphics.Vulkan namespace Ryujinx.Graphics.Vulkan
{ {
@ -11,7 +12,7 @@ namespace Ryujinx.Graphics.Vulkan
{ {
private const int BufferUsageTrackingGranularity = 4096; private const int BufferUsageTrackingGranularity = 4096;
private readonly FenceHolder[] _fences; public FenceHolder[] Fences { get; }
private readonly BufferUsageBitmap _bufferUsageBitmap; private readonly BufferUsageBitmap _bufferUsageBitmap;
/// <summary> /// <summary>
@ -19,7 +20,7 @@ namespace Ryujinx.Graphics.Vulkan
/// </summary> /// </summary>
public MultiFenceHolder() public MultiFenceHolder()
{ {
_fences = new FenceHolder[CommandBufferPool.MaxCommandBuffers]; Fences = ArrayPool<FenceHolder>.Shared.Rent(CommandBufferPool.MaxCommandBuffers);
} }
/// <summary> /// <summary>
@ -28,7 +29,7 @@ namespace Ryujinx.Graphics.Vulkan
/// <param name="size">Size of the buffer</param> /// <param name="size">Size of the buffer</param>
public MultiFenceHolder(int size) public MultiFenceHolder(int size)
{ {
_fences = new FenceHolder[CommandBufferPool.MaxCommandBuffers]; Fences = ArrayPool<FenceHolder>.Shared.Rent(CommandBufferPool.MaxCommandBuffers);
_bufferUsageBitmap = new BufferUsageBitmap(size, BufferUsageTrackingGranularity); _bufferUsageBitmap = new BufferUsageBitmap(size, BufferUsageTrackingGranularity);
} }
@ -90,7 +91,7 @@ namespace Ryujinx.Graphics.Vulkan
/// <returns>True if the command buffer's previous fence value was null</returns> /// <returns>True if the command buffer's previous fence value was null</returns>
public bool AddFence(int cbIndex, FenceHolder fence) public bool AddFence(int cbIndex, FenceHolder fence)
{ {
ref FenceHolder fenceRef = ref _fences[cbIndex]; ref FenceHolder fenceRef = ref Fences[cbIndex];
if (fenceRef == null) if (fenceRef == null)
{ {
@ -107,7 +108,7 @@ namespace Ryujinx.Graphics.Vulkan
/// <param name="cbIndex">Command buffer index of the command buffer that owns the fence</param> /// <param name="cbIndex">Command buffer index of the command buffer that owns the fence</param>
public void RemoveFence(int cbIndex) public void RemoveFence(int cbIndex)
{ {
_fences[cbIndex] = null; Fences[cbIndex] = null;
} }
/// <summary> /// <summary>
@ -117,7 +118,7 @@ namespace Ryujinx.Graphics.Vulkan
/// <returns>True if referenced, false otherwise</returns> /// <returns>True if referenced, false otherwise</returns>
public bool HasFence(int cbIndex) public bool HasFence(int cbIndex)
{ {
return _fences[cbIndex] != null; return Fences[cbIndex] != null;
} }
/// <summary> /// <summary>
@ -227,9 +228,9 @@ namespace Ryujinx.Graphics.Vulkan
{ {
int count = 0; int count = 0;
for (int i = 0; i < _fences.Length; i++) for (int i = 0; i < Fences.Length; i++)
{ {
FenceHolder fence = _fences[i]; FenceHolder fence = Fences[i];
if (fence != null) if (fence != null)
{ {
@ -251,9 +252,9 @@ namespace Ryujinx.Graphics.Vulkan
{ {
int count = 0; int count = 0;
for (int i = 0; i < _fences.Length; i++) for (int i = 0; i < Fences.Length; i++)
{ {
FenceHolder fence = _fences[i]; FenceHolder fence = Fences[i];
if (fence != null && _bufferUsageBitmap.OverlapsWith(i, offset, size)) if (fence != null && _bufferUsageBitmap.OverlapsWith(i, offset, size))
{ {

View file

@ -1453,7 +1453,7 @@ namespace Ryujinx.Graphics.Vulkan
FramebufferParams?.ClearBindings(); FramebufferParams?.ClearBindings();
} }
FramebufferParams = new FramebufferParams(Device, colors, depthStencil); FramebufferParams = FramebufferParams?.Update(colors, depthStencil) ?? new FramebufferParams(Device, colors, depthStencil);
if (IsMainPipeline) if (IsMainPipeline)
{ {
@ -1471,18 +1471,18 @@ namespace Ryujinx.Graphics.Vulkan
protected void UpdatePipelineAttachmentFormats() protected void UpdatePipelineAttachmentFormats()
{ {
Span<Silk.NET.Vulkan.Format> dstAttachmentFormats = _newState.Internal.AttachmentFormats.AsSpan(); Span<Silk.NET.Vulkan.Format> dstAttachmentFormats = _newState.Internal.AttachmentFormats.AsSpan();
FramebufferParams.AttachmentFormats.CopyTo(dstAttachmentFormats); FramebufferParams.AttachmentFormats.AsSpan(..FramebufferParams.AttachmentsCount).CopyTo(dstAttachmentFormats);
_newState.Internal.AttachmentIntegerFormatMask = FramebufferParams.AttachmentIntegerFormatMask; _newState.Internal.AttachmentIntegerFormatMask = FramebufferParams.AttachmentIntegerFormatMask;
_newState.Internal.LogicOpsAllowed = FramebufferParams.LogicOpsAllowed; _newState.Internal.LogicOpsAllowed = FramebufferParams.LogicOpsAllowed;
for (int i = FramebufferParams.AttachmentFormats.Length; i < dstAttachmentFormats.Length; i++) for (int i = FramebufferParams.AttachmentsCount; i < dstAttachmentFormats.Length; i++)
{ {
dstAttachmentFormats[i] = 0; dstAttachmentFormats[i] = 0;
} }
_newState.ColorBlendAttachmentStateCount = (uint)(FramebufferParams.MaxColorAttachmentIndex + 1); _newState.ColorBlendAttachmentStateCount = (uint)(FramebufferParams.MaxColorAttachmentIndex + 1);
_newState.HasDepthStencil = FramebufferParams.HasDepthStencil; _newState.HasDepthStencil = FramebufferParams.HasDepthStencil;
_newState.SamplesCount = FramebufferParams.AttachmentSamples.Length != 0 ? FramebufferParams.AttachmentSamples[0] : 1; _newState.SamplesCount = FramebufferParams.AttachmentsCount != 0 ? FramebufferParams.AttachmentSamples[0] : 1;
} }
protected unsafe void CreateRenderPass() protected unsafe void CreateRenderPass()

View file

@ -178,17 +178,16 @@ namespace Ryujinx.Graphics.Vulkan
{ {
if (_forcedFences.Count > 0) if (_forcedFences.Count > 0)
{ {
_forcedFences.RemoveAll((entry) => for (int i = 0; i < _forcedFences.Count; i++)
{ {
if (entry.Texture.Disposed) if (_forcedFences[i].Texture.Disposed)
{ {
return true; _forcedFences.RemoveAt(i--);
continue;
} }
entry.Texture.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, entry.StageFlags); _forcedFences[i].Texture.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, _forcedFences[i].StageFlags);
}
return false;
});
} }
} }

View file

@ -1,5 +1,6 @@
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Silk.NET.Vulkan; using Silk.NET.Vulkan;
using System.Buffers;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
@ -192,6 +193,7 @@ namespace Ryujinx.Graphics.Vulkan
{ {
_firstHandle = first.ID + 1; _firstHandle = first.ID + 1;
_handles.RemoveAt(0); _handles.RemoveAt(0);
ArrayPool<FenceHolder>.Shared.Return(first.Waitable.Fences);
first.Waitable = null; first.Waitable = null;
} }
} }

View file

@ -1,3 +1,4 @@
using Ryujinx.Common;
using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.HLE.HOS.Kernel.Threading;
using System.Collections.Generic; using System.Collections.Generic;
@ -5,6 +6,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
{ {
class KSynchronizationObject : KAutoObject class KSynchronizationObject : KAutoObject
{ {
private static readonly ObjectPool<LinkedListNode<KThread>> _nodePool = new(() => new LinkedListNode<KThread>(null));
public LinkedList<KThread> WaitingThreads { get; } public LinkedList<KThread> WaitingThreads { get; }
public KSynchronizationObject(KernelContext context) : base(context) public KSynchronizationObject(KernelContext context) : base(context)
@ -14,12 +17,16 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
public LinkedListNode<KThread> AddWaitingThread(KThread thread) public LinkedListNode<KThread> AddWaitingThread(KThread thread)
{ {
return WaitingThreads.AddLast(thread); LinkedListNode<KThread> node = _nodePool.Allocate();
node.Value = thread;
WaitingThreads.AddLast(node);
return node;
} }
public void RemoveWaitingThread(LinkedListNode<KThread> node) public void RemoveWaitingThread(LinkedListNode<KThread> node)
{ {
WaitingThreads.Remove(node); WaitingThreads.Remove(node);
_nodePool.Release(node);
} }
public virtual void Signal() public virtual void Signal()

View file

@ -110,7 +110,22 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
{ {
ulong size = PagesCount * KPageTableBase.PageSize; ulong size = PagesCount * KPageTableBase.PageSize;
return new KMemoryInfo( return KMemoryInfo.Pool.Allocate().Set(
BaseAddress,
size,
State,
Permission,
Attribute,
SourcePermission,
IpcRefCount,
DeviceRefCount);
}
public KMemoryInfo GetInfo(KMemoryInfo oldInfo)
{
ulong size = PagesCount * KPageTableBase.PageSize;
return oldInfo.Set(
BaseAddress, BaseAddress,
size, size,
State, State,

View file

@ -1,19 +1,23 @@
using Ryujinx.Common;
namespace Ryujinx.HLE.HOS.Kernel.Memory namespace Ryujinx.HLE.HOS.Kernel.Memory
{ {
class KMemoryInfo class KMemoryInfo
{ {
public ulong Address { get; } public static readonly ObjectPool<KMemoryInfo> Pool = new(() => new KMemoryInfo());
public ulong Size { get; }
public ulong Address { get; private set; }
public ulong Size { get; private set; }
public MemoryState State { get; } public MemoryState State { get; private set; }
public KMemoryPermission Permission { get; } public KMemoryPermission Permission { get; private set; }
public MemoryAttribute Attribute { get; } public MemoryAttribute Attribute { get;private set; }
public KMemoryPermission SourcePermission { get; } public KMemoryPermission SourcePermission { get; private set; }
public int IpcRefCount { get; } public int IpcRefCount { get; private set; }
public int DeviceRefCount { get; } public int DeviceRefCount { get; private set; }
public KMemoryInfo( public KMemoryInfo Set(
ulong address, ulong address,
ulong size, ulong size,
MemoryState state, MemoryState state,
@ -31,6 +35,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
SourcePermission = sourcePermission; SourcePermission = sourcePermission;
IpcRefCount = ipcRefCount; IpcRefCount = ipcRefCount;
DeviceRefCount = deviceRefCount; DeviceRefCount = deviceRefCount;
return this;
} }
} }
} }

View file

@ -25,17 +25,17 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
{ {
MemoryPermission output = MemoryPermission.None; MemoryPermission output = MemoryPermission.None;
if (permission.HasFlag(KMemoryPermission.Read)) if ((permission & KMemoryPermission.Read) == KMemoryPermission.Read)
{ {
output = MemoryPermission.Read; output = MemoryPermission.Read;
} }
if (permission.HasFlag(KMemoryPermission.Write)) if ((permission & KMemoryPermission.Write) == KMemoryPermission.Write)
{ {
output |= MemoryPermission.Write; output |= MemoryPermission.Write;
} }
if (permission.HasFlag(KMemoryPermission.Execute)) if ((permission & KMemoryPermission.Execute) == KMemoryPermission.Execute)
{ {
output |= MemoryPermission.Execute; output |= MemoryPermission.Execute;
} }

View file

@ -998,7 +998,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
} }
else else
{ {
return new KMemoryInfo( return KMemoryInfo.Pool.Allocate().Set(
AddrSpaceEnd, AddrSpaceEnd,
~AddrSpaceEnd + 1, ~AddrSpaceEnd + 1,
MemoryState.Reserved, MemoryState.Reserved,
@ -2544,10 +2544,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
KMemoryPermission firstPermission = info.Permission; KMemoryPermission firstPermission = info.Permission;
MemoryAttribute firstAttribute = info.Attribute; MemoryAttribute firstAttribute = info.Attribute;
do info = currBlock.GetInfo(info);
while (info.Address + info.Size - 1 < endAddr - 1 && (currBlock = currBlock.Successor) != null)
{ {
info = currBlock.GetInfo();
// Check if the block state matches what we expect. // Check if the block state matches what we expect.
if (firstState != info.State || if (firstState != info.State ||
firstPermission != info.Permission || firstPermission != info.Permission ||
@ -2559,11 +2559,16 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
outState = MemoryState.Unmapped; outState = MemoryState.Unmapped;
outPermission = KMemoryPermission.None; outPermission = KMemoryPermission.None;
outAttribute = MemoryAttribute.None; outAttribute = MemoryAttribute.None;
KMemoryInfo.Pool.Release(info);
return false; return false;
} }
info = currBlock.GetInfo(info);
} }
while (info.Address + info.Size - 1 < endAddr - 1 && (currBlock = currBlock.Successor) != null);
KMemoryInfo.Pool.Release(info);
outState = firstState; outState = firstState;
outPermission = firstPermission; outPermission = firstPermission;
@ -2582,16 +2587,26 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
MemoryAttribute attributeMask, MemoryAttribute attributeMask,
MemoryAttribute attributeExpected) MemoryAttribute attributeExpected)
{ {
foreach (KMemoryInfo info in IterateOverRange(address, address + size)) KMemoryBlock currBlock = _blockManager.FindBlock(address);
KMemoryInfo info = currBlock.GetInfo();
while (info.Address + info.Size - 1 < address + size - 1 && (currBlock = currBlock.Successor) != null)
{ {
// Check if the block state matches what we expect. // Check if the block state matches what we expect.
if ((info.State & stateMask) != stateExpected || if ((info.State & stateMask) != stateExpected ||
(info.Permission & permissionMask) != permissionExpected || (info.Permission & permissionMask) != permissionExpected ||
(info.Attribute & attributeMask) != attributeExpected) (info.Attribute & attributeMask) != attributeExpected)
{ {
KMemoryInfo.Pool.Release(info);
return false; return false;
} }
info = currBlock.GetInfo(info);
} }
KMemoryInfo.Pool.Release(info);
return true; return true;
} }
@ -2641,6 +2656,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
ulong currBaseAddr = info.Address + reservedPagesCount * PageSize; ulong currBaseAddr = info.Address + reservedPagesCount * PageSize;
ulong currEndAddr = info.Address + info.Size; ulong currEndAddr = info.Address + info.Size;
KMemoryInfo.Pool.Release(info);
if (aslrAddress >= regionStart && if (aslrAddress >= regionStart &&
aslrAddress >= currBaseAddr && aslrAddress >= currBaseAddr &&
@ -2721,6 +2738,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
allocationEndAddr <= regionEndAddr && allocationEndAddr <= regionEndAddr &&
allocationEndAddr <= currEndAddr) allocationEndAddr <= currEndAddr)
{ {
KMemoryInfo.Pool.Release(info);
return address; return address;
} }
} }
@ -2731,9 +2749,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
{ {
break; break;
} }
info = currBlock.GetInfo(); info = currBlock.GetInfo(info);
} }
KMemoryInfo.Pool.Release(info);
return 0; return 0;
} }

View file

@ -386,6 +386,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
} }
ulong rwdataStart = roInfo.Address + roInfo.Size; ulong rwdataStart = roInfo.Address + roInfo.Size;
KMemoryInfo.Pool.Release(roInfo);
try try
{ {

View file

@ -1,3 +1,4 @@
using Ryujinx.Common;
using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Common;
using Ryujinx.HLE.HOS.Kernel.Process; using Ryujinx.HLE.HOS.Kernel.Process;
using Ryujinx.Horizon.Common; using Ryujinx.Horizon.Common;
@ -11,6 +12,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
class KAddressArbiter class KAddressArbiter
{ {
private const int HasListenersMask = 0x40000000; private const int HasListenersMask = 0x40000000;
private static readonly ObjectPool<KThread[]> _threadArrayPool = new(() => []);
private readonly KernelContext _context; private readonly KernelContext _context;
@ -198,9 +200,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
{ {
_context.CriticalSection.Enter(); _context.CriticalSection.Enter();
WakeThreads(_condVarThreads, count, TryAcquireMutex, x => x.CondVarAddress == address); static bool SignalProcessWideKeyPredicate(KThread thread, ulong address)
{
return thread.CondVarAddress == address;
}
if (!_condVarThreads.Any(x => x.CondVarAddress == address)) int validThreads = WakeThreads(_condVarThreads, count, TryAcquireMutex, SignalProcessWideKeyPredicate, address);
if (validThreads == 0)
{ {
KernelTransfer.KernelToUser(address, 0); KernelTransfer.KernelToUser(address, 0);
} }
@ -480,9 +487,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
// or negative. It is incremented if there are no threads waiting. // or negative. It is incremented if there are no threads waiting.
int waitingCount = 0; int waitingCount = 0;
foreach (KThread thread in _arbiterThreads.Where(x => x.MutexAddress == address)) foreach (KThread thread in _arbiterThreads)
{ {
if (++waitingCount >= count) if (thread.MutexAddress == address &&
++waitingCount >= count)
{ {
break; break;
} }
@ -553,23 +561,55 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
thread.WaitingInArbitration = false; thread.WaitingInArbitration = false;
} }
WakeThreads(_arbiterThreads, count, RemoveArbiterThread, x => x.MutexAddress == address); static bool ArbiterThreadPredecate(KThread thread, ulong address)
{
return thread.MutexAddress == address;
}
WakeThreads(_arbiterThreads, count, RemoveArbiterThread, ArbiterThreadPredecate, address);
} }
private static void WakeThreads( private static int WakeThreads(
List<KThread> threads, List<KThread> threads,
int count, int count,
Action<KThread> removeCallback, Action<KThread> removeCallback,
Func<KThread, bool> predicate) Func<KThread, ulong, bool> predicate,
ulong address = 0)
{ {
IOrderedEnumerable<KThread> candidates = threads.Where(predicate).OrderBy(x => x.DynamicPriority); KThread[] candidates = _threadArrayPool.Allocate();
KThread[] toSignal = (count > 0 ? candidates.Take(count) : candidates).ToArray(); 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<KThread> candidatesSpan = candidates.AsSpan(..validCount);
candidatesSpan.Sort((x, y) => (x.DynamicPriority.CompareTo(y.DynamicPriority)));
foreach (KThread thread in toSignal) if (count > 0)
{
candidatesSpan = candidatesSpan[..Math.Min(count, candidatesSpan.Length)];
}
foreach (KThread thread in candidatesSpan)
{ {
removeCallback(thread); removeCallback(thread);
threads.Remove(thread); threads.Remove(thread);
} }
_threadArrayPool.Release(candidates);
return validCount;
} }
} }
} }

View file

@ -48,8 +48,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
public KThreadContext ThreadContext { get; private set; } public KThreadContext ThreadContext { get; private set; }
public int DynamicPriority { get; set; } public int DynamicPriority { get; private set; }
public ulong AffinityMask { get; set; } public ulong AffinityMask { get; private set; }
public ulong ThreadUid { get; private set; } public ulong ThreadUid { get; private set; }
@ -83,18 +83,18 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
public long LastScheduledTime { get; set; } public long LastScheduledTime { get; set; }
public LinkedListNode<KThread>[] SiblingsPerCore { get; private set; } public readonly LinkedListNode<KThread>[] SiblingsPerCore;
public LinkedList<KThread> Withholder { get; set; } public LinkedList<KThread> Withholder { get; set; }
public LinkedListNode<KThread> WithholderNode { get; set; } public readonly LinkedListNode<KThread> WithholderNode;
public LinkedListNode<KThread> ProcessListNode { get; set; } public readonly LinkedListNode<KThread> ProcessListNode;
private readonly LinkedList<KThread> _mutexWaiters; private readonly LinkedList<KThread> _mutexWaiters;
private LinkedListNode<KThread> _mutexWaiterNode; private readonly LinkedListNode<KThread> _mutexWaiterNode;
private readonly LinkedList<KThread> _pinnedWaiters; private readonly LinkedList<KThread> _pinnedWaiters;
private LinkedListNode<KThread> _pinnedWaiterNode; private readonly LinkedListNode<KThread> _pinnedWaiterNode;
public KThread MutexOwner { get; private set; } public KThread MutexOwner { get; private set; }
@ -1070,11 +1070,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
if (nextPrio != null) if (nextPrio != null)
{ {
thread._mutexWaiterNode = _mutexWaiters.AddBefore(nextPrio, thread); _mutexWaiters.AddBefore(nextPrio, thread._mutexWaiterNode);
} }
else else
{ {
thread._mutexWaiterNode = _mutexWaiters.AddLast(thread); _mutexWaiters.AddLast(thread._mutexWaiterNode);
} }
} }

View file

@ -14,6 +14,7 @@ using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap;
using Ryujinx.HLE.HOS.Services.Nv.Types; using Ryujinx.HLE.HOS.Services.Nv.Types;
using Ryujinx.Memory; using Ryujinx.Memory;
using System; using System;
using System.Buffers;
using System.Collections.Generic; using System.Collections.Generic;
using System.Reflection; using System.Reflection;
@ -46,6 +47,8 @@ namespace Ryujinx.HLE.HOS.Services.Nv
{ "/dev/nvhost-dbg-gpu", typeof(NvHostDbgGpuDeviceFile) }, { "/dev/nvhost-dbg-gpu", typeof(NvHostDbgGpuDeviceFile) },
{ "/dev/nvhost-prof-gpu", typeof(NvHostProfGpuDeviceFile) }, { "/dev/nvhost-prof-gpu", typeof(NvHostProfGpuDeviceFile) },
}; };
private static readonly ArrayPool<byte> _byteArrayPool = ArrayPool<byte>.Create();
public static IdDictionary DeviceFileIdRegistry = new(); public static IdDictionary DeviceFileIdRegistry = new();
@ -471,10 +474,13 @@ namespace Ryujinx.HLE.HOS.Services.Nv
errorCode = GetIoctlArgument(context, ioctlCommand, out Span<byte> arguments); errorCode = GetIoctlArgument(context, ioctlCommand, out Span<byte> arguments);
if (!context.Memory.TryReadUnsafe(inlineInBufferPosition, (int)inlineInBufferSize, out Span<byte> inlineInBuffer)) byte[] inlineInBuffer = null;
if (!context.Memory.TryReadUnsafe(inlineInBufferPosition, (int)inlineInBufferSize, out Span<byte> inlineInBufferSpan))
{ {
inlineInBuffer = new byte[inlineInBufferSize]; inlineInBuffer = _byteArrayPool.Rent((int)inlineInBufferSize);
context.Memory.Read(inlineInBufferPosition, inlineInBuffer); inlineInBufferSpan = inlineInBuffer;
context.Memory.Read(inlineInBufferPosition, inlineInBufferSpan[..(int)inlineInBufferSize]);
} }
if (errorCode == NvResult.Success) if (errorCode == NvResult.Success)
@ -483,7 +489,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv
if (errorCode == NvResult.Success) if (errorCode == NvResult.Success)
{ {
NvInternalResult internalResult = deviceFile.Ioctl2(ioctlCommand, arguments, inlineInBuffer); NvInternalResult internalResult = deviceFile.Ioctl2(ioctlCommand, arguments, inlineInBufferSpan[..(int)inlineInBufferSize]);
if (internalResult == NvInternalResult.NotImplemented) if (internalResult == NvInternalResult.NotImplemented)
{ {
@ -498,6 +504,11 @@ namespace Ryujinx.HLE.HOS.Services.Nv
} }
} }
} }
if (inlineInBuffer is not null)
{
_byteArrayPool.Return(inlineInBuffer);
}
} }
context.ResponseData.Write((uint)errorCode); context.ResponseData.Write((uint)errorCode);
@ -520,10 +531,13 @@ namespace Ryujinx.HLE.HOS.Services.Nv
errorCode = GetIoctlArgument(context, ioctlCommand, out Span<byte> arguments); errorCode = GetIoctlArgument(context, ioctlCommand, out Span<byte> arguments);
if (!context.Memory.TryReadUnsafe(inlineOutBufferPosition, (int)inlineOutBufferSize, out Span<byte> inlineOutBuffer)) byte[] inlineOutBuffer = null;
if (!context.Memory.TryReadUnsafe(inlineOutBufferPosition, (int)inlineOutBufferSize, out Span<byte> inlineOutBufferSpan))
{ {
inlineOutBuffer = new byte[inlineOutBufferSize]; inlineOutBuffer = _byteArrayPool.Rent((int)inlineOutBufferSize);
context.Memory.Read(inlineOutBufferPosition, inlineOutBuffer); inlineOutBufferSpan = inlineOutBuffer;
context.Memory.Read(inlineOutBufferPosition, inlineOutBufferSpan[..(int)inlineOutBufferSize]);
} }
if (errorCode == NvResult.Success) if (errorCode == NvResult.Success)
@ -532,7 +546,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv
if (errorCode == NvResult.Success) if (errorCode == NvResult.Success)
{ {
NvInternalResult internalResult = deviceFile.Ioctl3(ioctlCommand, arguments, inlineOutBuffer); NvInternalResult internalResult = deviceFile.Ioctl3(ioctlCommand, arguments, inlineOutBufferSpan[..(int)inlineOutBufferSize]);
if (internalResult == NvInternalResult.NotImplemented) if (internalResult == NvInternalResult.NotImplemented)
{ {
@ -544,10 +558,15 @@ namespace Ryujinx.HLE.HOS.Services.Nv
if ((ioctlCommand.DirectionValue & NvIoctl.Direction.Write) != 0) 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.ToArray());
context.Memory.Write(inlineOutBufferPosition, inlineOutBuffer.ToArray()); context.Memory.Write(inlineOutBufferPosition, inlineOutBufferSpan[..(int)inlineOutBufferSize].ToArray());
} }
} }
} }
if (inlineOutBuffer is not null)
{
_byteArrayPool.Return(inlineOutBuffer);
}
} }
context.ResponseData.Write((uint)errorCode); context.ResponseData.Write((uint)errorCode);

View file

@ -48,36 +48,36 @@ namespace Ryujinx.Horizon.Sdk.Sf
case CommandArgType.Buffer: case CommandArgType.Buffer:
HipcBufferFlags flags = argInfo.BufferFlags; HipcBufferFlags flags = argInfo.BufferFlags;
if (flags.HasFlag(HipcBufferFlags.In)) if ((flags & HipcBufferFlags.In) != 0)
{ {
if (flags.HasFlag(HipcBufferFlags.AutoSelect)) if ((flags & HipcBufferFlags.AutoSelect) != 0)
{ {
_inMapAliasBuffersCount++; _inMapAliasBuffersCount++;
_inPointerBuffersCount++; _inPointerBuffersCount++;
} }
else if (flags.HasFlag(HipcBufferFlags.MapAlias)) else if ((flags & HipcBufferFlags.MapAlias) != 0)
{ {
_inMapAliasBuffersCount++; _inMapAliasBuffersCount++;
} }
else if (flags.HasFlag(HipcBufferFlags.Pointer)) else if ((flags & HipcBufferFlags.Pointer) != 0)
{ {
_inPointerBuffersCount++; _inPointerBuffersCount++;
} }
} }
else else
{ {
bool autoSelect = flags.HasFlag(HipcBufferFlags.AutoSelect); bool autoSelect = (flags & HipcBufferFlags.AutoSelect) != 0;
if (autoSelect || flags.HasFlag(HipcBufferFlags.Pointer)) if (autoSelect || (flags & HipcBufferFlags.Pointer) != 0)
{ {
_outPointerBuffersCount++; _outPointerBuffersCount++;
if (flags.HasFlag(HipcBufferFlags.FixedSize)) if ((flags & HipcBufferFlags.FixedSize) != 0)
{ {
_outFixedSizePointerBuffersCount++; _outFixedSizePointerBuffersCount++;
} }
} }
if (autoSelect || flags.HasFlag(HipcBufferFlags.MapAlias)) if (autoSelect || (flags & HipcBufferFlags.MapAlias) != 0)
{ {
_outMapAliasBuffersCount++; _outMapAliasBuffersCount++;
} }
@ -150,17 +150,17 @@ namespace Ryujinx.Horizon.Sdk.Sf
HipcBufferFlags flags = _args[i].BufferFlags; HipcBufferFlags flags = _args[i].BufferFlags;
bool isMapAlias; bool isMapAlias;
if (flags.HasFlag(HipcBufferFlags.MapAlias)) if ((flags & HipcBufferFlags.MapAlias) != 0)
{ {
isMapAlias = true; isMapAlias = true;
} }
else if (flags.HasFlag(HipcBufferFlags.Pointer)) else if ((flags & HipcBufferFlags.Pointer) != 0)
{ {
isMapAlias = false; isMapAlias = false;
} }
else /* if (flags.HasFlag(HipcBufferFlags.HipcAutoSelect)) */ else /* if (flags & HipcBufferFlags.HipcAutoSelect)) */
{ {
HipcBufferDescriptor descriptor = flags.HasFlag(HipcBufferFlags.In) HipcBufferDescriptor descriptor = (flags & HipcBufferFlags.In) != 0
? context.Request.Data.SendBuffers[sendMapAliasIndex] ? context.Request.Data.SendBuffers[sendMapAliasIndex]
: context.Request.Data.ReceiveBuffers[recvMapAliasIndex]; : context.Request.Data.ReceiveBuffers[recvMapAliasIndex];
@ -171,7 +171,7 @@ namespace Ryujinx.Horizon.Sdk.Sf
if (isMapAlias) if (isMapAlias)
{ {
HipcBufferDescriptor descriptor = flags.HasFlag(HipcBufferFlags.In) HipcBufferDescriptor descriptor = (flags & HipcBufferFlags.In) != 0
? context.Request.Data.SendBuffers[sendMapAliasIndex++] ? context.Request.Data.SendBuffers[sendMapAliasIndex++]
: context.Request.Data.ReceiveBuffers[recvMapAliasIndex++]; : context.Request.Data.ReceiveBuffers[recvMapAliasIndex++];
@ -184,7 +184,7 @@ namespace Ryujinx.Horizon.Sdk.Sf
} }
else else
{ {
if (flags.HasFlag(HipcBufferFlags.In)) if ((flags & HipcBufferFlags.In) != 0)
{ {
HipcStaticDescriptor descriptor = context.Request.Data.SendStatics[sendPointerIndex++]; HipcStaticDescriptor descriptor = context.Request.Data.SendStatics[sendPointerIndex++];
ulong address = descriptor.Address; ulong address = descriptor.Address;
@ -197,11 +197,11 @@ namespace Ryujinx.Horizon.Sdk.Sf
pointerBufferTail = Math.Max(pointerBufferTail, address + size); pointerBufferTail = Math.Max(pointerBufferTail, address + size);
} }
} }
else /* if (flags.HasFlag(HipcBufferFlags.Out)) */ else /* if (flags & HipcBufferFlags.Out)) */
{ {
ulong size; ulong size;
if (flags.HasFlag(HipcBufferFlags.FixedSize)) if ((flags & HipcBufferFlags.FixedSize) != 0)
{ {
size = _args[i].BufferFixedSize; size = _args[i].BufferFixedSize;
} }
@ -234,12 +234,12 @@ namespace Ryujinx.Horizon.Sdk.Sf
private static bool IsMapTransferModeValid(HipcBufferFlags flags, HipcBufferMode mode) private static bool IsMapTransferModeValid(HipcBufferFlags flags, HipcBufferMode mode)
{ {
if (flags.HasFlag(HipcBufferFlags.MapTransferAllowsNonSecure)) if ((flags & HipcBufferFlags.MapTransferAllowsNonSecure) != 0)
{ {
return mode == HipcBufferMode.NonSecure; return mode == HipcBufferMode.NonSecure;
} }
if (flags.HasFlag(HipcBufferFlags.MapTransferAllowsNonDevice)) if ((flags & HipcBufferFlags.MapTransferAllowsNonDevice) != 0)
{ {
return mode == HipcBufferMode.NonDevice; return mode == HipcBufferMode.NonDevice;
} }
@ -259,18 +259,18 @@ namespace Ryujinx.Horizon.Sdk.Sf
} }
HipcBufferFlags flags = _args[i].BufferFlags; HipcBufferFlags flags = _args[i].BufferFlags;
if (!flags.HasFlag(HipcBufferFlags.Out)) if ((flags & HipcBufferFlags.Out) == 0)
{ {
continue; continue;
} }
PointerAndSize buffer = _bufferRanges[i]; PointerAndSize buffer = _bufferRanges[i];
if (flags.HasFlag(HipcBufferFlags.Pointer)) if ((flags & HipcBufferFlags.Pointer) != 0)
{ {
response.SendStatics[recvPointerIndex] = new HipcStaticDescriptor(buffer.Address, (ushort)buffer.Size, recvPointerIndex); response.SendStatics[recvPointerIndex] = new HipcStaticDescriptor(buffer.Address, (ushort)buffer.Size, recvPointerIndex);
} }
else if (flags.HasFlag(HipcBufferFlags.AutoSelect)) else if ((flags & HipcBufferFlags.AutoSelect) != 0)
{ {
if (!isBufferMapAlias[i]) if (!isBufferMapAlias[i])
{ {

View file

@ -88,7 +88,7 @@ namespace Ryujinx.Input.SDL2
_triggerThreshold = 0.0f; _triggerThreshold = 0.0f;
// Enable motion tracking // Enable motion tracking
if (Features.HasFlag(GamepadFeaturesFlag.Motion)) if ((Features & GamepadFeaturesFlag.Motion) != 0)
{ {
if (SDL_GameControllerSetSensorEnabled(_gamepadHandle, SDL_SensorType.SDL_SENSOR_ACCEL, SDL_bool.SDL_TRUE) != 0) if (SDL_GameControllerSetSensorEnabled(_gamepadHandle, SDL_SensorType.SDL_SENSOR_ACCEL, SDL_bool.SDL_TRUE) != 0)
{ {
@ -104,7 +104,7 @@ namespace Ryujinx.Input.SDL2
public void SetLed(uint packedRgb) public void SetLed(uint packedRgb)
{ {
if (!Features.HasFlag(GamepadFeaturesFlag.Led)) if ((Features & GamepadFeaturesFlag.Led) == 0)
return; return;
byte red = packedRgb > 0 ? (byte)(packedRgb >> 16) : (byte)0; byte red = packedRgb > 0 ? (byte)(packedRgb >> 16) : (byte)0;
@ -166,7 +166,7 @@ namespace Ryujinx.Input.SDL2
public void Rumble(float lowFrequency, float highFrequency, uint durationMs) public void Rumble(float lowFrequency, float highFrequency, uint durationMs)
{ {
if (!Features.HasFlag(GamepadFeaturesFlag.Rumble)) if ((Features & GamepadFeaturesFlag.Rumble) == 0)
return; return;
ushort lowFrequencyRaw = (ushort)(lowFrequency * ushort.MaxValue); ushort lowFrequencyRaw = (ushort)(lowFrequency * ushort.MaxValue);
@ -197,7 +197,7 @@ namespace Ryujinx.Input.SDL2
_ => SDL_SensorType.SDL_SENSOR_INVALID _ => SDL_SensorType.SDL_SENSOR_INVALID
}; };
if (!Features.HasFlag(GamepadFeaturesFlag.Motion) || sensorType is SDL_SensorType.SDL_SENSOR_INVALID) if ((Features & GamepadFeaturesFlag.Motion) == 0 || sensorType is SDL_SensorType.SDL_SENSOR_INVALID)
return Vector3.Zero; return Vector3.Zero;
const int ElementCount = 3; const int ElementCount = 3;
@ -232,7 +232,7 @@ namespace Ryujinx.Input.SDL2
{ {
_configuration = (StandardControllerInputConfig)configuration; _configuration = (StandardControllerInputConfig)configuration;
if (Features.HasFlag(GamepadFeaturesFlag.Led) && _configuration.Led.EnableLed) if ((Features & GamepadFeaturesFlag.Led) != 0 && _configuration.Led.EnableLed)
{ {
if (_configuration.Led.TurnOffLed) if (_configuration.Led.TurnOffLed)
(this as IGamepad).ClearLed(); (this as IGamepad).ClearLed();

View file

@ -82,7 +82,7 @@ namespace Ryujinx.Input.SDL2
Features = GetFeaturesFlag(); Features = GetFeaturesFlag();
// Enable motion tracking // Enable motion tracking
if (Features.HasFlag(GamepadFeaturesFlag.Motion)) if ((Features & GamepadFeaturesFlag.Motion) != 0)
{ {
if (SDL_GameControllerSetSensorEnabled(_gamepadHandle, SDL_SensorType.SDL_SENSOR_ACCEL, if (SDL_GameControllerSetSensorEnabled(_gamepadHandle, SDL_SensorType.SDL_SENSOR_ACCEL,
SDL_bool.SDL_TRUE) != 0) SDL_bool.SDL_TRUE) != 0)
@ -162,7 +162,7 @@ namespace Ryujinx.Input.SDL2
public void Rumble(float lowFrequency, float highFrequency, uint durationMs) public void Rumble(float lowFrequency, float highFrequency, uint durationMs)
{ {
if (!Features.HasFlag(GamepadFeaturesFlag.Rumble)) if ((Features & GamepadFeaturesFlag.Rumble) == 0)
return; return;
ushort lowFrequencyRaw = (ushort)(lowFrequency * ushort.MaxValue); ushort lowFrequencyRaw = (ushort)(lowFrequency * ushort.MaxValue);
@ -194,7 +194,7 @@ namespace Ryujinx.Input.SDL2
_ => SDL_SensorType.SDL_SENSOR_INVALID _ => SDL_SensorType.SDL_SENSOR_INVALID
}; };
if (!Features.HasFlag(GamepadFeaturesFlag.Motion) || sensorType is SDL_SensorType.SDL_SENSOR_INVALID) if ((Features & GamepadFeaturesFlag.Motion) == 0 || sensorType is SDL_SensorType.SDL_SENSOR_INVALID)
return Vector3.Zero; return Vector3.Zero;
const int ElementCount = 3; const int ElementCount = 3;

View file

@ -5,6 +5,7 @@ using Ryujinx.Common.Configuration.Hid.Controller.Motion;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Services.Hid; using Ryujinx.HLE.HOS.Services.Hid;
using System; using System;
using System.Buffers;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -291,7 +292,7 @@ namespace Ryujinx.Input.HLE
{ {
if (controllerConfig.Motion.MotionBackend == MotionInputBackendType.GamepadDriver) if (controllerConfig.Motion.MotionBackend == MotionInputBackendType.GamepadDriver)
{ {
if (gamepad.Features.HasFlag(GamepadFeaturesFlag.Motion)) if ((gamepad.Features & GamepadFeaturesFlag.Motion) != 0)
{ {
Vector3 accelerometer = gamepad.GetMotionData(MotionInputId.Accelerometer); Vector3 accelerometer = gamepad.GetMotionData(MotionInputId.Accelerometer);
Vector3 gyroscope = gamepad.GetMotionData(MotionInputId.Gyroscope); Vector3 gyroscope = gamepad.GetMotionData(MotionInputId.Gyroscope);
@ -531,6 +532,8 @@ namespace Ryujinx.Input.HLE
hidKeyboard.Modifier |= value << entry.Target; hidKeyboard.Modifier |= value << entry.Target;
} }
ArrayPool<bool>.Shared.Return(keyboardState.KeysState);
return hidKeyboard; return hidKeyboard;

View file

@ -1,8 +1,10 @@
using Ryujinx.Common;
using Ryujinx.Common.Configuration.Hid; using Ryujinx.Common.Configuration.Hid;
using Ryujinx.Common.Configuration.Hid.Controller; using Ryujinx.Common.Configuration.Hid.Controller;
using Ryujinx.Common.Configuration.Hid.Keyboard; using Ryujinx.Common.Configuration.Hid.Keyboard;
using Ryujinx.HLE.HOS.Services.Hid; using Ryujinx.HLE.HOS.Services.Hid;
using System; using System;
using System.Buffers;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
@ -18,6 +20,7 @@ namespace Ryujinx.Input.HLE
{ {
public class NpadManager : IDisposable public class NpadManager : IDisposable
{ {
private static readonly ObjectPool<List<SixAxisInput>> _hleMotionStatesPool = new (() => new List<SixAxisInput>(NpadDevices.MaxControllers));
private readonly CemuHookClient _cemuHookClient; private readonly CemuHookClient _cemuHookClient;
private readonly Lock _lock = new(); private readonly Lock _lock = new();
@ -215,7 +218,7 @@ namespace Ryujinx.Input.HLE
lock (_lock) lock (_lock)
{ {
List<GamepadInput> hleInputStates = []; List<GamepadInput> hleInputStates = [];
List<SixAxisInput> hleMotionStates = new(NpadDevices.MaxControllers); List<SixAxisInput> hleMotionStates = _hleMotionStatesPool.Allocate();
KeyboardInput? hleKeyboardInput = null; KeyboardInput? hleKeyboardInput = null;
@ -317,6 +320,8 @@ namespace Ryujinx.Input.HLE
Vector2 position = IMouse.GetScreenPosition(mouseInput.Position, mouse.ClientSize, aspectRatio); Vector2 position = IMouse.GetScreenPosition(mouseInput.Position, mouse.ClientSize, aspectRatio);
_device.Hid.Mouse.Update((int)position.X, (int)position.Y, buttons, (int)mouseInput.Scroll.X, (int)mouseInput.Scroll.Y, true); _device.Hid.Mouse.Update((int)position.X, (int)position.Y, buttons, (int)mouseInput.Scroll.X, (int)mouseInput.Scroll.Y, true);
ArrayPool<bool>.Shared.Return(mouseInput.ButtonState);
} }
else else
{ {
@ -324,6 +329,8 @@ namespace Ryujinx.Input.HLE
} }
_device.TamperMachine.UpdateInput(hleInputStates); _device.TamperMachine.UpdateInput(hleInputStates);
_hleMotionStatesPool.Release(hleMotionStates);
} }
} }

View file

@ -1,3 +1,4 @@
using System.Buffers;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace Ryujinx.Input namespace Ryujinx.Input
@ -28,7 +29,8 @@ namespace Ryujinx.Input
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
static KeyboardStateSnapshot GetStateSnapshot(IKeyboard keyboard) static KeyboardStateSnapshot GetStateSnapshot(IKeyboard keyboard)
{ {
bool[] keysState = new bool[(int)Key.Count];
bool[] keysState = ArrayPool<bool>.Shared.Rent((int)Key.Count);
for (Key key = 0; key < Key.Count; key++) for (Key key = 0; key < Key.Count; key++)
{ {

View file

@ -1,3 +1,4 @@
using System.Buffers;
using System.Drawing; using System.Drawing;
using System.Numerics; using System.Numerics;
@ -47,7 +48,7 @@ namespace Ryujinx.Input
/// <returns>A snaphost of the state of the mouse.</returns> /// <returns>A snaphost of the state of the mouse.</returns>
public static MouseStateSnapshot GetMouseStateSnapshot(IMouse mouse) public static MouseStateSnapshot GetMouseStateSnapshot(IMouse mouse)
{ {
bool[] buttons = new bool[(int)MouseButton.Count]; bool[] buttons = ArrayPool<bool>.Shared.Rent((int)MouseButton.Count);
mouse.Buttons.CopyTo(buttons, 0); mouse.Buttons.CopyTo(buttons, 0);

View file

@ -7,7 +7,7 @@ namespace Ryujinx.Input
/// </summary> /// </summary>
public class KeyboardStateSnapshot public class KeyboardStateSnapshot
{ {
private readonly bool[] _keysState; public readonly bool[] KeysState;
/// <summary> /// <summary>
/// Create a new <see cref="KeyboardStateSnapshot"/>. /// Create a new <see cref="KeyboardStateSnapshot"/>.
@ -15,7 +15,7 @@ namespace Ryujinx.Input
/// <param name="keysState">The keys state</param> /// <param name="keysState">The keys state</param>
public KeyboardStateSnapshot(bool[] keysState) public KeyboardStateSnapshot(bool[] keysState)
{ {
_keysState = keysState; KeysState = keysState;
} }
/// <summary> /// <summary>
@ -24,6 +24,6 @@ namespace Ryujinx.Input
/// <param name="key">The key</param> /// <param name="key">The key</param>
/// <returns>True if the given key is pressed</returns> /// <returns>True if the given key is pressed</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsPressed(Key key) => _keysState[(int)key]; public bool IsPressed(Key key) => KeysState[(int)key];
} }
} }

View file

@ -8,7 +8,7 @@ namespace Ryujinx.Input
/// </summary> /// </summary>
public class MouseStateSnapshot public class MouseStateSnapshot
{ {
private readonly bool[] _buttonState; public readonly bool[] ButtonState;
/// <summary> /// <summary>
/// The position of the mouse cursor /// The position of the mouse cursor
@ -28,7 +28,7 @@ namespace Ryujinx.Input
/// <param name="scroll">The scroll delta</param> /// <param name="scroll">The scroll delta</param>
public MouseStateSnapshot(bool[] buttonState, Vector2 position, Vector2 scroll) public MouseStateSnapshot(bool[] buttonState, Vector2 position, Vector2 scroll)
{ {
_buttonState = buttonState; ButtonState = buttonState;
Position = position; Position = position;
Scroll = scroll; Scroll = scroll;
@ -40,6 +40,6 @@ namespace Ryujinx.Input
/// <param name="button">The button</param> /// <param name="button">The button</param>
/// <returns>True if the given button is pressed</returns> /// <returns>True if the given button is pressed</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsPressed(MouseButton button) => _buttonState[(int)button]; public bool IsPressed(MouseButton button) => ButtonState[(int)button];
} }
} }

View file

@ -1,4 +1,5 @@
using System; using System;
using System.Buffers;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -38,7 +39,7 @@ namespace Ryujinx.Memory.Range
index = ~index; index = ~index;
} }
RangeItem<T> rangeItem = new(item); RangeItem<T> rangeItem = _rangeItemPool.Allocate().Set(item);
Insert(index, rangeItem); Insert(index, rangeItem);
} }
@ -144,6 +145,8 @@ namespace Ryujinx.Memory.Range
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private void RemoveAt(int index) private void RemoveAt(int index)
{ {
_rangeItemPool.Release(Items[index]);
if (index < Count - 1) if (index < Count - 1)
{ {
Items[index + 1].Previous = index > 0 ? Items[index - 1] : null; Items[index + 1].Previous = index > 0 ? Items[index - 1] : null;
@ -433,7 +436,7 @@ namespace Ryujinx.Memory.Range
return (Items[index], Items[endIndex - 1]); return (Items[index], Items[endIndex - 1]);
} }
public RangeItem<T>[] FindOverlapsAsArray(ulong address, ulong size) public RangeItem<T>[] FindOverlapsAsArray(ulong address, ulong size, out int length)
{ {
(int index, int endIndex) = BinarySearchEdges(address, address + size); (int index, int endIndex) = BinarySearchEdges(address, address + size);
@ -441,11 +444,13 @@ namespace Ryujinx.Memory.Range
if (index < 0) if (index < 0)
{ {
result = []; result = null;
length = 0;
} }
else else
{ {
result = new RangeItem<T>[endIndex - index]; result = ArrayPool<RangeItem<T>>.Shared.Rent(endIndex - index);
length = endIndex - index;
Array.Copy(Items, index, result, 0, endIndex - index); Array.Copy(Items, index, result, 0, endIndex - index);
} }

View file

@ -36,8 +36,6 @@ namespace Ryujinx.Memory.Range
public class RangeList<T> : RangeListBase<T> where T : IRange public class RangeList<T> : RangeListBase<T> where T : IRange
{ {
public readonly ReaderWriterLockSlim Lock = new(); public readonly ReaderWriterLockSlim Lock = new();
private readonly Dictionary<ulong, RangeItem<T>> _quickAccess = new(AddressEqualityComparer.Comparer);
/// <summary> /// <summary>
/// Creates a new range list. /// Creates a new range list.
@ -93,11 +91,6 @@ namespace Ryujinx.Memory.Range
Items[index + 1].Previous = rangeItem; Items[index + 1].Previous = rangeItem;
} }
foreach (ulong address in Items[index].QuickAccessAddresses)
{
_quickAccess.Remove(address);
}
Items[index] = rangeItem; Items[index] = rangeItem;
return true; return true;
@ -142,11 +135,6 @@ namespace Ryujinx.Memory.Range
Items[index + 1].Previous = rangeItem; Items[index + 1].Previous = rangeItem;
} }
foreach (ulong address in item.QuickAccessAddresses)
{
_quickAccess.Remove(address);
}
Items[index] = rangeItem; Items[index] = rangeItem;
return true; return true;
@ -210,11 +198,6 @@ namespace Ryujinx.Memory.Range
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private void RemoveAt(int index) private void RemoveAt(int index)
{ {
foreach (ulong address in Items[index].QuickAccessAddresses)
{
_quickAccess.Remove(address);
}
if (index < Count - 1) if (index < Count - 1)
{ {
Items[index + 1].Previous = index > 0 ? Items[index - 1] : null; Items[index + 1].Previous = index > 0 ? Items[index - 1] : null;
@ -253,15 +236,6 @@ namespace Ryujinx.Memory.Range
int startIndex = BinarySearch(startItem.Address); int startIndex = BinarySearch(startItem.Address);
int endIndex = BinarySearch(endItem.Address); int endIndex = BinarySearch(endItem.Address);
for (int i = startIndex; i <= endIndex; i++)
{
_quickAccess.Remove(Items[i].Address);
foreach (ulong addr in Items[i].QuickAccessAddresses)
{
_quickAccess.Remove(addr);
}
}
if (endIndex < Count - 1) if (endIndex < Count - 1)
{ {
Items[endIndex + 1].Previous = startIndex > 0 ? Items[startIndex - 1] : null; Items[endIndex + 1].Previous = startIndex > 0 ? Items[startIndex - 1] : null;
@ -349,11 +323,6 @@ namespace Ryujinx.Memory.Range
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public override RangeItem<T> FindOverlapFast(ulong address, ulong size) public override RangeItem<T> FindOverlapFast(ulong address, ulong size)
{ {
if (_quickAccess.TryGetValue(address, out RangeItem<T> quickResult))
{
return quickResult;
}
int index = BinarySearch(address, address + size); int index = BinarySearch(address, address + size);
if (index < 0) if (index < 0)
@ -361,12 +330,6 @@ namespace Ryujinx.Memory.Range
return null; return null;
} }
if (Items[index].OverlapsWith(address, address + 1))
{
_quickAccess.Add(address, Items[index]);
Items[index].QuickAccessAddresses.Add(address);
}
return Items[index]; return Items[index];
} }

View file

@ -1,20 +1,42 @@
using System.Collections; using Ryujinx.Common;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace Ryujinx.Memory.Range namespace Ryujinx.Memory.Range
{ {
public class RangeItem<TValue>(TValue value) where TValue : IRange public class RangeItem<TValue> where TValue : IRange
{ {
public RangeItem<TValue> Next; public RangeItem<TValue> Next;
public RangeItem<TValue> Previous; public RangeItem<TValue> Previous;
public readonly ulong Address = value.Address; public ulong Address;
public readonly ulong EndAddress = value.Address + value.Size; public ulong EndAddress;
public readonly TValue Value = value; public TValue Value;
public RangeItem()
{
}
public RangeItem(TValue value)
{
Address = value.Address;
EndAddress = value.Address + value.Size;
Value = value;
}
public readonly List<ulong> QuickAccessAddresses = []; public RangeItem<TValue> Set(TValue value)
{
Next = null;
Previous = null;
Address = value.Address;
EndAddress = value.Address + value.Size;
Value = value;
return this;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool OverlapsWith(ulong address, ulong endAddress) public bool OverlapsWith(ulong address, ulong endAddress)
@ -23,20 +45,9 @@ namespace Ryujinx.Memory.Range
} }
} }
class AddressEqualityComparer : IEqualityComparer<ulong>
{
public bool Equals(ulong u1, ulong u2)
{
return u1 == u2;
}
public int GetHashCode(ulong value) => (int)(value << 5);
public static readonly AddressEqualityComparer Comparer = new();
}
public unsafe abstract class RangeListBase<T> : IEnumerable<T> where T : IRange public unsafe abstract class RangeListBase<T> : IEnumerable<T> where T : IRange
{ {
protected static readonly ObjectPool<RangeItem<T>> _rangeItemPool = new(() => new RangeItem<T>());
private const int BackingInitialSize = 1024; private const int BackingInitialSize = 1024;
protected RangeItem<T>[] Items; protected RangeItem<T>[] Items;

View file

@ -1,5 +1,6 @@
using Ryujinx.Memory.Range; using Ryujinx.Memory.Range;
using System; using System;
using System.Buffers;
using System.Collections.Generic; using System.Collections.Generic;
namespace Ryujinx.Memory.Tracking namespace Ryujinx.Memory.Tracking
@ -300,10 +301,10 @@ namespace Ryujinx.Memory.Tracking
// We use the non-span method here because keeping the lock will cause a deadlock. // We use the non-span method here because keeping the lock will cause a deadlock.
regions.Lock.EnterReadLock(); regions.Lock.EnterReadLock();
RangeItem<VirtualRegion>[] overlaps = regions.FindOverlapsAsArray(address, size); RangeItem<VirtualRegion>[] overlaps = regions.FindOverlapsAsArray(address, size, out int length);
regions.Lock.ExitReadLock(); regions.Lock.ExitReadLock();
if (overlaps.Length == 0 && !precise) if (length == 0 && !precise)
{ {
if (_memoryManager.IsRangeMapped(address, size)) if (_memoryManager.IsRangeMapped(address, size))
{ {
@ -323,8 +324,8 @@ namespace Ryujinx.Memory.Tracking
// Increase the access size to trigger handles with misaligned accesses. // Increase the access size to trigger handles with misaligned accesses.
size += (ulong)_pageSize; size += (ulong)_pageSize;
} }
for (int i = 0; i < overlaps.Length; i++) for (int i = 0; i < length; i++)
{ {
VirtualRegion region = overlaps[i].Value; VirtualRegion region = overlaps[i].Value;
@ -337,6 +338,11 @@ namespace Ryujinx.Memory.Tracking
region.Signal(address, size, write, exemptId); region.Signal(address, size, write, exemptId);
} }
} }
if (length != 0)
{
ArrayPool<RangeItem<VirtualRegion>>.Shared.Return(overlaps);
}
} }
} }

View file

@ -95,7 +95,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
public bool IsRight { get; set; } public bool IsRight { get; set; }
public bool IsLeft { get; set; } public bool IsLeft { get; set; }
public string RevertDeviceId { get; set; } public string RevertDeviceId { get; set; }
public bool HasLed => SelectedGamepad.Features.HasFlag(GamepadFeaturesFlag.Led); public bool HasLed => (SelectedGamepad.Features & GamepadFeaturesFlag.Led) != 0;
public bool CanClearLed => SelectedGamepad.Name.ContainsIgnoreCase("DualSense"); public bool CanClearLed => SelectedGamepad.Name.ContainsIgnoreCase("DualSense");
public event Action NotifyChangesEvent; public event Action NotifyChangesEvent;