OpenGL VBO in multiple threads

I am developing a program in C ++ / OpenGL that paints the landscape of the whole world. I have a base of heights of heights stored as tiles. Each time I run the program, the tile loads. Then, when a person moves, another tile should be loaded, this does not happen every frame, maybe once every 5 minutes.

I load the source plate into the memory of the video card:

glBindBufferARB(GL_ARRAY_BUFFER_ARB, VertexBuffer[idx]); glBufferDataARB(GL_ARRAY_BUFFER_ARB, VBOsz * 3 * sizeof(float), tile_data, GL_STATIC_DRAW_ARB); 

... There are normals, color and index buffers

And I draw them:

 glBindBufferARB(GL_ARRAY_BUFFER_ARB, VertexBuffer[idx]); glBufferDataARB(GL_ARRAY_BUFFER_ARB, VBOsz * 3 * sizeof(float), tile_data, GL_STATIC_DRAW_ARB); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_COLOR_ARRAY); glEnableClientState(GL_NORMAL_ARRAY); glBindBufferARB(GL_ARRAY_BUFFER_ARB, VertexBuffer[idx]); glVertexPointer(3, GL_FLOAT, 0, BUFFER_OFFSET(0)); 

...

 glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, IndexBuffer[idx]); glDrawElements(GL_TRIANGLES, IndexBuffersz, GL_UNSIGNED_INT, BUFFER_OFFSET(0)); glDisableClientState(GL_NORMAL_ARRAY); glDisableClientState(GL_COLOR_ARRAY); glDisableClientState(GL_VERTEX_ARRAY); 

Since I want the program to be as smooth as possible, I cannot calculate the vertex + color + normal + another texture of the same frame, since it takes about 20 seconds to create the tile.

So, I decided to create a bootloader thread that will check when it will be necessary to load a new plate, and then load it. When everything is done, you just need to change the VBO (hence [idx].

So, for the bootloader thread, I know that I need a second OpenGL context, I created it, and I share the lists between them. The idea works, but in the bootloader thread, when I send new VBO data, I need this function: wglMakeCurrent

Only when all the data has been loaded can I set the context to the render stream (main program stream). This does not lead to the fact that nothing is done for this period of time, which makes the program useless.

Do you have any ideas for a solution? Do I need to change the concept?

I am using OpenGL 2.1. Will upgrading to OpenGL 3 solve the problem?

+4
source share
3 answers

I already have an answer for such tasks.

In a few words, you create two contexts of exchange. Then, as Damon suggested, make contexts current in your thread, only once at the start of the thread. Two contexts will be current simultaneously with different threads (one thread, one context).

Then the secondary stream will not be used for rendering, but for loading resources (I mean, I actually load the terrain data, textures ... and create the corresponding OpenGL object for every information, such as textures and buffer objects). This happens while drawing main context.

Essentially, you don’t have to worry about moving the pointer around the application and blocking the rendering stream for loading data; but at the cost of creating a different context. Let the driver synchronize contextual states: if it can perform these operations smoothly, your application will be useful to them; at least the code will be cleaner.

My other work on this subject:

+2
source

It really is not that difficult.

You simply create two buffer objects: one that you use for the current rendering, and one that you will use in the future. When an unused buffer is filled with data, the rendering stream switches to rendering from that. A previously used buffer becomes unused.

You can load data in one of two ways. One way is to create a data stream to create a data array that the rendering stream will load into the buffer object using glBufferData . Obviously, this will require some synchronization, but you need a synchronization code: your rendering stream should eventually be informed when the data is ready so that it can visualize new data.

Another way is that for the rendering stream you need to tell that the data should be generated. At this point, it will display the unused buffer object and pass the displayed pointer to the data creation stream. This stream will generate data directly to this displayed pointer. When it finishes, it informs the rendering stream, which will cancel the display of the buffer, and then display the data.

None of the methods require multiple contexts or threads through OpenGL code.

Note that performance will be best due to the fact that buffers will not be larger or smaller. Choose a size and stick to it; You do not want to resize buffers using glBufferData .

+6
source

You only need to call wglMakeCurrent exactly once in each thread. This works reliably, this is what I do (albeit with OpenGL 3.3). This refers to one context belonging to one thread. It remains so until you say OpenGL differently, so do it once at the beginning and forget (in fact, you don’t need to call it at all if you create contexts in your threads using them, but do it anyway just being 100% safe, also I prefer to create all contexts before starting, it's not so dirty ...).

You do not need to worry about the function pointer, by the way, just use the same one that you used in the rendering stream (if you initialized it correctly).
Technically, it is not valid to use a function pointer from a different context. However, WGL kindly guarantees (hidden in a small font!) That function pointers are identical for all contexts having the same pixel format. So you are good to go.

An alternative that works with a single context is glMapBuffer in the render stream and passes a pointer to the workflow. Then, upon completion (e.g. semaphore signaling), glUnmapBuffer , again in the rendering stream.
Some people prefer this because it is not related to juggling contexts and seems to work better on some older buggy drivers. I do not like this because of the additional synchronization. It is a matter of taste, the same effect.

+2
source

Source: https://habr.com/ru/post/1391663/


All Articles