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
{
if (flags.HasFlag(InstructionFlags.Prefix66))
if ((flags & InstructionFlags.Prefix66) != 0)
{
WriteByte(0x66);
}
if (flags.HasFlag(InstructionFlags.PrefixF2))
if ((flags & InstructionFlags.PrefixF2) != 0f)
{
WriteByte(0xf2);
}
if (flags.HasFlag(InstructionFlags.PrefixF3))
if ((flags & InstructionFlags.PrefixF3) != 0f)
{
WriteByte(0xf3);
}

View file

@ -1,6 +1,7 @@
using Ryujinx.Audio.Integration;
using Ryujinx.Audio.Renderer.Server.Sink;
using System;
using System.Buffers;
using System.Runtime.CompilerServices;
using System.Text;
@ -35,7 +36,9 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
Enabled = true;
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;
InputCount = sink.Parameter.InputCount;
InputBufferIndices = new ushort[InputCount];
@ -88,7 +91,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
inputCount = bufferCount;
}
short[] outputBuffer = new short[inputCount * SampleCount];
short[] outputBuffer = ArrayPool<short>.Shared.Rent((int)inputCount * SampleCount);
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
{

View file

@ -188,6 +188,8 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
/// </summary>
public Span<bool> BiquadFilterNeedInitialization => SpanHelpers.AsSpan<BiquadFilterNeedInitializationArrayStruct, bool>(ref _biquadFilterNeedInitialization);
private static List<ErrorInfo> _waveBufferUpdaterErrorInfosList;
/// <summary>
/// Initialize the <see cref="VoiceInfo"/>.
/// </summary>
@ -216,6 +218,8 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
DataSourceStateAddressInfo.Setup(0, 0);
InitializeWaveBuffers();
_waveBufferUpdaterErrorInfosList ??= [];
}
/// <summary>
@ -587,14 +591,14 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
Span<WaveBuffer> waveBuffersSpan = WaveBuffers.AsSpan();
Span<WaveBufferInternal> pWaveBuffersSpan = parameter.WaveBuffers.AsSpan();
List<ErrorInfo> errorInfosList = [];
_waveBufferUpdaterErrorInfosList.Clear();
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>
@ -628,14 +632,14 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
Span<WaveBuffer> waveBuffersSpan = WaveBuffers.AsSpan();
Span<WaveBufferInternal> pWaveBuffersSpan = parameter.WaveBuffers.AsSpan();
List<ErrorInfo> errorInfosList = [];
_waveBufferUpdaterErrorInfosList.Clear();
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>

View file

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

View file

@ -1,11 +1,12 @@
using Ryujinx.Graphics.GAL.Multithreading.Model;
using Ryujinx.Graphics.GAL.Multithreading.Resources;
using System.Linq;
using System.Buffers;
namespace Ryujinx.Graphics.GAL.Multithreading.Commands
{
struct SetRenderTargetsCommand : IGALCommand, IGALCommand<SetRenderTargetsCommand>
{
public static readonly ArrayPool<ITexture> ArrayPool = ArrayPool<ITexture>.Create(512, 50);
public readonly CommandType CommandType => CommandType.SetRenderTargets;
private TableRef<ITexture[]> _colors;
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)
{
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)
{
_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();
}

View file

@ -451,7 +451,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
// TODO: Confirm behaviour on hardware.
// 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;
}

View file

