【Qt QML】Qt Quick Scene Graph

Qt Quick 2是一个用于创建图形界面的库,它使用一个专门的场景图(Scene Graph)来进行渲染。通过使用OpenGL ES、OpenGL、Vulkan、Metal或Direct 3D等图形API,Qt Quick 2可以有效地优化图形渲染过程。使用场景图而不是传统的命令式绘制系统(QPainter和类似的)意味着在帧之间可以保留要渲染的场景,并且可以在开始渲染之前确定需要绘制的所有基本形状。这为一些优化提供了机会,例如批量渲染以最小化状态更改并删除被遮挡的几何体。

例如,假设用户界面包含一个包含十个项目的列表,每个项目都有背景颜色、图标和文本。使用传统的绘图技术,这将导致30次绘制调用和相似数量的状态更改。而场景图则可以重新整理要渲染的原语,例如先绘制所有背景,然后所有图标,最后所有文本,将绘制调用总数减少到仅为3次。像这样的批处理和状态更改减少可以大大提高某些硬件的性能。

场景图与Qt Quick 2.0紧密相关,无法单独使用。场景图由QQuickWindow类管理和渲染,并可以通过调用QQuickItem::updatePaintNode()将自定义项目类型的图形原语添加到场景图中。

场景图是Item场景的图形表示,是一个独立的结构,包含渲染所有项目所需的所有信息。一旦设置好,可以独立于项目状态进行操作和渲染。在许多平台上,场景图甚至会在专用的渲染线程上进行渲染,而GUI线程则准备下一帧的状态。

Qt Quick Scene Graph Structure

场景图是由一系列预定义的节点类型组成,每个节点都用于特定的目的。尽管我们称之为“场景图”,但更精确的定义是节点树。树由QML场景中的QQuickItem类型的对象构建,并通过内部渲染器处理该场景。节点本身不包含任何活跃的绘制代码或虚拟的paint()函数。虽然大部分节点树是由现有Qt Quick QML类型在内部构建的,但用户还可以添加自己的内容,包括3D模型的子树。

节点
对于用户来说最重要的节点是QSGGeometryNode。它用于通过定义其几何形状和材质来定义自定义图形。几何形状使用QSGGeometry来定义,并描述了图形原语的形状或网格。它可以是一条线、一个矩形、一个多边形、许多不相连的矩形或复杂的3D网格。材质定义了如何填充该形状中的像素。一个节点可以有任意数量的子节点,几何节点将被渲染以使它们按照孩子的顺序呈现,父节点在其子节点的后面。

可用的节点包括:
QSGClipNode:在场景图中实现裁剪功能
QSGGeometryNode:用于场景图中所有渲染内容
QSGNode:场景图中所有节点的基类
QSGOpacityNode:用于更改节点的不透明度
QSGTransformNode:在场景图中实现变换功能

通过继承 QQuickItem::updatePaintNode() 并设置 QQuickItem::ItemHasContents 标志,可以将自定义节点添加到场景图中。
警告:必须确保原生图形(OpenGL、Vulkan、Metal 等)操作和与场景图的交互仅在渲染线程上进行,主要是在 updatePaintNode() 调用过程中。准则是在 QQuickItem::updatePaintNode() 函数中只使用带有“QSG”前缀的类。

预处理
在QQuickItem类中,更新PaintNode()函数是用来绘制图形的。这个函数应该只使用与“QSG”相关的类(例如QSGGeometry、QSGFragmentShader等)来进行操作和交互。这是因为只有在渲染线程上执行这些操作时才能保证性能的最佳效果。
在QQuickItem类中,预处理是通过QSGNode::preprocess()函数实现的。这个函数会在图形被绘制之前调用。节点子类可以设置QSGNode::UsePreprocess标志并重写QSGNode::preprocess()函数来进行最终准备工作。例如,将贝塞尔曲线划分为当前缩放因子下的正确级别细节或更新纹理的一部分。

节点所有权
节点的所有权可以通过创建者或场景图设置QSGNode::OwnedByParent来进行。将所有权分配给场景图通常更方便,因为它简化了在场景图位于GUI线程之外时的清理工作。

