Search Unity

Sharing logic between Burst jobs

Discussion in 'Data Oriented Technology Stack' started by james_unity988, May 14, 2019.

  1. james_unity988

    james_unity988

    Joined:
    Aug 10, 2018
    Posts:
    70
    I'm having some difficulty understanding proper design patterns for ECS and the Job system, especially when Burst is enabled. Currently I wish to share common logic between Burst Jobs.

    I can't figure out how to do this because a) I read somewhere that I can't/shouldn't call static methods from a Burst job and b) Jobs are structs and can't have inheritance.

    Another option is to pass a "UtilMethods" type object into the job which has the methods I need. However, I'm concerned that this approach will prevent the util methods from being compiled by Burst, and this defeats the purpose. I hope I'm wrong about this.

    Any help is greatly appreciated!
     
  2. Vanamerax

    Vanamerax

    Joined:
    Jan 12, 2012
    Posts:
    817
    You can call static methods just fine, as long as you dontd acces any (managed) static variables.

    I'd still like to hear what Unity's recommended approach is though.
     
    james_unity988 likes this.
  3. Brendon_Smuts

    Brendon_Smuts

    Joined:
    Jun 12, 2017
    Posts:
    43
    Using static methods for shared functionality is probably the correct approach in most cases and burst should be able to handle this just fine. You can open up a couple of the source files for Unity's own in house systems and see they use static methods extensively.
     
    james_unity988 likes this.
  4. snacktime

    snacktime

    Joined:
    Apr 15, 2013
    Posts:
    2,172
    I use a mix of statics and structs for this.

    For cases where things are a bit more complex, like if you need to encapsulate logic that itself should be internally abstracted out more then just a big fat method, you can use structs for this. You can pass native containers, dynamic buffers, or ComponentDataFromEntity in the constructor. You can also pass structs into a job that contain native containers.

    Burst or ECS for that matter doesn't really change any fundamentals here. Good encapsulation you are going to use the same patterns for the most part that you use in most any domain with a few slight differences.
     
    james_unity988 likes this.
  5. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    1,280
    You can use static methods which compatable with burst (no managed, not static fields etc), you can even use static fields but they should be const\readonly
     
    Antypodish and james_unity988 like this.
  6. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    1,256
    As stated you can use static methods but I prefer using structs to share more complicated logic.

    Some random code for example

    Code (CSharp):
    1.  
    2. public interface ILine
    3. {
    4.     /// <summary>
    5.     /// Draw a horizontal line between 2 values of x and a value of y.
    6.     /// </summary>
    7.     /// <param name="x0">Draw line from. Note. must be &lt;= x0.</param>
    8.     /// <param name="x1">Draw line to. Note. must be &gt;= x0.</param>
    9.     /// <param name="y">The y position of horizontal line.</param>
    10.     /// <param name="color">The color of the line.</param>
    11.     void DrawHorizontal(int x0, int x1, int y);
    12.  
    13.     /// <summary>
    14.     /// Draw a line between 2 points that catches all grid squares the line passes through.
    15.     /// </summary>
    16.     /// <param name="p0">Draw line from.</param>
    17.     /// <param name="p1">Draw line to.</param>
    18.     /// <param name="color">The color of the line.</param>
    19.     void DrawSuperCover(float2 p0, float2 p1);
    20.  
    21.     // ...
    22. }
    23.  
    24. /// <summary>
    25. /// Line drawing algorithms.
    26. /// </summary>
    27. /// <typeparam name="T">The output.</typeparam>
    28. public struct LineDrawing<T> : ILine
    29.     where T : struct, IMap
    30. {
    31.     private T map;
    32.  
    33.     /// <summary>
    34.     /// Initializes a new instance of the <see cref="LineDrawing{T}"/> struct.
    35.     /// </summary>
    36.     /// <param name="map">The grid map.</param>
    37.     public LineDrawing(T map)
    38.     {
    39.         this.map= map;
    40.     }
    41.  
    42.     /// <inheritdoc />
    43.     public void DrawHorizontal(int x0, int x1, int y)
    44.     {
    45.         for (var x = x0; x <= x1; x++)
    46.         {
    47.             this.map.Visit(x, y);
    48.         }
    49.     }
    50.  
    51.     /// <inheritdoc />
    52.     public void DrawSuperCover(float2 p0, float2 p1)
    53.     {
    54.         this.RayTrace(p0.x, p0.y, p1.x, p1.y);
    55.     }
    56.  
    57.     // ...
    and using it

    Code (CSharp):
    1.     [BurstCompile]
    2.     public struct CalculateVisionJob : IJobForEach<Observer>
    3.     {
    4.         // ...
    5.  
    6.         public void Execute([ReadOnly] ref Observer observer)
    7.         {
    8.             var vision = this.Visions[observer.controllerEntity].Reinterpret<Color32>().AsNativeArray();
    9.             var visit = new VisitWithinRange(vision);
    10.             var line = new LineDrawing<VisitWithinRange>(visit);
    11.      
    12.             // example use
    13.             line.DrawHorizontal(0,10,5);
    14.         }
    15.  
    16.         // ..
    Otherwise you end up in these situations where you pass 10 parameters to a method.
     
  7. james_unity988

    james_unity988

    Joined:
    Aug 10, 2018
    Posts:
    70
    Thanks for the responses everyone! This is very helpful. I guess for some reason I was thinking that Burst would only apply to structs annotated as Burst. But it seems as though it's more sophisticated than that, applying Burst compilation to any code in the call stack. Please correct me if I'm wrong.
     
  8. 5argon

    5argon

    Joined:
    Jun 10, 2013
    Posts:
    1,191
    Burst compile codes in struct, but if that code call into other static method then that code is now "in the struct" as well. Since each [BurstCompile] seems to have to be self-contained. So multiple [BurstCompile] that went into the same static method, it would give duplicated assembly of that static method for each one (I guess) and then could be optimized differently.
     
    james_unity988 and Antypodish like this.
  9. james_unity988

    james_unity988

    Joined:
    Aug 10, 2018
    Posts:
    70
    Aha! Now that does make a lot of sense. Thanks for clarifying!