Material concept with uniform buffers?

Hello everyone!

I’ve been very fond of uniform buffers for quite some time now and have obeyed NVidia’s suggestions (albeit in the contex of D3D constant buffers) to not overuse them. I think they advised to not use more than 5.

Unfortunately this hinders the, at least conceptionally, efficient and natural implementation of materials (where a material in my case is a combination of a shader and corresponding inputs), which have multiple sets of properties depending on what the meshes they’re applied to need.

The following is to reflect my thought process and reasoning so any suggestions, personal experience or maybe reference to exsiting approaches is very welcome.

Up until now, I have my engine update the shader’s constant memory if the property group changes. Sorting by material and property group isn’t really a big deal but depending on the number of groups and the number of properties per group, the number of uniform calls per frame can go up pretty fast. Aside from that, the implementation of the concept is far more tedious.

On the plus side, with that approach it’s at least not possible to run out of buffer bindings. Among possible performance penalties this is my biggest concern when using a lot of shaders in one scene, especially on lower-end mobile chips which only support the minimum amount of bindings. (I should mention that the minimum requirements is support for OpenGL 3.0).

Another approach could be to put ALL materials into a single uniform buffer and store the offset per property group. The main concern here is, however, access performance. I remember reading aqnuep’s blog and, IIRC, coming accross his suggestion to best use uniform buffers in scenarios where sequential access is the predominant pattern. Assuming that materials and their property groups would be strictly ordered inside the buffer (which implies that sorting by material now means sorting by buffer offset range) this could be a good candidate to realize such a scheme.

What’s your experience on this?

I use one uniform block (and thus binding point) for all materials. I do UniformBlockBinding when each program is loaded and never after. I only have very few uniform blocks: material, camera, lights and globals.

Currently I put all material instances into a single uniform buffer. Material switch is one BindBufferRange, plus setting sampler uniforms, and a potential program switch.

Using a single uniform block for all materials no doubt wastes memory. But I like that I essentially only need to make a single BindBufferRange to make the material switch. And basically I can use any material with any program (not that it always is meaningful).