Jump to content

DynamicFloatArray to the rescue for efficient Instanced Array!


Recommended Posts

Ok folks, this is the last quick post I have for you (I think)...

The name of the class is well...I had to find a name, that's all...

The problematic: to render efficiently we have to minimize the draw calls as much as possible, so when it's possible we have to rely on the WebGL Instanced Array extension. Basically, this extension is used to create an Array Buffer that will contains all the settings specific to each instance of the same object you want to render multiple time. Say hundreds, thousands of Rounded Rectangles in the Canvas2D, drawn in one call for instance!

But how to feed the beast (the array buffer) ? Especially if the objects you send are subject to things like culling, clipping, visibility change: which make the whole list change frequently!

The solution: A class designed to allocate/free elements, storing them into a Float32Array (because believe me: size does matter, and 32bits per number is ok for us!) and providing a method to defragment its content in order to update the Instanced Array buffer without worries.

Well, ok, you've guessed, that's what DynamicFloatArray is about!

When you create an instance of this class you specify the size of one element (the size is in floats, NOT in bytes): this is called the Stride. So if you need to store 4 Vector4 for a transformation matrix and one Color4 for the color of you object, the stride will be 4*4 + 1 = 17! You also specify the initial count of elements the Float32Array can contains without being resized (and it will, after this count is reached, by 50%, I know it may looks a lot, but resizing in not my friend!)...

Then it's pretty simple: when you need to store one new element, you call allocElement(), it returns an instance of the DynamicFloatArrayElementInfo class (I know the name couldn't have been longer) which contains the offset into the Float32Array of where your element first float is located. It's up to you to be a big nice boy and copy the data accordingly, otherwise face the consequences!

If you don't need this element anymore, just call freeElement() giving the corresponding DynamicFloatArrayElementInfo object.

Now, what is the deal you might ask? When you call freeElement() it creates "holes" in your Float32Array, holes that you can't send to the Instanced Array buffer without facing another kind of very bad consequence!

So all you have to do thanks to this class, is to call the pack() method (which I spent 3 hours to write, true story, that long...) which will defrag the Float32Array, removing the holes by moving valid elements to fill them (the holes!). Don't fear to call this method, if nothing is fragmented it will return in no time, if the content if full of holes, well, I spent 3 hours to make sure it defrags it very quickly! Calling pack() returns what is called a subArray, this is NOT a copy (I hate copies, like resizing, a little less though), but a view of your dear unfragmented Float32Array, ready to feed the Instanced Array Buffer!

So to sum up: at each render when you use an Instanced Array, call pack() and update the Array Buffer that you just bind. (sadly, it's still up to you to detect if there was changes and if the update is needed or not, but it shouldn't be a big deal for you to handle)


As the smartest of you may have noticed, calling pack() will move some elements, which means their position in the Float32Array will change! That is why I return an instance of the DynamicFloatArrayElementInfo class (which only contain a single property: the offset) and not a single number I wouldn't be able to update for you! When an element is moved, its offset in the DynamicFloatArrayElementInfo object is updated accordingly and it's your responsibility to get this offset every time you are going to update a given element.


Well, I forgot to say the DynamicFloatArray will grow when needed...but will never shrink, sadly...a such feature could be open for debate.

If you plan to use Instanced Array, I believe this will a great help to you as it was for me when developing the Canvas2D. So far the class looks pretty reliable, but I have to admit: the pack() method...is not the easiest thing to code! (efficiently, for me at least!)

Oh, by the way, it might interest you, but now the methods Engine.updateAndBindInstancesBuffer() and Engine.unBindInstancesBuffer() are able to update any kind of Instanced Array, useful! :) All you have to do is using the offsetLocation argument as a InstancingAttributeInfo[] which contains everything to wire the Array correctly !

Have fun !

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


  • Recently Browsing   0 members

    • No registered users viewing this page.
  • Create New...