Nockawa

DynamicFloatArray to the rescue for efficient Instanced Array!

3 posts in this topic

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)

Beware!

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.

Conclusion

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 !

Dal, NasimiAsl, Deltakosh and 2 others like this

Share this post


Link to post
Share on other sites

greatissimo !

but how could you invent things to optimize the use of  webgl and in the same time choose so longVerboseAndComplicatedNames for your functions ? :D

just kidding

excellent tool !

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now

  • Recently Browsing   0 members

    No registered users viewing this page.