Performance 如何避免在Qt中滚动时感觉到闪烁?

Performance 如何避免在Qt中滚动时感觉到闪烁?,performance,qt,drawing,flicker,qpainter,Performance,Qt,Drawing,Flicker,Qpainter,我正在尝试使用Qt框架(4.7.4)演示一个滑动显示,其中新的像素数据被添加到屏幕的第一行,以前的像素在每次刷新时滚动到下面一个像素 每秒刷新20次,每次刷新时,在黑色背景上绘制随机绿点(像素) 问题是,;每次刷新都会出现非常明显的闪烁。我通过网络进行了研究,并尽可能优化了我的代码。我尝试将光栅渲染与QPaint(在QWidget上)和QGraphicscene(在QGraphicsView上)一起使用,甚至我还尝试在QGLWidget上使用OpenGL渲染。然而,最后我仍然有同样的闪烁问题 是

我正在尝试使用Qt框架(4.7.4)演示一个滑动显示,其中新的像素数据被添加到屏幕的第一行,以前的像素在每次刷新时滚动到下面一个像素

每秒刷新20次,每次刷新时,在黑色背景上绘制随机绿点(像素)

问题是,;每次刷新都会出现非常明显的闪烁。我通过网络进行了研究,并尽可能优化了我的代码。我尝试将光栅渲染与QPaint(在QWidget上)和QGraphicscene(在QGraphicsView上)一起使用,甚至我还尝试在QGLWidget上使用OpenGL渲染。然而,最后我仍然有同样的闪烁问题


是什么原因导致这种闪烁?我开始怀疑我的LCD监视器无法刷新显示器,使其从黑变绿。我还注意到,如果我选择灰色背景而不是黑色背景,则不会出现闪烁。

我建议您不要在适当的位置滚动pixmap,而是创建第二个pixmap,并使用
drawPixmap()
将所有内容从pixmap 1复制到pixmap 2(带有滚动偏移量)。然后继续在pixmap 2上绘制。在帧之后,交换对两个像素贴图的引用,然后重新开始


其基本原理是,从一个记忆区域复制到另一个记忆区域比就地修改一个记忆区域更容易优化。

你看到的效果纯粹是心理视觉。这是人类的缺陷,不是软件缺陷。我是认真的。您可以通过固定
x
的值进行验证-您仍将在窗口上重新绘制整个pixmap,不会出现任何闪烁-因为本身没有闪烁

当滚动速率与实时通道无关时,会出现心理视觉闪烁。有时,由于CPU负载或系统计时器不准确,更新之间的时间会发生变化,我们的视觉系统会集成两幅图像,看起来好像整体亮度发生了变化

您已经正确地注意到,当您通过将背景设置为灰色来降低图像的对比度时,感知到的闪烁会减少。这是一个额外的线索,表明效果是心理视觉的

下面是一种防止这种影响的方法。请注意滚动距离与时间的关系(此处:1ms=1像素)

#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
静态内联int rand(int range){return(double(qrand())*range)/rand_MAX;}
类Widget:publicqwidget
{
浮式fps;
最后一次秦T64;
QPixmap;
基本定时器;
QEDTimer已过;
无效时间预览(QTimerEvent*ev){
如果(ev->timerId()==timer.timerId())更新();
}
无效油漆事件(QPaintEvent*ev){
qint64时间=经过的时间。经过的时间();
qint64 delta=时间-上次时间;
lastTime=时间;
如果(增量>0){
恒浮重(0.05);
fps=(1.0-重量)*fps+重量*(1E3/增量);
如果(pixmap.size()!=size()){
pixmap=QPixmap(size());
pixmap.fill(Qt::黑色);
}
int dy=qMin((int)delta,pixmap.height());
滚动(0,dy,pixmap.rect());
QPainter pp(和pixmap);
pp.fillRect(0,0,pixmap.width(),dy,Qt::black);
对于(int i=0;i<30;++i){
intx=rand(pixmap.width());
pp.fillRect(x,0,3,dy,Qt::绿色);
}
}
油漆工p(本);
p、 drawPixmap(ev->rect(),pixmap,ev->rect());
p、 设置笔(Qt::黄色);
p、 fillRect(0,0,100,50,Qt::黑色);
p、 drawText(rect(),QString(“FPS:%1”).arg(FPS,0,'f',0));
}
公众:
显式小部件(QWidget*parent=0):QWidget(parent)、fps(0)、lastTime(0)、pixmap(size())
{
定时器启动(1000/60,本次);
已用。开始();
setAttribute(Qt::WA_OpaquePaintEvent);
}
};
int main(int argc,char*argv[])
{
质量保证申请a(argc、argv);
小部件w;
w、 show();
返回a.exec();
}

在绘制事件中保留pixmap绘制的QPaint实例是非常不寻常的。我认为,如果你在另一个绘制设备上绘制pixmap,而QPaint在pixmap上仍处于打开状态,那么该行为甚至可能是未定义的。我记得需要激活双缓冲以获得无闪烁响应(以内存为代价)。@RedX,AFAIK,Qt目前支持双缓冲,我们不需要编写任何额外的双缓冲代码。我忘了说滚动图像似乎没有性能问题。它以1毫秒(1000帧/秒)的速度成功退出paintEvent()方法。问题似乎是关于在显示时渲染pixmap。当我用灰色而不是黑色作为背景时,没有明显的闪烁。@Stefan,你有什么测量方法来支持这一点吗?
#include <QElapsedTimer>
#include <QPaintEvent>
#include <QBasicTimer>
#include <QApplication>
#include <QPainter>
#include <QPixmap>
#include <QWidget>
#include <QDebug>

static inline int rand(int range) { return (double(qrand()) * range) / RAND_MAX; }

class Widget : public QWidget
{
    float fps;
    qint64 lastTime;
    QPixmap pixmap;
    QBasicTimer timer;
    QElapsedTimer elapsed;
    void timerEvent(QTimerEvent * ev) {
        if (ev->timerId() == timer.timerId()) update();
    }
    void paintEvent(QPaintEvent * ev) {
        qint64 time = elapsed.elapsed();
        qint64 delta = time - lastTime;
        lastTime = time;
        if (delta > 0) {
            const float weight(0.05);
            fps = (1.0-weight)*fps + weight*(1E3/delta);

            if (pixmap.size() != size()) {
                pixmap = QPixmap(size());
                pixmap.fill(Qt::black);
            }
            int dy = qMin((int)delta, pixmap.height());
            pixmap.scroll(0, dy, pixmap.rect());
            QPainter pp(&pixmap);
            pp.fillRect(0, 0, pixmap.width(), dy, Qt::black);
            for(int i = 0; i < 30; ++i){
                int x = rand(pixmap.width());
                pp.fillRect(x, 0, 3, dy, Qt::green);
            }
        }
        QPainter p(this);
        p.drawPixmap(ev->rect(), pixmap, ev->rect());
        p.setPen(Qt::yellow);
        p.fillRect(0, 0, 100, 50, Qt::black);
        p.drawText(rect(), QString("FPS: %1").arg(fps, 0, 'f', 0));
    }

public:
    explicit Widget(QWidget *parent = 0) : QWidget(parent), fps(0), lastTime(0), pixmap(size())
    {
        timer.start(1000/60, this);
        elapsed.start();
        setAttribute(Qt::WA_OpaquePaintEvent);
    }
};

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    return a.exec();
}