DeviceState.cs 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.Runtime.CompilerServices;
  5. using System.Runtime.InteropServices;
  6. namespace Ryujinx.Graphics.Device
  7. {
  8. public class DeviceState<TState> : IDeviceState where TState : unmanaged
  9. {
  10. private const int RegisterSize = sizeof(int);
  11. public TState State;
  12. private uint Size => (uint)(Unsafe.SizeOf<TState>() + RegisterSize - 1) / RegisterSize;
  13. private readonly Func<int>[] _readCallbacks;
  14. private readonly Action<int>[] _writeCallbacks;
  15. private readonly Dictionary<uint, string> _fieldNamesForDebug;
  16. private readonly Action<string> _debugLogCallback;
  17. public DeviceState(IReadOnlyDictionary<string, RwCallback> callbacks = null, Action<string> debugLogCallback = null)
  18. {
  19. _readCallbacks = new Func<int>[Size];
  20. _writeCallbacks = new Action<int>[Size];
  21. if (debugLogCallback != null)
  22. {
  23. _fieldNamesForDebug = new Dictionary<uint, string>();
  24. _debugLogCallback = debugLogCallback;
  25. }
  26. var fields = typeof(TState).GetFields();
  27. int offset = 0;
  28. for (int fieldIndex = 0; fieldIndex < fields.Length; fieldIndex++)
  29. {
  30. var field = fields[fieldIndex];
  31. int sizeOfField = SizeCalculator.SizeOf(field.FieldType);
  32. for (int i = 0; i < ((sizeOfField + 3) & ~3); i += 4)
  33. {
  34. int index = (offset + i) / RegisterSize;
  35. if (callbacks != null && callbacks.TryGetValue(field.Name, out var cb))
  36. {
  37. if (cb.Read != null)
  38. {
  39. _readCallbacks[index] = cb.Read;
  40. }
  41. if (cb.Write != null)
  42. {
  43. _writeCallbacks[index] = cb.Write;
  44. }
  45. }
  46. }
  47. if (debugLogCallback != null)
  48. {
  49. _fieldNamesForDebug.Add((uint)offset, field.Name);
  50. }
  51. offset += sizeOfField;
  52. }
  53. Debug.Assert(offset == Unsafe.SizeOf<TState>());
  54. }
  55. public int Read(int offset)
  56. {
  57. uint index = (uint)offset / RegisterSize;
  58. if (index < Size)
  59. {
  60. uint alignedOffset = index * RegisterSize;
  61. var readCallback = Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(_readCallbacks), (IntPtr)index);
  62. if (readCallback != null)
  63. {
  64. return readCallback();
  65. }
  66. else
  67. {
  68. return GetRefUnchecked<int>(alignedOffset);
  69. }
  70. }
  71. return 0;
  72. }
  73. public void Write(int offset, int data)
  74. {
  75. uint index = (uint)offset / RegisterSize;
  76. if (index < Size)
  77. {
  78. uint alignedOffset = index * RegisterSize;
  79. DebugWrite(alignedOffset, data);
  80. GetRefIntAlignedUncheck(index) = data;
  81. Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(_writeCallbacks), (IntPtr)index)?.Invoke(data);
  82. }
  83. }
  84. public void WriteWithRedundancyCheck(int offset, int data, out bool changed)
  85. {
  86. uint index = (uint)offset / RegisterSize;
  87. if (index < Size)
  88. {
  89. uint alignedOffset = index * RegisterSize;
  90. DebugWrite(alignedOffset, data);
  91. ref var storage = ref GetRefIntAlignedUncheck(index);
  92. changed = storage != data;
  93. storage = data;
  94. Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(_writeCallbacks), (IntPtr)index)?.Invoke(data);
  95. }
  96. else
  97. {
  98. changed = false;
  99. }
  100. }
  101. [Conditional("DEBUG")]
  102. private void DebugWrite(uint alignedOffset, int data)
  103. {
  104. if (_fieldNamesForDebug != null && _fieldNamesForDebug.TryGetValue(alignedOffset, out string fieldName))
  105. {
  106. _debugLogCallback($"{typeof(TState).Name}.{fieldName} = 0x{data:X}");
  107. }
  108. }
  109. public ref T GetRef<T>(int offset) where T : unmanaged
  110. {
  111. if ((uint)(offset + Unsafe.SizeOf<T>()) > Unsafe.SizeOf<TState>())
  112. {
  113. throw new ArgumentOutOfRangeException(nameof(offset));
  114. }
  115. return ref GetRefUnchecked<T>((uint)offset);
  116. }
  117. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  118. private ref T GetRefUnchecked<T>(uint offset) where T : unmanaged
  119. {
  120. return ref Unsafe.As<TState, T>(ref Unsafe.AddByteOffset(ref State, (IntPtr)offset));
  121. }
  122. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  123. private ref int GetRefIntAlignedUncheck(ulong index)
  124. {
  125. return ref Unsafe.Add(ref Unsafe.As<TState, int>(ref State), (IntPtr)index);
  126. }
  127. }
  128. }