QT之 scene graph 的局部刷新尝试(一)


使用QT做HMI开发,因为GPU或者CPU的资源有限,为了得到更流畅的交互效果,局部刷新的方法是大家都一直寻找的解决方案,限制部分的刷新帧率,把资源更多的留给实时性要求高的。

Qt5以后的版本,都推荐使用GPU去渲染,提高渲染的效果,即使用scene graph这个机制去渲染。 使用Qt的Scene Graph来开发应用,要点是批量渲染。这是由OpenGL的特性决定的,因为通过OpenGL,将以往CPU串行的部分并行化,从而大大提升渲染效率,再加上OpenGL本质上是一个巨大的状态机,在进行批量渲染的时候,可以有效地减少OpenGL状态切换所带来的性能开销,同时OpenGL预留的一些状态,需要开发者有基本的认知,由于OpenGL是一个开放的标准,因此考虑到兼容性,其采用了C/S架构。C端即CPU部分,S端对应GPU。在顶点和纹理数据从C端传入S端之前,会在C端形成一个缓冲区(一说缓存),我们常说的VBO、FBO和PBO就是这一类缓冲区。正确地设置缓冲区的数量和大小,可以为应用程序的性能提升带来很大的帮助。但是它采用的是整屏刷新的策略,这时候就很让人头疼了,背景百年不变,又是这么大一张图,还每一帧都去刷新,这个效率的话就可想而知。所以一直也都在找相关的接口,能否达到想要的目的。

后来,发现QQuickItem有一个Flag的属性。如下:

flags QQuickItem::Flags
This enum type is used to specify various item properties.

Constant				Value				Description
QQuickItem::ItemClipsChildrenToShape	0x01			       Indicates this item should visually clip its children so that they are rendered only within the boundaries of this item.
QQuickItem::ItemAcceptsInputMethod	0x02				Indicates the item supports text input methods.
QQuickItem::ItemIsFocusScope		0x04				Indicates the item is a focus scope. See Keyboard Focus in Qt Quick for more information.
QQuickItem::ItemHasContents		0x08				Indicates the item has visual content and should be rendered by the scene graph.
QQuickItem::ItemAcceptsDrops		0x10				Indicates the item accepts drag and drop events.

其中的Flag  QQuickItem::ItemHasContents 描述这个item是否有可视化的内容,是否应该被scene graph去渲染。于是就猜想这个属性是否能到我们想要的效果呢?如果通过如下的方法:

void QQuickItem::setFlag(Flag flag, bool enabled = true)
Enables the specified flag for this item if enabled is true; if enabled is false, the flag is disabled.
These provide various hints for the item; for example, the ItemClipsChildrenToShape flag indicates that all children of this item should be clipped to fit within the item area.

设置flag  QQuickItem::ItemHasContents为false,是否GPU的buffer可以cache这个Item的东西,而不去在刷新渲染这个东西呢?

于是写了测试代码:

#include 
#include 
#include 
#include 

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    QQuickView myview;
    myview.setSource(QUrl(QStringLiteral("qrc:/main.qml")));


    QQuickItem* item = myview.rootObject();
    QQuickItem* testItem = item->findChild("txt");
    testItem->setFlag(QQuickItem::ItemHasContents,false);
    
    myview.show();

    return app.exec();
}
QML文件:

import QtQuick 2.5

Item {
    width: 640
    height: 480

    Text {
        id: txt
        objectName: "txt"
        text: qsTr("Hello World")
        anchors.centerIn: parent
    }
}

执行结果如下:

QT之 scene graph 的局部刷新尝试(一)_第1张图片
我们可以看到神马都没了,虽然不去渲染了,但是并没有在buffer区cache住这段图像。但是这个东西和设置这个item的visible有啥区别呢?有必要去重复这个东西嘛?然后我进行了各种尝试,最后发现一个使用的用处,如下:

将上面的QML修改一下,如下:

import QtQuick 2.5

Item {
    width: 640
    height: 480

    Text {
        id: txt
        objectName: "txt"
        text: qsTr("Hello World")
        anchors.centerIn: parent

        Rectangle{
            id: testRect
            objectName: "testRect"
            width: 20
            height: 20
            color: "red"
        }
    }
}

这个时候的测试结果会是啥呢?是否是和设置txt控件的visible属性一样呢?会整个界面都是空白吗?

测试结果如下:

QT之 scene graph 的局部刷新尝试(一)_第2张图片

结果只有txt控件没有显示,而它的子控件还是显示的。这点的用处可能就在某些情况下,可以设置单单设置某个节点不显示。

但是Qt提供这个flag真的是出于这个目的吗?后来在帮助文档上面找到了一些线索。

我在关于QQuickItem这边的帮助文档没有找到相关的描述,于是我去找QGraphicsItem 是否有相关的属性,从而会有说明呢?

结果功夫不负有心人,真的被我找到了,QGraphicsItem果然也有和QQuickItem相对应的属性,描述如下:

QGraphicsItem::ItemHasNoContents	0x400			The item does not paint anything (i.e., calling paint() on the item has no effect). You should set this flag on items that do not need to be painted to ensure that Graphics View avoids unnecessary painting preparations. This flag was introduced in Qt 4.6.

这边描述说明当设置了这个属性,渲染框架不但不回去渲染绘制它,连 painting preparations绘制的预准备 都不用了,更加提高了效率。


至于我为什么会去查找QGraphicsItem,是由于QT的scene graph渲染引擎和graphics view的渲染引擎除了底层的渲染的区别较大,但是上层提供的接口还是有很多类似之处,而且graphics view的渲染引擎是较为成熟了,所以相关的资料也会较为齐全。


总结:这个flag 的属性并没有预期的那样得到我想要的结果,没有达到局部渲染的效果,但是还是有收获的,了解了Flag QQuickItem::ItemHasContents  属性,而且也为接下来寻找局部渲染找到了一条思路,可去参考   graphics view  这边是否有方法解决,scene graph 这边是否会有对应的方法呢?


这篇文章没有解决问题,所以也只就是抛砖引玉了,大家如果有找到方法欢迎不吝赐教,后面我找到方法或者有其他的尝试我会继续加载照片文章~~





你可能感兴趣的:(Qt)