mirror of
https://git.ryujinx.app/kenji-nx/ryujinx.git
synced 2025-12-12 10:37:06 +00:00
Memory changes 3
General memory improvements to decrease GC pressure and frequency. Pool big arrays and objects that are created and deleted often. Skip data copies when they aren't needed. Inline flag checks to skip unneeded allocations. From my testing the performance is about the same, but the GC frequency is much lower and collection is faster causing less and smaller spikes.
This commit is contained in:
parent
2c50fbcc1f
commit
e3ee28605d
42 changed files with 703 additions and 331 deletions
|
|
@ -1106,17 +1106,17 @@ namespace ARMeilleure.CodeGen.X86
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (flags.HasFlag(InstructionFlags.Prefix66))
|
if ((flags & InstructionFlags.Prefix66) != 0)
|
||||||
{
|
{
|
||||||
WriteByte(0x66);
|
WriteByte(0x66);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flags.HasFlag(InstructionFlags.PrefixF2))
|
if ((flags & InstructionFlags.PrefixF2) != 0f)
|
||||||
{
|
{
|
||||||
WriteByte(0xf2);
|
WriteByte(0xf2);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flags.HasFlag(InstructionFlags.PrefixF3))
|
if ((flags & InstructionFlags.PrefixF3) != 0f)
|
||||||
{
|
{
|
||||||
WriteByte(0xf3);
|
WriteByte(0xf3);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
using Ryujinx.Audio.Integration;
|
using Ryujinx.Audio.Integration;
|
||||||
using Ryujinx.Audio.Renderer.Server.Sink;
|
using Ryujinx.Audio.Renderer.Server.Sink;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Buffers;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
|
|
@ -30,7 +31,9 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||||
Enabled = true;
|
Enabled = true;
|
||||||
NodeId = nodeId;
|
NodeId = nodeId;
|
||||||
|
|
||||||
DeviceName = Encoding.ASCII.GetString(sink.Parameter.DeviceName).TrimEnd('\0');
|
// Unused and wasting time and memory, re-add if needed
|
||||||
|
// DeviceName = Encoding.ASCII.GetString(sink.Parameter.DeviceName).TrimEnd('\0');
|
||||||
|
|
||||||
SessionId = sessionId;
|
SessionId = sessionId;
|
||||||
InputCount = sink.Parameter.InputCount;
|
InputCount = sink.Parameter.InputCount;
|
||||||
InputBufferIndices = new ushort[InputCount];
|
InputBufferIndices = new ushort[InputCount];
|
||||||
|
|
@ -83,7 +86,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||||
inputCount = bufferCount;
|
inputCount = bufferCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
short[] outputBuffer = new short[inputCount * SampleCount];
|
short[] outputBuffer = ArrayPool<short>.Shared.Rent((int)inputCount * SampleCount);
|
||||||
|
|
||||||
for (int i = 0; i < bufferCount; i++)
|
for (int i = 0; i < bufferCount; i++)
|
||||||
{
|
{
|
||||||
|
|
@ -95,7 +98,9 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
device.AppendBuffer(outputBuffer, inputCount);
|
device.AppendBuffer(outputBuffer.AsSpan(..((int)inputCount * SampleCount)), inputCount);
|
||||||
|
|
||||||
|
ArrayPool<short>.Shared.Return(outputBuffer);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -188,6 +188,8 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Span<bool> BiquadFilterNeedInitialization => SpanHelpers.AsSpan<BiquadFilterNeedInitializationArrayStruct, bool>(ref _biquadFilterNeedInitialization);
|
public Span<bool> BiquadFilterNeedInitialization => SpanHelpers.AsSpan<BiquadFilterNeedInitializationArrayStruct, bool>(ref _biquadFilterNeedInitialization);
|
||||||
|
|
||||||
|
private static List<ErrorInfo> _waveBufferUpdaterErrorInfosList;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initialize the <see cref="VoiceInfo"/>.
|
/// Initialize the <see cref="VoiceInfo"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -216,6 +218,8 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
|
||||||
DataSourceStateAddressInfo.Setup(0, 0);
|
DataSourceStateAddressInfo.Setup(0, 0);
|
||||||
|
|
||||||
InitializeWaveBuffers();
|
InitializeWaveBuffers();
|
||||||
|
|
||||||
|
_waveBufferUpdaterErrorInfosList ??= [];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -587,14 +591,14 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
|
||||||
|
|
||||||
Span<WaveBuffer> waveBuffersSpan = WaveBuffers.AsSpan();
|
Span<WaveBuffer> waveBuffersSpan = WaveBuffers.AsSpan();
|
||||||
Span<WaveBufferInternal> pWaveBuffersSpan = parameter.WaveBuffers.AsSpan();
|
Span<WaveBufferInternal> pWaveBuffersSpan = parameter.WaveBuffers.AsSpan();
|
||||||
List<ErrorInfo> errorInfosList = [];
|
_waveBufferUpdaterErrorInfosList.Clear();
|
||||||
|
|
||||||
for (int i = 0; i < Constants.VoiceWaveBufferCount; i++)
|
for (int i = 0; i < Constants.VoiceWaveBufferCount; i++)
|
||||||
{
|
{
|
||||||
UpdateWaveBuffer(errorInfosList, ref waveBuffersSpan[i], ref pWaveBuffersSpan[i], parameter.SampleFormat, voiceState.IsWaveBufferValid[i], mapper, ref behaviourInfo);
|
UpdateWaveBuffer(_waveBufferUpdaterErrorInfosList, ref waveBuffersSpan[i], ref pWaveBuffersSpan[i], parameter.SampleFormat, voiceState.IsWaveBufferValid[i], mapper, ref behaviourInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
errorInfos = errorInfosList.ToArray();
|
errorInfos = _waveBufferUpdaterErrorInfosList.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -628,14 +632,14 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
|
||||||
|
|
||||||
Span<WaveBuffer> waveBuffersSpan = WaveBuffers.AsSpan();
|
Span<WaveBuffer> waveBuffersSpan = WaveBuffers.AsSpan();
|
||||||
Span<WaveBufferInternal> pWaveBuffersSpan = parameter.WaveBuffers.AsSpan();
|
Span<WaveBufferInternal> pWaveBuffersSpan = parameter.WaveBuffers.AsSpan();
|
||||||
List<ErrorInfo> errorInfosList = [];
|
_waveBufferUpdaterErrorInfosList.Clear();
|
||||||
|
|
||||||
for (int i = 0; i < Constants.VoiceWaveBufferCount; i++)
|
for (int i = 0; i < Constants.VoiceWaveBufferCount; i++)
|
||||||
{
|
{
|
||||||
UpdateWaveBuffer(errorInfosList, ref waveBuffersSpan[i], ref pWaveBuffersSpan[i], parameter.SampleFormat, voiceState.IsWaveBufferValid[i], mapper, ref behaviourInfo);
|
UpdateWaveBuffer(_waveBufferUpdaterErrorInfosList, ref waveBuffersSpan[i], ref pWaveBuffersSpan[i], parameter.SampleFormat, voiceState.IsWaveBufferValid[i], mapper, ref behaviourInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
errorInfos = errorInfosList.ToArray();
|
errorInfos = _waveBufferUpdaterErrorInfosList.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,10 @@ namespace Ryujinx.Common.Collections
|
||||||
/// <exception cref="ArgumentNullException"><paramref name="key"/> is null</exception>
|
/// <exception cref="ArgumentNullException"><paramref name="key"/> is null</exception>
|
||||||
public int Get(TKey key, ref TValue[] overlaps)
|
public int Get(TKey key, ref TValue[] overlaps)
|
||||||
{
|
{
|
||||||
ArgumentNullException.ThrowIfNull(key);
|
if (!typeof(TKey).IsValueType)
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(key);
|
||||||
|
}
|
||||||
|
|
||||||
IntervalTreeNode<TKey, TValue> node = GetNode(key);
|
IntervalTreeNode<TKey, TValue> node = GetNode(key);
|
||||||
|
|
||||||
|
|
@ -91,7 +94,10 @@ namespace Ryujinx.Common.Collections
|
||||||
/// <returns>Number of deleted values</returns>
|
/// <returns>Number of deleted values</returns>
|
||||||
public int Remove(TKey key, TValue value)
|
public int Remove(TKey key, TValue value)
|
||||||
{
|
{
|
||||||
ArgumentNullException.ThrowIfNull(key);
|
if (!typeof(TKey).IsValueType)
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(key);
|
||||||
|
}
|
||||||
|
|
||||||
int removed = Delete(key, value);
|
int removed = Delete(key, value);
|
||||||
|
|
||||||
|
|
@ -144,7 +150,10 @@ namespace Ryujinx.Common.Collections
|
||||||
/// <exception cref="ArgumentNullException"><paramref name="key"/> is null</exception>
|
/// <exception cref="ArgumentNullException"><paramref name="key"/> is null</exception>
|
||||||
private IntervalTreeNode<TKey, TValue> GetNode(TKey key)
|
private IntervalTreeNode<TKey, TValue> GetNode(TKey key)
|
||||||
{
|
{
|
||||||
ArgumentNullException.ThrowIfNull(key);
|
if (!typeof(TKey).IsValueType)
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(key);
|
||||||
|
}
|
||||||
|
|
||||||
IntervalTreeNode<TKey, TValue> node = Root;
|
IntervalTreeNode<TKey, TValue> node = Root;
|
||||||
while (node != null)
|
while (node != null)
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
using Ryujinx.Graphics.GAL.Multithreading.Model;
|
using Ryujinx.Graphics.GAL.Multithreading.Model;
|
||||||
using Ryujinx.Graphics.GAL.Multithreading.Resources;
|
using Ryujinx.Graphics.GAL.Multithreading.Resources;
|
||||||
using System.Linq;
|
using System.Buffers;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.GAL.Multithreading.Commands
|
namespace Ryujinx.Graphics.GAL.Multithreading.Commands
|
||||||
{
|
{
|
||||||
struct SetRenderTargetsCommand : IGALCommand, IGALCommand<SetRenderTargetsCommand>
|
struct SetRenderTargetsCommand : IGALCommand, IGALCommand<SetRenderTargetsCommand>
|
||||||
{
|
{
|
||||||
|
public static readonly ArrayPool<ITexture> ArrayPool = ArrayPool<ITexture>.Create(512, 50);
|
||||||
public readonly CommandType CommandType => CommandType.SetRenderTargets;
|
public readonly CommandType CommandType => CommandType.SetRenderTargets;
|
||||||
private TableRef<ITexture[]> _colors;
|
private TableRef<ITexture[]> _colors;
|
||||||
private TableRef<ITexture> _depthStencil;
|
private TableRef<ITexture> _depthStencil;
|
||||||
|
|
@ -18,7 +19,18 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands
|
||||||
|
|
||||||
public static void Run(ref SetRenderTargetsCommand command, ThreadedRenderer threaded, IRenderer renderer)
|
public static void Run(ref SetRenderTargetsCommand command, ThreadedRenderer threaded, IRenderer renderer)
|
||||||
{
|
{
|
||||||
renderer.Pipeline.SetRenderTargets(command._colors.Get(threaded).Select(color => ((ThreadedTexture)color)?.Base).ToArray(), command._depthStencil.GetAs<ThreadedTexture>(threaded)?.Base);
|
ITexture[] colors = command._colors.Get(threaded);
|
||||||
|
ITexture[] colorsCopy = ArrayPool.Rent(colors.Length);
|
||||||
|
|
||||||
|
for (int i = 0; i < colors.Length; i++)
|
||||||
|
{
|
||||||
|
colorsCopy[i] = ((ThreadedTexture)colors[i])?.Base;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderer.Pipeline.SetRenderTargets(colorsCopy, command._depthStencil.GetAs<ThreadedTexture>(threaded)?.Base);
|
||||||
|
|
||||||
|
ArrayPool.Return(colorsCopy);
|
||||||
|
ArrayPool.Return(colors);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -269,7 +269,10 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
||||||
|
|
||||||
public unsafe void SetRenderTargets(ITexture[] colors, ITexture depthStencil)
|
public unsafe void SetRenderTargets(ITexture[] colors, ITexture depthStencil)
|
||||||
{
|
{
|
||||||
_renderer.New<SetRenderTargetsCommand>()->Set(Ref(colors.ToArray()), Ref(depthStencil));
|
ITexture[] colorsCopy = SetRenderTargetsCommand.ArrayPool.Rent(colors.Length);
|
||||||
|
colors.CopyTo(colorsCopy, 0);
|
||||||
|
|
||||||
|
_renderer.New<SetRenderTargetsCommand>()->Set(Ref(colorsCopy), Ref(depthStencil));
|
||||||
_renderer.QueueCommand();
|
_renderer.QueueCommand();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -451,7 +451,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||||
|
|
||||||
// TODO: Confirm behaviour on hardware.
|
// TODO: Confirm behaviour on hardware.
|
||||||
// When this is active, the origin appears to be on the bottom.
|
// When this is active, the origin appears to be on the bottom.
|
||||||
if (_state.State.YControl.HasFlag(YControl.NegateY))
|
if ((_state.State.YControl & YControl.NegateY) != 0)
|
||||||
{
|
{
|
||||||
dstY0 -= dstHeight;
|
dstY0 -= dstHeight;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -645,7 +645,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||||
int width = scissor.X2 - x;
|
int width = scissor.X2 - x;
|
||||||
int height = scissor.Y2 - y;
|
int height = scissor.Y2 - y;
|
||||||
|
|
||||||
if (_state.State.YControl.HasFlag(YControl.NegateY))
|
if ((_state.State.YControl & YControl.NegateY) != 0)
|
||||||
{
|
{
|
||||||
ref var screenScissor = ref _state.State.ScreenScissorState;
|
ref var screenScissor = ref _state.State.ScreenScissorState;
|
||||||
y = screenScissor.Height - height - y;
|
y = screenScissor.Height - height - y;
|
||||||
|
|
@ -729,7 +729,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||||
var face = _state.State.FaceState;
|
var face = _state.State.FaceState;
|
||||||
|
|
||||||
bool disableTransform = _state.State.ViewportTransformEnable == 0;
|
bool disableTransform = _state.State.ViewportTransformEnable == 0;
|
||||||
bool yNegate = yControl.HasFlag(YControl.NegateY);
|
bool yNegate = (yControl & YControl.NegateY) != 0;
|
||||||
|
|
||||||
UpdateFrontFace(yControl, face.FrontFace);
|
UpdateFrontFace(yControl, face.FrontFace);
|
||||||
UpdateDepthMode();
|
UpdateDepthMode();
|
||||||
|
|
@ -1229,7 +1229,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||||
/// <param name="frontFace">Front face</param>
|
/// <param name="frontFace">Front face</param>
|
||||||
private void UpdateFrontFace(YControl yControl, FrontFace frontFace)
|
private void UpdateFrontFace(YControl yControl, FrontFace frontFace)
|
||||||
{
|
{
|
||||||
bool isUpperLeftOrigin = !yControl.HasFlag(YControl.TriangleRastFlip);
|
bool isUpperLeftOrigin = (yControl & YControl.TriangleRastFlip) == 0;
|
||||||
|
|
||||||
if (isUpperLeftOrigin)
|
if (isUpperLeftOrigin)
|
||||||
{
|
{
|
||||||
|
|
@ -1520,7 +1520,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||||
{
|
{
|
||||||
// Make sure we update the viewport size on the support buffer if it will be consumed on the new shader.
|
// Make sure we update the viewport size on the support buffer if it will be consumed on the new shader.
|
||||||
|
|
||||||
if (!_fsReadsFragCoord && _state.State.YControl.HasFlag(YControl.NegateY))
|
if (!_fsReadsFragCoord && (_state.State.YControl & YControl.NegateY) != 0)
|
||||||
{
|
{
|
||||||
UpdateSupportBufferViewportSize();
|
UpdateSupportBufferViewportSize();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -371,9 +371,9 @@ namespace Ryujinx.Graphics.Gpu
|
||||||
/// <param name="flags">Modifiers for how host sync should be created</param>
|
/// <param name="flags">Modifiers for how host sync should be created</param>
|
||||||
internal void CreateHostSyncIfNeeded(HostSyncFlags flags)
|
internal void CreateHostSyncIfNeeded(HostSyncFlags flags)
|
||||||
{
|
{
|
||||||
bool syncpoint = flags.HasFlag(HostSyncFlags.Syncpoint);
|
bool syncPoint = (flags & HostSyncFlags.Syncpoint) == HostSyncFlags.Syncpoint;
|
||||||
bool strict = flags.HasFlag(HostSyncFlags.Strict);
|
bool strict = (flags & HostSyncFlags.Strict) == HostSyncFlags.Strict;
|
||||||
bool force = flags.HasFlag(HostSyncFlags.Force);
|
bool force = (flags & HostSyncFlags.Force) == HostSyncFlags.Force;
|
||||||
|
|
||||||
if (BufferMigrations.Count > 0)
|
if (BufferMigrations.Count > 0)
|
||||||
{
|
{
|
||||||
|
|
@ -392,24 +392,37 @@ namespace Ryujinx.Graphics.Gpu
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (force || _pendingSync || (syncpoint && SyncpointActions.Count > 0))
|
if (force || _pendingSync || (syncPoint && SyncpointActions.Count > 0))
|
||||||
{
|
{
|
||||||
foreach (var action in SyncActions)
|
foreach (var action in SyncActions)
|
||||||
{
|
{
|
||||||
action.SyncPreAction(syncpoint);
|
action.SyncPreAction(syncPoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var action in SyncpointActions)
|
foreach (var action in SyncpointActions)
|
||||||
{
|
{
|
||||||
action.SyncPreAction(syncpoint);
|
action.SyncPreAction(syncPoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
Renderer.CreateSync(SyncNumber, strict);
|
Renderer.CreateSync(SyncNumber, strict);
|
||||||
|
|
||||||
SyncNumber++;
|
SyncNumber++;
|
||||||
|
|
||||||
SyncActions.RemoveAll(action => action.SyncAction(syncpoint));
|
for (int i = 0; i < SyncActions.Count; i++)
|
||||||
SyncpointActions.RemoveAll(action => action.SyncAction(syncpoint));
|
{
|
||||||
|
if (SyncActions[i].SyncAction(syncPoint))
|
||||||
|
{
|
||||||
|
SyncActions.RemoveAt(i--);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < SyncpointActions.Count; i++)
|
||||||
|
{
|
||||||
|
if (SyncpointActions[i].SyncAction(syncPoint))
|
||||||
|
{
|
||||||
|
SyncpointActions.RemoveAt(i--);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_pendingSync = false;
|
_pendingSync = false;
|
||||||
|
|
|
||||||
|
|
@ -1616,7 +1616,15 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
{
|
{
|
||||||
lock (_poolOwners)
|
lock (_poolOwners)
|
||||||
{
|
{
|
||||||
int references = _poolOwners.RemoveAll(entry => entry.Pool == pool && entry.ID == id || id == -1);
|
int references = 0;
|
||||||
|
for (int i = 0; i < _poolOwners.Count; i++)
|
||||||
|
{
|
||||||
|
if (_poolOwners[i].Pool == pool && _poolOwners[i].ID == id || id == -1)
|
||||||
|
{
|
||||||
|
_poolOwners.RemoveAt(i--);
|
||||||
|
references++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (references == 0)
|
if (references == 0)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,11 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private const int GranularLayerThreshold = 8;
|
private const int GranularLayerThreshold = 8;
|
||||||
|
|
||||||
private delegate void HandlesCallbackDelegate(int baseHandle, int regionCount, bool split = false);
|
private delegate bool HandlesCallbackDelegate(int baseHandle, int regionCount, bool split = false, bool specialData = false);
|
||||||
|
|
||||||
|
private readonly HandlesCallbackDelegate _signalModifyingCallback;
|
||||||
|
private readonly HandlesCallbackDelegate _discardDataCallback;
|
||||||
|
private readonly HandlesCallbackDelegate _checkDirtyCallback;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The storage texture associated with this group.
|
/// The storage texture associated with this group.
|
||||||
|
|
@ -126,6 +130,10 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
|
|
||||||
_incompatibleOverlaps = incompatibleOverlaps;
|
_incompatibleOverlaps = incompatibleOverlaps;
|
||||||
_flushIncompatibleOverlaps = TextureCompatibility.IsFormatHostIncompatible(storage.Info, context.Capabilities);
|
_flushIncompatibleOverlaps = TextureCompatibility.IsFormatHostIncompatible(storage.Info, context.Capabilities);
|
||||||
|
|
||||||
|
_signalModifyingCallback = SignalModifyingCallback;
|
||||||
|
_discardDataCallback = DiscardDataCallback;
|
||||||
|
_checkDirtyCallback = CheckDirtyCallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -254,29 +262,33 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
/// <param name="consume">True to consume the dirty flags and reprotect, false to leave them as is</param>
|
/// <param name="consume">True to consume the dirty flags and reprotect, false to leave them as is</param>
|
||||||
/// <returns>True if a flag was dirty, false otherwise</returns>
|
/// <returns>True if a flag was dirty, false otherwise</returns>
|
||||||
public bool CheckDirty(Texture texture, bool consume)
|
public bool CheckDirty(Texture texture, bool consume)
|
||||||
|
{
|
||||||
|
EvaluateRelevantHandles(texture, _checkDirtyCallback, out bool dirty, consume);
|
||||||
|
|
||||||
|
return dirty;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CheckDirtyCallback(int baseHandle, int regionCount, bool split, bool consume)
|
||||||
{
|
{
|
||||||
bool dirty = false;
|
bool dirty = false;
|
||||||
|
|
||||||
EvaluateRelevantHandles(texture, (baseHandle, regionCount, _) =>
|
for (int i = 0; i < regionCount; i++)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < regionCount; i++)
|
TextureGroupHandle group = _handles[baseHandle + i];
|
||||||
|
|
||||||
|
foreach (RegionHandle handle in group.Handles)
|
||||||
{
|
{
|
||||||
TextureGroupHandle group = _handles[baseHandle + i];
|
if (handle.Dirty)
|
||||||
|
|
||||||
foreach (RegionHandle handle in group.Handles)
|
|
||||||
{
|
{
|
||||||
if (handle.Dirty)
|
if (consume)
|
||||||
{
|
{
|
||||||
if (consume)
|
handle.Reprotect();
|
||||||
{
|
|
||||||
handle.Reprotect();
|
|
||||||
}
|
|
||||||
|
|
||||||
dirty = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dirty = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
return dirty;
|
return dirty;
|
||||||
}
|
}
|
||||||
|
|
@ -288,15 +300,19 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
/// <param name="texture">The texture being discarded</param>
|
/// <param name="texture">The texture being discarded</param>
|
||||||
public void DiscardData(Texture texture)
|
public void DiscardData(Texture texture)
|
||||||
{
|
{
|
||||||
EvaluateRelevantHandles(texture, (baseHandle, regionCount, _) =>
|
EvaluateRelevantHandles(texture, _discardDataCallback, out _);
|
||||||
{
|
}
|
||||||
for (int i = 0; i < regionCount; i++)
|
|
||||||
{
|
|
||||||
TextureGroupHandle group = _handles[baseHandle + i];
|
|
||||||
|
|
||||||
group.DiscardData();
|
bool DiscardDataCallback(int baseHandle, int regionCount, bool split, bool bound)
|
||||||
}
|
{
|
||||||
});
|
for (int i = 0; i < regionCount; i++)
|
||||||
|
{
|
||||||
|
TextureGroupHandle group = _handles[baseHandle + i];
|
||||||
|
|
||||||
|
group.DiscardData();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -308,7 +324,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
{
|
{
|
||||||
FlushIncompatibleOverlapsIfNeeded();
|
FlushIncompatibleOverlapsIfNeeded();
|
||||||
|
|
||||||
EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) =>
|
EvaluateRelevantHandles(texture, (baseHandle, regionCount, split, _) =>
|
||||||
{
|
{
|
||||||
bool dirty = false;
|
bool dirty = false;
|
||||||
bool anyModified = false;
|
bool anyModified = false;
|
||||||
|
|
@ -384,7 +400,9 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
texture.SynchronizeFull();
|
texture.SynchronizeFull();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
return true;
|
||||||
|
}, out _);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -461,7 +479,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
/// <param name="texture">The texture to synchronize dependents of</param>
|
/// <param name="texture">The texture to synchronize dependents of</param>
|
||||||
public void SynchronizeDependents(Texture texture)
|
public void SynchronizeDependents(Texture texture)
|
||||||
{
|
{
|
||||||
EvaluateRelevantHandles(texture, (baseHandle, regionCount, _) =>
|
EvaluateRelevantHandles(texture, (baseHandle, regionCount, _, _) =>
|
||||||
{
|
{
|
||||||
for (int i = 0; i < regionCount; i++)
|
for (int i = 0; i < regionCount; i++)
|
||||||
{
|
{
|
||||||
|
|
@ -469,7 +487,9 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
|
|
||||||
group.SynchronizeDependents();
|
group.SynchronizeDependents();
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
return true;
|
||||||
|
}, out _);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -551,7 +571,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
tracked = tracked || ShouldFlushTriggerTracking();
|
tracked = tracked || ShouldFlushTriggerTracking();
|
||||||
bool flushed = false;
|
bool flushed = false;
|
||||||
|
|
||||||
EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) =>
|
EvaluateRelevantHandles(texture, (baseHandle, regionCount, split, _) =>
|
||||||
{
|
{
|
||||||
int startSlice = 0;
|
int startSlice = 0;
|
||||||
int endSlice = 0;
|
int endSlice = 0;
|
||||||
|
|
@ -605,7 +625,9 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
|
|
||||||
flushed = true;
|
flushed = true;
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
return true;
|
||||||
|
}, out _);
|
||||||
|
|
||||||
Storage.SignalModifiedDirty();
|
Storage.SignalModifiedDirty();
|
||||||
|
|
||||||
|
|
@ -694,7 +716,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
|
|
||||||
ClearIncompatibleOverlaps(texture);
|
ClearIncompatibleOverlaps(texture);
|
||||||
|
|
||||||
EvaluateRelevantHandles(texture, (baseHandle, regionCount, _) =>
|
EvaluateRelevantHandles(texture, (baseHandle, regionCount, _, _) =>
|
||||||
{
|
{
|
||||||
for (int i = 0; i < regionCount; i++)
|
for (int i = 0; i < regionCount; i++)
|
||||||
{
|
{
|
||||||
|
|
@ -702,7 +724,9 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
|
|
||||||
group.SignalModified(_context);
|
group.SignalModified(_context);
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
return true;
|
||||||
|
}, out _);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -716,15 +740,19 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
|
|
||||||
ClearIncompatibleOverlaps(texture);
|
ClearIncompatibleOverlaps(texture);
|
||||||
|
|
||||||
EvaluateRelevantHandles(texture, (baseHandle, regionCount, _) =>
|
EvaluateRelevantHandles(texture, _signalModifyingCallback, out _, bound);
|
||||||
{
|
}
|
||||||
for (int i = 0; i < regionCount; i++)
|
|
||||||
{
|
|
||||||
TextureGroupHandle group = _handles[baseHandle + i];
|
|
||||||
|
|
||||||
group.SignalModifying(bound, _context);
|
bool SignalModifyingCallback(int baseHandle, int regionCount, bool split, bool bound)
|
||||||
}
|
{
|
||||||
});
|
for (int i = 0; i < regionCount; i++)
|
||||||
|
{
|
||||||
|
TextureGroupHandle group = _handles[baseHandle + i];
|
||||||
|
|
||||||
|
group.SignalModifying(bound, _context);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -768,16 +796,16 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
/// A function to be called with the base index of the range of handles for the given texture, and the number of handles it covers.
|
/// A function to be called with the base index of the range of handles for the given texture, and the number of handles it covers.
|
||||||
/// This can be called for multiple disjoint ranges, if required.
|
/// This can be called for multiple disjoint ranges, if required.
|
||||||
/// </param>
|
/// </param>
|
||||||
private void EvaluateRelevantHandles(Texture texture, HandlesCallbackDelegate callback)
|
private void EvaluateRelevantHandles(Texture texture, HandlesCallbackDelegate callback, out bool result, bool specialData = false)
|
||||||
{
|
{
|
||||||
if (texture == Storage || !(_hasMipViews || _hasLayerViews))
|
if (texture == Storage || !(_hasMipViews || _hasLayerViews))
|
||||||
{
|
{
|
||||||
callback(0, _handles.Length);
|
result = callback(0, _handles.Length, specialData: specialData);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
EvaluateRelevantHandles(texture.FirstLayer, texture.FirstLevel, texture.Info.GetSlices(), texture.Info.Levels, callback);
|
EvaluateRelevantHandles(texture.FirstLayer, texture.FirstLevel, texture.Info.GetSlices(), texture.Info.Levels, callback, out result, specialData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -792,11 +820,13 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
/// A function to be called with the base index of the range of handles for the given texture, and the number of handles it covers.
|
/// A function to be called with the base index of the range of handles for the given texture, and the number of handles it covers.
|
||||||
/// This can be called for multiple disjoint ranges, if required.
|
/// This can be called for multiple disjoint ranges, if required.
|
||||||
/// </param>
|
/// </param>
|
||||||
private void EvaluateRelevantHandles(int firstLayer, int firstLevel, int slices, int levels, HandlesCallbackDelegate callback)
|
private void EvaluateRelevantHandles(int firstLayer, int firstLevel, int slices, int levels, HandlesCallbackDelegate callback, out bool result, bool specialData = false)
|
||||||
{
|
{
|
||||||
int targetLayerHandles = _hasLayerViews ? slices : 1;
|
int targetLayerHandles = _hasLayerViews ? slices : 1;
|
||||||
int targetLevelHandles = _hasMipViews ? levels : 1;
|
int targetLevelHandles = _hasMipViews ? levels : 1;
|
||||||
|
|
||||||
|
result = false;
|
||||||
|
|
||||||
if (_isBuffer)
|
if (_isBuffer)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
|
|
@ -809,7 +839,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
{
|
{
|
||||||
// When there are no layer views, the mips are at a consistent offset.
|
// When there are no layer views, the mips are at a consistent offset.
|
||||||
|
|
||||||
callback(firstLevel, targetLevelHandles);
|
result = callback(firstLevel, targetLevelHandles, specialData: specialData);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -823,7 +853,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
|
|
||||||
while (levels-- > 1)
|
while (levels-- > 1)
|
||||||
{
|
{
|
||||||
callback(firstLayer + levelIndex, slices);
|
result = callback(firstLayer + levelIndex, slices, specialData: specialData);
|
||||||
|
|
||||||
levelIndex += layerCount;
|
levelIndex += layerCount;
|
||||||
layerCount = Math.Max(layerCount >> 1, 1);
|
layerCount = Math.Max(layerCount >> 1, 1);
|
||||||
|
|
@ -840,7 +870,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
totalSize += layerCount;
|
totalSize += layerCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
callback(firstLayer + levelIndex, totalSize);
|
result = callback(firstLayer + levelIndex, totalSize, specialData: specialData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -857,12 +887,12 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
|
|
||||||
for (int i = 0; i < slices; i++)
|
for (int i = 0; i < slices; i++)
|
||||||
{
|
{
|
||||||
callback(firstLevel + (firstLayer + i) * levelHandles, targetLevelHandles, true);
|
result = callback(firstLevel + (firstLayer + i) * levelHandles, targetLevelHandles, true, specialData: specialData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
callback(firstLevel + firstLayer * levelHandles, targetLevelHandles + (targetLayerHandles - 1) * levelHandles);
|
result = callback(firstLevel + firstLayer * levelHandles, targetLevelHandles + (targetLayerHandles - 1) * levelHandles, specialData: specialData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1441,8 +1471,16 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
var targetRange = new List<(int BaseHandle, int RegionCount)>();
|
var targetRange = new List<(int BaseHandle, int RegionCount)>();
|
||||||
var otherRange = new List<(int BaseHandle, int RegionCount)>();
|
var otherRange = new List<(int BaseHandle, int RegionCount)>();
|
||||||
|
|
||||||
EvaluateRelevantHandles(firstLayer, firstLevel, other.Info.GetSlices(), other.Info.Levels, (baseHandle, regionCount, _) => targetRange.Add((baseHandle, regionCount)));
|
EvaluateRelevantHandles(firstLayer, firstLevel, other.Info.GetSlices(), other.Info.Levels, (baseHandle, regionCount, _, _) =>
|
||||||
otherGroup.EvaluateRelevantHandles(other, (baseHandle, regionCount, _) => otherRange.Add((baseHandle, regionCount)));
|
{
|
||||||
|
targetRange.Add((baseHandle, regionCount));
|
||||||
|
return true;
|
||||||
|
}, out _);
|
||||||
|
otherGroup.EvaluateRelevantHandles(other, (baseHandle, regionCount, _, _) =>
|
||||||
|
{
|
||||||
|
otherRange.Add((baseHandle, regionCount));
|
||||||
|
return true;
|
||||||
|
}, out _);
|
||||||
|
|
||||||
int targetIndex = 0;
|
int targetIndex = 0;
|
||||||
int otherIndex = 0;
|
int otherIndex = 0;
|
||||||
|
|
|
||||||
|
|
@ -93,6 +93,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||||
private ulong _dirtyStart = ulong.MaxValue;
|
private ulong _dirtyStart = ulong.MaxValue;
|
||||||
private ulong _dirtyEnd = ulong.MaxValue;
|
private ulong _dirtyEnd = ulong.MaxValue;
|
||||||
|
|
||||||
|
private readonly Action<ulong, ulong> _syncPreRangeAction;
|
||||||
|
private readonly Action<ulong, ulong> _syncRangeAction;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new instance of the buffer.
|
/// Creates a new instance of the buffer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -177,6 +180,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||||
_modifiedDelegate = RegionModified;
|
_modifiedDelegate = RegionModified;
|
||||||
|
|
||||||
_virtualDependenciesLock = new ReaderWriterLockSlim();
|
_virtualDependenciesLock = new ReaderWriterLockSlim();
|
||||||
|
|
||||||
|
_syncPreRangeAction = SyncPreRangeAction;
|
||||||
|
_syncRangeAction = SyncRangeAction;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -401,14 +407,16 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||||
|
|
||||||
if (_preFlush.ShouldCopy)
|
if (_preFlush.ShouldCopy)
|
||||||
{
|
{
|
||||||
_modifiedRanges?.GetRangesAtSync(Address, Size, _context.SyncNumber, (address, size) =>
|
_modifiedRanges?.GetRangesAtSync(Address, Size, _context.SyncNumber, _syncPreRangeAction);
|
||||||
{
|
|
||||||
_preFlush.CopyModified(address, size);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SyncPreRangeAction(ulong address, ulong size)
|
||||||
|
{
|
||||||
|
_preFlush.CopyModified(address, size);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Action to be performed when a syncpoint is reached after modification.
|
/// Action to be performed when a syncpoint is reached after modification.
|
||||||
/// This will register read/write tracking to flush the buffer from GPU when its memory is used.
|
/// This will register read/write tracking to flush the buffer from GPU when its memory is used.
|
||||||
|
|
@ -420,11 +428,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||||
|
|
||||||
if (_useGranular)
|
if (_useGranular)
|
||||||
{
|
{
|
||||||
_modifiedRanges?.GetRanges(Address, Size, (address, size) =>
|
|
||||||
{
|
|
||||||
_memoryTrackingGranular.RegisterAction(address, size, _externalFlushDelegate);
|
_modifiedRanges?.GetRanges(Address, Size, _syncRangeAction);
|
||||||
SynchronizeMemory(address, size);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -435,6 +441,12 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SyncRangeAction(ulong address, ulong size)
|
||||||
|
{
|
||||||
|
_memoryTrackingGranular.RegisterAction(address, size, _externalFlushDelegate);
|
||||||
|
SynchronizeMemory(address, size);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Inherit modified and dirty ranges from another buffer.
|
/// Inherit modified and dirty ranges from another buffer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
using Ryujinx.Memory.Range;
|
using Ryujinx.Memory.Range;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Buffers;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu.Memory
|
namespace Ryujinx.Graphics.Gpu.Memory
|
||||||
|
|
@ -276,13 +277,18 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||||
{
|
{
|
||||||
// We use the non-span method here because keeping the lock will cause a deadlock.
|
// We use the non-span method here because keeping the lock will cause a deadlock.
|
||||||
Lock.EnterReadLock();
|
Lock.EnterReadLock();
|
||||||
RangeItem<BufferModifiedRange>[] overlaps = FindOverlapsAsArray(address, size);
|
RangeItem<BufferModifiedRange>[] overlaps = FindOverlapsAsArray(address, size, out int length);
|
||||||
Lock.ExitReadLock();
|
Lock.ExitReadLock();
|
||||||
|
|
||||||
for (int i = 0; i < overlaps.Length; i++)
|
if (length != 0)
|
||||||
{
|
{
|
||||||
BufferModifiedRange overlap = overlaps[i].Value;
|
for (int i = 0; i < length; i++)
|
||||||
rangeAction(overlap.Address, overlap.Size);
|
{
|
||||||
|
BufferModifiedRange overlap = overlaps[i].Value;
|
||||||
|
rangeAction(overlap.Address, overlap.Size);
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayPool<RangeItem<BufferModifiedRange>>.Shared.Return(overlaps);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -392,9 +398,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||||
|
|
||||||
Lock.EnterWriteLock();
|
Lock.EnterWriteLock();
|
||||||
// We use the non-span method here because the array is partially modified by the code, which would invalidate a span.
|
// We use the non-span method here because the array is partially modified by the code, which would invalidate a span.
|
||||||
RangeItem<BufferModifiedRange>[] overlaps = FindOverlapsAsArray(address, size);
|
RangeItem<BufferModifiedRange>[] overlaps = FindOverlapsAsArray(address, size, out int rangeCount);
|
||||||
|
|
||||||
int rangeCount = overlaps.Length;
|
|
||||||
|
|
||||||
if (rangeCount == 0)
|
if (rangeCount == 0)
|
||||||
{
|
{
|
||||||
|
|
@ -410,7 +414,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||||
|
|
||||||
for (int i = 0; i < rangeCount; i++)
|
for (int i = 0; i < rangeCount; i++)
|
||||||
{
|
{
|
||||||
BufferModifiedRange overlap = overlaps[i].Value;
|
BufferModifiedRange overlap = overlaps![i].Value;
|
||||||
|
|
||||||
long diff = (long)(overlap.SyncNumber - currentSync);
|
long diff = (long)(overlap.SyncNumber - currentSync);
|
||||||
|
|
||||||
|
|
@ -430,7 +434,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||||
// Wait for the syncpoint.
|
// Wait for the syncpoint.
|
||||||
_context.Renderer.WaitSync(currentSync + (ulong)highestDiff);
|
_context.Renderer.WaitSync(currentSync + (ulong)highestDiff);
|
||||||
|
|
||||||
RemoveRangesAndFlush(overlaps.ToArray(), rangeCount, highestDiff, currentSync, address, endAddress);
|
RemoveRangesAndFlush(overlaps, rangeCount, highestDiff, currentSync, address, endAddress);
|
||||||
|
|
||||||
|
ArrayPool<RangeItem<BufferModifiedRange>>.Shared.Return(overlaps!);
|
||||||
|
|
||||||
Lock.ExitWriteLock();
|
Lock.ExitWriteLock();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -395,7 +395,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||||
/// <returns>True if queried, false otherwise</returns>
|
/// <returns>True if queried, false otherwise</returns>
|
||||||
public bool IsPrimitiveTopologyQueried()
|
public bool IsPrimitiveTopologyQueried()
|
||||||
{
|
{
|
||||||
return _queriedState.HasFlag(QueriedStateFlags.PrimitiveTopology);
|
return (_queriedState & QueriedStateFlags.PrimitiveTopology) == QueriedStateFlags.PrimitiveTopology;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -902,7 +902,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||||
specState.PipelineState = pipelineState;
|
specState.PipelineState = pipelineState;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (specState._queriedState.HasFlag(QueriedStateFlags.TransformFeedback))
|
if ((specState._queriedState & QueriedStateFlags.TransformFeedback) == QueriedStateFlags.TransformFeedback)
|
||||||
{
|
{
|
||||||
ushort tfCount = 0;
|
ushort tfCount = 0;
|
||||||
dataReader.Read(ref tfCount);
|
dataReader.Read(ref tfCount);
|
||||||
|
|
@ -928,7 +928,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||||
specState._textureSpecialization[textureKey] = textureState;
|
specState._textureSpecialization[textureKey] = textureState;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (specState._queriedState.HasFlag(QueriedStateFlags.TextureArrayFromBuffer))
|
if ((specState._queriedState & QueriedStateFlags.TextureArrayFromBuffer) == QueriedStateFlags.TextureArrayFromBuffer)
|
||||||
{
|
{
|
||||||
dataReader.Read(ref count);
|
dataReader.Read(ref count);
|
||||||
|
|
||||||
|
|
@ -944,7 +944,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (specState._queriedState.HasFlag(QueriedStateFlags.TextureArrayFromPool))
|
if ((specState._queriedState & QueriedStateFlags.TextureArrayFromPool) == QueriedStateFlags.TextureArrayFromPool)
|
||||||
{
|
{
|
||||||
dataReader.Read(ref count);
|
dataReader.Read(ref count);
|
||||||
|
|
||||||
|
|
@ -1007,7 +1007,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_queriedState.HasFlag(QueriedStateFlags.TransformFeedback))
|
if ((_queriedState & QueriedStateFlags.TransformFeedback) == QueriedStateFlags.TransformFeedback)
|
||||||
{
|
{
|
||||||
ushort tfCount = (ushort)TransformFeedbackDescriptors.Length;
|
ushort tfCount = (ushort)TransformFeedbackDescriptors.Length;
|
||||||
dataWriter.Write(ref tfCount);
|
dataWriter.Write(ref tfCount);
|
||||||
|
|
@ -1030,7 +1030,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||||
dataWriter.WriteWithMagicAndSize(ref textureState.Value, TexsMagic);
|
dataWriter.WriteWithMagicAndSize(ref textureState.Value, TexsMagic);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_queriedState.HasFlag(QueriedStateFlags.TextureArrayFromBuffer))
|
if ((_queriedState & QueriedStateFlags.TextureArrayFromBuffer) == QueriedStateFlags.TextureArrayFromBuffer)
|
||||||
{
|
{
|
||||||
count = (ushort)_textureArrayFromBufferSpecialization.Count;
|
count = (ushort)_textureArrayFromBufferSpecialization.Count;
|
||||||
dataWriter.Write(ref count);
|
dataWriter.Write(ref count);
|
||||||
|
|
@ -1045,7 +1045,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_queriedState.HasFlag(QueriedStateFlags.TextureArrayFromPool))
|
if ((_queriedState & QueriedStateFlags.TextureArrayFromPool) == QueriedStateFlags.TextureArrayFromPool)
|
||||||
{
|
{
|
||||||
count = (ushort)_textureArrayFromPoolSpecialization.Count;
|
count = (ushort)_textureArrayFromPoolSpecialization.Count;
|
||||||
dataWriter.Write(ref count);
|
dataWriter.Write(ref count);
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
private Dictionary<ulong, StagingBufferReserved> _mirrors;
|
private Dictionary<ulong, StagingBufferReserved> _mirrors;
|
||||||
private bool _useMirrors;
|
private bool _useMirrors;
|
||||||
|
|
||||||
|
private Action _decrementReferenceCount;
|
||||||
|
|
||||||
public BufferHolder(VulkanRenderer gd, Device device, VkBuffer buffer, MemoryAllocation allocation, int size, BufferAllocationType type, BufferAllocationType currentType)
|
public BufferHolder(VulkanRenderer gd, Device device, VkBuffer buffer, MemoryAllocation allocation, int size, BufferAllocationType type, BufferAllocationType currentType)
|
||||||
{
|
{
|
||||||
_gd = gd;
|
_gd = gd;
|
||||||
|
|
@ -75,6 +77,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
|
|
||||||
_flushLock = new ReaderWriterLockSlim();
|
_flushLock = new ReaderWriterLockSlim();
|
||||||
_useMirrors = gd.IsTBDR;
|
_useMirrors = gd.IsTBDR;
|
||||||
|
|
||||||
|
_decrementReferenceCount = _buffer.DecrementReferenceCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BufferHolder(VulkanRenderer gd, Device device, VkBuffer buffer, Auto<MemoryAllocation> allocation, int size, BufferAllocationType type, BufferAllocationType currentType, int offset)
|
public BufferHolder(VulkanRenderer gd, Device device, VkBuffer buffer, Auto<MemoryAllocation> allocation, int size, BufferAllocationType type, BufferAllocationType currentType, int offset)
|
||||||
|
|
@ -444,7 +448,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
|
|
||||||
_flushLock.ExitReadLock();
|
_flushLock.ExitReadLock();
|
||||||
|
|
||||||
return PinnedSpan<byte>.UnsafeFromSpan(result, _buffer.DecrementReferenceCount);
|
return PinnedSpan<byte>.UnsafeFromSpan(result, _decrementReferenceCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
BackgroundResource resource = _gd.BackgroundResources.Get();
|
BackgroundResource resource = _gd.BackgroundResources.Get();
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
using Silk.NET.Vulkan;
|
using Silk.NET.Vulkan;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Format = Ryujinx.Graphics.GAL.Format;
|
||||||
using VkFormat = Silk.NET.Vulkan.Format;
|
using VkFormat = Silk.NET.Vulkan.Format;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Vulkan
|
namespace Ryujinx.Graphics.Vulkan
|
||||||
|
|
@ -9,26 +11,27 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
class FramebufferParams
|
class FramebufferParams
|
||||||
{
|
{
|
||||||
private readonly Device _device;
|
private readonly Device _device;
|
||||||
private readonly Auto<DisposableImageView>[] _attachments;
|
private Auto<DisposableImageView>[] _attachments;
|
||||||
private readonly TextureView[] _colors;
|
private TextureView[] _colors;
|
||||||
private readonly TextureView _depthStencil;
|
private TextureView _depthStencil;
|
||||||
private readonly TextureView[] _colorsCanonical;
|
private TextureView[] _colorsCanonical;
|
||||||
private readonly TextureView _baseAttachment;
|
private TextureView _baseAttachment;
|
||||||
private readonly uint _validColorAttachments;
|
private uint _validColorAttachments;
|
||||||
|
private int _totalCount;
|
||||||
|
|
||||||
public uint Width { get; }
|
public uint Width { get; private set; }
|
||||||
public uint Height { get; }
|
public uint Height { get; private set; }
|
||||||
public uint Layers { get; }
|
public uint Layers { get; private set; }
|
||||||
|
|
||||||
public uint[] AttachmentSamples { get; }
|
public uint[] AttachmentSamples { get; private set; }
|
||||||
public VkFormat[] AttachmentFormats { get; }
|
public VkFormat[] AttachmentFormats { get; private set; }
|
||||||
public int[] AttachmentIndices { get; }
|
public int[] AttachmentIndices { get; private set; }
|
||||||
public uint AttachmentIntegerFormatMask { get; }
|
public uint AttachmentIntegerFormatMask { get; private set; }
|
||||||
public bool LogicOpsAllowed { get; }
|
public bool LogicOpsAllowed { get; private set; }
|
||||||
|
|
||||||
public int AttachmentsCount { get; }
|
public int AttachmentsCount { get; private set; }
|
||||||
public int MaxColorAttachmentIndex => AttachmentIndices.Length > 0 ? AttachmentIndices[^1] : -1;
|
public int MaxColorAttachmentIndex => ColorAttachmentsCount > 0 ? AttachmentIndices[ColorAttachmentsCount - 1] : -1;
|
||||||
public bool HasDepthStencil { get; }
|
public bool HasDepthStencil { get; private set; }
|
||||||
public int ColorAttachmentsCount => AttachmentsCount - (HasDepthStencil ? 1 : 0);
|
public int ColorAttachmentsCount => AttachmentsCount - (HasDepthStencil ? 1 : 0);
|
||||||
|
|
||||||
public FramebufferParams(Device device, TextureView view, uint width, uint height)
|
public FramebufferParams(Device device, TextureView view, uint width, uint height)
|
||||||
|
|
@ -49,7 +52,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_colors = [view];
|
_colors = [view];
|
||||||
_colorsCanonical = _colors;
|
_colorsCanonical = [view];
|
||||||
}
|
}
|
||||||
|
|
||||||
Width = width;
|
Width = width;
|
||||||
|
|
@ -63,6 +66,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
LogicOpsAllowed = !format.IsFloatOrSrgb();
|
LogicOpsAllowed = !format.IsFloatOrSrgb();
|
||||||
|
|
||||||
AttachmentsCount = 1;
|
AttachmentsCount = 1;
|
||||||
|
_totalCount = 1;
|
||||||
|
|
||||||
HasDepthStencil = isDepthStencil;
|
HasDepthStencil = isDepthStencil;
|
||||||
}
|
}
|
||||||
|
|
@ -132,7 +136,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
AttachmentIntegerFormatMask = attachmentIntegerFormatMask;
|
AttachmentIntegerFormatMask = attachmentIntegerFormatMask;
|
||||||
LogicOpsAllowed = !allFormatsFloatOrSrgb;
|
LogicOpsAllowed = !allFormatsFloatOrSrgb;
|
||||||
|
|
||||||
if (depthStencil is TextureView dsTexture && dsTexture.Valid)
|
if (depthStencil is TextureView { Valid: true } dsTexture)
|
||||||
{
|
{
|
||||||
_attachments[count - 1] = dsTexture.GetImageViewForAttachment();
|
_attachments[count - 1] = dsTexture.GetImageViewForAttachment();
|
||||||
_depthStencil = dsTexture;
|
_depthStencil = dsTexture;
|
||||||
|
|
@ -158,11 +162,151 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
Layers = layers;
|
Layers = layers;
|
||||||
|
|
||||||
AttachmentsCount = count;
|
AttachmentsCount = count;
|
||||||
|
_totalCount = colors.Length;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FramebufferParams Update(ITexture[] colors, ITexture depthStencil)
|
||||||
|
{
|
||||||
|
int colorsCount = colors.Count(IsValidTextureView);
|
||||||
|
|
||||||
|
int count = colorsCount + (IsValidTextureView(depthStencil) ? 1 : 0);
|
||||||
|
|
||||||
|
Array.Clear(_attachments);
|
||||||
|
Array.Clear(_colors);
|
||||||
|
|
||||||
|
if (_attachments.Length < count)
|
||||||
|
{
|
||||||
|
Array.Resize(ref _attachments, count);
|
||||||
|
}
|
||||||
|
if (_colors.Length < colorsCount)
|
||||||
|
{
|
||||||
|
Array.Resize(ref _colors, colorsCount);
|
||||||
|
}
|
||||||
|
if (_colorsCanonical.Length < colors.Length)
|
||||||
|
{
|
||||||
|
Array.Resize(ref _colorsCanonical, colors.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < colors.Length; i++)
|
||||||
|
{
|
||||||
|
ITexture color = colors[i];
|
||||||
|
if (color is TextureView { Valid: true } view)
|
||||||
|
{
|
||||||
|
_colorsCanonical[i] = view;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_colorsCanonical[i] = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Array.Clear(AttachmentSamples);
|
||||||
|
Array.Clear(AttachmentFormats);
|
||||||
|
Array.Clear(AttachmentIndices);
|
||||||
|
|
||||||
|
if (AttachmentSamples.Length < count)
|
||||||
|
{
|
||||||
|
uint[] attachmentSamples = AttachmentSamples;
|
||||||
|
Array.Resize(ref attachmentSamples, count);
|
||||||
|
AttachmentSamples = attachmentSamples;
|
||||||
|
}
|
||||||
|
if (AttachmentFormats.Length < count)
|
||||||
|
{
|
||||||
|
VkFormat[] attachmentFormats = AttachmentFormats;
|
||||||
|
Array.Resize(ref attachmentFormats, count);
|
||||||
|
AttachmentFormats = attachmentFormats;
|
||||||
|
}
|
||||||
|
if (AttachmentIndices.Length < colorsCount)
|
||||||
|
{
|
||||||
|
int[] attachmentIndices = AttachmentIndices;
|
||||||
|
Array.Resize(ref attachmentIndices, colorsCount);
|
||||||
|
AttachmentIndices = attachmentIndices;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint width = uint.MaxValue;
|
||||||
|
uint height = uint.MaxValue;
|
||||||
|
uint layers = uint.MaxValue;
|
||||||
|
|
||||||
|
int index = 0;
|
||||||
|
uint attachmentIntegerFormatMask = 0;
|
||||||
|
bool allFormatsFloatOrSrgb = colorsCount != 0;
|
||||||
|
|
||||||
|
_validColorAttachments = 0;
|
||||||
|
_baseAttachment = null;
|
||||||
|
|
||||||
|
for (int bindIndex = 0; bindIndex < colors.Length; bindIndex++)
|
||||||
|
{
|
||||||
|
TextureView texture = _colorsCanonical[bindIndex];
|
||||||
|
if (texture is not null)
|
||||||
|
{
|
||||||
|
_attachments[index] = texture.GetImageViewForAttachment();
|
||||||
|
_colors[index] = texture;
|
||||||
|
_validColorAttachments |= 1u << bindIndex;
|
||||||
|
_baseAttachment = texture;
|
||||||
|
|
||||||
|
AttachmentSamples[index] = (uint)texture.Info.Samples;
|
||||||
|
AttachmentFormats[index] = texture.VkFormat;
|
||||||
|
AttachmentIndices[index] = bindIndex;
|
||||||
|
|
||||||
|
Format format = texture.Info.Format;
|
||||||
|
|
||||||
|
if (format.IsInteger())
|
||||||
|
{
|
||||||
|
attachmentIntegerFormatMask |= 1u << bindIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
allFormatsFloatOrSrgb &= format.IsFloatOrSrgb();
|
||||||
|
|
||||||
|
width = Math.Min(width, (uint)texture.Width);
|
||||||
|
height = Math.Min(height, (uint)texture.Height);
|
||||||
|
layers = Math.Min(layers, (uint)texture.Layers);
|
||||||
|
|
||||||
|
if (++index >= colorsCount)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AttachmentIntegerFormatMask = attachmentIntegerFormatMask;
|
||||||
|
LogicOpsAllowed = !allFormatsFloatOrSrgb;
|
||||||
|
_depthStencil = null;
|
||||||
|
HasDepthStencil = false;
|
||||||
|
|
||||||
|
if (depthStencil is TextureView { Valid: true } dsTexture)
|
||||||
|
{
|
||||||
|
_attachments[count - 1] = dsTexture.GetImageViewForAttachment();
|
||||||
|
_depthStencil = dsTexture;
|
||||||
|
_baseAttachment ??= dsTexture;
|
||||||
|
|
||||||
|
AttachmentSamples[count - 1] = (uint)dsTexture.Info.Samples;
|
||||||
|
AttachmentFormats[count - 1] = dsTexture.VkFormat;
|
||||||
|
|
||||||
|
width = Math.Min(width, (uint)dsTexture.Width);
|
||||||
|
height = Math.Min(height, (uint)dsTexture.Height);
|
||||||
|
layers = Math.Min(layers, (uint)dsTexture.Layers);
|
||||||
|
|
||||||
|
HasDepthStencil = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count == 0)
|
||||||
|
{
|
||||||
|
width = height = layers = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Width = width;
|
||||||
|
Height = height;
|
||||||
|
Layers = layers;
|
||||||
|
|
||||||
|
AttachmentsCount = count;
|
||||||
|
_totalCount = colors.Length;
|
||||||
|
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Auto<DisposableImageView> GetAttachment(int index)
|
public Auto<DisposableImageView> GetAttachment(int index)
|
||||||
{
|
{
|
||||||
if ((uint)index >= _attachments.Length)
|
if ((uint)index >= AttachmentsCount)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
@ -182,7 +326,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
|
|
||||||
public ComponentType GetAttachmentComponentType(int index)
|
public ComponentType GetAttachmentComponentType(int index)
|
||||||
{
|
{
|
||||||
if (_colors != null && (uint)index < _colors.Length)
|
if (_colors != null && (uint)index < ColorAttachmentsCount)
|
||||||
{
|
{
|
||||||
var format = _colors[index].Info.Format;
|
var format = _colors[index].Info.Format;
|
||||||
|
|
||||||
|
|
@ -217,7 +361,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
|
|
||||||
private static bool IsValidTextureView(ITexture texture)
|
private static bool IsValidTextureView(ITexture texture)
|
||||||
{
|
{
|
||||||
return texture is TextureView view && view.Valid;
|
return texture is TextureView { Valid: true };
|
||||||
}
|
}
|
||||||
|
|
||||||
public ClearRect GetClearRect(Rectangle<int> scissor, int layer, int layerCount)
|
public ClearRect GetClearRect(Rectangle<int> scissor, int layer, int layerCount)
|
||||||
|
|
@ -232,9 +376,9 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
|
|
||||||
public unsafe Auto<DisposableFramebuffer> Create(Vk api, CommandBufferScoped cbs, Auto<DisposableRenderPass> renderPass)
|
public unsafe Auto<DisposableFramebuffer> Create(Vk api, CommandBufferScoped cbs, Auto<DisposableRenderPass> renderPass)
|
||||||
{
|
{
|
||||||
ImageView* attachments = stackalloc ImageView[_attachments.Length];
|
ImageView* attachments = stackalloc ImageView[AttachmentsCount];
|
||||||
|
|
||||||
for (int i = 0; i < _attachments.Length; i++)
|
for (int i = 0; i < AttachmentsCount; i++)
|
||||||
{
|
{
|
||||||
attachments[i] = _attachments[i].Get(cbs).Value;
|
attachments[i] = _attachments[i].Get(cbs).Value;
|
||||||
}
|
}
|
||||||
|
|
@ -243,7 +387,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
SType = StructureType.FramebufferCreateInfo,
|
SType = StructureType.FramebufferCreateInfo,
|
||||||
RenderPass = renderPass.Get(cbs).Value,
|
RenderPass = renderPass.Get(cbs).Value,
|
||||||
AttachmentCount = (uint)_attachments.Length,
|
AttachmentCount = (uint)AttachmentsCount,
|
||||||
PAttachments = attachments,
|
PAttachments = attachments,
|
||||||
Width = Width,
|
Width = Width,
|
||||||
Height = Height,
|
Height = Height,
|
||||||
|
|
@ -251,14 +395,13 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
};
|
};
|
||||||
|
|
||||||
api.CreateFramebuffer(_device, in framebufferCreateInfo, null, out var framebuffer).ThrowOnError();
|
api.CreateFramebuffer(_device, in framebufferCreateInfo, null, out var framebuffer).ThrowOnError();
|
||||||
return new Auto<DisposableFramebuffer>(new DisposableFramebuffer(api, _device, framebuffer), null, _attachments);
|
return new Auto<DisposableFramebuffer>(new DisposableFramebuffer(api, _device, framebuffer), null, _attachments[..AttachmentsCount]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public TextureView[] GetAttachmentViews()
|
public TextureView[] GetAttachmentViews()
|
||||||
{
|
{
|
||||||
var result = new TextureView[_attachments.Length];
|
var result = new TextureView[AttachmentsCount];
|
||||||
|
_colors?.AsSpan(..ColorAttachmentsCount).CopyTo(result.AsSpan());
|
||||||
_colors?.CopyTo(result, 0);
|
|
||||||
|
|
||||||
if (_depthStencil != null)
|
if (_depthStencil != null)
|
||||||
{
|
{
|
||||||
|
|
@ -277,8 +420,11 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
if (_colors != null)
|
if (_colors != null)
|
||||||
{
|
{
|
||||||
foreach (var color in _colors)
|
int count = ColorAttachmentsCount;
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
|
TextureView color = _colors[i];
|
||||||
// If Clear or DontCare were used, this would need to be write bit.
|
// If Clear or DontCare were used, this would need to be write bit.
|
||||||
color.Storage?.QueueLoadOpBarrier(cbs, false);
|
color.Storage?.QueueLoadOpBarrier(cbs, false);
|
||||||
}
|
}
|
||||||
|
|
@ -293,8 +439,11 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
if (_colors != null)
|
if (_colors != null)
|
||||||
{
|
{
|
||||||
foreach (var color in _colors)
|
int count = ColorAttachmentsCount;
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
|
TextureView color = _colors[i];
|
||||||
color.Storage?.AddStoreOpUsage(false);
|
color.Storage?.AddStoreOpUsage(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -306,7 +455,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
_depthStencil?.Storage.ClearBindings();
|
_depthStencil?.Storage.ClearBindings();
|
||||||
|
|
||||||
for (int i = 0; i < _colorsCanonical.Length; i++)
|
for (int i = 0; i < _totalCount; i++)
|
||||||
{
|
{
|
||||||
_colorsCanonical[i]?.Storage.ClearBindings();
|
_colorsCanonical[i]?.Storage.ClearBindings();
|
||||||
}
|
}
|
||||||
|
|
@ -316,7 +465,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
_depthStencil?.Storage.AddBinding(_depthStencil);
|
_depthStencil?.Storage.AddBinding(_depthStencil);
|
||||||
|
|
||||||
for (int i = 0; i < _colorsCanonical.Length; i++)
|
for (int i = 0; i < _totalCount; i++)
|
||||||
{
|
{
|
||||||
TextureView color = _colorsCanonical[i];
|
TextureView color = _colorsCanonical[i];
|
||||||
color?.Storage.AddBinding(color);
|
color?.Storage.AddBinding(color);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
using Ryujinx.Common.Memory;
|
using Ryujinx.Common.Memory;
|
||||||
using Silk.NET.Vulkan;
|
using Silk.NET.Vulkan;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Buffers;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Vulkan
|
namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
|
|
@ -11,7 +12,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
private const int BufferUsageTrackingGranularity = 4096;
|
private const int BufferUsageTrackingGranularity = 4096;
|
||||||
|
|
||||||
private readonly FenceHolder[] _fences;
|
public FenceHolder[] Fences { get; }
|
||||||
private readonly BufferUsageBitmap _bufferUsageBitmap;
|
private readonly BufferUsageBitmap _bufferUsageBitmap;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -19,7 +20,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public MultiFenceHolder()
|
public MultiFenceHolder()
|
||||||
{
|
{
|
||||||
_fences = new FenceHolder[CommandBufferPool.MaxCommandBuffers];
|
Fences = ArrayPool<FenceHolder>.Shared.Rent(CommandBufferPool.MaxCommandBuffers);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -28,7 +29,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
/// <param name="size">Size of the buffer</param>
|
/// <param name="size">Size of the buffer</param>
|
||||||
public MultiFenceHolder(int size)
|
public MultiFenceHolder(int size)
|
||||||
{
|
{
|
||||||
_fences = new FenceHolder[CommandBufferPool.MaxCommandBuffers];
|
Fences = ArrayPool<FenceHolder>.Shared.Rent(CommandBufferPool.MaxCommandBuffers);
|
||||||
_bufferUsageBitmap = new BufferUsageBitmap(size, BufferUsageTrackingGranularity);
|
_bufferUsageBitmap = new BufferUsageBitmap(size, BufferUsageTrackingGranularity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -90,7 +91,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
/// <returns>True if the command buffer's previous fence value was null</returns>
|
/// <returns>True if the command buffer's previous fence value was null</returns>
|
||||||
public bool AddFence(int cbIndex, FenceHolder fence)
|
public bool AddFence(int cbIndex, FenceHolder fence)
|
||||||
{
|
{
|
||||||
ref FenceHolder fenceRef = ref _fences[cbIndex];
|
ref FenceHolder fenceRef = ref Fences[cbIndex];
|
||||||
|
|
||||||
if (fenceRef == null)
|
if (fenceRef == null)
|
||||||
{
|
{
|
||||||
|
|
@ -107,7 +108,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
/// <param name="cbIndex">Command buffer index of the command buffer that owns the fence</param>
|
/// <param name="cbIndex">Command buffer index of the command buffer that owns the fence</param>
|
||||||
public void RemoveFence(int cbIndex)
|
public void RemoveFence(int cbIndex)
|
||||||
{
|
{
|
||||||
_fences[cbIndex] = null;
|
Fences[cbIndex] = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -117,7 +118,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
/// <returns>True if referenced, false otherwise</returns>
|
/// <returns>True if referenced, false otherwise</returns>
|
||||||
public bool HasFence(int cbIndex)
|
public bool HasFence(int cbIndex)
|
||||||
{
|
{
|
||||||
return _fences[cbIndex] != null;
|
return Fences[cbIndex] != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -227,9 +228,9 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
int count = 0;
|
int count = 0;
|
||||||
|
|
||||||
for (int i = 0; i < _fences.Length; i++)
|
for (int i = 0; i < Fences.Length; i++)
|
||||||
{
|
{
|
||||||
var fence = _fences[i];
|
var fence = Fences[i];
|
||||||
|
|
||||||
if (fence != null)
|
if (fence != null)
|
||||||
{
|
{
|
||||||
|
|
@ -251,9 +252,9 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
int count = 0;
|
int count = 0;
|
||||||
|
|
||||||
for (int i = 0; i < _fences.Length; i++)
|
for (int i = 0; i < Fences.Length; i++)
|
||||||
{
|
{
|
||||||
var fence = _fences[i];
|
var fence = Fences[i];
|
||||||
|
|
||||||
if (fence != null && _bufferUsageBitmap.OverlapsWith(i, offset, size))
|
if (fence != null && _bufferUsageBitmap.OverlapsWith(i, offset, size))
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1448,7 +1448,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
FramebufferParams?.ClearBindings();
|
FramebufferParams?.ClearBindings();
|
||||||
}
|
}
|
||||||
|
|
||||||
FramebufferParams = new FramebufferParams(Device, colors, depthStencil);
|
FramebufferParams = FramebufferParams?.Update(colors, depthStencil) ?? new FramebufferParams(Device, colors, depthStencil);
|
||||||
|
|
||||||
if (IsMainPipeline)
|
if (IsMainPipeline)
|
||||||
{
|
{
|
||||||
|
|
@ -1466,18 +1466,18 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
protected void UpdatePipelineAttachmentFormats()
|
protected void UpdatePipelineAttachmentFormats()
|
||||||
{
|
{
|
||||||
var dstAttachmentFormats = _newState.Internal.AttachmentFormats.AsSpan();
|
var dstAttachmentFormats = _newState.Internal.AttachmentFormats.AsSpan();
|
||||||
FramebufferParams.AttachmentFormats.CopyTo(dstAttachmentFormats);
|
FramebufferParams.AttachmentFormats.AsSpan(..FramebufferParams.AttachmentsCount).CopyTo(dstAttachmentFormats);
|
||||||
_newState.Internal.AttachmentIntegerFormatMask = FramebufferParams.AttachmentIntegerFormatMask;
|
_newState.Internal.AttachmentIntegerFormatMask = FramebufferParams.AttachmentIntegerFormatMask;
|
||||||
_newState.Internal.LogicOpsAllowed = FramebufferParams.LogicOpsAllowed;
|
_newState.Internal.LogicOpsAllowed = FramebufferParams.LogicOpsAllowed;
|
||||||
|
|
||||||
for (int i = FramebufferParams.AttachmentFormats.Length; i < dstAttachmentFormats.Length; i++)
|
for (int i = FramebufferParams.AttachmentsCount; i < dstAttachmentFormats.Length; i++)
|
||||||
{
|
{
|
||||||
dstAttachmentFormats[i] = 0;
|
dstAttachmentFormats[i] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
_newState.ColorBlendAttachmentStateCount = (uint)(FramebufferParams.MaxColorAttachmentIndex + 1);
|
_newState.ColorBlendAttachmentStateCount = (uint)(FramebufferParams.MaxColorAttachmentIndex + 1);
|
||||||
_newState.HasDepthStencil = FramebufferParams.HasDepthStencil;
|
_newState.HasDepthStencil = FramebufferParams.HasDepthStencil;
|
||||||
_newState.SamplesCount = FramebufferParams.AttachmentSamples.Length != 0 ? FramebufferParams.AttachmentSamples[0] : 1;
|
_newState.SamplesCount = FramebufferParams.AttachmentsCount != 0 ? FramebufferParams.AttachmentSamples[0] : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected unsafe void CreateRenderPass()
|
protected unsafe void CreateRenderPass()
|
||||||
|
|
|
||||||
|
|
@ -182,17 +182,16 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
if (_forcedFences.Count > 0)
|
if (_forcedFences.Count > 0)
|
||||||
{
|
{
|
||||||
_forcedFences.RemoveAll((entry) =>
|
for (int i = 0; i < _forcedFences.Count; i++)
|
||||||
{
|
{
|
||||||
if (entry.Texture.Disposed)
|
if (_forcedFences[i].Texture.Disposed)
|
||||||
{
|
{
|
||||||
return true;
|
_forcedFences.RemoveAt(i--);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
entry.Texture.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, entry.StageFlags);
|
_forcedFences[i].Texture.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, _forcedFences[i].StageFlags);
|
||||||
|
}
|
||||||
return false;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Silk.NET.Vulkan;
|
using Silk.NET.Vulkan;
|
||||||
|
using System.Buffers;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
@ -192,6 +193,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
_firstHandle = first.ID + 1;
|
_firstHandle = first.ID + 1;
|
||||||
_handles.RemoveAt(0);
|
_handles.RemoveAt(0);
|
||||||
|
ArrayPool<FenceHolder>.Shared.Return(first.Waitable.Fences);
|
||||||
first.Waitable = null;
|
first.Waitable = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
using Ryujinx.Common;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
|
@ -5,6 +6,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
|
||||||
{
|
{
|
||||||
class KSynchronizationObject : KAutoObject
|
class KSynchronizationObject : KAutoObject
|
||||||
{
|
{
|
||||||
|
private static readonly ObjectPool<LinkedListNode<KThread>> _nodePool = new(() => new LinkedListNode<KThread>(null));
|
||||||
|
|
||||||
public LinkedList<KThread> WaitingThreads { get; }
|
public LinkedList<KThread> WaitingThreads { get; }
|
||||||
|
|
||||||
public KSynchronizationObject(KernelContext context) : base(context)
|
public KSynchronizationObject(KernelContext context) : base(context)
|
||||||
|
|
@ -14,12 +17,16 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
|
||||||
|
|
||||||
public LinkedListNode<KThread> AddWaitingThread(KThread thread)
|
public LinkedListNode<KThread> AddWaitingThread(KThread thread)
|
||||||
{
|
{
|
||||||
return WaitingThreads.AddLast(thread);
|
LinkedListNode<KThread> node = _nodePool.Allocate();
|
||||||
|
node.Value = thread;
|
||||||
|
WaitingThreads.AddLast(node);
|
||||||
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveWaitingThread(LinkedListNode<KThread> node)
|
public void RemoveWaitingThread(LinkedListNode<KThread> node)
|
||||||
{
|
{
|
||||||
WaitingThreads.Remove(node);
|
WaitingThreads.Remove(node);
|
||||||
|
_nodePool.Release(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void Signal()
|
public virtual void Signal()
|
||||||
|
|
|
||||||
|
|
@ -110,7 +110,22 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
{
|
{
|
||||||
ulong size = PagesCount * KPageTableBase.PageSize;
|
ulong size = PagesCount * KPageTableBase.PageSize;
|
||||||
|
|
||||||
return new KMemoryInfo(
|
return KMemoryInfo.Pool.Allocate().Set(
|
||||||
|
BaseAddress,
|
||||||
|
size,
|
||||||
|
State,
|
||||||
|
Permission,
|
||||||
|
Attribute,
|
||||||
|
SourcePermission,
|
||||||
|
IpcRefCount,
|
||||||
|
DeviceRefCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
public KMemoryInfo GetInfo(KMemoryInfo oldInfo)
|
||||||
|
{
|
||||||
|
ulong size = PagesCount * KPageTableBase.PageSize;
|
||||||
|
|
||||||
|
return oldInfo.Set(
|
||||||
BaseAddress,
|
BaseAddress,
|
||||||
size,
|
size,
|
||||||
State,
|
State,
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,23 @@
|
||||||
|
using Ryujinx.Common;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
{
|
{
|
||||||
class KMemoryInfo
|
class KMemoryInfo
|
||||||
{
|
{
|
||||||
public ulong Address { get; }
|
public static readonly ObjectPool<KMemoryInfo> Pool = new(() => new KMemoryInfo());
|
||||||
public ulong Size { get; }
|
|
||||||
|
|
||||||
public MemoryState State { get; }
|
public ulong Address { get; private set; }
|
||||||
public KMemoryPermission Permission { get; }
|
public ulong Size { get; private set; }
|
||||||
public MemoryAttribute Attribute { get; }
|
|
||||||
public KMemoryPermission SourcePermission { get; }
|
|
||||||
|
|
||||||
public int IpcRefCount { get; }
|
public MemoryState State { get; private set; }
|
||||||
public int DeviceRefCount { get; }
|
public KMemoryPermission Permission { get; private set; }
|
||||||
|
public MemoryAttribute Attribute { get;private set; }
|
||||||
|
public KMemoryPermission SourcePermission { get; private set; }
|
||||||
|
|
||||||
public KMemoryInfo(
|
public int IpcRefCount { get; private set; }
|
||||||
|
public int DeviceRefCount { get; private set; }
|
||||||
|
|
||||||
|
public KMemoryInfo Set(
|
||||||
ulong address,
|
ulong address,
|
||||||
ulong size,
|
ulong size,
|
||||||
MemoryState state,
|
MemoryState state,
|
||||||
|
|
@ -31,6 +35,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
SourcePermission = sourcePermission;
|
SourcePermission = sourcePermission;
|
||||||
IpcRefCount = ipcRefCount;
|
IpcRefCount = ipcRefCount;
|
||||||
DeviceRefCount = deviceRefCount;
|
DeviceRefCount = deviceRefCount;
|
||||||
|
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,17 +25,17 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
{
|
{
|
||||||
MemoryPermission output = MemoryPermission.None;
|
MemoryPermission output = MemoryPermission.None;
|
||||||
|
|
||||||
if (permission.HasFlag(KMemoryPermission.Read))
|
if ((permission & KMemoryPermission.Read) == KMemoryPermission.Read)
|
||||||
{
|
{
|
||||||
output = MemoryPermission.Read;
|
output = MemoryPermission.Read;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (permission.HasFlag(KMemoryPermission.Write))
|
if ((permission & KMemoryPermission.Write) == KMemoryPermission.Write)
|
||||||
{
|
{
|
||||||
output |= MemoryPermission.Write;
|
output |= MemoryPermission.Write;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (permission.HasFlag(KMemoryPermission.Execute))
|
if ((permission & KMemoryPermission.Execute) == KMemoryPermission.Execute)
|
||||||
{
|
{
|
||||||
output |= MemoryPermission.Execute;
|
output |= MemoryPermission.Execute;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1005,7 +1005,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return new KMemoryInfo(
|
return KMemoryInfo.Pool.Allocate().Set(
|
||||||
AddrSpaceEnd,
|
AddrSpaceEnd,
|
||||||
~AddrSpaceEnd + 1,
|
~AddrSpaceEnd + 1,
|
||||||
MemoryState.Reserved,
|
MemoryState.Reserved,
|
||||||
|
|
@ -2557,10 +2557,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
KMemoryPermission firstPermission = info.Permission;
|
KMemoryPermission firstPermission = info.Permission;
|
||||||
MemoryAttribute firstAttribute = info.Attribute;
|
MemoryAttribute firstAttribute = info.Attribute;
|
||||||
|
|
||||||
do
|
info = currBlock.GetInfo(info);
|
||||||
{
|
|
||||||
info = currBlock.GetInfo();
|
|
||||||
|
|
||||||
|
while (info.Address + info.Size - 1 < endAddr - 1 && (currBlock = currBlock.Successor) != null)
|
||||||
|
{
|
||||||
// Check if the block state matches what we expect.
|
// Check if the block state matches what we expect.
|
||||||
if (firstState != info.State ||
|
if (firstState != info.State ||
|
||||||
firstPermission != info.Permission ||
|
firstPermission != info.Permission ||
|
||||||
|
|
@ -2573,10 +2573,15 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
outPermission = KMemoryPermission.None;
|
outPermission = KMemoryPermission.None;
|
||||||
outAttribute = MemoryAttribute.None;
|
outAttribute = MemoryAttribute.None;
|
||||||
|
|
||||||
|
KMemoryInfo.Pool.Release(info);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
info = currBlock.GetInfo(info);
|
||||||
}
|
}
|
||||||
while (info.Address + info.Size - 1 < endAddr - 1 && (currBlock = currBlock.Successor) != null);
|
|
||||||
|
KMemoryInfo.Pool.Release(info);
|
||||||
|
|
||||||
outState = firstState;
|
outState = firstState;
|
||||||
outPermission = firstPermission;
|
outPermission = firstPermission;
|
||||||
|
|
@ -2595,17 +2600,27 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
MemoryAttribute attributeMask,
|
MemoryAttribute attributeMask,
|
||||||
MemoryAttribute attributeExpected)
|
MemoryAttribute attributeExpected)
|
||||||
{
|
{
|
||||||
foreach (KMemoryInfo info in IterateOverRange(address, address + size))
|
KMemoryBlock currBlock = _blockManager.FindBlock(address);
|
||||||
|
|
||||||
|
KMemoryInfo info = currBlock.GetInfo();
|
||||||
|
|
||||||
|
while (info.Address + info.Size - 1 < address + size - 1 && (currBlock = currBlock.Successor) != null)
|
||||||
{
|
{
|
||||||
// Check if the block state matches what we expect.
|
// Check if the block state matches what we expect.
|
||||||
if ((info.State & stateMask) != stateExpected ||
|
if ((info.State & stateMask) != stateExpected ||
|
||||||
(info.Permission & permissionMask) != permissionExpected ||
|
(info.Permission & permissionMask) != permissionExpected ||
|
||||||
(info.Attribute & attributeMask) != attributeExpected)
|
(info.Attribute & attributeMask) != attributeExpected)
|
||||||
{
|
{
|
||||||
|
KMemoryInfo.Pool.Release(info);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
info = currBlock.GetInfo(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
KMemoryInfo.Pool.Release(info);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2655,6 +2670,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
ulong currBaseAddr = info.Address + reservedPagesCount * PageSize;
|
ulong currBaseAddr = info.Address + reservedPagesCount * PageSize;
|
||||||
ulong currEndAddr = info.Address + info.Size;
|
ulong currEndAddr = info.Address + info.Size;
|
||||||
|
|
||||||
|
KMemoryInfo.Pool.Release(info);
|
||||||
|
|
||||||
if (aslrAddress >= regionStart &&
|
if (aslrAddress >= regionStart &&
|
||||||
aslrAddress >= currBaseAddr &&
|
aslrAddress >= currBaseAddr &&
|
||||||
aslrEndAddr - 1 <= regionEndAddr - 1 &&
|
aslrEndAddr - 1 <= regionEndAddr - 1 &&
|
||||||
|
|
@ -2734,6 +2751,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
allocationEndAddr <= regionEndAddr &&
|
allocationEndAddr <= regionEndAddr &&
|
||||||
allocationEndAddr <= currEndAddr)
|
allocationEndAddr <= currEndAddr)
|
||||||
{
|
{
|
||||||
|
KMemoryInfo.Pool.Release(info);
|
||||||
return address;
|
return address;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2745,9 +2763,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
info = currBlock.GetInfo();
|
info = currBlock.GetInfo(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
KMemoryInfo.Pool.Release(info);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -384,6 +384,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||||
|
|
||||||
var rwdataStart = roInfo.Address + roInfo.Size;
|
var rwdataStart = roInfo.Address + roInfo.Size;
|
||||||
|
|
||||||
|
KMemoryInfo.Pool.Release(roInfo);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Span<byte> rodataBuf = stackalloc byte[0x208];
|
Span<byte> rodataBuf = stackalloc byte[0x208];
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
using Ryujinx.Common;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Common;
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Process;
|
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||||
using Ryujinx.Horizon.Common;
|
using Ryujinx.Horizon.Common;
|
||||||
|
|
@ -11,6 +12,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||||
class KAddressArbiter
|
class KAddressArbiter
|
||||||
{
|
{
|
||||||
private const int HasListenersMask = 0x40000000;
|
private const int HasListenersMask = 0x40000000;
|
||||||
|
private static readonly ObjectPool<KThread[]> _threadArrayPool = new(() => []);
|
||||||
|
|
||||||
private readonly KernelContext _context;
|
private readonly KernelContext _context;
|
||||||
|
|
||||||
|
|
@ -198,9 +200,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||||
{
|
{
|
||||||
_context.CriticalSection.Enter();
|
_context.CriticalSection.Enter();
|
||||||
|
|
||||||
WakeThreads(_condVarThreads, count, TryAcquireMutex, x => x.CondVarAddress == address);
|
static bool SignalProcessWideKeyPredicate(KThread thread, ulong address)
|
||||||
|
{
|
||||||
|
return thread.CondVarAddress == address;
|
||||||
|
}
|
||||||
|
|
||||||
if (!_condVarThreads.Any(x => x.CondVarAddress == address))
|
int validThreads = WakeThreads(_condVarThreads, count, TryAcquireMutex, SignalProcessWideKeyPredicate, address);
|
||||||
|
|
||||||
|
if (validThreads == 0)
|
||||||
{
|
{
|
||||||
KernelTransfer.KernelToUser(address, 0);
|
KernelTransfer.KernelToUser(address, 0);
|
||||||
}
|
}
|
||||||
|
|
@ -480,9 +487,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||||
// or negative. It is incremented if there are no threads waiting.
|
// or negative. It is incremented if there are no threads waiting.
|
||||||
int waitingCount = 0;
|
int waitingCount = 0;
|
||||||
|
|
||||||
foreach (KThread thread in _arbiterThreads.Where(x => x.MutexAddress == address))
|
foreach (KThread thread in _arbiterThreads)
|
||||||
{
|
{
|
||||||
if (++waitingCount >= count)
|
if (thread.MutexAddress == address &&
|
||||||
|
++waitingCount >= count)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -553,23 +561,55 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||||
thread.WaitingInArbitration = false;
|
thread.WaitingInArbitration = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
WakeThreads(_arbiterThreads, count, RemoveArbiterThread, x => x.MutexAddress == address);
|
static bool ArbiterThreadPredecate(KThread thread, ulong address)
|
||||||
|
{
|
||||||
|
return thread.MutexAddress == address;
|
||||||
|
}
|
||||||
|
|
||||||
|
WakeThreads(_arbiterThreads, count, RemoveArbiterThread, ArbiterThreadPredecate, address);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void WakeThreads(
|
private static int WakeThreads(
|
||||||
List<KThread> threads,
|
List<KThread> threads,
|
||||||
int count,
|
int count,
|
||||||
Action<KThread> removeCallback,
|
Action<KThread> removeCallback,
|
||||||
Func<KThread, bool> predicate)
|
Func<KThread, ulong, bool> predicate,
|
||||||
|
ulong address = 0)
|
||||||
{
|
{
|
||||||
var candidates = threads.Where(predicate).OrderBy(x => x.DynamicPriority);
|
KThread[] candidates = _threadArrayPool.Allocate();
|
||||||
var toSignal = (count > 0 ? candidates.Take(count) : candidates).ToArray();
|
if (candidates.Length < threads.Count)
|
||||||
|
{
|
||||||
|
Array.Resize(ref candidates, threads.Count);
|
||||||
|
}
|
||||||
|
|
||||||
foreach (KThread thread in toSignal)
|
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)));
|
||||||
|
|
||||||
|
if (count > 0)
|
||||||
|
{
|
||||||
|
candidatesSpan = candidatesSpan[..Math.Min(count, candidatesSpan.Length)];
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (KThread thread in candidatesSpan)
|
||||||
{
|
{
|
||||||
removeCallback(thread);
|
removeCallback(thread);
|
||||||
threads.Remove(thread);
|
threads.Remove(thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_threadArrayPool.Release(candidates);
|
||||||
|
|
||||||
|
return validCount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -48,8 +48,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||||
|
|
||||||
public KThreadContext ThreadContext { get; private set; }
|
public KThreadContext ThreadContext { get; private set; }
|
||||||
|
|
||||||
public int DynamicPriority { get; set; }
|
public int DynamicPriority { get; private set; }
|
||||||
public ulong AffinityMask { get; set; }
|
public ulong AffinityMask { get; private set; }
|
||||||
|
|
||||||
public ulong ThreadUid { get; private set; }
|
public ulong ThreadUid { get; private set; }
|
||||||
|
|
||||||
|
|
@ -83,18 +83,18 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||||
|
|
||||||
public long LastScheduledTime { get; set; }
|
public long LastScheduledTime { get; set; }
|
||||||
|
|
||||||
public LinkedListNode<KThread>[] SiblingsPerCore { get; private set; }
|
public readonly LinkedListNode<KThread>[] SiblingsPerCore;
|
||||||
|
|
||||||
public LinkedList<KThread> Withholder { get; set; }
|
public LinkedList<KThread> Withholder { get; set; }
|
||||||
public LinkedListNode<KThread> WithholderNode { get; set; }
|
public readonly LinkedListNode<KThread> WithholderNode;
|
||||||
|
|
||||||
public LinkedListNode<KThread> ProcessListNode { get; set; }
|
public readonly LinkedListNode<KThread> ProcessListNode;
|
||||||
|
|
||||||
private readonly LinkedList<KThread> _mutexWaiters;
|
private readonly LinkedList<KThread> _mutexWaiters;
|
||||||
private LinkedListNode<KThread> _mutexWaiterNode;
|
private readonly LinkedListNode<KThread> _mutexWaiterNode;
|
||||||
|
|
||||||
private readonly LinkedList<KThread> _pinnedWaiters;
|
private readonly LinkedList<KThread> _pinnedWaiters;
|
||||||
private LinkedListNode<KThread> _pinnedWaiterNode;
|
private readonly LinkedListNode<KThread> _pinnedWaiterNode;
|
||||||
|
|
||||||
public KThread MutexOwner { get; private set; }
|
public KThread MutexOwner { get; private set; }
|
||||||
|
|
||||||
|
|
@ -1070,11 +1070,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||||
|
|
||||||
if (nextPrio != null)
|
if (nextPrio != null)
|
||||||
{
|
{
|
||||||
thread._mutexWaiterNode = _mutexWaiters.AddBefore(nextPrio, thread);
|
_mutexWaiters.AddBefore(nextPrio, thread._mutexWaiterNode);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
thread._mutexWaiterNode = _mutexWaiters.AddLast(thread);
|
_mutexWaiters.AddLast(thread._mutexWaiterNode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap;
|
||||||
using Ryujinx.HLE.HOS.Services.Nv.Types;
|
using Ryujinx.HLE.HOS.Services.Nv.Types;
|
||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Buffers;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
|
|
@ -47,6 +48,8 @@ namespace Ryujinx.HLE.HOS.Services.Nv
|
||||||
{ "/dev/nvhost-prof-gpu", typeof(NvHostProfGpuDeviceFile) },
|
{ "/dev/nvhost-prof-gpu", typeof(NvHostProfGpuDeviceFile) },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private static readonly ArrayPool<byte> _byteArrayPool = ArrayPool<byte>.Create();
|
||||||
|
|
||||||
public static IdDictionary DeviceFileIdRegistry = new();
|
public static IdDictionary DeviceFileIdRegistry = new();
|
||||||
|
|
||||||
private IVirtualMemoryManager _clientMemory;
|
private IVirtualMemoryManager _clientMemory;
|
||||||
|
|
@ -471,10 +474,13 @@ namespace Ryujinx.HLE.HOS.Services.Nv
|
||||||
|
|
||||||
errorCode = GetIoctlArgument(context, ioctlCommand, out Span<byte> arguments);
|
errorCode = GetIoctlArgument(context, ioctlCommand, out Span<byte> arguments);
|
||||||
|
|
||||||
if (!context.Memory.TryReadUnsafe(inlineInBufferPosition, (int)inlineInBufferSize, out Span<byte> inlineInBuffer))
|
byte[] inlineInBuffer = null;
|
||||||
|
|
||||||
|
if (!context.Memory.TryReadUnsafe(inlineInBufferPosition, (int)inlineInBufferSize, out Span<byte> inlineInBufferSpan))
|
||||||
{
|
{
|
||||||
inlineInBuffer = new byte[inlineInBufferSize];
|
inlineInBuffer = _byteArrayPool.Rent((int)inlineInBufferSize);
|
||||||
context.Memory.Read(inlineInBufferPosition, inlineInBuffer);
|
inlineInBufferSpan = inlineInBuffer;
|
||||||
|
context.Memory.Read(inlineInBufferPosition, inlineInBufferSpan[..(int)inlineInBufferSize]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (errorCode == NvResult.Success)
|
if (errorCode == NvResult.Success)
|
||||||
|
|
@ -483,7 +489,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv
|
||||||
|
|
||||||
if (errorCode == NvResult.Success)
|
if (errorCode == NvResult.Success)
|
||||||
{
|
{
|
||||||
NvInternalResult internalResult = deviceFile.Ioctl2(ioctlCommand, arguments, inlineInBuffer);
|
NvInternalResult internalResult = deviceFile.Ioctl2(ioctlCommand, arguments, inlineInBufferSpan[..(int)inlineInBufferSize]);
|
||||||
|
|
||||||
if (internalResult == NvInternalResult.NotImplemented)
|
if (internalResult == NvInternalResult.NotImplemented)
|
||||||
{
|
{
|
||||||
|
|
@ -498,6 +504,11 @@ namespace Ryujinx.HLE.HOS.Services.Nv
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (inlineInBuffer is not null)
|
||||||
|
{
|
||||||
|
_byteArrayPool.Return(inlineInBuffer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
context.ResponseData.Write((uint)errorCode);
|
context.ResponseData.Write((uint)errorCode);
|
||||||
|
|
@ -520,10 +531,13 @@ namespace Ryujinx.HLE.HOS.Services.Nv
|
||||||
|
|
||||||
errorCode = GetIoctlArgument(context, ioctlCommand, out Span<byte> arguments);
|
errorCode = GetIoctlArgument(context, ioctlCommand, out Span<byte> arguments);
|
||||||
|
|
||||||
if (!context.Memory.TryReadUnsafe(inlineOutBufferPosition, (int)inlineOutBufferSize, out Span<byte> inlineOutBuffer))
|
byte[] inlineOutBuffer = null;
|
||||||
|
|
||||||
|
if (!context.Memory.TryReadUnsafe(inlineOutBufferPosition, (int)inlineOutBufferSize, out Span<byte> inlineOutBufferSpan))
|
||||||
{
|
{
|
||||||
inlineOutBuffer = new byte[inlineOutBufferSize];
|
inlineOutBuffer = _byteArrayPool.Rent((int)inlineOutBufferSize);
|
||||||
context.Memory.Read(inlineOutBufferPosition, inlineOutBuffer);
|
inlineOutBufferSpan = inlineOutBuffer;
|
||||||
|
context.Memory.Read(inlineOutBufferPosition, inlineOutBufferSpan[..(int)inlineOutBufferSize]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (errorCode == NvResult.Success)
|
if (errorCode == NvResult.Success)
|
||||||
|
|
@ -532,7 +546,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv
|
||||||
|
|
||||||
if (errorCode == NvResult.Success)
|
if (errorCode == NvResult.Success)
|
||||||
{
|
{
|
||||||
NvInternalResult internalResult = deviceFile.Ioctl3(ioctlCommand, arguments, inlineOutBuffer);
|
NvInternalResult internalResult = deviceFile.Ioctl3(ioctlCommand, arguments, inlineOutBufferSpan[..(int)inlineOutBufferSize]);
|
||||||
|
|
||||||
if (internalResult == NvInternalResult.NotImplemented)
|
if (internalResult == NvInternalResult.NotImplemented)
|
||||||
{
|
{
|
||||||
|
|
@ -544,10 +558,15 @@ namespace Ryujinx.HLE.HOS.Services.Nv
|
||||||
if ((ioctlCommand.DirectionValue & NvIoctl.Direction.Write) != 0)
|
if ((ioctlCommand.DirectionValue & NvIoctl.Direction.Write) != 0)
|
||||||
{
|
{
|
||||||
context.Memory.Write(context.Request.GetBufferType0x22(0).Position, arguments.ToArray());
|
context.Memory.Write(context.Request.GetBufferType0x22(0).Position, arguments.ToArray());
|
||||||
context.Memory.Write(inlineOutBufferPosition, inlineOutBuffer.ToArray());
|
context.Memory.Write(inlineOutBufferPosition, inlineOutBufferSpan[..(int)inlineOutBufferSize].ToArray());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (inlineOutBuffer is not null)
|
||||||
|
{
|
||||||
|
_byteArrayPool.Return(inlineOutBuffer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
context.ResponseData.Write((uint)errorCode);
|
context.ResponseData.Write((uint)errorCode);
|
||||||
|
|
|
||||||
|
|
@ -48,36 +48,36 @@ namespace Ryujinx.Horizon.Sdk.Sf
|
||||||
case CommandArgType.Buffer:
|
case CommandArgType.Buffer:
|
||||||
var flags = argInfo.BufferFlags;
|
var flags = argInfo.BufferFlags;
|
||||||
|
|
||||||
if (flags.HasFlag(HipcBufferFlags.In))
|
if ((flags & HipcBufferFlags.In) != 0)
|
||||||
{
|
{
|
||||||
if (flags.HasFlag(HipcBufferFlags.AutoSelect))
|
if ((flags & HipcBufferFlags.AutoSelect) != 0)
|
||||||
{
|
{
|
||||||
_inMapAliasBuffersCount++;
|
_inMapAliasBuffersCount++;
|
||||||
_inPointerBuffersCount++;
|
_inPointerBuffersCount++;
|
||||||
}
|
}
|
||||||
else if (flags.HasFlag(HipcBufferFlags.MapAlias))
|
else if ((flags & HipcBufferFlags.MapAlias) != 0)
|
||||||
{
|
{
|
||||||
_inMapAliasBuffersCount++;
|
_inMapAliasBuffersCount++;
|
||||||
}
|
}
|
||||||
else if (flags.HasFlag(HipcBufferFlags.Pointer))
|
else if ((flags & HipcBufferFlags.Pointer) != 0)
|
||||||
{
|
{
|
||||||
_inPointerBuffersCount++;
|
_inPointerBuffersCount++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
bool autoSelect = flags.HasFlag(HipcBufferFlags.AutoSelect);
|
bool autoSelect = (flags & HipcBufferFlags.AutoSelect) != 0;
|
||||||
if (autoSelect || flags.HasFlag(HipcBufferFlags.Pointer))
|
if (autoSelect || (flags & HipcBufferFlags.Pointer) != 0)
|
||||||
{
|
{
|
||||||
_outPointerBuffersCount++;
|
_outPointerBuffersCount++;
|
||||||
|
|
||||||
if (flags.HasFlag(HipcBufferFlags.FixedSize))
|
if ((flags & HipcBufferFlags.FixedSize) != 0)
|
||||||
{
|
{
|
||||||
_outFixedSizePointerBuffersCount++;
|
_outFixedSizePointerBuffersCount++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (autoSelect || flags.HasFlag(HipcBufferFlags.MapAlias))
|
if (autoSelect || (flags & HipcBufferFlags.MapAlias) != 0)
|
||||||
{
|
{
|
||||||
_outMapAliasBuffersCount++;
|
_outMapAliasBuffersCount++;
|
||||||
}
|
}
|
||||||
|
|
@ -149,17 +149,17 @@ namespace Ryujinx.Horizon.Sdk.Sf
|
||||||
var flags = _args[i].BufferFlags;
|
var flags = _args[i].BufferFlags;
|
||||||
bool isMapAlias;
|
bool isMapAlias;
|
||||||
|
|
||||||
if (flags.HasFlag(HipcBufferFlags.MapAlias))
|
if ((flags & HipcBufferFlags.MapAlias) != 0)
|
||||||
{
|
{
|
||||||
isMapAlias = true;
|
isMapAlias = true;
|
||||||
}
|
}
|
||||||
else if (flags.HasFlag(HipcBufferFlags.Pointer))
|
else if ((flags & HipcBufferFlags.Pointer) != 0)
|
||||||
{
|
{
|
||||||
isMapAlias = false;
|
isMapAlias = false;
|
||||||
}
|
}
|
||||||
else /* if (flags.HasFlag(HipcBufferFlags.HipcAutoSelect)) */
|
else /* if (flags & HipcBufferFlags.HipcAutoSelect)) */
|
||||||
{
|
{
|
||||||
var descriptor = flags.HasFlag(HipcBufferFlags.In)
|
var descriptor = (flags & HipcBufferFlags.In) != 0
|
||||||
? context.Request.Data.SendBuffers[sendMapAliasIndex]
|
? context.Request.Data.SendBuffers[sendMapAliasIndex]
|
||||||
: context.Request.Data.ReceiveBuffers[recvMapAliasIndex];
|
: context.Request.Data.ReceiveBuffers[recvMapAliasIndex];
|
||||||
|
|
||||||
|
|
@ -170,7 +170,7 @@ namespace Ryujinx.Horizon.Sdk.Sf
|
||||||
|
|
||||||
if (isMapAlias)
|
if (isMapAlias)
|
||||||
{
|
{
|
||||||
var descriptor = flags.HasFlag(HipcBufferFlags.In)
|
var descriptor = (flags & HipcBufferFlags.In) != 0
|
||||||
? context.Request.Data.SendBuffers[sendMapAliasIndex++]
|
? context.Request.Data.SendBuffers[sendMapAliasIndex++]
|
||||||
: context.Request.Data.ReceiveBuffers[recvMapAliasIndex++];
|
: context.Request.Data.ReceiveBuffers[recvMapAliasIndex++];
|
||||||
|
|
||||||
|
|
@ -183,7 +183,7 @@ namespace Ryujinx.Horizon.Sdk.Sf
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (flags.HasFlag(HipcBufferFlags.In))
|
if ((flags & HipcBufferFlags.In) != 0)
|
||||||
{
|
{
|
||||||
var descriptor = context.Request.Data.SendStatics[sendPointerIndex++];
|
var descriptor = context.Request.Data.SendStatics[sendPointerIndex++];
|
||||||
ulong address = descriptor.Address;
|
ulong address = descriptor.Address;
|
||||||
|
|
@ -196,11 +196,11 @@ namespace Ryujinx.Horizon.Sdk.Sf
|
||||||
pointerBufferTail = Math.Max(pointerBufferTail, address + size);
|
pointerBufferTail = Math.Max(pointerBufferTail, address + size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else /* if (flags.HasFlag(HipcBufferFlags.Out)) */
|
else /* if (flags & HipcBufferFlags.Out)) */
|
||||||
{
|
{
|
||||||
ulong size;
|
ulong size;
|
||||||
|
|
||||||
if (flags.HasFlag(HipcBufferFlags.FixedSize))
|
if ((flags & HipcBufferFlags.FixedSize) != 0)
|
||||||
{
|
{
|
||||||
size = _args[i].BufferFixedSize;
|
size = _args[i].BufferFixedSize;
|
||||||
}
|
}
|
||||||
|
|
@ -233,12 +233,12 @@ namespace Ryujinx.Horizon.Sdk.Sf
|
||||||
|
|
||||||
private static bool IsMapTransferModeValid(HipcBufferFlags flags, HipcBufferMode mode)
|
private static bool IsMapTransferModeValid(HipcBufferFlags flags, HipcBufferMode mode)
|
||||||
{
|
{
|
||||||
if (flags.HasFlag(HipcBufferFlags.MapTransferAllowsNonSecure))
|
if ((flags & HipcBufferFlags.MapTransferAllowsNonSecure) != 0)
|
||||||
{
|
{
|
||||||
return mode == HipcBufferMode.NonSecure;
|
return mode == HipcBufferMode.NonSecure;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flags.HasFlag(HipcBufferFlags.MapTransferAllowsNonDevice))
|
if ((flags & HipcBufferFlags.MapTransferAllowsNonDevice) != 0)
|
||||||
{
|
{
|
||||||
return mode == HipcBufferMode.NonDevice;
|
return mode == HipcBufferMode.NonDevice;
|
||||||
}
|
}
|
||||||
|
|
@ -258,18 +258,18 @@ namespace Ryujinx.Horizon.Sdk.Sf
|
||||||
}
|
}
|
||||||
|
|
||||||
var flags = _args[i].BufferFlags;
|
var flags = _args[i].BufferFlags;
|
||||||
if (!flags.HasFlag(HipcBufferFlags.Out))
|
if ((flags & HipcBufferFlags.Out) == 0)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var buffer = _bufferRanges[i];
|
var buffer = _bufferRanges[i];
|
||||||
|
|
||||||
if (flags.HasFlag(HipcBufferFlags.Pointer))
|
if ((flags & HipcBufferFlags.Pointer) != 0)
|
||||||
{
|
{
|
||||||
response.SendStatics[recvPointerIndex] = new HipcStaticDescriptor(buffer.Address, (ushort)buffer.Size, recvPointerIndex);
|
response.SendStatics[recvPointerIndex] = new HipcStaticDescriptor(buffer.Address, (ushort)buffer.Size, recvPointerIndex);
|
||||||
}
|
}
|
||||||
else if (flags.HasFlag(HipcBufferFlags.AutoSelect))
|
else if ((flags & HipcBufferFlags.AutoSelect) != 0)
|
||||||
{
|
{
|
||||||
if (!isBufferMapAlias[i])
|
if (!isBufferMapAlias[i])
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,7 @@ namespace Ryujinx.Input.SDL2
|
||||||
_triggerThreshold = 0.0f;
|
_triggerThreshold = 0.0f;
|
||||||
|
|
||||||
// Enable motion tracking
|
// Enable motion tracking
|
||||||
if (Features.HasFlag(GamepadFeaturesFlag.Motion))
|
if ((Features & GamepadFeaturesFlag.Motion) != 0)
|
||||||
{
|
{
|
||||||
if (SDL_GameControllerSetSensorEnabled(_gamepadHandle, SDL_SensorType.SDL_SENSOR_ACCEL, SDL_bool.SDL_TRUE) != 0)
|
if (SDL_GameControllerSetSensorEnabled(_gamepadHandle, SDL_SensorType.SDL_SENSOR_ACCEL, SDL_bool.SDL_TRUE) != 0)
|
||||||
{
|
{
|
||||||
|
|
@ -145,7 +145,7 @@ namespace Ryujinx.Input.SDL2
|
||||||
|
|
||||||
public void Rumble(float lowFrequency, float highFrequency, uint durationMs)
|
public void Rumble(float lowFrequency, float highFrequency, uint durationMs)
|
||||||
{
|
{
|
||||||
if (Features.HasFlag(GamepadFeaturesFlag.Rumble))
|
if ((Features & GamepadFeaturesFlag.Rumble) == 0)
|
||||||
{
|
{
|
||||||
ushort lowFrequencyRaw = (ushort)(lowFrequency * ushort.MaxValue);
|
ushort lowFrequencyRaw = (ushort)(lowFrequency * ushort.MaxValue);
|
||||||
ushort highFrequencyRaw = (ushort)(highFrequency * ushort.MaxValue);
|
ushort highFrequencyRaw = (ushort)(highFrequency * ushort.MaxValue);
|
||||||
|
|
@ -184,32 +184,32 @@ namespace Ryujinx.Input.SDL2
|
||||||
sensorType = SDL_SensorType.SDL_SENSOR_GYRO;
|
sensorType = SDL_SensorType.SDL_SENSOR_GYRO;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Features.HasFlag(GamepadFeaturesFlag.Motion) && sensorType != SDL_SensorType.SDL_SENSOR_INVALID)
|
if ((Features & GamepadFeaturesFlag.Motion) == 0 || sensorType is SDL_SensorType.SDL_SENSOR_INVALID)
|
||||||
|
return Vector3.Zero;
|
||||||
|
|
||||||
|
const int ElementCount = 3;
|
||||||
|
|
||||||
|
unsafe
|
||||||
{
|
{
|
||||||
const int ElementCount = 3;
|
float* values = stackalloc float[ElementCount];
|
||||||
|
|
||||||
unsafe
|
int result = SDL_GameControllerGetSensorData(_gamepadHandle, sensorType, (nint)values, ElementCount);
|
||||||
|
|
||||||
|
if (result == 0)
|
||||||
{
|
{
|
||||||
float* values = stackalloc float[ElementCount];
|
Vector3 value = new(values[0], values[1], values[2]);
|
||||||
|
|
||||||
int result = SDL_GameControllerGetSensorData(_gamepadHandle, sensorType, (nint)values, ElementCount);
|
if (inputId == MotionInputId.Gyroscope)
|
||||||
|
|
||||||
if (result == 0)
|
|
||||||
{
|
{
|
||||||
Vector3 value = new(values[0], values[1], values[2]);
|
return RadToDegree(value);
|
||||||
|
|
||||||
if (inputId == MotionInputId.Gyroscope)
|
|
||||||
{
|
|
||||||
return RadToDegree(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inputId == MotionInputId.Accelerometer)
|
|
||||||
{
|
|
||||||
return GsToMs2(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (inputId == MotionInputId.Accelerometer)
|
||||||
|
{
|
||||||
|
return GsToMs2(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,7 @@ namespace Ryujinx.Input.SDL2
|
||||||
Features = GetFeaturesFlag();
|
Features = GetFeaturesFlag();
|
||||||
|
|
||||||
// Enable motion tracking
|
// Enable motion tracking
|
||||||
if (Features.HasFlag(GamepadFeaturesFlag.Motion))
|
if ((Features & GamepadFeaturesFlag.Motion) != 0)
|
||||||
{
|
{
|
||||||
if (SDL_GameControllerSetSensorEnabled(_gamepadHandle, SDL_SensorType.SDL_SENSOR_ACCEL,
|
if (SDL_GameControllerSetSensorEnabled(_gamepadHandle, SDL_SensorType.SDL_SENSOR_ACCEL,
|
||||||
SDL_bool.SDL_TRUE) != 0)
|
SDL_bool.SDL_TRUE) != 0)
|
||||||
|
|
@ -163,7 +163,7 @@ namespace Ryujinx.Input.SDL2
|
||||||
|
|
||||||
public void Rumble(float lowFrequency, float highFrequency, uint durationMs)
|
public void Rumble(float lowFrequency, float highFrequency, uint durationMs)
|
||||||
{
|
{
|
||||||
if (!Features.HasFlag(GamepadFeaturesFlag.Rumble))
|
if ((Features & GamepadFeaturesFlag.Rumble) == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ushort lowFrequencyRaw = (ushort)(lowFrequency * ushort.MaxValue);
|
ushort lowFrequencyRaw = (ushort)(lowFrequency * ushort.MaxValue);
|
||||||
|
|
@ -195,7 +195,7 @@ namespace Ryujinx.Input.SDL2
|
||||||
_ => SDL_SensorType.SDL_SENSOR_INVALID
|
_ => SDL_SensorType.SDL_SENSOR_INVALID
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!Features.HasFlag(GamepadFeaturesFlag.Motion) || sensorType is SDL_SensorType.SDL_SENSOR_INVALID)
|
if ((Features & GamepadFeaturesFlag.Motion) == 0 || sensorType is SDL_SensorType.SDL_SENSOR_INVALID)
|
||||||
return Vector3.Zero;
|
return Vector3.Zero;
|
||||||
|
|
||||||
const int ElementCount = 3;
|
const int ElementCount = 3;
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ using Ryujinx.Common.Configuration.Hid.Controller.Motion;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.HLE.HOS.Services.Hid;
|
using Ryujinx.HLE.HOS.Services.Hid;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Buffers;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
@ -291,7 +292,7 @@ namespace Ryujinx.Input.HLE
|
||||||
{
|
{
|
||||||
if (controllerConfig.Motion.MotionBackend == MotionInputBackendType.GamepadDriver)
|
if (controllerConfig.Motion.MotionBackend == MotionInputBackendType.GamepadDriver)
|
||||||
{
|
{
|
||||||
if (gamepad.Features.HasFlag(GamepadFeaturesFlag.Motion))
|
if ((gamepad.Features & GamepadFeaturesFlag.Motion) != 0)
|
||||||
{
|
{
|
||||||
Vector3 accelerometer = gamepad.GetMotionData(MotionInputId.Accelerometer);
|
Vector3 accelerometer = gamepad.GetMotionData(MotionInputId.Accelerometer);
|
||||||
Vector3 gyroscope = gamepad.GetMotionData(MotionInputId.Gyroscope);
|
Vector3 gyroscope = gamepad.GetMotionData(MotionInputId.Gyroscope);
|
||||||
|
|
@ -532,6 +533,8 @@ namespace Ryujinx.Input.HLE
|
||||||
hidKeyboard.Modifier |= value << entry.Target;
|
hidKeyboard.Modifier |= value << entry.Target;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ArrayPool<bool>.Shared.Return(keyboardState.KeysState);
|
||||||
|
|
||||||
return hidKeyboard;
|
return hidKeyboard;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Configuration.Hid;
|
using Ryujinx.Common.Configuration.Hid;
|
||||||
using Ryujinx.Common.Configuration.Hid.Controller;
|
using Ryujinx.Common.Configuration.Hid.Controller;
|
||||||
using Ryujinx.Common.Configuration.Hid.Keyboard;
|
using Ryujinx.Common.Configuration.Hid.Keyboard;
|
||||||
using Ryujinx.HLE.HOS.Services.Hid;
|
using Ryujinx.HLE.HOS.Services.Hid;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Buffers;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
@ -17,6 +19,7 @@ namespace Ryujinx.Input.HLE
|
||||||
{
|
{
|
||||||
public class NpadManager : IDisposable
|
public class NpadManager : IDisposable
|
||||||
{
|
{
|
||||||
|
private static readonly ObjectPool<List<SixAxisInput>> _hleMotionStatesPool = new (() => new List<SixAxisInput>(NpadDevices.MaxControllers));
|
||||||
private readonly CemuHookClient _cemuHookClient;
|
private readonly CemuHookClient _cemuHookClient;
|
||||||
|
|
||||||
private readonly Lock _lock = new();
|
private readonly Lock _lock = new();
|
||||||
|
|
@ -204,8 +207,8 @@ namespace Ryujinx.Input.HLE
|
||||||
{
|
{
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
List<GamepadInput> hleInputStates = new();
|
List<GamepadInput> hleInputStates = [];
|
||||||
List<SixAxisInput> hleMotionStates = new(NpadDevices.MaxControllers);
|
List<SixAxisInput> hleMotionStates = _hleMotionStatesPool.Allocate();
|
||||||
|
|
||||||
KeyboardInput? hleKeyboardInput = null;
|
KeyboardInput? hleKeyboardInput = null;
|
||||||
|
|
||||||
|
|
@ -309,6 +312,8 @@ namespace Ryujinx.Input.HLE
|
||||||
var position = IMouse.GetScreenPosition(mouseInput.Position, mouse.ClientSize, aspectRatio);
|
var position = IMouse.GetScreenPosition(mouseInput.Position, mouse.ClientSize, aspectRatio);
|
||||||
|
|
||||||
_device.Hid.Mouse.Update((int)position.X, (int)position.Y, buttons, (int)mouseInput.Scroll.X, (int)mouseInput.Scroll.Y, true);
|
_device.Hid.Mouse.Update((int)position.X, (int)position.Y, buttons, (int)mouseInput.Scroll.X, (int)mouseInput.Scroll.Y, true);
|
||||||
|
|
||||||
|
ArrayPool<bool>.Shared.Return(mouseInput.ButtonState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -317,6 +322,8 @@ namespace Ryujinx.Input.HLE
|
||||||
}
|
}
|
||||||
|
|
||||||
_device.TamperMachine.UpdateInput(hleInputStates);
|
_device.TamperMachine.UpdateInput(hleInputStates);
|
||||||
|
|
||||||
|
_hleMotionStatesPool.Release(hleMotionStates);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
using System.Buffers;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace Ryujinx.Input
|
namespace Ryujinx.Input
|
||||||
|
|
@ -28,7 +29,8 @@ namespace Ryujinx.Input
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
static KeyboardStateSnapshot GetStateSnapshot(IKeyboard keyboard)
|
static KeyboardStateSnapshot GetStateSnapshot(IKeyboard keyboard)
|
||||||
{
|
{
|
||||||
bool[] keysState = new bool[(int)Key.Count];
|
|
||||||
|
bool[] keysState = ArrayPool<bool>.Shared.Rent((int)Key.Count);
|
||||||
|
|
||||||
for (Key key = 0; key < Key.Count; key++)
|
for (Key key = 0; key < Key.Count; key++)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
using System.Buffers;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
|
||||||
|
|
@ -47,7 +48,7 @@ namespace Ryujinx.Input
|
||||||
/// <returns>A snaphost of the state of the mouse.</returns>
|
/// <returns>A snaphost of the state of the mouse.</returns>
|
||||||
public static MouseStateSnapshot GetMouseStateSnapshot(IMouse mouse)
|
public static MouseStateSnapshot GetMouseStateSnapshot(IMouse mouse)
|
||||||
{
|
{
|
||||||
bool[] buttons = new bool[(int)MouseButton.Count];
|
bool[] buttons = ArrayPool<bool>.Shared.Rent((int)MouseButton.Count);
|
||||||
|
|
||||||
mouse.Buttons.CopyTo(buttons, 0);
|
mouse.Buttons.CopyTo(buttons, 0);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ namespace Ryujinx.Input
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class KeyboardStateSnapshot
|
public class KeyboardStateSnapshot
|
||||||
{
|
{
|
||||||
private readonly bool[] _keysState;
|
public readonly bool[] KeysState;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a new <see cref="KeyboardStateSnapshot"/>.
|
/// Create a new <see cref="KeyboardStateSnapshot"/>.
|
||||||
|
|
@ -15,7 +15,7 @@ namespace Ryujinx.Input
|
||||||
/// <param name="keysState">The keys state</param>
|
/// <param name="keysState">The keys state</param>
|
||||||
public KeyboardStateSnapshot(bool[] keysState)
|
public KeyboardStateSnapshot(bool[] keysState)
|
||||||
{
|
{
|
||||||
_keysState = keysState;
|
KeysState = keysState;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -24,6 +24,6 @@ namespace Ryujinx.Input
|
||||||
/// <param name="key">The key</param>
|
/// <param name="key">The key</param>
|
||||||
/// <returns>True if the given key is pressed</returns>
|
/// <returns>True if the given key is pressed</returns>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public bool IsPressed(Key key) => _keysState[(int)key];
|
public bool IsPressed(Key key) => KeysState[(int)key];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ namespace Ryujinx.Input
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class MouseStateSnapshot
|
public class MouseStateSnapshot
|
||||||
{
|
{
|
||||||
private readonly bool[] _buttonState;
|
public readonly bool[] ButtonState;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The position of the mouse cursor
|
/// The position of the mouse cursor
|
||||||
|
|
@ -28,7 +28,7 @@ namespace Ryujinx.Input
|
||||||
/// <param name="scroll">The scroll delta</param>
|
/// <param name="scroll">The scroll delta</param>
|
||||||
public MouseStateSnapshot(bool[] buttonState, Vector2 position, Vector2 scroll)
|
public MouseStateSnapshot(bool[] buttonState, Vector2 position, Vector2 scroll)
|
||||||
{
|
{
|
||||||
_buttonState = buttonState;
|
ButtonState = buttonState;
|
||||||
|
|
||||||
Position = position;
|
Position = position;
|
||||||
Scroll = scroll;
|
Scroll = scroll;
|
||||||
|
|
@ -40,6 +40,6 @@ namespace Ryujinx.Input
|
||||||
/// <param name="button">The button</param>
|
/// <param name="button">The button</param>
|
||||||
/// <returns>True if the given button is pressed</returns>
|
/// <returns>True if the given button is pressed</returns>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public bool IsPressed(MouseButton button) => _buttonState[(int)button];
|
public bool IsPressed(MouseButton button) => ButtonState[(int)button];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Buffers;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
@ -38,7 +39,7 @@ namespace Ryujinx.Memory.Range
|
||||||
index = ~index;
|
index = ~index;
|
||||||
}
|
}
|
||||||
|
|
||||||
RangeItem<T> rangeItem = new(item);
|
RangeItem<T> rangeItem = _rangeItemPool.Allocate().Set(item);
|
||||||
|
|
||||||
Insert(index, rangeItem);
|
Insert(index, rangeItem);
|
||||||
}
|
}
|
||||||
|
|
@ -144,6 +145,8 @@ namespace Ryujinx.Memory.Range
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private void RemoveAt(int index)
|
private void RemoveAt(int index)
|
||||||
{
|
{
|
||||||
|
_rangeItemPool.Release(Items[index]);
|
||||||
|
|
||||||
if (index < Count - 1)
|
if (index < Count - 1)
|
||||||
{
|
{
|
||||||
Items[index + 1].Previous = index > 0 ? Items[index - 1] : null;
|
Items[index + 1].Previous = index > 0 ? Items[index - 1] : null;
|
||||||
|
|
@ -433,7 +436,7 @@ namespace Ryujinx.Memory.Range
|
||||||
return (Items[index], Items[endIndex - 1]);
|
return (Items[index], Items[endIndex - 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public RangeItem<T>[] FindOverlapsAsArray(ulong address, ulong size)
|
public RangeItem<T>[] FindOverlapsAsArray(ulong address, ulong size, out int length)
|
||||||
{
|
{
|
||||||
(int index, int endIndex) = BinarySearchEdges(address, address + size);
|
(int index, int endIndex) = BinarySearchEdges(address, address + size);
|
||||||
|
|
||||||
|
|
@ -441,11 +444,13 @@ namespace Ryujinx.Memory.Range
|
||||||
|
|
||||||
if (index < 0)
|
if (index < 0)
|
||||||
{
|
{
|
||||||
result = [];
|
result = null;
|
||||||
|
length = 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
result = new RangeItem<T>[endIndex - index];
|
result = ArrayPool<RangeItem<T>>.Shared.Rent(endIndex - index);
|
||||||
|
length = endIndex - index;
|
||||||
|
|
||||||
Array.Copy(Items, index, result, 0, endIndex - index);
|
Array.Copy(Items, index, result, 0, endIndex - index);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,8 +37,6 @@ namespace Ryujinx.Memory.Range
|
||||||
{
|
{
|
||||||
public readonly ReaderWriterLockSlim Lock = new();
|
public readonly ReaderWriterLockSlim Lock = new();
|
||||||
|
|
||||||
private readonly Dictionary<ulong, RangeItem<T>> _quickAccess = new(AddressEqualityComparer.Comparer);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new range list.
|
/// Creates a new range list.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -93,11 +91,6 @@ namespace Ryujinx.Memory.Range
|
||||||
Items[index + 1].Previous = rangeItem;
|
Items[index + 1].Previous = rangeItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (ulong address in Items[index].QuickAccessAddresses)
|
|
||||||
{
|
|
||||||
_quickAccess.Remove(address);
|
|
||||||
}
|
|
||||||
|
|
||||||
Items[index] = rangeItem;
|
Items[index] = rangeItem;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -142,11 +135,6 @@ namespace Ryujinx.Memory.Range
|
||||||
Items[index + 1].Previous = rangeItem;
|
Items[index + 1].Previous = rangeItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (ulong address in item.QuickAccessAddresses)
|
|
||||||
{
|
|
||||||
_quickAccess.Remove(address);
|
|
||||||
}
|
|
||||||
|
|
||||||
Items[index] = rangeItem;
|
Items[index] = rangeItem;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -210,11 +198,6 @@ namespace Ryujinx.Memory.Range
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private void RemoveAt(int index)
|
private void RemoveAt(int index)
|
||||||
{
|
{
|
||||||
foreach (ulong address in Items[index].QuickAccessAddresses)
|
|
||||||
{
|
|
||||||
_quickAccess.Remove(address);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (index < Count - 1)
|
if (index < Count - 1)
|
||||||
{
|
{
|
||||||
Items[index + 1].Previous = index > 0 ? Items[index - 1] : null;
|
Items[index + 1].Previous = index > 0 ? Items[index - 1] : null;
|
||||||
|
|
@ -253,15 +236,6 @@ namespace Ryujinx.Memory.Range
|
||||||
int startIndex = BinarySearch(startItem.Address);
|
int startIndex = BinarySearch(startItem.Address);
|
||||||
int endIndex = BinarySearch(endItem.Address);
|
int endIndex = BinarySearch(endItem.Address);
|
||||||
|
|
||||||
for (int i = startIndex; i <= endIndex; i++)
|
|
||||||
{
|
|
||||||
_quickAccess.Remove(Items[i].Address);
|
|
||||||
foreach (ulong addr in Items[i].QuickAccessAddresses)
|
|
||||||
{
|
|
||||||
_quickAccess.Remove(addr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (endIndex < Count - 1)
|
if (endIndex < Count - 1)
|
||||||
{
|
{
|
||||||
Items[endIndex + 1].Previous = startIndex > 0 ? Items[startIndex - 1] : null;
|
Items[endIndex + 1].Previous = startIndex > 0 ? Items[startIndex - 1] : null;
|
||||||
|
|
@ -349,11 +323,6 @@ namespace Ryujinx.Memory.Range
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public override RangeItem<T> FindOverlapFast(ulong address, ulong size)
|
public override RangeItem<T> FindOverlapFast(ulong address, ulong size)
|
||||||
{
|
{
|
||||||
if (_quickAccess.TryGetValue(address, out RangeItem<T> quickResult))
|
|
||||||
{
|
|
||||||
return quickResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
int index = BinarySearch(address, address + size);
|
int index = BinarySearch(address, address + size);
|
||||||
|
|
||||||
if (index < 0)
|
if (index < 0)
|
||||||
|
|
@ -361,12 +330,6 @@ namespace Ryujinx.Memory.Range
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Items[index].OverlapsWith(address, address + 1))
|
|
||||||
{
|
|
||||||
_quickAccess.Add(address, Items[index]);
|
|
||||||
Items[index].QuickAccessAddresses.Add(address);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Items[index];
|
return Items[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,42 @@
|
||||||
using System.Collections;
|
using Ryujinx.Common;
|
||||||
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace Ryujinx.Memory.Range
|
namespace Ryujinx.Memory.Range
|
||||||
{
|
{
|
||||||
public class RangeItem<TValue>(TValue value) where TValue : IRange
|
public class RangeItem<TValue> where TValue : IRange
|
||||||
{
|
{
|
||||||
public RangeItem<TValue> Next;
|
public RangeItem<TValue> Next;
|
||||||
public RangeItem<TValue> Previous;
|
public RangeItem<TValue> Previous;
|
||||||
|
|
||||||
public readonly ulong Address = value.Address;
|
public ulong Address;
|
||||||
public readonly ulong EndAddress = value.Address + value.Size;
|
public ulong EndAddress;
|
||||||
|
|
||||||
public readonly TValue Value = value;
|
public TValue Value;
|
||||||
|
|
||||||
public readonly List<ulong> QuickAccessAddresses = [];
|
public RangeItem()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public RangeItem(TValue value)
|
||||||
|
{
|
||||||
|
Address = value.Address;
|
||||||
|
EndAddress = value.Address + value.Size;
|
||||||
|
Value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RangeItem<TValue> Set(TValue value)
|
||||||
|
{
|
||||||
|
Next = null;
|
||||||
|
Previous = null;
|
||||||
|
Address = value.Address;
|
||||||
|
EndAddress = value.Address + value.Size;
|
||||||
|
Value = value;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public bool OverlapsWith(ulong address, ulong endAddress)
|
public bool OverlapsWith(ulong address, ulong endAddress)
|
||||||
|
|
@ -23,20 +45,9 @@ namespace Ryujinx.Memory.Range
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class AddressEqualityComparer : IEqualityComparer<ulong>
|
|
||||||
{
|
|
||||||
public bool Equals(ulong u1, ulong u2)
|
|
||||||
{
|
|
||||||
return u1 == u2;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int GetHashCode(ulong value) => (int)(value << 5);
|
|
||||||
|
|
||||||
public static readonly AddressEqualityComparer Comparer = new();
|
|
||||||
}
|
|
||||||
|
|
||||||
public unsafe abstract class RangeListBase<T> : IEnumerable<T> where T : IRange
|
public unsafe abstract class RangeListBase<T> : IEnumerable<T> where T : IRange
|
||||||
{
|
{
|
||||||
|
protected static readonly ObjectPool<RangeItem<T>> _rangeItemPool = new(() => new RangeItem<T>());
|
||||||
private const int BackingInitialSize = 1024;
|
private const int BackingInitialSize = 1024;
|
||||||
|
|
||||||
protected RangeItem<T>[] Items;
|
protected RangeItem<T>[] Items;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
using Ryujinx.Memory.Range;
|
using Ryujinx.Memory.Range;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Buffers;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Ryujinx.Memory.Tracking
|
namespace Ryujinx.Memory.Tracking
|
||||||
|
|
@ -300,10 +301,10 @@ namespace Ryujinx.Memory.Tracking
|
||||||
|
|
||||||
// We use the non-span method here because keeping the lock will cause a deadlock.
|
// We use the non-span method here because keeping the lock will cause a deadlock.
|
||||||
regions.Lock.EnterReadLock();
|
regions.Lock.EnterReadLock();
|
||||||
RangeItem<VirtualRegion>[] overlaps = regions.FindOverlapsAsArray(address, size);
|
RangeItem<VirtualRegion>[] overlaps = regions.FindOverlapsAsArray(address, size, out int length);
|
||||||
regions.Lock.ExitReadLock();
|
regions.Lock.ExitReadLock();
|
||||||
|
|
||||||
if (overlaps.Length == 0 && !precise)
|
if (length == 0 && !precise)
|
||||||
{
|
{
|
||||||
if (_memoryManager.IsRangeMapped(address, size))
|
if (_memoryManager.IsRangeMapped(address, size))
|
||||||
{
|
{
|
||||||
|
|
@ -324,7 +325,7 @@ namespace Ryujinx.Memory.Tracking
|
||||||
size += (ulong)_pageSize;
|
size += (ulong)_pageSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < overlaps.Length; i++)
|
for (int i = 0; i < length; i++)
|
||||||
{
|
{
|
||||||
VirtualRegion region = overlaps[i].Value;
|
VirtualRegion region = overlaps[i].Value;
|
||||||
|
|
||||||
|
|
@ -337,6 +338,11 @@ namespace Ryujinx.Memory.Tracking
|
||||||
region.Signal(address, size, write, exemptId);
|
region.Signal(address, size, write, exemptId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (length != 0)
|
||||||
|
{
|
||||||
|
ArrayPool<RangeItem<VirtualRegion>>.Shared.Return(overlaps);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue