Bsnes new - delegate refactoring
Fix reliance on GetFields() ordering which, per MDN, is unspecified. CallingConventionAdapters.MakeWaterbox(...) doesn't care what order they're in, but snes_set_callbacks(...) does.
This commit is contained in:
parent
fe6bf7ba12
commit
a18ea12bc0
|
@ -81,7 +81,7 @@ namespace BizHawk.BizInvoke
|
|||
var aname = new AssemblyName("BizInvokeProxyAssembly");
|
||||
ImplAssemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(aname, AssemblyBuilderAccess.Run);
|
||||
ImplModuleBuilder = ImplAssemblyBuilder.DefineDynamicModule("BizInvokerModule");
|
||||
ClassFieldOffset = BizInvokerUtilities.ComputeClassFieldOffset();
|
||||
ClassFieldOffset = BizInvokerUtilities.ComputeClassFirstFieldOffset();
|
||||
StringOffset = BizInvokerUtilities.ComputeStringOffset();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace BizHawk.BizInvoke
|
||||
{
|
||||
|
@ -44,21 +47,18 @@ namespace BizHawk.BizInvoke
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Didn't I have code somewhere else to do this already?
|
||||
/// Computes the byte offset of the first field of any class relative to a class pointer.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static int ComputeClassFieldOffset()
|
||||
public static int ComputeClassFirstFieldOffset()
|
||||
{
|
||||
var c = new CF();
|
||||
int ret;
|
||||
fixed(int* fx = &c.FirstField)
|
||||
{
|
||||
U u = new(new U2(c));
|
||||
ret = (int) ((ulong) (UIntPtr) fx - (ulong) u.First!.P);
|
||||
}
|
||||
return ret;
|
||||
return ComputeFieldOffset(typeof(CF).GetField("FirstField"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compute the byte offset of the first byte of string data (UTF16) relative to a pointer to the string.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static int ComputeStringOffset()
|
||||
{
|
||||
var s = new string(new char[0]);
|
||||
|
@ -70,5 +70,34 @@ namespace BizHawk.BizInvoke
|
|||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compute the byte offset of a field relative to a pointer to the class instance.
|
||||
/// Slow, so cache it if you need it.
|
||||
/// </summary>
|
||||
public static int ComputeFieldOffset(FieldInfo fi)
|
||||
{
|
||||
if (fi.DeclaringType.IsValueType)
|
||||
{
|
||||
throw new NotImplementedException("Only supported for class fields right now");
|
||||
}
|
||||
|
||||
var obj = FormatterServices.GetUninitializedObject(fi.DeclaringType);
|
||||
var method = new DynamicMethod("ComputeFieldOffsetHelper", typeof(int), new[] { typeof(object) }, typeof(string).Module, true);
|
||||
var il = method.GetILGenerator();
|
||||
var local = il.DeclareLocal(fi.DeclaringType, true);
|
||||
il.Emit(OpCodes.Ldarg_0);
|
||||
il.Emit(OpCodes.Stloc, local);
|
||||
il.Emit(OpCodes.Ldarg_0);
|
||||
il.Emit(OpCodes.Ldflda, fi);
|
||||
il.Emit(OpCodes.Conv_I);
|
||||
il.Emit(OpCodes.Ldarg_0);
|
||||
il.Emit(OpCodes.Conv_I);
|
||||
il.Emit(OpCodes.Sub);
|
||||
il.Emit(OpCodes.Conv_I4);
|
||||
il.Emit(OpCodes.Ret);
|
||||
var del = (Func<object, int>)method.CreateDelegate(typeof(Func<object, int>));
|
||||
return del(obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ using BizHawk.Common;
|
|||
using BizHawk.Emulation.Cores.Waterbox;
|
||||
using BizHawk.BizInvoke;
|
||||
using BizHawk.Emulation.Common;
|
||||
using System.Linq;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Nintendo.BSNES
|
||||
{
|
||||
|
@ -92,12 +93,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.BSNES
|
|||
|
||||
public void SetCallbacks(SnesCallbacks callbacks)
|
||||
{
|
||||
FieldInfo[] fieldInfos = typeof(SnesCallbacks).GetFields();
|
||||
IntPtr[] functionPointerArray = new IntPtr[fieldInfos.Length];
|
||||
for (int i = 0; i < fieldInfos.Length; i++)
|
||||
{
|
||||
functionPointerArray[i] = _adapter.GetFunctionPointerForDelegate((Delegate) fieldInfos[i].GetValue(callbacks));
|
||||
}
|
||||
var functionPointerArray = callbacks
|
||||
.AllDelegatesInMemoryOrder()
|
||||
.Select(f => _adapter.GetFunctionPointerForDelegate(f))
|
||||
.ToArray();
|
||||
core.snes_set_callbacks(functionPointerArray);
|
||||
}
|
||||
|
||||
|
@ -167,6 +166,21 @@ namespace BizHawk.Emulation.Cores.Nintendo.BSNES
|
|||
public snes_audio_sample_t audioSampleCb;
|
||||
public snes_path_request_t pathRequestCb;
|
||||
public snes_trace_t snesTraceCb;
|
||||
|
||||
private static List<FieldInfo> FieldsInOrder = null;
|
||||
|
||||
public IEnumerable<Delegate> AllDelegatesInMemoryOrder()
|
||||
{
|
||||
if (FieldsInOrder == null)
|
||||
{
|
||||
FieldsInOrder = GetType()
|
||||
.GetFields()
|
||||
.OrderBy(fi => BizInvokerUtilities.ComputeFieldOffset(fi))
|
||||
.ToList();
|
||||
}
|
||||
return FieldsInOrder
|
||||
.Select(f => (Delegate)f.GetValue(this));
|
||||
}
|
||||
}
|
||||
|
||||
public void Seal()
|
||||
|
@ -186,7 +200,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.BSNES
|
|||
public void SaveStateBinary(BinaryWriter writer)
|
||||
{
|
||||
// if (serializedSize == 0)
|
||||
// serializedSize = _core.snes_serialized_size();
|
||||
// serializedSize = _core.snes_serialized_size();
|
||||
// TODO: do some profiling and testing to check whether this is actually better than _exe.SaveStateBinary(writer);
|
||||
// re-adding bsnes's own serialization will need to be done once it's confirmed to be deterministic, aka after libco update
|
||||
|
||||
|
|
|
@ -59,16 +59,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.BSNES
|
|||
snesTraceCb = snes_trace
|
||||
};
|
||||
|
||||
Api = new BsnesApi(CoreComm.CoreFileProvider.DllPath(), CoreComm, new Delegate[]
|
||||
{
|
||||
callbacks.inputPollCb,
|
||||
callbacks.inputStateCb,
|
||||
callbacks.noLagCb,
|
||||
callbacks.videoFrameCb,
|
||||
callbacks.audioSampleCb,
|
||||
callbacks.pathRequestCb,
|
||||
callbacks.snesTraceCb
|
||||
});
|
||||
Api = new BsnesApi(CoreComm.CoreFileProvider.DllPath(), CoreComm, callbacks.AllDelegatesInMemoryOrder());
|
||||
|
||||
_controllers = new BsnesControllers(_syncSettings);
|
||||
|
||||
|
|
Loading…
Reference in New Issue