素材(Materials)

素材描述了QSGGeometryNode中几何体的内部填充方式。它封装图形着色器,用于图形管道的顶点和片段阶段,并提供了丰富的灵活性,尽管大多数Qt Quick项目本身仅使用非常基本的材质,如固体颜色和纹理填充。对于希望直接在QML上应用自定义着色到某个QML Item类型的用户来说,可以使用ShaderEffect类型来实现这一点。
以下是所有材料类的完整列表:

QSGFlatColorMaterial:方便地在场景图中呈现固体颜色的几何体。
QSGMaterial:封装渲染状态以供一个着色器程序使用。
QSGMaterialShader:表示一个图形API无关的着色器程序。
QSGMaterialType:与QSGMaterial一起用作唯一类型标记。
QSGOpaqueTextureMaterial:方便地在场景图中呈现纹理化的几何体。
QSGTextureMaterial:方便地在场景图中呈现纹理化的几何体。
QSGVertexColorMaterial:方便地在场景图中呈现每个顶点颜色的几何体

便捷节点
场景图API是低级的,侧重于性能而非便利性。从头开始编写自定义几何体和材质,即使是最基本的,也需要相当数量的代码。因此,该API包括一些便捷类,使最常见的自定义节点能够方便地使用。
QSGSimpleRectNode - QSGGeometryNode 的子类,定义一个具有固体颜色材质的矩形几何。
QSGSimpleTextureNode - QSGGeometryNode 的子类,定义一个具有纹理材质的矩形几何。

场景图(Scene Graph)和渲染

场景图的渲染在 QQuickWindow 类内部进行,没有公开的 API 来访问它。然而,在渲染管道中有一些地方用户可以附加应用程序代码。这可以用于添加自定义的场景图内容,或通过直接调用场景图使用的图形 API(如 OpenGL、Vulkan、Metal 等)来插入任意的渲染命令。集成点由渲染循环定义。有两种可用的渲染循环变体:basic 和 threaded.basic 是单线程的,而 threaded 在专用线程上执行场景图渲染。Qt 会根据平台和可能使用的图形驱动程序选择合适的循环。当这不令人满意,或者出于测试目的时,可以使用环境变量 QSG_RENDER_LOOP 来强制使用给定循环。要验证正在使用哪种渲染循环,请启用 qt.scenegraph.general 日志类别。

线程化渲染循环 (‘threaded’)
在许多配置中,场景图渲染将在专用的渲染线程上进行。这样做是为了增加多核处理器的并行性,并更好地利用等待阻塞交换缓冲调用等停滞时间。这提供了显着的性能改进,但对场景图的交互发生的位置和时间施加了一定的限制。以下是使用线程化渲染循环和 OpenGL 渲染帧的简单概述。除了 OpenGL 上下文特定的细节外,其他图形 API 的步骤也是相同的。
在这里插入图片描述

