Qt OpenGL 更新非常缓慢

7

我似乎无法让Qt5以有意义的速度更新我的场景。我的场景是一个512x512纹理矩形。我得到的速率大约是每秒1帧(!)。

在我的构造函数中

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

并且

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

有办法设置优先级吗?我是不是做错了什么?Qt里面是否存在某种内在的更新限制,而且肯定不止1 FPS吧?我的理论是Qt忽略了我对实际屏幕更新的调用。
我尝试过:
  1. 插入QCoreApplication :: processEvents();,但这没有帮助。
  2. 在父部件上调用update,并从父部件运行定时器。
  3. 创建一个名为animate()的函数,其中包含forever {},并在其中调用update。
  4. wigglywidget示例似乎可以工作,这提示我QT OpenGL在某种程度上折叠帧并忽略了我对更新的调用。是否存在控制此操作的启发式算法?
重现的最小代码:

(版本有点不同,模仿wigglywidget类,但问题完全相同)

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 +=

尝试在动画中重新启动定时器,至少这是我在Windows中管理定时器的方式。顺便问一句,你使用双缓冲吗? - thAAAnos
你也使用调用列表吗?在渲染方面。 - thAAAnos
是的,我尝试过使用单次触发方法,但它并没有起到帮助作用(aTimer->setInterval(10);aTimer->singleShot(0,this,SLOT(animato()));aTimer->start();)。你所说的调用列表是什么意思? - Mikhail
我很乐意看到一个简短、自包含、正确(可编译)的示例来帮助你。 - karlphillip
@Mikhail 我添加了一个答案。祝你好运。 - karlphillip
显示剩余5条评论
1个回答

10

我克隆了代码库,并对代码进行了一些更改,以帮助您调试问题。首先,在paintGL()的开头使用std::cout打印一条消息:

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

paintGL结束时添加另一条消息。之后,递增计数器变量:

   program1.release();

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

这种方法清楚地表明每秒调用paintGL()近30次,因为在2秒内有大量的消息打印到控制台:

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

因此,关于计时器存在问题的理论可以被排除了。计时器正常,您确实每秒左右绘制窗口30次。

问题可能出现在代码的其他位置。由于您只在存储库中共享了应用程序的一部分,我恐怕无法为您提供更多帮助。

编辑:

我注意到在addNoise() 中有一个随机数生成器。如果您期望每次基于其他方法生成的数字调用paintGL() 时绘图会发生变化,那么您将会失望。

当前实现的数字生成机制不会对毫秒级别的更改敏感,因此同一秒钟内对addNoise() 的所有调用都将生成相同的数字,这又将在整个秒钟内绘制相同的内容。这就解释了为什么绘图每秒只更改一次。

要解决这个问题,我建议参考以下内容:


感谢您关注此事。当我在我的Windows 7 Xeon机器上运行代码时,我发现图像变化可见速度较慢,不到30帧每秒。我期望代码会闪烁一张图片,但它似乎只更新大约一秒钟一次?您是否遇到过类似的情况?据我所知,图形引擎将合并(折叠)重绘函数,以便您不能触发更多的重绘,因此我想知道这是否是问题所在,因为没有OpenGL小部件的类似代码可以正常工作。 - Mikhail
@Mikhail 在最后一个 std::cout 之前调用 glFinish()。该函数会阻塞直到所有操作完成,因此您可以确定窗口将每30ms重新绘制一次。我忘了说我从 .pro 文件中删除了 CONFIG += c++11,因此我不得不替换: std::default_random_engine e((unsigned int)(time(0))); GLubyte c = e()%256; 通过传统的方式: srand((unsigned int)time(0)); GLubyte c = rand()%256; - karlphillip

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接