@ -646,7 +646,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
int width = scissor.X2 - x;
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;
y = screenScissor.Height - height - y;
@ -730,7 +730,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
FaceState face = _state.State.FaceState;
bool disableTransform = _state.State.ViewportTransformEnable == 0;
bool yNegate = yControl.HasFlag(YControl.NegateY);
bool yNegate = (yControl & YControl.NegateY) != 0;
UpdateFrontFace(yControl, face.FrontFace);
UpdateDepthMode();
@ -1230,7 +1230,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// <param name="frontFace">Front face</param>
private void UpdateFrontFace(YControl yControl, FrontFace frontFace)
{
bool isUpperLeftOrigin = !yControl.HasFlag(YControl.TriangleRastFlip);
bool isUpperLeftOrigin = (yControl & YControl.TriangleRastFlip) == 0;
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.
if (!_fsReadsFragCoord && _state.State.YControl.HasFlag(YControl.NegateY))
if (!_fsReadsFragCoord && (_state.State.YControl & YControl.NegateY) != 0)
{
UpdateSupportBufferViewportSize();
}

View file

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

View file

@ -1628,7 +1628,15 @@ namespace Ryujinx.Graphics.Gpu.Image
{
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)
{

View file

@ -45,7 +45,11 @@ namespace Ryujinx.Graphics.Gpu.Image
/// </summary>
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>
/// The storage texture associated with this group.
@ -126,6 +130,10 @@ namespace Ryujinx.Graphics.Gpu.Image
_incompatibleOverlaps = incompatibleOverlaps;
_flushIncompatibleOverlaps = TextureCompatibility.IsFormatHostIncompatible(storage.Info, context.Capabilities);
_signalModifyingCallback = SignalModifyingCallback;
_discardDataCallback = DiscardDataCallback;
_checkDirtyCallback = CheckDirtyCallback;
}
/// <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>
/// <returns>True if a flag was dirty, false otherwise</returns>
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;
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];
foreach (RegionHandle handle in group.Handles)
if (handle.Dirty)
{
if (handle.Dirty)
if (consume)
{
if (consume)
{
handle.Reprotect();
}
dirty = true;
handle.Reprotect();
}
dirty = true;
}
}
});
}
return dirty;
}
@ -287,15 +299,19 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="texture">The texture being discarded</param>
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>
@ -307,7 +323,7 @@ namespace Ryujinx.Graphics.Gpu.Image
{
FlushIncompatibleOverlapsIfNeeded();
EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) =>
EvaluateRelevantHandles(texture, (baseHandle, regionCount, split, bound) =>
{
bool dirty = false;
bool anyModified = false;
@ -383,7 +399,9 @@ namespace Ryujinx.Graphics.Gpu.Image
texture.SynchronizeFull();
}
}
});
return true;
}, out _);
}
/// <summary>
@ -460,7 +478,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="texture">The texture to synchronize dependents of</param>
public void SynchronizeDependents(Texture texture)
{
EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) =>
EvaluateRelevantHandles(texture, (baseHandle, regionCount, split, bound) =>
{
for (int i = 0; i < regionCount; i++)
{
@ -468,7 +486,9 @@ namespace Ryujinx.Graphics.Gpu.Image
group.SynchronizeDependents();
}
});
return true;
}, out _);
}
/// <summary>
@ -550,7 +570,7 @@ namespace Ryujinx.Graphics.Gpu.Image
tracked = tracked || ShouldFlushTriggerTracking();
bool flushed = false;
EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) =>
EvaluateRelevantHandles(texture, (baseHandle, regionCount, split, bound) =>
{
int startSlice = 0;
int endSlice = 0;
@ -604,7 +624,9 @@ namespace Ryujinx.Graphics.Gpu.Image
flushed = true;
}
});
return true;
}, out _);
Storage.SignalModifiedDirty();
@ -693,7 +715,7 @@ namespace Ryujinx.Graphics.Gpu.Image
ClearIncompatibleOverlaps(texture);
EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) =>
EvaluateRelevantHandles(texture, (baseHandle, regionCount, split, bound) =>
{
for (int i = 0; i < regionCount; i++)
{
@ -701,7 +723,9 @@ namespace Ryujinx.Graphics.Gpu.Image
group.SignalModified(_context);
}
});
return true;
}, out _);
}
/// <summary>
@ -714,16 +738,20 @@ namespace Ryujinx.Graphics.Gpu.Image
ModifiedSequence = _context.GetModifiedSequence();
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>
@ -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.
/// This can be called for multiple disjoint ranges, if required.
/// </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))
{
callback(0, _handles.Length);
result = callback(0, _handles.Length, specialData: specialData);
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>
@ -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.
/// This can be called for multiple disjoint ranges, if required.
/// </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 targetLevelHandles = _hasMipViews ? levels : 1;
result = false;
if (_isBuffer)
{
return;
@ -808,7 +838,7 @@ namespace Ryujinx.Graphics.Gpu.Image
{
// When there are no layer views, the mips are at a consistent offset.
callback(firstLevel, targetLevelHandles);
result = callback(firstLevel, targetLevelHandles, specialData: specialData);
}
else
{
@ -822,7 +852,7 @@ namespace Ryujinx.Graphics.Gpu.Image
while (levels-- > 1)
{
callback(firstLayer + levelIndex, slices);
result = callback(firstLayer + levelIndex, slices, specialData: specialData);
levelIndex += layerCount;
layerCount = Math.Max(layerCount >> 1, 1);
@ -839,7 +869,7 @@ namespace Ryujinx.Graphics.Gpu.Image
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++)
{
callback(firstLevel + (firstLayer + i) * levelHandles, targetLevelHandles, true);
result = callback(firstLevel + (firstLayer + i) * levelHandles, targetLevelHandles, true, specialData: specialData);
}
}
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)> otherRange = [];
EvaluateRelevantHandles(firstLayer, firstLevel, other.Info.GetSlices(), other.Info.Levels, (baseHandle, regionCount, split) => targetRange.Add((baseHandle, regionCount)));
otherGroup.EvaluateRelevantHandles(other, (baseHandle, regionCount, split) => otherRange.Add((baseHandle, regionCount)));
EvaluateRelevantHandles(firstLayer, firstLevel, other.Info.GetSlices(), other.Info.Levels, (baseHandle, regionCount, split, specialData) =>
{
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 otherIndex = 0;

View file

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

View file

@ -1,5 +1,6 @@
using Ryujinx.Memory.Range;
using System;
using System.Buffers;
using System.Linq;
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.
Lock.EnterReadLock();
RangeItem<BufferModifiedRange>[] overlaps = FindOverlapsAsArray(address, size);
RangeItem<BufferModifiedRange>[] overlaps = FindOverlapsAsArray(address, size, out int length);
Lock.ExitReadLock();
for (int i = 0; i < overlaps.Length; i++)
if (length != 0)
{
BufferModifiedRange overlap = overlaps[i].Value;
rangeAction(overlap.Address, overlap.Size);
for (int i = 0; i < length; i++)
{
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();
// 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);
int rangeCount = overlaps.Length;
RangeItem<BufferModifiedRange>[] overlaps = FindOverlapsAsArray(address, size, out int rangeCount);
if (rangeCount == 0)
{
@ -410,7 +414,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
for (int i = 0; i < rangeCount; i++)
{
BufferModifiedRange overlap = overlaps[i].Value;
BufferModifiedRange overlap = overlaps![i].Value;
long diff = (long)(overlap.SyncNumber - currentSync);
@ -430,7 +434,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
// Wait for the syncpoint.
_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();
}

View file

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

View file

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

View file

@ -1,6 +1,7 @@
using Ryujinx.Graphics.GAL;
using Silk.NET.Vulkan;
using System;
using System.Collections.Generic;
using System.Linq;
using Format = Ryujinx.Graphics.GAL.Format;
using VkFormat = Silk.NET.Vulkan.Format;
@ -10,26 +11,27 @@ namespace Ryujinx.Graphics.Vulkan
class FramebufferParams
{
private readonly Device _device;
private readonly Auto<DisposableImageView>[] _attachments;
private readonly TextureView[] _colors;
private readonly TextureView _depthStencil;
private readonly TextureView[] _colorsCanonical;
private readonly TextureView _baseAttachment;
private readonly uint _validColorAttachments;
private Auto<DisposableImageView>[] _attachments;
private TextureView[] _colors;
private TextureView _depthStencil;
private TextureView[] _colorsCanonical;
private TextureView _baseAttachment;
private uint _validColorAttachments;
private int _totalCount;
public uint Width { get; }
public uint Height { get; }
public uint Layers { get; }
public uint Width { get; private set; }
public uint Height { get; private set; }
public uint Layers { get; private set; }
public uint[] AttachmentSamples { get; }
public VkFormat[] AttachmentFormats { get; }
public int[] AttachmentIndices { get; }
public uint AttachmentIntegerFormatMask { get; }
public bool LogicOpsAllowed { get; }
public uint[] AttachmentSamples { get; private set; }
public VkFormat[] AttachmentFormats { get; private set; }
public int[] AttachmentIndices { get; private set; }
public uint AttachmentIntegerFormatMask { get; private set; }
public bool LogicOpsAllowed { get; private set; }
public int AttachmentsCount { get; }
public int MaxColorAttachmentIndex => AttachmentIndices.Length > 0 ? AttachmentIndices[^1] : -1;
public bool HasDepthStencil { get; }
public int AttachmentsCount { get; private set; }
public int MaxColorAttachmentIndex => ColorAttachmentsCount > 0 ? AttachmentIndices[ColorAttachmentsCount - 1] : -1;
public bool HasDepthStencil { get; private set; }
public int ColorAttachmentsCount => AttachmentsCount - (HasDepthStencil ? 1 : 0);
public FramebufferParams(Device device, TextureView view, uint width, uint height)
@ -50,7 +52,7 @@ namespace Ryujinx.Graphics.Vulkan
else
{
_colors = [view];
_colorsCanonical = _colors;
_colorsCanonical = [view];
}
Width = width;
@ -64,6 +66,7 @@ namespace Ryujinx.Graphics.Vulkan
LogicOpsAllowed = !format.IsFloatOrSrgb();
AttachmentsCount = 1;
_totalCount = 1;
HasDepthStencil = isDepthStencil;
}
@ -133,7 +136,7 @@ namespace Ryujinx.Graphics.Vulkan
AttachmentIntegerFormatMask = attachmentIntegerFormatMask;
LogicOpsAllowed = !allFormatsFloatOrSrgb;
if (depthStencil is TextureView dsTexture && dsTexture.Valid)
if (depthStencil is TextureView { Valid: true } dsTexture)
{
_attachments[count - 1] = dsTexture.GetImageViewForAttachment();
_depthStencil = dsTexture;
@ -159,11 +162,151 @@ namespace Ryujinx.Graphics.Vulkan
Layers = layers;
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)
{
if ((uint)index >= _attachments.Length)
if ((uint)index >= AttachmentsCount)
{
return null;
}
@ -183,7 +326,7 @@ namespace Ryujinx.Graphics.Vulkan
public ComponentType GetAttachmentComponentType(int index)
{
if (_colors != null && (uint)index < _colors.Length)
if (_colors != null && (uint)index < ColorAttachmentsCount)
{
Format format = _colors[index].Info.Format;
@ -218,7 +361,7 @@ namespace Ryujinx.Graphics.Vulkan
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)
@ -233,9 +376,9 @@ namespace Ryujinx.Graphics.Vulkan
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;
}
@ -244,7 +387,7 @@ namespace Ryujinx.Graphics.Vulkan
{
SType = StructureType.FramebufferCreateInfo,
RenderPass = renderPass.Get(cbs).Value,
AttachmentCount = (uint)_attachments.Length,
AttachmentCount = (uint)AttachmentsCount,
PAttachments = attachments,
Width = Width,
Height = Height,
@ -252,14 +395,13 @@ namespace Ryujinx.Graphics.Vulkan
};
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()
{
TextureView[] result = new TextureView[_attachments.Length];
_colors?.CopyTo(result, 0);
TextureView[] result = new TextureView[AttachmentsCount];
_colors?.AsSpan(..ColorAttachmentsCount).CopyTo(result.AsSpan());
if (_depthStencil != null)
{
@ -278,8 +420,11 @@ namespace Ryujinx.Graphics.Vulkan
{
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.
color.Storage?.QueueLoadOpBarrier(cbs, false);
}
@ -294,8 +439,11 @@ namespace Ryujinx.Graphics.Vulkan
{
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);
}
}
@ -307,7 +455,7 @@ namespace Ryujinx.Graphics.Vulkan
{
_depthStencil?.Storage.ClearBindings();
for (int i = 0; i < _colorsCanonical.Length; i++)
for (int i = 0; i < _totalCount; i++)
{
_colorsCanonical[i]?.Storage.ClearBindings();
}
@ -317,7 +465,7 @@ namespace Ryujinx.Graphics.Vulkan
{
_depthStencil?.Storage.AddBinding(_depthStencil);
for (int i = 0; i < _colorsCanonical.Length; i++)
for (int i = 0; i < _totalCount; i++)
{
TextureView color = _colorsCanonical[i];
color?.Storage.AddBinding(color);

View file

@ -1,6 +1,7 @@
using Ryujinx.Common.Memory;
using Silk.NET.Vulkan;
using System;
using System.Buffers;
namespace Ryujinx.Graphics.Vulkan
{
@ -11,7 +12,7 @@ namespace Ryujinx.Graphics.Vulkan
{
private const int BufferUsageTrackingGranularity = 4096;
private readonly FenceHolder[] _fences;
public FenceHolder[] Fences { get; }
private readonly BufferUsageBitmap _bufferUsageBitmap;
/// <summary>
@ -19,7 +20,7 @@ namespace Ryujinx.Graphics.Vulkan
/// </summary>
public MultiFenceHolder()
{
_fences = new FenceHolder[CommandBufferPool.MaxCommandBuffers];
Fences = ArrayPool<FenceHolder>.Shared.Rent(CommandBufferPool.MaxCommandBuffers);
}
/// <summary>
@ -28,7 +29,7 @@ namespace Ryujinx.Graphics.Vulkan
/// <param name="size">Size of the buffer</param>
public MultiFenceHolder(int size)
{
_fences = new FenceHolder[CommandBufferPool.MaxCommandBuffers];
Fences = ArrayPool<FenceHolder>.Shared.Rent(CommandBufferPool.MaxCommandBuffers);
_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>
public bool AddFence(int cbIndex, FenceHolder fence)
{
ref FenceHolder fenceRef = ref _fences[cbIndex];
ref FenceHolder fenceRef = ref Fences[cbIndex];
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>
public void RemoveFence(int cbIndex)
{
_fences[cbIndex] = null;
Fences[cbIndex] = null;
}
/// <summary>
@ -117,7 +118,7 @@ namespace Ryujinx.Graphics.Vulkan
/// <returns>True if referenced, false otherwise</returns>
public bool HasFence(int cbIndex)
{
return _fences[cbIndex] != null;
return Fences[cbIndex] != null;
}
/// <summary>
@ -227,9 +228,9 @@ namespace Ryujinx.Graphics.Vulkan
{
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)
{
@ -251,9 +252,9 @@ namespace Ryujinx.Graphics.Vulkan
{
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))
{

View file

@ -1453,7 +1453,7 @@ namespace Ryujinx.Graphics.Vulkan
FramebufferParams?.ClearBindings();
}
FramebufferParams = new FramebufferParams(Device, colors, depthStencil);
FramebufferParams = FramebufferParams?.Update(colors, depthStencil) ?? new FramebufferParams(Device, colors, depthStencil);
if (IsMainPipeline)
{
@ -1471,18 +1471,18 @@ namespace Ryujinx.Graphics.Vulkan
protected void UpdatePipelineAttachmentFormats()
{
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.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;
}
_newState.ColorBlendAttachmentStateCount = (uint)(FramebufferParams.MaxColorAttachmentIndex + 1);
_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()

View file

@ -178,17 +178,16 @@ namespace Ryujinx.Graphics.Vulkan
{
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);
return false;
});
_forcedFences[i].Texture.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, _forcedFences[i].StageFlags);
}
}
}

View file

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

View file

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

View file

@ -110,7 +110,22 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
{
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,
size,
State,

View file

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

View file

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

View file

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

View file

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

View file

@ -1,3 +1,4 @@
using Ryujinx.Common;
using Ryujinx.HLE.HOS.Kernel.Common;
using Ryujinx.HLE.HOS.Kernel.Process;
using Ryujinx.Horizon.Common;
@ -11,6 +12,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
class KAddressArbiter
{
private const int HasListenersMask = 0x40000000;
private static readonly ObjectPool<KThread[]> _threadArrayPool = new(() => []);
private readonly KernelContext _context;
@ -198,9 +200,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
{
_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);
}
@ -480,9 +487,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
// or negative. It is incremented if there are no threads waiting.
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;
}
@ -553,23 +561,55 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
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,
int count,
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[] toSignal = (count > 0 ? candidates.Take(count) : candidates).ToArray();
KThread[] candidates = _threadArrayPool.Allocate();
if (candidates.Length < threads.Count)
{
Array.Resize(ref candidates, threads.Count);
}
int validCount = 0;
for (int i = 0; i < threads.Count; i++)
{
if (predicate(threads[i], address))
{
candidates[validCount++] = threads[i];
}
}
Span<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);
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 int DynamicPriority { get; set; }
public ulong AffinityMask { get; set; }
public int DynamicPriority { get; private set; }
public ulong AffinityMask { 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 LinkedListNode<KThread>[] SiblingsPerCore { get; private set; }
public readonly LinkedListNode<KThread>[] SiblingsPerCore;
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 LinkedListNode<KThread> _mutexWaiterNode;
private readonly LinkedListNode<KThread> _mutexWaiterNode;
private readonly LinkedList<KThread> _pinnedWaiters;
private LinkedListNode<KThread> _pinnedWaiterNode;
private readonly LinkedListNode<KThread> _pinnedWaiterNode;
public KThread MutexOwner { get; private set; }
@ -1070,11 +1070,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
if (nextPrio != null)
{
thread._mutexWaiterNode = _mutexWaiters.AddBefore(nextPrio, thread);
_mutexWaiters.AddBefore(nextPrio, thread._mutexWaiterNode);
}
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.Memory;
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Reflection;
@ -46,6 +47,8 @@ namespace Ryujinx.HLE.HOS.Services.Nv
{ "/dev/nvhost-dbg-gpu", typeof(NvHostDbgGpuDeviceFile) },
{ "/dev/nvhost-prof-gpu", typeof(NvHostProfGpuDeviceFile) },
};
private static readonly ArrayPool<byte> _byteArrayPool = ArrayPool<byte>.Create();
public static IdDictionary DeviceFileIdRegistry = new();
@ -471,10 +474,13 @@ namespace Ryujinx.HLE.HOS.Services.Nv
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];
context.Memory.Read(inlineInBufferPosition, inlineInBuffer);
inlineInBuffer = _byteArrayPool.Rent((int)inlineInBufferSize);
inlineInBufferSpan = inlineInBuffer;
context.Memory.Read(inlineInBufferPosition, inlineInBufferSpan[..(int)inlineInBufferSize]);
}
if (errorCode == NvResult.Success)
@ -483,7 +489,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv
if (errorCode == NvResult.Success)
{
NvInternalResult internalResult = deviceFile.Ioctl2(ioctlCommand, arguments, inlineInBuffer);
NvInternalResult internalResult = deviceFile.Ioctl2(ioctlCommand, arguments, inlineInBufferSpan[..(int)inlineInBufferSize]);
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);
@ -520,10 +531,13 @@ namespace Ryujinx.HLE.HOS.Services.Nv
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];
context.Memory.Read(inlineOutBufferPosition, inlineOutBuffer);
inlineOutBuffer = _byteArrayPool.Rent((int)inlineOutBufferSize);
inlineOutBufferSpan = inlineOutBuffer;
context.Memory.Read(inlineOutBufferPosition, inlineOutBufferSpan[..(int)inlineOutBufferSize]);
}
if (errorCode == NvResult.Success)
@ -532,7 +546,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv
if (errorCode == NvResult.Success)
{
NvInternalResult internalResult = deviceFile.Ioctl3(ioctlCommand, arguments, inlineOutBuffer);
NvInternalResult internalResult = deviceFile.Ioctl3(ioctlCommand, arguments, inlineOutBufferSpan[..(int)inlineOutBufferSize]);
if (internalResult == NvInternalResult.NotImplemented)
{
@ -544,10 +558,15 @@ namespace Ryujinx.HLE.HOS.Services.Nv
if ((ioctlCommand.DirectionValue & NvIoctl.Direction.Write) != 0)
{
context.Memory.Write(context.Request.GetBufferType0x22(0).Position, arguments.ToArray());
context.Memory.Write(inlineOutBufferPosition, inlineOutBuffer.ToArray());
context.Memory.Write(inlineOutBufferPosition, inlineOutBufferSpan[..(int)inlineOutBufferSize].ToArray());
}
}
}
if (inlineOutBuffer is not null)
{
_byteArrayPool.Return(inlineOutBuffer);
}
}
context.ResponseData.Write((uint)errorCode);

View file

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

View file

@ -88,7 +88,7 @@ namespace Ryujinx.Input.SDL2
_triggerThreshold = 0.0f;
// 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)
{
@ -104,7 +104,7 @@ namespace Ryujinx.Input.SDL2
public void SetLed(uint packedRgb)
{
if (!Features.HasFlag(GamepadFeaturesFlag.Led))
if ((Features & GamepadFeaturesFlag.Led) == 0)
return;
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)
{
if (!Features.HasFlag(GamepadFeaturesFlag.Rumble))
if ((Features & GamepadFeaturesFlag.Rumble) == 0)
return;
ushort lowFrequencyRaw = (ushort)(lowFrequency * ushort.MaxValue);
@ -197,7 +197,7 @@ namespace Ryujinx.Input.SDL2
_ => 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;
const int ElementCount = 3;
@ -232,7 +232,7 @@ namespace Ryujinx.Input.SDL2
{
_configuration = (StandardControllerInputConfig)configuration;
if (Features.HasFlag(GamepadFeaturesFlag.Led) && _configuration.Led.EnableLed)
if ((Features & GamepadFeaturesFlag.Led) != 0 && _configuration.Led.EnableLed)
{
if (_configuration.Led.TurnOffLed)
(this as IGamepad).ClearLed();

View file

@ -82,7 +82,7 @@ namespace Ryujinx.Input.SDL2
Features = GetFeaturesFlag();
// 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)
@ -162,7 +162,7 @@ namespace Ryujinx.Input.SDL2
public void Rumble(float lowFrequency, float highFrequency, uint durationMs)
{
if (!Features.HasFlag(GamepadFeaturesFlag.Rumble))
if ((Features & GamepadFeaturesFlag.Rumble) == 0)
return;
ushort lowFrequencyRaw = (ushort)(lowFrequency * ushort.MaxValue);
@ -194,7 +194,7 @@ namespace Ryujinx.Input.SDL2
_ => 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;
const int ElementCount = 3;

View file

@ -5,6 +5,7 @@ using Ryujinx.Common.Configuration.Hid.Controller.Motion;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Services.Hid;
using System;
using System.Buffers;
using System.Collections.Concurrent;
using System.Numerics;
using System.Runtime.CompilerServices;
@ -291,7 +292,7 @@ namespace Ryujinx.Input.HLE
{
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 gyroscope = gamepad.GetMotionData(MotionInputId.Gyroscope);
@ -531,6 +532,8 @@ namespace Ryujinx.Input.HLE
hidKeyboard.Modifier |= value << entry.Target;
}
ArrayPool<bool>.Shared.Return(keyboardState.KeysState);
return hidKeyboard;

View file

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

View file

@ -1,3 +1,4 @@
using System.Buffers;
using System.Runtime.CompilerServices;
namespace Ryujinx.Input
@ -28,7 +29,8 @@ namespace Ryujinx.Input
[MethodImpl(MethodImplOptions.AggressiveInlining)]
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++)
{

View file

@ -1,3 +1,4 @@
using System.Buffers;
using System.Drawing;
using System.Numerics;
@ -47,7 +48,7 @@ namespace Ryujinx.Input
/// <returns>A snaphost of the state of the mouse.</returns>
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);

View file

@ -7,7 +7,7 @@ namespace Ryujinx.Input
/// </summary>
public class KeyboardStateSnapshot
{
private readonly bool[] _keysState;
public readonly bool[] KeysState;
/// <summary>
/// Create a new <see cref="KeyboardStateSnapshot"/>.
@ -15,7 +15,7 @@ namespace Ryujinx.Input
/// <param name="keysState">The keys state</param>
public KeyboardStateSnapshot(bool[] keysState)
{
_keysState = keysState;
KeysState = keysState;
}
/// <summary>
@ -24,6 +24,6 @@ namespace Ryujinx.Input
/// <param name="key">The key</param>
/// <returns>True if the given key is pressed</returns>
[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>
public class MouseStateSnapshot
{
private readonly bool[] _buttonState;
public readonly bool[] ButtonState;
/// <summary>
/// The position of the mouse cursor
@ -28,7 +28,7 @@ namespace Ryujinx.Input
/// <param name="scroll">The scroll delta</param>
public MouseStateSnapshot(bool[] buttonState, Vector2 position, Vector2 scroll)
{
_buttonState = buttonState;
ButtonState = buttonState;
Position = position;
Scroll = scroll;
@ -40,6 +40,6 @@ namespace Ryujinx.Input
/// <param name="button">The button</param>
/// <returns>True if the given button is pressed</returns>
[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.Buffers;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;
@ -38,7 +39,7 @@ namespace Ryujinx.Memory.Range
index = ~index;
}
RangeItem<T> rangeItem = new(item);
RangeItem<T> rangeItem = _rangeItemPool.Allocate().Set(item);
Insert(index, rangeItem);
}
@ -144,6 +145,8 @@ namespace Ryujinx.Memory.Range
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void RemoveAt(int index)
{
_rangeItemPool.Release(Items[index]);
if (index < Count - 1)
{
Items[index + 1].Previous = index > 0 ? Items[index - 1] : null;
@ -433,7 +436,7 @@ namespace Ryujinx.Memory.Range
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);
@ -441,11 +444,13 @@ namespace Ryujinx.Memory.Range
if (index < 0)
{
result = [];
result = null;
length = 0;
}
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);
}

View file

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

View file

@ -1,20 +1,42 @@
using System.Collections;
using Ryujinx.Common;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
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> Previous;
public readonly ulong Address = value.Address;
public readonly ulong EndAddress = value.Address + value.Size;
public ulong Address;
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)]
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
{
protected static readonly ObjectPool<RangeItem<T>> _rangeItemPool = new(() => new RangeItem<T>());
private const int BackingInitialSize = 1024;
protected RangeItem<T>[] Items;

View file

@ -1,5 +1,6 @@
using Ryujinx.Memory.Range;
using System;
using System.Buffers;
using System.Collections.Generic;
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.
regions.Lock.EnterReadLock();
RangeItem<VirtualRegion>[] overlaps = regions.FindOverlapsAsArray(address, size);
RangeItem<VirtualRegion>[] overlaps = regions.FindOverlapsAsArray(address, size, out int length);
regions.Lock.ExitReadLock();
if (overlaps.Length == 0 && !precise)
if (length == 0 && !precise)
{
if (_memoryManager.IsRangeMapped(address, size))
{
@ -323,8 +324,8 @@ namespace Ryujinx.Memory.Tracking
// Increase the access size to trigger handles with misaligned accesses.
size += (ulong)_pageSize;
}
for (int i = 0; i < overlaps.Length; i++)
for (int i = 0; i < length; i++)
{
VirtualRegion region = overlaps[i].Value;
@ -337,6 +338,11 @@ namespace Ryujinx.Memory.Tracking
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 IsLeft { 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 event Action NotifyChangesEvent;