1
$\begingroup$

I have a situation, where I have an array of vertices on the CPU side, for which, besides rendering-relevant data, like their position, I have a lot of other data at each vertex. All vertex data is updated (more or less) every frame, requiring transferring the changes over to the GPU every time.

So far I have used two approaches, neither of which I am particularly happy with:

Approach 1

Just copy everything over to the GPU. This might seem silly, but my application does not use a significant amount of VRAM overall, so this is no issue in that regard, but of course, when the vertex count increases, so does the amount of data needlessly transferred to the GPU every frame, which does become a performance issue.

Approach 2

Keep separate arrays for the GPU-relevant vertex data and the non-relevant vertex data. While this solves the unnecessary transfer issue, I find it very clunky to work with on the CPU side, since I at all times have to keep two arrays in sync. Ideally, I would like to have one clean struct per vertex, which I can easily work with.


Is there some other approach to this?

Something I could think of, which unfortunately doesn't seem to exist in OpenGL, is a variant of glBufferData, which I could tell some stride and offset, like with glVertexAttribPointer, which parts of each data block I want to transfer to the buffer in VRAM.

There also might be some way to neatly solve this with some C++ features that I am not aware of. Some way to have two arrays act as one, or something like that.

Any ideas and suggestions are appreciated.

$\endgroup$
3
  • 2
    $\begingroup$ Striding across memory also has a negative effect on cache performance so if the constant data is small enough is could just be faster to send it all every time. How many bytes of extra data per vertex is there? $\endgroup$
    – pmw1234
    Jan 2, 2021 at 18:11
  • $\begingroup$ For C++ you can hide it behind the type system so create a struct that just has your data and hand that offf to a class to manage it. The class hides the fact that the data is two seperate arrays. (sure you thought of this but thought I would throw it out there just in case) The class would overload the array operator so it would all seem to work naturally. $\endgroup$
    – pmw1234
    Jan 2, 2021 at 18:18
  • $\begingroup$ @pmw1234 The amount of extra data depends on a few things. I'll keep your point about caching in mind, I haven't considered that. Regarding hiding the arrays behind a class, yes, I thought of that, but I was hoping not having to resort to writing yet more custom code for the issue. $\endgroup$
    – user15232
    Jan 2, 2021 at 20:58

2 Answers 2

0
$\begingroup$

This more or less a long comment than an answer.

Some other options that come to mind are the following:

  • A little old school, but you can create an interface with one of the texture objects. You can query different mipmap levels for your needs inside the shader. Textures are one of the better cached objects in OpenGL.

  • You can defer the lightening process by sending everything to a framebuffer with multiple rendering targets. The first pass would filter out the relevant parts of your data and create a geometry buffer. You can use the g-buffer for lightening pass afterwards.

  • You can create a flat array where your relevant and non relevant data are divided in equal strides, then use the indices of the relevant parts stored in your Element Array Buffer object in glDrawElements call. Basically you can use something like the following:

struct MyRealData {
vec3 myRelevantData
float myNotSoRelevantData
};
void to_drawable(MyRealData[20] ds, vec3[40] flat_drawable_array){
  for (int i = 0; i < 40; i+=2){
    flat_drawable_array[i] = ds[i].myRelevantData;
    flat_drawable_array[i+1] = vec3(ds[i].myNotSoRelevantData, 0, 0);
  }
}
std::vector<unsigned int> indices[] = { 
  0, 2, 4, // first triangle
  2, 6, 4, // second triangle
  4, 6, 8, // third triangle
// ... etc
}; 
.
. // later in the code
.
glDrawElements(GL_TRIANGLES, indices.size() , GL_UNSIGNED_INT, (GLvoid *)indices​.data());

``` 
$\endgroup$
0
$\begingroup$

If you only need to update a subset of the vertex data, you could use glBufferSubData() with the offset and size to upload only that section to the GPU. That has worked well for me in the past. It's hard to say how well suited it is to your case without knowing more about how big the data chunk associated with each of your vertices is, how many you nominally update per frame, and how contiguous that subsection is within the full set.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.