Is there a way to safely bind texture using OpenGL in the Qt GUI application workflow?

I am currently working on a GUI software project for rendering 3D scenes using Qt. The GUI allows the user to load batches of 3D data files, such as .obj with support for some .mtl and .stl, as well as 2D image files into the scene as objects of the SceneObject class, which are displayed in widgets obtained from QGLWidget.

However, when I load them in batches over the main GUI thread, the long loading time silences the GUI, which is ugly. I tried loading in a separate thread, but there is one big catch: when loading .obj textures or image files, I will also bind using OpenGL glBindtexture () right after loading each image or texture, so I only need to save the Texture IDs in each instance of SceneObject. When I tried to load in the workflow, the whole program would just crash.

I read that each thread can access only one OGL context, and switching contexts on a thread is one but dangerous way to achieve what I wanted to do. Another possible way would be to bind the texture to the GUI stream after the download is complete, but that would mean a complete redesign of my SceneObject class: (

Can someone give me some tips on how to implement a downloadable stream to load assets into an OpenGL scene?

+6
source share
1 answer

I will also bind using OpenGL glBindtexture () immediately after loading each image or texture, so I only need to save the texture identifiers in each SceneObject instance. When I tried to load in the workflow, the whole program would just crash.

An OpenGL context can only be active in one thread at a time. In general, an OpenGL multithreaded operation usually becomes a major nightmare in order to qualify. In your case, what you intend to do is delegate resource loading. In the old days, before a buffer object existed, you would have done this by creating an auxiliary context and dividing its β€œlists” with the main context.

Today we have something better: buffer objects. The buffer object allows you to send data to OpenGL asynchronously. He goes along like

glGenBuffers(...); glBindBuffer(...); glBufferData(..., size, usage); void *p = glMapBuffer(...); memcpy(p, data, size); glUnmapBuffer(...); glTexImage / glDrawPixels / etc. 

An important part to understand is that the address space allocated by glMapBuffer is shared between threads. Thus, you can specify the OpenGL context in the main thread to map the buffer object, send a signal to the workflow with distribution. Then the workflow fills in the data and, upon completion, sends a signal to the OpenGL contextual thread for cancellation.

EDIT for multithreading

So, for this you would use some signal handlers on both sides (pseudo-code)

 signal OpenGLThread::mapPixelBufferObjectForWrite(ImageLoader il): glGenBuffers(1, &self.bufferId) glBindBuffer(GL_PIXEL_UNPACK_BUFFER, self.bufferId) glBufferData(GL_PIXEL_UNPACK_BUFFER, il.unpackedImageSize, NULL, GL_STATIC_DRAW) BufferObjectMapping map(glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY)) send_signal(target_thread = workerthread, target_signal_handler = workerthread::loadImageToBuffer(map, il), signal_on_finish = self.unmapPixelBufferObjectWriteFinishedGenTexture(map)) signal IOThread::loadImageToBuffer(BufferObjectMapping map, ImageLoader il): /* ... */ signal OpenGLThread::unmapPixelBufferObjectWriteFinishedGenTexture(BufferObjectMapping map, ImageLoader il): if(map.mapping_target == GL_PIXEL_UNPACK_BUFFER) glBindBuffer(GL_PIXEL_UNPACK_BUFFER, self.bufferId) glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER) glGenTextures(1, &il.textureId) glBindTexture(il.target, il.textureId) for mipmaplevel in il.levels glTexImage2D(il.target, mipmaplevel, il.internalformat, il.width, il.height, il.border, il.format, il.type, mipmaplevel.offset) 
+5
source

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


All Articles