When I started working on this project nearly a year ago, the frame rate rarely stayed consistent as the player moved around the world. The engine was simply allocating and discarding too much memory, resulting in frequent pauses in gameplay as the interpreter ran garbage collection. As I re-wrote more and more of the game engine, I came to understand exactly which types of objects and arrays I needed to allocate, where I could cut down on allocations, and where I might use an “object pool” to stash allocated objects for re-use. The old engine used typedarray-pool, but I’ve decided to create my own object-pool.
The object-pool is used very heavily during the meshing process, which converts a 3d object into points (x/y/z coordinates) and faces for the GPU. The voxeling engine creates a mesh for each world chunk, and tries to create an optimal mesh by combining adjacent cube faces that share the same texture (like two dirt blocks that are next to each other). As the meshing logic loops over the 32768 cubes within each world chunk, we push the resulting coordinates into a fixed-size Float32Array. Once we exhaust that array’s available space, we must to allocate a larger array and copy the existing data into it. I’ve created a growable object to take care of this task. The mesher creates an initial float32 growable of size 256. Once it is filled, the growable copies the original data into a float32 of 512 elements and puts the old 256 element array into the object pool for future use. The growable always reallocates at twice the size of the current array, which is a best-practice in Computer Science.
Using an object-pool and a growable helps the voxeling engine to maintain a consistent frame rate by preventing garbage collection from being a source of performance hiccups. I ran a CPU profile within Chrome recently and garbage collection only accounted for 0.37% of CPU time. Success!