【问题标题】:How can I optimize the performance of a QGraphicsView-based app?如何优化基于 QGraphicsView 的应用程序的性能?
【发布时间】:2013-03-02 18:49:18
【问题描述】:

我有一个基于 Qt Graphics View 框架的应用程序。
这是一个拼图游戏,它基本上将像素图切割成更小的像素图(拼图块)并在QGraphicsView 中显示为QGraphicsItems。我希望这个应用程序可以在智能手机和平板电脑上运行。 (它已经在诺基亚 N900 和一些 Symbian 手机上运行。尚未针对 Symbian^3 进行优化。)
来源是on Gitorious

这些项目继承QGraphicsItemQObject,并具有pos()rotation()Q_PROPERTYQGraphicsItem,以便使用Qt Animation 框架为它们设置动画。
我对项目执行转换,例如缩放和旋转(后者仅在开发中的多点触控分支中),并且我还在它们上使用QGraphicsDropShadowEffect

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

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

秘诀是什么?我该如何优化这个应用程序?

【问题讨论】:

  • 你能量化 a) 有和 b) 没有动画属性的性能吗?
  • @genphault - 删除 “提前感谢您的回答” 有什么意义?
  • @spraff - 我无法真正量化,我只能说动画很慢,尤其是当我在游戏开始时同时为所有项目设置动画以及拖动/旋转项目时使用更大的像素图。
  • @spraff - 如果你能告诉我如何量化它,我会很高兴。 :)
  • Qt Graphics Dojo blogs 是有关该领域性能的重要资源。

标签: performance qt opengl qt4 qgraphicsview


【解决方案1】:

好的,我已经等了这么久了。

与此同时,我用 QML 重写了应用程序的 UI,令我惊讶的是,性能大大提高了,应用程序现在非常流畅。

一些结论:

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

Source code is here.

【讨论】:

    【解决方案2】:

    我的答案是针对那些像我一样在他们的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 性能,即使在涉及多个图层的大型场景中也是如此。它甚至可以支持层之间形状的动态裁剪。

    1. 自定义 GraphicsItems 应该继承 QAbstractGraphicsShapeItem, 所以你有 setPen() 和 setBrush() 支持。
    2. 公开一个更新画笔和画笔的界面,并使用一些逻辑 仅在需要时触发更新。

    .h

    class AbstractGraphicsItem : public QAbstractGraphicsShapeItem
    {
        private :
            bool m_hovered ;
    
        public :
            AbstractGraphicsItem() ;
            virtual ~AbstractGraphicsItem() ;
    
            bool isHovered() const { return m_hovered ; }
            void setIsHovered(bool state) ;
    
            // control render mode update
            virtual void updatePenAndBrush()=0 ;
    
        protected :
            virtual QVariant itemChange(GraphicsItemChange change, const QVariant &value) ;
            virtual void hoverEnterEvent(QGraphicsSceneHoverEvent *e);
            virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent *e);
    };
    

    .cpp

    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);
    }
    

    然后你的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::updatePenAndBrush() 中,并且不时被调用,但不是在每次绘制调用时都调用。另一方面,绘画方法涉及基础知识。 显然你必须自己打电话给updatePenAndBrush(),但对我来说这并不难。 这不是我为提高性能所做的唯一事情。我进行了很多搜索,图形视图系统有很多调整可能,但是有了这个,我的应用程序从几乎不可用变为实时(终于!)

    【讨论】:

    • 好东西!虽然这不是我想要的,但感谢分享。 :)
    【解决方案3】:

    特别是当您在场景中移动项目时,QGraphicsScene 的索引可能需要一些时间来更新其索引,从而降低性能。您可以使用setItemIndexMethod() 调整索引。如果您不依赖 items()itemAt(),这可能有助于提高性能。

    但是,这是一个远大的目标。如果您的场景中只有少量项目,则性能提升可能很小。

    【讨论】:

    • 项目的数量取决于用户可设置的拼图的大小。在大多数情况下,它低于 30。(在小屏幕上更多会太难了。)
    • setItemIndexMethod(NoIndex); 似乎不会对我的应用程序的性能造成任何明显的影响。
    • 测试这个很容易。只需在场景构建后直接执行scene.setItemIndexMethod(QGraphicsScene::NoIndex);。至少在那次实验之后,您知道索引是否是您的瓶颈。默认行为是创建(和更新)一个 BspTreeIndex。
    • 好的,看起来索引不是你的问题。对不起。 :(
    • 我将setItemIndexMethod(NoIndex); 直接添加到我的QGraphicsScene 子类的构造函数中。似乎没有任何作用。 :(但仍然感谢您的回答。:)
    【解决方案4】:

    通常最好将 Graphicssystem 设置为“光栅”(由于 GL Widget 作为视口,最终输出仍将是 OpenGL)。您没有提及它,但是如果将“-graphicssystem raster”添加到命令行会产生任何改进,您可以轻松尝试。

    【讨论】:

    • 将图形系统设置为光栅对应用程序的性能完全没有影响。
    【解决方案5】:

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

    【讨论】:

    • 没有特效也不流畅。
    猜你喜欢
    • 1970-01-01
    • 2012-12-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-20
    相关资源
    最近更新 更多