Eliminate boxing allocations caused by ISampledData structs (#4556)
* Redesign use of ISampledData for accessing the SamplingNumber value on input data structs. * Always read SamplingNumber as little-endian * Restored field order for SixAxisSensorState. Rework to allow possibility of non-zero offsets for the SamplingNumber field. Set StructLayout Pack=8 - the KeyboardState struct is 4 bytes shorter with any other value. * fix spelling Co-authored-by: riperiperi <rhy3756547@hotmail.com> * set Pack = 1 for ISampledDataStruct types, added Unknown field to KeyboardState * extend size of KeyboardModifier --------- Co-authored-by: riperiperi <rhy3756547@hotmail.com>
This commit is contained in:
parent
c95be55091
commit
49be977588
12 changed files with 91 additions and 34 deletions
|
@ -2,7 +2,7 @@
|
|||
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common
|
||||
{
|
||||
struct AtomicStorage<T> where T: unmanaged
|
||||
struct AtomicStorage<T> where T: unmanaged, ISampledDataStruct
|
||||
{
|
||||
public ulong SamplingNumber;
|
||||
public T Object;
|
||||
|
@ -14,9 +14,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common
|
|||
|
||||
public void SetObject(ref T obj)
|
||||
{
|
||||
ISampledData samplingProvider = obj as ISampledData;
|
||||
ulong samplingNumber = ISampledDataStruct.GetSamplingNumber(ref obj);
|
||||
|
||||
Interlocked.Exchange(ref SamplingNumber, samplingProvider.SamplingNumber);
|
||||
Interlocked.Exchange(ref SamplingNumber, samplingNumber);
|
||||
|
||||
Thread.MemoryBarrier();
|
||||
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common
|
||||
{
|
||||
interface ISampledData
|
||||
{
|
||||
ulong SamplingNumber { get; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
using System;
|
||||
using System.Buffers.Binary;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common
|
||||
{
|
||||
/// <summary>
|
||||
/// This is a "marker interface" to add some compile-time safety to a convention-based optimization.
|
||||
///
|
||||
/// Any struct implementing this interface should:
|
||||
/// - use <c>StructLayoutAttribute</c> (and related attributes) to explicity control how the struct is laid out in memory.
|
||||
/// - ensure that the method <c>ISampledDataStruct.GetSamplingNumberFieldOffset()</c> correctly returns the offset, in bytes,
|
||||
/// to the ulong "Sampling Number" field within the struct. Most types have it as the first field, so the default offset is 0.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// <c>
|
||||
/// [StructLayout(LayoutKind.Sequential, Pack = 8)]
|
||||
/// struct DebugPadState : ISampledDataStruct
|
||||
/// {
|
||||
/// public ulong SamplingNumber; // 1st field, so no need to add special handling to GetSamplingNumberFieldOffset()
|
||||
/// // other members...
|
||||
/// }
|
||||
///
|
||||
/// [StructLayout(LayoutKind.Sequential, Pack = 8)]
|
||||
/// struct SixAxisSensorState : ISampledDataStruct
|
||||
/// {
|
||||
/// public ulong DeltaTime;
|
||||
/// public ulong SamplingNumber; // Not the first field - needs special handling in GetSamplingNumberFieldOffset()
|
||||
/// // other members...
|
||||
/// }
|
||||
/// </c>
|
||||
/// </summary>
|
||||
internal interface ISampledDataStruct
|
||||
{
|
||||
// No Instance Members - marker interface only
|
||||
|
||||
public static ulong GetSamplingNumber<T>(ref T sampledDataStruct) where T : unmanaged, ISampledDataStruct
|
||||
{
|
||||
ReadOnlySpan<T> structSpan = MemoryMarshal.CreateReadOnlySpan(ref sampledDataStruct, 1);
|
||||
|
||||
ReadOnlySpan<byte> byteSpan = MemoryMarshal.Cast<T, byte>(structSpan);
|
||||
|
||||
int fieldOffset = GetSamplingNumberFieldOffset(ref sampledDataStruct);
|
||||
|
||||
if (fieldOffset > 0)
|
||||
{
|
||||
byteSpan = byteSpan.Slice(fieldOffset);
|
||||
}
|
||||
|
||||
ulong value = BinaryPrimitives.ReadUInt64LittleEndian(byteSpan);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private static int GetSamplingNumberFieldOffset<T>(ref T sampledDataStruct) where T : unmanaged, ISampledDataStruct
|
||||
{
|
||||
return sampledDataStruct switch
|
||||
{
|
||||
Npad.SixAxisSensorState _ => sizeof(ulong),
|
||||
_ => 0
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@ using System.Threading;
|
|||
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common
|
||||
{
|
||||
struct RingLifo<T> where T: unmanaged
|
||||
struct RingLifo<T> where T: unmanaged, ISampledDataStruct
|
||||
{
|
||||
private const ulong MaxEntries = 17;
|
||||
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.DebugPad
|
||||
{
|
||||
struct DebugPadState : ISampledData
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
struct DebugPadState : ISampledDataStruct
|
||||
{
|
||||
public ulong SamplingNumber;
|
||||
public DebugPadAttribute Attributes;
|
||||
public DebugPadButton Buttons;
|
||||
public AnalogStickState AnalogStickR;
|
||||
public AnalogStickState AnalogStickL;
|
||||
|
||||
ulong ISampledData.SamplingNumber => SamplingNumber;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,9 +2,8 @@
|
|||
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Keyboard
|
||||
{
|
||||
// TODO: This seems entirely wrong
|
||||
[Flags]
|
||||
enum KeyboardModifier : uint
|
||||
enum KeyboardModifier : ulong
|
||||
{
|
||||
None = 0,
|
||||
Control = 1 << 0,
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Keyboard
|
||||
{
|
||||
struct KeyboardState : ISampledData
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
struct KeyboardState : ISampledDataStruct
|
||||
{
|
||||
public ulong SamplingNumber;
|
||||
public KeyboardModifier Modifiers;
|
||||
public KeyboardKey Keys;
|
||||
|
||||
ulong ISampledData.SamplingNumber => SamplingNumber;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Mouse
|
||||
{
|
||||
struct MouseState : ISampledData
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
struct MouseState : ISampledDataStruct
|
||||
{
|
||||
public ulong SamplingNumber;
|
||||
public int X;
|
||||
|
@ -13,7 +15,5 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Mouse
|
|||
public int WheelDeltaY;
|
||||
public MouseButton Buttons;
|
||||
public MouseAttribute Attributes;
|
||||
|
||||
ulong ISampledData.SamplingNumber => SamplingNumber;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad
|
||||
{
|
||||
struct NpadCommonState : ISampledData
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
struct NpadCommonState : ISampledDataStruct
|
||||
{
|
||||
public ulong SamplingNumber;
|
||||
public NpadButton Buttons;
|
||||
|
@ -10,7 +12,5 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad
|
|||
public AnalogStickState AnalogStickR;
|
||||
public NpadAttribute Attributes;
|
||||
private uint _reserved;
|
||||
|
||||
ulong ISampledData.SamplingNumber => SamplingNumber;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad
|
||||
{
|
||||
struct NpadGcTriggerState : ISampledData
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
struct NpadGcTriggerState : ISampledDataStruct
|
||||
{
|
||||
#pragma warning disable CS0649
|
||||
public ulong SamplingNumber;
|
||||
public uint TriggerL;
|
||||
public uint TriggerR;
|
||||
#pragma warning restore CS0649
|
||||
|
||||
ulong ISampledData.SamplingNumber => SamplingNumber;
|
||||
}
|
||||
}
|
|
@ -1,9 +1,11 @@
|
|||
using Ryujinx.Common.Memory;
|
||||
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad
|
||||
{
|
||||
struct SixAxisSensorState : ISampledData
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
struct SixAxisSensorState : ISampledDataStruct
|
||||
{
|
||||
public ulong DeltaTime;
|
||||
public ulong SamplingNumber;
|
||||
|
@ -13,7 +15,5 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad
|
|||
public Array9<float> Direction;
|
||||
public SixAxisSensorAttribute Attributes;
|
||||
private uint _reserved;
|
||||
|
||||
ulong ISampledData.SamplingNumber => SamplingNumber;
|
||||
}
|
||||
}
|
|
@ -1,15 +1,15 @@
|
|||
using Ryujinx.Common.Memory;
|
||||
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.TouchScreen
|
||||
{
|
||||
struct TouchScreenState : ISampledData
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
struct TouchScreenState : ISampledDataStruct
|
||||
{
|
||||
public ulong SamplingNumber;
|
||||
public int TouchesCount;
|
||||
private int _reserved;
|
||||
public Array16<TouchState> Touches;
|
||||
|
||||
ulong ISampledData.SamplingNumber => SamplingNumber;
|
||||
}
|
||||
}
|
||||
|
|
Reference in a new issue