Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Resolved Burstable callback "events" through methods? FunctionPointer?

Discussion in 'Entity Component System' started by toomasio, Apr 7, 2022.

  1. toomasio

    toomasio

    Joined:
    Nov 19, 2013
    Posts:
    198
    Trying to avoid boilerplate using static methods in a generic way. Is there a way to do something like:

    Code (CSharp):
    1. public partial class MySystem : SystemBase
    2.     {
    3.         protected override void OnUpdate()
    4.         {
    5.             Dependency = Entities
    6.                 .ForEach((Entity entity) =>
    7.                 {
    8.                     MyUtilities.MyStaticMethod(entity, MyMethod);
    9.                 })
    10.                 .Schedule(Dependency);
    11.         }
    12.  
    13.         static void MyMethod(MyComponent value)
    14.         {
    15.             Debug.Log($"{value}!");
    16.         }
    17.     }
    18.  
    ?

    I know there are FunctionPointers but I am not sure if that is the proper way to go. Basically I have multiple systems sifting through the same buffer but I want to do something slightly different based on certain values I grab from the buffer....without rewriting the same code over and over.

    Thanks,
     
  2. nijnstein

    nijnstein

    Joined:
    Feb 6, 2021
    Posts:
    78
    this class is from some unity sample i think:

    Code (CSharp):
    1.  
    2.  
    3.     static class BurstCompilerUtil<T>
    4.         where T : class
    5.     {
    6.         private static readonly MethodInfo compileMethodInfo;
    7.  
    8.         static BurstCompilerUtil()
    9.         {
    10.             foreach (var mi in typeof(BurstCompiler).GetMethods(
    11.                 BindingFlags.Default
    12.                 | BindingFlags.Static
    13.                 | BindingFlags.NonPublic))
    14.             {
    15.                 if (mi.Name == "Compile")
    16.                 {
    17.                     compileMethodInfo = mi.MakeGenericMethod(typeof(T));
    18.                     break;
    19.                 }
    20.             }
    21.         }
    22.    
    23.         public static unsafe IntPtr CompileFunctionPointer(T del)
    24.         {
    25.             var obj = compileMethodInfo.Invoke(null, new object[] { del, true });
    26.             return new IntPtr(Pointer.Unbox(obj));
    27.         }
    28.     }
    then in your code with the delegate as T

    Code (CSharp):
    1.    
    2.      var fp =  new FunctionPointer<T>( BurstCompilerUtil<T>.CompileFunctionPointer(function_delegate))
    3.  
    and use it like:

    Code (CSharp):
    1.    
    2. fp.Invoke(.....);
    3.  
     
  3. toomasio

    toomasio

    Joined:
    Nov 19, 2013
    Posts:
    198
    Thanks for the reply! annnnd apparently we cant use generic types for the delegates on these function pointers...... :(

    back to the drawing board!
     
  4. PhilSA

    PhilSA

    Joined:
    Jul 11, 2013
    Posts:
    1,926
    Instead of passing a function ptr, you can pass a struct that implements a "MyMethod()" function, which does different things based on an ID that was given to the struct (in MyMethod(), it does a switch statement on that ID, and each case does a different thing

    It's what I tried to solve with these "DOTS polymorphic components". Codegen makes this possible without boilerplate

    Now I'm working on a version of this that uses SourceGenerators, in order to make the codegen process totally invisible

    _____________

    EDIT: Got the SourceGenerators version working here: https://forum.unity.com/threads/sou...m-in-dots-now-with-source-generators.1264616/
     
    Last edited: Apr 8, 2022
    toomasio and PublicEnumE like this.
  5. PublicEnumE

    PublicEnumE

    Joined:
    Feb 3, 2019
    Posts:
    729
    How are you using source generators with Entities?

    Entities 0.50 only supports Unity 2020 LTS, and Unity didn’t support SGs until 2021.2, at least.
     
  6. PhilSA

    PhilSA

    Joined:
    Jul 11, 2013
    Posts:
    1,926
    I'm still evaluating whether or not they are supported well enough in 2020.3, but I did get them to work:
    • try following the example on this page, step by step (just the part under the "Source Generators" section) https://docs.unity3d.com/2021.2/Documentation/Manual/roslyn-analyzers.html
    • Make sure your generator dll has these options
    • Make sure your generator dll has these two asset labels:
      • RoslynAnalyzer
      • SourceGenerator
    • Make sure you have the com.unity.roslyn package in your project (but Entities 0.50 depends on it)
    The problems I'm seeing so far is that the generation fails very often and I often have to restart VS in order to make it work. No idea if that's really a problem with sourceGenerators support, or a problem with my code (I've just begun learning how to use them)

    Worst case scenario I'm gonna implement all this in 2021.2 with regular structs while waiting for DOTS 0.51. That way it'll be basically ready when that happens
     
    Last edited: Apr 8, 2022
    toomasio likes this.
  7. toomasio

    toomasio

    Joined:
    Nov 19, 2013
    Posts:
    198
    You are a god damn genius. :) So simple!

    Here's what I did with my code now. Really opens the opportunity to buffer almost everything and sort matches by IDs. I can trigger different methods as well. The only boilerplate now is just creating a different struct per method, but whatever, at least it cleans up all the code smells I was trying to avoid.

    Code (CSharp):
    1. public interface IFunctionStruct<TArg>
    2.     {
    3.         void DoFunction(TArg arg);
    4.     }
    5.  
    6.     public interface IFunctionStruct<TArg00, TArg01>
    7.     {
    8.         void DoFunction(TArg00 arg00, TArg01 arg01);
    9.     }
    10.  
    11. public static void DoTriggerAction<TBuffer, TBufferTrigger, TFunction>(
    12.             FixedString64Bytes label,
    13.             IContainerTrigger.ContainerTriggerType triggerType,
    14.             in DynamicBuffer<TBuffer> buffer,
    15.             in DynamicBuffer<TBufferTrigger> bufferTrigger,
    16.             TFunction onMatch)
    17.             where TBuffer : struct, IBufferElementData, IContainLabel, IContainID
    18.             where TBufferTrigger : struct, IBufferElementData, IDynamicBufferTrigger
    19.             where TFunction : struct, IFunctionStruct<TBuffer>
    20.         {
    21.             for (int i = 0; i < bufferTrigger.Length; i++)
    22.             {
    23.                 if (bufferTrigger[i].Label != label) continue;
    24.                 if (bufferTrigger[i].TriggerType != triggerType) continue;
    25.                 for (int j = 0; j < buffer.Length; j++)
    26.                 {
    27.                     if (bufferTrigger[i].ID == buffer[j].ID)
    28.                     {
    29.                         onMatch.DoFunction(buffer[j]);
    30.                     }
    31.                 }
    32.             }
    33.         }
    34.  
    35. public partial class ContainerBufferEnterSystem : SystemBase
    36.     {
    37.  
    38.         private struct ContainerBufferEnterFunction : IFunctionStruct<ContainerBuffer>
    39.         {
    40.             public void DoFunction(ContainerBuffer arg)
    41.             {
    42.                 Debug.Log($"{arg.Entity}");
    43.             }
    44.         }
    45.  
    46.         private ContainerBufferEnterFunction func = new ContainerBufferEnterFunction();
    47.  
    48.         protected override void OnUpdate()
    49.         {
    50.             FixedString64Bytes label = "Testing";
    51.             var func = this.func;
    52.             Entities
    53.                 .ForEach((ref DynamicBuffer<DynamicBufferTrigger> bufferTrigger, in DynamicBuffer<ContainerBuffer> buffer) =>
    54.                 {
    55.                     ContainerEventUtilities.DoTriggerAction(label, IContainerTrigger.ContainerTriggerType.Enter, buffer, bufferTrigger, func);
    56.                 })
    57.                 .ScheduleParallel();
    58.         }
    59.     }
    THANKS!:D
     
    bb8_1, apkdev and PhilSA like this.