Qt:使用ShaderEffectSource对Item拍摄"UI快照",提升渲染效率
2017-03-19 16:03
483 查看
在上一篇中,提到了如何使用QML Profiler对程序性能问题进行分析
http://blog.csdn.net/wsj18808050/article/details/62226574
而这片文章,就开始我们第一个实战。
这个实战,对应一个很简单并且很常见的需求,就是我们有一个静态(固定内容)的图像,但是这个图像需要一直显示在那里,也许我们还需要对这个图像进行动画。这时候,根据这个图像本身的复杂程度,会给我们整体渲染带来不同程度的渲染开销,当图像足够复杂时,带来的开销可能会大到导致界面卡顿。而既然这是一个静态的图像,照道理就不应该带来过多的开销。所以本次文章的思路就是利用ShaderEffectSource对复杂的Item拍摄一个”UI快照”,代替其本体显示,提升整体渲染效率。
在我写的Demo里,我有一个GridView并且里面创建了240个Item(就是下图中的小方块),每个Item都有一个子Rectangle并且做了旋转等各种操作,同时Item本身也开启了clip(这个clip会给渲染带来很高的负担,因为每个clip都会开启一个渲染批次)。
代码如下:
效果如下:
这时候,我对GridView进行了中心旋转动画,类似于这样的效果:
恩,也许在我电脑上肉眼看不出旋转时卡顿,但是这不代表性能很高。我们继续分析,打开QML Profiler,可以看到如下数据:
根据QML Profiler给出的数据,我们可以得知渲染一帧,大约需要5ms到6ms。虽然这符合60FPS的要求,但是这开销也太大了吧,毕竟就这么点静态图像,不能忍。
这时候,我们可以拿出ShaderEffectSource来解决这个问题。首先介绍一下ShaderEffectSource,按照官方的描述:
更多信息请前往文档查看:http://doc.qt.io/qt-5/qml-qtquick-shadereffectsource.html
也就是说,ShaderEffectSource可以直接将设置进去的Item,渲染成一个纹理,并且用于显示。
这正是我们需要的,因为对于静态图像,我们不需要每次显示的时候,都把里面的数据渲染一遍,毕竟他们没有任何的变化,我们只需要渲染一次,然后记录下来并用于显示即可。
注意,ShaderEffectSource默认情况下会随着设置的Item变化而变化的,在目前我们场景中不需要这个特性,因为我们希望的就是只渲染一次。所以有一个live的参数,要设置为false,这样渲染完一次后,就不会发生变化了。
根据我们的需求和ShaderEffectSource用法,我封装了一个Snapshots.qml,代码如下:
使用的话,直接把sourceItem设置一下,并且把active设置为true即可
那么使用效果如何呢?
我先让这个GridView按照正常方式旋转,然后在鼠标点击后切换为这个Snapshots,效果如下:
切换前:
切换中:
切换后:
可以看出来,对于我们这个测试工程,每帧的渲染时间从5毫秒,直接下降到了90微秒不到,渲染效率直接提高几十倍。性能优化目标达成。
而至于切换中,也就是快照拍摄的那一帧渲染,虽然比正常渲染一帧慢一些,因为多了一次渲染,就是sourceItem渲染成材质那一步,但是对于我们要实现的功能而言,已经是可以接受的了。
其实Qt也有提供一个grabToImage接口,可以提取出图像,但是这个需要把显存中的数据复制到内存中,非常耗时,而这个ShaderEffectSource是完全GPU内实现,不存在拷贝到内存的开销,速度飞起。
示例工程我传百度云了,链接如下:
https://pan.baidu.com/s/1i4XoehB
http://blog.csdn.net/wsj18808050/article/details/62226574
而这片文章,就开始我们第一个实战。
这个实战,对应一个很简单并且很常见的需求,就是我们有一个静态(固定内容)的图像,但是这个图像需要一直显示在那里,也许我们还需要对这个图像进行动画。这时候,根据这个图像本身的复杂程度,会给我们整体渲染带来不同程度的渲染开销,当图像足够复杂时,带来的开销可能会大到导致界面卡顿。而既然这是一个静态的图像,照道理就不应该带来过多的开销。所以本次文章的思路就是利用ShaderEffectSource对复杂的Item拍摄一个”UI快照”,代替其本体显示,提升整体渲染效率。
在我写的Demo里,我有一个GridView并且里面创建了240个Item(就是下图中的小方块),每个Item都有一个子Rectangle并且做了旋转等各种操作,同时Item本身也开启了clip(这个clip会给渲染带来很高的负担,因为每个clip都会开启一个渲染批次)。
代码如下:
GridView { id:gridView width: parent.width height: parent.height cellWidth: 50 cellHeight: 50 delegate: Item { width: 50 height: 50 clip: true Rectangle { anchors.centerIn: parent width: 32 height: 32 color: Qt.rgba( Math.random(), Math.random(), Math.random(), 1 ).toString() rotation: Math.random() * 360 Text { anchors.centerIn: parent font.pixelSize: 8 + Math.random() * 5 color: Qt.rgba( Math.random(), Math.random(), Math.random(), 1 ).toString() text: "Text" } transform: [ Rotation { id: rotation origin.x: Math.random() * 32 origin.y: Math.random() * 32 axis { x: 0; y: 1; z: 0 } angle: Math.random() * 15 } ] } } model: ListModel { Component.onCompleted: { for ( var count = 0; count < 240; ++count ) { append( { } ); } } } }
效果如下:
这时候,我对GridView进行了中心旋转动画,类似于这样的效果:
恩,也许在我电脑上肉眼看不出旋转时卡顿,但是这不代表性能很高。我们继续分析,打开QML Profiler,可以看到如下数据:
根据QML Profiler给出的数据,我们可以得知渲染一帧,大约需要5ms到6ms。虽然这符合60FPS的要求,但是这开销也太大了吧,毕竟就这么点静态图像,不能忍。
这时候,我们可以拿出ShaderEffectSource来解决这个问题。首先介绍一下ShaderEffectSource,按照官方的描述:
The ShaderEffectSource type renders sourceItem into a texture and displays it in the scene. sourceItem is drawn into the texture as though it was a fully opaque root item. Thus sourceItem itself can be invisible, but still appear in the texture.
更多信息请前往文档查看:http://doc.qt.io/qt-5/qml-qtquick-shadereffectsource.html
也就是说,ShaderEffectSource可以直接将设置进去的Item,渲染成一个纹理,并且用于显示。
这正是我们需要的,因为对于静态图像,我们不需要每次显示的时候,都把里面的数据渲染一遍,毕竟他们没有任何的变化,我们只需要渲染一次,然后记录下来并用于显示即可。
注意,ShaderEffectSource默认情况下会随着设置的Item变化而变化的,在目前我们场景中不需要这个特性,因为我们希望的就是只渲染一次。所以有一个live的参数,要设置为false,这样渲染完一次后,就不会发生变化了。
根据我们的需求和ShaderEffectSource用法,我封装了一个Snapshots.qml,代码如下:
import QtQuick 2.7 Loader { id: snapshots width: sourceItem.width height: sourceItem.height active: false property var sourceItem property int shaderEffectFormat: ShaderEffectSource.RGBA sourceComponent: Component { ShaderEffectSource { id: shaderEffectSource width: snapshots.width height: snapshots.height live: false format: snapshots.shaderEffectFormat sourceItem: snapshots.sourceItem Component.onCompleted: { sourceItem.visible = false; } Component.onDestruction: { sourceItem.visible = true; } } } }
使用的话,直接把sourceItem设置一下,并且把active设置为true即可
那么使用效果如何呢?
我先让这个GridView按照正常方式旋转,然后在鼠标点击后切换为这个Snapshots,效果如下:
切换前:
切换中:
切换后:
可以看出来,对于我们这个测试工程,每帧的渲染时间从5毫秒,直接下降到了90微秒不到,渲染效率直接提高几十倍。性能优化目标达成。
而至于切换中,也就是快照拍摄的那一帧渲染,虽然比正常渲染一帧慢一些,因为多了一次渲染,就是sourceItem渲染成材质那一步,但是对于我们要实现的功能而言,已经是可以接受的了。
其实Qt也有提供一个grabToImage接口,可以提取出图像,但是这个需要把显存中的数据复制到内存中,非常耗时,而这个ShaderEffectSource是完全GPU内实现,不存在拷贝到内存的开销,速度飞起。
示例工程我传百度云了,链接如下:
https://pan.baidu.com/s/1i4XoehB
相关文章推荐
- Qt插入大量数据到sqlite数据库,使用事务提升效率(效率提高的惊人)
- Qt 界面使用自定义控件 "提升为"
- Qt 界面使用自定义控件 "提升为"
- Qt 界面使用自己定义控件 "提升为"
- <meta http-equiv="X-UA-Compatible" content="IE=7" />的意思:将IE8使用IE7进行渲染,使网站在IE8上显示正常
- 提升Delphi编程效率必须使用的快捷键(Delphi2007版本)
- FLEX在datagrid中的itemreader中渲染combobox使用outerDocument
- 使用对象-关系映射(ORM)系统中间件提升软件开发效率及质量
- [导入]使用ORM中间件提升软件开发效率及质量
- Pscp使用小结-"more than one remote source not supported"
- Eclipse中使用代码模版提升开发效率
- 使用ORM中间件提升软件开发效率及质量
- 使用ECMAScript 5严格模式提升开发效率
- Linux中使用history命令提升效率
- 使用forall与bulk collect 提升数据割接效率
- Qt4另类使用教程(五)---Qt4使用win32 wgl渲染OpenGL FrameWork + cg language(GPU渲染语言)
- 使用对象-关系映射(ORM)系统中间件提升软件开发效率及质量
- 使用对象-关系映射(ORM)系统中间件提升软件开发效率及质量
- 使用对象-关系映射(ORM)系统中间件提升软件开发效率及质量
- 使用 EffectCustomTool 为 Xna 创建自定义 Shader 特效(二)