Есть ли способ безопасно связать текстуру с помощью 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)