1、在 QML 场景中发生变化,导致调用 QQuickItem::update()。这可能是动画或用户输入等的结果。然后,会向渲染线程发布一个事件以启动新的帧。
2、渲染线程准备绘制新帧并在 GUI 线程上发起阻塞。
3、在渲染线程准备新帧的同时,GUI 线程调用 QQuickItem::updatePolish() 对项目进行最终的润色操作,以便在它们被渲染前完成。
4、GUI 线程被阻塞。
5、当发射 QQuickWindow::beforeSynchronizing() 信号时,应用程序可以直接连接(使用 Qt::DirectConnection)到该信号,以便在调用 QQuickItem::updatePaintNode() 之前进行任何必要的准备工作。
6、将QML状态同步到场景图中。这是通过在所有自上一帧以来发生更改的项目上调用QQuickItem::updatePaintNode()函数来完成的。这是QML项目和场景图节点进行交互的唯一时机。
7、GUI线程阻塞被释放。
8、场景图被渲染。
1、QQuickWindow::beforeRendering() 信号被发射。应用程序可以直接连接(使用 Qt::DirectConnection)到这个信号,以便使用自定义的图形 API 调用,这些调用将在 QML 场景之下堆叠显示。
2、已指定了 QSGNode::UsePreprocess 的项将调用其 QSGNode::preprocess() 函数。
3、渲染程序处理节点。
4、渲染程序生成状态并记录用于所用图形 API 的绘制调用。
5、QQuickWindow::afterRendering() 信号被发射。应用程序可以直接连接(使用 Qt::DirectConnection)到这个信号,以发出自定义的图形 API 调用,这些调用将在 QML 场景之上堆叠显示。
6、帧现在已准备就绪。缓冲区被交换(OpenGL),或者提交一个呈现命令并将命令缓冲区提交给图形队列(Vulkan、Metal)。QQuickWindow::frameSwapped() 被发射。
9、在渲染线程进行渲染的同时,GUI 可以自由进行动画推进、处理事件等操作。
默认情况下,线程化渲染器当前在以下平台上被使用:Windows 上使用 Direct3D 11 和 opengl32.dll 的 OpenGL,Linux(不包括 Mesa llvmpipe),macOS 上使用 Metal,移动平台,以及 Embedded Linux 上使用 EGLFS。同时,在使用 Vulkan 的情况下,无论平台如何都会使用线程化渲染器。这一切在未来的发布版本中可能会发生变化。用户始终可以设置环境变量 QSG_RENDER_LOOP=threaded 来强制使用线程化渲染器。

非线程化渲染循环(‘basic’)
非线程化渲染循环目前默认在以下平台上被使用:Windows 上使用 OpenGL 但不使用系统的标准 opengl32.dll,macOS 上使用 OpenGL,以及某些 Linux 驱动程序。对于后者,这主要是一种预防性措施,因为并非所有 OpenGL 驱动程序和窗口系统的组合都经过测试。
在 macOS 和使用 OpenGL 的情况下,当使用 XCode 10(10.14 SDK)或更高版本构建时,不支持线程化渲染循环,因为这会选择在 macOS 10.14 上使用基于层的视图。您可以使用 Xcode 9(10.13 SDK)进行构建以取消使用层次化,这样线程化渲染循环将可用并且默认使用。Metal 方面没有这样的限制。
即使使用非线程化渲染循环,您也应该编写您的代码,就好像您正在使用线程化渲染器一样,因为否则代码将不具备可移植性。
以下是非线程化渲染器中帧渲染序列的简化示意图:

Driving Animations

在上述图表中,Advance Animations 指的是动画的演进过程。它描述了如何将动画从一个帧到下一个帧进行转换的过程。
默认情况下,Qt Quick动画(如NumberAnimation)由默认的动画驱动程序驱动。这依赖于基本的系统计时器,比如QObject::startTimer()。计时器通常以16毫秒的间隔运行。虽然这永远不会完全准确,而且也取决于底层平台计时器的准确性,但它的优点是独立于渲染。它提供了统一的结果,无论显示刷新率如何,以及是否活动与显示的垂直同步。这就是动画与基本渲染循环的工作方式。

为了提供更准确的结果,并减少屏幕上的卡顿,独立于渲染循环设计(无论是单线程还是多线程),渲染循环可以决定安装自己的自定义动画驱动程序,并掌握推进它的操作,而不依赖于计时器。

这就是线程化渲染循环实现的方式。事实上,它不仅安装了一个动画驱动程序:一个在gui线程上(用于驱动常规动画,例如NumberAnimation),一个在渲染线程上(用于驱动渲染线程动画,即Animator类型,如OpacityAnimator或XAnimator)。两者都在帧准备期间推进,即动画现在与渲染同步。这是因为基础图形堆栈将显示的呈现节奏控制在与垂直同步相匹配的位置。

