2
\$\begingroup\$

I was looking for a way to update uniforms for all of my scene objects efficiently and I came across the following gamedev stackexchange question, where the top answer gives a bonus tip. It states:

So long as all your shaders agree on the layout and binding point of each such buffer then updating becomes a breeze. Models have no need to know anything about shaders at all. They need only update the model-view matrix in their constant buffer and the rendering pipeline will use it automatically

emphasis mine.

On the face of it, this seems like a preferable option; I don't have to worry about the uniform's location in a specific program object, or whether a program is bound to the current OpenGL context. I'll just establish a convention in my application for uniform variable names, update some global uniform buffer and render. This would also be nice if I have to support GLSL 1.5 and earlier, where I can't explicitly have a layout location qualifier in my vertex shader.

However, I read in this thread that some users disagree, saying that using UBO's over plain old uniforms incured an undesirable performance penalty.

Should I always prefer using UBO's over uniform variables? What are the pros and cons of each option for per-model uniforms?

\$\endgroup\$
9
  • \$\begingroup\$ Are you drawing multiple copies of a single model? or... Are you drawing multiple models that happen to share similar constants/uniforms? \$\endgroup\$
    – Jon
    Commented Feb 24, 2016 at 0:27
  • \$\begingroup\$ They are not the same model. But the answer in the link provided says that you should have at least 2 UBOs, one for the whole program (which contains things like the view transform, camera, etc) which usually apply to all objects in the scene, as well as another one which is updated on a per-model basis, and contains things like the model-view matrix, normal matrix, etc. \$\endgroup\$ Commented Feb 24, 2016 at 0:30
  • \$\begingroup\$ So the question is basically asking: normally, I would use uniforms to store certain per-model information; should I be using UBO's for that instead? I describe my perceived benefits of this in the question \$\endgroup\$ Commented Feb 24, 2016 at 0:34
  • \$\begingroup\$ 1 UBO or 2 UBOs would produce the same end result. The difference is that when using 1 UBO, you have to update the entire thing even though you have only changed a world matrix. Updating two matrices takes twice as long as updating one... :) \$\endgroup\$
    – Jon
    Commented Feb 24, 2016 at 0:36
  • \$\begingroup\$ Using UBO's, you no longer have to update someFloat, someOtherFloat, and someUint. You can store UBO contents as a struct<> and assign them all at once, per model. \$\endgroup\$
    – Jon
    Commented Feb 24, 2016 at 0:40

1 Answer 1

1
\$\begingroup\$

CPU side:

struct ModelPerInstance
{
  matrix World;
}

class Model
{
public:
  void Draw() = 0;
  ...
private:
  ModelPerInstance m_perInstance;
}

UpdateUniformBufferSlot0(camera->GetViewProj()); //Once per frame
foreach...
{
  UpdateUniformBufferSlot1(model->GetPerInstance()); //Once per frame, per model
  model->Draw();
}

Children of the Model class can append more-specific, per-instance, data to subsequent uniform slots.

UpdateUniformBufferSlot2(specificTypeOfModel->GetModelSpecificPerInstance());

The contents of slot 2 may contain entirely different layout/data from model to model and the model doesn't need to know about which shader is active to bind it. When you create the shared buffer, you just need to be sure to size it for the largest thing you are going to put into it. Since shaders can't read beyond the end of a constant buffer, it doesn't matter that the unused part of the buffer is empty or garbage; it won't be read anyway.

GPU side: (sorry for D3D-style)

cbuffer cb_ModelPerInstance
{
  matrix g_World;
}
\$\endgroup\$

You must log in to answer this question.

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