【Qt QML】Qt Quick Scene Graph

server/2024/11/15 4:46:40/

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如何处理多个窗口。


http://www.ppmy.cn/server/36820.html

相关文章

卸载系统自带APP

Firefly RK3588 android 12自动多个系统软件,无法从UI界面进行手动删除。因此,考虑使用shell指令进行处理。 系统自动APP大多都安装在system/app目录下,且该目录多为只读。因此采用如下步骤, //Shell su adb shell su //重新挂载…

一文了解什么是SSL证书?——值得收藏

SSL证书,全称Secure Sockets Layer证书,是一种网络安全协议的实现方式,现在通常指的是其继任者TLS(Transport Layer Security)证书,不过习惯上仍称为SSL证书。它的主要作用是确保互联网上的数据传输安全&am…

STM32F4xx开发学习_SysTick

SysTick系统定时器 SysTick属于CM4内核外设,有关寄存器的定义和部分库函数都在core_cm4.h这个头文件中实现,可用于操作系统,提供必要的时钟节拍 SysTick简介 SysTick是一个 24 位向下定时器,属于CM4内核中的一个外设,…

机器学习实践:超市商品购买关联规则分析

第2关:动手实现Apriori算法 任务描述 本关任务:编写 Python 代码实现 Apriori 算法。 相关知识 为了完成本关任务,你需要掌握 Apriori 算法流程。 Apriori 算法流程 Apriori 算法的两个输人参数分别是最小支持度和数据集。该算法首先会生成所…

Flutter分模块开发、模块可单独启动、包含Provider

前言 当前案例 Flutter SDK版本:3.13.2 目前Flutter都是在一个项目中,创建不同目录进行模块开发,我进行Android原生开发时,发现原生端,是可以将每个模块独立运行起来的,灵感来自这; 折腾了几…

Node 【切换commonjs模块和esm模块】【配置esm模块规范的坑】

文章目录 前言一、在commonjs规范中使用esm模块二、在esm规范中使用commonjs模块三、在node中使用esm规范的坑 前言 node有两种模块规范 一种是以export导出,import引入的esm模块规范。即.js文件会被视为es6模块,采取解析一种是以module.exports导出,require引入的…

C语言——每日一题(轮转数组)

一.前言 前不久学习了时间复杂度的概念,便在力扣上刷了一道需要参考时间复杂度的题——轮转数组 https://leetcode.cn/problems/rotate-array/submissions这道题不能使用暴力算法,因为这道题对时间复杂度的要求不能为O(N^2)。因…

【负载均衡在线OJ项目日记】项目简介

目录 前言 什么是负载均衡 所用的技术和开发环境 所用技术 开发环境 项目的宏观结构 leetcode 结构 结构 编写思路 前言 从C语言的文章到现在Linux网络部分,我已经涉猎了很多知识;终于在今天我要开始搞项目了,通过项目我也可以开始…