因此,在上面的线程化渲染循环图中,两个线程上都有明确的“推进动画”步骤。对于渲染线程,这是微不足道的:由于该线程被节流到垂直同步,像16.67毫秒已经过去了这样的操作给出的结果比依赖系统计时器更准确。(当被节流到垂直同步定时时,这是有道理的,因为从上一帧开始执行相同操作已经大致这么长时间了)
相同的方法也适用于gui(主)线程上的动画:由于gui和渲染线程之间的数据的基本同步,gui线程实际上被节流到与渲染线程相同的速率,同时仍然具有更少的任务要做,为应用程序逻辑留出更多空间,因为许多渲染准备工作现在已经转移到了渲染线程。
尽管上面的例子使用了每秒60帧,但Qt Quick也准备了其他刷新率:刷新率从QScreen和平台查询。例如,使用144赫兹屏幕的间隔为6.94毫秒。同时,这也是如果垂直同步节流没有按预期运行会引起麻烦的原因,因为如果渲染循环认为发生的事情与现实不匹配,将发生不正确的动画节奏。
注意:从Qt 6.5开始,线程化渲染循环提供了选择另一个动画驱动程序的可能性,仅基于经过的时间(QElapsedTimer)。要启用此功能,请将QSG_USE_SIMPLE_ANIMATION_DRIVER环境变量设置为非零值。这样做的好处是不需要任何基础设施来回退到QTimer(当存在多个窗口时),也不需要试图确定基于垂直同步的节流是否缺失或损坏的启发式算法,与基于垂直同步的节流中的任何时间漂移兼容,并且不受主屏幕刷新率的限制,因此在多屏设置中可能工作得更好。即使基于垂直同步的节流被破坏或禁用,它也可以正确地驱动渲染线程动画(Animator类型)。另一方面,利用这种方法,动画可能被认为不太平滑。考虑到兼容性,暂时将其作为选择功能提供。
总之,线程化渲染循环预计在满足以下条件的情况下将提供更顺畅、更少抖动的动画效果:
1、屏幕上只有一个窗口(例如QQuickWindow)。
2、基于垂直同步的节流与底层的图形和显示堆栈正常工作。

如果屏幕上没有窗口或有更多于一个可见的窗口,线程化渲染循环可能会导致动画效果不稳定。这是因为在多窗口情况下,每个窗口都需要单独处理和绘制,这可能导致资源竞争、性能下降等问题。因此,为了获得最佳的动画效果,建议确保只有一个窗口在屏幕上并保持其可见性。
当没有可渲染的窗口时,例如因为我们的QQuickWindow被最小化(Windows)或完全被遮挡(macOS),我们无法呈现帧,因此无法依赖线程与屏幕刷新率同步地“运行”。在这种情况下,线程化渲染循环会自动切换到基于系统定时器的方法来推动动画,即暂时转换为基本循环所使用的机制。
当屏幕上出现多个QQuickWindow实例时也是同样的情况。之前提出的用于在GUI线程上推进动画的模型,由于其与渲染线程的同步,不再令人满意,因为现在有多个同步点和多个渲染线程(每个窗口一个)。在这种情况下,也会需要借助系统定时器为基础的方法,因为GUI线程的阻塞时间和频率现在取决于多个因素,包括窗口中的内容(是否在执行动画?更新频率如何?)以及图形堆栈的行为(它如何处理两个或更多个线程进行等待垂直同步?)。由于我们无法稳定且跨平台地保证被节流到窗口的显示速率(首先应该是哪个窗口?),无法基于渲染来推进动画。
这种动画处理机制的切换对应用程序是透明的。

