Complex Shared Data Between Systems

I’m trying to come to grips with how to share complex data structures between systems in a DOTS/ECS codebase. I’m working on a project with a voxel style world where terrain can be edited (think 7 Days / Minecraft / etc), and one of the obvious optimizations I’m trying to get going early is culling faces on chunk boundaries that aren’t required. There’s obviously other times this is going to be necessary, such as changing the value of a block when mining occurs. Performance is important for this, as my plan is to have many types of structures that will rapidly change the landscape, making the efficient manipulation of chunk contents very important.

Are there any established patterns for how to do this? The data structure is relatively simple for each chunk, but there’s obviously a more complex set of data about things like what material is in a given location.

I’ve gone through all of the official unity samples as extensively as I can, but it seems like they’ve just not attempted to build this sort of thing?

You can create a static nativearray of blobasset data “complex blitable data” and pass this array to your systems. make sure to capture the nativearray in a local variable and set it to read only in your systems, if you are writing to it set it to disableParallelForRestriction but it’s your responsibility to prevent race conditions.

I started poking at my voxel game again and that’s more or less what I’m doing. I have a NativeHashMap<int3,Entity> where the key is the ChunkIndex for a chunk of blocks. I pass it around to systems/jobs and they can read/write to it at will using BufferFromEntity. The big downside of course is you need to manually manage the job handles for any job that touches the container. It’s annoying but it works.

3 Likes

Are you talking about having each chunk know the voxel information from neighboring chunks just to avoid creating unneeded faces on chunk edges?

First, is it really that important to do? I’ve seen some recent comments that it is not really worth while.

Second, my approach is that each voxel chunk includes some shared border voxels. So if it is a 16 cubed chunk in mesh, the data is actually 17 cubed. That means each chunk can independently mesh or not mesh edge faces, and for smooth shading, it is also much easier to deal with normals this way. I’ve tried the approach of having chunks talk or borrow voxels, its just not worth it. It’s very efficient for each chunk to be completely independent of its neighbors.
For editing, each chunk has a “parent” entity that knows what chunk entity is associated with what world position. So if I’m editing in chunk A, and the edit is going to affect something on the edge, the chunk can just look up what other chunk should be edited, and pass the edit information along. I haven’t got the editing part working in ECS yet, but I’ve it before in mono-behavior, and its not a bad approach, and it really doesn’t require sharing of complex data.

I mean in my case I might have 32x32x32 chunks in render range, and if I’m computing unnecessary faces on that, we could be talking a LOT of verts and tris that don’t need to exist. I can’t believe that would ever be an optimization that’s “not worth it”?

Sounds like an empirical question to be honest. I’m just putting it out there because I have heard it debated. Within-chunk face culling, yes, absolutely necessary.