在使用QGraphicsView框架进行开发的时候,遇到了很多性能问题,本系列文章将总结一些我使用到的性能优化方案。 首先要讨论的是基于框架自身功能的优化方法,这不是本系列的重点,因此简单提一下。

调整Item的索引模式

在图元视图框架内部,各个Item的位置采用BSP Tree来进行存储。这种数据结构的优势是是查询比较快,也就是通过items()或者itemAt()来定位查找图元的时候会比较快。但在这种情况下,如果一次性移动很多图元,那么框架就可能需要更新整个二叉树,这个更新操作是很耗时的。

对于这种情况,可以通过下面的函数来禁用索引:

scene()->setItemIndexMethod(QGraphicsScene::NoIndex);

绘制缓存

如果某个图元的绘制函数是很耗时的,我们可以开启该图元的缓存模式来进行优化,默认的情况,缓存模式是被关闭的。缓存模式开启的函数如下:

void QGraphicsItem::setCacheMode(CacheMode mode, const QSize &logicalCacheSize = QSize());

常见的缓存模式如下表所示:

模式说明
NoCache默认缓存模式被禁用,每次图元需要重绘的时候 QGraphicsItem::paint()函数都会被调用。
ItemCoordinateCache给图元指定一个QSize()大小的缓存,缓存里面的图像可以用来进行后续的绘制和位移变换。如果想调整缓存图像的质量和分辨率,可以通过调用setCacheMode()分配一块更大的空间。
DeviceCoordinateCache绘制设备级别的缓存优化,主要适用于那些可以移动,但不会发生位移变化(旋转、缩放、剪切)的图元。如果图元发生了位移变化,那么缓存会自动更新。和ItemCoordinateCache不同 DeviceCoordinateCache不需要指定缓存空间大小,它始终以最高质量进行渲染。

视图优化

默认的情况下QGraphicsView在绘制背景和图元的时候会保护pianter的绘制状态。如果我们通过setPen()或者setBrush()修改了绘制状态,该状态会被一直保持,如果图元比较多的时候该绘制状态保持操作是比较耗时的,View中每个图元都需要执行对应的操作。我们可以通过下面的配置来禁用这种配置:

setOptimizationFlag(DontSavePainterState, true);

在禁用了自动绘制状态保持之后,我们就需要负责对painter()的状态进行控制了。禁用了自动绘制状态保持之后,标准控件也会影响painter()的状态。因此,如果同时使用标准图元和自定义图元,保持默认行为或设置 DontSavePainterState之后,在每次paint()绘制完毕的时候都需要将画笔和画刷恢复默认值。

默认情况下如果图元开启了抗锯齿操作,渲染的时候由于差值的影响很可能渲染的范围就超出了图元的边界,这就导致了渲染范围增大,从而影响了渲染性能。我们可以通过下面的配置来禁用边界以外的渲染行为:

setOptimizationFlag(DontAdjustForAntialiasing, true);

启用了该配置之后,抗锯齿渲染便不再渲染边界以外的图形,从而提升渲染性能。这个配置的副作用就是,以抗锯齿的模式绘制的图元在移动时候,会在Scene上留下绘制轨迹。

除了上面的配置之外,还可以通过指定QGraphicsView的渲染更新范围来优化图形框架的性能。对应的配置接口如下:

更新模式分为五种分别如下:

模式说明
FullViewportUpdate更新整个viewport中的图元
MinimalViewportUpdate只更新需要更新的图元
SmartViewportUpdateQGraphicsView会寻找最优的更新模式
BoundingRectViewportUpdateQt检测需要重绘的区域,然后在viewport上寻找一个矩形范围,将所有需要重绘的区域包含进去,对找到的矩形范围进行更新。
NoViewportUpdate当场景改变时, QGraphicsView永远不会更新它的视口,用户控制所有更新。

如果图元比较多,找需要更新的图元的时间大于更新整个viewport更新的时间,我们可以选择FullViewportUpdate模式,相反如果找需要更新的图元的时间小于更新整个viewport的时间,我们可以选择MinimalViewportUpdate模式。

使用OpenGL渲染

QGraphicsView默认的viewport是基于QWidget的性能受限制,我们可以通过使用OpenGL widget替代QWidget来提升渲染性能。对应的配置如下:

QGraphicsView view;
view.setViewport(new QOpenGLWidget());

通常配置OpenGL替代QWidget能提升渲染性能,但Qt的图形视图框架并不是针对GPU设计的,并不能充分发挥GPU的性能,在很多情况下加速效果并不明显。如果想使用可以利用GPU加速的图形框架,可以参考Qt Quick中对应的模块。