QRhi Class
加速的 2D/3D 图形 API 抽象。更多内容...
| 标题: | #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 |
- 所有成员列表(包括继承的成员)
- QRhi 属于“3D 渲染”类。
公共类型
(since 6.10) | AdapterList |
| enum | BeginFrameFlag { } |
| flags | BeginFrameFlags |
| enum | EndFrameFlag { SkipPresent } |
| flags | EndFrameFlags |
| enum | Feature { MultisampleTexture, MultisampleRenderBuffer, DebugMarkers, Timestamps, Instancing, …, ShaderDrawParameters } |
| enum | Flag { EnableDebugMarkers, EnableTimestamps, PreferSoftwareRenderer, EnablePipelineCacheDataSave, SuppressSmokeTestWarnings } |
| flags | Flags |
| enum | FrameOpResult { FrameOpSuccess, FrameOpError, FrameOpSwapChainOutOfDate, FrameOpDeviceLost } |
| enum | Implementation { Null, Vulkan, OpenGLES2, D3D11, D3D12, Metal } |
| enum | ResourceLimit { TextureSizeMin, TextureSizeMax, MaxColorAttachments, FramesInFlight, MaxAsyncReadbackFrames, …, ShadingRateImageTileSize } |
公共函数
| ~QRhi() | |
| void | addCleanupCallback(const QRhi::CleanupCallback &callback) |
| void | addCleanupCallback(const void *key, const QRhi::CleanupCallback &callback) |
| QRhi::Implementation | backend() const |
| const char * | backendName() const |
| QRhi::FrameOpResult | beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags = {}) |
| QRhi::FrameOpResult | beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi::BeginFrameFlags flags = {}) |
| QMatrix4x4 | clipSpaceCorrMatrix() const |
| int | currentFrameSlot() const |
| QRhiDriverInfo | driverInfo() const |
| QRhi::FrameOpResult | endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags = {}) |
| QRhi::FrameOpResult | endOffscreenFrame(QRhi::EndFrameFlags flags = {}) |
| QRhi::FrameOpResult | finish() |
| bool | isClipDepthZeroToOne() const |
| bool | isDeviceLost() const |
| bool | isFeatureSupported(QRhi::Feature feature) const |
| bool | isRecordingFrame() const |
| bool | isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags = {}) const |
| bool | isYUpInFramebuffer() const |
| bool | isYUpInNDC() const |
| bool | makeThreadLocalNativeContextCurrent() |
| const QRhiNativeHandles * | nativeHandles() |
| QRhiBuffer * | newBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, quint32 size) |
| QRhiComputePipeline * | newComputePipeline() |
| QRhiGraphicsPipeline * | newGraphicsPipeline() |
| QRhiRenderBuffer * | newRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize, int sampleCount = 1, QRhiRenderBuffer::Flags flags = {}, QRhiTexture::Format backingFormatHint = QRhiTexture::UnknownFormat) |
| QRhiSampler * | newSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter, QRhiSampler::Filter mipmapMode, QRhiSampler::AddressMode addressU, QRhiSampler::AddressMode addressV, QRhiSampler::AddressMode addressW = QRhiSampler::Repeat) |
| QRhiShaderResourceBindings * | newShaderResourceBindings() |
(since 6.9) QRhiShadingRateMap * | newShadingRateMap() |
| QRhiSwapChain * | newSwapChain() |
| QRhiTexture * | newTexture(QRhiTexture::Format format, const QSize &pixelSize, int sampleCount = 1, QRhiTexture::Flags flags = {}) |
| QRhiTexture * | newTexture(QRhiTexture::Format format, int width, int height, int depth, int sampleCount = 1, QRhiTexture::Flags flags = {}) |
| QRhiTexture * | newTextureArray(QRhiTexture::Format format, int arraySize, const QSize &pixelSize, int sampleCount = 1, QRhiTexture::Flags flags = {}) |
| QRhiTextureRenderTarget * | newTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc, QRhiTextureRenderTarget::Flags flags = {}) |
| QRhiResourceUpdateBatch * | nextResourceUpdateBatch() |
| QByteArray | pipelineCacheData() |
| void | releaseCachedResources() |
| void | removeCleanupCallback(const void *key) |
| int | resourceLimit(QRhi::ResourceLimit limit) const |
| void | setPipelineCacheData(const QByteArray &data) |
(since 6.9) void | setQueueSubmitParams(QRhiNativeHandles *params) |
| QRhiStats | statistics() const |
| QList<int> | supportedSampleCounts() const |
(since 6.9) QList<QSize> | supportedShadingRates(int sampleCount) const |
| QThread * | thread() const |
| int | ubufAligned(int v) const |
| int | ubufAlignment() const |
静态公共成员
| const char * | backendName(QRhi::Implementation impl) |
| QRhi * | create(QRhi::Implementation impl, QRhiInitParams *params, QRhi::Flags flags, QRhiNativeHandles *importDevice, QRhiAdapter *adapter) |
| QRhi * | create(QRhi::Implementation impl, QRhiInitParams *params, QRhi::Flags flags = {}, QRhiNativeHandles *importDevice = nullptr) |
(since 6.10) QRhi::AdapterList | enumerateAdapters(QRhi::Implementation impl, QRhiInitParams *params, QRhiNativeHandles *nativeHandles = nullptr) |
| int | mipLevelsForSize(const QSize &size) |
| bool | probe(QRhi::Implementation impl, QRhiInitParams *params) |
| QSize | sizeForMipLevel(int mipLevel, const QSize &baseLevelSize) |
| QRhiSwapChainProxyData | updateSwapChainProxyData(QRhi::Implementation impl, QWindow *window) |
相关的非成员
(since 6.7) | QRhiShaderResourceBindingSet |
详细说明
Qt 渲染硬件接口是对硬件加速图形 API(例如OpenGL、OpenGL ES、Direct3D、Metal 和Vulkan)的一种抽象表示。
警告: Qt GUI 模块中的QRhi 类族 (包括QShader 和QShaderDescription )仅提供有限的兼容性保证。这些类不提供源代码或二进制兼容性保证,这意味着该 API 仅保证与应用程序开发时所基于的 Qt 版本兼容。 不过,旨在将源代码不兼容的更改控制在最低限度,且仅在次要版本(如 6.7、6.8 等)中进行。若要在应用程序中使用这些类,请链接至Qt::GuiPrivate (若使用 CMake),并包含以rhi 为前缀的头文件,例如#include <rhi/qrhi.h> 。
每个 QRhi 实例都由一个针对特定图形 API 的后端提供支持。后端的选定是在运行时进行的,由创建 QRhi 实例的应用程序或库决定。 某些后端可在多个平台上使用(如 OpenGL、Vulkan、Null),而特定于某个平台的 API 仅在该平台上运行时才可用(例如 macOS/iOS 上的 Metal、Windows 上的 Direct3D)。
当前可用的后端包括:
- OpenGL 2.1 / OpenGL ES 2.0 或更高版本。若存在某些扩展和较新的核心规范特性,系统将予以利用,例如启用多采样帧缓冲区或计算着色器。同时支持在核心配置上下文中运行。 如有必要,应用程序可在运行时查询feature flags ,以检查支持 QRhi 的 OpenGL 上下文中不支持的功能。OpenGL 后端基于QOpenGLContext 、QOpenGLFunctions 以及Qt GUI 模块的相关跨平台基础设施构建。
- Direct3D 11.2 及更高版本(搭配 DXGI 1.3 及更高版本),使用 Shader Model 5.0 或更高版本。 当 D3D 运行时不支持 11.2 功能或着色器模型 5.0 时,使用加速图形设备进行初始化将失败,但仍可选择使用软件适配器。
- Windows 10 1703 及更高版本上的 Direct3D 12,且支持着色器模型 5.0 或更高版本。 Qt 要求存在 ID3D12Device2,因此需要至少 Windows 10 1703 版。D3D12 设备默认创建时指定的最低功能级别为
D3D_FEATURE_LEVEL_11_0。 - Metal 1.2 或更高版本。
- Vulkan 1.0 或更高版本,可选支持部分 Vulkan 1.1 级别的功能。
- Null,一个完全不发出任何图形调用的“虚拟”后端。
为了使着色器代码能在 Qt 应用程序和库中“一次编写”,所有着色器均需使用单一语言编写,随后编译为 SPIR-V。随后,系统会据此生成各种着色语言的版本,并附带反射信息(输入、输出、着色器资源)。 随后,这些内容会被打包为易于且高效序列化的QShader 实例。用于生成此类着色器的编译器和工具不属于QRhi和Qt GUI 模块,但用于使用此类着色器的核心类QShader 和QShaderDescription 则包含在其中。用于执行编译和转换的API及工具属于Qt的Shader Tools 模块。
请参阅RHI 窗口示例,该示例介绍了如何使用 QRhi 在QWindow 上创建一个可移植的、跨平台的应用程序,以实现加速的 3D 渲染。
API 概览
为了通过一个简短但完整的示例快速了解该 API(该示例不涉及窗口相关的设置),下面提供了一个完整的、可运行的跨平台应用程序,该程序在屏幕外渲染 20 帧,然后从 GPU 读回纹理内容后将生成的图像保存到文件中。 若需查看在屏幕上进行渲染的示例(该示例涉及设置QWindow 和交换链),请参阅RHI窗口示例。
为简洁起见,QRhi的初始化是根据平台进行的:此处的示例代码在Windows上选择Direct 3D 12,在macOS和iOS上选择Metal,其余情况则选择Vulkan。本应用程序从未使用过OpenGL和Direct 3D 11,但只需添加几行代码即可引入对它们的支持。
#include <QGuiApplication>
#include <QImage>
#include <QFile>
#include <rhi/qrhi.h>
intmain(intargc, char**argv)
{
QGuiApplication app(argc,argv);
#if QT_CONFIG(vulkan)
QVulkanInstance inst;
#endif
std::unique_ptr<QRhi>rhi;
#if defined(Q_OS_WIN)
QRhiD3D12InitParams params;
rhi.reset(QRhi::create(QRhi::D3D12, ¶ms));
#elif QT_CONFIG(metal)
QRhiMetalInitParams params;
rhi.reset(QRhi::create(QRhi::Metal, ¶ms));
#elif QT_CONFIG(vulkan)
inst.setExtensions(QRhiVulkanInitParams::preferredInstanceExtensions());
if(inst.create()) {
QRhiVulkanInitParams params;
params.inst= &inst;
rhi.reset(QRhi::create(QRhi::Vulkan, ¶ms));
}else{
qFatal("Failed to create Vulkan instance");
}
#endif
if(rhi)
qDebug() << rhi->backendName() << rhi->driverInfo();
else
qFatal("Failed to initialize RHI");
floatrotation= 0.0f;
floatopacity= 1.0f;
intopacityDir= 1;
std::unique_ptr<QRhiTexture>tex(rhi->newTexture(QRhiTexture::RGBA8,
QSize(1280, 720),
1,
QRhiTexture::RenderTarget|QRhiTexture::UsedAsTransferSource));
tex->create();
std::unique_ptr<QRhiTextureRenderTarget>rt(rhi->newTextureRenderTarget({ tex.get() }));
std::unique_ptr<QRhiRenderPassDescriptor>rp(rt->newCompatibleRenderPassDescriptor());
rt->setRenderPassDescriptor(rp.get());
rt->create();
QMatrix4x4 viewProjection= rhi->clipSpaceCorrMatrix();
viewProjection.perspective(45.0f, 1280 / 720.f, 0.01f, 1000.0f);
viewProjection.translate(0, 0,-4);
static floatvertexData[] ={// Y轴向上,逆时针
0.0f, 0.5f, 1.0f, 0.0f, 0.0f,
-0.5f,-0.5f, 0.0f, 1.0f, 0.0f,
0.5f, -0.5f, 0.0f, 0.0f, 1.0f,
};
std::unique_ptr<QRhiBuffer>vbuf(rhi->newBuffer(QRhiBuffer::Immutable,
QRhiBuffer::VertexBuffer,
sizeof(vertexData)));
vbuf->create();
std::unique_ptr<QRhiBuffer>ubuf(rhi->newBuffer(QRhiBuffer::Dynamic,
QRhiBuffer::UniformBuffer,
64 + 4));
ubuf->create();
std::unique_ptr<QRhiShaderResourceBindings>srb(rhi->newShaderResourceBindings());
srb->setBindings({
QRhiShaderResourceBinding::uniformBuffer(0,
QRhiShaderResourceBinding::VertexStage|QRhiShaderResourceBinding::FragmentStage,
ubuf.get())
});
srb->create();
std::unique_ptr<QRhiGraphicsPipeline>ps(rhi->newGraphicsPipeline());
QRhiGraphicsPipeline::TargetBlendpremulAlphaBlend;
premulAlphaBlend.enable= true;
ps->setTargetBlends({ premulAlphaBlend });
static autogetShader= [](constQString&name) {
QFile f(name);
returnf.open(QIODevice::ReadOnly)?QShader::fromSerialized(f.readAll()) : QShader();
};
ps->setShaderStages({
{ QRhiShaderStage::Vertex,getShader(QLatin1String("color.vert.qsb")) },
{ QRhiShaderStage::Fragment,getShader(QLatin1String("color.frag.qsb")) }
});
QRhiVertexInputLayout inputLayout;
inputLayout.setBindings({
{5 * sizeof(float) }
});
inputLayout.setAttributes({
{0, 0,QRhiVertexInputAttribute::Float2, 0},
{0, 1,QRhiVertexInputAttribute::Float3, 2 * sizeof(float) }
});
ps->setVertexInputLayout(inputLayout);
ps->setShaderResourceBindings(srb.get());
ps->setRenderPassDescriptor(rp.get());
ps->create();
QRhiCommandBuffer*cb;
for(intframe= 0; frame< 20;++frame) {
rhi->beginOffscreenFrame(&cb);
QRhiResourceUpdateBatch*u = rhi->nextResourceUpdateBatch();
if(frame== 0)
u->uploadStaticBuffer(vbuf.get(),vertexData);
QMatrix4x4 mvp=viewProjection;
mvp.rotate(rotation, 0, 1, 0);
u->updateDynamicBuffer(ubuf.get(), 0, 64,mvp.constData());
rotation+= 5.0f;
u->updateDynamicBuffer(ubuf.get(), 64, 4, &opacity);
opacity+=opacityDir* 0.2f;
if(opacity< 0.0f||opacity> 1.0f) {
opacityDir*=-1;
opacity= qBound(0.0f,opacity, 1.0f);
}
cb->beginPass(rt.get(), Qt::green,{1.0f, 0},u);
cb->setGraphicsPipeline(ps.get());
cb->setViewport({0, 0, 1280, 720});
cb->setShaderResources();
constQRhiCommandBuffer::VertexInput vbufBinding(vbuf.get(), 0);
cb->setVertexInput(0, 1, &vbufBinding);
cb->draw(3);
QRhiReadbackResult readbackResult;
u= rhi->nextResourceUpdateBatch();
u->readBackTexture({ tex.get() }, &readbackResult);
cb->endPass(u);
rhi->endOffscreenFrame();
QImage image(reinterpret_cast<constuchar*>(readbackResult.data.constData()),
readbackResult.pixelSize.width(),
readbackResult.pixelSize.height(),
QImage::Format_RGBA8888_Premultiplied);
if(rhi->isYUpInFramebuffer())
image.flip();
image.save(QString::asprintf("frame%d.png",frame));
}
return 0;
}该程序生成的结果是 20 张PNG 图片(frame0.png - frame19.png)。这些图片中包含一个在绿色背景上旋转且不透明度不断变化的三角形。
顶点着色器和片段着色器将被处理并打包为.qsb 文件。以下是兼容Vulkan的GLSL源代码:
color.vert
#version 440
layout(location = 0) in vec4 position;
layout(location = 1) in vec3 color;
layout(location = 0) out vec3 v_color;
layout(std140, binding = 0) uniform buf {
mat4 mvp;
float opacity;
};
void main()
{
v_color = color;
gl_Position = mvp * position;
}color.frag
#version 440
layout(location = 0) in vec3 v_color;
layout(location = 0) out vec4 fragColor;
layout(std140, binding = 0) uniform buf {
mat4 mvp;
float opacity;
};
void main()
{
fragColor = vec4(v_color * opacity, opacity);
}若要手动将这些着色器编译并转译为多种目标格式(SPIR-V、HLSL、MSL、GLSL),并生成应用程序在运行时加载的.qsb 文件,请运行qsb --qt6 color.vert -o color.vert.qsb 和qsb --qt6 color.frag -o color.frag.qsb 。此外,Qt的Shader Tools 模块提供了与CMake集成的构建系统——即CMake函数qt_add_shaders() ,该函数可在构建时实现相同的效果。
安全注意事项
QRhi 及其相关类(如QShader )所处理的所有数据均被视为可信内容。
警告: 建议应用程序开发 人员在允许引入不属于应用程序且不受开发者控制的用户提供的内容之前,仔细考虑其潜在影响。(这包括所有顶点/索引数据、着色器、管道和绘制调用参数等。)
设计基础
无法直接实例化 QRhi。应使用create() 函数。请按常规方式删除 QRhi 实例以释放图形设备。
资源
继承自QRhiResource 的类(例如QRhiBuffer 、QRhiTexture 等)的实例,封装了零个、一个或多个本机图形资源。此类类的实例始终通过QRhi的new 函数创建,例如newBuffer()、newTexture()、newTextureRenderTarget()、newSwapChain()。
QRhiBuffer *vbuf = rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(vertexData));
if (!vbuf->create()) { error(); }
// ...
delete vbuf;- 诸如newBuffer() 之类的函数返回的值始终由调用方拥有。
- 仅创建QRhiResource 子类的实例时,绝不会分配或初始化任何本机资源。只有在调用子类的
create()函数时才会进行这些操作,例如QRhiBuffer::create() 或QRhiTexture::create()。 - 例外情况包括QRhiTextureRenderTarget::newCompatibleRenderPassDescriptor()、QRhiSwapChain::newCompatibleRenderPassDescriptor() 和QRhiRenderPassDescriptor::newCompatibleRenderPassDescriptor()。对于这些方法,不存在
create()操作,且返回的对象会立即生效。 - 资源对象本身被视为不可变的:一旦资源的create() 被调用,通过设置器(如QRhiTexture::setPixelSize())更改任何参数都将无效,除非底层本机资源已被释放且再次调用了
create()。有关资源重用的更多信息,请参阅下文各节。 - 底层原生资源将由QRhiResource 的析构函数或通过调用QRhiResource::destroy() 进行释放调度。后端通常会将释放请求加入队列,并推迟至未指定的时间执行,这一过程对应用程序是透明的。这样,应用程序就无需担心释放那些可能仍被正在处理的帧所使用的原生资源。
- 请注意,这并不意味着可以在帧内(即在beginFrame() -endFrame() 区间内)随意对QRhiResource 调用 destroy() 或 delete()。一般而言,所有被引用的QRhiResource 对象必须保持不变,直到通过调用endFrame() 提交该帧为止。为简化此操作,提供了QRhiResource::deleteLater() 作为便捷方法。
命令缓冲区与延迟命令执行
无论底层图形 API 的设计和功能如何,所有 QRhi 后端都实现了某种程度的命令缓冲区。 没有任何QRhiCommandBuffer 函数会直接发出任何本机绑定或绘制命令(例如glDrawElements )。命令总是被记录到队列中,该队列可能是本机队列,也可能是由QRhi后端提供的队列。只有在调用QRhi::endFrame()或QRhi::finish()时,命令缓冲区才会被提交,执行才会开始。
这种延迟执行的特性会对某些类型的对象产生影响。 例如,如果动态缓冲区由主机可见内存支持,则在一个帧内多次向该缓冲区写入数据,将导致该帧命令缓冲区中的所有绘制调用都能看到所有写入的结果,无论动态缓冲区的更新相对于绘制调用是在何时记录的。
此外,在以任何方式引用QRhiResource 子类的帧内,必须将其实例视为不可变的。 请在开始记录下一帧的命令之前,预先创建所有资源。应避免在同一帧内重复使用QRhiResource 实例(即先调用create() ,然后在同一beginFrame - endFrame 段中再次引用该实例),因为这可能会导致意外结果,具体取决于后端实现。
一般而言,所有被引用的QRhiResource 对象必须保持有效且未被修改,直到通过调用endFrame()提交帧为止。 另一方面,一旦帧被提交,调用destroy() 或删除QRhiResource 总是安全的,无论底层本机资源的状态如何(这些资源可能仍被 GPU 使用——但这会在内部得到处理)。
与 OpenGL 等 API 不同,上传和复制类型的命令不能与绘制命令混合使用。典型的渲染器将涉及类似于以下序列的操作:
- (重新)创建资源
- 开始帧
- 记录/发出上传和复制命令
- 开始记录渲染通道
- 记录绘制调用
- 结束渲染通道
- 结束帧
操作的复制类型记录是通过QRhiResourceUpdateBatch 实现的。此类操作通常在beginPass()中提交。
在使用专为 OpenGL 设计的旧版渲染引擎时,向 QRhi 的迁移通常涉及重新设计:从仅有一个render 步骤(该步骤将复制、上传、清除缓冲区和发出绘制调用等操作混合在一起)转变为清晰分离的、 两阶段的prepare -render 架构:其中render 步骤仅启动渲染通道并记录绘制调用,而所有资源创建以及更新、上传和复制的排队操作均在之前的prepare 步骤中完成。
目前 QRhi 不允许自由创建和提交命令缓冲区。 未来这一限制可能会在一定程度上被解除,特别是如果引入了计算支持的话,但明确定义的frame-start 和frame-end 点,结合专用的“帧”命令缓冲区(其中frame-end 表示呈现),仍将是主要的工作方式,因为这最适合 Qt 的各种 UI 技术。
多线程
QRhi 实例及其关联资源可在任何线程上创建和使用,但所有操作必须仅限于该单一线程。当在应用程序中向多个 QWindow 渲染时,通常建议为每个窗口分配一个专用线程和 QRhi 实例,因为这可以消除因向多个窗口呈现而导致的意外限速问题。 从概念上讲,这与Qt Quick 场景图在直接使用OpenGL时多线程渲染循环的工作方式相同:每个窗口一个线程,每个线程一个QOpenGLContext 。在迁移到QRhi时,只需将QOpenGLContext 替换为QRhi,从而使迁移过程变得简单直接。
对于外部创建的原生对象(例如通过QRhiGles2NativeHandles 传递的 OpenGL 上下文),应用程序需确保它们不会被其他线程误用。
不同 QRhi 实例之间无法共享资源。这是有意为之的设计,因为 QRhi 隐藏了大部分与队列、命令缓冲区及资源同步相关的任务,且未为此提供任何 API。 然而,从多个线程安全且高效地并行使用图形资源与这些概念密切相关,因此目前尚不在讨论范围内,但未来可能会引入。
注意: Metal后端 要求在渲染线程上提供一个自动释放池,理想情况下应包裹渲染循环的每一轮迭代。当在主(GUI)线程上渲染时,QRhi 用户无需采取任何操作;但在使用独立的专用渲染线程时,这一点就变得至关重要。
资源同步
QRhi 未提供用于资源屏障或图像布局转换的 API。此类同步由后端在适用情况下(例如 Vulkan)通过按需跟踪资源使用情况来隐式完成。缓冲区和图像屏障会在渲染或计算阶段开始前被插入,对应用程序而言是透明的。
注意: 渲染或计算阶段内的资源 ,在该阶段内应绑定到单一用途。 例如,一个缓冲区可以在单个处理阶段中用作顶点缓冲区、索引缓冲区、统一缓冲区或存储缓冲区,但不能在同一处理阶段中同时兼具这些用途。不过,完全可以将一个缓冲区在计算处理阶段用作存储缓冲区,随后在渲染处理阶段用作顶点缓冲区——前提是该缓冲区在创建时已声明了这两种用途。
注意: 在某些情况下,纹理 对此规则有所放宽,因为即使在同一通道内,也支持对同一纹理的两个子资源(通常是两个不同的 Mip 级别)进行不同的访问(一个用于加载,一个用于存储)。
资源复用
从用户的角度来看,在调用QRhiResource::destroy() 之后,QRhiResource 即可立即被重用。除了交换链(swapchains)之外,对已创建的对象调用create() 会隐式执行destroy() 。这提供了一个便捷的捷径,可以重用QRhiResource 实例并使用不同的参数,同时底层会生成一个新的本机图形对象。
重复使用同一对象的重要性在于,某些对象会引用其他对象:例如,一个QRhiShaderResourceBindings 可以引用QRhiBuffer 、QRhiTexture 和QRhiSampler 实例。如果在后续帧中需要调整其中一个缓冲区的大小或更改采样器参数,那么销毁并创建一个全新的QRhiBuffer 或QRhiSampler 会导致所有对旧实例的引用失效。 只需通过QRhiBuffer::setSize()或类似方法修改相应参数,然后调用QRhiBuffer::create(),一切便会按预期运行,且完全无需触碰QRhiShaderResourceBindings ——尽管在底层,QRhiBuffer 很可能已被一个全新的原生缓冲区所替代。
QRhiBuffer *ubuf = rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 256);
ubuf->create();
QRhiShaderResourceBindings *srb = rhi->newShaderResourceBindings()
srb->setBindings({
QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, ubuf)
});
srb->create();
// ...
// now in a later frame we need to grow the buffer to a larger size
ubuf->setSize(512);
ubuf->create(); // same as ubuf->destroy(); ubuf->create();
// srb needs no changes whatsoever, any references in it to ubuf
// stay valid. When it comes to internal details, such as that
// ubuf may now be backed by a completely different native buffer
// resource, that is is recognized and handled automatically by the
// next setShaderResources().QRhiTextureRenderTarget 提供了相同的契约:即使渲染目标创建后,其关联的纹理或渲染缓冲区之一已被重建(通过对其调用create() ),调用QRhiCommandBuffer::beginPass() 也是安全的。 这使得应用程序可以通过在QRhiTexture 上设置新的像素尺寸并调用create()来调整纹理的大小,从而在底层创建一个全新的本机纹理资源,而无需更新QRhiTextureRenderTarget ,因为这将在beginPass()中隐式完成。
池化对象
除了资源之外,还有池化对象,例如QRhiResourceUpdateBatch 。可以通过next 函数(如nextResourceUpdateBatch())获取实例。在此情况下,调用方并不拥有返回的实例。 此处的唯一有效操作方式是调用QRhiResourceUpdateBatch 上的函数,然后将其传递给QRhiCommandBuffer::beginPass()或QRhiCommandBuffer::endPass()。这些函数会负责将批处理归还给池。此外,还可以通过调用QRhiResourceUpdateBatch::release()来“取消”批处理,使其未经处理即归还给池。
因此,典型的模式如下:
QRhiResourceUpdateBatch *resUpdates = rhi->nextResourceUpdateBatch();
// ...
resUpdates->updateDynamicBuffer(ubuf, 0, 64, mvp.constData());
if (!image.isNull()) {
resUpdates->uploadTexture(texture, image);
image = QImage();
}
// ...
QRhiCommandBuffer *cb = m_sc->currentFrameCommandBuffer();
// note the last argument
cb->beginPass(swapchain->currentFrameRenderTarget(), clearCol, clearDs, resUpdates);Swapchain 的具体特性
QRhiSwapChain 由于 Swapchains 的特殊性质,其具备一些特殊的语义。
- 它没有
create(),而是提供了一个QRhiSwapChain::createOrResize()。反复调用此函数的效果,与先调用QRhiSwapChain::destroy()再调用QRhiSwapChain::createOrResize()的效果并不相同。这是因为SwapChain通常具备处理缓冲区调整大小的情况的方法,其效率高于直接强行销毁并从头重建。 - 在QWindow 的底层 QPlatformWindow(及其关联的原生窗口对象)被销毁之前,必须通过调用destroy() 或销毁该对象来释放一个活动的QRhiSwapChain 。 不应推迟此操作,因为当原生窗口不再存在时(例如由于调用QWindow::close()导致QPlatformWindow被销毁),释放交换链可能会引发问题(而且某些API,如Vulkan,明确禁止此操作)。 因此,每当目标QWindow 发送QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed 事件时,都必须释放交换链。如果该事件在QWindow 销毁之前未到达(这在使用QCoreApplication::quit()时可能会发生),则应在事件循环退出后检查QWindow::handle(),并在其不为空时(意味着底层原生窗口仍然存在)调用交换链释放操作。
所有权
一般规则是不进行所有权转移。使用已存在的图形设备创建 QRhi 并不意味着 QRhi 获得了该设备对象的所有权。同样,当通过QRhi::nativeHandles() 或QRhiTexture::nativeTexture() “导出”设备或纹理对象时,也不会转让所有权。最重要的是,在结构体中或通过设置器传递指针不会转移所有权。
故障排除与性能分析
错误报告
诸如QRhi::create() 以及资源类的create() 成员函数(例如QRhiBuffer::create())会通过返回值(分别是nullptr 或false )来指示失败。在使用QShader 时,若传递给函数的数据无法成功反序列化,则QShader::fromSerialized() 将返回一个无效的QShader (此时isValid() 会返回false )。 某些函数(特别是beginFrame())有时也会报告“软故障”,例如FrameOpSwapChainOutOfDate ,这并不表示发生了不可恢复的错误,而应被视为“稍后重试”的响应。
警告和错误可能会随时通过qWarning() 输出到调试日志中。因此,建议始终检查应用程序的输出。
可以通过以下日志类别启用额外的调试消息。默认情况下,这些类别的消息不会被打印,除非通过QLoggingCategory 或环境变量QT_LOGGING_RULES 显式启用。为了更好地与Qt Quick 协同工作,环境变量QSG_INFO 也会启用这些调试输出。
qt.rhi.general
此外,应用程序可以从已成功初始化的 QRhi 中查询QRhi backend name 和graphics device information 。如果需要,即使在生产构建中,这些信息也可以显示给用户或存储在应用程序日志中。
排查渲染问题
当渲染结果与预期不符,或应用程序出现问题时,请务必考虑使用原生 3D API 的调试和验证功能进行检查。由于复制底层层中已有的海量功能并不合理,因此 QRhi 本身提供的错误检查功能较为有限。
- 对于 Vulkan,控制Vulkan 验证层不属于 QRhi 的范围,而是可以通过配置QVulkanInstance 并指定相应的层来实现。 例如,在QVulkanInstance 上调用create()之前,请先调用
instance.setLayers({ "VK_LAYER_KHRONOS_validation" });。(请注意,这假设验证层已实际安装并可用,例如来自Vulkan SDK)默认情况下,QVulkanInstance 会将Vulkan调试消息重定向到qDebug ,这意味着验证消息会像其他Qt警告一样被打印出来。 - 对于 Direct 3D 11 和 12,可以通过在相应的init params struct 中切换
enableDebugLayer标志来请求启用调试层的图形设备。在 Direct 3D 12 中,只要调试层支持消息回调,这些消息就会通过qDebug 输出,就像 Vulkan 验证消息一样。 否则,以及在 Direct 3D 11 环境下,这些消息将显示在调试输出中,用户可在Qt Creator 的消息面板中查看,或通过DebugView 等工具进行查看。 - 对于 Metal,控制 Metal 验证功能不在 QRhi 的范围之内。若要启用验证,请在设置环境变量
METAL_DEVICE_WRAPPER_TYPE=1的条件下运行应用程序,或在 XCode 中运行应用程序。在较新的 XCode 和 macOS 版本中,可能还存在其他设置和环境变量。例如,请参阅此页面。
帧捕获与性能分析
一个使用 QRhi 将内容渲染到窗口中、同时在底层依赖 3D API 的 Qt 应用程序,至少从窗口化和图形处理管道的角度来看,与使用相同 3D API 的任何其他(非 Qt)应用程序并无二致。 这意味着用于调试和分析涉及 3D 图形的应用程序(如游戏)的工具和方法,同样适用于此类 Qt 应用程序。
以下列举了若干可深入分析使用 QRhi 的 Qt 应用程序渲染内部机制的工具,其中也包括基于Qt Quick 和Qt Quick 3D 的项目:
- RenderDoc允许在 Windows 和 Linux 系统上对使用 OpenGL、Vulkan、D3D11 或 D3D12 的应用程序进行帧捕获,并检查记录的命令和渲染管线状态。 当需要排查 3D 场景中某些部分为何未按预期显示时,RenderDoc 通常是检查渲染管线各阶段及相关状态、并发现缺失或错误值的快速高效方法。在 Qt 自身的开发过程中,该工具也被广泛应用。
- 对于基于 NVIDIA 的系统,Nsight Graphics在 Windows 和 Linux 平台上提供了一款图形调试工具。除了分析帧中的命令和渲染管线外,这些厂商专有的工具还能查看时序和硬件性能信息,而这并非简单的帧捕获所能提供的。
- 对于基于 AMD 的系统,可使用Radeon GPU Profiler更深入地了解应用程序的渲染过程及其性能。
- 显示实时性能信息的叠加层同样非常有用,通常比在应用程序内部实现简单的每秒帧数计数器更可取,因为它们更可靠且能显示更多信息。PresentMon 就是其中一个例子,它支持多家厂商的图形硬件。
- 由于 QRhi 支持 Direct 3D 12,因此使用PIX(一款针对 Windows 平台 DirectX 12 游戏的性能调优和调试工具)也是可行的选择。
- 在 macOS 上,可使用XCode Metal 调试器来捕获和分析帧数据,以调查性能细节并调试着色器。在 macOS 13 中,还可以通过设置环境变量
MTL_HUD_ENABLED=1来启用一个叠加层,为任何基于 Metal 的窗口显示帧率及其他信息。
在移动和嵌入式平台上,可能有由 GPU 或 SoC 厂商提供的、针对特定厂商和平台的工具,可用于对使用 OpenGL ES 或 Vulkan 的应用程序进行性能分析。
在捕获帧时,请注意:只要 QRhi 的debug markers were enabled 以及所使用的图形 API 支持此功能,即可通过调试标记为对象和命令组命名。要为命令流添加注释,请调用debugMarkBegin()、debugMarkEnd() 和/或debugMarkMsg()。这在包含多个渲染通道的大型帧中尤为有用。 通过在调用create()之前先调用setName()来为资源命名。
若要在应用程序中对 CPU 和 GPU 端进行基本计时测量,可使用QElapsedTimer 和QRhiCommandBuffer::lastCompletedGpuTime()。目前后者仅在部分图形 API 中可用,且需要通过QRhi::EnableTimestamps 标志进行启用。
资源泄漏检查
在销毁 QRhi 对象时,若未正确销毁其创建的所有缓冲区、纹理及其他资源,当应用程序处于调试构建状态,或环境变量QT_RHI_LEAK_CHECK 被设置为非零值时,系统会将相关警告打印到调试输出中。这是发现应用程序渲染逻辑中资源处理设计问题的简便方法。 但请注意,某些平台和底层图形 API 可能也会执行自己的内存分配和资源泄漏检测,而 Qt 对此无法直接控制。例如,在使用 Vulkan 时,如果拥有图形内存分配的资源在 QRhi 之前未被销毁,内存分配器可能会在调试构建中触发失败的断言。 此外,当启用 Vulkan 验证层时,它会针对未释放的原生图形资源发出警告。同样,在 Direct 3D 中,如果应用程序未按正确顺序销毁 QRhi 及其资源,可能会打印出关于未释放 COM 对象的警告。
另请参阅 RHI 窗口示例、QRhiCommandBuffer 、QRhiResourceUpdateBatch 、QRhiShaderResourceBindings 、QShader 、QRhiBuffer 、QRhiTexture 、QRhiRenderBuffer 、QRhiSampler 、QRhiTextureRenderTarget 、QRhiGraphicsPipeline 、QRhiComputePipeline 以及QRhiSwapChain 。
成员类型文档
[alias, since 6.10] QRhi::AdapterList
QVector 的同义词 <QRhiAdapter *>。
该 typedef 是在 Qt 6.10 中引入的。
enum QRhi::BeginFrameFlag
flags QRhi::BeginFrameFlags
QRhi::beginFrame() 的标志值
BeginFrameFlags 类型是QFlags<BeginFrameFlag> 的 typedef。它存储 BeginFrameFlag 值的按“或”运算组合。
enum QRhi::EndFrameFlag
flags QRhi::EndFrameFlags
QRhi::endFrame() 的标志值
| 常量 | 值 | 说明 |
|---|---|---|
QRhi::SkipPresent | 1 << 0 | 指定不将任何呈现命令加入队列,也不调用 swapBuffers。这样就不会呈现任何图像。 不建议生成多个均设置了此标志的帧(例如,用于基准测试的情况除外——但请注意,在等待命令完成而不进行呈现时,不同后端的行为可能不同,因此它们之间的结果无法相互比较) |
EndFrameFlags 类型是QFlags<EndFrameFlag> 的 typedef。它存储 EndFrameFlag 值的按“或”运算组合。
enum QRhi::Feature
用于指示当前使用的后端支持哪些功能的标志值。
| 常量 | 值 | 描述 |
|---|---|---|
QRhi::MultisampleTexture | 1 | 表示支持采样数大于 1 的纹理。实际上,OpenGL ES 3.1 之前的版本以及 OpenGL 3.0 之前的版本均不支持此功能。 |
QRhi::MultisampleRenderBuffer | 2 | 表示支持采样数大于 1 的渲染缓冲区。实际上,OpenGL ES 2.0 不支持此功能,除非存在相关扩展,否则 OpenGL 2.x 可能也不支持。 |
QRhi::DebugMarkers | 3 | 表示支持调试标记组(因此也支持QRhiCommandBuffer::debugMarkBegin())。 |
QRhi::Timestamps | 4 | 表示支持命令缓冲区时间戳。这与QRhiCommandBuffer::lastCompletedGpuTime() 相关。预计 Metal、Vulkan、Direct 3D 11 和 12 以及 3.3 及更高版本的 OpenGL 上下文均支持此功能。 然而,对于其中某些 API 而言,对时间戳查询的支持在技术上属于可选功能,因此无法保证该功能在所有实现中均受支持。 |
QRhi::Instancing | 5 | 表示支持实例化绘制。实际上,OpenGL ES 2.0 以及 OpenGL 3.2 或更早版本不支持此功能。 |
QRhi::CustomInstanceStepRate | 6 | 表示支持除 1 以外的实例步长。实际上,OpenGL 始终不支持此功能。此外,在未启用 VK_EXT_vertex_attribute_divisor 的情况下运行 Vulkan 1.0,也会导致此功能被报告为 false。 |
QRhi::PrimitiveRestart | 7 | 表示在遇到索引值 0xFFFF(IndexUInt16 )或 0xFFFFFFFF(IndexUInt32 )时,至少对于某些基元拓扑,已启用基元组装重启功能。QRhi 将尝试在所有后端上启用此功能,但在某些情况下可能不被支持。 无法动态控制原始重启,因为某些 API 中,固定索引的原始重启始终处于启用状态。 应用程序必须假设,每当报告支持此功能时,上述索引值may 都会根据拓扑结构受到特殊处理。只要报告支持此功能,唯一两种在所有后端上都能保证原始重启行为完全一致的拓扑结构是LineStrip 和TriangleStrip 。 |
QRhi::NonDynamicUniformBuffers | 8 | 表示支持使用UniformBuffer 以及类型Immutable 或Static 创建缓冲区。若报告为不支持,则必须将统一(常量)缓冲区创建为Dynamic 。(无论如何,这都是推荐的做法) |
QRhi::NonFourAlignedEffectiveIndexBufferOffset | 9 | 表示支持未对齐至 4 字节的有效索引缓冲区偏移量(indexOffset + firstIndex * indexComponentSize )。若不支持,尝试调用drawIndexed() 并传入未对齐的有效偏移量可能会导致未定义的行为。这在 Metal 中尤为重要,该环境会报告此功能不支持。 |
QRhi::NPOTTextureRepeat | 10 | 表示对于非2的幂大小的纹理,支持Repeat 的环绕模式和mipmap过滤模式。实际上,只有在不支持GL_OES_texture_npot 的OpenGL ES 2.0实现中,此项才会为false。 |
QRhi::RedOrAlpha8IsRed | 11 | 表示RED_OR_ALPHA8 格式映射为单分量8位red 格式。当使用OpenGL ES或非核心配置文件上下文时,除OpenGL外,所有后端均符合此情况。 此时将改用GL_ALPHA ,即一种单组件 8 位alpha 格式。使用这种特殊纹理格式可实现创建纹理的单一代码路径,由后端决定实际格式,同时可通过功能标志选择适合的着色器变体来采样纹理。 |
QRhi::ElementIndexUint | 12 | 表示索引缓冲区支持 32 位无符号整数元素。实际上,除在未启用必要扩展的纯 OpenGL ES 2.0 实现上运行外,此处均成立。当为 false 时,索引缓冲区仅支持 16 位无符号元素。 |
QRhi::Compute | 13 | 表示支持计算着色器、图像加载/存储以及存储缓冲区。早于 4.3 版的 OpenGL 和早于 3.1 版的 OpenGL ES 不支持计算功能。 |
QRhi::WideLines | 14 | 表示支持宽度不为 1 的线条。若报告不支持,则图形管线状态中设置的线宽将被忽略。对于某些后端(如 D3D11、D3D12、Metal),此值始终为 false。 在 Vulkan 中,该值取决于具体实现。在 OpenGL 中,核心配置上下文不支持宽线。 |
QRhi::VertexShaderPointSize | 15 | 表示会考虑顶点着色器中通过gl_PointSize 设置的栅格化点大小。若报告为不支持,则不支持绘制大小非1的点。此时在着色器中设置gl_PointSize 仍然有效,但会被忽略。 (例如,在生成 HLSL 时,该赋值会被从生成的代码中静默省略)请注意,某些 API(如 Metal、Vulkan)要求在绘制点时必须在着色器中显式设置点大小,即使大小为 1 也是如此,因为它们不会自动默认设为 1。 |
QRhi::BaseVertex | 16 | 表示drawIndexed()支持vertexOffset 参数。若报告为不支持,则索引绘制中的vertexOffset值将被忽略。实际上,此功能在低于3.2版本的OpenGL和OpenGL ES中不被支持,在旧版iOS设备(包括iOS模拟器)上的Metal中也不被支持。 |
QRhi::BaseInstance | 17 | 表示实例化绘制命令支持 `firstInstance ` 参数。 当报告为不支持时,firstInstance 值将被忽略,且实例 ID 从 0 开始。实际上,此功能在旧版 iOS 设备(包括 iOS 模拟器)上的 Metal 以及所有版本的 OpenGL 中均不被支持。后者是因为 OpenGL ES 完全不支持带有基实例的绘制调用。 目前,QRhi 的 OpenGL 后端同样未为 OpenGL(非 ES)实现该功能,因为受 GLES 限制,可移植应用程序在实际中无法依赖非零基实例。如果应用程序仍选择这样做,则还应了解 InstanceIndexIncludesBaseInstance 功能。 |
QRhi::TriangleFanTopology | 18 | 表示QRhiGraphicsPipeline::setTopology() 支持QRhiGraphicsPipeline::TriangleFan 。实际上,此功能在 Metal 和 Direct 3D 11/12 中将不被支持。 |
QRhi::ReadBackNonUniformBuffer | 19 | 表示对于使用方式不同于UniformBuffer的QRhiBuffer 实例,支持reading buffer contents 。实际上,此功能在OpenGL ES 2.0中将不被支持。 |
QRhi::ReadBackNonBaseMipLevel | 20 | 表示在读回纹理内容时,支持指定除 0 以外的 MIP 级别。若不支持,在QRhiReadbackDescription 中指定非零级别将导致返回全零图像。实际上,此功能在 OpenGL ES 2.0 中不被支持。 |
QRhi::TexelFetch | 21 | 表示着着色器中可用 texelFetch() 和 textureLod() 函数。实际上,在 OpenGL ES 2.0 和 OpenGL 2.x 上下文中,这将被报告为不支持,因为 GLSL 100 es 及 130 之前的版本不支持这些函数。 |
QRhi::RenderToNonBaseMipLevel | 22 | 表示在创建以QRhiTexture 作为颜色附件的QRhiTextureRenderTarget 时,支持指定非零的mip级别。若不支持,当目标mip级别不为零时,create()将失败。实际上,此功能在OpenGL ES 2.0中将不被支持。 |
QRhi::IntAttributes | 23 | 表示支持为着色器管道指定带符号和无符号整数类型的输入属性。如果不支持,QRhiGraphicsPipeline::create() 将成功执行但会显示警告消息,且目标属性的值将出现异常。实际上,OpenGL ES 2.0 和 OpenGL 2.x 不支持此功能。 |
QRhi::ScreenSpaceDerivatives | 24 | 表示着着色器中支持 dFdx()、dFdy() 和 fwidth() 等函数。实际上,在未启用 GL_OES_standard_derivatives 扩展的情况下,OpenGL ES 2.0 不支持此功能。 |
QRhi::ReadBackAnyTextureFormat | 25 | 表示对于任何QRhiTexture::Format ,读回纹理内容均可预期正常工作。除 OpenGL 以外的后端通常会为此功能返回 true。当报告为 false 时(这通常发生在 OpenGL 中),仅保证支持QRhiTexture::RGBA8 和QRhiTexture::BGRA8 这两种格式的读回操作。 此外,在 OpenGL(但不包括 OpenGL ES)中,还支持读取每分量 1 字节的格式QRhiTexture::R8 和QRhiTexture::RED_OR_ALPHA8 。只要实现提供了支持,OpenGL 可能也支持读取浮点格式QRhiTexture::RGBA16F 和 RGBA32F,但如该标志所示,QRhi 无法对此提供任何保证。 |
QRhi::PipelineCacheDataLoadSave | 26 | 表示pipelineCacheData() 和setPipelineCacheData() 函数可正常工作。若不支持,这些函数将不执行任何操作,检索到的 blob 始终为空,因此无法通过检索并在后续应用程序运行时重新加载管道缓存内容来获得任何好处。 |
QRhi::ImageDataStride | 27 | 表示支持在纹理上传中为原始图像数据指定自定义步长(行长度)。当不支持时(例如底层 API 为 OpenGL ES 2.0 且不支持 GL_UNPACK_ROW_LENGTH 时),不得使用QRhiTextureSubresourceUploadDescription::setDataStride()。 |
QRhi::RenderBufferImport | 28 | 表示支持QRhiRenderBuffer::createFrom()。对于大多数图形 API 而言,这并不合理,因为QRhiRenderBuffer 在内部封装了纹理对象,这与QRhiTexture 类似。 然而,在 OpenGL 中,渲染缓冲区对象作为 API 中的独立对象类型存在,并且在某些环境中(例如,当需要将渲染缓冲区对象与 EGLImage 对象关联时),允许使用QRhiRenderBuffer 封装现有的 OpenGL 渲染缓冲区对象非常重要。 |
QRhi::ThreeDimensionalTextures | 29 | 表示支持 3D 纹理。实际上,在低于 3.0 版本的 OpenGL 和 OpenGL ES 中,此功能将不被支持。 |
QRhi::RenderTo3DTextureSlice | 30 | 表示支持渲染到3D纹理的切片中。由于依赖于VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT(这是Vulkan 1.1的功能),因此在Vulkan 1.0中可能不支持此功能。 |
QRhi::TextureArrays | 31 | 表示支持纹理数组,且QRhi::newTextureArray() 函数可用。请注意,即使不支持纹理数组,纹理数组仍可使用,因为这两者是两个独立的功能。 |
QRhi::Tessellation | 32 | 表示支持细分控制和评估阶段。当报告为支持时,QRhiGraphicsPipeline 的拓扑结构可设置为 `Patches`,控制点数量可通过 `setPatchControlPointCount()` 设置,且细分控制和评估着色器可在 `QRhiShaderStage ` 列表中指定。 细分着色器在不同 API 之间存在可移植性问题(例如,由于外壳着色器的结构方式,将 GLSL/SPIR-V 转换为 HLSL 存在困难,而 Metal 使用的细分管道与其他 API 略有不同),因此即使所有底层 API 都实现了基本功能,仍可能出现意料之外的问题。 特别是对于 Direct 3D,必须将手动编写的 HLSL 外壳着色器和域着色器分别注入到每个QShader 中,以用于曲面细分控制和评估阶段,因为 qsb 无法从 SPIR-V 生成这些着色器。请注意,应避免使用等线曲面细分,因为并非所有后端都支持该功能。 不同后端之间可移植的补丁控制点最大数量为 32。 |
QRhi::GeometryShader | 33 | 表示支持几何着色器阶段。若受支持,可在QRhiShaderStage 列表中指定几何着色器。 在QRhi 中,几何着色器被视为实验性功能,预计仅在 Vulkan、Direct 3D 11 和 12、OpenGL(3.2+)以及 OpenGL ES(3.2+)中受支持,前提是实现是在运行时报告支持该功能的。 从 Qt 6.11 开始,几何着色器会自动转换为 HLSL,因此不再需要手动注入 HLSL 几何着色器(但请注意,gl_in 以及 gl_in[0].gl_Position 等表达式不受支持; 而应将位置作为顶点着色器中的输出变量传递)。Metal 不支持几何着色器。 |
QRhi::TextureArrayRange | 34 | 表示对于texture arrays ,可以指定向着色器暴露的范围。通常所有数组层都会被暴露,具体选择哪个层由着色器决定(通过在采样sampler2DArray 时传递给texture()的第三个坐标)。 若受支持,在调用building 或importing 处理原生纹理之前,调用QRhiTexture::setArrayRangeStart()和QRhiTexture::setArrayRangeLength()将产生效果,从而仅从数组中选择指定的范围。 在某些特殊情况下(例如处理加速视频解码和 Direct 3D 11 时),这将必不可少,因为同时具有D3D11_BIND_DECODER 和D3D11_BIND_SHADER_RESOURCE 的纹理数组,只有在选择单个数组层时才能作为着色器资源使用。 请注意,所有这些仅适用于将纹理用作QRhiShaderResourceBinding::SampledTexture 或QRhiShaderResourceBinding::Texture 着色器资源的情况,且与图像加载/存储不兼容。由于该功能无法很好地映射到所有图形API,因此仅在某些后端上可用,而且它本来就只是为了支持特殊情况而设计的。 实际上,预计 Direct3D 11/12 和 Vulkan 会支持此功能。 |
QRhi::NonFillPolygonMode | 35 | 表示QRhiGraphicsPipeline 支持将PolygonMode设置为默认值Fill以外的模式。将模式更改为Line的常见用例是实现线框渲染。但这并非OpenGL ES的核心功能,在Vulkan中也是可选的,且某些移动端GPU可能不提供此功能。 |
QRhi::OneDimensionalTextures | 36 | 表示支持一维纹理。实际上,此功能在 OpenGL ES 上不被支持。 |
QRhi::OneDimensionalTextureMipmaps | 37 | 表示支持生成一维纹理的米普贴图。实际上,在未报告支持 OneDimensionalTextures、Metal 以及 Direct 3D 12 的后端上,此功能将不被支持。 |
QRhi::HalfAttributes | 38 | 表示支持为着色器管道指定采用半精度(16 位)浮点类型的输入属性。若不支持,QRhiGraphicsPipeline::create() 调用将成功执行但会显示警告消息,且目标属性的值将出现异常。 实际上,某些 OpenGL ES 2.0 和 OpenGL 2.x 实现将不支持此功能。 请注意,虽然 Direct3D 11/12 确实支持半精度输入属性,但不支持 half3 类型。D3D 后端会将 half3 属性作为 half4 传递。为确保跨平台兼容性,应将 half3 输入补零至 8 字节。 |
QRhi::RenderToOneDimensionalTexture | 39 | 表示支持一维纹理渲染目标。实际上,在未报告支持 OneDimensionalTextures 的后端以及 Metal 上,此功能将不被支持。 |
QRhi::ThreeDimensionalTextureMipmaps | 40 | 表示支持生成 3D 纹理 MIP 贴图。通常,从 Qt 6.10 开始的所有后端都支持此功能。 |
QRhi::MultiView | 41 | 表示支持多视图(例如VK_KHR_multiview)。对于 OpenGL ES 2.0、Direct 3D 11 以及未实现 `GL_OVR_multiview2 ` 的 OpenGL (ES) 实现,此功能将不被支持。 在 Vulkan 1.1 及更高版本以及 Direct 3D 12 中,多视图通常受支持。当报告为受支持时,通过使用引用纹理数组且设置了multiViewCount 的QRhiColorAttachment 来创建QRhiTextureRenderTarget ,即可记录使用多视图渲染的渲染通道。 此外,该渲染通道中使用的任何QRhiGraphicsPipeline 都必须具有the same view count set 。请注意,多视图功能仅在与2D纹理数组结合使用时才可用。它不能用于优化渲染到单独的纹理中(例如,分别渲染到左眼和右眼的两个纹理)。 相反,多视图渲染通道的目标始终是一个纹理数组,系统会自动将渲染结果渲染到与每个视图对应的图层(数组元素)中。因此,该功能同时也依赖于 TextureArrays。 多视图渲染不支持与曲面细分或几何着色器结合使用。有关多视图渲染的更多详细信息,请参阅QRhiColorAttachment::setMultiViewCount()。此枚举值在 Qt 6.7 中引入。 |
QRhi::TextureViewFormat | 42 | 表示在QRhiTexture 上设置view format 有效。当报告为受支持时,设置读取(采样)或写入(渲染目标/图像加载-存储)视图模式会更改纹理的视图格式。当不支持时,设置视图格式将无效。 请注意,Qt 3D 无法知晓或控制底层 3D API 及其实现中的格式兼容性或资源视图规则。传入不合适或不兼容的格式可能会导致错误和未定义的行为。 此功能主要旨在允许将渲染结果“转换”为非 sRGB 格式,从而避免在着色器写入时发生不必要的线性→sRGB 转换。其他类型的转换是否有效取决于底层 API。目前已针对 Vulkan 和 Direct 3D 12 实现。 在 D3D12 中,仅当支持CastingFullyTypedFormatSupported 时此功能才可用,详见https://microsoft.github.io/DirectX-Specs/d3d/RelaxedCasting.html(请注意,QRhi 始终为纹理使用完全类型化的格式)。此枚举值自 Qt 6.8 起引入。 |
QRhi::ResolveDepthStencil | 43 | 表示支持解析多采样深度或深度-模板纹理。否则,setting a depth resolve texture 将无法正常工作,必须避免使用。Direct 3D 11 和 12 不支持解析深度/深度-模板格式,因此这些版本将永远不支持此功能。 Vulkan 1.0 没有用于请求解析深度-模板附件的 API。因此,在 Vulkan 中,此功能仅在 Vulkan 1.2 及更高版本中受支持,而在 1.1 实现中需具备相应的扩展才能使用。 提供此功能是为了应对极少数必须将深度纹理解析为非多采样深度纹理的情况,例如渲染到 OpenXR 提供的深度纹理(XR_KHR_composition_layer_depth)时。该枚举值自 Qt 6.8 起引入。 |
QRhi::VariableRateShading | 44 | 表示支持按渲染(按管道)可变速率着色。当报告为支持时,QRhiCommandBuffer::setShadingRate() 方法有效,并对在标志中声明了QRhiGraphicsPipeline::UsesShadingRate 的QRhiGraphicsPipeline 对象产生影响。 调用QRhi::supportedShadingRates() 可检查支持哪些速率。(1x1 始终受支持,其他典型值包括 2x2、1x2、2x1、2x4、4x2、4x4)。 预计 Direct 3D 12 和 Vulkan 将支持此功能,前提是运行时使用的实现和 GPU 支持 VRS。此枚举值已于 Qt 6.9 中引入。 |
QRhi::VariableRateShadingMap | 45 | 表示可以基于图像指定着色率。该“图像”不一定是纹理,也可能是原生 3D API 对象,具体取决于运行时底层后端和图形 API。 实际上,只要 GPU 足够新且支持 VRS,预计 Direct 3D 12、Vulkan 和 Metal 都将支持此功能。若要检查是否支持 D3D12/Vulkan 风格的基于图像的 VRS,请改用 `VariableRateShadingMapWithTexture`。 当报告支持此功能时,有两种可能:若 VariableRateShadingMapWithTexture 也为 true,则QRhiShadingRateMap 会通过接受QRhiTexture 参数的 createFrom() 重载方法消耗QRhiTexture 对象。 当 `VariableRateShadingMapWithTexture` 为 false 时,`QRhiShadingRateMap ` 会使用其他类型的原生对象,例如在 Metal 环境中使用 `MTLRasterizationRateMap`。在此情况下,请使用接受 `NativeShadingRateMap` 参数的 `createFrom()` 重载方法。该枚举值自 Qt 6.9 起引入。 |
QRhi::VariableRateShadingMapWithTexture | 46 | 表示支持通过常规纹理以图像形式指定着色速率。实际上,Direct 3D 12 和 Vulkan 可能支持此功能。该枚举值自 Qt 6.9 起引入。 |
QRhi::PerRenderTargetBlending | 47 | 表示支持按渲染目标进行混合,即 MRT 帧缓冲区中的不同渲染目标可以具有不同的混合模式。实际上,除 OpenGL ES 之外,预计所有平台均支持此功能;而在 OpenGL ES 中,该功能仅在 GLES 3.2 实现中可用。此枚举值于 Qt 6.9 中引入。 |
QRhi::SampleVariables | 48 | 表示 gl_SampleID、gl_SamplePosition、gl_SampleMaskIn 和 gl_SampleMask 变量在片元着色器中可用。 实际上,除 OpenGL ES 之外,其他所有环境均应支持此功能;而在 OpenGL ES 中,该功能仅在 GLES 3.2 及以上版本中可用。该枚举值自 Qt 6.9 起引入。 |
QRhi::InstanceIndexIncludesBaseInstance | 49 | 表示gl_InstanceIndex 的值中包含基实例(即渲染调用中的firstInstance 参数)。当此功能不被支持,但BaseInstance被支持时,这表示gl_InstanceIndex 始终从0开始,而非从基值开始。 实际上,目前 Direct 3D 11 和 12 即属于这种情况。对于 Vulkan 和 Metal,预计该功能将始终被报告为受支持。该枚举值自 Qt 6.11 起引入。 |
QRhi::DepthClamp (since Qt 6.11) | 50 | 表示支持启用深度裁剪。当报告为不支持时(例如 OpenGL ES、未启用相关扩展的 3.2 之前版本的 OpenGL,以及 iOS 模拟器上的 Metal),调用QRhiGraphicsPipeline::setDepthClamp() 并传入参数true 将无效。 |
QRhi::DrawIndirect (since Qt 6.12) | 51 | 表示drawIndirect() 和drawIndexedIndirect() 函数可用。实际上,除 OpenGL ES < 3.1 之外,预计所有环境均支持此功能。 |
QRhi::DrawIndirectMulti (since Qt 6.12) | 52 | 表示后端在drawIndirect() 和drawIndexedIndirect() 中原生支持 drawCount > 1。否则,RHI 将在 CPU 上发出多个绘制调用。实际上,预计 Vulkan 1.1 及以上、OpenGL 4.3 及以上和 D3D12 均支持此功能。 |
QRhi::ShaderDrawParameters (since Qt 6.12) | 53 | 表示着着色器中可使用gl_BaseInstance 、gl_BaseVertex 和gl_DrawID 这三个内置变量。实际上,预计 Vulkan 1.1 及以上版本,以及桌面版 OpenGL 4.6 或GL_ARB_shader_draw_parameters 均支持此功能。 |
enum QRhi::Flag
flags QRhi::Flags
描述了应启用哪些特殊功能。
| 常量 | 值 | 说明 |
|---|---|---|
QRhi::EnableDebugMarkers | 1 << 0 | 启用调试标记组。若未启用此功能,则无法使用框架调试功能(例如在外部 GPU 调试工具中显示调试组和自定义资源名称),且诸如QRhiCommandBuffer::debugMarkBegin() 之类的函数将变为无操作。请避免在生产构建中启用此功能,因为这可能会对性能产生轻微影响。当QRhi::DebugMarkers 功能未报告为受支持时,此设置无效。 |
QRhi::EnableTimestamps | 1 << 3 | 启用 GPU 时间戳收集。若未设置,QRhiCommandBuffer::lastCompletedGpuTime() 将始终返回 0。仅在需要时启用此功能,因为根据底层图形 API 的不同,可能会涉及少量额外工作(例如时间戳查询)。当QRhi::Timestamps 功能未报告为受支持时,此设置无效。 |
QRhi::PreferSoftwareRenderer | 1 << 1 | 指示后端应优先选择在 CPU 上通过软件渲染的适配器或物理设备。例如,在 Direct3D 中,通常存在一个名为“Basic Render Driver”的适配器,其DXGI_ADAPTER_FLAG_SOFTWARE 属性可用。设置此标志将要求后端优先选择该适配器,除非通过其他后端特有的方式强制指定了特定适配器。 对于 Vulkan,这对应于优先选择VK_PHYSICAL_DEVICE_TYPE_CPU 中的物理设备。当该选项不可用,或者无法判断适配器/设备是否基于软件时,该标志将被忽略。对于没有适配器/设备枚举概念和手段的图形 API,该标志也可能被忽略。 |
QRhi::EnablePipelineCacheDataSave | 1 << 2 | 启用管道缓存内容的检索(如适用)。若未设置,pipelineCacheData() 将始终返回一个空的 blob。对于不支持检索和恢复管道缓存内容的后端,该标志无效,且序列化的缓存数据始终为空。 该标志提供了一种可选机制,因为在某些后端中,维护相关数据结构的开销不容小觑。在 Vulkan 中,此功能直接映射到 VkPipelineCache、vkGetPipelineCacheData 和 VkPipelineCacheCreateInfo::pInitialData。 在 Direct3D 11 中,虽然不存在真正的管道缓存,但 HLSL 到 DXBC 的编译结果会被存储,并可通过此机制进行序列化/反序列化。 这使得应用程序在后续运行时,对于带有 HLSL 源代码(而非离线预编译的字节码)的着色器,可以跳过耗时的 D3DCompile() 调用。如果需要编译大量的 HLSL 源代码,这将极大提升启动和加载速度。 在 OpenGL 中,“管道缓存”是通过检索和加载着色器程序二进制文件来模拟的(如果驱动程序支持的话)。 在 Qt OpenGL 中,Qt 还提供了基于磁盘的额外着色器/程序二进制文件缓存机制。一旦设置此标志,对这些缓存的写入操作可能会被禁用,因为将程序二进制文件存储在多个缓存中并不合理。 |
QRhi::SuppressSmokeTestWarnings | 1 << 4 | 表示在相关后端中,某些非致命的QRhi::create()失败不应触发qWarning()调用。例如,在D3D11中,传递此标志会将一些警告消息(因QRhi::create()失败而出现)重新归类为调试输出,并归入常用的qt.rhi.general 日志类别。 这可用于具备回退逻辑的引擎(例如Qt Quick ),即这些引擎会使用不同的标志集(例如 PreferSoftwareRenderer)重试调用create(),以此隐藏首次create() 尝试失败时会打印到输出中的无条件警告。 |
Flags 类型是QFlags<Flag> 的 typedef。它存储了 Flag 值的按“或”运算组合。
enum QRhi::FrameOpResult
描述可能发生软故障的操作结果。
| 常量 | 值 | 描述 |
|---|---|---|
QRhi::FrameOpSuccess | 0 | 成功 |
QRhi::FrameOpError | 1 | 未指定的错误 |
QRhi::FrameOpSwapChainOutOfDate | 2 | 交换链在内部处于不一致状态。可以通过稍后尝试重复该操作(例如,beginFrame()) 来恢复。 |
QRhi::FrameOpDeviceLost | 3 | 图形设备已丢失。可通过释放并重新初始化所有由本机图形资源支持的对象,然后尝试重复该操作(例如,beginFrame ())来恢复。请参阅isDeviceLost ()。 |
enum QRhi::Implementation
描述QRhi 实例使用的是哪个图形API专用的后端。
| 常量 | 值 |
|---|---|
QRhi::Null | 0 |
QRhi::Vulkan | 1 |
QRhi::OpenGLES2 | 2 |
QRhi::D3D11 | 3 |
QRhi::D3D12 | 5 |
QRhi::Metal | 4 |
enum QRhi::ResourceLimit
描述要查询的资源限制。
| 常量 | 值 | 描述 |
|---|---|---|
QRhi::TextureSizeMin | 1 | 纹理的最小宽度和高度。该值通常为 1。系统会优雅地处理最小纹理大小,这意味着尝试创建大小为空的纹理时,系统会自动创建一个具有最小大小的纹理。 |
QRhi::TextureSizeMax | 2 | 纹理的最大宽度和高度。这取决于图形 API,有时也取决于平台或具体实现。通常该值在 4096 到 16384 之间。尝试创建超过此限值的纹理预计会失败。 |
QRhi::MaxColorAttachments | 3 | 当支持多渲染目标(MRT)时,QRhiTextureRenderTarget 的颜色附件最大数量。若不支持 MRT,该值为 1。否则通常为 8,但需注意 OpenGL 仅规定 4 为最小值,且某些 OpenGL ES 实现也仅提供 4 个。 |
QRhi::FramesInFlight | 4 | 后端可能“在处理中”保留的帧数:对于 Vulkan 或 Metal 等后端,当开始新帧时,若发现 CPU 已N - 1 帧领先于 GPU(因为在第current -N 帧中提交的命令缓冲区尚未完成),则由QRhi 负责进行阻塞。 此处返回的值即为 N,通常为 2。这对于直接集成图形 API 渲染功能的应用程序可能具有重要意义,因为此类渲染代码可能需要对资源(例如缓冲区)执行双重缓冲(若值为 2),这与QRhi 后端本身的行为类似。 当前帧槽索引(取值范围为 0、1、…、N-1,之后循环重置)可通过QRhi::currentFrameSlot() 获取。对于图形 API 不提供此类低级命令提交过程控制的后端,该值为 1。 请注意,即使该值为 1,流水线处理仍可能发生(某些后端,如 D3D11,在设计上会尝试启用此功能,例如通过针对统一缓冲区采用不会阻塞流水线的更新策略),但这不受QRhi 的控制,因此不会在此 API 中体现。 |
QRhi::MaxAsyncReadbackFrames | 5 | 在调用starting a new frame 后,保证异步纹理或缓冲区读回操作完成的submitted 帧数(包括包含读回操作的那一帧)。 |
QRhi::MaxThreadGroupsPerDimension | 6 | 可分发的计算工作组/线程组的最大数量。实际上即QRhiCommandBuffer::dispatch() 函数参数的最大值。通常为 65535。 |
QRhi::MaxThreadsPerThreadGroup | 7 | 单个本地工作组中的最大调用次数,或者换言之,线程组中的最大线程数。 实际上,这是计算着色器中local_size_x 、local_size_y 和local_size_z 三者乘积的最大值。典型值为 128、256、512、1024 或 1536。 请注意,OpenGL ES 和 Vulkan 均仅规定 128 为实现所需的最低限制。虽然在 Vulkan 中较为罕见,但某些面向移动/嵌入式设备的 OpenGL ES 3.1 实现仅支持规范规定的最小值。 |
QRhi::MaxThreadGroupX | 8 | 工作组/线程组在 X 方向上的最大大小。实际上即计算着色器中 `local_size_x ` 的最大值。通常为 256 或 1024。 |
QRhi::MaxThreadGroupY | 9 | 工作组/线程组在 Y 维度上的最大大小。实际上即计算着色器中local_size_y 的最大值。通常为 256 或 1024。 |
QRhi::MaxThreadGroupZ | 10 | 工作组/线程组在 Z 维度上的最大大小。实际上即计算着色器中 `local_size_z ` 的最大值。通常为 64 或 256。 |
QRhi::TextureArraySizeMax | 11 | 纹理数组的最大大小。通常在 256 至 2048 之间。若尝试将 `create a texture array ` 设置为包含更多元素,很可能导致失败。 |
QRhi::MaxUniformBufferRange | 12 | 统一缓冲区一次可向着色器暴露的字节数。在 OpenGL ES 2.0 和 3.0 的实现中,该值可能低至 3584 字节(224 个四分量向量,每个分量 32 位)。 在其他情况下,该值通常为 16384(1024 个 vec4)或 65536(4096 个 vec4)。 |
QRhi::MaxVertexInputs | 13 | 顶点着色器的输入属性数量。在QRhiVertexInputAttribute 中的位置必须在[0, MaxVertexInputs-1] 范围内。在 OpenGL ES 2.0 中,该值可能低至 8。在其他情况下,典型值为 16、31 或 32。 |
QRhi::MaxVertexOutputs | 14 | 顶点着色器的最大输出数量(4个分量向量out 变量)。在 OpenGL ES 2.0 中,该值最低可为 8;在 OpenGL ES 3.0 及部分 Metal 设备中,最低可为 15。其他情况下,典型值为 32。 |
QRhi::ShadingRateImageTileSize | 15 | 着色速率纹理的瓦片大小。如果不支持QRhi::VariableRateShadingMapWithTexture 功能,则为 0。 否则,该值如 16,表示 16x16 的瓦片大小。此时,(R8UI) 着色速率纹理中的每个字节即定义了一个 16x16 像素瓦片的着色速率。详情请参阅QRhiShadingRateMap 。 |
成员函数文档
[noexcept] QRhi::~QRhi()
析构函数。销毁后端并释放资源。
void QRhi::addCleanupCallback(const QRhi::CleanupCallback &callback)
注册一个callback ,该回调函数将在QRhi 被销毁时被调用。
回调函数执行时图形资源仍可用,因此这为应用程序提供了一个机会,可以干净地释放属于QRhi 的QRhiResource 实例。这对于管理存储在cache 类型对象中的资源的生命周期特别有用,其中缓存中包含QRhiResources或包含QRhiResources的对象。
另请参阅 ~QRhi()。
void QRhi::addCleanupCallback(const void *key, const QRhi::CleanupCallback &callback)
注册回调函数callback ,以便在QRhi 被销毁时调用该函数。此重载接受一个不透明指针key ,用于确保给定的回调函数仅被注册(并因此被调用)一次。
这是一个重载函数。
另请参阅 removeCleanupCallback()。
QRhi::Implementation QRhi::backend() const
返回此QRhi 的后端类型。
const char *QRhi::backendName() const
返回此QRhi 的后端类型(字符串形式)。
[static] const char *QRhi::backendName(QRhi::Implementation impl)
返回后端impl 的友好名称,通常为所用3D API的名称。
QRhi::FrameOpResult QRhi::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags = {})
启动一个新帧,目标为swapChain 中的下一个可用缓冲区。
一个帧由资源更新以及一个或多个渲染和计算过程组成。
flags 可能表示某些特殊情况。
使用交换链将内容渲染到QWindow 中的高级模式如下:
- 创建一个交换链。
- 每当表面大小与之前不同时,调用 `QRhiSwapChain::createOrResize()`。
- 在 `QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed` 上调用 `QRhiSwapChain::destroy()`。
- 然后在每一帧中:
beginFrame(sc); updates = nextResourceUpdateBatch(); updates->... QRhiCommandBuffer *cb = sc->currentFrameCommandBuffer(); cb->beginPass(sc->currentFrameRenderTarget(), colorClear, dsClear, updates); ... cb->endPass(); ... // more passes as necessary endFrame(sc);
成功时返回QRhi::FrameOpSuccess ,失败时返回另一个QRhi::FrameOpResult 值。其中某些情况应被视为软错误,即“稍后重试”类型的错误:当返回QRhi::FrameOpSwapChainOutOfDate 时,应通过调用QRhiSwapChain::createOrResize()来调整交换链的大小或更新交换链。 随后,应用程序应尝试生成一个新的帧。QRhi::FrameOpDeviceLost 表示图形设备已丢失,但通过释放所有资源(包括QRhi 本身),然后重新创建所有资源,可能仍可恢复。有关详细讨论,请参阅isDeviceLost()。
另请参阅 endFrame()、beginOffscreenFrame() 以及isDeviceLost()。
QRhi::FrameOpResult QRhi::beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi::BeginFrameFlags flags = {})
启动一个新的离屏帧。提供一个适用于在cb 中记录渲染命令的命令缓冲区。flags 用于指示某些特殊情况,这与beginFrame()的功能类似。
注意: 存储在 *cb中的 QRhiCommandBuffer 不归调用方所有。
也可以不使用交换链进行渲染。典型的用例是在完全离屏的应用程序中使用,例如通过渲染和回读来生成图像序列,而完全不显示窗口。
在屏幕内应用程序中(即beginFrame 、endFrame 、beginOffscreenFrame、endOffscreenFrame 、beginFrame 等)也可以使用。
当安排了texture 或buffer 的回读操作时,离屏帧可防止CPU在GPU仍在处理上一帧时生成新帧。这带来的附带效果是:如果安排了回读操作,则可以保证endOffscreenFrame()返回时,结果已可用。 针对交换链的帧则并非如此:虽然在此情况下 GPU 的利用率可能更高,但应用程序在处理读回操作时需要更加谨慎,因为endFrame() 与endOffscreenFrame() 不同,它无法保证读回结果在该时刻已可用。
不使用交换链渲染帧并随后读回帧内容的代码框架可能如下所示:
QRhiReadbackResult rbResult;
QRhiCommandBuffer *cb;
rhi->beginOffscreenFrame(&cb);
cb->beginPass(rt, colorClear, dsClear);
// ...
u = nextResourceUpdateBatch();
u->readBackTexture(rb, &rbResult);
cb->endPass(u);
rhi->endOffscreenFrame();
// image data available in rbResult另请参阅 endOffscreenFrame() 和beginFrame()。
QMatrix4x4 QRhi::clipSpaceCorrMatrix() const
返回一个矩阵,应用程序可利用该矩阵继续使用针对 OpenGL 的顶点数据和透视投影矩阵(例如由QMatrix4x4::perspective() 生成的那些),而无论当前活动的QRhi 后端为何。
在典型的渲染器中,一旦使用this_matrix * mvp 代替单纯的mvp ,即可使用 Y 轴向上且深度范围为 0 - 1 的视口,而无需考虑运行时将使用何种后端(以及相应的图形 API)。 这样可以避免基于isYUpInNDC() 和isClipDepthZeroToOne() 的条件分支(尽管在实现某些高级图形技术时,此类逻辑可能仍然不可或缺)。
有关从 Vulkan 角度对该主题的讨论,请参阅此页面。
[static] QRhi *QRhi::create(QRhi::Implementation impl, QRhiInitParams *params, QRhi::Flags flags, QRhiNativeHandles *importDevice, QRhiAdapter *adapter)
返回一个新的QRhi 实例,其后端为由impl 指定的图形API,并使用指定的flags 。如果函数执行失败,则返回nullptr 。
params 必须指向QRhiInitParams 的某个后端特定子类的实例,例如:QRhiVulkanInitParams 、QRhiMetalInitParams 、QRhiD3D11InitParams 、QRhiD3D12InitParams 、QRhiGles2InitParams 。有关创建QRhi 的示例,请参阅这些类。
QRhi 按设计不实现任何备用逻辑:如果无法初始化指定的 API,create() 将失败,后端会在调试输出中打印警告。不过,QRhi 的客户端(例如Qt Quick )可能会根据平台提供额外的逻辑,允许回退到与请求不同的 API。 如果仅是为了测试在稍后调用 `create()` 时初始化是否会成功,建议使用 `probe()` 代替 `create()`,因为对于某些后端,探测功能可以以比 `create()` 更轻量的方式实现;而 `create()` 会对基础架构进行完整初始化,如果随后立即丢弃该 `QRhi ` 实例,则会造成资源浪费。
importDevice 允许使用已存在的图形设备,而无需由QRhi 自行创建。当该参数不为空时,必须指向QRhiNativeHandles 的某个子类的实例:QRhiVulkanNativeHandles 、QRhiD3D11NativeHandles 、QRhiD3D12NativeHandles 、QRhiMetalNativeHandles 、QRhiGles2NativeHandles 。具体细节和语义取决于后端以及底层的图形 API。
在adapter 中指定QRhiAdapter ,提供了一种透明的、跨API的替代方案,可替代通过QRhiVulkanNativeHandles 传入VkPhysicalDevice ,或通过QRhiD3D12NativeHandles 传入适配器LUID。不会获取adapter 的所有权。有关此方法的更多信息,请参阅enumerateAdapters()。
注意: importDevice 和adapter 不能同时指定。
另请参阅 probe()。
[static] QRhi *QRhi::create(QRhi::Implementation impl, QRhiInitParams *params, QRhi::Flags flags = {}, QRhiNativeHandles *importDevice = nullptr)
相当于 create(impl,params,flags,importDevice,nullptr)。
这是一个重载函数。
int QRhi::currentFrameSlot() const
在录制帧时返回当前帧槽索引。若在非活动帧外调用(即当isRecordingFrame()为false 时),则未指定该索引。
对于 Vulkan 或 Metal 等后端,当开始一个新帧时,若发现 CPU 已FramesInFlight - 1 帧领先于 GPU(因为在帧号current -FramesInFlight 提交的命令缓冲区尚未完成),则由QRhi 后端负责进行阻塞。
那些在帧与帧之间容易发生变化的资源(例如,作为类型为QRhiBuffer::Dynamic 的QRhiBuffer 的后端原生缓冲区对象)会存在多个版本,这样,当上一帧仍在处理中时提交的每一帧都可以使用自己的副本,从而避免了在准备帧时需要让管道停滞。 (不应触及可能仍在 GPU 中被使用的资源内容,但如果总是等待上一帧处理完毕,将会降低 GPU 利用率,并最终影响性能和效率。)
从概念上讲,这与某些 C++ 容器及其他类型所采用的“写时复制”(copy-on-write)机制有些相似。它可能也类似于 OpenGL 或 Direct 3D 11 实现中针对特定类型对象所执行的内部操作。
实际上,在Vulkan、Metal以及类似的QRhi 后端中,此类双缓冲(或三缓冲)资源是通过在QRhiResource 背后配置固定数量的原生资源(如VkBuffer)slots 来实现的。 随后可通过帧槽索引(范围从 0、1、…、FramesInFlight-1,并循环往复)对这些资源进行索引。
对于QRhi 的用户而言,这一切都是透明管理的。然而,直接集成图形 API 渲染功能的应用程序可能希望对其自身的图形资源执行类似的双缓冲或三缓冲操作。 要实现这一点,最简单的方法是了解待处理帧的最大数量(可通过resourceLimit() 获取)以及当前帧(槽)索引(由该函数返回)。
另请参阅 isRecordingFrame()、beginFrame() 以及endFrame()。
QRhiDriverInfo QRhi::driverInfo() const
返回此已成功初始化的QRhi 实例所使用的图形设备的元数据。
QRhi::FrameOpResult QRhi::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags = {})
结束、提交并呈现一个在swapChain 上通过上一次beginFrame()方法启动的帧。
双缓冲(或三缓冲)由QRhiSwapChain 和QRhi 在内部进行管理。
flags 可选地,可使用该方法以特定方式更改行为。传递 `QRhi::SkipPresent ` 会跳过将 `Present` 命令加入队列或调用 `swapBuffers`。
成功时返回QRhi::FrameOpSuccess ,失败时返回另一个QRhi::FrameOpResult 值。其中某些情况应被视为软错误,即“稍后重试”类型的错误:当返回QRhi::FrameOpSwapChainOutOfDate 时,应通过调用QRhiSwapChain::createOrResize()来调整交换链的大小或更新交换链。 随后,应用程序应尝试生成一个新帧。QRhi::FrameOpDeviceLost 表示图形设备已丢失,但通过释放所有资源(包括QRhi 本身),然后重新创建所有资源,可能仍可恢复。有关详细讨论,请参阅isDeviceLost()。
另请参阅 beginFrame() 和isDeviceLost()。
QRhi::FrameOpResult QRhi::endOffscreenFrame(QRhi::EndFrameFlags flags = {})
结束、提交,并可能等待该帧的离屏渲染完成。
与endFrame()不同,当存在活跃的缓冲区或纹理回读时,此函数将阻塞并等待 GPU 端工作完成。
flags 目前未被使用。
另请参阅 beginOffscreenFrame()。
[static, since 6.10] QRhi::AdapterList QRhi::enumerateAdapters(QRhi::Implementation impl, QRhiInitParams *params, QRhiNativeHandles *nativeHandles = nullptr)
返回现有适配器(物理设备)的列表;如果给定的图形 API 不支持此功能,则返回空列表。
对于不支持此级别控制的后端,返回的列表始终为空。因此,空列表并不表示系统中没有图形设备,而是表示无法对选择使用哪个设备进行精细控制。
Direct 3D 11、Direct 3D 12 和 Vulkan 的后端通常完全支持适配器的枚举。其他后端可能不支持。 后端由impl 指定。该函数返回的QRhiAdapter 仅可用于带有相同impl 的create()调用。某些底层API可能会存在进一步的限制,特别是Vulkan中,QRhiAdapter 被指定为QVulkanInstance (VkInstance )。
调用方应销毁列表中的QRhiAdapter 对象。除了调用info()之外,这些对象的唯一用途就是传递给create(),或者传递给更高层级的相应函数,例如Qt Quick 。
以下代码片段专为 Vulkan 编写,演示了如何枚举可用的物理设备,并请求为选定的设备创建一个QRhi 。实际上,这相当于通过QRhiVulkanNativeHandles 将VkPhysicalDevice 传递给create(),但在应用程序侧涉及的 API 特定代码更少:
QRhiVulkanInitParams initParams;
initParams.inst= &vulkanInstance;
QRhi::AdapterList adapters=QRhi::enumerateAdapters(QRhi::Vulkan, &initParams);
QRhiAdapter*chosenAdapter =nullptr;
for(QRhiAdapter*adapter: adapters) {
if(looksGood(adapter->info())) {
chosenAdapter=adapter;
break;
}
}
QRhi*rhi =QRhi::create(QRhi::Vulkan, &initParams,{},nullptr,chosenAdapter);
qDeleteAll(adapters);由于某些底层图形 API 的设计原因,必须在params 中传递参数。特别是对于 Vulkan,必须提供QVulkanInstance ,因为没有它就无法进行枚举。后端特有的params 中的其他字段实际上不会被此函数使用。
nativeHandles 是可选的。若指定,它必须是有效的QRhiD3D11NativeHandles 、QRhiD3D12NativeHandles 或QRhiVulkanNativeHandles ,与create() 类似。但与create() 不同,此处仅使用物理设备(Vulkan 情况下)或适配器 LUID(D3D 情况下)字段,所有其他字段均被忽略。这可用于将结果限制在指定的适配器上。 在此情况下,返回的列表将包含 1 个或 0 个元素。
请注意,在之前的代码片段中,looksGood() 函数的实现无法基于真正的适配器/物理设备标识(例如 Windows 上的适配器 LUID 或 Vulkan 中的 VkPhysicalDevice)进行任何平台特定的过滤。这是因为QRhiDriverInfo 不包含平台特定数据。 取而代之,请使用 `nativeHandles ` 来获取已在 `enumerateAdapters()` 内部经过过滤的结果。
以下两个代码片段以 Direct 3D 12 为例,实际上效果等同:
// 自 Qt 6.10 起基于 enumerateAdapters 的方法
QRhiD3D12InitParams initParams;
QRhiD3D12NativeHandles nativeHandles;
nativeHandles.adapterLuidLow=luid.LowPart;// 从某处获取了 LUID,现在将其传递给 Qt
nativeHandles.adapterLuidHigh=luid.HighPart;
QRhi::AdapterList adapters=QRhi::enumerateAdapters(QRhi::D3D12, &initParams, &nativeHandles);
if(adapters.isEmpty()) {qWarning("未找到请求的适配器");}
QRhi*rhi =QRhi::create(QRhi::D3D12, &initParams,{},nullptr,adapters[0]);
qDeleteAll(adapters);// traditional approach, more lightweight
QRhiD3D12InitParams initParams;
QRhiD3D12NativeHandles nativeHandles;
nativeHandles.adapterLuidLow = luid.LowPart; // retrieved a LUID from somewhere, now pass it on to Qt
nativeHandles.adapterLuidHigh = luid.HighPart;
QRhi *rhi = QRhi::create(QRhi::D3D12, &initParams, {}, &nativeHandles, nullptr);该函数在 Qt 6.10 中引入。
另请参阅 create()。
QRhi::FrameOpResult QRhi::finish()
等待图形队列(如适用)中的任何任务完成,然后执行所有延迟操作,例如完成读回和资源释放。该函数可在帧内和帧外调用,但不能在渲染通道内调用。在帧内调用时,这意味着会提交命令缓冲区中的任何任务。
注意:请避免使用 此函数。唯一可能需要使用此函数的情况是:当基于交换链的帧中已入队读回的结果需要在某个固定的给定点获得,因此需要等待结果时。
bool QRhi::isClipDepthZeroToOne() const
如果底层图形 API 在裁剪空间中使用深度范围 [0, 1],则返回true 。
实际上,这仅对 OpenGL 而言是false ,因为 OpenGL 使用投影后的深度范围 [-1, 1]。(请勿与由 glDepthRange() 控制的 NDC 到窗口映射混淆,后者使用范围 [0, 1],除非被QRhiViewport 覆盖) 在某些 OpenGL 版本中,可以使用 glClipControl() 来更改此设置,但QRhi 的 OpenGL 后端并未使用该函数,因为该函数在 OpenGL ES 或低于 4.5 版本的 OpenGL 中不可用。
注意: clipSpaceCorrMatrix() 在其返回的矩阵中已包含相应的调整。 因此,许多QRhi 的用户除了将投影矩阵与clipSpaceCorrMatrix() 相乘外,无需采取任何进一步措施。然而,某些图形技术(例如某些类型的阴影映射)涉及在着色器中处理和输出深度值。这些情况需要查询该函数的值,并酌情将其纳入考虑。
bool QRhi::isDeviceLost() const
如果图形设备丢失,则返回 true。
通常在beginFrame()、endFrame()或QRhiSwapChain::createOrResize()中检测到设备丢失,具体取决于后端和底层原生API。最常见的是endFrame(),因为呈现操作正是在此处进行的。对于某些后端,QRhiSwapChain::createOrResize()也可能因设备丢失而失败。 因此,提供此函数作为一种通用方法,用于检查先前操作是否检测到设备丢失。
当设备丢失时,不应再通过QRhi 执行任何操作。相反,应释放所有QRhi 资源,随后销毁QRhi 。然后可以尝试创建一个新的QRhi 。如果成功,必须重新初始化所有图形资源。如果不成功,请稍后重试,并反复尝试。
虽然简单的应用程序可能选择忽略设备丢失的情况,但在常用的桌面平台上,设备丢失可能由多种原因引起,包括物理断开图形适配器、禁用设备或驱动程序、卸载或升级图形驱动程序,或者因导致图形设备重置的错误所致。 其中一些情况甚至可能在完全正常的环境下发生,例如将图形驱动程序升级到新版本是一项常见操作,可能在 Qt 应用程序运行的任何时候发生。用户完全有理由期望应用程序能够在此情况下继续正常运行,即使该应用程序正在积极使用 OpenGL 或 Direct3D 等 API 也是如此。
基于QRhi 构建的Qt自有框架(例如Qt Quick )在发生设备丢失时,通常能够进行处理并采取适当措施。 如果图形资源(如纹理和缓冲区)的数据在CPU端仍然可用,则此类事件在应用程序层面上可能完全不会被察觉,因为图形资源可以无缝地重新初始化。然而,直接使用QRhi 的应用程序和库应做好准备,自行检查并处理设备丢失的情况。
注意:在 OpenGL中 ,应用程序可能需要通过在 `QOpenGLContext` 上设置 `QSurfaceFormat::ResetNotification ` 来主动启用上下文重置通知。这通常是在 `QRhiGles2InitParams::format` 中启用该标志来实现的。但请注意,即使未设置该标志,某些系统仍可能触发上下文重置情况。
bool QRhi::isFeatureSupported(QRhi::Feature feature) const
如果指定的feature 被支持,则返回true
bool QRhi::isRecordingFrame() const
当存在活动帧时返回 true,这意味着已调用beginFrame()(或beginOffscreenFrame()),但尚未有对应的endFrame()(或endOffscreenFrame())。
另请参阅 currentFrameSlot()、beginFrame() 和endFrame()。
bool QRhi::isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags = {}) const
如果由flags 修改的指定纹理format 受支持,则返回true 。
该查询同时支持未压缩和压缩格式。
bool QRhi::isYUpInFramebuffer() const
如果底层图形 API 在帧缓冲区和图像中将 Y 轴设置为向上,则返回true 。
实际上,仅在 OpenGL 中,此函数返回值为true 。
bool QRhi::isYUpInNDC() const
如果底层图形 API 的归一化设备坐标系中 Y 轴向上,则返回true 。
实际上,仅在 Vulkan 中,该函数返回值为 `false `。
注意: clipSpaceCorrMatrix() 会在其返回的矩阵中包含相应的调整(使 Y 轴向上)。
bool QRhi::makeThreadLocalNativeContextCurrent()
在 OpenGL 中,这会将该 OpenGL 上下文设为当前线程的活动上下文。在其他后端中,该函数无效。
通常在 Qt 框架代码中调用此函数,目的是确保应用程序提供的外部 OpenGL 代码,只要QRhi 仍在使用 OpenGL 后端,就能像以前直接使用 OpenGL 时那样正常运行。
失败时返回 false,与 `QOpenGLContext::makeCurrent()` 类似。当操作失败时,可调用 `isDeviceLost()` 来确定是否发生了上下文丢失的情况。此类检查等同于通过 `QOpenGLContext::isValid()` 进行检查。
另请参阅 QOpenGLContext::makeCurrent() 和QOpenGLContext::isValid()。
[static] int QRhi::mipLevelsForSize(const QSize &size)
返回给定size 的MIP级别数量。
const QRhiNativeHandles *QRhi::nativeHandles()
返回指向后端特定本机对象集合的指针,该集合包含后端所使用的设备、上下文及类似概念。
请根据需要将其强制转换为QRhiVulkanNativeHandles 、QRhiD3D11NativeHandles 、QRhiD3D12NativeHandles 、QRhiGles2NativeHandles 或QRhiMetalNativeHandles 。
注意: 无论对于返回的指针还是任何本机对象,均不发生 所有权的转移。
QRhiBuffer *QRhi::newBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, quint32 size)
返回一个新的缓冲区,其中包含指定的type 、usage 和size 。
注意: 并非所有后端都支持所有 usage 和type 的 组合。请参阅UsageFlags 和the feature flags 。
注意:后端 可能会选择分配比size 更大的缓冲区。此操作对应用程序是透明的,因此size 的值没有特殊限制。QRhiBuffer::size() 始终会返回size 中请求的值。
另请参见 QRhiResource::destroy()。
QRhiComputePipeline *QRhi::newComputePipeline()
返回一个新的计算管道资源。
注意: 只有当“Compute ”功能被报告为受支持时,Compute 功能 才可用。
另请参阅 QRhiResource::destroy()。
QRhiGraphicsPipeline *QRhi::newGraphicsPipeline()
返回一个新的图形管道资源。
另请参阅 QRhiResource::destroy()。
QRhiRenderBuffer *QRhi::newRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize, int sampleCount = 1, QRhiRenderBuffer::Flags flags = {}, QRhiTexture::Format backingFormatHint = QRhiTexture::UnknownFormat)
返回一个新的渲染缓冲区,其type 、pixelSize 、sampleCount 和flags 均按指定值设置。
当backingFormatHint 设置为QRhiTexture::UnknownFormat 以外的纹理格式时,后端可能会根据该设置来决定渲染缓冲区的存储底层应采用何种格式。
注意: backingFormatHint 通常在涉及多采样和浮点纹理格式时才起作用:渲染到多采样QRhiRenderBuffer ,然后解析为非RGBA8的QRhiTexture ,这意味着(在某些图形API中)QRhiRenderBuffer 的存储后端将使用匹配的非RGBA8格式。 这意味着传递类似QRhiTexture::RGBA32F 的格式非常重要,因为后端通常会默认选择QRhiTexture::RGBA8 ,这会导致后续因尝试在QRhiTextureRenderTarget 的颜色附件中设置 RGBA8→RGBA32F 多采样解析而引发错误。
另请参阅 QRhiResource::destroy()。
QRhiSampler *QRhi::newSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter, QRhiSampler::Filter mipmapMode, QRhiSampler::AddressMode addressU, QRhiSampler::AddressMode addressV, QRhiSampler::AddressMode addressW = QRhiSampler::Repeat)
返回一个新的采样器,其放大滤波器为magFilter ,缩小滤波器为minFilter ,MIP映射模式为mipmapMode ,以及寻址(环绕)模式为addressU 、addressV 和addressW 。
注意:将 mipmapMode 设置 为None 以外的值,意味着所有相关MIP级别的图像将通过texture uploads 提供,或者通过调用此采样器所用纹理的generateMips()方法提供。 若尝试将采样器与一个在所有相关MIP级别上均无数据的纹理配合使用,将导致渲染错误,具体表现取决于底层图形API。
另请参阅 QRhiResource::destroy()。
QRhiShaderResourceBindings *QRhi::newShaderResourceBindings()
返回一个新的着色器资源绑定集合资源。
另请参阅 QRhiResource::destroy()。
[since 6.9] QRhiShadingRateMap *QRhi::newShadingRateMap()
返回一个新的着色率映射对象。
该函数在 Qt 6.9 中引入。
QRhiSwapChain *QRhi::newSwapChain()
返回一个新的交换链。
另请参阅 QRhiResource::destroy() 和QRhiSwapChain::createOrResize()。
QRhiTexture *QRhi::newTexture(QRhiTexture::Format format, const QSize &pixelSize, int sampleCount = 1, QRhiTexture::Flags flags = {})
返回一个新的 1D 或 2D 纹理,其format 、pixelSize 、sampleCount 和flags 均按指定值设置。
一维纹理必须在flags 中设置QRhiTexture::OneDimensional 。如果pixelSize 的高度为0,则该函数会隐式设置此标志。
注意: format 指定了请求的内部和外部格式,这意味着要上传到纹理的数据必须采用兼容的格式,而原生纹理可能(但至少在 OpenGL 中无法保证)在内部使用此格式。
注意:1D 纹理仅在运行时报告支持OneDimensionalTextures 功能时才有效。此外,1D 纹理上的 MIP 图仅在运行时报告支持OneDimensionalTextureMipmaps 功能时才有效。
另请参阅 QRhiResource::destroy()。
QRhiTexture *QRhi::newTexture(QRhiTexture::Format format, int width, int height, int depth, int sampleCount = 1, QRhiTexture::Flags flags = {})
返回一个新的 1D、2D 或 3D 纹理,其format 、width 、height 、depth 、sampleCount 和flags 均按指定值设置。
此重载适用于 3D 纹理,因为它允许指定depth 。3D 纹理必须在flags 中设置QRhiTexture::ThreeDimensional ,但使用此重载时可以省略该设置,因为只要depth 大于 0,该标志就会被隐式设置。对于 1D、2D 和立方体纹理,depth 应设置为 0。
一维纹理必须在flags 中设置QRhiTexture::OneDimensional 。如果height 和depth 均为0,此重载会隐式设置该标志。
注意: 只有当运行时报告支持ThreeDimensionalTextures 功能时,3D 纹理才能正常工作。
注意:1D 纹理仅在运行时报告支持OneDimensionalTextures 功能时才有效。此外,1D 纹理上的 MIP 贴图仅在运行时报告支持OneDimensionalTextureMipmaps 功能时才有效。
这是一个重载函数。
QRhiTexture *QRhi::newTextureArray(QRhiTexture::Format format, int arraySize, const QSize &pixelSize, int sampleCount = 1, QRhiTexture::Flags flags = {})
返回一个新的 1D 或 2D 纹理数组,其format 、arraySize 、pixelSize 、sampleCount 和flags 均按指定值设置。
该函数会在flags 中隐式设置QRhiTexture::TextureArray 。
一维纹理数组必须在flags 中设置QRhiTexture::OneDimensional 。如果pixelSize 的高度为0,该函数将隐式设置此标志。
注意:请 勿将纹理数组与纹理的数组混淆。 通过此函数创建的QRhiTexture 可在着色器中与1D或2D数组采样器配合使用,例如:layout(binding = 1) uniform sampler2DArray texArr; 。纹理数组是指通过QRhiShaderResourceBinding::sampledTextures()并指定计数>1向着色器暴露的纹理列表,并在着色器中声明,例如如下所示:layout(binding = 1) uniform sampler2D textures[4];
注意: 仅当运行时报告支持TextureArrays 功能时,此功能 才有效。
注意:1D 纹理仅在运行时报告支持OneDimensionalTextures 功能时才有效。此外,1D纹理上的Mipmap仅在运行时报告支持OneDimensionalTextureMipmaps 功能时才有效。
另请参阅 newTexture()。
QRhiTextureRenderTarget *QRhi::newTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc, QRhiTextureRenderTarget::Flags flags = {})
返回一个新的纹理渲染目标,其颜色和深度/模板附件由desc 中指定,并具有指定的flags 。
另请参阅 QRhiResource::destroy()。
QRhiResourceUpdateBatch *QRhi::nextResourceUpdateBatch()
返回一个可用的、空的批处理,可将各种操作记录到其中。
注意: 返回值 不属于调用方所有,绝不能被销毁。相反,应将该批处理交还给池以供重复使用,具体方法包括将其传递给QRhiCommandBuffer::beginPass()、QRhiCommandBuffer::endPass()或QRhiCommandBuffer::resourceUpdate(),或者对其调用QRhiResourceUpdateBatch::release()。
注意:该方法也可在 beginFrame() -endFrame() 之外调用,因为批处理实例仅负责自行收集数据,不会执行任何操作。
由于不与正在录制的帧绑定,因此例如以下代码序列是有效的:
rhi->beginFrame(swapchain);
QRhiResourceUpdateBatch *u = rhi->nextResourceUpdateBatch();
u->uploadStaticBuffer(buf, data);
// ... do not commit the batch
rhi->endFrame();
// u stays valid (assuming buf stays valid as well)
rhi->beginFrame(swapchain);
swapchain->currentFrameCommandBuffer()->resourceUpdate(u);
// ... draw with buf
rhi->endFrame();警告: 每个QRhi 的最大批处理数量为64。当达到此限制时,该函数将返回null,直到有一个批处理被归还到池中。
QByteArray QRhi::pipelineCacheData()
返回一个二进制数据块,其中包含从QRhiGraphicsPipeline 和QRhiComputePipeline 收集的数据,这些缓存是在此QRhi 生命周期内成功创建的。
通过保存缓存数据,并在后续运行同一应用程序时重新加载,可能缩短管道和着色器的创建时间。缓存及其序列化版本的具体内容未作明确规定,始终取决于所使用的后端,在某些情况下还取决于图形 API 的具体实现。
当报告PipelineCacheDataLoadSave 不被支持时,返回的QByteArray 为空。
如果在调用create() 时未指定EnablePipelineCacheDataSave 标志,则返回的QByteArray 可能为空,即使支持PipelineCacheDataLoadSave 功能也是如此。
当返回的数据不为空时,它总是与 Qt 版本和QRhi 后端相关。 此外,在某些情况下,它还与图形设备以及所使用的确切驱动程序版本有着很强的依赖关系。QRhi 负责添加适当的头文件和保护措施,以确保数据总能安全地传递给setPipelineCacheData(),因此,尝试从其他版本的驱动程序运行中加载数据将得到安全且优雅的处理。
注意: 根据后端的不同,调用 releaseCachedResources() 可能会清除已收集的管道数据。随后再次调用此函数可能不会返回任何数据。
有关此功能的更多详细信息,请参阅EnablePipelineCacheDataSave 。
注意:请尽量减少 对该函数的调用次数。检索 blob 并非总是低开销的操作,因此应低频调用此函数,理想情况下仅在关闭应用程序时调用一次。
另请参阅 setPipelineCacheData()、create() 以及isFeatureSupported()。
[static] bool QRhi::probe(QRhi::Implementation impl, QRhiInitParams *params)
如果调用create() 时,预期在给定的impl 和params 下能够成功,则返回 true。
对于某些后端,这等同于调用create(),检查其返回值,然后销毁生成的QRhi 。
对于其他后端,特别是 Metal,可能会有特定的探测实现,这允许以更轻量级的方式进行测试,同时避免在失败时用警告污染调试输出。
另请参阅 create()。
void QRhi::releaseCachedResources()
尝试释放后端缓存中的资源。这可能包括 CPU 和 GPU 资源。 仅适用于可自动重建的内存和资源。例如,如果后端的QRhiGraphicsPipeline 实现维护了一个着色器编译结果缓存,调用此函数将清空该缓存,从而可能释放内存和图形资源。
在资源受限的环境中调用此函数是合理的,在这些环境中,有时需要以牺牲性能为代价来确保资源使用量降至最低。
void QRhi::removeCleanupCallback(const void *key)
使用 `key` 注销回调。如果未通过 `key` 注册清理回调,则该函数不执行任何操作。未使用键注册的回调无法被移除。
另请参阅 addCleanupCallback()。
int QRhi::resourceLimit(QRhi::ResourceLimit limit) const
返回指定资源limit 的值。
这些值通常会在后端初始化时被查询,因此调用此函数是一项轻量级操作。
void QRhi::setPipelineCacheData(const QByteArray &data)
在适用时,将data 加载到管道缓存中。
当PipelineCacheDataLoadSave 被报告为不受支持时,该函数仍可安全调用,但不会产生任何效果。
pipelineCacheData() 返回的 blob 总是与 Qt 版本、QRhi 后端以及(在某些情况下)图形设备和特定版本的图形驱动程序相关。QRhi 负责添加适当的头文件和保护措施,以确保数据总能安全地传递给此函数。 如果出现不匹配的情况(例如,由于驱动程序已升级到新版本,或者数据是由不同的QRhi 后端生成的),则会输出一个警告,并且可以安全地忽略data 。
在 Vulkan 中,这直接映射到 VkPipelineCache。调用此函数会创建一个新的 Vulkan 管道缓存对象,其初始数据来自data 。随后创建的所有QRhiGraphicsPipeline 和QRhiComputePipeline 对象都会使用该管道缓存对象,从而可能加速管道的创建。
其他 API 虽然没有真正的管道缓存,但可能会提供包含着色器编译生成的字节码(D3D)或程序二进制文件(OpenGL)的缓存。 在运行时需要大量从源代码进行着色器编译的应用程序中,如果通过此函数将“管道缓存”从之前的运行中预先填充,则可以在后续运行中显著提升性能。
注意: QRhi 无法保证data 会对管道和着色器创建性能产生影响。对于 Vulkan 等 API,是否将data 用于特定目的,或是将其忽略,完全取决于驱动程序的决定。
有关此功能的更多详细信息,请参阅EnablePipelineCacheDataSave 。
注意: QRhi 提供的此机制 与驱动程序自身的内部缓存机制(如有)相互独立。这意味着,根据图形 API 及其实现的不同,检索并重新加载data 的确切效果无法预测。如果 Qt 无法控制的其他缓存机制已经处于活动状态,则可能完全无法观察到性能提升。
注意:请尽量减少 对该函数的调用次数。加载 blob 操作并非总是低开销的,因此应以较低频率调用此函数,理想情况下仅在应用程序启动时调用一次。
警告:序列化的 管道缓存数据被视为可信内容。Qt XML会对 `data` 中包含的标头和元数据进行严格解析,但建议应用程序开发人员切勿传入来自不可信来源的数据。
另请参阅 pipelineCacheData() 和isFeatureSupported()。
[since 6.9] void QRhi::setQueueSubmitParams(QRhiNativeHandles *params)
结合后端和相应的图形 API,此函数允许在下次向图形命令队列提交命令时提供额外参数。
特别是在 Vulkan 环境中,这允许将一组 Vulkan 信号量对象作为参数传递给 `vkQueueSubmit() ` 函数,以便对其进行信号发送和等待。此时,`params ` 必须是一个 `QRhiVulkanQueueSubmitParams`。在某些高级用例中,这一点至关重要,例如在执行涉及等待和向 VkSemaphores 发送信号的原生 Vulkan 调用时,这些信号量由应用程序自定义的 Vulkan 渲染或计算代码进行管理。 此外,这还允许指定在下次调用 `vkQueuePresentKHR()` 时需要等待的其他信号量。
注意:此 函数仅影响下一次队列提交,该提交将在endFrame()、endOffscreenFrame()或finish()中发生。present操作的入队则在endFrame()中进行。
对于许多其他后端,此函数的实现为空操作。
该函数于 Qt 6.9 中引入。
[static] QSize QRhi::sizeForMipLevel(int mipLevel, const QSize &baseLevelSize)
返回给定mipLevel 的纹理图像大小,该大小基于baseLevelSize 中给出的第0级大小计算得出。
QRhiStats QRhi::statistics() const
收集并返回有关图形资源使用时长和分配情况的统计数据。
有关内存分配的数据仅在某些后端中可用,在这些后端中,此类操作由 Qt 控制。对于无法对资源内存分配进行底层控制的图形 API,此功能将永远不被支持,且结果中的所有相关字段均为 0。
特别是对于 Vulkan,这些值始终有效,且是从底层内存分配库中查询获得的。这有助于了解活动缓冲区和纹理的内存需求。
Direct 3D 12 也是如此。除了内存分配器库的统计数据外,此处的结果还包含一个 `totalUsageBytes ` 字段,该字段报告了包括 DXGI 报告的非内存分配器库控制的附加资源(如交换链缓冲区、描述符堆等)在内的总大小。
这些数值对应于所有已用内存类型的总和(即,对于独立显卡而言,包括显存和系统内存)。
大多数后端还提供其他数据,例如用于图形和计算管道创建的总时间(以毫秒为单位),该过程通常涉及着色器编译或缓存查找,以及可能较为耗时的处理。
注意: 管道创建等操作的耗时 可能会受到多种因素的影响。由于“管道”的概念,以及在调用QRhiGraphicsPipeline::create()等操作时底层具体发生的情况,在不同的图形 API 及其实现之间存在很大差异,因此不应在不同的后端之间比较这些结果。
注意:此外, 许多驱动程序可能会针对着色器、程序和管道采用各种缓存策略。(这与 Qt 自身类似的功能(如setPipelineCacheData() 或 OpenGL 专有的程序二进制磁盘缓存)无关。) 由于此类内部行为对 API 客户端是透明的,因此 Qt 和QRhi 无法获知或控制确切的缓存策略、缓存数据的持久性、缓存数据的失效处理等。在分析时间统计数据(例如管道创建所耗时间)时,应考虑到驱动程序级缓存机制可能存在且其行为未明确规定这一情况。
QList<int> QRhi::supportedSampleCounts() const
返回支持的样本计数列表。
一个典型的示例是 (1, 2, 4, 8)。
在某些后端中,该支持值列表是预先确定的,而在另一些后端中,则由(物理)设备属性在运行时决定支持哪些值。
另请参阅 QRhiRenderBuffer::setSampleCount()、QRhiTexture::setSampleCount()、QRhiGraphicsPipeline::setSampleCount() 以及QRhiSwapChain::setSampleCount()。
[since 6.9] QList<QSize> QRhi::supportedShadingRates(int sampleCount) const
返回指定sampleCount 所支持的可变着色速率列表。
1x1始终受支持。
该函数自 Qt 6.9 起引入。
QThread *QRhi::thread() const
返回QRhi 被initialized 的那个线程。
int QRhi::ubufAligned(int v) const
返回值(通常为偏移量)v ,该值已根据ubufAlignment() 函数指定的统一缓冲区对齐方式进行对齐。
int QRhi::ubufAlignment() const
返回统一缓冲区偏移量的最小对齐值(以字节为单位)。该值通常为 256。
如果尝试绑定一个偏移量未对齐到此值的统一缓冲区区域,则根据后端和底层图形 API 的不同,可能会导致绑定失败。
另请参阅 ubufAligned()。
[static] QRhiSwapChainProxyData QRhi::updateSwapChainProxyData(QRhi::Implementation impl, QWindow *window)
生成并返回一个QRhiSwapChainProxyData 结构体,其中包含由impl 指定的后端和图形API特有的不透明数据。window 是swapchain所针对的QWindow 。
返回的结构体可传递给QRhiSwapChain::setProxyData()。这在多线程渲染系统中很有意义:与所有QRhi 操作不同,该静态函数应在线程(GUI)主线程上调用,随后将数据传递给处理QRhi 和QRhiSwapChain 的线程,并最终传递给交换链。 这使得可以执行仅在主线程上调用才安全的原生平台查询,例如从 NSView 查询 CAMetalLayer,然后将数据传递给位于渲染线程上的QRhiSwapChain 。 以 Metal 示例为例,在专用渲染线程上访问 view.layer 会导致 Xcode 线程检查器发出警告。借助数据代理机制,可以避免这种情况。
当不涉及线程时,无需生成和传递QRhiSwapChainProxyData :后端保证能够自行查询所需的一切,而且如果所有内容都位于主(GUI)线程上,这应该就足够了。
注意: impl 应与创建QRhi 时所用的参数一致。例如,在非 Apple 平台上调用QRhi::Metal 将无法生成任何有用的数据。
相关非成员
[alias, since 6.7] QRhiShaderResourceBindingSet
QRhiShaderResourceBindings 的同义词。
该 typedef 是在 Qt 6.7 中引入的。
© 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.