Performance 如何优化基于QGraphicsView的应用程序的性能?

Performance 如何优化基于QGraphicsView的应用程序的性能?,performance,qt,opengl,qt4,qgraphicsview,Performance,Qt,Opengl,Qt4,Qgraphicsview,我有一个基于Qt图形视图框架的应用程序。 这是一款拼图游戏,基本上是将一个pixmap切割成更小的pixmap(拼图块),并在QGraphicsView中将它们显示为QGraphicsItems。我希望此应用程序在智能手机和平板电脑上运行。(它已经在诺基亚N900和一些Symbian手机上运行。尚未针对Symbian ^3进行优化。) 消息来源是 这些项继承QGraphicsItem和QObject,并具有QGraphicsItem的pos()和rotation()的Q\u属性宏,以便使用Qt动

我有一个基于Qt图形视图框架的应用程序。
这是一款拼图游戏,基本上是将一个pixmap切割成更小的pixmap(拼图块),并在
QGraphicsView
中将它们显示为
QGraphicsItem
s。我希望此应用程序在智能手机和平板电脑上运行。(它已经在诺基亚N900和一些Symbian手机上运行。尚未针对Symbian ^3进行优化。)
消息来源是

这些项继承
QGraphicsItem
QObject
,并具有
QGraphicsItem
pos()
rotation()
Q\u属性
宏,以便使用Qt动画框架对它们进行动画制作。
我对项目执行转换,例如缩放和旋转(后者仅在开发中的多点触控分支中),我还对它们使用
QGraphicsDropShadowEffect

我使用
QGLWidget
作为
QGraphicsView
的视口,以便为应用程序启用OpenGL加速

问题是,尽管OpenGL加速了,但应用程序一点也不流畅。(尤其是动画,尤其是在我将旋转变换添加到多点触控分支之后。)显示的图形项目不多,也没有3D操作或任何严重的操作,只有2D绘图。
我根本不是一个图形专家,所以我不知道为什么这个应用程序运行缓慢。我见过其他效果更复杂的游戏比这更流畅


秘密是什么?如何优化此应用程序?

尤其是当场景中有移动项目时,其索引可能需要一些时间来更新其索引,从而降低性能。您可以使用调整索引。如果您不依赖或,这可能有助于提高性能


然而,这是一个漫长的过程。如果场景中只有很少的项目,则性能改进可能很小。

通常最好将Graphicssystem设置为“光栅”(最终输出仍然是OpenGL,因为GL小部件作为视口)。您没有提到它,但是如果在命令行中添加“-graphicssystem raster”会带来任何改进,您可以很容易地尝试。

根据我自己的经验,QGraphicsItem中的图形效果确实占用了大量内存和计算量。如果在动画过渡期间使用它们,可能就是问题所在。你应该把它们取下来,看看它有多平滑,然后试着实现你自己的效果。

好的,我等了这么久才找到解决方案

与此同时,我已经用QML重写了应用程序的UI,令我惊讶的是,性能要好得多,应用程序现在非常流畅

一些结论:

  • 在全屏模式下运行时,OpenGL加速效果最佳。将整个UI放在QDeclarativeView中,并将其视口设置为QGLWidget,并以全屏显示,这使得这成为可能
  • QWidgets的开销似乎比我们想象的要大得多
  • QML的性能比预期的要好得多
  • QGraphicsDropShadow效果的影响很小,但我删除了它,现在我使用了笔划效果。在未来,我可能会考虑使用QML着色器效果。
  • 为QDeclarativeView设置各种优化标志是值得的
  • 使用alpha透明度绘制项目的效果要比不使用alpha透明度绘制项目的效果差得多。尽可能避免alpha透明
  • 将QGraphicsItem子类重写为QDeclarativeItem子类非常简单,值得付出努力

我的答案适用于那些像我一样在
GraphicsItem::paint()方法中实现渲染模式逻辑的人。
例如:

