Stan
Stan

Reputation: 731

Clearing Shader Storage Buffer efficiently

I am trying to figure out how to clear a shader storage buffer efficiently. The ssb looks like this in my shader:

struct Type{
    vec4 A, B, C;
};
layout (std430, binding = 1) buffer TypeBuffer {
    Type items[];
};

I write to this buffer once per frame and need it to be cleared at the beginning of the next frame. The specs offer a convenient function to do it:

glClearNamedBufferData(GLuint buffer, GLenum internalformat, GLenum format, GLenum type, const void *data);

Unfortunately, no internalFormat enum corresponds to the format of Type. To me it seems this function is not suited to clear arbitrary SSBO's, although it explicitly offers to clear them in the specs.

At the moment I am sticking to this method:

  //Do this only once!
  float* nullData = new float[N * sizeof(float) * 4 * 3];
  for(int i = 0; i < N * sizeof(float) * 4 * 3; i++){
    nullData[i] = 0.f;
  }
  glGenBuffers(1, &bufferID);

  //Execute this every frame
  glBindBuffer(GL_SHADER_STORAGE_BUFFER, bufferID);
  glBufferData(GL_SHADER_STORAGE_BUFFER, N * sizeof(float) * 4 * 3, nullData, GL_STATIC_READ);
  glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);

Assuming I could use glClearNamedBufferData, would there be even a difference in performance between those two clearing methods? Is there a faster way to clear the SSBO every frame compared to my current solution?

Upvotes: 4

Views: 2451

Answers (2)

Nicol Bolas
Nicol Bolas

Reputation: 473407

It doesn't matter if there is no internalformat value that matches exactly your Type declarations. What is a Type, at it's core?

It's 12 floats.

There is an internalformat that matches a float. Indeed, your manual clearing of the buffer does exactly that: it creates not a Type array, but a float array. It fills them with zeros, and writes them to the buffer. So just tell OpenGL to do it:

float val = 0.0f;
glCreatNamedBufferData(buff, GL_R32F, GL_RED, GL_FLOAT, &val);

Indeed, it doesn't even have to be that complicated. Since you know that IEEE-754 floats represent the 0.0 value as a sequence of bytes containing all zeros, you could just clear the buffer to all zero bytes:

GLubyte val = 0;
glClearNamedBufferData(buff, GL_R8UI, GL_RED_INTEGER, GL_UNSIGNED_BYTE, &val);

Is there a faster way to clear the SSBO every frame compared to my current solution?

In terms of executing the clearing operation, almost certainly.

Upvotes: 2

derhass
derhass

Reputation: 45332

glClear[Named]BufferData does not directly handle compound data types like structures. It does treat the buffer as a an array of basic types, and sets every single array element to the same value. So if you wanted to use a structure containing for example two floats a and b, and you would want to initialize the buffer so that every entry's a is set to 0.0f, but every b to 1.0f, it would be impossible to do so with that function.

However, since your buffer contains just a sequence of floats, so you can clear it as such with internalFormat set to GL_R32F. Since you use vec4's, the "most matching" format would actually be GL_RGBA32F.

Assuming I could use glClearNamedBufferData, would there be even a difference in performance between those two clearing methods?

Very likely yes. The glBufferData will have to retransfer the complete array, while the glClear[Named]BufferData variant just needs to transfer the data for one pixel. If the buffer is bigger than just a few elements, it will be much faster.

There is another difference, though. glBufferData does not just overwrite the contents of the buffer (glBufferSubData would do that), but it creates a completely new data storage for it. This can also have some impact on performance, in one way or the other. For example, the glClear[Named]BufferData call might have to be delayed until the GL is done processed the current contents of this buffer. The actual depenencies between your operations regarding that buffer, and the implicit or explicit synchronizations these may require, will greatly influence the performance for the different scenarios.

Conceptually, glClearNamedBufferData is the correct approach for your use case. You may still need to consider combining it with explicit buffer oprhaning or using some double- or tipple-buffering strategy.

Upvotes: 1

Related Questions