如果基于垂直同步的节流不起作用,全局禁用,或应用程序自身将其禁用呢?
线程渲染循环依赖于图形API实现和/或窗口系统来进行频率限制,例如,在OpenGL(GLX、EGL、WGL)中请求交换间隔为1,对于Direct3D调用带有间隔为1的Present(),或在Vulkan中使用基于FIFO的呈现模式。
一些图形驱动程序允许用户覆盖此设置并将其关闭,忽略Qt的请求。一个例子是图形驱动程序的系统范围控制面板允许覆盖应用程序关于垂直同步的设定。也可能出现图形堆栈无法提供适当基于垂直同步的节流的情况,这在一些虚拟机中可能会发生(主要是由于使用基于软件光栅化的OpenGL或Vulkan实现)。
在交换/呈现操作中没有阻塞(或其他图形操作)的情况下,这样的渲染循环会使动画速度过快。这对基本的呈现循环没有问题,因为它总是依赖于系统定时器。但在使用线程渲染循环时,行为可能会因Qt版本而异。
在Qt 6.4之前,如果已知系统无法提供基于垂直同步的节流,唯一的选择是在运行应用程序之前在环境中手动设置QSG_RENDER_LOOP=basic,以使用基本的渲染循环。
从Qt 6.4开始,设置QSG_NO_VSYNC环境变量为非零值,或将窗口的QSurfaceFormat::swapInterval()设置为0,都可以缓解这个问题:通过明确请求禁用基于垂直同步的阻塞,无论实际情况下该请求是否生效,线程渲染循环都会顺理成章地意识到依赖垂直同步驱动动画是徒劳无益,它将退回到使用系统定时器,就像对于多个窗口一样。
从Qt 6.4开始,场景图也尝试通过一些简单的启发式算法来识别呈现的帧速度“过快”,并在必要时自动切换到系统定时器。这意味着在大多数情况下不需要做任何事情,应用程序将按预期运行动画,即使默认的渲染循环是线程化的。虽然应用程序对此是透明的,但出于故障排除和开发目的,了解到启用了QSG_INFO或qt.scenegraph.general时,会输出打印的“Window 0x7ffc8489c3d0 is determined to have broken vsync throttling …”消息很有用。这种方法的缺点是在一小部分帧后才会触发,因为其首先需要收集数据进行评估,这意味着当打开一个QQuickWindow时,应用程序可能仍会显示过快的动画一小段时间。此外,它可能无法捕捉所有可能的垂直同步中断情况。
然而,需要注意的是,这些方法仅适用于渲染线程的普通动画(Animator类型)。在没有基于垂直同步的阻塞的情况下,默认情况下,动画将以错误的方式进行,比预期快得多,即使工作区激活了常规动画的工作区。如果这个问题成为问题,请考虑使用替代动画驱动器,通过设置QSG_USE_SIMPLE_ANIMATION_DRIVER来启用它。
请注意,即使禁用等待vsync,渲染循环逻辑和GUI(主)线程上的事件处理仍然可能受到限制:通过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子类,在场景图中发出与场景图自己的渲染相同的绘制调用。这与第一种方法类似,但自定义绘制调用实际上被注入到场景图的命令流中。

