QML 渲染機制

作者:billy
版權聲明:著作權歸作者所有,商業轉載請聯繫作者獲得授權,非商業轉載請註明出處

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 Scene Graph Default Renderer

如何在 Qt Quick Scene Graph 中實現自定義幾何可以參考官方文檔:Scene Graph - Custom Geometry

如何定義場景圖材料可以參考官方文檔:Scene Graph - Simple Material

如何在Qt Quick場景下渲染OpenGL可以參考官方文檔:Scene Graph - OpenGL Under QML

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 渲染方式。

  1. threaded 渲染方式
    threaded 渲染方式使用獨立線程來渲染,由於使用多線程,性能得到顯著提升,且界面反饋更加流暢。其簡易示意圖如下:
    在這裏插入圖片描述

  2. 非 threaded 渲染方式 (basic 和 windows)
    這兩種方式使用單線程渲染,但寫代碼時要按照threaded渲染方式來寫,方便日後移植。其簡易示意圖如下:
    在這裏插入圖片描述

  3. 查看自己電腦上的渲染方式

#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

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