Vertices and indices in the same buffer?

Started by
9 comments, last by Ubik 9 years ago

I'm thinking of having a single Buffer concept against having a concept for each of VertexBuffer, IndexBuffer and so on, mainly from the perspective of type safety (i.e. type "Buffer" vs types "VertexBuffer", "IndexBuffer" and so on). Both OpenGL and Direct3D tell that it is possible to mix different kinds of data in the same buffer, but also say that it might come with a performance penalty.

OpenGL's vertex buffer object extension text says:

Note that it is expected that implementations may have different
memory type requirements for efficient storage of indices and
vertices. For example, some systems may prefer indices in AGP
memory and vertices in video memory, or vice versa; or, on
systems where DMA of index data is not supported, index data must
be stored in (cacheable) system memory for acceptable
performance. As a result, applications are strongly urged to
put their models' vertex and index data in separate buffers, to
assist drivers in choosing the most efficient locations.

But that is very old text, apparently from 2003, so it might be completely outdated. So I looked into what D3D documentation says. Here's what D3D11_BIND_FLAG's Remarks section tells:

In general, binding flags can be combined using a logical OR (except the constant-buffer flag); however, you should use a single flag to allow the device to optimize the resource usage.

D3D11 is from 2008, so it's documentation may be not very relevant anymore in this regard too. I don't know what would be good specific terms to find this out, and don't really have too good chances of just testing on a multitude of platforms, so am asking from you now.

I guess I could ask this in two ways:

1) Is it (more often than not) a bad idea to make heterogeneous buffers (I have no idea if there is an official term for this) today?
2) Is it (more often than not) a good idea to make heterogeneous buffers today?

For more specific context, I'm personally interested in how things are in current hardware (and drivers) in the PC, not mobile nor console world, but also the not so recent HW interests me. (Though I have access to one not exactly new GPU myself when I just boot up my own 'puter, to personally test things out...) My API of choice is OpenGL. But let's not restrict this only to my circumstances, it would be interesting to know how things are in general.

Advertisement

I believe that on current (and recent) hardware it's all just "memory", and that the days where a driver may prefer to store different buffer types differently are behind us. This is definitely confirmed by e.g the Mantle documentation:

In the Mantle API, specialized vertex and constant buffers are removed in favor of more general buffer support. An application can use regular buffers to store vertex data, shader constants, or any other data.

In any event there may be organizational or data reasons to keep the buffers separate, but it appears as though you no longer have to.

Under D3D12, and since it has feature levels going down to 9, I would expect at some stage you'll want to keep them separate for performance reasons too, but I haven't looked at the D3D12 documentation in great detail.

Direct3D has need of instancing, but we do not. We have plenty of glVertexAttrib calls.

With GL vertex layout is defined by vertex attrib function call, so you can mix different data type on the same buffer as long as you use correct offset/stride and pay attention to data alignment.

However I don't know if you can bind a buffer to several "type bind point", ie array_element_buffer and array_buffer, or array_element_buffer and shader_storage_buffer, at the same time. I never read it was forbidden by the spec but I don't know the spec by heart.

On D3D12 it isdefinitively be possible since you only pass gpu virtual address, gpu doesnt know that they belong to the same buffer from OS or app perspective.

I just tried to do that with D3D11, using both index and vertex bind flags. It seems to work without complaints. Data looks fine, too.

Back in the day, GPUs weren't actually able to read indices from GPU memory. They were always streamed from main memory. But that is ancient history. Nowadays, on at least some hardware, I've heard that uniform buffers, texture buffers, and possibly shader storage buffers get special handling. The glBufferStorage API (which you should be using, not BufferData) requires you to pick a specific buffer type, not a mask. However on closer reading this appears to be simply a question of the buffer's binding point, not anything to do with the actual usage (which doesn't kick in until BindBuffer). I don't see anything offhand that says you can't bind the same buffer to more than one binding point, and from there it's just a question of offsets.

So yeah, go for it. I would be inclined to keep constant/uniform buffers and texture buffers separate from everything else, though.

SlimDX | Ventspace Blog | Twitter | Diverse teams make better games. I am currently hiring capable C++ engine developers in Baltimore, MD.

@mhagain: Good point on the new APIs revealing much about the current state of the hardware and them being largely agnostic about the buffer contents (as far as I can tell too).

@vlj: Your wording on it being forbidden by the spec somehow brought up one OpenGL-related thought I've had on background - the API may permit things, but may not actually work outside the common ways of use.

@unbird: Thanks for testing this out!

@Promit: I may very well be reading incorrectly between the lines, but I get the impression that mixing different kinds of data in single buffer (in old or current GL or D3D anyway) is not something you've done or seen done, at least enough to be a somewhat common pattern?


I get the impression that mixing different kinds of data in single buffer (in old or current GL or D3D anyway) is not something you've done or seen done

unbird is the only one I've heard of. Without a clear indication that performance is drastically enhanced, it would be a bit of a pain to fill the buffer correctly (vertex-data + offset + index-data, or index-data + offset + vertex-data), if a D3D11 DrawIndexed call would be used for rendering. In spite of the advertised behavior, I was surprised when I (also) experimented and found that D3D11 didn't complain when the same buffer was bound to the input as both a vertex and index buffer! I didn't go further than that - i.e., I didn't calc the offset, fill the buffer, etc.

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.

You don't forget how to play when you grow old; you grow old when you forget how to play.

On modern OpenGL 4: You can use glBufferStorage to create a single chunk of memory that can be used for: vertex, index, constants, UAV and texture buffers. The first binding point the memory is used with can be used as a hint to the driver for performance, but it's just a hint (and afaik... ignored by modern implementations).

On D3D12: See OpenGL 4.

On D3D11: You can use same buffer for vertex & index buffers, but constants have their own. UAV and texture buffers have their own binding point as well.

On WebGL: Every buffer can only be used for one specific type for security reasons (the browser can assume what contents the buffer will contain and perform a lot of validation; as long as you don't modify the buffer again, the validation triggers when you upload data, if WebGL allowed multiple binding points, the validation would have to trigger every time it's bound differently or with a different range which could cripple performance).

@vlj: Your wording on it being forbidden by the spec somehow brought up one OpenGL-related thought I've had on background - the API may permit things, but may not actually work outside the common ways of use.

The recommended use (AZDO) encourages one buffer for everything, being bound at multiple binding points with different offsets. This is not "uncommon".
Of course, if you bind the same region (or an overlapping region) as a vertex buffer and as SSDO with write access, don't expect it to run glitch-free (because that's literally a hazard); same if you bind a buffer at an unaligned offset, etc etc.


@Promit: I may very well be reading incorrectly between the lines, but I get the impression that mixing different kinds of data in single buffer (in old or current GL or D3D anyway) is not something you've done or seen done, at least enough to be a somewhat common pattern?

Correct, but that's not to suggest there's any issue with it. I've just never seen any of the documentation from the IHVs suggest that you should do it, so it never really crossed my mind to try it. I think it's a good idea if GPU memory fragmentation is a concern.

This was a few years ago: https://developer.nvidia.com/sites/default/files/akamai/gamedev/files/gdc12/Efficient_Buffer_Management_McDonald.pdf

SlimDX | Ventspace Blog | Twitter | Diverse teams make better games. I am currently hiring capable C++ engine developers in Baltimore, MD.

This topic is closed to new replies.

Advertisement