本页内容

QRhiSwapChain Class

Swapchain 资源。更多内容...

标题: #include <rhi/qrhi.h>
CMake: find_package(Qt6 REQUIRED COMPONENTS GuiPrivate)
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 的调整大小事件来调整交换链的大小,特别是考虑到表面尺寸未必总是与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,还需通过QSurfaceFormat 请求适当的采样数,具体方法是在初始化QRhi 之前调用QSurfaceFormat::setDefaultFormat()。

注意:这是一个 兼容性保证有限的 RHI API,详情请参阅QRhi 。

成员类型文档

enum QRhiSwapChain::Flag
flags QRhiSwapChain::Flags

用于描述交换链属性的标志值

常量值描述
QRhiSwapChain::SurfaceHasPreMulAlpha1 << 0表示目标渲染面具有预乘透明度的透明度。例如,当目标QWindow 上启用了透明度通道时,Qt Quick 便会使用此设置,因为场景图渲染器始终会输出将透明度值乘入红、绿、蓝各色值中的片段。 为确保跨平台行为一致,当交换链上设置此标志时,请务必在目标QWindow 上将QSurfaceFormat::alphaBufferSize() 设置为非零值。
QRhiSwapChain::SurfaceHasNonPreMulAlpha1 << 1表示目标表面具有非预乘透明度。请注意,如果系统合成器始终期望内容具有预乘透明度,则某些系统可能不支持此功能。在这种情况下,设置此标志后的行为预计将等同于 SurfaceHasPreMulAlpha。
QRhiSwapChain::sRGB1 << 2请求为交换链的颜色缓冲区和/或渲染目标视图(如适用)选择 sRGB 格式。请注意,这意味着针对该交换链的所有内容都将启用 sRGB 帧缓冲区更新和混合,且无法选择退出。 对于 OpenGL,还需在QWindow 的QSurfaceFormat 上设置 `sRGBColorSpace `。仅当交换链格式设置为 `QRhiSwapChain::SDR` 时才适用。
QRhiSwapChain::UsedAsTransferSource1 << 3表示该交换链将作为QRhiResourceUpdateBatch::readBackTexture()中读回操作的源。
QRhiSwapChain::NoVSync1 << 4请求禁用垂直同步等待,同时避免对渲染线程进行限速。该行为因后端而异,且仅在能够控制此行为的情况下适用。某些后端可能会完全忽略此请求。对于 OpenGL,请尝试通过QSurfaceFormat::setSwapInterval() 将QWindow 中的交换间隔设置为 0。
QRhiSwapChain::MinimalBufferCount1 << 5请求使用最少数量的缓冲区创建交换链,实际中通常为 2 个,除非图形实现的最小缓冲区数量高于此值。 仅适用于可通过图形 API 进行此类控制的后端,例如 Vulkan。默认情况下,请求多少个缓冲区由后端决定(实际上几乎总是 2 或 3),这与应用程序无关。 然而,以 Vulkan 为例,后端通常会倾向于选择较高的缓冲区数量(3),例如为了避免某些移动设备上 Vulkan 实现中出现的奇怪性能问题。在某些平台上,强制使用较低的缓冲区数量(2)可能会带来好处,因此该标志允许强制执行此操作。 请注意,这一切都不会影响待处理帧的数量,因此 CPU(QRhi )仍会最多提前N - 1 帧为 GPU 准备帧,即使 swapchain 的图像缓冲区数量大于N 也是如此。(N =QRhi::FramesInFlight ,通常为 2)。

Flags 类型是QFlags<Flag> 的 typedef。它存储 Flag 值的按“或”运算组合。

enum QRhiSwapChain::Format

描述了交换链格式。默认格式为 SDR。

该枚举与isFormatSupported()配合使用,用于预先检查平台及窗口关联的屏幕是否支持使用给定格式创建交换链;并与setFormat()配合使用,用于在首次调用createOrResize()之前,将请求的格式设置到交换链中。

常量值描述
QRhiSwapChain::SDR08 位 RGBA 或 BGRA,具体取决于后端和平台。特别是在 OpenGL ES 中,可能会出现平台提供的位数少于 8 位的情况(例如,由于 EGL 和QSurfaceFormat 选择了 565 或 444 格式——这超出了QRhi 的控制范围)。标准动态范围。 可与设置QRhiSwapChain::sRGB 标志结合使用。
QRhiSwapChain::HDRExtendedSrgbLinear116 位浮点 RGBA,高动态范围,扩展线性 sRGB(scRGB)色彩空间。这涉及 Rec. 709 原色(与 SDR/sRGB 相同)和线性颜色。 向显示器原生色彩空间(例如 HDR10)的转换由窗口系统完成。在 Windows 系统中,这是系统合成器的规范色彩空间,也是桌面平台上 HDR 交换链的一般推荐格式。
QRhiSwapChain::HDR10210 位无符号整数 RGB 或 BGR 格式,带 2 位透明度通道,高动态范围,采用 ST2084 PQ 传输函数的 HDR10(Rec. 2020)色彩空间。
QRhiSwapChain::HDRExtendedDisplayP3Linear316 位浮点 RGBA,高动态范围,扩展线性 Display P3 色彩空间。这是 iOS 和 VisionOS 等平台上 HDR 的首选方案。

enum QRhiSwapChain::StereoTargetBuffer

选择要与立体交换链配合使用的后缓冲区。