GraphicsItem::paint(QPainter * painter, const QStyleOptionGraphicsItem*, QWidget*)
{
    QPen _pen ;
    const qreal normalPenWidthF = 1.5 ;
    if(isSelected()) {
        _pen.setColor(Settings::s_selectionColor) ;
        _pen.setWidthF(Settings::s_selectionWidth) ;
    }
    else
    if(isHovered()) {
        _pen.setColor(Settings::s_hoveredColor) ;
        _pen.setWidthF(Settings::s_selectionWidth) ;
    }
    else
    if(someOtherLogic()) {
        _pen.setColor(Settings::s_otherColor) ;
        _pen.setWidthF(normalPenWidthF) ;
    }
    else {
        _pen.setColor(TSPSettings::s_defaultColor) ;
        _pen.setWidthF(normalPenWidthF) ;
    }
    //
    painter->setPen(_pen) ;
    painter->setBrush(Qt::NoBrush) ;
    painter->drawEllipse(m_rect) ;
}
下面是我如何获得良好的QGraphicsView性能,即使是涉及多个层的大型场景。它甚至可以支持层间形状的动态剪裁

  • 自定义GraphicsSitem应继承QAbstractGraphicsShapeItem, 因此,您有setPen()和setBrush()支持
  • 公开一个接口来更新画笔和画笔,并使用一些逻辑来 仅在需要时触发更新
  • h

    .cpp

    然后您的
    GraphicsItem
    (继承了
    AbstractGraphicsItem
    )变成:

    void GraphicsItem::updatePenAndBrush()
    {
        QPen _pen ;
        if(isSelected()) {
            _pen.setColor(Settings::s_selectionColor) ;
            _pen.setWidthF(Settings::s_selectionWidth) ;
        } else
        if(isHovered()) {
            _pen.setColor(Settings::s_hoveredColor) ;
            _pen.setWidthF(Settings::s_selectionWidth) ;
        } else
        if(someOtherLogic()) {
            _pen.setColor(Settings::s_otherColor) ;
            _pen.setWidthF(normalPenWidthF) ;
        } else {
            _pen.setColor(Settings::s_defaultColor) ;
            _pen.setWidthF(normalPenWidthF) ;
        }
        _pen.setCosmetic(true) ;
        setPen(_pen) ;
    }
    
    void GraphicsItem::paint(QPainter *painter, const QStyleOptionGraphicsItem*, QWidget *)
    {
        painter->setPen(pen()) ;
        painter->setBrush(brush()) ;
        painter->drawEllipse(s_rect) ;
    }
    
    旧的
    GraphicsItem::paint()
    方法的内容现在位于
    GraphicsItem::updatepenadbrush()
    中,并且偶尔调用,但不是每次调用paint时都调用。在另一方面,绘制方法可以触及基础。 显然,您必须自己调用
    updatepenadbrush()
    ,但对我来说并不难。
    这不是我为提高绩效所做的唯一一件事。我搜索了很多,对于图形视图系统有很多可能的调整,但是有了这个,我的应用程序从几乎不可用变成了实时(最终!)

    项目的数量取决于用户可设置的拼图大小。在大多数情况下,它低于30。(在小屏幕上可能太难了。)
    setItemIndexMethod(NoIndex)似乎不会对我的应用程序的性能造成任何可见的影响。测试这一点非常简单。只需执行一个
    scene.setItemIndexMethod(QGraphicscene::NoIndex)直接在场景构建之后。至少在那个实验之后,你知道索引是否是你的瓶颈。默认行为是创建(和更新)BspTreeIndex。好的,看来索引不是您的问题。对不起(我将
    setItemIndexMethod(NoIndex);
    直接添加到我的
    qgraphicscene
    子类的构造函数中。似乎没有任何效果。:(但仍然感谢你的回答。:)你能量化性能a)和b)而不设置属性动画吗?@genphault-删除的目的是什么“提前谢谢你的回答”@spraff-我不能真正量化,我能说的是动画很慢,特别是当我在游戏开始时一次为所有项目设置动画,以及当我
    AbstractGraphicsItem::AbstractGraphicsItem()
        : QAbstractGraphicsShapeItem()
        , m_hovered(false)
    {
    }
    
    AbstractGraphicsItem::~AbstractGraphicsItem()
    {
    }
    
    void AbstractGraphicsItem::setHovered(bool state)
    {
        if (h!=isHovered()) {
            m_hovered = h ;
            updatePenAndBrush() ;
            update() ;
        }
    }
    
    void AbstractGraphicsItem::hoverEnterEvent(QGraphicsSceneHoverEvent*)
    {
        setHovered(true) ;
    }
    
    void AbstractGraphicsItem::hoverLeaveEvent(QGraphicsSceneHoverEvent*)
    {
        setHovered(false) ;
    }
    
    QVariant AbstractGraphicsItem::itemChange(GraphicsItemChange change, const QVariant &value)
    {
        switch(change) {
            case ItemSelectedHasChanged :
                updatePenAndBrush() ;
                break ;
        }
    
        return QAbstractGraphicsShapeItem::itemChange(change, value);
    }
    
    void GraphicsItem::updatePenAndBrush()
    {
        QPen _pen ;
        if(isSelected()) {
            _pen.setColor(Settings::s_selectionColor) ;
            _pen.setWidthF(Settings::s_selectionWidth) ;
        } else
        if(isHovered()) {
            _pen.setColor(Settings::s_hoveredColor) ;
            _pen.setWidthF(Settings::s_selectionWidth) ;
        } else
        if(someOtherLogic()) {
            _pen.setColor(Settings::s_otherColor) ;
            _pen.setWidthF(normalPenWidthF) ;
        } else {
            _pen.setColor(Settings::s_defaultColor) ;
            _pen.setWidthF(normalPenWidthF) ;
        }
        _pen.setCosmetic(true) ;
        setPen(_pen) ;
    }
    
    void GraphicsItem::paint(QPainter *painter, const QStyleOptionGraphicsItem*, QWidget *)
    {
        painter->setPen(pen()) ;
        painter->setBrush(brush()) ;
        painter->drawEllipse(s_rect) ;
    }