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.
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
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.
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.
Thanks for the reply on this it really does help with structuring code knowing the full story here. Makes more sense now also.
@xoofx How is accessing static readonly managed arrays supposed to work? Would it pin the data before running the job?
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.
Hi! Does this rule out assigning a static readonly variable in a static constructor, like this? Code (CSharp): public static class FooUtility { private readonly static EntityArchetype fooArchetype; static FooUtility() { fooArchetype = World.Active.EntityManager.CreateArchetype(ComponentType.ReadWrite<Foo>()); } public void FooUtilityMethod() { // uses fooArchetype so the user doesn't have to pass it in, avoids user error, etc. } } Since you don't actually reference the allocation, does that limit the usage of readonly statics to inline-assigned constants?
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): public static class DelayedStaticInitializer { private readonly static int foo; [BurstDiscard] static void Modify(ref uint x) { x++; } static DelayedStaticInitializer() { uint seed = 0; Modify(ref seed); var random = new Unity.Mathematics.Random(seed); foo = random.NextInt(); } public static int FooUtilityMethod() { return foo; } } 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.
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): using Unity.Burst; using Unity.Entities; using Unity.Mathematics; public class HapticsSystem : SystemBase { public static void ShakeHands (float shakeDuration, float shakeStrength, float currentTime) { HapticsMutableStaticData.Field.Data.shakeTill = currentTime + shakeDuration; HapticsMutableStaticData.Field.Data.shaking = true; Vibration.Vibrate(shakeStrength); } protected override void OnUpdate () { if (HapticsMutableStaticData.Field.Data.shaking && Time.ElapsedTime > HapticsMutableStaticData.Field.Data.shakeTill) { Vibration.Vibrate(0); HapticsMutableStaticData.Field.Data.shaking = false; } } } public abstract class HapticsMutableStaticData { public struct HapticsData { public bool shaking; public float shakeTill; } public static readonly SharedStatic<HapticsData> Field = SharedStatic<HapticsData>.GetOrCreate<HapticsMutableStaticData, DataFieldKey>(); private class DataFieldKey {} }
Looks great , how to use that with arrays ? Seems like its not supported atm ? Code (CSharp): 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): public abstract class TableData { private class UVOffsetsKey { } public static readonly SharedStatic<NativeArray<int2>> UVOffsets = SharedStatic<NativeArray<int2>>.GetOrCreate<TableData, UVOffsetsKey>(); static TableData() { UVOffsets.Data = new NativeArray<int2>(new int2[] { new int2( 0, 1 ) , // 0 ------ 1 new int2( 1, 1 ) , // | | new int2( 1, 0 ) , // | | new int2( 0, 0 ) , // 3 ------ 2 }, Allocator.Persistent); } } public static class TableDataOld { public static readonly int[] UVOffsets = { 0, 1 , // 0 ------ 1 1, 1 , // | | 1, 0 , // | | 0, 0 , // 3 ------ 2 }; }
Currently NativeArray internally has managed DisposeSentinel, so it cannot be used in SharedStatic. Maybe you can use UnsafeList<T> or FixedListBytesXXX<T> instead?