If use single EntityCommandBuffer.ParallelWriter through multiple Entities.ForEach, it will cause entity command execute order non-deterministic.
Maybe it should have a warning?
var ecb = barrier.CreateCommandBuffer().AsParallelWriter();
Entities.ForEach((Entity entity, int entityInQueryIndex) =>
{
ecb.AddComponent<Translation>(entityInQueryIndex, entity);
}).ScheduleParallel();
Entities.ForEach((Entity entity, int entityInQueryIndex) =>
{
ecb.RemoveComponent<Translation>(entityInQueryIndex, entity);
}).ScheduleParallel();
barrier.AddJobHandleForProducer(Dependency);
When the ecb playback, it could be any order.
1 Like
Creating entities does not guarantee determinism.
Consider even simple case scenario, when one player has CPU with 4 cores and other with 8. Both will end up with different results.
You should not trust entities orders, indexing nor version for deterministic purposes. Entities may also switch orders, as they execute.
If you need deterministic order of entities, you need create custom mechanism of indexing and tracking entities.
@Antypodish : No, both will have the same result due to the sorting mechanism of ParallelWriter. The issue here is that the commands in the command buffer get sorted by entityInQueryIndex coming from two different queries which makes no sense. The result is still independent of CPU count but it’s not the result one wants. The simple fix of course is to use two command buffers.
I guess it would make sense to deny passing the same parallel writer to multiple jobs.
And can be this guaranteed across multiple cpu architectures?
Still, I wouldn’t trust entities orders, for any deterministic purposes.
Yes. The command order is different everywhere but sorted by the sortkey will be the same on all CPUs.
Yes.
The issue I explained up there actually isn’t a problem here as both have exactly the same query. This means the resulting buffer after sorting should contain AddComponent/RemoveComponent pairs for each entity in correct order.
It seems like ECB’s ParallelWriter currently relies on each thread not accessing each sort key multiple times and accessing a different in between.
Even singlethreaded this fails and results in a pseudo random result:
Job.WithCode(() =>
{
for (var i = 0; i < entities.Length; i++)
{
ecb.AddComponent<Translation>(i, entities[i]);
}
for (var i = 0; i < entities.Length; i++)
{
ecb.RemoveComponent<Translation>(i, entities[i]);
}
}).Schedule(default).Complete();
1 Like