>tfw you spend all day writing code and you have nothing to show for it but a few console lines.
Basically, my simulation is too complicated for its own good and the only way to make it efficient is to parallel thread it and to do that you have to learn and abide by a few programming practices which are remarkably different than anything I know and also very hard to understand, conceptualize, and implement correctly.
My grid might by 256^2 with a depth of 3 so that's about 196,608 'Orts' (I needed a word for 'bit of air' and someone suggested the german word for 'Spot'). Unlike most voxel games, this does not represent the ground but instead the air. An Ort is a cubic meter or kiloliter of air, but as those are both units and I need a name, I went with Ort.
The question is, if you have 196,608 Orts and then you have a bunch of chemicals that can be in the air, how do you efficiently handle that? Well, you don't, probably. A lot of the issue with parallelism and something at this scale is you have to be able to presuppose a lot. The more abstraction (classes / entities) you have, the worse it gets. The more dynamic things like arrays are, the worse it gets.
If I want to represent the chemical composition of an Ort, I need another struct that tells me which chemical it has, how much of it, and at what temperature. Well, I probably have more than one (air by itself is Oxygen and Nitrogen so having more than one is the default, not the exception) so I need to be able to accommodate between zero and the total number of simulated chemicals in the game per Ort.
The Chemical is a struct that is about 64 bytes in size. That's a big boy. So I have to somehow keep track of 196.608 Orts that each have 64 bytes of data attached to them n times. So for just a 256 grid with breathing air, that's 25MB of memory which has to be managed. Managing this at a high level with garbage collection is going to be very slow, so I'm writing pretty low level code and I'm going to have to implement custom mesh generation because without the entity system Unity's hybrid renderer can't make meshes for me.
To solve a lot of my problems, I've started relying heavily on what I call Mortons: the serialized value of a coordinate. I've moved a lot of shit to BlobArrays. These are immutable data sets which are single-dimensioned, but can be accessed serially and, as a benefit, can be read parallel - so putting Chemical data in one does not stop 196,608 Orts from reading it together.
Problem: You cannot store a reference to a BlobArray in another BlobArray, and you cannot edit them. So the majority of data - the actual Chemical compositions that exist in the Orts - cannot be in one.
Answer: Native Streams.
Old struct (buffer elements which belong to entities)
New native streams (arrays in a system)
The native streams are built out knowing 1) how big the grid is, 2) how many chemicals are in the simulation. Say you have a 3x3 grid that's 3 Orts tall (27 Orts) and you have [0]Nitrogen, [1]Oxygen, and [2]Chlorine Gas. I populate the grid, create and store the chemicals, and then pass that data to the Ort system. Those two native streams should look like this:
Matter
[0] 11.472f
[1] 4.704f
[2] 0f
[3] 11.472f
...
[79] 0f
[80] 0f
Heat
[0] 293.15f
[1] 293.15f
[2] 0f
[3] 293.15f
...
[79] 293.15f
[80] 0f
This is the same data as having 26 of the structs, but this can be accessed in parallel and with as little data as possible.
One thing the struct contains that the array does not is a reference to the chemical itself. This is not needed because the chemical id can be obtained deterministically from the index of the array.
ort count = 27, chemical count = 3, so the arrays are 81 items long (0 index).
ort = i / 3
chemical = i % 27
Thus, the 5 bytes per chemical composition to identify the chemical have been completely eliminated and now the data can be ripped open on a 64 core system without issue.
Tomorrow, diffusion!
Actually, I stream tomorrow. SATURDAY!