“underlay”或“overlay”模式
“underlay”模式将应用程序提供的图形命令作为底层内容呈现给用户。这意味着这些命令会在场景图的绘制过程中被渲染到屏幕上,并与场景图中的其他内容一起显示。这种模式通常用于在场景图中添加额外的内容或效果,例如贴图、阴影等。
“overlay”模式将应用程序提供的图形命令作为覆盖层呈现给用户。这意味着这些命令会在场景图的绘制过程中被渲染到屏幕上,并覆盖场景图中的其他内容。这种模式通常用于在场景图中显示特定的信息或交互元素,如按钮、文本框等。
请注意,使用“underlay”或“overlay”模式时,您需要确保应用程序提供的图形命令与场景图的绘制风格和效果相容。例如,如果应用程序提供了透明的图形内容,则可能需要在场景图中调整其显示位置以避免覆盖其他内容。
通过连接到QQuickWindow::beforeRendering()和QQuickWindow::afterRendering()信号,应用程序可以直接在与场景图渲染到同一上下文的QRhi或本机3D API中进行调用。使用像Vulkan或Metal这样的API,应用程序可以通过QSGRendererInterface查询本机对象,例如场景图的命令缓冲区,并根据需要记录命令到其中。正如信号名称所示,用户可以在Qt Quick场景下或上渲染内容。通过这种方式集成的好处是不需要额外的渲染目标来执行渲染,并且可能昂贵的纹理步骤被消除。缺点是自定义渲染只能在Qt Quick自己渲染的开头或结尾时执行。使用QSGRenderNode而不是QQuickWindow信号可以在一定程度上解除这种限制,但无论哪种情况,当涉及3D内容和深度缓冲区使用时,都必须小心,因为依赖深度测试和启用深度写入进行渲染可能很容易导致自定义内容和Qt Quick内容的深度缓冲区使用发生冲突。
从Qt 6.6版本开始,QRhi API 被认为是半公开的,即向应用程序提供并进行了文档化,尽管其兼容性保证有限。这允许通过使用与场景图本身使用的相同的图形和着色器抽象来创建可移植的、跨平台的2D/3D渲染代码。
Scene Graph - RHI Under QML示例提供了如何使用QRhi实现底层/覆盖层的方法的示例。
Scene Graph - OpenGL Under QML示例展示了如何使用这些信号和OpenGL。
Scene Graph - Direct3D 11 Under QML示例展示了如何使用这些信号和Direct3D。
Scene Graph - Metal Under QML示例展示了如何使用这些信号和Metal。
Scene Graph - Vulkan Under QML示例展示了如何使用这些信号和Vulkan。
从Qt 6.0开始,必须在直接使用底层图形API的代码块中加上QQuickWindow::beginExternalCommands()和QQuickWindow::endExternalCommands()的调用。这个概念可能与QPainter::beginNativePainting()类似,其目的是相似的:它允许Qt Quick场景图识别到当前记录的渲染过程中任何缓存的状态和假设都已经无效,因为应用程序代码可能已经通过直接使用底层图形API来修改它。在使用QRhi时,这个概念是不适用和不必要的。
当将自定义的OpenGL渲染与场景图混合时,重要的是应用程序不应该让OpenGL上下文处于状态不明的情况,例如绑定了缓冲区,启用了属性,z-buffer或stencil-buffer中有特殊值等。这样做可能会导致行为不可预测。
自定义渲染代码必须具备线程意识,即不应该假设自己在应用程序的GUI (主)线程上执行。当连接到QQuickWindow信号时,应用程序应该使用Qt::DirectConnection,并理解连接的槽会在场景图的专用渲染线程上被调用(如果存在的话)。

基于纹理的方法

基于纹理的替代方法是当应用程序需要在Qt Quick场景中具有“扁平化”,2D图像的一些自定义3D渲染时最灵活的方法。这还允许使用一个独立于主渲染通道使用的专用深度/模板缓冲区。
在使用OpenGL时,可以使用传统的便利类QQuickFramebufferObject来实现这一点。基于QRhi的自定义渲染器和除OpenGL之外的其他图形API也可以遵循这种方法,尽管QQuickFramebufferObject目前不支持它们。直接使用底层API创建和渲染纹理,然后在自定义QQuickItem中包装和使用这个资源在Qt Quick场景中的示例如下:
场景图 - RHI纹理项目示例。
场景图 - Vulkan纹理导入示例。
场景图 - Metal纹理导入示例。

内联方法
使用QSGRenderNode时,自定义绘图调用不会注入在场景图渲染过程的开头或结尾,而是在场景图的渲染过程中进行。这是通过创建一个基于QSGRenderNode实例的自定义QQuickItem实现的,QSGRenderNode是一个专门存在来允许通过QRhi或本地3D API(如OpenGL,Vulkan,Metal或Direct 3D)发出绘图命令的场景图节点。
Scene Graph - Custom QSGRenderNode示例演示了这种方法。

使用QPainter创建自定义项
QQuickItem提供了一个子类QQuickPaintedItem,允许用户使用QPainter来渲染内容。警告:使用QQuickPaintedItem会使用间接的2D表面来渲染其内容,可能使用软件光栅化或使用OpenGL帧缓冲对象(FBO),因此渲染是一个两步操作。首先光栅化表面,然后绘制表面。直接使用场景图API始终更快。

日志支持

