Slow rendering after large transfer from GPU to RAM?

Viewed 174

I was trying to render a point cloud (>100 million points), I transferred all the data to GPU and just use the buffer name to access it. Everything works fine until I implemented a feature that requires me to transfer all the data back to the RAM.

This is how I pass the data to the GPU:

glBindVertexArray(vao);

// some data
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * GetSize()  * 3, &vertices[0], GL_STATIC_DRAW); // pos

// some attributes
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0); // pos

...
// some other code
...

glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);

glDisableVertexAttribArray(0);

// Clear the contents in the RAM
vertices.clear();
vertices.shrink_to_fit();

Then, I needed a function at some moment to bring back the data in order to save the changes, this is what I did:

// restore data back to RAM
// this code was called for >4000 times

glBindBuffer(GL_ARRAY_BUFFER, c.vbo);
c.vertices.clear();
c.vertices.resize(c.sizeg);
glGetBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(glm::vec3) * c.sizeg, &c.vertices[0].position.x);

After that, the program will hang everytime I need to render that point cloud.

I removed all other codes to test just this code, and this piece of data transfer code is exactly what caused the lag

It feels like there is a huge amount of data copy inside the GPU each time I try to render the data that caused it to hang. However, after I transferred back the data from GPU to RAM, the data that was originally inside the GPU stay untouched, I thought the rendering should be just as smooth as before but it is not.

Is something changed in the OpenGL state that caused this lag?

1 Answers

You made a promise to OpenGL. Then you broke your promise. So OpenGL punished you for it.

You told OpenGL this about how you were going to use this buffer object:

GL_STATIC_DRAW

That means you told OpenGL that you would be writing to the buffer (DRAW) but not reading from it. And you said you would be writing to it rarely (STATIC). So the OpenGL implementation dutifully put the buffer object's storage in a place that was optimal for GPU access, but not for CPU access.

Then you read from it. You did the thing you promised OpenGL you weren't going to do.

Now, people (lots of people) break these kinds of promises to OpenGL implementations all the time. So many implementations basically stopped believing them. That is, instead of listening to what you say that you're going to do with the buffer, they pay attention to what you actually do.

The implementation saw you read from the buffer. So it assumes that you intend to do that with some regularity. As such, it now transfers the buffer's storage to a place that is more optimal for CPU reading... but less optimal for GPU reading.

Modern OpenGL has a buffer object creation API where lying is no longer permitted. You specify which operations you can use on the buffer, and the OpenGL implementation will hold you to that by making all other accesses fail.

If you use the buffer storage API, then it's very likely that attempting to read from the buffer with glGetBufferSubData will not cause the API to just shuffle the memory around. Preventing storage shuffling is half the point of this API. If you still get this slowing effect from your implementation, there may not be much you can do to stop it in OpenGL.

The most effective alternative (other than using Vulkan where you have complete control over memory) would be to just not try to read it from OpenGL at all. That is, don't treat GPU storage like it's something you can just read back when you need some data. If you need to use the data on the CPU, keep it there. That is, keep a copy of it in CPU memory too.

Related