Creating a NativeHashMap using IJobParallelFor in JobComponentSystem

Can anyone please provide me with a simple example of how to create a NativeHashMap using IJobParallelFor in JobComponentSystem.

eg I have 10 entities and I want to create a HashMap <i, entityIndex>

...

struct BuildIHash_IJobParallelFor : IJobParallelFor
{
    [NativeDisableParallelForRestriction
    [WriteOnly]
    public NativeHashMap<int, int> hashMap;
    
    public void Execute(int i)
    {
          hashMap.TryAdd(i, entity.Index);
    }
}

BuildIHash_IJobParallelFor job = new BuildIHash_IJobParallelFor()
{
    hashMap = new NativeHashMap<int, int>(10, Allocator.TempJob)
}

ToConcurrent() for concurrent writing.

1 Like

Like this ? … and how do I get this hasMap out . Do I create a NativeHashMap<int, int> and pass it to the job. Lastly [DeallocateOnJobCompletion] is not supported how do i dispose?
hashMap = myHashMapFinal.ToConcurrent()

struct BuildIHash_IJobParallelFor : IJobParallelFor
{
    [NativeDisableParallelForRestriction
    [WriteOnly]
    public NativeHashMap<int, int> hashMap.Concerent;
 
    public void Execute(int i)
    {
          hashMap.TryAdd(i, entity.Index);
    }
}
BuildIHash_IJobParallelFor job = new BuildIHash_IJobParallelFor()
{
    hashMap = new NativeHashMap<int, int>(10, Allocator.TempJob).ToConcurrent()
}

Create persistant NHM in OnCreate and just pass it to job, Clear when you need in OnUpdate and destroy in OnDestroy.

1 Like

OK came up with this but this feels super unsafe to me. Is this how we are suppose to access specific entity Index data?

public class Test1 : JobComponentSystem
{

    public static NativeHashMap<int, Entity> hashMapTest;


    protected override void OnCreate()
    {
        hashMapTest = new NativeHashMap<int, Entity>(10, Allocator.Persistent);
    }

    protected override void OnDestroy()
    {
        hashMapTest.Dispose();
    }

    struct BuildIHash_IJobParallelFor : IJobParallelFor
    {
        [NativeDisableParallelForRestriction]
        [WriteOnly]
        public NativeHashMap<int, Entity>.Concurrent hashMap;

        public void Execute(int i)
        {
            Entity entity = entityArrayBuffer[i];
            hashMap.TryAdd(i, entity.Index);
        }
    }

    protected override JobHandle OnUpdate(JobHandle inputDeps)
    {


        BuildIHash_IJobParallelFor job = new BuildIHash_IJobParallelFor()
        {
            hashMap = new NativeHashMap<int, Entity>(10, Allocator.TempJob).ToConcurrent()
        };

        inputDeps = job.Schedule(10, 12, inputDeps);
        inputDeps.Complete();
        return inputDeps;
    }
}

public class Test2 : ComponentSystem
{

    protected override void OnCreate()
    {
        Entity e;
        Test1.tilesHashMap.TryGetValue(1, out e)
        Debug.Log(entityIndex);

        // Get entity data by entityIndex?
         Debug.Log(EntityManager.GetComponentData<VertexComponent>(e).data);
    }

    protected override void OnUpdate() { }
}

What are you doing. Your code is real mess…

Why you use NDPFR if here is not requiread at all

[NativeDisableParallelForRestriction]
        [WriteOnly]
        public NativeHashMap<int, Entity>.Concurrent hashMap;

Why you doing this if you already have created map:

BuildIHash_IJobParallelFor job = new BuildIHash_IJobParallelFor()
        {
            hashMap = new NativeHashMap<int, Entity>(10, Allocator.TempJob).ToConcurrent()
        };

Why you use batch count 12 if your map is 10, its destroys all common sense of parallel job, cos your job will be processed on one thread and in this case is redundant and you can use simple IJob

inputDeps = job.Schedule(10, 12, inputDeps);

in addition if you immediatley complete job after schedule, why you schedule job and not just Run() it

inputDeps.Complete();

You HM is <int, Entity> why you put index and not whole entity

hashMap.TryAdd(i, entity.Index);

For me it looks like random coding:face_with_spiral_eyes:

1 Like

sorry i was trying to write a simplified example of what i was trying to learn. Here is the actual code. All im trying to do is access an entity and draw it position.

//Todo: Comments need to be revised.
namespace HexSphereECS
{

