Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

Question NativeParallelHashMap as Singleton

Discussion in 'Entity Component System' started by dennis124, Oct 9, 2022.

  1. dennis124

    dennis124

    Joined:
    Oct 9, 2022
    Posts:
    17
    Hello everyone,

    In my new game that I make with ecs I want to be able to store all my buildings in a NativeParallelHashMap.
    The HashMap should be a dictionary with the key int2 as the position and the value is an entity.
    The problem is that the NativeParallelHashMap is not primitive (so I can't set this as a singleton) and I neither found a solution nor an alternative for the hashmap.

    Thanks in advance for help
     
  2. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    4,196
    If you're still thinking "singleton" you're in the wrong forum. ;)

    You will not find a single singleton in jobified (entitified?) DOTS code and for good reason: they won't work with jobs, or entity systems for that matter.

    A singleton implies a static reference to a managed instance, neither static variables nor managed references are allowed in Jobs.
     
    Rockaso likes this.
  3. mattdymott

    mattdymott

    Joined:
    Dec 27, 2016
    Posts:
    29
    Something like this maybe:

    Code (CSharp):
    1.  
    2. public struct BuildingSingleton : IComponentData
    3. {
    4.     public NativeParallelHashMap<int2, Entity> BuildingHashMap;
    5. }
    6.  
    7. public partial class BuildingSystem : SystemBase
    8. {
    9.     protected override void OnCreate()
    10.     {
    11.         EntityManager.AddComponentData(SystemHandle, new BuildingSingleton
    12.         {
    13.             BuildingHashMap = new NativeParallelHashMap<int2, Entity>(32, Allocator.Persistent)
    14.         });
    15.     }
    16.  
    17.     protected override void OnDestroy()
    18.     {
    19.         var buildingSingleton = GetSingleton<BuildingSingleton>();
    20.         buildingSingleton.BuildingHashMap.Dispose();
    21.     }
    22.  
    23.     protected override void OnUpdate()
    24.     {
    25.          var buildingSingleton = GetSingleton<BuildingSingleton>();
    26.  
    27.          //do stuff with buildingSingleton...
    28.     }
    29. }
    This requires the experimental 1.0 version though.
     
    bb8_1 likes this.
  4. mattdymott

    mattdymott

    Joined:
    Dec 27, 2016
    Posts:
    29
    Or if you are using a previous version then you can just store it as member of the system which is just a singleton anyway.

    Code (CSharp):
    1.  
    2. public partial class BuildingSystem : SystemBase
    3. {
    4.     public NativeParallelHashMap<int2, Entity> BuildingHashMap;
    5.  
    6.     protected override void OnCreate()
    7.     {
    8.         BuildingHashMap = new NativeParallelHashMap<int2, Entity>(32, Allocator.Persistent);
    9.     }
    10.  
    11.     protected override void OnDestroy()
    12.     {
    13.         BuildingHashMap.Dispose();
    14.     }
    15.  
    16.     protected override void OnUpdate()
    17.     {
    18.     }
    19. }
    And then if you need to access it in another system

    Code (CSharp):
    1.  
    2. public partial class SomeOtherSystem : SystemBase
    3. {
    4.         protected override void OnUpdate()
    5.         {
    6.                 var buildingSystem = World.GetOrCreateSystem<BuildingSystem>();
    7.         }
    8. }
    9.  
     
    Rockaso and bb8_1 like this.
  5. dennis124

    dennis124

    Joined:
    Oct 9, 2022
    Posts:
    17
    Thank you very much mattdymott.
    That was just what I was looking for.
     
  6. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,647
    That's pretty untrue in 1.0, 'singleton' (maybe poorly named) is very much a concept of core parts of entities.

    PhysicsWorld access
    Code (CSharp):
    1. SystemAPI.GetSingleton<PhysicsWorldSingleton>().PhysicsWorld
    Create CommandBuffers
    Code (CSharp):
    1. SystemAPI.GetSingleton<EndSimulationEntityCommandBufferSystem.Singleton>().CreateCommandBuffer(state.WorldUnmanaged)
    Also you can totally have similar concept in jobs using SharedStatic<T>
     
  7. Enzi

    Enzi

    Joined:
    Jan 28, 2013
    Posts:
    910
    What Tertle said. In 1.0 system data is encouraged to move outside of systems unless it's local but for something like a Hashmap that's read by several systems, a Singleton is pretty cool. The nice thing about it, if you set the system dependency via GetSingleton or GetSingletonRW in OnCreate you don't even have to worry about some scheduled jobs reading while it's still being written to. The job system takes care of it. It could work as static just fine but having clear dependencies is quite important in Entities.

    The best way to find out how this works is to look at PhysicsWorldSingleton in BuildPhysicsWorld.
     
    bb8_1 likes this.
  8. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,655
    That's incorrect. As many said above. We use singleton entities for shared native containers for the long time before 1.0 (since the first versions if I remember correctly, it's been very long time ago). We using class IComponentData (as before 1.0 native containers not allowed for struct ICD because of managed DisposeSentinel) with our custom dependency tracking mechanism, and every system which requires access to that shared native container (for example global map container on pathfinding requests queue) doing this properly through singleton entity with required access pattern (RO\RW) and passing that native container to required jobs without breaking any dependency chains.
     
    bb8_1 likes this.
  9. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    4,196
    I stand corrected. Wasn't aware that was a thing. :)

    The verbosity of this though ... just to get a singleton ref:
    Code (CSharp):
    1. SystemAPI.GetSingleton<EndSimulationEntityCommandBufferSystem.Singleton>()
     
    bb8_1 likes this.
  10. dennis124

    dennis124

    Joined:
    Oct 9, 2022
    Posts:
    17
    So I'm a bit further. But I still have a question.
    How can I use the Singleton in a Entities.ForEach with ScheduleParallel?
    When I want to get access to it, it says:
    error DC0004: Entities.ForEach Lambda expression captures a non-value type 'buildingSystem'. This is only allowed with .WithoutBurst() and .Run()

    (I used the ecs 0.5 example of mattdymott)
    Thank you
     
  11. mattdymott

    mattdymott

    Joined:
    Dec 27, 2016
    Posts:
    29
    Because BuildingSystem is a managed system you won't be able to use it directly inside a job.
    Instead assign a local variable with the hash map and because the hash map is not managed you can use it inside a job.

    Code (CSharp):
    1.  
    2. public partial class SomeOtherSystem : SystemBase
    3. {
    4.     protected override void OnUpdate()
    5.     {
    6.         var buildingSystem = World.GetOrCreate<BuildingSystem>();
    7.         var buildingHashMap = buildingSystem.BuildingHashMap;
    8.        
    9.         Entities
    10.             .ForEach((Entity entity) =>
    11.             {
    12.                 var entity = buildingHashMap[new int2(0, 0)];
    13.             })
    14.             .Run();
    15.     }
    16. }
     
  12. dennis124

    dennis124

    Joined:
    Oct 9, 2022
    Posts:
    17
    Again very helpful.
    Thanks a lot