场景图支持多个日志类别。这些日志对于追踪性能问题和错误。
qt.scenegraph.time.texture - 记录用于纹理上传的时间
qt.scenegraph.time.compilation - 记录用于着色器编译的时间
qt.scenegraph.time.renderer - 记录渲染器各个步骤中的时间
qt.scenegraph.time.renderloop - 记录渲染循环各个步骤中的时间。使用线程渲染循环可以了解GUI线程和渲染线程之间不同帧准备步骤之间经过的时间。因此,这也可以是一个有用的故障排除工具,例如,确认基于垂直同步的节流以及其他低级的Qt启用器,例如QWindow::requestUpdate(),如何影响渲染和呈现流水线。
qt.scenegraph.time.glyph - 记录准备距离场字形的时间
qt.scenegraph.general - 记录有关场景图和图形堆栈各个部分的一般信息
qt.scenegraph.renderloop - 创建一个详细的日志,记录渲染中涉及的各个阶段。这种日志模式主要对
注意:在遇到图形问题时,或者怀疑正在使用的渲染循环或图形API时,请始终使用至少qt.scenegraph.general和qt.rhi.*启用,或者设置QSG_INFO=1。这将在初始化期间打印一些基本信息到调试输出中。

场景图后端

除了公共API之外,场景图还具有一个适配层,该层打开了实现硬件特定适配的可能性。这是一个未记录、内部和私有的插件API,允许硬件适配团队充分利用其硬件。其中包括:
自定义纹理;具体讲就是实现QQuickWindow::createTextureFromImage和Image及BorderImage类型所使用的纹理的内部表示。
自定义渲染器;适配层让插件决定如何遍历和渲染场景图,从而使优化渲染算法以适配特定硬件或利用改进性能的扩展变得可能。
许多默认QML类型的自定义场景图实现,包括其文本和字体呈现。
自定义动画驱动程序;允许动画系统连接到底层显示垂直刷新,实现平滑渲染。
自定义渲染循环;允许更好地控制QML如何处理多个窗口。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/599561.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

1688工厂货源API接口:用于商品采集、商品搜索、商品详情数据抓取

item_get 获得1688商品详情item_search 按关键字搜索商品item_search_img 按图搜索1688商品(拍立淘)item_search_suggest 获得搜索词推荐item_fee 获得商品快递费用seller_info 获得店铺详情item_search_shop 获得店铺的所有商品item_password 获得淘口令…

在拥有多个同名称密码的ap环境中,如何连接到指定信道或mac的ap路由器?

在给客户做ESP32-C3入墙开关项目时,客户问:在拥有多个同名称密码的ap环境中,如何连接到指定信道或mac的ap路由器?针对这个问题,启明云端工程师给出下面解决方法。 1、将wifi_sta_config_t配置中的channel配置为该信道…

神经网络怎么把隐含层变量融合到损失函数中?

🏆本文收录于「Bug调优」专栏,主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&…

matplotlib和pandas与numpy

1.matplotlib介绍 一个2D绘图库; 2.Pandas介绍: Pandas一个分析结构化数据的工具; 3.NumPy 一个处理n纬数组的包; 4.实践:绘图matplotlip figure()生成一个图像实例 %matplotlib inline:图形直接在…

前端传递list(数组)类型参数,后端接收失败

一顿报错,我之前遇到的list都是Long类型 貌似用GET也是可以的,但是很奇怪一直报错 就是不可以 后来去百度 查询到可以用两种方法解决这个问题 1、拆开 传 以GET方式,后端GetMappingRequestParam接收。 2、以Post方式传,后端创建dto PostMappingReques…

elementUI table表格相同元素合并行----支持多列