    public class HexSphereBuildSystem : JobComponentSystem
    {
        #region FIELDS ---------------------------------------------------------

        public static NativeHashMap<int, Entity> tilesHashMap;

        [ReadOnly]
        EntityCommandBufferSystem ecbs;
        [ReadOnly]
        HexSphereBuildSettingsComponent hexSphereBuildSettingsManager;
        [ReadOnly]
        public static EntityArchetype tileArchetype;
        [ReadOnly]
        [DeallocateOnJobCompletion]
        NativeArray<Entity> entityArray;
        [ReadOnly]
        static float3[] icosahedronPosData =
        {
            new float3(0.723606f,0.0f,1.17082f),
            new float3(0.0f, 1.17082f, 0.723606f),
            new float3(-0.723606f, 0.0f, 1.17082f),
            new float3(0.0f, -1.17082f, 0.723606f),
            new float3(0.723606f, 0.0f, -1.17082f),
            new float3(0.0f, -1.17082f, -0.723606f),
            new float3(-0.723606f, 0.0f, -1.17082f),
            new float3(0.0f, 1.17082f, -0.723606f),
            new float3(1.017082f, -0.723606f, 0.0f),
            new float3(1.17082f, 0.723606f, 0.0f),
            new float3(-1.17082f, 0.723606f, 0.0f),
            new float3(-1.17082f, -0.723606f, 0.0f)
        };

        #endregion


        #region SYSTEM METHODS -------------------------------------------------

        /// <summary>
        /// Ons the create.
        /// </summary>
        protected override void OnCreate()
        {
            //
            // Deligate for  OnSceneLoade/OnSceneUnloaded methods
            // This is a workaround to make sure my HexSphereBuildSettingsComponent
            // exists before getting a reference to it.
            SceneManager.sceneLoaded += OnSceneLoaded;
            SceneManager.sceneUnloaded += OnSceneUnloaded;

            ecbs = EntityManager.World.GetOrCreateSystem<BeginSimulationEntityCommandBufferSystem>();
        }

        /// <summary>
        /// Ons the destroy manager.
        /// </summary>
        protected override void OnDestroyManager()
        {
            entityArray.Dispose();
        }

        #endregion


        #region SCENEMANAGER METHODS -------------------------------------------
        /// <summary>
        /// Ons the scene loaded.
        /// </summary>
        /// <param name="scene">Scene.</param>
        /// <param name="mode">Mode.</param>
        void OnSceneLoaded(Scene scene, LoadSceneMode mode)
        {
            hexSphereBuildSettingsManager = GetSingleton<HexSphereBuildSettingsComponent>();
            tileArchetype = EntityManager.CreateArchetype(typeof(TagTileEmpty), typeof(VertexComponent), typeof(VertexTrianglesComponent));
            entityArray = new NativeArray<Entity>(hexSphereBuildSettingsManager.hexesCount, Allocator.TempJob);
            EntityManager.CreateEntity(tileArchetype, entityArray);
            tilesHashMap = new NativeHashMap<int, Entity>(hexSphereBuildSettingsManager.hexesCount, Allocator.Persistent);
            // ToDo : only use for BuildIcosahedronJob_IJobForEach
            //entityArray.Dispose();
        }

        /// <summary>
        /// Ons the scene unloaded.
        /// </summary>
        /// <param name="current">Current.</param>
        private void OnSceneUnloaded(Scene current)
        {
            entityArray.Dispose();
            tilesHashMap.Dispose();
        }

        #endregion


        #region JOBS -----------------------------------------------------------

        ////[BurstCompile]
        //struct BuildIcosahedronJob_IJobForEach : IJobForEachWithEntity<TagTileEmpty>
        //{
        //    public EntityCommandBuffer ecb;
        //    public int i;

        //    public void Execute(Entity entity, int index, ref TagTileEmpty c0)
        //    {
        //        int min, max;

        //        HexSphereESCUtils.GetSubdivisionVertexRange(0, out min, out max);
        //        if (i > min && i < max || i == 0)
        //        {
        //            ecb.AddComponent<TagSubdivision00Component>( entity, new TagSubdivision00Component { });
        //        }

