Search Unity

Burst static restrictions

Discussion in 'Burst' started by snacktime, Jun 20, 2018.

  1. snacktime

    snacktime

    Joined:
    Apr 15, 2013
    Posts:
    3,356
    I'm curious what the intent is with static fields and methods generally, so I don't go off creating something that just gets broken later on.

    We need static to keep encapsulation with jobs. Jobs need to be able work against larger abstractions that exist outside of a specific job, or even outside of ECS. Currently proper encapsulation and code reuse is more painful then it should be.

    When I say we need static, that's until/if we get a better solution provided. It's not terribly difficult to get around these restrictions, but I don't think the value of stopping stupid from being stupid outweighs forcing us to either write bad code or do an end run around the constraints. But honestly I don't know what the intent here is. Like for all I know this could be a technical constraint or hard problem with burst.
     
    mannyhams likes this.
  2. 5argon

    5argon

    Joined:
    Jun 10, 2013
    Posts:
    1,555
    Do you mean specifically Burst or threaded Jobs? If for Jobs (and in effect for Burst also) the intent is here : https://github.com/Unity-Technologi...ystem.md#do-not-access-static-data-from-a-job

     
  3. snacktime

    snacktime

    Joined:
    Apr 15, 2013
    Posts:
    3,356
    Well burst is the only place it's enforced right now that I can tell. And not all cases are equal.

    So the problem isn't really per say blocking statics. They are just the only good simple solution right now. I'm fine with no statics as long as there is some other good way around the problem.

    It prevents you from encapsulating logic with data that lives in containers and then using that from inside a job. So you have one of two choices when that encapsulation is something you want to use in multiple locations one of them being a job. You duplicate code or you break encapsulation. One of the two take your choice.

    IMO I think they do care about this, but they likely care more about stupid accessing Monobehaviour from a job. But that leaves professionals out in the cold kind of, or at least doing end runs around the current constraints. Which isn't difficult to do I just hate going there.
     
  4. xoofx

    xoofx

    Unity Technologies

    Joined:
    Nov 5, 2016
    Posts:
    417
    It is not about writing bad code (though write-able statics are notoriously bad) but the fact that technically, burst can't access static variables that are hold by the .NET runtime, it is a hard technical constraint.

    Though, in recent versions of burst, we have added support for reading from readonly statics which should cover a good chunk of scenarios compatible with the job system (but technically here, we don't access the static readonly memory allocated by the .NET runtime, but we encode the whole static initialization in the generated code, that can lead to full constant folding and optimizations in burst)

    In a future version (towards 2018.3) we will also try to introduce accessing static readonly managed arrays (meaning that you don't write to them at runtime from C# code) in case you are using an array as a const array.
     
  5. snacktime

    snacktime

    Joined:
    Apr 15, 2013
    Posts:
    3,356
    Thanks for the reply on this it really does help with structuring code knowing the full story here. Makes more sense now also.
     
  6. Razmot

    Razmot

    Joined:
    Apr 27, 2013
    Posts:
    346
    talking about constant folding, burst should handle const too !
     
  7. OndrejP

    OndrejP

    Joined:
    Jul 19, 2017
    Posts:
    304
    @xoofx How is accessing static readonly managed arrays supposed to work? Would it pin the data before running the job?
     
    mannyhams likes this.
  8. xoofx

    xoofx

    Unity Technologies

    Joined:
    Nov 5, 2016
    Posts:
    417
    It is making a copy of the array at compile time, so the array should not be changed in any non-HPC# code otherwise you will get a mismatch.
     
    xVergilx and OndrejP like this.
  9. PublicEnumE

    PublicEnumE

    Joined:
    Feb 3, 2019
    Posts:
    729
    Hi! Does this rule out assigning a static readonly variable in a static constructor, like this?

    Code (CSharp):
    1. public static class FooUtility
    2. {
    3.     private readonly static EntityArchetype fooArchetype;
    4.  
    5.     static FooUtility()
    6.     {
    7.         fooArchetype = World.Active.EntityManager.CreateArchetype(ComponentType.ReadWrite<Foo>());
    8.     }
    9.  
    10.     public void FooUtilityMethod()
    11.     {
    12.         // uses fooArchetype so the user doesn't have to pass it in, avoids user error, etc.
    13.     }
    14. }
    Since you don't actually reference the allocation, does that limit the usage of readonly statics to inline-assigned constants?
     
  10. sheredom

    sheredom

    Unity Technologies

    Joined:
    Jul 15, 2019
    Posts:
    300
    I don't think the above does what you think it does. Lets assume you want to use fooUtilityMethod inside a burst function, that then brings in a use of fooArchetype, which Burst realises it has to do something with.

    What Burst is going to do in this instance is try and re-construct fooArchetype itself. If the FooUtility static constructor contains any managed code Burst is going to compile error.

    I've tried to create a little example that shows how this goes wrong:

    Code (CSharp):
    1. public static class DelayedStaticInitializer
    2. {
    3.     private readonly static int foo;
    4.  
    5.     [BurstDiscard]
    6.     static void Modify(ref uint x)
    7.     {
    8.         x++;
    9.     }
    10.  
    11.     static DelayedStaticInitializer()
    12.     {
    13.         uint seed = 0;
    14.         Modify(ref seed);
    15.         var random = new Unity.Mathematics.Random(seed);
    16.         foo = random.NextInt();
    17.     }
    18.  
    19.     public static int FooUtilityMethod()
    20.     {
    21.         return foo;
    22.     }
    23. }
    In the above code, I've added a BurstDiscard method Modify, that would only be run on non-Burst'ed code. If I call DelayedStaticInitializer.FooUtilityMethod() from a Burst method and a managed method, they will produce different results, because the private readonly static int foo object is constructed two times - once in Burst, once not. For most static readonly variables this is fine because the constructors match, but obviously with the torturous test I wrote above there are some frailties.

    I don't think we could support your requested example above, but there is maybe some better error messaging we can produce to alert y'all if someone tried to do the bad pattern.
     
    longsl likes this.
  11. seyyiddogan

    seyyiddogan

    Joined:
    Jul 29, 2021
    Posts:
    2
    Please do have a look at Shared Static. It will let you read and write static values.

    https://docs.unity3d.com/Packages/com.unity.burst@1.2/manual/index.html#shared-static
    Here is what i did come up with with this:

    Code (CSharp):
    1. using Unity.Burst;
    2. using Unity.Entities;
    3. using Unity.Mathematics;
    4.  
    5. public class HapticsSystem : SystemBase
    6. {
    7.     public static void ShakeHands (float shakeDuration, float shakeStrength, float currentTime)
    8.     {
    9.         HapticsMutableStaticData.Field.Data.shakeTill = currentTime + shakeDuration;
    10.         HapticsMutableStaticData.Field.Data.shaking = true;
    11.         Vibration.Vibrate(shakeStrength);
    12.  
    13.     }
    14.     protected override void OnUpdate ()
    15.     {
    16.         if (HapticsMutableStaticData.Field.Data.shaking && Time.ElapsedTime > HapticsMutableStaticData.Field.Data.shakeTill)
    17.         {
    18.             Vibration.Vibrate(0);
    19.             HapticsMutableStaticData.Field.Data.shaking = false;
    20.         }
    21.     }
    22. }
    23.  
    24. public abstract class HapticsMutableStaticData
    25. {
    26.     public struct HapticsData
    27.     {
    28.         public bool shaking;
    29.         public float shakeTill;
    30.     }
    31.     public static readonly SharedStatic<HapticsData> Field = SharedStatic<HapticsData>.GetOrCreate<HapticsMutableStaticData, DataFieldKey>();
    32.     private class DataFieldKey {}
    33. }
     
  12. nukadelic

    nukadelic

    Joined:
    Aug 5, 2017
    Posts:
    78
    Looks great , how to use that with arrays ? Seems like its not supported atm ?

    Code (CSharp):
    1. Invalid managed type found for `Unity.Collections.NativeArray`1<Unity.Mathematics.int2>&` as a generic argument of the struct `Unity.Burst.SharedStatic`1<Unity.Collections.NativeArray`1<Unity.Mathematics.int2>>`.: the type `ref Unity.Collections.NativeArray`1<Unity.Mathematics.int2>` is a managed type and  is not supported
    For the following :

    Code (CSharp):
    1. public abstract class TableData
    2. {
    3.     private class UVOffsetsKey { }
    4.     public static readonly SharedStatic<NativeArray<int2>> UVOffsets
    5.         = SharedStatic<NativeArray<int2>>.GetOrCreate<TableData, UVOffsetsKey>();
    6.  
    7.     static TableData()
    8.     {
    9.         UVOffsets.Data = new NativeArray<int2>(new int2[] {
    10.  
    11.                 new int2( 0, 1 ) ,  //  0 ------ 1
    12.                 new int2( 1, 1 ) ,  //  |        |
    13.                 new int2( 1, 0 ) ,  //  |        |
    14.                 new int2( 0, 0 ) ,  //  3 ------ 2
    15.  
    16.         }, Allocator.Persistent);
    17.     }
    18. }
    19.  
    20. public static class TableDataOld
    21. {
    22.     public static readonly int[] UVOffsets =
    23.     {
    24.         0, 1  , //  0 ------ 1
    25.         1, 1  , //  |        |
    26.         1, 0  , //  |        |
    27.         0, 0  , //  3 ------ 2
    28.     };
    29. }
     
  13. R2-RT

    R2-RT

    Joined:
    May 8, 2019
    Posts:
    38

    Currently
    NativeArray 
    internally has managed
    DisposeSentinel
    , so it cannot be used in SharedStatic. Maybe you can use
    UnsafeList<T>
    or
    FixedListBytesXXX<T>
    instead?