diff options
Diffstat (limited to 'src/Ryujinx.Graphics.Device')
| -rw-r--r-- | src/Ryujinx.Graphics.Device/DeviceState.cs | 162 | ||||
| -rw-r--r-- | src/Ryujinx.Graphics.Device/IDeviceState.cs | 8 | ||||
| -rw-r--r-- | src/Ryujinx.Graphics.Device/IDeviceStateWithContext.cs | 9 | ||||
| -rw-r--r-- | src/Ryujinx.Graphics.Device/RwCallback.cs | 16 | ||||
| -rw-r--r-- | src/Ryujinx.Graphics.Device/Ryujinx.Graphics.Device.csproj | 7 | ||||
| -rw-r--r-- | src/Ryujinx.Graphics.Device/SizeCalculator.cs | 63 |
6 files changed, 265 insertions, 0 deletions
diff --git a/src/Ryujinx.Graphics.Device/DeviceState.cs b/src/Ryujinx.Graphics.Device/DeviceState.cs new file mode 100644 index 00000000..a9b446e1 --- /dev/null +++ b/src/Ryujinx.Graphics.Device/DeviceState.cs @@ -0,0 +1,162 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Ryujinx.Graphics.Device +{ + public class DeviceState<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] TState> : IDeviceState where TState : unmanaged + { + private const int RegisterSize = sizeof(int); + + public TState State; + + private uint Size => (uint)(Unsafe.SizeOf<TState>() + RegisterSize - 1) / RegisterSize; + + private readonly Func<int>[] _readCallbacks; + private readonly Action<int>[] _writeCallbacks; + + private readonly Dictionary<uint, string> _fieldNamesForDebug; + private readonly Action<string> _debugLogCallback; + + public DeviceState(IReadOnlyDictionary<string, RwCallback> callbacks = null, Action<string> debugLogCallback = null) + { + _readCallbacks = new Func<int>[Size]; + _writeCallbacks = new Action<int>[Size]; + + if (debugLogCallback != null) + { + _fieldNamesForDebug = new Dictionary<uint, string>(); + _debugLogCallback = debugLogCallback; + } + + var fields = typeof(TState).GetFields(); + int offset = 0; + + for (int fieldIndex = 0; fieldIndex < fields.Length; fieldIndex++) + { + var field = fields[fieldIndex]; + + int sizeOfField = SizeCalculator.SizeOf(field.FieldType); + + for (int i = 0; i < ((sizeOfField + 3) & ~3); i += 4) + { + int index = (offset + i) / RegisterSize; + + if (callbacks != null && callbacks.TryGetValue(field.Name, out var cb)) + { + if (cb.Read != null) + { + _readCallbacks[index] = cb.Read; + } + + if (cb.Write != null) + { + _writeCallbacks[index] = cb.Write; + } + } + } + + if (debugLogCallback != null) + { + _fieldNamesForDebug.Add((uint)offset, field.Name); + } + + offset += sizeOfField; + } + + Debug.Assert(offset == Unsafe.SizeOf<TState>()); + } + + public int Read(int offset) + { + uint index = (uint)offset / RegisterSize; + + if (index < Size) + { + uint alignedOffset = index * RegisterSize; + + var readCallback = Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(_readCallbacks), (IntPtr)index); + if (readCallback != null) + { + return readCallback(); + } + else + { + return GetRefUnchecked<int>(alignedOffset); + } + } + + return 0; + } + + public void Write(int offset, int data) + { + uint index = (uint)offset / RegisterSize; + + if (index < Size) + { + uint alignedOffset = index * RegisterSize; + DebugWrite(alignedOffset, data); + + GetRefIntAlignedUncheck(index) = data; + + Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(_writeCallbacks), (IntPtr)index)?.Invoke(data); + } + } + + public void WriteWithRedundancyCheck(int offset, int data, out bool changed) + { + uint index = (uint)offset / RegisterSize; + + if (index < Size) + { + uint alignedOffset = index * RegisterSize; + DebugWrite(alignedOffset, data); + + ref var storage = ref GetRefIntAlignedUncheck(index); + changed = storage != data; + storage = data; + + Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(_writeCallbacks), (IntPtr)index)?.Invoke(data); + } + else + { + changed = false; + } + } + + [Conditional("DEBUG")] + private void DebugWrite(uint alignedOffset, int data) + { + if (_fieldNamesForDebug != null && _fieldNamesForDebug.TryGetValue(alignedOffset, out string fieldName)) + { + _debugLogCallback($"{typeof(TState).Name}.{fieldName} = 0x{data:X}"); + } + } + + public ref T GetRef<T>(int offset) where T : unmanaged + { + if ((uint)(offset + Unsafe.SizeOf<T>()) > Unsafe.SizeOf<TState>()) + { + throw new ArgumentOutOfRangeException(nameof(offset)); + } + + return ref GetRefUnchecked<T>((uint)offset); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private ref T GetRefUnchecked<T>(uint offset) where T : unmanaged + { + return ref Unsafe.As<TState, T>(ref Unsafe.AddByteOffset(ref State, (IntPtr)offset)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private ref int GetRefIntAlignedUncheck(ulong index) + { + return ref Unsafe.Add(ref Unsafe.As<TState, int>(ref State), (IntPtr)index); + } + } +} diff --git a/src/Ryujinx.Graphics.Device/IDeviceState.cs b/src/Ryujinx.Graphics.Device/IDeviceState.cs new file mode 100644 index 00000000..077d69f2 --- /dev/null +++ b/src/Ryujinx.Graphics.Device/IDeviceState.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.Graphics.Device +{ + public interface IDeviceState + { + int Read(int offset); + void Write(int offset, int data); + } +} diff --git a/src/Ryujinx.Graphics.Device/IDeviceStateWithContext.cs b/src/Ryujinx.Graphics.Device/IDeviceStateWithContext.cs new file mode 100644 index 00000000..17b2fa21 --- /dev/null +++ b/src/Ryujinx.Graphics.Device/IDeviceStateWithContext.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.Graphics.Device +{ + public interface IDeviceStateWithContext : IDeviceState + { + long CreateContext(); + void DestroyContext(long id); + void BindContext(long id); + } +} diff --git a/src/Ryujinx.Graphics.Device/RwCallback.cs b/src/Ryujinx.Graphics.Device/RwCallback.cs new file mode 100644 index 00000000..dc8499e9 --- /dev/null +++ b/src/Ryujinx.Graphics.Device/RwCallback.cs @@ -0,0 +1,16 @@ +using System; + +namespace Ryujinx.Graphics.Device +{ + public readonly struct RwCallback + { + public Action<int> Write { get; } + public Func<int> Read { get; } + + public RwCallback(Action<int> write, Func<int> read) + { + Write = write; + Read = read; + } + } +} diff --git a/src/Ryujinx.Graphics.Device/Ryujinx.Graphics.Device.csproj b/src/Ryujinx.Graphics.Device/Ryujinx.Graphics.Device.csproj new file mode 100644 index 00000000..082dac9c --- /dev/null +++ b/src/Ryujinx.Graphics.Device/Ryujinx.Graphics.Device.csproj @@ -0,0 +1,7 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <TargetFramework>net7.0</TargetFramework> + </PropertyGroup> + +</Project> diff --git a/src/Ryujinx.Graphics.Device/SizeCalculator.cs b/src/Ryujinx.Graphics.Device/SizeCalculator.cs new file mode 100644 index 00000000..7409c041 --- /dev/null +++ b/src/Ryujinx.Graphics.Device/SizeCalculator.cs @@ -0,0 +1,63 @@ +using System; +using System.Reflection; + +namespace Ryujinx.Graphics.Device +{ + public static class SizeCalculator + { + public static int SizeOf(Type type) + { + // Is type a enum type? + if (type.IsEnum) + { + type = type.GetEnumUnderlyingType(); + } + + // Is type a pointer type? + if (type.IsPointer || type == typeof(IntPtr) || type == typeof(UIntPtr)) + { + return IntPtr.Size; + } + + // Is type a struct type? + if (type.IsValueType && !type.IsPrimitive) + { + // Check if the struct has a explicit size, if so, return that. + if (type.StructLayoutAttribute.Size != 0) + { + return type.StructLayoutAttribute.Size; + } + + // Otherwise we calculate the sum of the sizes of all fields. + int size = 0; + var fields = type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + + for (int fieldIndex = 0; fieldIndex < fields.Length; fieldIndex++) + { + size += SizeOf(fields[fieldIndex].FieldType); + } + + return size; + } + + // Primitive types. + return (Type.GetTypeCode(type)) switch + { + TypeCode.SByte => sizeof(sbyte), + TypeCode.Byte => sizeof(byte), + TypeCode.Int16 => sizeof(short), + TypeCode.UInt16 => sizeof(ushort), + TypeCode.Int32 => sizeof(int), + TypeCode.UInt32 => sizeof(uint), + TypeCode.Int64 => sizeof(long), + TypeCode.UInt64 => sizeof(ulong), + TypeCode.Char => sizeof(char), + TypeCode.Single => sizeof(float), + TypeCode.Double => sizeof(double), + TypeCode.Decimal => sizeof(decimal), + TypeCode.Boolean => sizeof(bool), + _ => throw new ArgumentException($"Length for type \"{type.Name}\" is unknown.") + }; + } + } +} |
