You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Schedule can only be used if the __ScheduleParameters__ are created with __JobType.Single__, the other two schedule functions require __JobType.ParallelFor__.
37
-
The __arrayLength__ and __innerloopBatchCount__ parameter passed to __ScheduleParallelFor__ are used to determine how many indices the jobs should process and how many indices it should handle in the inner loop (see the section on [__Execution__ and __JobRanges__](#execution-and-jobranges) for more information on the inner loop count).
37
+
The __arrayLength__ and __innerLoopBatchCount__ parameter passed to __ScheduleParallelFor__ are used to determine how many indices the jobs should process and how many indices it should handle in the inner loop (see the section on [__Execution__ and __JobRanges__](#execution-and-jobranges) for more information on the inner loop count).
38
38
__ScheduleParallelForTransform__ is similar to ScheduleParallelFor, but it also has access to a __TransformAccessArray__ that allows you to modify __Transform__ components on __GameObjects__. The number of indices and batch size is inferred from the TransformAccessArray.
39
39
40
40
## Execution and JobRanges
@@ -51,7 +51,7 @@ The JobRanges contain the batches and indices a ParallelFor job is supposed to p
This continues until it returns `false`, and after calling it process all items with index between begin and end.
54
+
This continues until it returns `false`, and after calling it process all items with index between __begin__ and __end__.
55
55
The reason you get batches of items, rather than the full set of items the job should process, is that Unity will apply [work stealing](https://en.wikipedia.org/wiki/Work_stealing) if one job completes before the others. Work stealing in this context means that when one job is done it will look at the other jobs running and see if any of them still have a lot of work left. If it finds a job which is not complete it will steal some of the batches that it has not yet started; to dynamically redistribute the work.
56
56
57
57
Before a ParallelFor job starts processing items it also needs to limit the write access to NativeContainers on the range of items which the job is processing. If it does not do this several jobs can potentially write to the same index which leads to race conditions. The NativeContainers that need to be limited is passed to the job and there is a function to patch them; so they cannot access items outside the correct range. The code to do it looks like this:
Copy file name to clipboardExpand all lines: Documentation/content/ecs_in_detail.md
+92-5Lines changed: 92 additions & 5 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -27,7 +27,7 @@ IComponentData structs may not contain references to managed objects. Since the
27
27
28
28
An __EntityArchetype__ is a unique array of __ComponentType__. __EntityManager__ uses EntityArchetypes to group all Entities using the same ComponentTypes in chunks.
29
29
30
-
```
30
+
```C#
31
31
// Using typeof to create an EntityArchetype from a set of components
@@ -98,7 +98,8 @@ The ComponentData for each Entity is stored in what we internally refer to as a
98
98
99
99
A chunk is always linked to a specific EntityArchetype. Thus all Entities in one chunk follow the exact same memory layout. When iterating over components, memory access of components within a chunk is always completely linear, with no waste loaded into cache lines. This is a hard guarantee.
100
100
101
-
__ComponentDataArray__ is essentially an iterator over all EntityArchetypes compatible with the set of required components; for each EntityArchetype iterating over all chunks compatible with it and for each chunk iterating over all Entities in that chunk.
101
+
__ComponentDataArray__ is essentially a convenience index based iterator for a single component type;
102
+
First we iterate over all EntityArchetypes compatible with the ComponentGroup; for each EntityArchetype iterating over all chunks compatible with it and for each chunk iterating over all Entities in that chunk.
102
103
103
104
Once all Entities of a chunk have been visited, we find the next matching chunk and iterate through those Entities.
104
105
@@ -115,6 +116,22 @@ By default we create a single World when entering Play Mode and populate it with
115
116
116
117
> Note: We are currently working on multiplayer demos, that will show how to work in a setup with separate simulation & presentation Worlds. This is a work in progress, so right now have no clear guidelines and are likely missing features in ECS to enable it.
117
118
119
+
## System update order
120
+
121
+
In ECS all systems are updated on the main thread. The order in which the are updated is based on a set of constraints and an optimization pass which tries to order the system in a way so that the time between scheduling a job and waiting for it is as long as possible.
122
+
The attributes to specify update order of systems are ```[UpdateBefore(typeof(OtherSystem))]``` and ```[UpdateAfter(typeof(OtherSystem))]```. In addition to update before or after other ECS systems it is possible to update before or after different phases of the Unity PlayerLoop by using typeof([UnityEngine.Experimental.PlayerLoop.FixedUpdate](https://docs.unity3d.com/2018.1/Documentation/ScriptReference/Experimental.PlayerLoop.FixedUpdate.html)) or one of the other phases in the same namespace.
123
+
124
+
The UpdateInGroup attribute will put the system in a group and the same __UpdateBefore__ and __UpdateAfter__ attributes can be specified on a group or with a group as the target of the before/after dependency.
125
+
126
+
To use __UpdateInGroup__ you need to create and empty class and pass the type of that to the __UpdateInGroup__ attribute
@@ -294,7 +311,7 @@ Lastly you can also inject a reference to another system. This will populate the
294
311
295
312
## ComponentGroup
296
313
297
-
The ComponentGroup is foundation class on top of which all iteration methods are built (Injection, foreach, IJobProcessComponetnData etc)
314
+
The ComponentGroup is foundation class on top of which all iteration methods are built (Injection, foreach, IJobProcessComponentData etc)
298
315
299
316
Essentially a ComponentGroup is constructed with a set of required components, subtractive components.
300
317
@@ -348,7 +365,9 @@ class PositionToRigidbodySystem : ComponentSystem
348
365
The Entity struct identifies an Entity. If you need to access component data on another Entity, the only stable way of referencing that component data is via the Entity ID. EntityManager provides a simple get & set component data API for it.
However EntityManager can't be used on a C# job. __ComponentDataFromEntity__ gives you a simple API that can also be safely used in a job.
@@ -364,10 +383,78 @@ var position = m_LocalPositions[myEntity];
364
383
365
384
## ExclusiveEntityTransaction
366
385
367
-
EntityTransaction is an API to create & destroy entities from a job. The purpose is to enable procedural generation scenarios where construction of massive scale instantiation must happen on jobs. This API is very much a work in progress.
386
+
EntityTransaction is an API to create & destroy entities from a job. The purpose is to enable procedural generation scenarios where instantiation on big scale must happen on jobs. As the name implies it is exclusive to any other access to the EntityManager.
387
+
388
+
ExclusiveEntityTransaction should be used on manually created world that acts as a staging area to construct & setup entities.
389
+
After the job has completed you can end the EntityTransaction and use ```EntityManager.MoveEntitiesFrom(EntityManager srcEntities);``` to move the entities to an active world.
390
+
368
391
369
392
## EntityCommandBuffer
370
393
394
+
The command buffer class solves two important problems:
395
+
396
+
1. When you're in a job, you can't access the entity manager
397
+
2. When you access the entity manager (to say, create an entity) you invalidate all injected arrays and component groups
398
+
399
+
The command buffer abstraction allows you to queue up changes to be performed (from either a job or from the main thread) so that they can take effect later on the main thread. There are two ways to use a command buffer:
400
+
401
+
1.`ComponentSystem` subclasses which update on the main thread have one available automatically called `PostUpdateCommands`. To use it, simply reference the attribute and queue up your changes. They will be automatically applied to the world immediately after you return from your system's `Update` function.
402
+
403
+
Here's an example from the two stick shooter sample:
As you can see, the API is very similar to the entity manager API. In this mode, it is helpful to think of the automatic command buffer as a convenience that allows you to prevent array invalidation inside your system while still making changes to the world.
417
+
418
+
2. For jobs, you must request command buffers from a `Barrier` on the main thread, and pass them to jobs. The barriers will play back in the created order on the main thread when the barrier system updates. This extra step is required so that memory management can be centralized and determinism of the generated entities and components can be guaranteed.
419
+
420
+
Again let's look at the two stick shooter sample to see how this works in practice.
421
+
422
+
First, a barrier system is declared:
423
+
424
+
```cs
425
+
publicclassShotSpawnBarrier : BarrierSystem
426
+
{}
427
+
```
428
+
429
+
There's no code in a barrier system, it just serves as a synchronization point.
430
+
431
+
Next, we inject this barrier into the system that will request command buffers from it:
In the job, we can use the command buffer normally:
450
+
451
+
```cs
452
+
CommandBuffer.CreateEntity(ShotArchetype);
453
+
CommandBuffer.SetComponent(spawn);
454
+
```
455
+
456
+
When the barrier system updates, it will automatically play back the command buffers. It's worth noting that the barrier system will take a dependency on any jobs spawned by systems that access it (so that it can now that the command buffers have been filled in fully). If you see bubbles in the frame, it may make sense to try moving the barrier later in the frame, if your game logic allows for this.
457
+
371
458
## GameObjectEntity
372
459
373
460
ECS ships with the __GameObjectEntity__ component. It is a MonoBehaviour. In __OnEnable__, the GameObjectEntity component creates an Entity with all components on the GameObject. As a result the full GameObject and all its components are now iterable by ComponentSystems.
Copy file name to clipboardExpand all lines: Documentation/content/ecs_principles_and_vision.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -11,7 +11,7 @@ We measure ourselves against the performance that can be achieved in C++ with ha
11
11
We are using a combination of compiler technology (Burst), containers (Unity.Collections), data layout of components (ECS) to make it easy to write efficient code by default.
12
12
13
13
* Data layout & iteration - The Entity Component System gurantees linear data layout when iterating entities in chunks by default. This is a critical part of the performance gains provided by the Entity Component System.
14
-
* The C# job system lets you write multithreaded code in a simple way. It also safe. The C# Job Debugger detects any race conditions.
14
+
* The C# job system lets you write multithreaded code in a simple way. It is also safe. The C# Job Debugger detects any race conditions.
15
15
* Burst is our compiler specifically for C# jobs. C# job code follows certain patterns that we can use to produce more efficient machine code. Code is compiled & optimized for each target platforms taking advantage of SIMD instructions.
16
16
17
17
An example of this is the performance of Instantiation. Comparing to the theoretical limit, of instantiating 100.000 entities with 320 bytes of a memcpy takes 9ms. Instantiating those entities via the Entity Component System takes 10ms. So we are very close to the theoretical lmit.
0 commit comments