Qt Quick 场景图
中的场景图Qt Quick
Qt Quick 2 中的场景图使用专用场景图,然后通过 OpenGL ES、OpenGL、Vulkan、Metal 或 Direct 3D 等图形应用程序接口进行遍历和渲染。使用场景图而不是传统的命令式绘制系统( 和类似系统),意味着要渲染的场景可以在帧与帧之间保留,并且在渲染开始前就知道要渲染的整套基元。这样就可以进行大量优化,例如批量渲染以尽量减少状态变化和丢弃模糊的基元。QPainter
例如,用户界面包含一个由十个项目组成的列表,每个项目都有一个背景色、一个图标和一个文本。使用传统的绘制技术,这将导致 30 次绘制调用和类似数量的状态变化。另一方面,场景图可以重新组织要渲染的基元,在一次调用中绘制所有背景,然后绘制所有图标,最后绘制所有文本,从而将绘制调用的总数减少到 3 次。
场景图与Qt Quick 2.0 紧密相连,不能单独使用。场景图由QQuickWindow 类管理和渲染,自定义项目类型可通过调用QQuickItem::updatePaintNode() 将其图形基元添加到场景图中。
场景图是 Item 场景的图形表示,是一个独立的结构,其中包含的信息足以渲染所有 Item。场景图一旦建立,就可以不受项目状态的影响进行操作和渲染。在许多平台上,当图形用户界面线程准备下一帧的状态时,场景图甚至会在专门的渲染线程上进行渲染。
注意: 本页面列出的大部分信息都是针对Qt Quick 场景图的内置默认行为。在使用其他场景图适配(如software
适配)时,并非所有概念都适用。有关不同场景图适配的更多信息,请参阅场景图适配。
Qt Quick 场景图结构
场景图由许多预定义的节点类型组成,每种类型都有专门的用途。虽然我们将其称为场景图,但更准确的定义是节点树。该树由 QML 场景中的QQuickItem 类型构建,然后由绘制场景的渲染器对场景进行内部处理。节点本身不包含任何主动绘制代码或虚拟paint()
函数。
尽管节点树主要是由现有的Qt Quick QML 类型在内部构建的,但用户也可以添加包含自己内容的完整子树,包括表示 3D 模型的子树。
节点
对于用户来说,最重要的节点是QSGGeometryNode 。它通过定义几何体和材料来定义自定义图形。几何体使用QSGGeometry 定义,描述图形基元的形状或网格。它可以是一条直线、一个矩形、一个多边形、多个互不相连的矩形或复杂的 3D 网格。材质定义了如何填充该形状中的像素。
一个节点可以有任意数量的子节点,几何节点在渲染时将按子节点顺序显示,父节点位于子节点之后。
注意: 这并不涉及渲染器中的实际渲染顺序。只保证视觉输出。
可用的节点有
在场景图中实现剪切功能 | |
用于场景图中的所有渲染内容 | |
场景图中所有节点的基类 | |
用于改变节点的不透明度 | |
在场景图中实现变换 |
自定义节点可通过子类化QQuickItem::updatePaintNode() 并设置QQuickItem::ItemHasContents 标志添加到场景图中。
警告 本机图形(OpenGL、Vulkan、Metal 等)操作和与场景图的交互必须完全在渲染线程上进行,主要是在 updatePaintNode() 调用期间。经验法则是只在QQuickItem::updatePaintNode() 函数中使用带有 "QSG "前缀的类。
更多详情,请参阅场景图 - 自定义几何体。
预处理
节点有一个虚拟的QSGNode::preprocess() 函数,它将在渲染场景图之前被调用。节点子类可以设置标志QSGNode::UsePreprocess 并覆盖QSGNode::preprocess() 函数,对其节点进行最终准备。例如,根据当前比例因子将贝塞尔曲线划分为正确的细节级别,或更新纹理的某个部分。
节点所有权
节点的所有权要么由创建者明确指定,要么由场景图通过设置标记QSGNode::OwnedByParent 来指定。将所有权分配给场景图通常更可取,因为当场景图处于 GUI 线程之外时,可以简化清理工作。
材料
材质描述了QSGGeometryNode 中几何体内部的填充方式。它封装了图形流水线顶点和片段阶段的图形着色器,可灵活实现各种效果,但大多数Qt Quick 项目本身只使用非常基本的材质,如纯色和纹理填充。
对于只想在 QML 项目类型中应用自定义着色的用户,可以直接在 QML 中使用ShaderEffect 类型来实现。
以下是材质类的完整列表:
在场景图中渲染纯色几何体的便捷方法 | |
为着色器程序封装渲染状态 | |
代表独立于图形 API 的着色器程序 | |
与 QSGMaterial 结合使用,作为唯一的类型标记 | |
在场景图中渲染纹理几何体的便捷方法 | |
在场景图中渲染纹理几何体的便捷方法 | |
在场景图中渲染每顶点彩色几何图形的便捷方法 |
便捷节点
场景图应用程序接口(API)是底层应用程序接口,侧重于性能而非便捷性。从头开始编写自定义几何体和材质,即使是最基本的,也需要大量代码。因此,API 包含了一些方便的类,使最常见的自定义节点可以随时使用。
- QSGSimpleRectNode - 子类,用于定义带有纯色材质的矩形几何体。QSGGeometryNode
- QSGSimpleTextureNode - 子类,用于定义带有纹理材质的矩形几何体。QSGGeometryNode
场景图和渲染
场景图的渲染在QQuickWindow 类内部进行,没有公共 API 可以访问。不过,用户可以在渲染管道的一些地方附加应用程序代码。这些代码可用于添加自定义场景图内容,或通过直接调用场景图使用的图形 API(OpenGL、Vulkan、Metal 等)插入任意渲染命令。集成点由渲染循环定义。
有关场景图渲染器工作原理的详细说明,请参见Qt Quick Scene Graph Default Renderer。
有两种渲染循环可供选择:basic
threaded
basic
是单线程的,而 则在专用线程上执行场景图渲染。Qt 会根据平台和可能使用的图形驱动程序选择合适的循环。如果效果不理想,或出于测试目的,可使用环境变量 强制使用给定的循环。要验证正在使用哪个渲染循环,请启用 。threaded
QSG_RENDER_LOOP
qt.scenegraph.general
logging category
线程渲染循环("threaded)
在许多配置中,场景图渲染都是在专用渲染线程上进行的。这样做是为了提高多核处理器的并行性,并更好地利用停滞时间,例如等待阻塞的交换缓冲调用。这样可以大大提高性能,但对与场景图交互的时间和地点有一定限制。
以下是使用线程渲染循环和 OpenGL 渲染帧的简单概述。除了 OpenGL 上下文的特殊性外,其他图形应用程序接口的步骤也是一样的。
- QML 场景发生变化,导致调用
QQuickItem::update()
。例如,这可能是动画或用户输入的结果。一个事件被发布到呈现线程,以启动一个新帧。 - 呈现线程准备绘制新帧,并在 GUI 线程上启动一个块。
- 在渲染线程准备绘制新帧时,GUI 线程会调用QQuickItem::updatePolish() 在渲染前对项目进行最后润色。
- GUI 线程被阻塞。
- 发出QQuickWindow::beforeSynchronizing() 信号。应用程序可以直接连接(使用Qt::DirectConnection )该信号,在调用QQuickItem::updatePaintNode() 之前做任何必要的准备。
- 将 QML 状态同步到场景图中。这是通过调用QQuickItem::updatePaintNode() 函数来完成的,所有的项目自上一帧以来都发生了变化。这是 QML 项目和场景图中节点唯一的交互时间。
- 释放 GUI 线程块。
- 渲染场景图:
- 发出QQuickWindow::beforeRendering() 信号。应用程序可以直接连接(使用Qt::DirectConnection )到该信号,使用自定义图形 API 调用,然后在 QML 场景下堆叠显示。
- 指定QSGNode::UsePreprocess 的项目将调用其QSGNode::preprocess() 函数。
- 呈现器处理节点
- 呈现器会生成状态,并记录所使用图形 API 的绘制调用。
- QQuickWindow::afterRendering() 信号会被发出。应用程序可以直接连接(使用Qt::DirectConnection )到该信号,发出自定义图形 API 调用,然后在 QML 场景上堆叠显示。
- 帧现在准备就绪。缓冲区将被交换(OpenGL),或记录当前命令并将命令缓冲区提交到图形队列(Vulkan、Metal)。QQuickWindow::frameSwapped() 被发出。
- 在渲染线程进行渲染时,图形用户界面可以自由推进动画、处理事件等。
目前,线程渲染器默认用于使用 Direct3D 11 的 Windows 系统和使用 opengl32.dll 的 OpenGL 系统、不包括 Mesa llvmpipe 的 Linux 系统、使用 Metal 的 macOS 系统、移动平台和使用 EGLFS 的嵌入式 Linux 系统,以及任何平台的 Vulkan 系统。所有这些都可能在未来版本中发生变化。通过在环境中设置QSG_RENDER_LOOP=threaded
,始终可以强制使用线程渲染器。
非线程渲染循环("基本)
目前,非线程渲染循环默认用于不使用系统标准 opengl32.dll 的使用 OpenGL 的 Windows 系统、使用 OpenGL 的 macOS 系统、WebAssembly 以及使用某些驱动程序的 Linux 系统。对于后者,这主要是一种预防措施,因为尚未对所有 OpenGL 驱动程序和窗口系统组合进行过测试。
在 macOS 和 OpenGL 上,当使用 XCode 10(10.14 SDK)或更高版本构建时,不支持线程渲染循环,因为在 macOS 10.14 上,它选择了层支持视图。你可以使用 Xcode 9 (10.13 SDK) 构建,选择不使用层支持,在这种情况下,线程渲染循环是可用的,并且默认使用。而 Metal 则没有这种限制。
WebAssembly 不支持线程渲染循环,因为网络平台对在主线程以外的其他线程上使用 WebGL 的支持有限,对阻塞主线程的支持也有限。
即使在使用非线程渲染循环时,也应像使用线程渲染器一样编写代码,否则会导致代码不可移植。
下面是无线程呈现器中帧呈现序列的简化示例。
驱动动画
Advance Animations
在上图中指代什么?
默认情况下,Qt Quick 动画(如NumberAnimation )由默认动画驱动程序驱动。这依赖于基本的系统定时器,如QObject::startTimer() 。定时器通常以 16 毫秒的间隔运行。虽然这永远不会完全准确,而且还取决于底层平台中定时器的准确性,但它的优点是独立于渲染。无论显示器的刷新率如何,也无论与显示器的垂直同步是否激活,它都能提供统一的结果。这就是basic
渲染循环的动画工作原理。
为了提供更精确的结果,减少屏幕上的卡顿,独立于渲染循环设计(无论是单线程还是多线程),渲染循环可决定安装自己的自定义动画驱动程序,并自行操作advancing
,而无需依赖计时器。
threaded
渲染循环就实现了这一点。事实上,它安装的动画驱动程序不是一个,而是两个:一个在 gui 线程上(用于驱动常规动画,如NumberAnimation ),另一个在呈现线程上(用于驱动呈现线程动画,即Animator 类型,如OpacityAnimator 或XAnimator )。这两种动画都是在准备帧的过程中进行的,也就是说,动画现在与呈现同步。这是因为底层图形栈会根据显示的垂直同步对演示进行节流。
因此,在threaded
渲染循环的上图中,两个线程都有一个明确的Advance animations
步骤。对于渲染线程来说,这是微不足道的:当线程被节流到 vsync 时,在每一帧中推进动画(对于Animator 类型),就像 16.67 毫秒已经过去一样,这比依赖系统定时器得到的结果更准确。(当节流到 vsync 时序时(在 60 Hz 刷新率下为1000/60
毫秒),可以合理地假设上一帧的相同操作大约经过了这么长的时间
同样的方法也适用于 gui(主)线程上的动画:由于 gui 线程和渲染线程之间的数据同步至关重要,因此 gui 线程实际上被节流到与渲染线程相同的速度,同时还能减少要做的工作,为应用程序逻辑留出更多空间,因为大部分渲染准备工作现在都卸载给了渲染线程。
虽然上述示例使用的是每秒 60 帧的刷新率,但Qt Quick 也为其他刷新率做好了准备:刷新率可通过QScreen 和平台查询。例如,在 144 Hz 的屏幕上,时间间隔为 6.94 ms。同时,如果基于 vsync 的节流功能未按预期运行,这也会带来麻烦,因为如果渲染循环认为正在发生的事情与现实不符,就会出现不正确的动画步调。
注: 从 Qt 6.5 开始,线程化渲染循环提供了仅根据已用时间 (QElapsedTimer) 选择另一个动画驱动程序的可能性。要启用此功能,请将QSG_USE_SIMPLE_ANIMATION_DRIVER
环境变量设置为非零值。这样做的好处是:当有多个窗口时,不需要任何基础设施来退回到QTimer ;不需要启发式方法来确定基于 vsync 的节流是否丢失或损坏;兼容 vsync 节流中任何类型的时间漂移;不与主屏幕的刷新率挂钩,因此在多屏幕设置中可能会更好用。即使基于 vsync 的节流被破坏或禁用,它也能正确驱动渲染线程动画(Animator 类型)。另一方面,这种方法可能会让人觉得动画不那么流畅。考虑到兼容性问题,该功能目前仅作为选配功能提供。
总之,只要满足以下条件,threaded
渲染循环有望提供更流畅的动画,减少卡顿:
- 屏幕上正好有一个窗口(如QQuickWindow )。
- 基于 VSync 的节流功能可在底层图形和显示堆栈中正常工作。
如果没有或只有一个以上的窗口,该怎么办?
当没有可呈现的窗口时,例如,由于QQuickWindow 已最小化(Windows)或完全遮挡(macOS),我们无法呈现帧,因此无法依靠线程与屏幕刷新率同步 "工作"。在这种情况下,threaded
渲染循环会自动切换到基于系统定时器的方法来驱动动画,即暂时切换到basic
循环会使用的机制。
当屏幕上有多个QQuickWindow 实例时,情况也是如此。上面介绍的在 gui 线程上通过与呈现线程同步来推进动画的模式已经不能令人满意,因为现在有多个呈现线程的多个同步点(每个窗口一个)。(在这里,回到基于系统定时器的方法也变得很有必要,因为 gui 线程阻塞的时间和频率现在取决于很多因素,包括窗口中的内容(它们是否在做动画? 它们多久更新一次?)和图形栈行为(它究竟如何处理两个或更多线程以 wait-for-vsync 方式呈现?)由于我们无法保证以稳定、跨平台的方式按照窗口(首先是哪个窗口?)的呈现速率进行节流,因此无法根据渲染来推进动画。
这种动画处理机制的切换对应用程序是透明的。
如果基于 vsync 的节流功能失灵、被全局禁用或应用程序自己禁用了,该怎么办?
threaded
渲染循环依赖图形应用程序接口实现和/或窗口系统进行节流,例如,在 OpenGL(GLX、EGL、WGL)情况下请求 1 的交换间隔,在 Direct 3D 情况下调用间隔为 1 的 Present(),或在 Vulkan 情况下使用呈现模式FIFO
。
有些图形驱动程序允许用户覆盖此设置并将其关闭,而忽略 Qt 的请求。例如,图形驱动程序的系统范围控制面板允许覆盖应用程序有关 vsync 的设置。还有一种情况是,图形栈无法提供基于 vsync 的适当节流,某些虚拟机就可能出现这种情况(主要是由于使用了基于软件光栅化实现的 OpenGL 或 Vulkan)。
如果不在交换/呈现操作(或其他图形操作)中进行阻塞,这样的渲染循环就会使动画推进过快。basic
渲染循环不会出现这种问题,因为它始终依赖于系统定时器。在threaded
中,行为会根据 Qt XML 版本的不同而变化:
- 如果已知系统无法提供基于 vsync 的节流,那么在 Qt 6.4 之前,唯一的选择就是使用
basic
渲染循环,方法是在运行应用程序之前在环境中手动设置QSG_RENDER_LOOP=basic
。 - 从 Qt 6.4 开始,将
QSG_NO_VSYNC
环境变量设置为非零值,或将窗口的QSurfaceFormat::swapInterval() 设置为0
,都可以缓解这一问题:通过明确请求禁用基于 vsync 的阻塞(无论该请求在实践中是否有任何效果),threaded
渲染循环可以通过扩展认识到,依赖 vsync 来驱动动画是徒劳的,它会退回到使用系统定时器,就像对多个窗口一样。 - 更好的是,从 Qt 6.4 开始,场景图还会尝试使用一些简单的启发式方法来识别帧的呈现是否 "过快",并在必要时自动切换到系统定时器。这意味着在大多数情况下无需做任何事情,即使默认的渲染循环是
threaded
,应用程序也能按预期运行动画。虽然这对应用程序来说是透明的,但为了排除故障和开发目的,当启用QSG_INFO
或qt.scenegraph.general
时,会通过打印"Window 0x7ffc8489c3d0 is determined to have broken vsync throttling ..."
消息记录这一点,这一点非常有用。这种方法的缺点是只能在一小部分帧后激活,因为它首先需要收集数据进行评估,这意味着在打开QQuickWindow 时,应用程序可能仍会在短时间内显示过快的动画。此外,它可能无法捕捉到所有可能的 vsync 中断情况。
但请记住,从设计上讲,这一切都无助于呈现线程动画(Animator 类型)。在没有基于 vsync 的阻塞的情况下,即使为常规animations 激活了变通方法,animators 也会在默认情况下以比预期更快的速度错误前进。如果这成为一个问题,可考虑通过设置QSG_USE_SIMPLE_ANIMATION_DRIVER
来使用替代动画驱动程序。
注意: 请注意,即使禁用了等待 vsync,图形用户界面(主)线程上的渲染循环逻辑和事件处理也不一定是无节流的:两个渲染循环都会通过QWindow::requestUpdate() 为窗口安排更新。在大多数平台上,这由 5 毫秒的 GUI 线程计时器支持,以便为事件处理留出时间。在某些平台(如 macOS)上,它会使用特定平台的 API(如 CVDisplayLink)来获取有关准备新帧的适当时间的通知,这可能与显示器的 vsync 以某种形式绑定。这可能与基准测试和类似情况有关。对于试图执行底层基准测试的应用程序和工具,将QT_QPA_UPDATE_IDLE_TIME
环境变量设置为0
可能会有好处,这样可以减少 GUI 线程的空闲时间。对于正常的应用程序使用,大多数情况下默认值就足够了。
注意: 如果有疑问,请启用qt.scenegraph.general
和qt.scenegraph.time.renderloop
日志类别进行故障排除,因为这些类别可能会提供一些线索,说明渲染和动画未按预期速度运行的原因。
使用 QQuickRenderControl 对渲染进行自定义控制
在使用QQuickRenderControl 时,驱动渲染循环的责任被转移到应用程序。在这种情况下,不会使用内置的渲染循环。取而代之的是,由应用程序在适当的时候调用抛光、同步和渲染步骤。可以实现与上图类似的线程或非线程行为。
此外,应用程序可能希望结合QQuickRenderControl 实现并安装自己的 QAnimationDriver。这样就可以完全控制Qt Quick 动画的驱动,这对于不在屏幕上显示的内容尤为重要,因为没有帧的显示,所以与显示速率无关。这是可选项,默认情况下动画将根据系统计时器前进。
使用基于 QRhi 的原生 3D 渲染扩展场景图
场景图提供了三种集成应用程序提供的图形命令的方法:
- 在场景图自身渲染之前或之后直接发布基于QRhi 或 OpenGL、Vulkan、Metal、Direct3D 的命令。这实际上是将一组绘制调用预置或附加到主渲染传递中。不会使用额外的渲染目标。
- 渲染到纹理并在场景图中创建纹理节点。这需要额外的渲染传递和渲染目标。
- 通过在场景图中实例化QSGRenderNode 子类,与场景图自身的渲染联机发出绘制调用。这与第一种方法类似,但自定义绘制调用实际上是注入到场景图的命令流中。
底层/覆盖模式
通过连接到QQuickWindow::beforeRendering() 和QQuickWindow::afterRendering() 信号,应用程序可以直接在与场景图渲染相同的上下文中调用QRhi 或本地 3D API。通过 Vulkan 或 Metal 等应用程序接口,应用程序可以通过QSGRendererInterface 查询原生对象,如场景图的命令缓冲区,并根据需要向其记录命令。正如信号名称所示,用户可以在Qt Quick 场景下或场景上渲染内容。以这种方式集成的好处是不需要额外的渲染目标来执行渲染,也省去了可能很昂贵的纹理制作步骤。缺点是自定义渲染只能在Qt Quick 自己的渲染开始或结束时进行。使用QSGRenderNode 而不是QQuickWindow 信号可以在一定程度上解除这一限制,但无论哪种情况,都必须注意 3D 内容和深度缓冲区的使用,因为依赖深度测试和启用深度写入的渲染很容易造成自定义内容和Qt Quick 内容的深度缓冲区使用相互冲突的情况。
从 Qt XML 6.6 开始,QRhi API 被视为半公开的,即提供给应用程序并记录在案,尽管有有限的兼容性保证。这样就可以通过使用与场景图本身相同的图形和着色器抽象来创建可移植的跨平台 2D/3D 渲染代码。
场景图 - QML 下的 RHI示例介绍了如何使用QRhi 实现底层/覆盖方法。
场景图 - QML 下的 OpenGL示例说明了如何使用 OpenGL 来使用这些信号。
场景图 - QML 下的 Direct3D 11示例说明了如何使用 Direct3D 来使用这些信号。
场景图 - QML 示例下的 Metal示例说明了如何使用 Metal 来使用这些信号。
Scene Graph - Vulkan Under QML示例说明了如何使用 Vulkan 来使用这些信号。
从 Qt XML 6.0 开始,直接使用底层图形 API 必须调用QQuickWindow::beginExternalCommands() 和QQuickWindow::endExternalCommands() 。大家可能对QPainter::beginNativePainting() 中的这一概念并不陌生,其作用与此类似:它允许Qt Quick 场景图识别当前记录的呈现传递(如果有的话)中的任何缓存状态和状态假设现在都已无效,因为应用程序代码可能已通过直接使用底层图形 API 而对其进行了更改。这在使用QRhi 时既不适用,也没有必要。
在将自定义 OpenGL 渲染与场景图混合时,重要的是应用程序不要让 OpenGL 上下文处于缓冲区已绑定、属性已启用、z 缓冲区或模板缓冲区中存在特殊值或类似情况的状态。否则会导致不可预测的行为。
自定义渲染代码必须具有线程感知能力,即它不应假定在应用程序的图形用户界面(主)线程上执行。在连接到QQuickWindow 信号时,应用程序应使用Qt::DirectConnection ,并了解所连接的插槽是在场景图的专用渲染线程(如果有的话)上调用的。
基于纹理的方法
当应用程序需要在Qt Quick 场景中对某些自定义 3D 渲染生成 "扁平化 "的 2D 图像时,基于纹理的替代方法是最灵活的方法。这种方法还允许使用专用的深度/模版缓冲区,该缓冲区独立于主渲染通道使用的缓冲区。
使用 OpenGL 时,可使用传统的便利类QQuickFramebufferObject 来实现这一功能。QRhi基于自定义的渲染器和 OpenGL 以外的图形 API 也可以采用这种方法,尽管QQuickFramebufferObject 目前还不支持它们。下面的示例演示了直接使用底层 API 创建并渲染纹理,然后在自定义QQuickItem 的Qt Quick 场景中封装并使用该资源:
场景图 - 金属纹理导入示例。
内联方法
使用QSGRenderNode ,自定义绘制调用不是在场景图渲染过程记录的开始或结束时注入,而是在场景图渲染过程中注入。这是通过创建基于QSGRenderNode 实例的自定义QQuickItem 来实现的,该实例是一个场景图节点,专门用于通过QRhi 或 OpenGL、Vulkan、Metal 或 Direct 3D 等本地 3D API 发布图形命令。
场景图--自定义 QSGRenderNode示例演示了这种方法。
使用 QPainter 的自定义项目
QQuickItem 提供了一个子类,即QQuickPaintedItem ,允许用户使用QPainter 渲染内容。
警告: 使用QQuickPaintedItem 会使用软件光栅化或 OpenGL 帧缓冲对象(FBO)间接 2D 曲面来渲染其内容,因此渲染需要两步操作。首先对曲面进行光栅化,然后绘制曲面。直接使用场景图应用程序接口总是要快得多。
日志支持
场景图支持多种日志类别。除了对 Qt 的贡献者有帮助外,这些日志对追踪性能问题和错误也很有用。
qt.scenegraph.time.texture
- 记录纹理上传所花费的时间qt.scenegraph.time.compilation
- 记录着色器编译所花费的时间qt.scenegraph.time.renderer
- 记录渲染器各步骤所用时间qt.scenegraph.time.renderloop
- 记录渲染循环各步骤所用的时间。通过 渲染循环,可以了解图形用户界面和渲染线程上各帧准备步骤之间的耗时。因此,它也是一个有用的故障排除工具,例如,确认基于 vsync 的节流和其他低级 Qt 启用程序(如 () )如何影响渲染和演示管道。threaded
QWindow::requestUpdateqt.scenegraph.time.glyph
- 记录准备距离字段字形所花费的时间qt.scenegraph.general
- 记录场景图和图形栈各部分的一般信息qt.scenegraph.renderloop
- 创建渲染各阶段的详细日志。这种日志模式主要适用于使用 Qt 的开发人员。
传统的QSG_INFO
环境变量也可用。将其设置为非零值可启用qt.scenegraph.general
类别。
注意: 当遇到图形问题,或对使用的是哪个渲染循环或图形 API 有疑问时,在启动应用程序时至少要启用qt.scenegraph.general
和qt.rhi.*
,或设置QSG_INFO=1
。这将在初始化过程中将一些重要信息打印到调试输出中。
场景图后台
除公共 API 外,场景图还有一个适配层,可开放实现硬件特定的适配。这是一个未记录的内部私有插件 API,可让硬件适配团队充分利用其硬件。它包括
- 自定义纹理;特别是QQuickWindow::createTextureFromImage 的实现以及Image 和BorderImage 类型使用的纹理的内部表示。
- 自定义渲染器;适配层允许插件决定如何遍历和渲染场景图,从而可以针对特定硬件优化渲染算法,或使用可提高性能的扩展功能。
- 为许多默认 QML 类型定制场景图,包括文本和字体渲染。
- 自定义动画驱动程序;允许动画系统与底层显示垂直刷新挂钩,以获得流畅的渲染效果。
- 自定义渲染循环;可更好地控制 QML 如何处理多个窗口。
© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.