- class QSGRenderNode#
QSGRenderNode
类表示针对场景图所使用的图形 API 的一些自定义渲染命令。...概要
方法
def
__init__()
def
clipList()
def
commandBuffer()
def
matrix()
def
renderTarget()
虚函数
def
changedStates()
def
prepare()
def
rect()
def
render()
注意
本文档可能包含自动从 C++ 转换为 Python 的代码片段。我们始终欢迎对代码片段翻译的贡献。如果您发现翻译问题,您也可以通过在 https:/bugreports.qt.io/projects/PYSIDE 创建工单的方式来告知我们。
详细描述
QSGRenderNode
允许创建 scene graph 节点,这些节点通过 QRhi(从 Qt 6.6开始的常用方法),直接通过 3D 图形 API,例如 OpenGL、Vulkan 或 Metal 进行自定义渲染,或者在software
后端使用时,通过 QPainter 进行渲染。QSGRenderNode
是将自定义 2D/3D 渲染集成到 Qt Quick 场景的三个方法之一的关键。其他两种选项是执行渲染操作在 Qt Quick 场景自己的渲染之前或之后,或者是生成一个针对专用渲染目标(纹理)的单独渲染通道,然后让场景中的一个项目显示这个纹理。基于QSGRenderNode
的方法类似于前者,因为不涉及额外的渲染通道或渲染目标,并允许在 Qt Quick 场景自己的渲染“inline”中注入自定义渲染命令。参见
场景图 - 自定义 QSGRenderNode
- class StateFlag#
(继承自
enum.Flag
) 这个枚举是一个位掩码,标识了几个状态。常量
描述
QSGRenderNode.DepthState
深度
QSGRenderNode.StencilState
模板
QSGRenderNode.ScissorState
剪裁
QSGRenderNode.ColorState
颜色
QSGRenderNode.BlendState
混合
QSGRenderNode.CullState
消除
QSGRenderNode.ViewportState
视点
QSGRenderNode.RenderTargetState
渲染目标
- class RenderingFlag#
(继承自
enum.Flag
) 表示从flags()
返回的位掩码的可能值。常量
描述
QSGRenderNode.BoundedRectRendering
表示
render()
的实现不会渲染超出从rect()
报告的区域。这样的节点实现可能导致更有效的渲染,具体取决于场景图后端。例如,软件后端可以在场景中所有渲染节点设置此标志时继续使用更优的局部更新路径。QSGRenderNode.DepthAwareRendering
表示
render()
的实现根据场景图的期望只生成场景坐标中为 0 的 Z 值,然后通过从projectionMatrix()
和matrix()
获取的矩阵转换,正如render()
的说明中所述。这样的节点实现可能导致更有效的渲染,具体取决于场景图后端。例如,批处理 OpenGL 渲染器可以在场景中所有渲染节点设置此标志时继续使用更优的路径。QSGRenderNode.OpaqueRendering
表示从
rect()
返回的整个区域,render()
实现将不透明像素写入。默认情况下,渲染器必须假设render()
也可以输出半透明或完全透明的像素。设置此标志在某些情况下可以提升性能。QSGRenderNode.NoExternalRendering
表示
prepare()
和render()
的实现使用 QRhi 系列API,而不是直接调用 OpenGL、Vulkan 或 Metal 等三维API。
- __init__()#
当底层渲染API是OpenGL时,该函数应返回一个掩码,其中每个位代表由
render()
函数修改的图形状态深度状态
深度写入掩码、深度测试启用、深度比较函数
除OpenGL以外的API中,只有相关值对应于记录在命令列表/缓冲区上的动态状态更改。例如,在D3D11中,RSSetViewports、RSSetScissorRects、OMSetBlendState、OMSetDepthStencilState等情况;在Vulkan中,vkCmdSetViewport、vkCmdSetScissor、vkCmdSetBlendConstants、vkCmdSetStencilRef等情况,且只在将这些命令添加到通过QSGRendererInterface::CommandList资源枚举查询的场景图命令列表时。在管道状态对象中设置的状态不需要在此报告。同样,与绘制调用相关的设置(管道状态、描述符集、顶点或索引缓冲区绑定、根签名、描述符堆等)总是由场景图重新设置,
render()
可以自由地修改这些设置。使用API如Vulkan时,
RenderTargetState
已不再受支持。这是由于本质上的原因。《a class="reference internal" href="#PySide6.QtQuick.QSGRenderNode.render" title="PySide6.QtQuick.QSGRenderNode.render">render()
在Qt Quick场景图的的主要命令缓冲区正在记录渲染阶段时被调用,因此不可能更改目标并开始另一个渲染阶段(至少是在该命令缓冲区上)。因此,当设置RenderTargetState
并返回一个值是不合逻辑的。注意
软件后端在调用
render()
之前和之后会暴露其QPainter并保存和恢复。因此,从这里报告任何更改的状态是不必要的。该函数由渲染器调用,以便在渲染此节点后重置状态。这使得
render()
的实现更加简单,因为它不必查询和恢复这些状态。默认实现返回0,表示在
render()
中没有更改任何相关状态。注意
此函数可能在调用
render()
之前被调用。注意
在Qt 6和基于QRhi的渲染中,只有相关的值是
ViewportState
和ScissorState
。- clipList()#
- 返回类型:
返回当前的裁剪列表。
- commandBuffer()#
- 返回类型:
QRhiCommandBuffer
返回当前命令缓冲区。
- inheritedOpacity()#
- 返回类型:
float
返回当前的有效不透明度。
- matrix()#
- 返回类型:
返回当前模型视图矩阵的指针。
- prepare()#
在帧准备阶段被调用。在每个调用
render()
之前,都会调用此函数。与
render()
不同,此函数在场景图开始记录当前帧的渲染过程之前在底层命令缓冲区中调用。这在使用图形API(如Vulkan)进行渲染时非常有用,其中复制类型的操作需要在渲染过程之前记录。默认实现为空。
当实现使用QRhi进行渲染的
QSGRenderNode
时,通过QQuickWindow
的rhi()
方法查询QRhi对象。要获取用于提交工作的QRhiCommandBuffer,请调用commandBuffer()
。要查询活动渲染目标的信息,请调用renderTarget()
。有关详细信息,请参阅 {场景图 - 自定义QSGRenderNode} 例子。- projectionMatrix()#
- 返回类型:
返回当前投影矩阵的指针。
在
render()
中,这与从projectionMatrix()
返回的矩阵相同。此getter的存在是为了让prepare()
也有查询投影矩阵的方法。当与现代图形API或Qt自己的图形抽象层一起工作时,很可能需要将
*projectionMatrix() * *matrix()
加载到统一缓冲区中。然而,这需要在prepare()
中完成,这样就可以在渲染过程的记录之外完成。这也是为什么可以从QSGRenderNode
直接查询这两个矩阵的原因,无论是在prepare()
还是render()
中。返回在
render()
作用区域内的项坐标边界矩形。当flags()
包括BoundedRectRendering
时,此值有效,否则被忽略。结合
BoundedRectRendering
报告矩形非常重要,尤其是在软件后端,因为否则在场景中有一个渲染节点将触发全屏更新,跳过所有部分更新优化。对于覆盖相应
QQuickItem
整个区域的渲染节点,返回值将是 (0, 0, item->width(), item->height())。- releaseResources()#
当需要立即释放此节点分配的所有自定义图形资源时,将调用此函数。如果该节点没有通过正在使用的图形API(缓冲区、纹理、渲染目标、fences等)直接分配图形资源,则此处无需执行任何操作。
未能释放所有自定义资源可能导致某些系统在图形设备丢失场景中行为不正确,因为在图形系统的后续重新初始化可能失败。
注意
某些场景图后端可能选择不调用此函数。因此,期望
QSGRenderNode
实现在其析构函数和 releaseResources() 中执行清理。与析构函数不同,期望在调用 releaseResources() 后重新调用
render()
可以重新初始化所有需要的资源。在 OpenGL 中,当调用析构函数和此函数时,场景图的 OpenGL contexts 都将保持当前状态。
- 抽象 render(state)#
- 参数:
state –
RenderState
此函数由渲染器调用,应通过在当前使用的图形API(OpenGL、Direct3D等)中直接调用命令来绘制此节点。
可以通过
inheritedOpacity()
检索有效不透明度。通过
state
获取投影矩阵,可以使用matrix()
获取模型视图矩阵。组合矩阵是投影矩阵乘以模型视图矩阵。场景中项目的正确堆叠由投影矩阵保证。在使用提供的矩阵时,顶点数据的坐标系遵循常见的
QQuickItem
约定:左上角是(0, 0),右下角对应的是QQuickItem
的宽度和高度减一。例如,假设每个顶点坐标由两个浮点数(x-y)组成,一个覆盖项目一半的三角形可以使用逆时针方向指定为(宽度 - 1,高度 - 1),(0,0),(0,高度 - 1)。注意
QSGRenderNode
作为一个实现自定义 2D 或 2.5D Qt Quick 项目的手段提供。它并不适用于将真正的 3D 内容集成到 Qt Quick 场景中。这个用例更适合由QQuickFramebufferObject
,beforeRendering()
或者那些针对 OpenGL 以外的 API 的等效方法来支持。注意
QSGRenderNode
的性能可以显著优于基于纹理的方法(例如,QQuickFramebufferObject
),特别是在片段处理能力有限的系统上。这是因为它避免渲染到纹理然后再绘制带有纹理的四边形。相反,QSGRenderNode
允许在场景图的其它命令的同时记录绘制调用,避免了额外的渲染目标和可能昂贵的纹理化和混合操作。剪裁信息是在函数调用之前计算的。希望考虑剪裁的实现可以根据
state
中的信息设置剪裁或模板。模板缓冲区被填充了必要的剪裁形状,但是否启用模板测试由实现者决定。一些场景图后端,尤其是软件后端,不会使用剪裁或模板。在那里,剪裁区域以普通的 QRegion 提供的。
当实现使用QRhi进行渲染的
QSGRenderNode
时,通过QQuickWindow
的rhi()
方法查询QRhi对象。要获取用于提交工作的QRhiCommandBuffer,请调用commandBuffer()
。要查询活动渲染目标的信息,请调用renderTarget()
。有关详细信息,请参阅 {场景图 - 自定义QSGRenderNode} 例子。在 Qt 6 和其基于 QRhi 的场景图渲染器中,在调用此函数时,不应假设活动的(OpenGL)状态,即使是当使用 OpenGL 时。在调用此函数时,不要对命令列表/缓冲区上绑定 pipelines 和动态状态做出任何假设。
注意
预期深度写入被禁用。启用深度写入可能导致意料之外的结果,具体取决于所使用的场景图后端和场景中的内容,因此在使用时要谨慎。
注意
在 Qt 6 中,
changedStates()
的用途有限。有关changedStates()
的更多信息,请参阅相关文档。在某些图形API中,包括直接使用QRhi时,可能需要重新实现或连接到
beforeRendering()
信号。prepare()
,这会在命令缓冲区上记录渲染通道的开始之前调用/产生。对于Vulkan,是葵CmdBeginRenderPass,对于Metal,是通过MTLRenderCommandEncoder进行编码的开始。使用这些API不能在render()内部记录复制操作。相反,要么在prepare()
中,要么在与beforeRendering(带有DirectConnection)连接的槽处执行这些操作。- renderTarget()#
- 返回类型:
QRhiRenderTarget
返回当前渲染目标。
这主要是为了启用使用QRhi访问QRhiRenderTarget的renderPassDescriptor或像素大小的
prepare()
和render()
实现。要构建QRhiGraphicsPipeline,这意味着需要提供一个QRhiRenderPassDescriptor,可以查询渲染目标中的renderPassDescriptor。然而,要注意的是,渲染目标可能在自定义
QQuickItem
和QSGRenderNode
的生存期内发生变化。例如,考虑当动态设置项或其祖先的layer.enabled:
true
时会发生什么:这会将渲染触发到纹理中,而不是直接到窗口,这意味着从那时起,QSGRenderNode
将使用不同的渲染目标。新的渲染目标可能具有不同的像素格式,这会使已经构建的图形管道不兼容。可以通过以下逻辑来处理这个问题if (m_pipeline && renderTarget()->renderPassDescriptor()->serializedFormat() != m_renderPassFormat) { delete m_pipeline; m_pipeline = nullptr; } if (!m_pipeline) { // Build a new QRhiGraphicsPipeline. // ... // Store the serialized format for fast and simple comparisons later on. m_renderPassFormat = renderTarget()->renderPassDescriptor()->serializedFormat(); }