        //        HexSphereESCUtils.GetSubdivisionVertexRange(1, out min, out max);
        //        if (i > min && i < max)
        //        {
        //            ecb.AddComponent<TagSubdivision01Component>(entity, new TagSubdivision01Component { });
        //        }
        //        i++;
        //        ecb.RemoveComponent<TagTileEmpty>(entity);
        //    }
        //}

        /// <summary>
        /// Build icosahedron job IJ ob parallel for.
        /// </summary>
        //[BurstCompile]
        struct BuildIcosahedronJob_IJobParallelFor : IJobParallelFor
        {
            public EntityCommandBuffer.Concurrent ecb;
            [DeallocateOnJobCompletion]
            [ReadOnly]
            public NativeArray<Entity> entityArrayBuffer;
            [ReadOnly]
            [DeallocateOnJobCompletion]
            public NativeArray<float3> icosahedronPosBuffer;

            //TEST
            [NativeDisableParallelForRestriction]
            [WriteOnly]
            public NativeHashMap<int, Entity>.Concurrent hashMap;

            public void Execute(int i)
            {
                int min, max;
                Entity entity = entityArrayBuffer[i];

                HexSphereESCUtils.GetSubdivisionVertexRange(0, out min, out max);
                if (i > min && i < max || i == 0)
                {
                    ecb.AddComponent<TagSubdivision00Component>(i, entity, new TagSubdivision00Component { });
                    ecb.SetComponent<VertexComponent>(i, entity, new VertexComponent { tileIndex = i, position = icosahedronPosData[i] });
                    ecb.SetComponent<VertexTrianglesComponent>(i, entity, new VertexTrianglesComponent
                    {
                        connectedTriA = Entity.Null,
                        connectedTriB = Entity.Null,
                        connectedTriC = Entity.Null,
                        connectedTriD = Entity.Null,
                        connectedTriE = Entity.Null,
                        connectedTriF = Entity.Null
                    });

                    //TEST
                    hashMap.TryAdd(i, entity);
                }

                HexSphereESCUtils.GetSubdivisionVertexRange(1, out min, out max);
                if (i > min && i < max)
                {
                    ecb.AddComponent<TagSubdivision01Component>(i, entity, new TagSubdivision01Component { });
                    ecb.SetComponent<VertexComponent>(i, entity, new VertexComponent { tileIndex = i, position = new float3(0.0f, 0.0f, 0.0f)});
                    ecb.SetComponent<VertexTrianglesComponent>(i, entity, new VertexTrianglesComponent
                    {
                        connectedTriA = Entity.Null,
                        connectedTriB = Entity.Null,
                        connectedTriC = Entity.Null,
                        connectedTriD = Entity.Null,
                        connectedTriE = Entity.Null,
                        connectedTriF = Entity.Null
                    });

                    //TEST
                    hashMap.TryAdd(i, entity);
                }

            }
        }


        #endregion


        #region JOBS METHODS----------------------------------------------------

        protected override JobHandle OnUpdate(JobHandle inputDeps)
        {
            hexSphereBuildSettingsManager = GetSingleton<HexSphereBuildSettingsComponent>();


            //var jobForEach = new BuildIcosahedronJob_IJobForEach
            //{
            //    ecb = ecbs.CreateCommandBuffer(),
            //    i = 0

            //}.ScheduleSingle(this, inputDeps);
            //ecbs.AddJobHandleForProducer(inputDeps);

            //jobForEach.Complete();
            //return jobForEach;



            BuildIcosahedronJob_IJobParallelFor jobParallelFor = new BuildIcosahedronJob_IJobParallelFor()
            {
                ecb = ecbs.CreateCommandBuffer().ToConcurrent(),
                entityArrayBuffer = new NativeArray<Entity>(entityArray, Allocator.TempJob),
                icosahedronPosBuffer = new NativeArray<float3>(icosahedronPosData, Allocator.TempJob),
                hashMap = tilesHashMap.ToConcurrent()
            };

            inputDeps = jobParallelFor.Schedule(hexSphereBuildSettingsManager.hexesCount, 12, inputDeps);
            ecbs.AddJobHandleForProducer(inputDeps);
            inputDeps.Complete();
            //entityArray.Dispose();

            World.Active.GetExistingSystem<HexSphereBuildSystem>().Enabled = false;

            return inputDeps;


        }

