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 渲染方式使用独立线程来渲染,由于使用多线程,性能得到显著提升,且界面反馈更加流畅。其简易示意图如下:
    QML 渲染机制_第1张图片

  2. 非 threaded 渲染方式 (basic 和 windows)
    这两种方式使用单线程渲染,但写代码时要按照threaded渲染方式来写,方便日后移植。其简易示意图如下:
    QML 渲染机制_第2张图片

  3. 查看自己电脑上的渲染方式

#include 
#include 
#include 

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

你可能感兴趣的:(QML)