效果图如下: vue2代码如下: 只粘贴了js方法哦, methods: {// 设置合并行 setrowspans() { const columns [‘name’, ‘value’]; // 需要合并的列名 // 为每个需要合并的列设置默认 rowspan this.tableData.forEach(row > { columns.forEach(col …

光电探测器性能指标测试

光电探测器的三个核心指标: 带宽,转换增益,噪声(信噪比,NEP,噪声密度) 测试环境:可调谐激光器(CW LASER),强度调制器(AM),信号发生器(AWG),可调衰…

【算法】滑动窗口——最大连续1的个数

本篇文章讲的是“最大连续1的个数”这道题,从最开始的简单暴力到用滑动窗口算法实现解题的思路历程,有需要借鉴即可。 目录 1.题目2.暴力求解3.滑动窗口解法3.1优化一:end重返start优化,end指针不回退3.2优化二:某一st…

Day_2

1. 菜品管理 新增菜品 接口设计 1. 根据类型查询分类(分类管理已完成) 查看接口文档即可 2. 文件上传 创建Bucket 采用的是阿里云的OSS对象存储服务 新增AccessKey 3. 菜品的新增逻辑 代码开发 1. 文件上传接口开发 为了提高代码的解耦性&#…

Java_方法引用

方法引用就是把已经有的方法拿过来用,当作函数式接口中抽象方法的方法体。 条件: 1.引用处需要是函数式接口 2.被引用的方法需要已经存在 3.被引用的方法的形参和返回值需要跟抽象方法的形参和返回值保持一致 4.被引用方法的功能需要满足当前的要求 简…

ATA-2161高压放大器用途有哪些种类

高压放大器是一种电子设备,其主要功能是将输入信号放大到较高的电压水平,同时保持信号的形状和特性。这种设备在各种应用领域中都有重要作用,它的种类繁多,根据不同的用途可以分为多种类型。 1.医学领域 在医学设备中,…

搭建Harbor仓库

文章目录 Harbor仓库搭建Harbor仓库安装 docker 服务修改配置文件 Harbor仓库 搭建Harbor仓库 下载 Harbor 仓库 安装 docker 服务 # step 1: 安装必要的一些系统工具 yum install -y yum-utils device-mapper-persistent-data lvm2 # Step 2: 添加软件源信息 yum-config-m…

notepad++安装 hex-editor插件

打开notepad 点击插件 搜索 hex-editor,点击右侧 安装install 安装成功后,在已安装插件中就有显示了

Java性能优化(五)-多线程调优-Lock同步锁的优化

作者主页: 🔗进朱者赤的博客 精选专栏:🔗经典算法 作者简介:阿里非典型程序员一枚 ,记录在大厂的打怪升级之路。 一起学习Java、大数据、数据结构算法(公众号同名) ❤️觉得文章还…

《QT实用小工具·五十九》随机图形验证码,带有一些可人的交互与动画

1、概述 源码放在文章末尾 该项目实现了可交互的动画验证码控件,趣味性十足: 字符变换动画 噪音动画 可拖动交互 项目demo演示如下所示: 项目部分代码如下所示: #ifndef CAPTCHAMOVABLELABEL_H #define CAPTCHAMOVABLELABEL…

【影片欣赏】【指环王】【魔戒:护戒使者 The Lord of the Rings: The Fellowship of the Ring】

2001年发行,Extended DVD Edition Part One 1. Prologue: One Ring to Rule Them All… 2. Concerning Hobbits 3. The Shire 4. Very Old Friends 5. A Long-expected Party 6. Farewell Dear Bilbo 7. Keep It Secret, Keep It Safe 8. The Account of Isildur 9…

MyBatis入门例子

1、建立与数据库对应的POJO类 2、建立mybatis的配置文件 修改后如下: 3、创建POJO对象和Mysql数据的表之间的映射配置 4、建一个测试方法 实现从数据库中取数一条数据,封装成User对象返回 注意点: 这点,大家应该不陌生了&#x…

28-代码随想录18四数之和

18. 四数之和 给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复)&#xff…

小米手机miui14 android chrome如何取消网页自动打开app

搜索媒体打开应用 选择你要阻止打开的app,以github为例 取消勾选打开支持的链接。 参考:https://www.reddit.com/r/chrome/s/JBsGkZDkRZ

【进程终止】退出信号 | 三种退出情况 | 如何进程终止returnexit_exit

目录 退出码 退出信号 进程终止情况3 如何进程终止 return退出 库函数exit 系统调用函数_exit ​exit和_exit的区别缓冲区 exit _exit 退出码 回顾上篇 代码跑完,结果正确(退出码为0)代码跑完,结果不正确(退…