Advantage of one interleaved buffer over multiple buffers

Started by Rene, February 01, 2010, 20:57:19

Previous topic - Next topic

Rene

Hi all,

I have a question regarding vertex buffers. I've been working on a 3D engine over the past half year, and a week ago it was time to start a new one. I've learned a lot from the last one, and that's exactly what it was for. In the new one I want to use the lessons I learned and make a much better one.

Last time, geometric data for a single mesh was stored in multiple vertex buffers. One for vertex coordinates, one for normals, etc.

But the book I'm using to help me design the engine states that its better to store all geometric data in a single buffer. quote: "The separation of vertex attributes is not natural for an extensible shader-based system.", but it doesn't say why not. Last time, I used a different buffer for each attribute, and I don't see why that's less practical. (no need to reallocate the buffer when adding normal or texture coordinate data for example). However I don't have any practical experience with shaders as the previous engine was fixed function pipeline only.

Can anyone give me a pointer why I should use one buffer for all data? Thanks ;)
When I am king, they shall not have bread and shelter only, but also teachings out of books, for a full belly is little worth where the mind is starved - Mark Twain

Rene

I've tried to put all data in a single buffer, and it appears to me it has so many disadvantages and no real advantages, so I decided to go with my old approach. If I find out why a single buffer is better later on, I'll post it here.
When I am king, they shall not have bread and shelter only, but also teachings out of books, for a full belly is little worth where the mind is starved - Mark Twain

spasi

Interleaving your geometry data was recommended in the past when T&L was done on the CPU most of the time. Interleaved data fetching utilized the caches better and was generally faster, especially if your data was properly aligned to 32 bytes etc. On modern GPUs it is generally not a requirement for optimal performance but you may still want to do it to be sure, it certainly won't hurt.

I found this paper that explores the available options and recommends some best practices. It's a bit old though, so not sure how it applies on the latest generation of GPUs. The paper seems to suggest that for optimal performance you want to use either interleaved VBOs (VNCVNCVNCVNC) or separate VBOs per vertex attribute (VVVV) (NNNN) (CCCC). You don't want to use a single VBO with the vertex attributes batched one after the other (VVVVNNNNCCCC). If that's true, then I can see how interleaved VBOs could be a little more practical (less VBOs to manage) and have little better performance (less state to change per model draw) than non-interleaved VBOs.

I personally haven't done any tests lately, but I've always used interleaved VBOs, I don't find them to be any less practical than non-interleaved ones.

edit: In any case, having properly indexed and sorted geometry for optimal post-T&L cache usage is far more important.

Rene

I see, thanks for the info.

Actually, the reason I used separate VBO's in the first place was to minimize cache misses when iterating over the data. For example when generating normals, which is something I do quite a lot on dynamic geometry, I iterate over all of the vertex coordinates first, calculate the new normals, and then overwrite the old values. Tasks like this are more complicated on one interleaved buffer, and (I think) more efficient when the buffers are stored separately. There's always the possibility to store the data in different buffers in the engine, and then send them to the driver as one interleaved buffer but that's making things more complicated than needed in my opinion.

Another problem arises with the texture coordinates. I need to support both 2D and 3D textures, and a variable number of texture coordinates per mesh. I couldn't think of an elegant way to put everything in one class managing a single buffer.

So, the classes I have now are:
IndexBuffer, self explanatory I think
VertexBuffer, contains one kind of data (coordinates, normals, colors, texture1D, texture2D or texture3D)
GeometricData, contains one IndexBuffer and one or more VertexBuffers. (must have coordinates, may have normals and colors, and has an array with VertexBuffer pointers for the texture coordinates of which any may be null)

This is quite easy to manage, and makes it easy to share data between instances of the same model in the scene. For example, the texture coordinates of a model may be the same for every instance of the model, but the vertex coordinates not if it is dynamic geometry. This structure makes it easy to share the texture coordinates, but have a different VertexBuffer instances for the vertex coordinates.

Anyway, thanks for your answer. It appears performance is the only thing that matters here, and the multiple buffer approach is very fast. A scene with +- 500K triangles (quite a lot of shared buffers however), four dynamic lights working on every mesh and no texturing is rendered at over 1200 frames per second.
screenie
When I am king, they shall not have bread and shelter only, but also teachings out of books, for a full belly is little worth where the mind is starved - Mark Twain