2

I've created one thread that be used for loading textures from images and the other thread for drawing images on the screen via paintGL(). But exception thrown.

  1. Do I need a lock or locks? I'm worried that there may be problems caused by using locks, coz these two threads are doing different things but share the same resources.

  2. How can I do share contexts in a QOpenGLWidget way?

Memory Model

The multithreading model that OpenGL uses is built on one fact: the same OpenGL context cannot be current within multiple threads simultaneously. While you can have multiple OpenGL contexts which are current in multiple threads, you cannot manipulate a single context simultaneously from two threads.

I'v added QOpenGLWidget::makeCurrent() in initializeGL() and QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts) in main(), but is's not working. It seems more complicated than just adding these statements.

MyOpenGLWidget.cpp

void MyOpenGLWidget::initializeGL()
{
    QMutexLocker LOCKER(&mutext);

    QOpenGLWidget::makeCurrent();
    QOpenGLFunctions::initializeOpenGLFunctions();
    // some other code here
}

void MyOpenGLWidget::loadTexture()
{
    if (eof == true)
        return;
    QMutexLocker LOCKER(&mutext);

    img_data = SOIL_load_image(path, &width, &height, &channels, SOIL_LOAD_RGB);
    glEnable(GL_TEXTURE_2D);
    glGenTextures(1, &texture); // Access violation reading location thrown
    // some other code here
}

void MyOpenGLWidget::paintGL()
{
    QMutexLocker LOCKER(&mutext);

    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, texture);
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}

MyGLThread.cpp

void MyGLThread::init(MyOpenGLWidget* widget)
{
    gl_widget = widget;
}

void MyGLThread::loadTexture()
{
    gl_widget->loadTexture();
}

void MyGLThread::run()
{
    while (true) {

        if (!gl_widget)
            continue;
        // some other code
        loadTexture();
        msleep(30);
    }
}

MyMainWindow.cpp

MyMainWindow::MyMainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    ui.setupUi(this);
    gl = new MyOpenGLWidget(this);
    gl_thread = new MyGLThread();
    gl_thread->init(gl);
    gl_thread->start();
    // some other code
}
SpellTheif
  • 715
  • 2
  • 12
  • 26
  • 1
    It's not the missing `makeCurrent()` in [`initializeGL()`](https://doc.qt.io/qt-5/qopenglwidget.html#initializeGL) that's your issue. `initializeGL` is even one of the few member functions which does not require it. (It's already done internally.) Additionally, `makeCurrent()` doesn't provide any thread locking for OpenGL. It just makes the context current if accessed from anywhere. For proper thread safety, you need an additional mutex which ensures that always either GUI or your loader thread uses the context exclusively. That said, think about how much sense the concurrency makes then... – Scheff's Cat Apr 23 '19 at 06:04
  • @Scheff "you need an additional mutex which ensures that always either GUI or your loader thread uses the context exclusively" yeah, but where to exactly add the extra mutex? for the `gl_thread` or for the `MainWindow` – SpellTheif Apr 23 '19 at 06:11
  • 2
    Hmm. Both threads have to share and use it. I guess it's on your own. However, my actual intention was to convince you to drop the extra thread texture loader idea. You won't gain extra performance if always only one of the two threads may do work. (The mutex would serialize them.) OpenGL and multi-threading is a bad idea in general as GUI and multi-threading. – Scheff's Cat Apr 23 '19 at 06:14
  • I once sketched something similar for [SO: Qt C++ Displaying images outside the GUI thread (Boost thread)](https://stackoverflow.com/a/47470395/7478597). It hasn't anything to do with OpenGL but image loading, multi threading, and GUI. May be, interesting... – Scheff's Cat Apr 23 '19 at 06:16
  • @Scheff thank you so much. I will check it out. Yeah I've heard that multithreading is a bad practice for OpenGL. – SpellTheif Apr 23 '19 at 06:19
  • 1
    I'd recommend throwing Qt out the window if doing anything even nearly unconventional. BUT, if you are invested enough to not stop now, this is what I would do: make one context for each thread, setup to share textures, use [Sync Objects](https://www.khronos.org/opengl/wiki/Sync_Object) to synchronize the threads/contexts – Andreas Apr 23 '19 at 07:54
  • 1
    As @Scheff already pointed out, your whole approach is broken. _If_ you really want to do the loading and GL object creation in parallel, you must create _two_ separate context, and need to make current each to one thread. Set the context to _shared_, so that the objects are visible to both contexts, and use Sync Objects to tell allow the main thread to query when new objects are ready. The next best thing to not using two contexts is to load the image data into a mapped PBO in a separate thread, and fire off the gl Object creation (as well as PBO mapping / unmapping) in the main thread. – derhass Apr 23 '19 at 17:41

0 Answers0