        #endregion
    }
    public class HexSphereECSVisualizerSystem : ComponentSystem
    {
        EntityQuery queryVertexComponent;
        /*HexSphereVisualizerComponent _hexSphereVisualizerComponent;*/
        HexSphereECSVisualizer hexSphereECSVisualizer;

        protected override void OnCreate()
        {
            SceneManager.sceneLoaded += OnSceneLoaded;
        }


        void OnSceneLoaded(Scene scene, LoadSceneMode mode)
        {
            /*_hexSphereVisualizerComponent = GetSingleton<HexSphereVisualizerComponent>();*/

            hexSphereECSVisualizer = GameObject.FindGameObjectWithTag("tagHexSphereBuildManagerObj").GetComponent<HexSphereECSVisualizer>();
            if (hexSphereECSVisualizer == null)
                throw new InvalidOperationException("Could not get GameObject.FindGameObjectWithTag(\"tagHexSphereBuildManagerObj\").GetComponent<HexSphereECSVisualizer>();");
        }

        protected override void OnCreateManager()
        {
            queryVertexComponent = GetEntityQuery
                (
                    ComponentType.ReadOnly<VertexComponent>()//,
                    //ComponentType.ReadOnly<VertexTrianglesComponent>()
                );
        }


        protected override void OnUpdate(){ }


        public void OnDrawGizmos()
        {
            if (hexSphereECSVisualizer.vertexIndex == -1)
            {
                if (hexSphereECSVisualizer.drawVertex)
                {
                    Entities.With(queryVertexComponent).ForEach((ref VertexComponent v) =>
                    {
                        Gizmos.color = hexSphereECSVisualizer.vertexColor;
                    /*Gizmos.DrawSphere(new Vector3(v.position.x, v.position.y, v.position.z), _hexSphereVisualizerComponent.drawRaduis);*/
                        Gizmos.DrawSphere(new Vector3(v.position.x, v.position.y, v.position.z), hexSphereECSVisualizer.drawRaduis);
                    });
                }
            }
            else
            {
                //
                Entity e;
                HexSphereBuildSystem.tilesHashMap.TryGetValue(hexSphereECSVisualizer.vertexIndex, out e);
                //EntityManager.GetComponentData<VertexComponent>(e).tileIndex;
                Debug.Log(e.Index + " e ");
                Debug.Log(EntityManager.GetComponentData<VertexComponent>(e).position);
                // Will draw sphere from position
            }
        }
    }

Elinor Defense looks cool btw

Im not sure it good pratice to access my data via entity through a NativeHashMap. With my limited knowlage Im not sure what the best way would be to access this data. Eg wanting to debugDraw some stuff in HexSphereECSVisualizerSystem classs

public static NativeHashMap<int, Entity> tilesHashMap;
       public void OnDrawGizmos()
        {
            if (hexSphereECSVisualizer.vertexIndex == -1)
            {
                if (hexSphereECSVisualizer.drawVertex)
                {
                    Entities.With(queryVertexComponent).ForEach((ref VertexComponent v) =>
                    {
                        Gizmos.color = hexSphereECSVisualizer.vertexColor;
                    /*Gizmos.DrawSphere(new Vector3(v.position.x, v.position.y, v.position.z), _hexSphereVisualizerComponent.drawRaduis);*/
                        Gizmos.DrawSphere(new Vector3(v.position.x, v.position.y, v.position.z), hexSphereECSVisualizer.drawRaduis);
                    });
                }
            }
            else
            {
                //
                Entity e;
                HexSphereBuildSystem.tilesHashMap.TryGetValue(hexSphereECSVisualizer.vertexIndex, out e);
                //EntityManager.GetComponentData<VertexComponent>(e).tileIndex;
                Debug.Log(e.Index + " e ");
                Debug.Log(EntityManager.GetComponentData<VertexComponent>(e).position);
                // Will draw sphere from position
            }
        }

Reading through this thread is blobData the correct way to handle my problem?

Mmm I’m not sure if blobData is the way to go here , anyone have any thought on this before I commit 100+ hours on this