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 <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQuickItem>
#include <QQuickView>

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<QQuickItem *>("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
    }
}

執行結果如下:


我們可以看到神馬都沒了,雖然不去渲染了,但是並沒有在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屬性一樣呢?會整個界面都是空白嗎?

測試結果如下:


結果只有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 這邊是否會有對應的方法呢?


這篇文章沒有解決問題,所以也只就是拋磚引玉了,大家如果有找到方法歡迎不吝賜教,後面我找到方法或者有其他的嘗試我會繼續加載照片文章~~





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章