Skip to content

Instantly share code, notes, and snippets.

@Jura-Z
Last active June 17, 2023 18:05
Show Gist options
  • Save Jura-Z/03960c9b9ceacb270256a26e80fb77a1 to your computer and use it in GitHub Desktop.
Save Jura-Z/03960c9b9ceacb270256a26e80fb77a1 to your computer and use it in GitHub Desktop.
Burst -> Managed C# call
internal static class Burst2ManagedCall<T, Key>
{
private static T s_Delegate;
// alignment 16 is important to not crash on arm cpu
private static readonly SharedStatic<FunctionPointer<T>> s_SharedStatic = SharedStatic<FunctionPointer<T>>.GetOrCreate<FunctionPointer<T>, Key>(16);
public static bool IsCreated => s_SharedStatic.Data.IsCreated;
public static void Init(T @delegate)
{
CheckIsNotCreated();
s_Delegate = @delegate;
s_SharedStatic.Data = new FunctionPointer<T>(Marshal.GetFunctionPointerForDelegate(s_Delegate));
}
public static ref FunctionPointer<T> Ptr()
{
CheckIsCreated();
return ref s_SharedStatic.Data;
}
[Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")] // ENABLE_UNITY_COLLECTIONS_CHECKS or UNITY_DOTS_DEBUG
private static void CheckIsCreated()
{
if (IsCreated == false)
throw new InvalidOperationException("Burst2ManagedCall was NOT created!");
}
[Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")] // ENABLE_UNITY_COLLECTIONS_CHECKS or UNITY_DOTS_DEBUG
private static void CheckIsNotCreated()
{
if (IsCreated)
throw new InvalidOperationException("Burst2ManagedCall was already created!");
}
}
//////////////////////////////
// Usage:
// key to avoid conflicts, like in SharedStatic
private struct UniqueStructKey{}
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public unsafe delegate void SystemWriteLineDelegate(byte* stringBuffer, int bufferLength, byte newLine);
static void Init()
{
// somewhere in managed C#:
Burst2ManagedCall<SystemWriteLineDelegate, UniqueStructKey>.Init(ConsoleWriteImpl);
}
// example of the function to call
[AOT.MonoPInvokeCallback(typeof(SystemWriteLineDelegate))]
private static unsafe void ConsoleWriteImpl(byte* stringBuffer, int bufferLength, byte newLine)
{
// C# managed land
try
{
if (newLine != 0)
{
System.Console.WriteLine(System.Text.Encoding.UTF8.GetString(stringBuffer, bufferLength));
}
else
{
System.Console.Write(System.Text.Encoding.UTF8.GetString(stringBuffer, bufferLength));
}
}
catch (Exception ex)
{
System.Console.WriteLine(ex.Message);
}
}
// Calling from burst:
var ptr = Burst2ManagedCall<SystemWriteLineDelegate, UniqueStructKey>.Ptr();
#if CAN_USE_UNMANAGED_DELEGATES
unsafe
{
// this is better - not going to alloc if burst is disabled
((delegate * unmanaged[Cdecl] <byte*, int, byte, void>)ptr.Value)(data, length, newLine);
}
#else
ptr.Invoke(data, length, newLine);
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment