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

Should I Use Entities or Other Means For Memory Management?

Discussion in 'Entity Component System' started by Filter_Feeder, Jul 13, 2021.

  1. Filter_Feeder

    Filter_Feeder

    Joined:
    Oct 19, 2017
    Posts:
    45
    I'm working on a voxel-based geo-simulation projectwhere I am trying to create a huge world with dynamic geological processes like erosion, tectonic movements etc. I have used the ECS system to create the most basic fundamental calculations already (runs fast as hell on smaller worlds), but testing with a grid of 20^3 voxels, up to 6GB of memory is already being used by the system.

    To clarify, every voxel in the world is an entity.

    My question - and I'm sorry if this seems lazy for me to simly ask and not do the research on myself, but I'm not a computer engineer but just a simple hobbyist - is if ECS is really the path to take in such a project. As my project revolves around geological simulation, it really doesn't need to be very fast (I'm not going to do real-time waves or fast moving stuff like "from dust"). It needs however to be huge, ginormous in fact, and I'm guessing there are systems out there for better handling of large datasets.

    The voxels themselves don't have to be very heavy on memory. They basically just contain a dussin of floating-point values, which could be reduced down to int16 or even 8-bit values. Regardless, I'm guessing it's going to be big enough that I will probably have to stream data back-and-forth to disc and not actually have everything loaded up on the memory.

    I would be very helpful if someone more exeperienced could comment on this, and possbly give me some pointers as to where to start digging for more information. Thanks!
     
  2. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,753
    > To clarify, every voxel in the world is an entity.

    This is not sustainable at any large voxel count. You have at the very minimum 8 bytes overhead per entity (it's more in practice). If all your voxel data was is 1 float or int (4 bytes), that is 3x the memory usage over just using an array!

    You have a couple of options.

    - Each chunk should be an entity and store the voxels on a dynamic buffer
    - Just write the voxel world as a separate standalone simulation that the entity world can interact with
     
    JesOb likes this.
  3. Krajca

    Krajca

    Joined:
    May 6, 2014
    Posts:
    347
    This is probably the way to go for now as you have few advantages here because while using mono you can still:
    • have data in NativeArrays for compatibility with jobs
    • have jobs so there is minimal overhead for registering them
    • you still can interact with it (as was said) from the ECS environment
    It's surprising that ECS still has no good support for simple arrays. I know there are dynamic buffers and blob assets, but in my opinion, these are too clunky for use in situations when you only want a simple NativeArray.
    For me, there is a huge gap between mono+jobs and ECS. Don't get me wrong, it's a gap from both sides. Mono+jobs are much simpler to use but ECS has that connectivity of data between systems.
     
    eatbuckshot likes this.
  4. WAYNGames

    WAYNGames

    Joined:
    Mar 16, 2019
    Posts:
    988
    My understanding is that unsafe container are "simply" pointers to memory locations.
    It is possible to have such container in an IComponentData.

    Question is would that be considered safe ?
    If it's accessed only through that component in systems that properly declare read and writes to the IComponentData. Wouldn't the dependency graph avoid reading and writing to the component (and by extension the container) in parallel. And since it's allocated for each entity, whatever the job is there should be no chance of a race condition.

    I feel like I'm missing something obvious because otherwise the dynamic buffer kind of seem pointless...
     
  5. Micz84

    Micz84

    Joined:
    Jul 21, 2012
    Posts:
    447
    There are two things I can think of. Firstly, safety. It would not be as safe as you think. As you said it is just a pointer (long address). So nothing prevents you from declaring it as read only access but data and write to it. Unity can't track that. Of course if you are aware of that you can avoid issues. Second difference to the domain buffer, is way off storage. Dynamic buffers are stored in the chunk to declared capacity, so it can be faster to access.
     
    WAYNGames likes this.
  6. WAYNGames

    WAYNGames

    Joined:
    Mar 16, 2019
    Posts:
    988
    That's the obvious part I overlooked. It's safe as long as you trust the developer to respect the read only declaration, it's not checked by the safety system (as there is none...).
     
  7. varnon

    varnon

    Joined:
    Jan 14, 2017
    Posts:
    52
    I'll second the other thoughts here. Voxels as entities is not a good idea long-term. I use the Entity as voxel chunk method, with voxel data stored in a dynamic buffer. Use the [InternalBufferCapacity(N)] attribute to set how many voxel elements a buffer can and still be stored on an ECS chunk. Set it to 0 to force the data to always be stored outside the ECS chunk.


    Is your main issue the lack of the ability to directly use a dynamic buffer in a job? I would like to understand your frustration more.
     
  8. Krajca

    Krajca

    Joined:
    May 6, 2014
    Posts:
    347
    DynamicBuffer is more like a list. And it's not like it's not usable but it has its caveats.
    Firstly as far as I know DynamicBuffer shouldn't be large, and with games like i.e. rimworld where you have tilemaps, a 256x265 map is 65k tiles large. Of course, I can divide it into chunks and so on but then I need to recreate the map later and some algorithms need rewriting in that case. Then there is that usability in jobs that you have mentioned. I need to stick to ForEach or convert them every time. Maybe there is a workaround for this but I don't know any.
    In my case is just simpler and easier to have a simple array somewhere. As far as I know, a NativeArray should be placed linearly in memory so there shouldn't be much of a performance hit for getting it to the job struct. So yeah, I'm using mono with arrays as storage and a system with jobs for compatibility with the rest.
    The one downside of this is that dependency must be maintained manually for this data. Or this is as I understand it for now anyway. Looking for a way to make one system dependant on another completeness not just order of execution.
    Blobs are more for static data than something like a game map.
     
  9. sngdan

    sngdan

    Joined:
    Feb 7, 2014
    Posts:
    1,154
    It sounds like there are some misconceptions around dynamic buffers here. They can be large (they will just not be in chunk memory but access wise be on par with native array), they can be used in jobs.
     
  10. Krajca

    Krajca

    Joined:
    May 6, 2014
    Posts:
    347
    And my few tests showed me that using them in the jobs was limited at best as you need to get them from the entity manager. Only ForEach allowed them to be used in a burstable and parallel way.

    So what are the real limitations of DynamicBuffers? What do you mean they will not be in chunk memory as I thought they weren't in the first place because of their dynamic properties?
     
  11. sngdan

    sngdan

    Joined:
    Feb 7, 2014
    Posts:
    1,154
    I have not used ECS in a while (about 2 years) so I am not familiar with the current API but I follow the forum and as far as I can tell dynamic buffers have not changed.

    best to read up on them in the manual and check some forum example (I have used them extensively for spatial partitioning and rendering and posted examples as far as I can recall)

    If their capacity (you can set it) allows them to reside in a chunk they behave like component data, otherwise they allocate in heap (if you know they will not fit in the chunk you can set their capacity to 0 and they will directly go on the heap)

    They can grow in size like a list (single threaded) but otherwise can be viewed as an array -> .asarray() is cheap and let’s you use them like an array.

    they clean fast -> set length to 0

    they work single threaded Only but if you can ensure thread safety access you can operate on them multi threaded (allow parallel access to the array)
     
    Krajca likes this.
  12. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,683
    That's wrong.
    BufferFromEntity
    exactly for that purpose. You can use them in
    ForEach
    and in
    regular parallel jobs
    through
    BufferFromEntity
    without problems.
    When you declare
    IBufferElementData
    and set
    InternalBufferCapacity
    attribute it will set the number of elements in a buffer which will be stored in chunks and total buffer size (with buffer headers) can't be larger than chunks size (that at least one entity fits chunk) when you set this attribute (this is why you'll see an error if will try to set big number or will have a big struct, that is not fit chunk size - 16k),
    upload_2021-7-14_17-43-56.png

    but if you declared some proper size and then will add to buffer more items than you set in attribute - it will be moved to the heap from chunk memory.
    upload_2021-7-14_17-38-19.png
     
    Last edited: Jul 14, 2021
    Krajca likes this.
  13. Krajca

    Krajca

    Joined:
    May 6, 2014
    Posts:
    347
    Thanks for clearing that up! And sorry for spreading false information.
     
  14. Filter_Feeder

    Filter_Feeder

    Joined:
    Oct 19, 2017
    Posts:
    45
    Thanks! So each entity has an IComponentData with a dynamic buffer then? What is the reason for using a dynamic buffer and not a native array?
     
  15. Krajca

    Krajca

    Joined:
    May 6, 2014
    Posts:
    347
    You can't use a native array in the component so you need a dynamic buffer to associate array-like data with an entity.
     
  16. Filter_Feeder

    Filter_Feeder

    Joined:
    Oct 19, 2017
    Posts:
    45
    Thanks!
    Some of this discussion wen't a bit over my head but I think I have some new directions because of it, I appreciate it a lot!

    However, I have been pondering my problem some more and I think that regardless of using entities as voxel chunks or not, I think I will have to run data back and forth to the disk. I have been reading a bit about using C# to make HDF5 files, but I didn't feel like I hit the jackpot. Does anyone have any pointers for me? Cheers!