Archived
1
0
forked from MeloNX/MeloNX
This repository has been archived on 2025-04-07. You can view files and clone it, but cannot push or open issues or pull requests.
Andrey Sukharev edf7e628ca
Use method overloads that support trimming. Mark some types to be trimming friendly (#4083)
* Use method overloads that support trimming. Mark some types to be trimming friendly

* Use generic version of marshalling method
2022-12-12 15:10:05 +01:00

163 lines
5.2 KiB
C#

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);
}
}
}