diff options
Diffstat (limited to 'Ryujinx.Graphics.Device')
| -rw-r--r-- | Ryujinx.Graphics.Device/AccessControl.cs | 10 | ||||
| -rw-r--r-- | Ryujinx.Graphics.Device/DeviceState.cs | 124 | ||||
| -rw-r--r-- | Ryujinx.Graphics.Device/IDeviceState.cs | 8 | ||||
| -rw-r--r-- | Ryujinx.Graphics.Device/RegisterAttribute.cs | 15 | ||||
| -rw-r--r-- | Ryujinx.Graphics.Device/RwCallback.cs | 16 | ||||
| -rw-r--r-- | Ryujinx.Graphics.Device/Ryujinx.Graphics.Device.csproj | 7 | ||||
| -rw-r--r-- | Ryujinx.Graphics.Device/SizeCalculator.cs | 63 |
7 files changed, 243 insertions, 0 deletions
diff --git a/Ryujinx.Graphics.Device/AccessControl.cs b/Ryujinx.Graphics.Device/AccessControl.cs new file mode 100644 index 00000000..02203783 --- /dev/null +++ b/Ryujinx.Graphics.Device/AccessControl.cs @@ -0,0 +1,10 @@ +namespace Ryujinx.Graphics.Device +{ + public enum AccessControl + { + None = 0, + ReadOnly = 1 << 0, + WriteOnly = 1 << 1, + ReadWrite = ReadOnly | WriteOnly + } +} diff --git a/Ryujinx.Graphics.Device/DeviceState.cs b/Ryujinx.Graphics.Device/DeviceState.cs new file mode 100644 index 00000000..ea6942ec --- /dev/null +++ b/Ryujinx.Graphics.Device/DeviceState.cs @@ -0,0 +1,124 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Reflection; +using System.Runtime.CompilerServices; + +namespace Ryujinx.Graphics.Device +{ + public class DeviceState<TState> : IDeviceState where TState : unmanaged + { + private const int RegisterSize = sizeof(int); + + public TState State; + + private readonly BitArray _readableRegisters; + private readonly BitArray _writableRegisters; + + private readonly Dictionary<int, Func<int>> _readCallbacks; + private readonly Dictionary<int, Action<int>> _writeCallbacks; + + public DeviceState(IReadOnlyDictionary<string, RwCallback> callbacks = null) + { + int size = (Unsafe.SizeOf<TState>() + RegisterSize - 1) / RegisterSize; + + _readableRegisters = new BitArray(size); + _writableRegisters = new BitArray(size); + + _readCallbacks = new Dictionary<int, Func<int>>(); + _writeCallbacks = new Dictionary<int, Action<int>>(); + + var fields = typeof(TState).GetFields(); + int offset = 0; + + for (int fieldIndex = 0; fieldIndex < fields.Length; fieldIndex++) + { + var field = fields[fieldIndex]; + var regAttr = field.GetCustomAttributes<RegisterAttribute>(false).FirstOrDefault(); + + int sizeOfField = SizeCalculator.SizeOf(field.FieldType); + + for (int i = 0; i < ((sizeOfField + 3) & ~3); i += 4) + { + _readableRegisters[(offset + i) / RegisterSize] = regAttr?.AccessControl.HasFlag(AccessControl.ReadOnly) ?? true; + _writableRegisters[(offset + i) / RegisterSize] = regAttr?.AccessControl.HasFlag(AccessControl.WriteOnly) ?? true; + } + + if (callbacks != null && callbacks.TryGetValue(field.Name, out var cb)) + { + if (cb.Read != null) + { + _readCallbacks.Add(offset, cb.Read); + } + + if (cb.Write != null) + { + _writeCallbacks.Add(offset, cb.Write); + } + } + + offset += sizeOfField; + } + + Debug.Assert(offset == Unsafe.SizeOf<TState>()); + } + + public virtual int Read(int offset) + { + if (Check(offset) && _readableRegisters[offset / RegisterSize]) + { + int alignedOffset = Align(offset); + + if (_readCallbacks.TryGetValue(alignedOffset, out Func<int> read)) + { + return read(); + } + else + { + return GetRef<int>(alignedOffset); + } + } + + return 0; + } + + public virtual void Write(int offset, int data) + { + if (Check(offset) && _writableRegisters[offset / RegisterSize]) + { + int alignedOffset = Align(offset); + + if (_writeCallbacks.TryGetValue(alignedOffset, out Action<int> write)) + { + write(data); + } + else + { + GetRef<int>(alignedOffset) = data; + } + } + } + + private bool Check(int offset) + { + return (uint)Align(offset) < Unsafe.SizeOf<TState>(); + } + + 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 Unsafe.As<TState, T>(ref Unsafe.AddByteOffset(ref State, (IntPtr)offset)); + } + + private static int Align(int offset) + { + return offset & ~(RegisterSize - 1); + } + } +} diff --git a/Ryujinx.Graphics.Device/IDeviceState.cs b/Ryujinx.Graphics.Device/IDeviceState.cs new file mode 100644 index 00000000..077d69f2 --- /dev/null +++ b/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/Ryujinx.Graphics.Device/RegisterAttribute.cs b/Ryujinx.Graphics.Device/RegisterAttribute.cs new file mode 100644 index 00000000..6e198963 --- /dev/null +++ b/Ryujinx.Graphics.Device/RegisterAttribute.cs @@ -0,0 +1,15 @@ +using System; + +namespace Ryujinx.Graphics.Device +{ + [AttributeUsage(AttributeTargets.Field, AllowMultiple = false)] + public sealed class RegisterAttribute : Attribute + { + public AccessControl AccessControl { get; } + + public RegisterAttribute(AccessControl ac) + { + AccessControl = ac; + } + } +} diff --git a/Ryujinx.Graphics.Device/RwCallback.cs b/Ryujinx.Graphics.Device/RwCallback.cs new file mode 100644 index 00000000..6f1c8898 --- /dev/null +++ b/Ryujinx.Graphics.Device/RwCallback.cs @@ -0,0 +1,16 @@ +using System; + +namespace Ryujinx.Graphics.Device +{ + public 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/Ryujinx.Graphics.Device/Ryujinx.Graphics.Device.csproj b/Ryujinx.Graphics.Device/Ryujinx.Graphics.Device.csproj new file mode 100644 index 00000000..7c4ae4ca --- /dev/null +++ b/Ryujinx.Graphics.Device/Ryujinx.Graphics.Device.csproj @@ -0,0 +1,7 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <TargetFramework>netcoreapp3.1</TargetFramework> + </PropertyGroup> + +</Project> diff --git a/Ryujinx.Graphics.Device/SizeCalculator.cs b/Ryujinx.Graphics.Device/SizeCalculator.cs new file mode 100644 index 00000000..7cc48915 --- /dev/null +++ b/Ryujinx.Graphics.Device/SizeCalculator.cs @@ -0,0 +1,63 @@ +using System; +using System.Reflection; + +namespace Ryujinx.Graphics.Device +{ + 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.") + }; + } + } +} |
