3
\$\begingroup\$

I've started learning WebGL, currently I'm building a 2D lighting system, but there is some confusion going on inside my head.

How the lighting works is based on this tutorial http://archive.gamedev.net/archive/reference/programming/features/2dsoftshadow/default.html (probably the most linked article of the genre).

My question is about the proper way to store/create/update the Vertex Buffer Objects of the polygons.

Currently I have something like:

//-- during initialization of each polygon
//build vertices from the provided points
//[...]
//create the VBO
this._VBO = gl.createBuffer();
gl.bindBuffer( gl.ARRAY_BUFFER, this._VBO );
gl.bufferData( gl.ARRAY_BUFFER, vertices, gl.DYNAMIC_DRAW );

//-- during rendering of each polygon
gl.bindBuffer( gl.ARRAY_BUFFER, this._VBO );
gl.enableVertexAttribArray( program.aVertexPosLoc );
gl.vertexAttribPointer( program.aVertexPosLoc, 3, gl.FLOAT, false, 0, 0 );

The vertices array is created only one time at the top of the file, and reused for each new polygon, the motive to do that is to avoid the creation of various Float32Arrays per object.

//top of file
var vertices = new Float32Array( 20 * 3 ); //maximum of 20 points

It's okay to have one VBO for each object? I was thinking about the possibility of a single VBO, but I don't understand how I can make it work with only one. The number of vertices can vary between one polygon and another, so how I'm going to store that?

Currently I'm not taking into consideration textures, however, answers that already take they into account are welcome.

\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

One VBO per polygon, and updating it at runtime as you seem to be doing, is going to perform horribly. You'd be better off with old-school client-side arrays or even (if you weren't using ES) immediate mode. VBOs just aren't designed for good performance with that kind of usage pattern.

If the data is absolutely static then stuff it all into a single big VBO, using the parameters of your glDrawArrays call to specify the range of the VBO to draw for each polygon. In this scenario the VBO is never updated, and yes, uses more memory - but memory isn't everything. This is a fair tradeoff of memory for performance.

If the data needs to be dynamic then look at a streaming buffer pattern. Unfortunately ES2 doesn't have glMapBufferRange so you can't do this reasonably; either stuff it all into a system memory copy, then glBufferSubData it (your render would be in two passes then - one to build the data, the other to draw it - making sure that you're using a single big glBufferSubData call to update the buffer rather than lots of small ones) or go back to client-side arrays and let the driver do the streaming for you.

\$\endgroup\$
5
  • \$\begingroup\$ "so you can't do this reasonably" Nonsense. People have been doing buffer streaming since before MapBufferRange's GL_INVALIDATE_BIT and so forth. You just have to do it a different way. \$\endgroup\$ Commented Apr 1, 2013 at 10:36
  • \$\begingroup\$ I'm not talking about invalidate (for which you'd use glBufferData with NULL) I'm talking about unsynchronized and the classic append/append/append/orphan pattern. Invalidate is only relevant for the orphaning part of this; for everything else you have to deal with synchronization issues. See posts by Rob Barris at opengl.org/discussion_boards/showthread.php/… - especially the quote "allow for idioms where the client is generating a large number of small batches dynamically" which matches this classic streaming pattern. \$\endgroup\$ Commented Apr 2, 2013 at 12:20
  • \$\begingroup\$ "the classic append/append/append/orphan pattern" That's not the only pattern you can use to efficiently transfer vertex data (such as orphaning every frame, regardless of how much data you use. Or explicit double-buffering). Also, there's no requirement that said pattern only works with glMapBufferRange. There's no reason why glBufferSubData usage in a similar pattern couldn't produce similar results, depending on how you render with the buffer. My point is that you're not limited to either that one pattern or just using client-side vertex arrays. \$\endgroup\$ Commented Apr 2, 2013 at 12:25
  • \$\begingroup\$ But that all comes back to the key word "reasonably" doesn't it? "Unreasonable" != "impossible". Orphaning every frame can be too much performance overhead, particularly if you don't have a fixed data size. Explicit double buffering can be too much memory overhead for mobile devices (noting the ES2 tag on the original question here). Of course you can do it, but can you do it reasonably? \$\endgroup\$ Commented Apr 2, 2013 at 12:30
  • \$\begingroup\$ Who decides what "reasonably" means? The OP doesn't explain what the specific conditions of the application are. Therefore, any solution that is fast could be reasonable. Also, if orphaning each frame is too much overhead, then orphaning at all is too much overhead. Smoothness and consistency of performance is vital for creating a consistent feel. I would consider it "unreasonable" to employ a solution that has a framerate hitch every few frames. Also, double buffering is memory-wise identical to orphaning, since the driver allocates new memory when you orphan a buffer. It's just implicit. \$\endgroup\$ Commented Apr 2, 2013 at 13:27

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .