Search code examples
c++qtopenglqtopengl

How to set a uniform variable on user input only?


I have a RenderWidget class inherited from QOpenGLWidget, which has the following two methods, among others:

RenderWidget : public QOpenGLWidget, protected QOpenGLFunctions_4_3_Core {
    // ...
public slots:
    void setSmthEnabled(bool enabled) {
        float val = (enabled == true) ? 1.0f : 0.0f;
        shader.setUniformValue("uniformUserInput", val);
    }

public:
    void paintGL() {
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        // ...
        shader.setUniformValue("uniform1", value1);
        shader.setUniformValue("uniform2", value2);
        // ...
        mesh->draw();
    }
};

paintGL is being called 100 times/second, while setSmthEnabled is only called when a user toggles a checkbox in the UI, which happens occasionally. uniformUserInput uniform does not need to be set every frame, so I try to set it only in user input slot, but it does not work. The uniform preserves it's value, which was set on initialization.

I guess this happens because rendering is asynchronous, and uniform cannot be updated while the pipeline is busy. That's why I tried to call glFinish() before setting uniformUserInput in setSmthEnabled slot, but it didn't help. The only solution I found is to rewrite the class in the following manner:

RenderWidget : public QOpenGLWidget, protected QOpenGLFunctions_4_3_Core {
    // ...
private:
    float val = 1.0f;

public slots:
    void setSmthEnabled(bool enabled) {
        val = (enabled == true) ? 1.0f : 0.0f;
    }

public:
    void paintGL() {
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        // ...
        shader.setUniformValue("uniform1", value1);
        shader.setUniformValue("uniform2", value2);
        // ...
        shader.setUniformValue("uniformUserInput", val);
        mesh->draw();
    }
};

Real-world code contains much more user-input uniforms, so I wouldn't like to update all of them each frame and keep redundant member variables for that purpose. How do I update uniforms on user input only?

shader.setUniformValue in the core above only calls QOpenGLShaderProgram::setUniformValue on a relevant QOpenGLShaderProgram object and returns.


Solution

  • so I try to set it only in user input slot, but it does not work

    Which is to be expected. OpenGL is a statefull API and the state is encapsulated in a context. There may be any number of contexts used in a single program, which must be bound and can be unbound. for the sake of clarity in Qt's programming model the a OpenGL context is only bound in very specific circumstances, namely when inside initializeGL, paintGL and resizeGL. Also you can make a context current with QOpenGLContext::makeCurrent.

    Only when a OpenGL context is bound current you can do things with it. Like setting uniform values.

    I guess this happens because rendering is asynchronous, and uniform cannot be updated while the pipeline is busy.

    That's not the reason. Also OpenGL is perfectly capable, in fact it is specified, that you can make any call at any time and everything gets queued up. You can even replace texture images (even through a PBO) even while the GPU is still using the texture object for rendering (the driver has to keep track of these changes and defer the execution until all affected resources are free to use).