Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Question How to fix a race condition between these 2 systems

Discussion in 'Entity Component System' started by Michelle_Ca, Jan 29, 2023.

  1. Michelle_Ca

    Michelle_Ca

    Joined:
    Aug 19, 2019
    Posts:
    113
    I have made these two systems to enable/disable the gameObjects. is there any way to solve the race condition without calling JobHandle.Complete() on the first job


    Code (CSharp):
    1. public class RenderOnOffEntitiesSystem : SystemBase
    2. {
    3.  
    4.     protected override void OnUpdate()
    5.     {
    6.  
    7.  
    8.    
    9.         Matrix4x4 V = Camera.main.worldToCameraMatrix;
    10.         Matrix4x4 P = Camera.main.projectionMatrix;
    11.         Matrix4x4 MVP = P * V;
    12.         float3 cameraPos = Camera.main.transform.position;
    13.  
    14.         Entities.ForEach((ref renderEnabledCo renderEnabledCo, in LocalToWorld localToWorld) =>
    15.             {
    16.  
    17.                 float3 screenPos = MVP.MultiplyPoint(localToWorld.Position);
    18.                 Vector2 screenPoint = new Vector2(screenPos.x + 1f, screenPos.y + 1f) * 0.5f;
    19.  
    20.                 if (screenPoint.x < 0 || screenPoint.x > 1 || screenPoint.y < 0 || screenPoint.y > 1 || math.distancesq(localToWorld.Position, cameraPos) > 1900 /*35*/)
    21.                 {
    22.  
    23.                     renderEnabledCo.Enabled = false;
    24.  
    25.                 }
    26.                 else
    27.                 {
    28.                     renderEnabledCo.Enabled = true;
    29.  
    30.                 }
    31.  
    32.  
    33.             }).ScheduleParallel();
    34.  
    35.  
    36.  
    37.  
    38.     }
    39. }
    system 2

    Code (CSharp):
    1. public class RenderOnOffObjectsSystem : SystemBase
    2. {
    3.  
    4.     private EntityQuery entities;
    5.     protected override void OnCreate()
    6.     {
    7.         EntityQueryDesc entitiesQueryDesc = new EntityQueryDesc
    8.         {
    9.             All = new ComponentType[] { typeof(renderTag) }
    10.         };
    11.  
    12.         entities = GetEntityQuery(entitiesQueryDesc);
    13.     }
    14.     protected override void OnUpdate()
    15.     {
    16.  
    17.         var renderEnabledCoType = GetArchetypeChunkComponentType<renderEnabledCo>(true);
    18.         var RenderIndexCoType = GetArchetypeChunkComponentType<RenderIndexCo>(true);
    19.         var entitiesChunks = entities.CreateArchetypeChunkArray(Allocator.TempJob);
    20.    
    21.         var gameobjects = GMSingleton.Singleton.gameobjects.ToArray();
    22.  
    23.         for (int c = 0; c < entitiesChunks.Length; c++)
    24.         {
    25.             var chunk = entitiesChunks[c];
    26.  
    27.             var renderEnabledCos = chunk.GetNativeArray(renderEnabledCoType);
    28.             var RenderIndexCos = chunk.GetNativeArray(RenderIndexCoType);
    29.        
    30.  
    31.             for (int i = 0; i < chunk.Count; i++)
    32.             {
    33.                     if (renderEnabledCos[i].Enabled )
    34.                     {
    35.                    
    36.                         gameobjects[RenderIndexCos[i].value].SetActive(true);
    37.                    
    38.                     }
    39.                     else
    40.                     {
    41.                         gameobjects[RenderIndexCos[i].value].SetActive(false);
    42.                  
    43.                     }
    44.             }
    45.        
    46.         }
    47.         entitiesChunks.Dispose();
    48.     }
    49. }
     
    Last edited: Jan 29, 2023
  2. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    4,223
    You have a job that writes to renderEnabledCo. And then later you want to read from that component on the main thread. Therefore, you must complete that job, probably in the second system via CompleteDependency().
     
    Michelle_Ca likes this.
  3. TheOtherMonarch

    TheOtherMonarch

    Joined:
    Jul 28, 2012
    Posts:
    862
    Michelle_Ca likes this.
  4. Michelle_Ca

    Michelle_Ca

    Joined:
    Aug 19, 2019
    Posts:
    113
    Yes that is the problem so I get an error


    I am looking for something similar to LambdaJobDescriptionConstructionMethods like :
    WithNativeDisableContainerSafetyRestriction.
    WithNativeDisableParallelForRestriction.
    WithNativeDisableUnsafePtrRestriction.


    however when I use Entities.ForEach.Run() in the second system the race condition error doesn't occur what is actually happening here so the problem is solved automatically ?

    Code (CSharp):
    1. public class RenderOnOffObjectsSystem : SystemBase
    2. {
    3.  
    4.     protected override void OnUpdate()
    5.     {
    6.         var gameobjects = GMSingleton.Singleton.gameobjects.ToArray();
    7.  
    8.         Entities.WithoutBurst().ForEach((in renderEnabledCo renderEnabledCo, in RenderIndexCo renderIndexCo) =>
    9.  
    10.         {
    11.  
    12.             if (renderEnabledCo.Enabled)
    13.             {
    14.                 gameobjects[renderIndexCo.value].SetActive(true);
    15.             }
    16.             else
    17.             {
    18.                 gameobjects[renderIndexCo.value].SetActive(false);
    19.             }
    20.  
    21.         }).Run();
    22.  
    23.     }
    24. }
     
  5. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    4,223
    Entities.ForEach().Run() completes the JobHandle behind the scenes.
     
    Michelle_Ca likes this.
  6. Michelle_Ca

    Michelle_Ca

    Joined:
    Aug 19, 2019
    Posts:
    113
    aha i see thank you! i also have this System

    Code (CSharp):
    1.  
    2. public class AttackSystem : SystemBase
    3. {
    4.     protected override void OnUpdate()
    5.     {
    6.  
    7.         ComponentDataFromEntity<LocalToWorld> LocalToWorlds = GetComponentDataFromEntity<LocalToWorld>(true);
    8.         BufferFromEntity<dmBufferBufferElement> dmBuffer = GetBufferFromEntity<dmBufferBufferElement>();
    9.  
    10.         Entities.WithReadOnly(LocalToWorlds).WithNativeDisableParallelForRestriction(dmBufferElements)
    11.             .ForEach((Entity entity,in ReferencesComponent references) =>
    12.             {
    13.  
    14.                             float distance = math.distance(LocalToWorlds[entity].Position, LocalToWorlds[references.enemy].Position);
    15.  
    16.                             if (distance <= 5)
    17.                             {
    18.                                dmBuffer[references.enemy].Add(new dmBufferBufferElement { value = 1 });
    19.                             }
    20.  
    21.             }).ScheduleParallel();
    22.     }
    23. }
    I would like to ask if it is safe to add BufferElements like so in Parallel, and Is it safe to read or write from ComponentDataFromEntity inside Entities.ForEach, I mean will the system add this to the dependency to complete the JobHandle to avoid race condition in the other systems?
     
  7. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    4,223
    If the dynamic buffer was a ref argument in your ForEach(), adding would for sure be safe. However, since you are using a lookup (is there a reason you are still on Entities 0.51?), you yourself have to be sure that no two threads can touch the same dynamic buffer at the same time. Otherwise, a race condition could cause a hard crash on rare occasions.

    Reading lookups inside an Entities.ForEach in pretty much always safe. Writing is allowed in non-parallel jobs by default, but you have to be more careful with parallel jobs (by default the safety system will scream at you).
     
    Michelle_Ca likes this.
  8. Michelle_Ca

    Michelle_Ca

    Joined:
    Aug 19, 2019
    Posts:
    113
    Yes, actually i have a project that I tried to upgrade it to (Unity 2022) and (Dots 1) and (URP) and (hybrid renderer v2) without changing anything, i just updated the materials to URP and i also disabled all the systems but the frame rate dropped from 300 fps to 180 just because of the render
     
  9. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    4,223
    You are on built-in? That must be a really old version. It is worth noting that prior to 2022.2 beta 15 that there was a significant rendering performance regression. But now it outperforms URP in 2021 with HR V2 in every benchmark I have thrown at it. If you are seeing a difference in builds (forget comparing in the editor, there's a lot more Entities tooling in 2022.2), I would suspect that would be due to the shaders themselves. HR V1 had major issues with main thread performance with dynamic entities.
     
    Michelle_Ca and bb8_1 like this.
  10. Michelle_Ca

    Michelle_Ca

    Joined:
    Aug 19, 2019
    Posts:
    113
    Yes I read that it is faster and more optimized, but I think it is something related to my project because I am still using GameObjects for render, and entities just for logic, so (i guess) any system related to entities graphic or render is just waste of cpu for my project, also, I ran into another problem, which is converting components because i have a lot of entities and i'm using [GenerateAuthoringComponent] then setting values in inspector.