天天看點

Qt Quick 渲染機制

Scene Graph 的優勢

在Qt5中推出了一個新的渲染底層 Scene Graph,來替代Qt4時期的 Graphics View。如果大家使用過Qt5的 Qt Quick 子產品,你會感覺 Qt Quick 的畫面渲染速度和效率比Qt4的 GraphicsView 來說好了很多。主要原因是在渲染部分精簡了渲染堆棧,并且充分利用顯示卡加速,将渲染負擔轉移到GPU來進行,實作了負載均衡。

Scene Graph 是直接建構在OpenGL之上的,是以 Scene Graph 對于OpenGL開發者來說要熟悉一些,而從來沒有接觸過OpenGL開發的開發者就有些為難了,幸好Qt在其之上有 QQuickPaintedItem 等友善的類,它可以像QPainter那樣對其進行渲染操作。

使用Qt的 Scene Graph 來開發應用,為了提升性能要點是批量渲染。這是由OpenGL的特性決定的,因為通過OpenGL,将以往CPU串行的部分并行化,進而大大提升渲染效率,再加上OpenGL本質上是一個巨大的狀态機,在進行批量渲染的時候,可以有效地減少OpenGL狀态切換所帶來的性能開銷,同時OpenGL預留的一些狀态,需要開發者有基本的認知,由于OpenGL是一個開放的标準,是以考慮到相容性,其采用了C/S架構。C端即CPU部分,S端對應GPU。在頂點和紋理資料從C端傳入S端之前,會在C端形成一個緩沖區(一說緩存)。正确地設定緩沖區的數量和大小,可以為應用程式的性能提升帶來很大的幫助。

Qt Quick 中的渲染

渲染如何具體工作可以參考官方文檔

Qt Quick 渲染循環

渲染循環有三種方式:basic,windows 和 threaded。其中 basic 和 windows 是單線程,而 threaded 是指定線程内渲染。Qt會根據情況自動選擇使用哪種方式。當性能不滿足,或者出于測試考慮時,可以強制啟動QSG_RENDER_LOOP。

windows 和 threaded 方式非常依賴于OpenGL将交換間隔設定為1。一些顯示卡驅動允許使用者覆寫或關閉這個值,并忽略Qt的修改請求。但如果沒有這個設定,會導緻交換間隔太短,CPU滿負荷運轉。如果知道系統不能自動調整 vsync-based,請手動設定 QSG_RENDER_LOOP=basic 來啟動 basic 渲染方式。

threaded 渲染方式

threaded 渲染方式使用獨立線程來渲染,由于使用多線程,性能得到顯著提升,且界面回報更加流暢。其簡易示意圖如下:

Qt Quick 渲染機制

非 threaded 渲染方式 (basic 和 windows)

這兩種方式使用單線程渲染,但寫代碼時要按照threaded渲染方式來寫,友善日後移植。其簡易示意圖如下:

Qt Quick 渲染機制

檢視自己電腦上的渲染方式

QT開發交流+赀料君羊:714620761
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QLoggingCategory>

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);

    QGuiApplication app(argc, argv);

    QLoggingCategory::setFilterRules(QStringLiteral("qt.scenegraph.general=true"));
    qSetMessagePattern("%{category}: %{message}");

    QQmlApplicationEngine engine;
    const QUrl url(QStringLiteral("qrc:/main.qml"));
    QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                     &app, [url](QObject *obj, const QUrl &objUrl) {
        if (!obj && url == objUrl)
            QCoreApplication::exit(-1);
    }, Qt::QueuedConnection);
    engine.load(url);

    return app.exec();
}

運作結果:
qt.scenegraph.general: threaded render loop
qt.scenegraph.general: Using sg animation driver
qt.scenegraph.general: Animation Driver: using vsync: 16.67 ms
qt.scenegraph.general: Using sg animation driver
qt.scenegraph.general: Animation Driver: using vsync: 16.67 ms
qt.scenegraph.general: texture atlas dimensions: 1024x512
qt.scenegraph.general: R/G/B/A Buffers:   8 8 8 8
qt.scenegraph.general: Depth Buffer:      24
qt.scenegraph.general: Stencil Buffer:    8
qt.scenegraph.general: Samples:           0
qt.scenegraph.general: GL_VENDOR:         Intel
qt.scenegraph.general: GL_RENDERER:       Intel(R) UHD Graphics 630
qt.scenegraph.general: GL_VERSION:        4.5.0 - Build 24.20.100.6345
qt.scenegraph.general: GL_EXTENSIONS: 	  略
qt.scenegraph.general: Max Texture Size: 16384
qt.scenegraph.general: Debug context:    false
           

可以看到我使用的是 threaded 渲染方式,畫面垂直同步花費了 16.67 ms。