From 38840fa7c32c0d922aab5bc879c0e7b54424495c Mon Sep 17 00:00:00 2001 From: Nico de Poel Date: Mon, 29 Mar 2021 11:00:24 +0200 Subject: [PATCH] Reorganized callbacks so that the function pointers and the containing class are all pinned, which makes them safe to store inside the DLL and reuse them on subsequent update calls. --- Assets/Scripts/Uniquake.cs | 61 ++++++++++++++++++++++++++++++++------ 1 file changed, 52 insertions(+), 9 deletions(-) diff --git a/Assets/Scripts/Uniquake.cs b/Assets/Scripts/Uniquake.cs index 859e319..7c65509 100644 --- a/Assets/Scripts/Uniquake.cs +++ b/Assets/Scripts/Uniquake.cs @@ -11,8 +11,8 @@ public class Uniquake: MonoBehaviour private const int MemSize = 0x4000000; // 64 MB of heap space private const int MaxArgs = 50; // Corresponds with MAX_NUM_ARGVS in engine - private UnityCallbacks callbacks = new UnityCallbacks(); private QuakeParms quakeParms; + private UnityCallbacks callbacks = new UnityCallbacks(); void Start() { @@ -32,7 +32,7 @@ public class Uniquake: MonoBehaviour quakeParms = new QuakeParms { - baseDir = Application.dataPath, + baseDir = Application.persistentDataPath, cacheDir = null, argc = arguments.Length, argv = argv, @@ -40,7 +40,7 @@ public class Uniquake: MonoBehaviour memSize = MemSize, }; - Uniquake_Init(callbacks, quakeParms); + Uniquake_Init(callbacks.CallbacksPtr, quakeParms); } void Update() @@ -50,6 +50,8 @@ public class Uniquake: MonoBehaviour private void OnDestroy() { + callbacks.Destroy(); + if (quakeParms != null) { if (quakeParms.memBase != IntPtr.Zero) @@ -76,7 +78,7 @@ public class Uniquake: MonoBehaviour private static extern void Uniquake_Echo(UnityCallbacks.DebugLogCallback logCallback, [MarshalAs(UnmanagedType.LPStr)] string message); [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] - private static extern void Uniquake_Init(UnityCallbacks callbacks, QuakeParms parms); + private static extern void Uniquake_Init(IntPtr callbacks, QuakeParms parms); [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] private static extern void Uniquake_Update(float deltaTime); @@ -107,20 +109,61 @@ public class Uniquake: MonoBehaviour return Time.realtimeSinceStartupAsDouble; } - [StructLayout(LayoutKind.Sequential, Pack = 0)] + private class UnityCallbacks { + private UnityCallbacksContainer container; + private GCHandle containerHandle; + + public UnityCallbacks() + { + container = new UnityCallbacksContainer(); + + CreateCallback(Callback_DebugLog, out DebugLogHandle, out container.DebugLogPtr); + CreateCallback(Callback_DebugLogError, out DebugLogErrorHandle, out container.DebugLogErrorPtr); + CreateCallback(Callback_ApplicationQuit, out ApplicationQuitHandle, out container.ApplicationQuitPtr); + CreateCallback(Callback_RealtimeSinceStartup, out RealtimeSinceStartupHandle, out container.RealtimeSinceStartupPtr); + + containerHandle = GCHandle.Alloc(container, GCHandleType.Pinned); + } + + public IntPtr CallbacksPtr => containerHandle.AddrOfPinnedObject(); + + public void Destroy() + { + containerHandle.Free(); + DebugLogHandle.Free(); + DebugLogErrorHandle.Free(); + ApplicationQuitHandle.Free(); + RealtimeSinceStartupHandle.Free(); + } + + private static void CreateCallback(TDelegate callback, out GCHandle handle, out IntPtr ptr) + { + handle = GCHandle.Alloc(callback); + ptr = Marshal.GetFunctionPointerForDelegate(callback); + } + public delegate void DebugLogCallback([MarshalAs(UnmanagedType.LPStr)] string message); - public DebugLogCallback DebugLog = Callback_DebugLog; + private GCHandle DebugLogHandle; public delegate void DebugLogErrorCallback([MarshalAs(UnmanagedType.LPStr)] string message); - public DebugLogErrorCallback DebugLogError = Callback_DebugLogError; + private GCHandle DebugLogErrorHandle; public delegate void ApplicationQuitCallback(int exitCode); - public ApplicationQuitCallback ApplicationQuit = Callback_ApplicationQuit; + private GCHandle ApplicationQuitHandle; public delegate double RealtimeSinceStartupCallback(); - public RealtimeSinceStartupCallback RealtimeSinceStartup = Callback_RealtimeSinceStartup; + private GCHandle RealtimeSinceStartupHandle; + } + + [StructLayout(LayoutKind.Sequential, Pack = 0)] + private class UnityCallbacksContainer + { + public IntPtr DebugLogPtr; + public IntPtr DebugLogErrorPtr; + public IntPtr ApplicationQuitPtr; + public IntPtr RealtimeSinceStartupPtr; } [StructLayout(LayoutKind.Sequential, Pack = 0)]