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

发布于 2024-12-09 18:05:00 字数 872 浏览 0 评论 0原文

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

这些项目继承了QGraphicsItemQObject,并具有用于pos()rotation(的Q_PROPERTY宏。 )QGraphicsItem 来启用使用 Qt 动画框架对它们进行动画处理。
我对项目执行转换,例如缩放和旋转(后者仅在开发中的多点触控分支中),并且我还在它们上使用 QGraphicsDropShadowEffect。

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

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

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

I have an app which is based on the Qt Graphics View framework.
It's a jigsaw puzzle game which basically cuts a pixmap into smaller pixmaps (puzzle pieces) and displays them as QGraphicsItems in a QGraphicsView. I want this app to run on smartphones and tablets. (It already runs on the Nokia N900 and some Symbian phones. Not optimized for Symbian^3 yet.)
The source is on Gitorious.

The items inherit QGraphicsItem and QObject, and have Q_PROPERTY macros for the pos() and rotation() of the QGraphicsItem to enable animating them with the Qt Animation framework.
I perform transformations on the items, such as scaling and rotating (the latter only in the in-development multitouch branch), and I also use QGraphicsDropShadowEffect on them.

I use a QGLWidget as viewport of the QGraphicsView in order to enable OpenGL acceleration for the app.

The problem is that despite being OpenGL-accelerated, the app is not smooth at all. (Especially the animations and especially since I added the rotation transform to the multitouch branch.) There are not many graphics items displayed, and there are no 3D operations or anything serious, just 2D drawing.
I'm not a graphics expert at all, so I have no idea why this app runs slowly. I've seen other games with lot more complicated effects run a lot smoother than this.

What's the secret? How could I optimize this app?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

一笑百媚生 2024-12-16 18:05:00

好吧,我等了这么久,终于找到解决方案了。

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

一些结论:

  • OpenGL 加速在全屏模式下运行时效果最佳。将整个 UI 放在 QDeclarativeView 中并将其 viewPort 设置为 QGLWidget,然后全屏显示它使这成为可能。
  • 看来QWidgets的开销比我们想象的要多很多。
  • QML 的表现比预期好很多。
  • QGraphicsDropshadowEffect 的影响很小,但我删除了它,现在我使用描边效果。将来,我可能会考虑使用 QML 着色器效果。
  • 为 QDeclarativeView 设置各种优化标志是值得的
  • 。具有 alpha 透明度的绘图项目的性能比不使用 alpha 透明度的绘图项目要差得多。尽可能避免 Alpha 透明度。
  • 将 QGraphicsItem 子类重写为 QDeclarativeItem 子类确实很容易并且值得付出努力。

源代码在这里。

Okay, I've waited for this long for a solution.

In the meantime, I've rewritten the app's UI in QML, and to my surprise, the performance is a LOT better and the app is very smooth now.

Some conclusions:

  • OpenGL acceleration is best when running in full-screen mode. Having the whole UI in a QDeclarativeView and setting its viewPort to a QGLWidget, and displaying it in fullscreen make this possible.
  • It seems that the overhead of QWidgets is a lot more than we had thought.
  • QML can perform a lot better than expected.
  • The impact of the QGraphicsDropshadowEffect was marginal, but I removed it and now I use a stroke effect instead. In the future, I might consider using QML shader effects.
  • It's worth to set all kinds of optimalization flags for the QDeclarativeView
  • Drawing items with alpha transparency performs a lot worse than drawing them without. Avoid alpha transparency whenever possible.
  • Rewriting a QGraphicsItem subclass to be a QDeclarativeItem subclass is really easy and worth the effort.

Source code is here.

沙与沫 2024-12-16 18:05:00

我的答案是针对那些像我一样在他们的 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(),但对我来说这并不难。
这并不是我为提高绩效所做的唯一事情。我搜索了很多,并且图形视图系统有很多可能的调整,但是有了这个,我的应用程序从几乎不可用变成了实时(终于!)

My answer is for people who, like I did for a while, implement render mode logic in their GraphicsItem::paint() method.
For example :

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

Here is how I achieved good QGraphicsView performance, even with large scenes involving multiple layers. It could even support dynamic clipping of shapes between layers.

  1. Custom GraphicsItems should inherit of QAbstractGraphicsShapeItem,
    so you have setPen() and setBrush() support.
  2. Expose an interface to update pen and brush, and use some logic to
    trigger an update only when needed.

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

And then your GraphicsItem (which inherits of AbstractGraphicsItem) becomes :

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

The contents of the old GraphicsItem::paint() method is now in GraphicsItem::updatePenAndBrush() and is called every now and then, but not at every paint call. On the other side, the paint method gets to the basics.
Obviously you'll have to call updatePenAndBrush() yourself, but it was not hard in my case.
It's not the only thing I did to improve performance. I searched a lot, and there is a lot of tweaking possible for a Graphics View system, but with this one my app went from barely usable to real-time (finally!)

时常饿 2024-12-16 18:05:00

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

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

Especially when you have moving items in a scene, QGraphicsScene's indexing may need some time to update its index, decreasing performance. You can tune the indexing by using setItemIndexMethod(). If you do not rely on items() or itemAt(), this may help increase performance.

However, this is a long shot. If you have only few items in your scene, the performance improvements may be minimal.

弥繁 2024-12-16 18:05:00

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

Usually it is best to set the Graphicssystem to "raster" (final output will still be OpenGL because of the GL Widget as viewport). You don't mention it, but you can easily try if adding "-graphicssystem raster" to the command line yields any improvements.

路弥 2024-12-16 18:05:00

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

From my own experience, graphics effects in QGraphicsItem is really memory and computation heavy. If you use them during the animated transitions, it could be the problem. You should take them off and see how much smoother it is then try to implement your own effects.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文