常量值
QRhiSwapChain::LeftBuffer0
QRhiSwapChain::RightBuffer1

成员函数文档

[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()` 配合使用,以渲染交换链的当前后缓冲区。仅在使用此交换链调用 `beginFrame()` 的 `QRhi::beginFrame()` - `QRhi::endFrame()` 代码块内有效。

注意:该 值不得在帧与帧之间被缓存和重复使用

[virtual] QRhiRenderTarget *QRhiSwapChain::currentFrameRenderTarget(QRhiSwapChain::StereoTargetBuffer targetBuffer)

返回一个渲染目标,该目标可与 `beginPass()` 配合使用,以将渲染结果写入交换链的左侧或右侧后缓冲区。此重载仅应在立体渲染场景中使用,即当关联的 `QWindow ` 由两个颜色缓冲区(每只眼睛各一个)支持,而非仅有一个颜色缓冲区时。

当不支持立体渲染时,返回值将为默认渲染目标。除 Metal 之外,所有硬件后端均支持此功能(需配合QSurfaceFormat::StereoBuffers 使用),前提是图形和显示驱动程序堆栈在运行时支持该功能。Metal 和 Null 后端调用此重载时将返回默认渲染目标。

注意:该 值不得在帧与帧之间被缓存和重复使用

QSize QRhiSwapChain::currentPixelSize() const

返回 swapchain 上一次成功构建时使用的尺寸。可利用此值判断是否需要再次调用 `createOrResize()`:如果 `currentPixelSize() != surfacePixelSize() `,则需要调整 swapchain 的大小。

注意:典型的 渲染逻辑会在开始准备新帧时调用此函数以获取输出尺寸,并基于此函数返回的尺寸进行相关计算(例如视口尺寸)。

虽然在许多情况下该值与QWindow::size() * QWindow::devicePixelRatio() 相同,但依赖QWindow 报告的尺寸并不能保证在所有平台和图形 API 实现中都正确。因此,每当需要确定输出层或表面的像素尺寸时,强烈建议使用此函数。

此外,当在专用渲染线程上使用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 格式,结果很可能为 false),因为对 HDR 格式的支持通常与 swapchain 关联的窗口在任意给定时刻所属的输出(屏幕)相关联。 如果对于某种 HDR 格式结果为 true,那么只要在此期间窗口未被移动到另一块屏幕上,使用该格式创建交换链预计会成功。

此函数的主要用途是在窗口已设置后,于首次调用 `createOrResize()` 之前调用它。这允许 `QRhi ` 后端执行针对平台或窗口系统的特定查询,以确定该窗口(及其所在的屏幕)是否能够以指定格式输出真正的 HDR。

当报告支持该格式时,调用setFormat() 设置请求的格式,并调用createOrResize()。但请注意由此带来的后果:成功请求 HDR 格式将涉及处理不同的色彩空间,可能需要对不支持 HDR 的内容进行白点校正、调整色调映射方法、调整离屏渲染目标设置等。

另请参阅 setFormat()。

[pure virtual] QRhiRenderPassDescriptor *QRhiSwapChain::newCompatibleRenderPassDescriptor()

返回一个与该交换链兼容的新QRhiRenderPassDescriptor 对象。

返回值有两种用途:可传递给setRenderPassDescriptor()和QRhiGraphicsPipeline::setRenderPassDescriptor()函数。渲染通道描述符(render pass descriptor)描述了附件(颜色、深度/模板)以及可能受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() const。

返回资源类型。

int QRhiSwapChain::sampleCount() const

返回当前设置的采样数。1 表示不进行多采样抗锯齿。

另请参阅 setSampleCount()。

void QRhiSwapChain::setDepthStencil(QRhiRenderBuffer *ds)

将渲染缓冲区ds 设置为深度-模板缓冲区。

另请参阅 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(4 倍 MSAA)或 8(8 倍 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()` 返回的结果相同。在某些图形 API 和窗口系统接口(例如 Vulkan)中,表面尺寸理论上可能与关联窗口的尺寸不同。 为支持这些情况,渲染逻辑必须始终基于QRhiSwapChain 报告的大小进行尺寸相关的计算(例如视口),而绝不能基于QWindow 查询到的尺寸。

注意: 如果至少已设置window(),也可以在调用createOrResize() 之前调用此函数。结合currentPixelSize() 的使用,可以检测到何时需要调整 swapchain 的大小。但需注意,底层原生对象(如表面、图层等)的大小是“动态”的,因此每次调用此函数时,它返回的都是底层实现报告的最新值,且不保证原子性。 因此,强烈不建议使用此函数来确定帧中使用的图形资源的像素尺寸。请改用currentPixelSize(),该函数返回的尺寸是原子性的,且在两次createOrResize() 调用之间不会发生变化。

注意:对于与 交换链(swapchain)的颜色缓冲区结合使用的深度-模板缓冲区,强烈建议依赖QRhiRenderBuffer:UsedWithSwapChainOnly 标志所提供的自动尺寸调整和重建行为。 请避免仅为了获取可传递给 `QRhiRenderBuffer::setPixelSize()` 的尺寸而通过此函数查询表面尺寸,因为如上所述,这会受到缺乏原子性的影响。

另请参阅 currentPixelSize()。

QWindow *QRhiSwapChain::window() const

返回当前设置的窗口。

另请参阅 ` setWindow()`。

© 2026 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.