QRhiSwapChain Class
交换链资源。更多
头文件: | #include <rhi/qrhi.h> |
CMake: | find_package(Qt6 REQUIRED COMPONENTS Gui) target_link_libraries(mytarget PRIVATE Qt6::GuiPrivate) |
qmake: | QT += gui-private |
自 | Qt 6.6 |
继承: | QRhiResource |
公共类型
enum | Flag { SurfaceHasPreMulAlpha, SurfaceHasNonPreMulAlpha, sRGB, UsedAsTransferSource, NoVSync, MinimalBufferCount } |
flags | Flags |
enum | Format { SDR, HDRExtendedSrgbLinear, HDR10, HDRExtendedDisplayP3Linear } |
enum | StereoTargetBuffer { LeftBuffer, RightBuffer } |
公共函数
virtual bool | createOrResize() = 0 |
virtual QRhiCommandBuffer * | currentFrameCommandBuffer() = 0 |
virtual QRhiRenderTarget * | currentFrameRenderTarget() = 0 |
virtual QRhiRenderTarget * | currentFrameRenderTarget(QRhiSwapChain::StereoTargetBuffer targetBuffer) |
QSize | currentPixelSize() const |
QRhiRenderBuffer * | depthStencil() const |
QRhiSwapChain::Flags | flags() const |
QRhiSwapChain::Format | format() const |
virtual QRhiSwapChainHdrInfo | hdrInfo() |
virtual bool | isFormatSupported(QRhiSwapChain::Format f) = 0 |
virtual QRhiRenderPassDescriptor * | newCompatibleRenderPassDescriptor() = 0 |
QRhiSwapChainProxyData | proxyData() const |
QRhiRenderPassDescriptor * | renderPassDescriptor() const |
int | sampleCount() const |
void | setDepthStencil(QRhiRenderBuffer *ds) |
void | setFlags(QRhiSwapChain::Flags f) |
void | setFormat(QRhiSwapChain::Format f) |
void | setProxyData(const QRhiSwapChainProxyData &d) |
void | setRenderPassDescriptor(QRhiRenderPassDescriptor *desc) |
void | setSampleCount(int samples) |
(since 6.9) void | setShadingRateMap(QRhiShadingRateMap *map) |
void | setWindow(QWindow *window) |
(since 6.9) QRhiShadingRateMap * | shadingRateMap() const |
virtual QSize | surfacePixelSize() = 0 |
QWindow * | window() const |
重新实现的公共函数
virtual QRhiResource::Type | resourceType() const override |
详细说明
交换链可将渲染结果呈现给曲面。交换链通常由一组颜色缓冲区支持。每次只显示其中一个。
以下是创建和管理 swapchain 以及一些相关资源的典型模式,以便在QWindow 上进行渲染:
void init() { sc = rhi->newSwapChain(); ds = rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, QSize(), // no need to set the size here due to UsedWithSwapChainOnly 1, QRhiRenderBuffer::UsedWithSwapChainOnly); sc->setWindow(window); sc->setDepthStencil(ds); rp = sc->newCompatibleRenderPassDescriptor(); sc->setRenderPassDescriptor(rp); resizeSwapChain(); } void resizeSwapChain() { hasSwapChain = sc->createOrResize(); } void render() { if (!hasSwapChain || notExposed) return; if (sc->currentPixelSize() != sc->surfacePixelSize() || newlyExposed) { resizeSwapChain(); if (!hasSwapChain) return; newlyExposed = false; } rhi->beginFrame(sc); // ... rhi->endFrame(sc); }
避免依赖QWindow 调整大小事件来调整 swapchain 的大小,特别是考虑到表面尺寸不一定完全符合QWindow 报告的尺寸。安全的跨平台方法是在开始新帧时通过surfacePixelSize() 进行检查。
释放交换链必须在QWindow 和底层本地窗口完全启动和运行时进行。以前面的示例为基础:
void releaseSwapChain() { if (hasSwapChain) { sc->destroy(); hasSwapChain = false; } } // assuming Window is our QWindow subclass bool Window::event(QEvent *e) { switch (e->type()) { case QEvent::UpdateRequest: // for QWindow::requestUpdate() render(); break; case QEvent::PlatformSurface: if (static_cast<QPlatformSurfaceEvent *>(e)->surfaceEventType() == QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed) releaseSwapChain(); break; default: break; } return QWindow::event(e); }
初始化交换链和开始渲染第一帧不能在任何时候开始。安全的跨平台方法是依靠 expose 事件。QExposeEvent 是一个松散的指定事件,每当窗口被映射、遮挡或调整大小时都会发送,具体取决于平台。
void Window::exposeEvent(QExposeEvent *) { // initialize and start rendering when the window becomes usable for graphics purposes if (isExposed() && !running) { running = true; init(); } // stop pushing frames when not exposed or size becomes 0 if ((!isExposed() || (hasSwapChain && sc->surfacePixelSize().isEmpty())) && running) notExposed = true; // continue when exposed again and the surface has a valid size if (isExposed() && running && notExposed && !sc->surfacePixelSize().isEmpty()) { notExposed = false; newlyExposed = true; } if (isExposed() && !sc->surfacePixelSize().isEmpty()) render(); }
渲染开始后,请求新帧的简单方法是QWindow::requestUpdate() 。在某些平台上,这只是一个小定时器,而在其他平台上,它有特定的实现方式:例如,在 macOS 或 iOS 上,它可能由CVDisplayLink 支持。上面的示例已经通过处理QEvent::UpdateRequest 为更新请求做好了准备。
在充当QRhiRenderTarget 的同时,QRhiSwapChain 还管理着一个QRhiCommandBuffer 。调用QRhi::endFrame() 会提交已记录的命令,同时也会查询present
请求。默认情况下,交换间隔为 1,这意味着与显示屏垂直刷新同步已启用。因此,调用 beginFrame() 和 endFrame() 的渲染线程将被控制为 vsync。在某些后端,可以通过在flags() 中传递 QRhiSwapChain:NoVSync 来禁用此功能。
当通过setSampleCount() 提出请求时,多采样(MSAA)会以对应用程序透明的方式处理。在适用情况下,QRhiSwapChain 将负责创建额外的颜色缓冲区,并在帧结束时发出多采样解析命令。对于 OpenGL,有必要在初始化QRhi 之前通过QSurfaceFormat 调用QSurfaceFormat::setDefaultFormat() 来请求适当的样本数。
注意: 这是一个 RHI API,兼容性保证有限,详情请参见QRhi 。
成员类型文档
枚举 QRhiSwapChain::Flag
flags QRhiSwapChain::Flags
用于描述交换链属性的标志值
常量 | 值 | 描述 |
---|---|---|
QRhiSwapChain::SurfaceHasPreMulAlpha | 1 << 0 | 表示目标表面具有预乘法 alpha 的透明度。例如,当目标QWindow 上启用了 alpha 通道时,Qt Quick 就会使用该值,因为场景图渲染器输出的片段总是将 alpha 乘以红、绿、蓝值。为确保不同平台上的行为一致,在目标QWindow 上设置此标志时,应始终将QSurfaceFormat::alphaBufferSize() 设置为非零值。 |
QRhiSwapChain::SurfaceHasNonPreMulAlpha | 1 << 1 | 表示目标表面具有非预乘 alpha 的透明度。需要注意的是,如果系统合成器总是希望内容具有预乘法 alpha,则某些系统可能不支持此功能。在这种情况下,设置此标记后的行为将与 SurfaceHasPreMulAlpha 相同。 |
QRhiSwapChain::sRGB | 1 << 2 | 请求为交换链的色彩缓冲区和/或渲染目标视图(如适用)选择 sRGB 格式。请注意,这意味着所有以该交换链为目标的内容都将启用 sRGB 帧缓冲更新和混合,而且无法选择退出。对于 OpenGL,还需在QWindow 的QSurfaceFormat 上设置sRGBColorSpace 。仅当交换链格式设置为QRhiSwapChain::SDR 时适用。 |
QRhiSwapChain::UsedAsTransferSource | 1 << 3 | 表示交换链将被用作QRhiResourceUpdateBatch::readBackTexture() 中的回读来源。 |
QRhiSwapChain::NoVSync | 1 << 4 | 请求禁用垂直同步等待,同时避免对渲染线程进行节流。该行为取决于后端,只有在可以控制的情况下才适用。有些后端可能会完全忽略该请求。对于 OpenGL,可尝试在QWindow 上通过QSurfaceFormat::setSwapInterval() 将交换间隔设为 0。 |
QRhiSwapChain::MinimalBufferCount | 1 << 5 | 请求使用最少的缓冲区数量(实际上是 2 个)创建交换链,除非图形实现的最少缓冲区数量比它更多。仅适用于可通过图形 API(如 Vulkan)进行控制的后端。默认情况下,应由后端决定其请求的缓冲区数量(实际上几乎总是 2 或 3),这与应用程序无关。不过,以 Vulkan 为例,后端可能更倾向于更高的数量(3),例如,这样可以避免移动设备上某些 Vulkan 实现出现奇怪的性能问题。在某些平台上,强制使用较低的缓冲区计数(2)可能会有好处,因此该标记允许强制使用较低的缓冲区计数。请注意,所有这些都不会影响飞行中的帧数,因此 CPU (QRhi) 仍会比 GPU 最多提前N - 1 帧准备帧,即使交换链图像缓冲区数大于N 也是如此(N =QRhi::FramesInFlight ,通常为 2)。 |
Flags 类型是QFlags<Flag> 的类型定义。它存储 Flag 值的 OR 组合。
enum QRhiSwapChain::Format
描述交换链格式。默认格式为 SDR。
该枚举与isFormatSupported() 一起使用,用于预先检查平台和窗口的相关屏幕是否支持使用给定格式创建交换链,并与setFormat() 一起使用,用于在首次调用createOrResize() 之前在交换链中设置所需的格式。
常量 | 值 | 说明 |
---|---|---|
QRhiSwapChain::SDR | 0 | 8 位 RGBA 或 BGRA,具体取决于后端和平台。特别是 OpenGL ES,平台提供的格式可能少于 8 位(例如,由于 EGL 和QSurfaceFormat 选择了 565 或 444 格式,这不在QRhi 的控制范围内)。标准动态范围。可结合设置QRhiSwapChain::sRGB 标志。 |
QRhiSwapChain::HDRExtendedSrgbLinear | 1 | 16 位浮点 RGBA、高动态范围、扩展线性 sRGB (scRGB) 色彩空间。这涉及 Rec. 709 原色(与 SDR/sRGB 相同)和线性色彩。窗口系统会将其转换为显示器的本机色彩空间(如 HDR10)。在 Windows 上,这是系统合成器的标准色彩空间,也是一般桌面平台上 HDR 交换链的推荐格式。 |
QRhiSwapChain::HDR10 | 2 | 10 位无符号 int RGB 或 BGR,带 2 位 alpha,高动态范围,HDR10(Rec.2020)色彩空间,带 ST2084 PQ 传输函数。 |
QRhiSwapChain::HDRExtendedDisplayP3Linear | 3 | 16 位浮点 RGBA、高动态范围、扩展线性显示 P3 色彩空间。iOS 和 VisionOS 等平台上 HDR 的主要选择。 |
enum QRhiSwapChain::StereoTargetBuffer
选择用于立体交换链的后缓冲区。
常数 | 值 |
---|---|
QRhiSwapChain::LeftBuffer | 0 |
QRhiSwapChain::RightBuffer | 1 |
成员函数文档
[pure virtual]
bool QRhiSwapChain::createOrResize()
如果尚未创建交换链,则创建交换链,并调整交换链缓冲区的大小以匹配目标曲面的当前大小。当目标表面的大小与之前不同时,调用此命令。
注意: 只有在需要完全释放交换链时才调用destroy() ,通常是在QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed 时。要调整大小,只需调用 createOrResize() 即可。
成功时返回true
,图形操作失败时返回false
。无论返回值如何,调用destroy() 总是安全的。
[pure virtual]
QRhiCommandBuffer *QRhiSwapChain::currentFrameCommandBuffer()
返回一个命令缓冲区,在beginFrame -endFrame 块中记录渲染命令和资源更新,前提是调用了该交换链的 beginFrame()。
注意: 返回的对象在 endFrame() 之后也有效,直到下一次 beginFrame(),但此时不应使用返回的命令缓冲区记录任何命令。相反,它可用于查询在该帧(或前几帧)期间收集的数据,例如通过调用lastCompletedGpuTime() 进行查询。
注意: 该值不得在帧间缓存和重复使用。再次调用beginFrame() 时,调用者不应保留返回的对象。相反,应通过调用该函数再次查询命令缓冲区对象。
[pure virtual]
QRhiRenderTarget *QRhiSwapChain::currentFrameRenderTarget()
返回一个可与 beginPass() 一起使用的渲染目标,以渲染 swapchain 的当前 backbuffer。仅在QRhi::beginFrame() -QRhi::endFrame() 块中有效,其中 beginFrame() 已被调用。
注意: 该值不得在帧间缓存和重复使用。
[virtual]
QRhiRenderTarget *QRhiSwapChain::currentFrameRenderTarget(QRhiSwapChain::StereoTargetBuffer targetBuffer)
返回一个可与 beginPass() 一起使用的渲染目标,以便渲染到交换链的左侧或右侧背缓冲区。此重载只能用于立体渲染,即相关QWindow 由两个颜色缓冲区(每只眼睛一个)支持,而不是只有一个。
不支持立体渲染时,返回值将是默认目标值。除 Metal 外,所有硬件后端都支持立体渲染,并与QSurfaceFormat::StereoBuffers 结合使用,前提是运行时图形和显示驱动程序栈支持立体渲染。Metal 和 Null 后端将通过此重载返回默认渲染目标。
注意: 该值不得在帧间缓存和重复使用
QSize QRhiSwapChain::currentPixelSize() const
返回上次成功构建交换链的大小。用它来决定是否需要再次调用createOrResize(): 如果currentPixelSize() != surfacePixelSize()
,则需要调整 swapchain 的大小。
注: 典型的渲染逻辑会在开始准备新帧时调用此函数来获取输出尺寸,并根据此函数返回的尺寸进行相关计算(如视口)。
虽然在很多情况下,该值与QWindow::size() * QWindow::devicePixelRatio()
相同,但并不能保证在所有平台和图形 API 实现中,依赖QWindow 报告的尺寸都是正确的。因此,如果需要确定输出图层或曲面的像素尺寸,强烈建议使用此函数。
当QRhi 在专用渲染线程上使用时,还可以避免潜在的数据竞赛,因为这样可以避免调用QWindow 函数,而这些函数可能会访问主线程上更新的数据。
另请参见 surfacePixelSize().
QRhiRenderBuffer *QRhiSwapChain::depthStencil() const
返回当前与深度模版相关的呈现缓冲区。
另请参阅 setDepthStencil().
QRhiSwapChain::Flags QRhiSwapChain::flags() const
返回当前设置的标志。
另请参见 setFlags()。
QRhiSwapChain::Format QRhiSwapChain::format() const
返回当前设置的格式。
另请参阅 setFormat()。
[virtual]
QRhiSwapChainHdrInfo QRhiSwapChain::hdrInfo()
返回相关显示器的 HDR 信息。
请勿认为这是一种廉价操作。根据平台的不同,该函数会进行各种平台查询,这可能会影响性能。
注: 只要窗口是set ,就可以在createOrResize() 之前调用。
注: 在不同显示器(HDR 到不同特性的 HDR、HDR 到 SDR 等)之间移动已初始化交换链的窗口时会发生什么情况,目前还没有明确定义,而且在很大程度上取决于窗口系统和合成器,不同平台之间可能会有不同的行为。目前,QRhi 只能保证 hdrInfo() 在createOrResize() 时返回交换链关联窗口所属显示屏的有效数据(如果可用)。
另请参见 QRhiSwapChainHdrInfo 。
[pure virtual]
bool QRhiSwapChain::isFormatSupported(QRhiSwapChain::Format f)
如果给定的交换链格式f 受支持,则返回 true。始终支持 SDR。
注意: 可独立于createOrResize() 调用,但window() 必须已设置。在未设置窗口的情况下调用可能会导致意想不到的结果,这取决于后端和平台(对于任何 HDR 格式都很可能是假),因为 HDR 格式支持通常与交换链的相关窗口在任何给定时间内所属的输出(屏幕)相关联。如果某个 HDR 格式的结果为 true,那么只要窗口在此期间没有移动到其他屏幕,使用该格式创建交换链就会成功。
该函数的主要用途是在窗口设置完成后,在第一个createOrResize() 之前调用。这样,QRhi 后端就可以执行平台或窗口系统特定的查询,以确定窗口(及其所在屏幕)是否能够以指定格式进行真正的 HDR 输出。
当格式被报告为支持时,调用setFormat() 设置所请求的格式,然后调用createOrResize() 。但请注意这样做的后果:成功请求 HDR 格式将需要处理不同的色彩空间,可能要对不支持 HDR 的内容进行白电平校正,调整色调映射方法,调整屏幕外渲染目标设置等。
另请参阅 setFormat().
[pure virtual]
QRhiRenderPassDescriptor *QRhiSwapChain::newCompatibleRenderPassDescriptor()
返回与该交换链兼容的新QRhiRenderPassDescriptor 。
返回值有两种用途:可传递给setRenderPassDescriptor() 和QRhiGraphicsPipeline::setRenderPassDescriptor() 。呈现传递描述符描述了可受flags() 影响的附件(颜色、深度/模版)和加载/存储行为。QRhiGraphicsPipeline 只能与设置了compatible QRhiRenderPassDescriptor 的交换链结合使用。
另请参阅 createOrResize()。
QRhiSwapChainProxyData QRhiSwapChain::proxyData() const
返回当前设置的代理数据。
另请参阅 setProxyData()。
QRhiRenderPassDescriptor *QRhiSwapChain::renderPassDescriptor() const
返回当前关联的QRhiRenderPassDescriptor 对象。
另请参阅 setRenderPassDescriptor().
[override virtual]
QRhiResource::Type QRhiSwapChain::resourceType() const
重实现:QRhiResource::resourceType() 常量。
返回资源类型。
int QRhiSwapChain::sampleCount() const
返回当前设置的采样计数。1 表示无多采样抗锯齿。
另请参见 setSampleCount()。
void QRhiSwapChain::setDepthStencil(QRhiRenderBuffer *ds)
设置 renderbufferds ,以用作深度模版缓冲区。
另请参见 depthStencil().
void QRhiSwapChain::setFlags(QRhiSwapChain::Flags f)
设置标志f 。
另请参阅 flags() 。
void QRhiSwapChain::setFormat(QRhiSwapChain::Format f)
设置格式f 。
避免设置isFormatSupported() 报告为不支持的格式。请注意,对特定格式的支持可能取决于打开交换链相关窗口的屏幕。在某些平台(如 Windows 和 macOS)上,要实现 HDR 输出,必须在显示设置中启用 HDR 输出。
有关高动态范围输出的更多信息,请参阅isFormatSupported(),QRhiSwapChainHdrInfo 和Format 。
另请参阅 format() 。
void QRhiSwapChain::setProxyData(const QRhiSwapChainProxyData &d)
设置代理数据d 。
另请参阅 proxyData() 和QRhi::updateSwapChainProxyData()。
void QRhiSwapChain::setRenderPassDescriptor(QRhiRenderPassDescriptor *desc)
联系QRhiRenderPassDescriptor desc 。
另见 renderPassDescriptor().
void QRhiSwapChain::setSampleCount(int samples)
设置样本数。samples 的常用值为 1(无 MSAA)、4(4x MSAA)或 8(8x MSAA)。
另请参阅 sampleCount() 和QRhi::supportedSampleCounts()。
[since 6.9]
void QRhiSwapChain::setShadingRateMap(QRhiShadingRateMap *map)
与指定的QRhiShadingRateMap map 关联。只有在报告支持QRhi::VariableRateShadingMap 功能时,该功能才起作用。
当同时调用QRhiCommandBuffer::setShadingRate() 时,每个磁贴将使用两个着色率中较高的一个。目前无法控制组合器的行为。
注意: 设置着色率映射意味着需要一个不同的、新的QRhiRenderPassDescriptor ,而且必须重建一些本地交换链对象。因此,如果已经设置了交换链,请在 setShadingRateMap() 之后立即调用newCompatibleRenderPassDescriptor() 和setRenderPassDescriptor()。然后,还必须再次调用createOrResize() 。这会产生滚动后果,例如对于图形管道:这些管道也需要与新的QRhiRenderPassDescriptor 关联,然后重建。有关如何处理这一问题的建议,请参阅QRhiRenderPassDescriptor::serializedFormat() 。记住也要为它们设置QRhiGraphicsPipeline::UsesShadingRate 标志。
此功能在 Qt 6.9 中引入。
另请参阅 shadingRateMap()。
void QRhiSwapChain::setWindow(QWindow *window)
设置window 。
另请参见 window() 。
[since 6.9]
QRhiShadingRateMap *QRhiSwapChain::shadingRateMap() const
返回当前设置的QRhiShadingRateMap 。默认情况下为nullptr
。
此函数在 Qt 6.9 中引入。
另请参阅 setShadingRateMap()。
[pure virtual]
QSize QRhiSwapChain::surfacePixelSize()
返回窗口相关表面或图层的大小。
警告: 不要认为它与QWindow::size() * QWindow::devicePixelRatio()
相同。对于某些图形应用程序接口和窗口系统接口(如 Vulkan),理论上曲面的尺寸可能与关联窗口的尺寸不同。为支持这些情况,渲染逻辑必须始终根据QRhiSwapChain 报告的尺寸进行尺寸派生计算(如视口),而绝不能根据QWindow 查询的尺寸进行计算。
注: 如果至少已经设置了window() ,也可以在createOrResize() 之前调用。与currentPixelSize() 结合使用,可以检测交换链何时需要调整大小。不过要注意的是,底层原生对象(曲面、图层或类似对象)的大小是 "实时 "的,因此无论何时调用此函数,它都会返回底层实现所报告的最新值,而不会保证任何原子性。因此,强烈建议不要使用此函数来确定在帧中使用的图形资源的像素尺寸。请使用currentPixelSize() 代替它,它返回的尺寸是原子性的,在调用createOrResize() 之间不会改变。
注意: 对于与交换链颜色缓冲区结合使用的深度模版缓冲区,强烈建议使用QRhiRenderBuffer:UsedWithSwapChainOnly 标志提供的自动大小调整和重建行为。请避免通过此函数查询表面尺寸,以获得可传递给QRhiRenderBuffer::setPixelSize() 的尺寸,因为这样做会导致上述缺乏原子性的问题。
另请参见 currentPixelSize()。
QWindow *QRhiSwapChain::window() const
返回当前设置的窗口。
另请参见 setWindow()。
© 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.