7
votes

I can't seem to get Qt5 to update my scene at a meaningful rate. My scene is a 512x512 textured rectangle. The rate I am getting is about 1 frame per second (!).

In my constructor

   aTimer.setSingleShot(false);
   aTimer->setTimerType(Qt::PreciseTimer);
   connect(&aTimer,SIGNAL(timeout()),this,SLOT(animate()));
   aTimer.start(50);
   setAutoFillBackground(false);

and

void GLWidget::animate()
{
//Logic for every time step
updateGL();
}

Is there a way to set priority? Am I doing something totally, wrong? Is there some sort of intrinsic update limitation in Qt, and its certainly not on the order of 1 FPS? My theory is that Qt is ignoring my calls to actually update the screen.

I have tried

  1. to insert a QCoreApplication::processEvents(); but this doesn't help
  2. Call update on the parent widget, and run the timer from the parent
  3. Create a function called animate() that ran forever{} and call update from within it
  4. The wigglywidget example seems to work, which hints to me that QT OpenGL is somehow collapsing frames, ignoring my calls to update. Is there a heuristic that controls this?

Minimal Code to Recreate

(The version is a bit different, being modeled after the wigglywidget class, but has the exact same problem)

git clone https://bitbucket.org/FunFarm/qtcapturesoftware.git

glwidget.h

/****************************************************************************/

#ifndef GLWIDGET_H
#define GLWIDGET_H

#include <QGLWidget>
#include <QtOpenGL/qglshaderprogram.h>
#include <QTimer>
#include <math.h>
#include "time.h"
#include <assert.h>
#include <random>

class GLWidget : public QGLWidget
{
    Q_OBJECT

public:
    GLWidget(QWidget *parent = 0);
    ~GLWidget();
    void addNoise();
protected:
    void initializeGL();
    void paintGL();
    void timerEvent(QTimerEvent *event);
    void resizeGL(int width, int height);
private:
    QBasicTimer timer;
    QPoint lastPos;
    GLuint textures[6];
    QVector<QVector2D> vertices;
    QVector<QVector2D> texCoords;
    QGLShaderProgram program1;
    int vertexAttr1;
    int vertexTexr1;
    //
    int heightGL;
    int widthGL;
    //
    GLubyte* noise;
    //
    QTimer* aTimer;
    //
};
#endif

glwidget.cpp

#include <QtWidgets>
#include <QtOpenGL>
#include "glwidget.h"


#ifndef GL_MULTISAMPLE
#define GL_MULTISAMPLE  0x809D
#endif

GLWidget::GLWidget(QWidget *parent)
    : QGLWidget(QGLFormat(QGL::SampleBuffers), parent)
{
    setAutoFillBackground(false);
    aTimer = new QTimer();
    timer.start(30, this); // 30 fps?
}
void GLWidget::timerEvent(QTimerEvent *event)
{
    addNoise();
    update();// Doesn't matter which update function I call, this is the one from the wigglywidget example
}
 GLWidget::~GLWidget(){}
void GLWidget::initializeGL()
{
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_CULL_FACE);
    glShadeModel(GL_SMOOTH);
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glEnable(GL_MULTISAMPLE);
    static GLfloat lightPosition[4] = { 0.5, 5.0, 7.0, 1.0 };
    glLightfv(GL_LIGHT0, GL_POSITION, lightPosition);
    QGLShader *vshader1 = new QGLShader(QGLShader::Vertex, this);
    const char *vsrc1 =
            "attribute vec2 coord2d;   \n"
            "attribute mediump vec4 texCoord;\n"
            "varying mediump vec4 texc;\n"
            "void main()                  \n"
            "{                            \n"
            "   gl_Position = vec4(coord2d, 0.0, 1.0); \n"
            "   texc = texCoord;\n"
            "}                          \n";
    vshader1->compileSourceCode(vsrc1);

    QGLShader *fshader1 = new QGLShader(QGLShader::Fragment, this);
    const char *fsrc1 =
            "uniform sampler2D texture;\n"
            "varying mediump vec4 texc;\n"
            "void main(void)\n"
            "{\n"
            "    gl_FragColor = texture2D(texture, texc.st);\n"
            "}\n"                                                  ;
    fshader1->compileSourceCode(fsrc1);

    program1.addShader(vshader1);
    program1.addShader(fshader1);
    program1.link();
    vertexAttr1 = program1.attributeLocation( "coord2d");
    vertexTexr1 = program1.attributeLocation( "texCoord");

    // Create the vertex buffer.
    vertices.clear();
    float u=1;
#define AVEC -u,u
#define BVEC -u,-u
#define CVEC u,u
#define DVEC u,-u
    vertices << QVector2D(AVEC);
    vertices << QVector2D(BVEC);
    vertices << QVector2D(CVEC);
    vertices << QVector2D(BVEC);
    vertices << QVector2D(DVEC);
    vertices << QVector2D(CVEC);
    // Create the texture vertex buffer
