Okay so I have been trying to solve this issue for a very long time. Basically, I have a task that I want perform, but it requires that one job knows the length of a NativeHashMap before it is scheduled.
Here is a synopsis of what I am doing:
Basically, I have an entire world of sections of voxel terrain. Each section is called a “chunk” (confusing in ECS context, but that is what it is called in voxel context, so imma still use this terminology). For each chunk, I do the following to create a mesh for it:
- Generate voxel data for the “chunk” using a compute shader inside of a component system, and populate the dynamic buffer attached to the entity with this data
After this everything falls to shambles. Ideally, this is how it would work after the data generation:
- Find all active edges and voxels within the chunk, and add them to a nativehashmap with their own unique codes
However, the next step requires that the length of this nativehashmap is known so that a vertexBuffer for a mesh can be filled. Therefore, it creates a super annoying sync point. So I honestly have no idea what to do for this situation.
-
Find all active edges and voxels within the chunk, and add them to a nativehashmap with their own unique codes
-
Visit each active voxel, and create a vertex that will inserted into the vertex buffer <— huge annoying sync point required for this one
-
Create a triangle order, this also required that the length of the hashmap is known
On top of all of these issues, I have no way of deallocating the nativehashmaps without adding anther sync point since they cant be deallocated with the tag.
AND, allocating these hashmaps is painfully slow. At the most, a chunk can contain 3 times the amount of voxels it contains. That means that the length of the hashmap must be SizeX * SizeY * SizeZ * 3. Allocating this takes a painful amount of time in the editor.
So in total, here are my 3 issues (along with some possible solution that also aren’t very ideal):
- Solving the issue of needing to know the size of a nativehashmap before running the next step (job)
- Perhaps attempt to add the vertices that comprise the mesh to another hashmap?
- Somehow saving hashmaps for another job system, or deallocating on the fly somehow
- I guess you can save hashmaps for another job system by adding a SharedComponent that contains these hashmaps to your entity. However, this just adds more possible sync points (since the first job would have to finish before reading from the hashmap in the next step)
- Super slow allocation time
- TBH I have no idea what the hell I am doing at this point LOL. Plz help.
Here is my JobComponentSystem code so far for the first step. I still don’t really know if what I am doing is correct. Please help me correct any mistakes.
Thanks guys!!
using Unity.Entities;
using Unity.Jobs;
using Unity.Collections;
using UnityEngine;
using static Unity.Mathematics.math;
using Unity.Mathematics;
public class ChunkMeshingSystem : JobComponentSystem
{
EntityQuery chunksQuery; //and entity query that will grab all the chunks currently in the world
NativeArray<Entity> chunks;
Entity chunk;
NativeHashMap<uint, Edge> activeEdges;
NativeHashMap<uint, uint> activeVoxels;
NativeArray<int4> AXIS_OFFSET;
NativeArray<int4> EDGE_NODE_OFFSETS;
static int4[] AXIS_OFFSET_ARRAY =
{
int4(1, 0, 0, 0),
int4(0, 1, 0, 0),
int4(0, 0, 1, 0)
};
static int4[] EDGE_NODE_OFFSETS_ARRAY =
{
int4(0), int4(0, 0, 1, 0), int4(0, 1, 0, 0), int4(0, 1, 1, 0),
int4(0), int4(1, 0, 0, 0), int4(0, 0, 1, 0), int4(1, 0, 1, 0),
int4(0), int4(0, 1, 0, 0), int4(1, 0, 0, 0), int4(1, 1, 0, 0),
};
protected override void OnCreate()
{
chunksQuery = World.Active.EntityManager.CreateEntityQuery(typeof(NeedsMeshing)); //initialize the chunk query to grab everything with "ChunkInfo" on it
}
protected override JobHandle OnUpdate(JobHandle inputDeps)
{
chunks = chunksQuery.ToEntityArray(Allocator.TempJob);
if(chunks.Length == 0)
{
chunks.Dispose();
return inputDeps;
}
chunk = chunks[0];
chunks.Dispose();
ChunkInfo chunkInfo = EntityManager.GetComponentData<ChunkInfo>(chunk);
ChunkMeshingInfo chunkMeshingInfo = new ChunkMeshingInfo
{
activeEdges = new NativeHashMap<uint, Edge>((chunkInfo.size.x) * (chunkInfo.size.y) * (chunkInfo.size.z) * 3, Allocator.TempJob),
activeVoxels = new NativeHashMap<uint, uint>((chunkInfo.size.x) * (chunkInfo.size.y) * (chunkInfo.size.z), Allocator.TempJob),
};
EntityManager.AddSharedComponentData(chunk, chunkMeshingInfo);
AXIS_OFFSET = new NativeArray<int4>(3, Allocator.TempJob);
AXIS_OFFSET.CopyFrom(AXIS_OFFSET_ARRAY);
EDGE_NODE_OFFSETS = new NativeArray<int4>(12, Allocator.TempJob);
EDGE_NODE_OFFSETS.CopyFrom(EDGE_NODE_OFFSETS_ARRAY);
var FindActiveVoxels = new FindActiveVoxelsAndEdgesJobs() {
activeEdges = EntityManager.GetSharedComponentData<ChunkMeshingInfo>(chunk).activeEdges,
activeVoxels = EntityManager.GetSharedComponentData<ChunkMeshingInfo>(chunk).activeVoxels,
AXIS_OFFSET = AXIS_OFFSET,
EDGE_NODE_OFFSETS = EDGE_NODE_OFFSETS,
voxelData = EntityManager.GetBuffer<VoxelData>(chunk).Reinterpret<float>().ToNativeArray(Allocator.TempJob),
SizeX = chunkInfo.size.x,
SizeY = chunkInfo.size.y,
};
EntityManager.RemoveComponent<NeedsMeshing>(chunk);
return FindActiveVoxels.Schedule(chunkInfo.size.x * chunkInfo.size.y * chunkInfo.size.z,1, inputDeps);
}
}