Есть ли способ безопасно связать текстуру с помощью OpenGL в рабочем потоке приложения Qt GUI?
в настоящее время я работаю над программным проектом GUI для визуализации 3D-сцен с использованием Qt. GUI позволяет пользователю загружать пакеты 3D-файлов данных, таких как .obj с некоторым .МТЛ и поддержку .stl, а также 2D-файлы изображений в сцену как объекты класса SceneObject, которые отображаются на виджете, производном от QGLWidget.
когда я загружаю их партиями в основной поток GUI, долгое время загрузки заставляет GUI замерзать, что уродливо. Я попытался выполнить загрузку на a отдельный поток, но есть один большой улов: при загрузке .obj текстуры или файлы изображений, я также буду выполнять привязку с помощью OpenGL glBindtexture () сразу после загрузки каждого изображения или текстуры, так что мне нужно только сохранить идентификаторы текстуры в каждом экземпляре SceneObject. Когда я пытался выполнить загрузку в рабочем потоке, вся программа просто сбой.
Я прочитал, что каждый поток может получить доступ только к одному контексту OGL, а переключение контекста между потоками-один, но опасный способ достичь того, чего я хотел. Другим возможным способом было бы выполнить привязку текстуры к потоку GUI после завершения загрузки, но это означало бы полный перепроектирование в моем классе SceneObject: (
может ли кто-нибудь дать мне несколько советов о том, как реализовать поток загрузки для загрузки активов в сцену OpenGL?
1 ответов
Я также выполню привязку с помощью OpenGL glBindtexture () сразу после загрузки каждого изображения или текстуры, так что мне нужно только сохранить идентификаторы текстуры в каждом экземпляре SceneObject. Когда я пытался выполнить загрузку в рабочем потоке, вся программа просто сбой.
контекст OpenGL может быть активен только в одном потоке за раз. В целом многопоточная операция OpenGL обычно становится основным кошмаром, чтобы получить право. В вашем случае, что вы собираетесь делать делегирует загрузку ресурсов. В старые времена, прежде чем был объект buffer, вы бы сделали это, создав вспомогательный контекст и поделившись своими "списками" с основным контекстом.
сегодня у нас есть кое-что получше: буфер объектов. Объект buffer позволяет отправлять данные в OpenGL асинхронным способом. Он идет вдоль подобных
glGenBuffers(...);
glBindBuffer(...);
glBufferData(..., size, usage);
void *p = glMapBuffer(...);
memcpy(p, data, size);
glUnmapBuffer(...);
glTexImage / glDrawPixels / etc.
важно понимать, что адресное пространство, выделенное glMapBuffer, является общим для потоков. Так вы можете сказать Контекст OpenGL в основном потоке для сопоставления объекта буфера, отправляя сигнал в рабочий поток, с выделением. Затем рабочий поток заполняет данные и по завершении отправляет сигнал в контекстный поток OpenGL для unmap.
редактировать для многопоточности
поэтому для этого вы должны реализовать некоторые обработчики сигналов с обеих сторон (псевдокод)
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)