mirror of
https://git.ryujinx.app/ryubing/ryujinx.git
synced 2025-12-12 22:36:59 +00:00
Memory changes 3 (ryubing/ryujinx!202)
See merge request ryubing/ryujinx!202
This commit is contained in:
parent
ab7aeee67b
commit
92b61f9d73
43 changed files with 686 additions and 315 deletions
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -386,6 +386,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||
}
|
||||
|
||||
ulong rwdataStart = roInfo.Address + roInfo.Size;
|
||||
|
||||
KMemoryInfo.Pool.Release(roInfo);
|
||||
|
||||
try
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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])
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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++)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue