Best practive for dynamic vertex buffer updates in WebGL

4,027 views
Skip to first unread message

Andre Weissflog

unread,
Jul 11, 2014, 4:14:12 PM7/11/14
to webgl-d...@googlegroups.com
Hi,

I'm wondering what's the best (fastest) way to update big chunks of dynamic vertex data in WebGL, given the differences to GLES2 and desktop GL (more validation, GL renderer living in different process in the case of Chrome, etc...).

My use case is quite simple: have a single big vertex buffer which holds per-instance data for ANGLE_instanced_arrays rendering, which is updated once per frame (with varying number of instances).

What I'm currently doing is buffer orphaning, basically "unlinking" the previous buffer which might still be in flight, and then writing new data hoping that this will effectively result in buffer renaming, and no expensive allocation.

So basically once per frame in pseudo code:

- update instance positions with CPU in a normal memory chunk
- bindBuffer(ARRAY_BUFFER, vb)
- bufferData(ARRAY_BUFFER, vbSize, STREAM_DRAW); // orphan previous data 
- bufferSubData(ARRAY_BUFFER, 0, data); // write into (hopefully renamed) new buffer

In GL this would be:
- glBindBuffer(GL_ARRAY_BUFFER, vb);
- glBufferData(GL_ARRAY_BUFFER, vbSize, NULL, GL_STREAM_DRAW);
- glBufferSubData(GL_ARRAY_BUFFER, 0, numBytes, data);

Here's a demo which uses this pattern (requires ANGLE_instanced_arrays):


Depending on browser and platform, this begins to stutter (in Chrome on OSX with IntelHD4000) at around 40k..50k instances (16 byte per instance in instance vertex buffer, so ~800kByte to 1MB), but on a beefy Windows machine with nVidia 600-something it goes to over 600k instances and while the frame rate drops to 30fps (because of the CPU code updating the vertices) there's no stuttering like on OSX.

So my question is basically: is buffer orphaning supposed to work on WebGL (in the sense that behind the scenes, chunks of buffer storage would be rotated/renamed instead of freed/reallocated), and is this the recommended way to update big chunks of dynamic vertex data once per frame?

Thanks!
-Floh.

Jeff Dash

unread,
Jul 11, 2014, 4:39:09 PM7/11/14
to webgl-d...@googlegroups.com

Is bufferData(null) known to do anything special? I would think the implementation would treat this as a no op if the size doesn't need to change. I would think there should be a big difference between bufferData(null)+bufferSubData(data) and bufferData(data). In fact, at least in Firefox, bufferData(null) causes us to upload a calloc'd buffer of that size, so perf should be strictly worse.

--
You received this message because you are subscribed to the Google Groups "WebGL Dev List" group.
To unsubscribe from this group and stop receiving emails from it, send an email to webgl-dev-lis...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Kenneth Russell

unread,
Jul 11, 2014, 4:56:57 PM7/11/14
to webgl-d...@googlegroups.com
WebGL's bufferData(target, size, usage) call is guaranteed to
initialize the buffer to 0, so it can't be a no-op.

Internally, if a call to bufferSubData is going to reinitialize the
whole buffer's contents, Chrome replaces it with a call to
glBufferData depending on whether, per-platform, glBufferData or
glBufferSubData were measured as being faster for that use case. Since
the browser doesn't provide the same low-level control as on the
desktop, I would recommend just keeping your code simple and not doing
the bufferData(...) call attempting to abandon the buffer. You might
consider keeping around two buffers of the same size and alternating
which one you draw with and which one you populate if it looks like
that will provide better performance. If not, then again, I recommend
keeping your code simple.

Floh, by the way, cool demo. It would be great if you could turn this
into some sort of benchmark, where it would automatically measure
whether it's stuttering or not, and ramp up more quickly to the
maximum number of instances the current browser can support. That
would help browsers compete on the efficiency of their WebGL
implementations (and JavaScript VMs, graphics subsystems, etc.).

-Ken

Andre Weissflog

unread,
Jul 11, 2014, 5:56:11 PM7/11/14
to webgl-d...@googlegroups.com
Thanks for the valuable info! I didn't think of the set-to-zero. I think pre-allocating 2 buffers and alternating between them is the better idea then, I'll try this out next.

As far as I understood the orphaning pattern on desktop GL uses the special behaviour of calling glBufferData with a null data ptr causing the previous content of the buffer to be "unlinked" from the buffer object, the previous content stays alive if currently in use, until consumed by the GPU, and then this memory chunk is put back into some free queue to be recycled. Of course the GL spec doesn't say anything about this but as far as I understood this is common practice in native GL implementations.

This way you can do a call to glBufferData with nullptr followed by a glBufferData with data pointer to prevent a sync-lock. Just calling glBufferData with data ptr repeatedly on the same vertex buffer would have to wait until the buffer is no longer in use by the GPU.

I'm using BufferSubData because the data I'm writing may be much smaller (if few instances are rendered) then the capacity of the buffer, but changing the capacity would break the recycling.

But since WebGL has to clear the content to zero anyway it's probably better to pre-allocate several buffers and do the rotation myself.

Ken, my plan is to build a good number of simple demos which test specific features (and also performance). One motivation for this (and the engine behind it) was that it is difficult to isolate specific problems in test cases when working in a big production 3D engine. Hopefully smaller demos like this help the browser teams to improve their WebGL implementation and JS engines, even if they often don't represent real-world workloads.

There's no proper benchmarking yet, but I definitely plan to improve this.

The parent page (http://floooh.github.io/oryol/) has more demos and will grow over time, the engine itself is here: https://github.com/floooh

Cheers,
-Floh.

Andre Weissflog

unread,
Jul 11, 2014, 5:59:54 PM7/11/14
to webgl-d...@googlegroups.com
PS: Correct link to engine: https://github.com/floooh/oryol

Travis Gesslein

unread,
Jun 14, 2016, 3:12:08 PM6/14/16
to WebGL Dev List
Sorry for the bump, but did you come to any conclusions about what to do?

I'm having the same problem, if you can call it that. I do sprite (i.e. simple quad vertex data) batching and need to dynamically update a ton of different buffers every frame. It's one of the bottlenecks of our engine.

Regards,
Travis
Reply all
Reply to author
Forward
0 new messages