#define TAVEC 0,1
#define TBVEC 0,0
#define TCVEC 1,1
#define TDVEC 1,0
    texCoords << QVector2D(TAVEC);
    texCoords << QVector2D(TBVEC);
    texCoords << QVector2D(TCVEC);
    texCoords << QVector2D(TBVEC);
    texCoords << QVector2D(TDVEC);
    texCoords << QVector2D(TCVEC);
    QPixmap aMap(":/images/testmap.jpg");
    assert(aMap.width());
    heightGL = aMap.height();
    widthGL = aMap.width();
    textures[0] =bindTexture(aMap,GL_TEXTURE_2D,GL_LUMINANCE);
    noise = (GLubyte*) calloc(1*widthGL*heightGL,sizeof(GLubyte));//GL_RGB8
    memset(noise,100,1*widthGL*heightGL);
    //
}
void GLWidget::addNoise()
{
    std::default_random_engine e((unsigned int)(time(0)));
    GLubyte c = e()%256;
    assert(noise);
    for (int i = 0; i<heightGL;i++)
    {
        for(int j =0;j<widthGL;j++)
            noise[i*widthGL+j]= c;
    }
}

void GLWidget::paintGL()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();
    //
    //

    program1.bind();
    program1.setUniformValue("texture", 0);
    program1.enableAttributeArray(vertexAttr1);
    program1.enableAttributeArray(vertexTexr1);
    program1.setAttributeArray(vertexAttr1, vertices.constData());
    program1.setAttributeArray(vertexTexr1, texCoords.constData());
    glBindTexture(GL_TEXTURE_2D, textures[0]);
    glTexSubImage2D(GL_TEXTURE_2D,0,0,0,
                    widthGL,heightGL,GL_LUMINANCE,GL_UNSIGNED_BYTE, //lets hope these are correct
                    noise);
    glDrawArrays(GL_TRIANGLES, 0, vertices.size());
    program1.disableAttributeArray(vertexTexr1);
    program1.disableAttributeArray(vertexAttr1);
    program1.release();
}

void GLWidget::resizeGL(int width, int height)
{
    int side = qMin(width, height);
    glViewport((width - side) / 2, (height - side) / 2, side, side);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(-0.5, +0.5, -0.5, +0.5, 4.0, 15.0);
    glMatrixMode(GL_MODELVIEW);

}

main.cpp

/****************************************************************************/
#include <QApplication>
#include <QDesktopWidget>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    //cameraView cV;
    //cV.show();
    GLWidget mW;
    mW.show();
    return a.exec();
}

test.pro

HEADERS       = glwidget.h \
    cameraview.h
SOURCES       = glwidget.cpp \
                main.cpp \
    cameraview.cpp
QT           += opengl widgets

CONFIG += console
CONFIG += c++11

# install
target.path = $$[QT_INSTALL_EXAMPLES]/qt_OpenGL_3x/02_First_Triangle
sources.files = $$SOURCES $$HEADERS $$RESOURCES $$FORMS 02-first-triangle.pro
sources.path = $$[QT_INSTALL_EXAMPLES]/qt_OpenGL_3x/02_First_Triangle
INSTALLS += target sources


simulator: warning(This example might not fully work on Simulator platform)

FORMS += \
    cameraview.ui

RESOURCES += \
    images.qrc

OTHER_FILES +=
1
try restarting the timer from within animate, at least that's how I managed the timers in windoz BTW do you use double buffering?thAAAnos
This didn't help. There is a slight increase in the frame rate (now its ~500ms), but it also causes the program to appear to be constantly loading.Mikhail
i trust you restarted at the end? with singleshot true?thAAAnos
do you also use call lists? on the renderingthAAAnos
Did you try calling QGLWidget::update() after updateGL()? Related: stackoverflow.com/questions/4468846/….kfunk

1 Answers

10
votes

I cloned the repository and made a few changes to the code to help you debug the problem. First, print a message in the beginning of paintGL() with std::cout:

void GLWidget::paintGL()
{
   static int xyz = 0;
   std::cout << "PaintGL begin " << xyz << std::endl;

and another message at the end of paintGL. After that, increment the counter variable:

   program1.release();

   std::cout << "PaintGL end " << xyz << std::endl;
   xyz++;
}

This approach clearly shows that paintGL() is called nearly 30 times per second due to the amount of messages that are printed to the console in 2 seconds:

PaintGL begin 0
PaintGL end 0
PaintGL begin 1
PaintGL end 1
...
...
PaintGL begin 60
PaintGL end 60
PaintGL begin 61
PaintGL end 61

So the theory that there is a problem with the timer can be discarded. The timer is fine, and you are indeed painting the window 30 timer per second or so.

The problem is probably somewhere else in your code. Since you shared only a piece of the application in the repository I'm afraid I won't be able to help you any further.

EDIT:

I noticed that inside addNoise() you have a random number generator. If you are expecting the drawing to change at every paintGL() call based on the number generated by the other method you will be disappointed.

The current number generation mechanism that is implemented is not sensitive to millisecond changes, so all the calls to addNoise() within the same second will generate the same number, which in turn will paint the same thing on your window for the entire second. That explains why the drawing changes only once per second.

To fix this problem I suggest taking a look at: