Unity Transforms Question: How does system ensure parent's LocalToWorld is Updated Before Childs

I am new to DOTs and wondering how to implement Graph and Tree traversals. All of the documentation talks about iterating through entities in memory order or chunk-by-chunk order. I think this would not work for graph or tree algorithms where the Entities that correspond to the nodes of the graph/tree need to be visited in a specific order.

I looked at the code for the Unity.Transforms LocalToParentSystem.cs to see how the hierarchy tree is traversed. The System uses IJobChunk to visit the Entities block-by-block, in the order that they appear in the block. I don’t understand how this can work. How does the system ensure that a parent’s LocalToWorld is calculated before a child’s LocalToWorld? What if the parent’s LocalToWorld is in another chunk that is being executed by another job?

What am I missing here?

Here it the IJobChunk Execute method that visits each entity in the chunk, then updates the children of that entity. How does it ensure that the parent’s are always visited before the children? What if a child appears before its parent in chunkLocalToWorld, then it would update its children’s LocalToWorld before its own LocalToWorld was updated.

LocalToParentSystem.cs

            public void Execute(ArchetypeChunk chunk, int index, int entityOffset)
            {
                bool updateChildrenTransform =
                    chunk.DidChange<LocalToWorld>(LocalToWorldType, LastSystemVersion) ||
                    chunk.DidChange<Child>(ChildType, LastSystemVersion);

                var chunkLocalToWorld = chunk.GetNativeArray(LocalToWorldType);
                var chunkChildren = chunk.GetBufferAccessor(ChildType);
                for (int i = 0; i < chunk.Count; i++)
                {
                    var localToWorldMatrix = chunkLocalToWorld[i].Value;
                    var children = chunkChildren[i];
                    for (int j = 0; j < children.Length; j++)
                    {
                        ChildLocalToWorld(localToWorldMatrix, children[j].Value, updateChildrenTransform);
                    }
                }
            }

Because this IJobChunk runs on Root entities if you look at Entity query
5866771--624208--upload_2020-5-18_21-12-54.png

and it calculates matrices sequentially for every root,


which mean you process only top root entities (without Parent and with Child) and recursively update matrices from top to bottom, which means every next level work with an already updated parent matrix.
If you, for example, split your roots to different chunks (by some SharedComponentData, like the first tree root is SCD==1, second tree root SCD== 2 etc.), then IJobChunk will run 4 Execute calls for these 4 roots (every in his own chunk, because we divided them by SCD), and every Execute will process one root with all his child’s sequentially.

3 Likes

Ahhh, that makes sense! Thanks :)… I did look at the query, but being new to DOTs I didn’t interpret it correctly.

I presume that the children could be in other chunks being processed by a different job, … I suppose that doesn’t matter. Those child entities won’t be found by the job processing the other chunk.