Search Unity

  1. Unity 2018.3 is now released.
    Dismiss Notice
  2. The Unity Pro & Visual Studio Professional Bundle gives you the tools you need to develop faster & collaborate more efficiently. Learn more.
    Dismiss Notice
  3. Want more efficiency in your development work? Sign up to receive weekly tech and creative know-how from Unity experts.
    Dismiss Notice
  4. Build games and experiences that can load instantly and without install. Explore the Project Tiny Preview today!
    Dismiss Notice
  5. Nominations have been announced for this years Unity Awards. Celebrate the wonderful projects made by your peers this year and get voting! Vote here!
    Dismiss Notice
  6. Want to provide direct feedback to the Unity team? Join the Unity Advisory Panel.
    Dismiss Notice
  7. Improve your Unity skills with a certified instructor in a private, interactive classroom. Watch the overview now.
    Dismiss Notice

API usability

Discussion in 'Entity Component System and C# Job system' started by Adam-Mechtley, Sep 4, 2018.

  1. Adam-Mechtley

    Adam-Mechtley

    Unity Technologies

    Joined:
    Feb 5, 2007
    Posts:
    170
    Good point. Thanks!

    Thanks for pointing this out! A lot of the documentation is behind and undergoing catch-up work, but I have now removed this comment on our dev branch
     
  2. TJHeuvel-net

    TJHeuvel-net

    Joined:
    Jul 31, 2012
    Posts:
    362
    I've been quite vocal on the Math API syntax, apologies if i continue here for a little.

    Trying out some jobs and i decided to refactor a method to not accept Transform[] but an TransformAccessArray as parameter instead. Normally that would go just fine, but now i got an error.

    The code below has the same error, can you spot it?

    Code (csharp):
    1.  
    2. var job = new SomeJob()
    3. {
    4.     Safe = new bool[transforms.Length],
    5.     Dead = new bool[enemyPositions.Length],
    6. };
    7.  
    TransformAccessArray doesnt have a Length property, it has a length property. Is it intentional? It makes porting code just that bit more of a nuisance, if they would be equal you can easily change the type without any other changes.

    It also seems to miss some other methods you'd expect in a List, like Clear. List has a RemoveAt method, while TransformAccessArray's is called RemoveAtSwapBack.
     
    Last edited: Sep 28, 2018
  3. Adam-Mechtley

    Adam-Mechtley

    Unity Technologies

    Joined:
    Feb 5, 2007
    Posts:
    170
    It's definitely an annoyance! I would hesitate to call it intentional as such, but in general, Unity has historically used camelCased names for properties, while Microsoft FDG recommends PascalCase for property names. To bring ourselves more in line with the rest of the .net ecosystem, it was decided that new code moving forward (i.e. Unity namespace) would conform to FDG, while existing code (i.e. UnityEngine namespace) would retain the old standard. I believe this was discussed elsewhere on the forum (e.g., this thread).
     
  4. TJHeuvel-net

    TJHeuvel-net

    Joined:
    Jul 31, 2012
    Posts:
    362
    Thanks for the quick and clear answer! Legacy is something very difficult to handle, i understand that you cant go back in time (yet ;) ).

    In other cases Unity has made certain fields and classes obsolete, perhaps its possible to do the same here and move it into the Unity.Jobs namespace.
     
    Adam-Mechtley likes this.
  5. Adam-Mechtley

    Adam-Mechtley

    Unity Technologies

    Joined:
    Feb 5, 2007
    Posts:
    170
    Hi there! I wanted to follow up on this quick. I asked about it and in Burst the function call overhead should be the same in both cases (Entity.Null vs default(Entity)).
     
    SubPixelPerfect and pcysl5edgo like this.
  6. pcysl5edgo

    pcysl5edgo

    Joined:
    Jun 3, 2018
    Posts:
    38
    Wow!
    The Burst optimization is great!
    Thanks for giving me the valuable information!
     
    Adam-Mechtley likes this.
  7. pcysl5edgo

    pcysl5edgo

    Joined:
    Jun 3, 2018
    Posts:
    38
    There is an API DynamicBuffer<T> EntityCommandBuffer.SetBuffer<T>(Entity entity).
    I suppose that this API's name is wrong.
    Isn't it GetBuffer<T>()?
     
  8. noio

    noio

    Joined:
    Dec 17, 2013
    Posts:
    78
    Is there going to be a single-pass of API overhaul? Or will the changes be introduced gradually?

    And if there is, is there an estimation of when that's coming? I'm trying to decide when to start introducing some ECS Systems into my project, or hold off for a bit longer. (I'm currently using ECS only indirectly, through the SpatialOS GDK)
     
  9. deplinenoise

    deplinenoise

    Unity Technologies

    Joined:
    Dec 20, 2017
    Posts:
    20
    You can't get a buffer, because the entity doesn't necessarily exist yet.

    In the case that the entity doesn't exist yet, you're essentially saying:

    1. First create an entity
    2. Then set this buffer on it (which I will now fill in so you know what to set)

    In the case the entity already exists, you're saying:

    1. In the future, replace this buffer on this entity with this version I'm preparing
     
  10. Adam-Mechtley

    Adam-Mechtley

    Unity Technologies

    Joined:
    Feb 5, 2007
    Posts:
    170
    All changes will be gradual, and they've unfortunately fallen a little bit in the priority queue for the time being. That said, you can be reasonably sure that IJobProcessComponentData (at the high level) and iterating NativeArrays of ArchetypeChunk (at the low level) should remain mostly as-is for quite awhile. The things in between (e.g., Injection, ComponentDataArray, etc.) are more likely to be subject to nearer-term changes.
     
  11. bac9-flcl

    bac9-flcl

    Joined:
    Dec 5, 2012
    Posts:
    722
    Figured I should post two small details that stumped me for a bit:
    • Maybe I'm misunderstanding something here, but it might be worth reiterating in the docs that buffers are not a special type of a data component with a collection inside - IBufferElementData structs are component equivalents. I was really confused about the reason why archetype construction is using the entry types and not the buffer itself - until a colleague told me that "buffers were added so that you could use the same component multiple times per entity, not as a component with a collection inside". To that end, maybe calling the buffer entry structs something like IBufferComponentData (similarly how you have another special case, ISharedComponentData) would make this more intuitive? To be fair, some other aspects of buffer use make it ambiguously component-like, e.g. you have to use HasComponent (but with an entry struct type as a type argument) to check if a buffer exists on an entity. Maybe this is intuitive to most, but coming from traditional component land, my chain of thinking went like this:
    • "I've got to move all the data that was previously sitting in the fields of a MonoBehaviour into individual single-responsibility boxes called data components"
    • "I had an array on my MonoBehaviour - how do I create a box with an array in it?"
    • "Oh, there is a feature called dynamic buffers - this looks like a component with an array in it!" :)
    • ComponentType.Create stumped me for a bit due to its name. The results of that call were used to construct archetypes but the name of the method suggests that it creates/allocates something, like a buffer with a type you specify in it. I'd rename it to Resolve or Get - using Create is like calling MonoBehaviour.GetType a name like Instantiate, it just has totally different expectations attached to it. :)
     
    Last edited: Oct 4, 2018
  12. Adam-Mechtley

    Adam-Mechtley

    Unity Technologies

    Joined:
    Feb 5, 2007
    Posts:
    170
    Thanks for the feedback! It's all helpful
     
  13. TJHeuvel-net

    TJHeuvel-net

    Joined:
    Jul 31, 2012
    Posts:
    362
    I'm jobifying some routine that does mesh skinning. I need to access a matrix from a transform, its a bit odd that TransformAccess doesnt have this property.
     
  14. 5argon

    5argon

    Joined:
    Jun 10, 2013
    Posts:
    814
    If [Inject] and ComponentDataArray is going away, is the concept of ShouldRunSystem also ? So in effect, after the default world initialization all systems would be running its OnUpdate unconditionally?
     
  15. Joachim_Ante

    Joachim_Ante

    Unity Technologies

    Joined:
    Mar 16, 2005
    Posts:
    4,201
    No. Injection essentially does automatic calls to GetComponentGroup in OnCreateManager, that is still possible and is still the recommended approach. You should never call GetComponentGroup in the middle of OnUpdate, but instead cache them in OnCreateManager in a local variable.

    Based on that ShouldRunSystem() can skip running a system. ArchetypeChunk will also pull its filtered chunk list via ComponentGroup. IJobProcessComponentData already does.
     
    5argon and eizenhorn like this.
  16. illinar

    illinar

    Joined:
    Apr 6, 2011
    Posts:
    422
    *Cough cough*

    I've been just kindly given an example of how my 14-lines-of-code system would look with the new API.

    Here is my current system:
    Code (CSharp):
    1. public class ApplyTransformToGameObjectSystem : ComponentSystem
    2. {
    3.     [Inject] ReadonlyComponentInjection<Transform, LocalToWorld, KeepGOTransformUpdated> _data;
    4.  
    5.     protected override void OnUpdate()
    6.     {
    7.         for (int i = 0; i < _data.Length; i++)
    8.         {
    9.             var transform = _data.Components[i];
    10.             var matrix = _data.Data1[i].Value;
    11.             transform.localPosition = new Vector3(matrix.c3.x, matrix.c3.y, matrix.c3.z);
    12.         }
    13.     }
    14. }
    Mind you, most of my systems are quite simple so far, so those custom generic injections work pretty well and save a lot of typing, code lines, and look quite clear in small systems.

    Now here is the new API, and I don't know if it is correct or if it could be optimized to take less code:
    Code (CSharp):
    1. public class ApplyTransformToGameObjectSystem : ComponentSystem
    2. {
    3.     EntityArchetypeQuery query;
    4.  
    5.     protected override void OnCreateManager ()
    6.     {
    7.         query = new EntityArchetypeQuery
    8.         {
    9.             Any = Array.Empty<ComponentType> (),
    10.             None = Array.Empty<ComponentType> (),
    11.             All = new ComponentType[] { typeof (Position), typeof (LocalToWorld), typeof (KeepGOTransformUpdated) }
    12.         };
    13.     }
    14.  
    15.     protected override void OnUpdate ()
    16.     {
    17.         var chunks = EntityManager.CreateArchetypeChunkArray (query, Allocator.TempJob);
    18.         int chunkLength = chunks.Length;
    19.         if (chunkLength == 0)
    20.         {
    21.             chunks.Dispose ();
    22.             return;
    23.         }
    24.         var entityCommandBuffer = new EntityCommandBuffer (Allocator.Temp);
    25.         var entityChunkType = GetArchetypeChunkEntityType ();
    26.         var matrixChunkType = GetArchetypeChunkComponentType<LocalToWorld> ();
    27.         for (int i = 0; i < chunkLength; i++)
    28.         {
    29.             var chunk = chunks[i];
    30.             var entities = chunk.GetNativeArray (entityChunkType);
    31.             var matrices = chunk.GetNativeArray (matrixChunkType);
    32.             int entitiesLength = entities.Length;
    33.             for (int e = 0; e < entitiesLength; e++)
    34.             {
    35.                 var entity = entities[e];
    36.                 var matrix = matrices[e].Value;
    37.                 entityCommandBuffer.SetComponent (entity, new Position { Value = new float3 (matrix.c3.x, matrix.c3.y, matrix.c3.z) });
    38.             }
    39.         }
    40.         chunks.Dispose ();
    41.         entityCommandBuffer.Playback (EntityManager);
    42.         entityCommandBuffer.Dispose ();
    43.     }
    44. }
    I mean... Kill me now?
     
    e199 likes this.
  17. dartriminis

    dartriminis

    Joined:
    Feb 3, 2017
    Posts:
    134
    Keep in mind you've got IJobProcessComponentData at your disposal. Use it whenever feasible. It works in parallel and has a very small code footprint.

    If that's not feasible, you can take advantage of the new IChunkJob struct. If doesn't necessarily reduce your actual lines of code, but I think it does make it a bit cleaner.

    Code (CSharp):
    1. public class ApplyTransformToGameObjectSystem : ComponentSystem
    2. {
    3.     private ComponentGroup _group;
    4.  
    5.     protected override void OnCreateManager()
    6.     {
    7.         _group = GetComponentGroup(new EntityArchetypeQuery
    8.         {
    9.             Any  = Array.Empty<ComponentType>(),
    10.             None = Array.Empty<ComponentType>(),
    11.             All  = new ComponentType[] { typeof(Position), typeof(LocalToWorld), typeof(KeepGOTransformUpdated) }
    12.         });
    13.     }
    14.  
    15.     protected override void OnUpdate()
    16.     {
    17.         new ApplyTransformToGameObjectJob
    18.             {
    19.                 Commands        = PostUpdateCommands.ToConcurrent(),
    20.                 EntityChunkType = GetArchetypeChunkEntityType(),
    21.                 MatrixChunkType = GetArchetypeChunkComponentType<LocalToWorld>()
    22.             }
    23.            .Run(_group);
    24.     }
    25.  
    26.     private struct ApplyTransformToGameObjectJob : IJobChunk
    27.     {
    28.         [NativeSetThreadIndex] private int _jobIndex;
    29.      
    30.         public EntityCommandBuffer.Concurrent Commands;
    31.  
    32.         public ArchetypeChunkEntityType                  EntityChunkType;
    33.         public ArchetypeChunkComponentType<LocalToWorld> MatrixChunkType;
    34.  
    35.         public void Execute(ArchetypeChunk chunk, int chunkIndex)
    36.         {
    37.             var count    = chunk.Count;
    38.             var entities = chunk.GetNativeArray(EntityChunkType);
    39.             var matrices = chunk.GetNativeArray(MatrixChunkType);
    40.             for (var e = 0; e < count; e++)
    41.             {
    42.                 var entity = entities[e];
    43.                 var matrix = matrices[e].Value;
    44.                 Commands.SetComponent(_jobIndex, entity, new Position { Value = new float3(matrix.c3.x, matrix.c3.y, matrix.c3.z) });
    45.             }
    46.         }
    47.     }
    48. }
    Unfortunately, neither works well when using a hybrid approach. But I'm sure once the Unity devs settle on a solid way to get the components you need (weather that's injection, component groups, queries, etc) they'll start finding ways to make it simpler and with less code (as long as it doesn't jeopardize performance).
     
  18. Adam-Mechtley

    Adam-Mechtley

    Unity Technologies

    Joined:
    Feb 5, 2007
    Posts:
    170
    Just to make sure we're clear, the idea here is not that iterating over ArchetypeChunks is "the new API" intended to replace all the injection-based stuff. It is simply the low-level API that you can rest assured should see few changes going forward. Likewise, as @dartriminis indicated, IJobProcessComponentData is another good choice for now, in situations where it meets your needs, that should see few changes.

    While injection/ComponentDataArray/etc will go away at some point, the goal is to replace them with APIs that help you more gradually traverse the distance between these two endpoints.
     
  19. 5argon

    5argon

    Joined:
    Jun 10, 2013
    Posts:
    814
    I just realized that the whole "chunk iteration" method cannot get what's equivalent to ComponentData? (the hybrid approach) For example if I have a GameObjectEntity that dragged some other mono components into ECS world.

    - Previously [Inject] containing ComponentData + injection hook works
    - GetArchetypeChunkComponentType<T> does not accept T that is my mono behaviour class.
    - Cannot use IJobComponentData since those class cannot work in non-main thread
    - Now the only way seems to be GetComponentGroup -> GetComponentArray
     
  20. starikcetin

    starikcetin

    Joined:
    Dec 7, 2017
    Posts:
    173
    I really like the injection API. I wish you wouldn't phase it out.
     
  21. nttLIVE

    nttLIVE

    Joined:
    Sep 13, 2018
    Posts:
    43
    That sounds like the problem I posted here. No one seems to know the answer for that yet. What I use is a mix of ComponentGroup(Query) and create my chunks using ComponentGroup.CreateArchetypeChunkArray() when I need to.

    One thing I can't wrap my head around is what is the best way to inject components in a system.

    1. [Inject] : (-) Is going to be deprecated so we can cross that one out.

    2. GetEntities<Filter> / ComponentGroupArray : (-) Can't find a good use case for this.

    3. EntityManager.CreateArchetypeChunkArray(Query) : (-) Need to early exit if length == 0 / (-) Filter Manually / (-) Can't get GameObject Components / (+) Can use Query / (+) Can get SharedComponentData values through Chunks

    4. ComponentGroup(ComponentType[]) : (-) Can't use Query / Same as #5

    5. ComponentGroup(Query) : (+) Can use Query / (+) Can use filter nevermind it's bugged / (+) Can get GameObject Components / (+) Can create Chunks / (+) Can get SharedComponentData values through Chunks

    So should all my systems strive to use the ComponentGroup(Query) pattern to inject components in my systems? It seems like since I started my project using ECS I find myself rewriting how I inject components in my systems constantly and I still am not sure what's the best pattern. Let alone if there hidden performance costs between each type of injection?

    It's my first time using a ECS framework but can this not be more straightforward? So far ComponentGroup(Query) seems to fill pretty much every one of my use cases and it's become pretty straight forward to use but I can't trust that I won't have to refactor again in the future for X pattern.

    Hopefully this doesn't come across as all negative, the benefits of ECS are definitely amazing, I'm just constantly in a state of confusion.
     
    Last edited: Oct 9, 2018
    e199 and 5argon like this.
  22. 5argon

    5argon

    Joined:
    Jun 10, 2013
    Posts:
    814
    I also liked the injection, but they want to go magic-less (no reflection) and that's also understandable.

    For my take on the usability, the problem we have is that to not use attribute + reflection, the only way to specify "what the system want" is via the ComponentGroup system, or use chunk iteration and skipping adding ComponentGroup to the system. (And use the central, EntityManager's component group instead? Is that right?)

    For ComponentGroup way that determines the group at runtime (Using OnCreateManager) the best practice right now is to cache ComponentGroup. There are some friction to sending type parameters to the GetComponentGroup. Best might be an array of ComponentType.ReadOnly<T>(), which supports generic, but we have to define a variable for the resulting group similarly making it annoying to type as it looks like 2 unrelated things that must work together. And also it is quite long

    Code (CSharp):
    1.     ComponentGroup group;
    2.     ComponentType[] type = new ComponentType[]{ ComponentType.ReadOnly<PerformanceDebug>(), ComponentType.ReadOnly<NoteData.Hitted>() };
    3.     protected override void OnCreateManager()
    4.     {
    5.         group = GetComponentGroup(type);
    6.     }
    Here's some idea to simplify that in a special case, with a generic. In the same vein as IJobProcessComponentData which provides some numbers of generic definition, in this case for one component.

    Code (CSharp):
    1. public class PerformanceDebugSystem : ComponentSystem
    2. {
    3.     InjectionGroup<PerformanceDebug> performanceDebug;
    4.     protected override void OnCreateManager()
    5.     {
    6.         performanceDebug.Init(GetComponentGroup(performanceDebug));
    7.     }
    8.  
    9.     protected override void OnUpdate()
    10.     {
    11.         if(performanceDebug.Injected)
    12.         {
    13.             PerformanceDebug pd = performanceDebug.First;
    14.             //pd. do things...
    15.         }
    16.     }
    17. }
    18.  
    19. public struct InjectionGroup<T> where T : Component
    20. {
    21.     private ComponentGroup cg;
    22.     public void Init(ComponentGroup cg)
    23.     {
    24.         this.cg = cg;
    25.     }
    26.  
    27.     public int Length => cg.GetComponentArray<T>().Length;
    28.     public bool Injected => Length > 0;
    29.     public T First => cg.GetComponentArray<T>()[0];
    30.  
    31.     public IEnumerable<T> ComponentIterator
    32.     {
    33.         get
    34.         {
    35.             var ca = cg.GetComponentArray<T>();
    36.             for (int i = 0; i < ca.Length; i++)
    37.             {
    38.                 yield return ca[i];
    39.             }
    40.         }
    41.     }
    42.  
    43.     public static implicit operator ComponentType (InjectionGroup<T> s) => ComponentType.ReadOnly<T>();
    44. }
    With implicit we can exploit the the generic on struct as an input type to GetComponentData, but it still looks weird (line 6) on the returning ComponentGroup that has to be kept into the struct somehow.

    Based on that instead of an implicit keyword hack, my next idea which requires changing the ECS API is using struct's interface so that there is a required setter that GetComponentGroup can assign the resulting ComponentGroup via `ref`. In this case the user can implement his own functions that utilize the CG inside together with using a generic pattern.

    Code (CSharp):
    1. public class PerformanceDebugSystem : ComponentSystem
    2. {
    3.     Inject<PerformanceDebug> performanceDebug;
    4.     protected override void OnCreateManager()
    5.     {
    6.         GetComponentGroup(ref performanceDebug);
    7.     }
    8.  
    9.     protected override void OnUpdate()
    10.     {
    11.         ...
    12.     }
    13. }
    14.  
    15. public struct Inject<T> : IComponentGroupQuery where T : Component
    16. {
    17.     private ComponentGroup cg;
    18.     public ComponentGroup ComponentGroup { get => cg; set => cg = value; }
    19.     public ComponentType[] Types => new ComponentType[] { ComponentType.ReadOnly<T>() };
    20. }
    21.  
    22. public interface IComponentGroupQuery
    23. {
    24.     ComponentGroup ComponentGroup { set; }
    25.     ComponentType[] Types { get; }
    26. }
     
    Last edited: Oct 9, 2018
  23. illinar

    illinar

    Joined:
    Apr 6, 2011
    Posts:
    422
    But.. IJobProcessComponentData is also magic, isn't it? And I think it is not going away.

    I prefer magic if it means a lot of saved time and cleaner API.

    Alternatively, is some kind of Code generation a-la Entitas possible? Where you have Heavy classes generated in the background generating a super-light API for you to actually work with. The problem here is the multistep compilation process and refactoring issues (YourCode>GeneratedCode>YourCode) which would require connecting IDE to compiler somehow and trigger recompile for the generated code when refactoring your first layer of code.
     
    Last edited: Oct 9, 2018
    e199 likes this.
  24. 5argon

    5argon

    Joined:
    Jun 10, 2013
    Posts:
    814
    The difference is that the data contained in the inject struct appeared to came from nowhere (without the [Inject] attribute it would be just a default valued struct with no connection to anything), but IJobProcessComponentData's `ref` in the `Execute`'s input parameter is already implying that some data would be coming in.. it do feel magical but in not a hacky way. I think...

    Also I am curious about the future of [Inject] System, as it is the fundamental of how to use the barrier system. Should we ask World.GetExistingManager manually for barrier/other systems from now on? That way will a JobComponentSystem works with dependency correctly?
     
    Last edited: Oct 9, 2018
    illinar likes this.
  25. starikcetin

    starikcetin

    Joined:
    Dec 7, 2017
    Posts:
    173
    I used to feel about reflection the same way you do, then I actually used it extensively in a project and saw that it is no different than any other language feature.

    Therefore I can't say I agree that it is magic, it is just another facility of the language.
     
    Last edited: Oct 10, 2018
    illinar likes this.
  26. Adam-Mechtley

    Adam-Mechtley

    Unity Technologies

    Joined:
    Feb 5, 2007
    Posts:
    170
    Yes injection actually added very little in this case.

    Code (CSharp):
    1. // instead of this
    2. [Inject] EndFrameBarrier m_Barrier;
    3.  
    4. // simply do this
    5. protected override void OnCreateManager()
    6. {
    7.     m_Barrier = World.GetOrCreateManager<EndFrameBarrier>();
    8. }
     
    Creepgin and TZ- like this.
  27. Cyberwiz15

    Cyberwiz15

    Joined:
    Nov 10, 2016
    Posts:
    53
    Personally I'm not crazy about either usages above. From a get the World up and running extremely quickly the two methods work really well, but I would rather prefer to stick to constructor injection. This would require a lot more manual wire-up code in your bootstrap method, but from having the ability to quickly glance at which systems actually have dependencies on others it'll be really simple to track down.

    That said having it be possible to easily inject systems opens up the possibility to inject systems other than barrier systems. One would then have to think really hard about perhaps taking what would seem like the easy route to a solution, but one that might snowball into more spaghetti. I'm not outright against it though, it's just a question I haven't been able to answer myself.
     
  28. 5argon

    5argon

    Joined:
    Jun 10, 2013
    Posts:
    814
    I was referring to this line in JobComponentSystem code about the dependence on injection. If the barrier does not pass through the injection system then the playback will not know about having to wait for the job of this system first before the playback can start. Isn't it?

    Code (CSharp):
    1.         unsafe void AfterOnUpdate(JobHandle outputJob, bool throwException)
    2.         {
    3.             AfterUpdateVersioning();
    4.          
    5.             JobHandle.ScheduleBatchedJobs();
    6.  
    7.             AddDependencyInternal(outputJob);
    8.  
    9.             // Notify all injected barrier systems that they will need to sync on any jobs we spawned.
    10.             // This is conservative currently - the barriers will sync on too much if we use more than one.
    11.             for (int i = 0; i < m_BarrierList.Length; ++i)
    12.             {
    13.                 m_BarrierList[i].AddJobHandleForProducer(outputJob); // <------
    14.             }
     
    pcysl5edgo likes this.
  29. julian-moschuering

    julian-moschuering

    Joined:
    Apr 15, 2014
    Posts:
    230
    I don't understand why that is the case. In both cases the same memory has to be available in cache?
     
    M_R and capyvara like this.
  30. capyvara

    capyvara

    Joined:
    Mar 11, 2010
    Posts:
    63
    There are any plans to use Span<T> and Memory<T>?

    Also an unsafe low-level layer (maybe accessed thru extension methods such as NativeArray GetUnsafePtr() will come handy, for dealing with raw pointers instead.
     
  31. 5argon

    5argon

    Joined:
    Jun 10, 2013
    Posts:
    814
    While we are at it, I wanted to know why `GetComponentGroup` is `protected` while `
    GetArchetypeChunkComponentType` is `public` on ComponentSystem API?

    Both methods needs to register something to the system I understand, but could you make `GetComponentGroup` a `public` also? That way it is possible to simplify the logic to somewhere else like a struct keeping reference to the system and do all the required work.
     
  32. pcysl5edgo

    pcysl5edgo

    Joined:
    Jun 3, 2018
    Posts:
    38
    You can use them by putting System.Memory.dll into Plugins Folder.
    System.Memory.dll is in the nuget package which is actually a zip file.

    I don't use Span<T> and Memory<T> in the Unity environment because they are much slower than NativeArray<T>.
    .NET 4.x does no optimization of Span<T>.
     
  33. M_R

    M_R

    Joined:
    Apr 15, 2015
    Posts:
    288
    not related to ECS specifically, but to all APIs using NativeArray:

    1) if an API accepts a NativeArray input, make it also/instead accept NativeSlice (NativeArray implicitly converts to NativeSlice)
    1.1) make other types capable of being converted to NativeSlice if it makes sense, e.g. NativeList
    2) for APIs that return native containers, use a convention to specify if Dispose should be called, e.g.
    2a) Dispose should be called -> have allocator arg (
    NativeArray<T> API(..., Allocator label)
    )
    2b) Dispose should not be called -> return native slice (
    NativeSlice<T> API(...)
    ).
     
    pvloon, illinar, e199 and 2 others like this.
  34. Adam-Mechtley

    Adam-Mechtley

    Unity Technologies

    Joined:
    Feb 5, 2007
    Posts:
    170
    Sorry for the confusion on my part, I had inaccurate information. I have updated the post accordingly
     
  35. illinar

    illinar

    Joined:
    Apr 6, 2011
    Posts:
    422
    I read here and there in different threads about problems with ComponentDataArray and thus it's depreciation. Pretty interesting. I think there should be a pinned thread or documentation page about feature status, specifically about features and APIs to be deprecated or under risk, or definitely there to stay.

    Also, I'm currently relying on ComponentDataArray (injection) so I would like to get any idea of how it's replacement might look to see what it could take me to rewrite my code in the future. And it's just very interesting to learn about the ECS development process and what works, what doesn't, what things you are trying out, etc.
     
    Creepgin, recursive, 5argon and 5 others like this.
  36. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    2,010
    Yes and how they relate to each other. Diving into ECS is like jumping on a running train, fun but dangerous.
     
    illinar, Sylmerria and FROS7 like this.
  37. LurkingNinjaDev

    LurkingNinjaDev

    Joined:
    Jan 20, 2015
    Posts:
    1,940
    If I may vote, I'd like to vote to use the man-hour required by maintaining a constantly changing documentation should be applied to actual development instead. When the API is more or less stable, I will demand proper documentation. The more time wasted on rewriting API-docs the later will arrive that point. Those who like the adventure can dive in now. I'm just dipping my toe.
     
    julian-moschuering likes this.
  38. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    686
    Samples repository and release notes is what you need :) sample source code - best documentation :)
     
    LurkingNinjaDev likes this.
  39. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    2,010
    Rubber duck developing :D
     
  40. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    686
    You write code in notepad? :) Use IDE.
    Array is System namespace.
    StartIndex => not exists anymore
    GetNativeSlice => GetNativeArray
     
  41. TZ-

    TZ-

    Joined:
    Oct 23, 2017
    Posts:
    18
    I use Visual Studio Community, what makes you think i write in notepad? that is just how it copy pasted (sorry i have only ever used VS and have no clue why you asked about notepad. Edit: i have since found the code buttons, i guess that is how i should have posted it, my apologies very new to all this :(

    So do i gather then that StartIndex is no longer needed hence the going away of it, and thanks re the NativeSlice issue :D

    btw in the time it took you to reply i had found another thread i thought more appropriate to ask in, as the discussion was directly regarding that page i linked, so i came back here to delete this post as i did not want to be in trouble for double posting.


    the question i asked
    I read this morning that the struct and inject way was going away and followed a link to

    https://github.com/Unity-Technologi...ster/Documentation/content/chunk_iteration.md

    it said this was the recommended way forward.

    But i have hit a couple of errors, when i am trying to implement this new (toMe) way of doing things

    query = new EntityArchetypeQuery
    {
    Any = Array.Empty<ComponentType> (),
    None = Array.Empty<ComponentType> (),
    All = new ComponentType [] { typeof (Position), typeof (Player) }
    };

    Array - gives error "the name Array does not exist in the current context, and i have no clue whether that is due to a change or that its one of them hint words like foo and bar, and i am supposed to know what to put there.

    also

    public void Execute (int chunkIndex)
    {
    var chunk = chunks [chunkIndex];
    var chunkStartIndex = chunk.StartIndex;
    var chunkCount = chunk.Count;
    var chunkEcsTestData = chunk.GetNativeSlice (ecsTestData);
    chunk.
    for (int i = 0; i < chunkCount; i++)
    {
    values [chunkStartIndex + i] = chunkEcsTestData .value;
    }
    }

    errors are - ArchetypeChunk does not contain a definition for "StartIndex"
    - ArchetypeChunk does not contain a definition for "GetNativeSlice"


    Could anyone be so kind as to point me in the right direction as to what i should be doing please?

    any help much appreciated :D
     
  42. recursive

    recursive

    Joined:
    Jul 12, 2012
    Posts:
    409
    Some notes on the API:

    • I feel RequireComponentTag and RequireSubtractiveComponent should be renamed to be more in line with the query nomenclature. Perhaps something like RequireAllComponents and RequireNoneComponents?
    • I often find I'm writing a lot of code to handle Add/Remove Events that update config registries and have similar or the same data layouts/payloads, with the exception of one component tag. In all other ways they're the same and the IJobProcessComponentData to eventually transform them into lists for denoising/processing is the same, but they're separate jobs due to the attributes needed for IJobProcessComponentData. Will there be a way to schedule IJobProcessComponentData via a ComponentGroup (so I can reuse the same job struct), or should I use IJobChunk + ComponentGroup instead?
    • Also related: I also often find I use a NativeQueue<T> with IJobProcessComponentData then have an IJob flatten to a NativeList<T>, then back to IJobParallelFor. Will there be a way to concurrently AddRange() to a NativeList<T> from something like IJobChunk or IJobProcessComponentData in the future?
     
  43. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    686
    I tell about notepad because in notepad you not have intellisense which show you correct namespace for Array :)
     
  44. davenirline

    davenirline

    Joined:
    Jul 7, 2010
    Posts:
    266
    Any one of you guys know how to use TransformAccessArray with chunk iteration? I'm kind of stuck. See here.
     
  45. illinar

    illinar

    Joined:
    Apr 6, 2011
    Posts:
    422
    upload_2018-10-26_14-28-12.png

    Just wanna say, this would be awesome. I don't fully understand what would be happening behind the scene there and what "in" operator is, but such API is slick and instantly obvious.

    Does it create queries with reflection when a system is created or when the first call is made? What ForEach would be exactly and is there round bracket missing or is it allowable syntax? :p

    P.S. if someone missed the Unite presentation that is an example from the first presentation here: https://www.youtube.com/watch?v=fZfPv-oxO7U
     
  46. e199

    e199

    Joined:
    Mar 24, 2015
    Posts:
    52
    illinar likes this.
  47. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    3,474
    That part of Unite was very comforting to see. I had a lot of misgivings about the direction of ECS - the two stick "pure" sample project uses 54 lines of code and imports 7 namespaces to move an object forwards at a certain speed - but @Joachim_Ante pretty much attacked all of my worries head on with that presentation.

    I was planning to make a large post here to complain about it, now I don't have to! Swell!


    I still think the code presented could be made terser! If we add a bunch of extension methods to Entity, you'd get:

    Code (csharp):
    1. ForEach((ref Spawner spawner, in Position position) =>
    2. {
    3.     var instance = spawner.Entity.Instantiate(); //Maybe this should be named Copy?
    4.     instance.SetComponentData(position);
    5.     instance.GetComponentData<CharacterDef>().Name = "Player3";
    6. });
    Hey, it's almost as few characters as the corresponding MonoBehaviour code! If we remove the "Data" keyword (everything is data, after all), it's fewer characters the same number of symbols, as we've replaced
    .transform.position = 
    with
    SetComponentData()
    .

    If the extension methods are inlined, there would be no overhead.
     
    Arowx, illinar and bac9-flcl like this.
  48. Micz84

    Micz84

    Joined:
    Jul 21, 2012
    Posts:
    164
    Entity would need to have reference to entity manager to create such API. So I do not think it is possible. Data at the end of all ECS component related methods and interfaces are added to make clear separation from Component class currently used in Unity.
     
  49. illinar

    illinar

    Joined:
    Apr 6, 2011
    Posts:
    422
    Minor thing, but I'm curious if it would be possible to have:
    PostUpdateCommands.AddFlagComponent<MyComponent>(e);

    Instead of:
    PostUpdateCommands.AddComponent<MyComponent>(e, new MyComponent());
     
    Last edited: Oct 26, 2018
  50. Micz84

    Micz84

    Joined:
    Jul 21, 2012
    Posts:
    164
    I think this should work:
    Code (CSharp):
    1. public static class EntityCommandBufferExtension
    2. {
    3.     public static void AddTagComponent<T>(this EntityCommandBuffer self, Entity entity ) where T:struct,IComponentData
    4.     {
    5.         self.AddComponent(entity, new T());
    6.     }
    7.     public static void SetTagComponent<T>(this EntityCommandBuffer self, Entity entity) where T : struct, IComponentData
    8.     {
    9.         self.SetComponent(entity, new T());
    10.     }
    11. }
    12.  
    13. public static class EntityCommandBufferConcurrentExtension
    14. {
    15.     public static void AddTagComponent<T>(this EntityCommandBuffer.Concurrent self,int index, Entity entity) where T : struct, IComponentData
    16.     {
    17.         self.AddComponent(index, entity, new T());
    18.     }
    19.     public static void SetTagComponent<T>(this EntityCommandBuffer.Concurrent self, int index, Entity entity) where T : struct, IComponentData
    20.     {
    21.         self.SetComponent(index, entity, new T());
    22.     }
    23. }
     
    davenirline, RaL, TZ- and 1 other person like this.