Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Feature Request Full support for C# 9 FunctionPointer (delegate* unmanaged[Cdecl]).

Discussion in 'Burst' started by mm_hohu, Jul 16, 2023.

  1. mm_hohu

    mm_hohu

    Joined:
    Jun 4, 2021
    Posts:
    38
    Code (CSharp):
    1. using System.Runtime.InteropServices;
    2. using AOT;
    3. using Unity.Burst;
    4. using UnityEngine;
    5.  
    6. [BurstCompile]
    7. public unsafe class FunctionPointerC9Burst : MonoBehaviour {
    8.     [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    9.     public delegate int UnmanagedDelegate(int value);
    10.  
    11.     [MonoPInvokeCallback(typeof(UnmanagedDelegate))]
    12.     private static int Increment(int value) => ++value;
    13.  
    14.     private static readonly delegate* unmanaged[Cdecl]<int, int> c9Ptr
    15.         = (delegate* unmanaged[Cdecl]<int, int>) Marshal.GetFunctionPointerForDelegate((UnmanagedDelegate) Increment);
    16.  
    17.     void Start() {
    18.         SuccessfullyCompiled(c9Ptr, 0);
    19.         CompilationFailsAndFallbackToManagedCallback(c9Ptr, 0);
    20.     }
    21.  
    22.     [BurstCompile]
    23.     static void SuccessfullyCompiled(void* unmanagedCallback, int value) {
    24.         Debug.Log($"Value: {((delegate* unmanaged[Cdecl]<int, int>) unmanagedCallback)(value)}, RunOnBurst ? {RunOnBurst.IsTrue}");
    25.     }
    26.  
    27.     [BurstCompile]
    28.     static void CompilationFailsAndFallbackToManagedCallback(delegate* unmanaged[Cdecl]<int, int> unmanagedCallback, int value) {
    29.         Debug.Log($"Value: {unmanagedCallback(value)}, RunOnBurst ? {RunOnBurst.IsTrue}");
    30.     }
    31. }
    32.  
    33. public static class RunOnBurst {
    34.     public static bool IsTrue {
    35.         get {
    36.             var runOnBurst = true;
    37.             Detector(ref runOnBurst);
    38.             return runOnBurst;
    39.         }
    40.     }
    41.     [BurstDiscard] static void Detector(ref bool runOnBurst) => runOnBurst = false;
    42. }
    If I pass a c#9 function pointer to the burst code, it does not compile correctly. If I cast the function pointer to void*, it compiles successfully, and it works fine on the actual Android IL2CPP device, so I think it is failing to get the method signature correctly.

    Also both JobSystem and IL2CPP fail to recognize the C#9 pointer signature (delegate* unmanaged[Cdecl]<SomeType>), resulting in runtime or compilation errors.

    However, if I cast it to void*, it works fine in all cases.

    I guess for the c#9 pointer to work correctly, Burst, JobSystem, and IL2CPP all need to be modified to get the method signatures correctly.

    If it is technically fixable, I would appreciate your response.
     
    Enderlook likes this.
  2. tim_jones

    tim_jones

    Unity Technologies

    Joined:
    May 2, 2019
    Posts:
    280
    @mm_hohu thanks for reporting this. We'll take a look at the Burst side of it. I can't remember why this isn't already supported, and if there's a problem with supporting it, I don't know how deep the problem goes, but we'll take a look.

    What are those errors? If you can let me know, then I'll route the request to the relevant teams inside Unity.
     
    mm_hohu and DevDunk like this.
  3. mm_hohu

    mm_hohu

    Joined:
    Jun 4, 2021
    Posts:
    38
    Code (CSharp):
    1. using System.Runtime.InteropServices;
    2. using AOT;
    3. using Unity.Burst;
    4. using Unity.Jobs;
    5. using UnityEngine;
    6.  
    7. public unsafe class FunctionPointerC9Burst : MonoBehaviour {
    8.     [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate int UnmanagedDelegate(int value);
    9.     [MonoPInvokeCallback(typeof(UnmanagedDelegate))] private static int Increment(int value) => ++value;
    10.     private static readonly delegate* unmanaged[Cdecl]<int, int> c9Ptr
    11.         = (delegate* unmanaged[Cdecl]<int, int>) Marshal.GetFunctionPointerForDelegate((UnmanagedDelegate) Increment);
    12.  
    13.     private void Start() {
    14.         var job = new Job() {fp = c9Ptr, Value = 0};
    15.         job.Run();
    16.     }
    17. }
    18.  
    19. [BurstCompile]
    20. public unsafe struct Job : IJob {
    21.     public delegate* unmanaged[Cdecl]<int, int> fp;
    22.     public int Value;
    23.     public void Execute() => Debug.Log($"Value: {fp(Value)}");
    24. }
    Thanks for your reply. As for JobSystem, I can reproduce the above error with one file. 2022.3.4f1 (LTS)
     
  4. mm_hohu

    mm_hohu

    Joined:
    Jun 4, 2021
    Posts:
    38
    Code (CSharp):
    1. using System.Runtime.InteropServices;
    2. using AOT;
    3. using Unity.Burst;
    4. using UnityEngine;
    5.  
    6. [BurstCompile] public unsafe class FunctionPointerC9Burst : MonoBehaviour {
    7.     [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate int UnmanagedDelegate(int value);
    8.     [MonoPInvokeCallback(typeof(UnmanagedDelegate))] private static int Increment(int value) => ++value;
    9.     private static readonly delegate* unmanaged[Cdecl]<int, int> c9Ptr
    10.         = (delegate* unmanaged[Cdecl]<int, int>) Marshal.GetFunctionPointerForDelegate((UnmanagedDelegate) Increment);
    11.     private void Start() => IL2CppError(c9Ptr, 0);
    12.  
    13.     [BurstCompile] private static void IL2CppError(delegate* unmanaged[Cdecl]<int, int> unmanagedCallback, int value)
    14.         => Debug.Log($"Call C#9 Function Pointer: {unmanagedCallback(value)}");
    15. }
    This is an error in the Android build of IL2CPP. (2022.3.4f1) This is also reproducible with one file above.
     
  5. tim_jones

    tim_jones

    Unity Technologies

    Joined:
    May 2, 2019
    Posts:
    280
    Thanks @mm_hohu, I've reached out to the relevant folks about the job system and IL2CPP errors.
     
    mm_hohu likes this.
  6. ScottHFerguson

    ScottHFerguson

    Unity Technologies

    Joined:
    Dec 13, 2019
    Posts:
    6
    @mm_hohu The IL2CPP error is a problem in IL2CPP - could you submit a bug report for that issue.

    IL2CPP's delegate code code is incorrectly handling delegates when the first parameter of the delegate is an unmanaged function pointer type. That isn't directly in your code, but the it is being generated from:

    Code (CSharp):
    1. [BurstCompile] private static void IL2CppError(delegate* unmanaged[Cdecl]<int, int> unmanagedCallback, int value)            => Debug.Log($"Call C#9 Function Pointer: {unmanagedCallback(value)}");
    As long as the first parameter isn't an unmanaged function pointer you will not hit the error. So reordering the parameters will work around the error:

    Code (CSharp):
    1. [BurstCompile] private static void IL2CppError(int value, delegate* unmanaged[Cdecl]<int, int> unmanagedCallback)            => Debug.Log($"Call C#9 Function Pointer: {unmanagedCallback(value)}");
     
    tim_jones and mm_hohu like this.
  7. mm_hohu

    mm_hohu

    Joined:
    Jun 4, 2021
    Posts:
    38
    Thanks for the reply. Good to hear that there is a walk-around. Here is the bug report number.

    IN-48119 - IL2CPP Build ErrorWhen Method First Parameter Is Unmanaged Delegate

    Edit:
    Unity Issue Tracker - [Android] IL2CPP build error occurs when method first parameter is an unmanaged delegate (unity3d.com)
     
    Last edited: Jul 25, 2023
    tim_jones likes this.