QRhi Class
Accelerated 2D/3D graphics API abstraction. More...
Header: | #include <QRhi> |
qmake: | QT += rhi |
Public Types
enum | BeginFrameFlag { } |
flags | BeginFrameFlags |
typedef | CleanupCallback |
enum | EndFrameFlag { SkipPresent } |
flags | EndFrameFlags |
enum | Feature { MultisampleTexture, MultisampleRenderBuffer, DebugMarkers, Timestamps, ..., Compute } |
enum | Flag { EnableProfiling, EnableDebugMarkers } |
flags | Flags |
enum | FrameOpResult { FrameOpSuccess, FrameOpError, FrameOpSwapChainOutOfDate, FrameOpDeviceLost } |
enum | Implementation { Null, Vulkan, OpenGLES2, D3D11, Metal } |
enum | ResourceLimit { TextureSizeMin, TextureSizeMax, MaxColorAttachments, FramesInFlight } |
Public Functions
~QRhi() | |
void | addCleanupCallback(const QRhi::CleanupCallback &callback) |
QRhi::Implementation | backend() const |
QRhi::FrameOpResult | beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags = ...) |
QRhi::FrameOpResult | beginOffscreenFrame(QRhiCommandBuffer **cb) |
QMatrix4x4 | clipSpaceCorrMatrix() const |
int | currentFrameSlot() const |
QRhi::FrameOpResult | endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags = ...) |
QRhi::FrameOpResult | endOffscreenFrame() |
QRhi::FrameOpResult | finish() |
bool | isClipDepthZeroToOne() 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 |
int | mipLevelsForSize(const QSize &size) const |
const QRhiNativeHandles * | nativeHandles() |
QRhiBuffer * | newBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, int size) |
QRhiComputePipeline * | newComputePipeline() |
QRhiGraphicsPipeline * | newGraphicsPipeline() |
QRhiRenderBuffer * | newRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize, int sampleCount = 1, QRhiRenderBuffer::Flags flags = ...) |
QRhiSampler * | newSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter, QRhiSampler::Filter mipmapMode, QRhiSampler::AddressMode u, QRhiSampler::AddressMode v) |
QRhiShaderResourceBindings * | newShaderResourceBindings() |
QRhiSwapChain * | newSwapChain() |
QRhiTexture * | newTexture(QRhiTexture::Format format, const QSize &pixelSize, int sampleCount = 1, QRhiTexture::Flags flags = ...) |
QRhiTextureRenderTarget * | newTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc, QRhiTextureRenderTarget::Flags flags = ...) |
QRhiResourceUpdateBatch * | nextResourceUpdateBatch() |
QRhiProfiler * | profiler() |
int | resourceLimit(QRhi::ResourceLimit limit) const |
void | runCleanup() |
QSize | sizeForMipLevel(int mipLevel, const QSize &baseLevelSize) const |
QVector<int> | supportedSampleCounts() const |
QThread * | thread() const |
int | ubufAligned(int v) const |
int | ubufAlignment() const |
Static Public Members
const int | MAX_LAYERS |
const int | MAX_LEVELS |
QRhi * | create(QRhi::Implementation impl, QRhiInitParams *params, QRhi::Flags flags = ..., QRhiNativeHandles *importDevice = nullptr) |
Detailed Description
Accelerated 2D/3D graphics API abstraction.
The Qt Rendering Hardware Interface is an abstraction for hardware accelerated graphics APIs, such as, OpenGL, OpenGL ES, Direct3D, Metal, and Vulkan.
Some of the main design goals are:
- Simple, minimal, understandable, extensible. Follow the proven path of the Qt Quick scenegraph.
- Aim to be a product - and in the bigger picture, part of a product (Qt) - that is usable out of the box both by internal (such as, Qt Quick) and, eventually, external users.
- Not a complete 1:1 wrapper for any of the underlying APIs. The feature set is tuned towards the needs of Qt's 2D and 3D offering (QPainter, Qt Quick, Qt 3D Studio). Iterate and evolve in a sustainable manner.
- Intrinsically cross-platform, without reinventing: abstracting cross-platform aspects of certain APIs (such as, OpenGL context creation and windowing system interfaces, Vulkan instance and surface management) is not in scope here. These are delegated to the existing QtGui facilities (QWindow, QOpenGLContext, QVulkanInstance) and its backing QPA architecture.
Each QRhi instance is backed by a backend for a specific graphics API. The selection of the backend is a run time choice and is up to the application or library that creates the QRhi instance. Some backends are available on multiple platforms (OpenGL, Vulkan, Null), while APIs specific to a given platform are only available when running on the platform in question (Metal on macOS/iOS/tvOS, Direct3D on Windows).
The available backends currently are:
- OpenGL 2.1 or OpenGL ES 2.0 or newer. Some extensions are utilized when present, for example to enable multisample framebuffers.
- Direct3D 11.1
- Metal
- Vulkan 1.0, optionally with some extensions that are part of Vulkan 1.1
- Null - A "dummy" backend that issues no graphics calls at all.
In order to allow shader code to be written once in Qt applications and libraries, all shaders are expected to be written in a single language which is then compiled into SPIR-V. Versions for various shading language are then generated from that, together with reflection information (inputs, outputs, shader resources). This is then packed into easily and efficiently serializable QShader instances. The compilers and tools to generate such shaders are not part of QRhi, but the core classes for using such shaders, QShader and QShaderDescription, are.
Design Fundamentals
A QRhi cannot be instantiated directly. Instead, use the create() function. Delete the QRhi instance normally to release the graphics device.
Resources
Instances of classes deriving from QRhiResource, such as, QRhiBuffer, QRhiTexture, etc., encapsulate zero, one, or more native graphics resources. Instances of such classes are always created via the new
functions of the QRhi, such as, newBuffer(), newTexture(), newTextureRenderTarget(), newSwapChain().
vbuf = rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(vertexData)); if (!vbuf->build()) { error } ... delete vbuf;
- The returned value from both create() and functions like newBuffer() is owned by the caller.
- Just creating a QRhiResource subclass never allocates or initializes any native resources. That is only done when calling the
build
function of a subclass, for example, QRhiBuffer::build() or QRhiTexture::build(). - The exception is QRhiTextureRenderTarget::newCompatibleRenderPassDescriptor() and QRhiSwapChain::newCompatibleRenderPassDescriptor(). There is no
build
operation for these and the returned object is immediately active. - The resource objects themselves are treated as immutable: once a resource is built, changing any parameters via the setters, such as, QRhiTexture::setPixelSize(), has no effect, unless the underlying native resource is released and
build
is called again. See more about resource reuse in the sections below. - The underlying native resources are scheduled for releasing by the QRhiResource destructor, or by calling QRhiResource::release(). Backends often queue release requests and defer executing them to an unspecified time, this is hidden from the applications. This way applications do not have to worry about releasing native resources that may still be in use by an in-flight frame.
- Note that this does not mean that a QRhiResource can freely be destroyed or release()'d within a frame (that is, in a beginFrame() - endFrame() section). As a general rule, all referenced QRhiResource objects must stay unchanged until the frame is submitted by calling endFrame(). To ease this, QRhiResource::releaseAndDestroyLater() is provided as a convenience.
Command buffers and deferred command execution
Regardless of the design and capabilities of the underlying graphics API, all QRhi backends implement some level of command buffers. No QRhiCommandBuffer function issues any native bind or draw command (such as, glDrawElements
) directly. Commands are always recorded in a queue, either native or provided by the QRhi backend. The command buffer is submitted, and so execution starts only upon QRhi::endFrame() or QRhi::finish().
The deferred nature has consequences for some types of objects. For example, writing to a dynamic buffer multiple times within a frame, in case such buffers are backed by host-visible memory, will result in making the results of all writes are visible to all draw calls in the command buffer of the frame, regardless of when the dynamic buffer update was recorded relative to a draw call.
Furthermore, instances of QRhiResource subclasses must be treated immutable within a frame in which they are referenced in any way. Create or rebuild all resources upfront, before starting to record commands for the next frame. Reusing a QRhiResource instance within a frame (by rebuilding it and then referencing it again in the same beginFrame - endFrame
section) should be avoided as it may lead to unexpected results, depending on the backend.
As a general rule, all referenced QRhiResource objects must stay valid and unmodified until the frame is submitted by calling endFrame(). On the other hand, calling release() or destroying the QRhiResource are always safe once the frame is submitted, regardless of the status of the underlying native resources (which may still be in use by the GPU - but that is taken care of internally).
Unlike APIs like OpenGL, upload and copy type of commands cannot be mixed with draw commands. The typical renderer will involve a sequence similar to the following: (re)build resources
- begin frame
- record uploads and copies
- start renderpass
- record draw calls
- end renderpass
- end frame
. Recording copy type of operations happens via QRhiResourceUpdateBatch. Such operations are committed typically on beginPass().
When working with legacy rendering engines designed for OpenGL, the migration to QRhi often involves redesigning from having a single render
step (that performs copies and uploads, clears buffers, and issues draw calls, all mixed together) to a clearly separated, two phase prepare
- render
setup where the render
step only starts a renderpass and records draw calls, while all resource creation and queuing of updates, uploads and copies happens beforehand, in the prepare
step.
QRhi does not at the moment allow freely creating and submitting command buffers. This may be lifted in the future to some extent, in particular if compute support is introduced, but the model of well defined frame-start
and frame-end
points, combined with a dedicated, "frame" command buffer, where frame-end
implies presenting, is going to remain the primary way of operating since this is what fits Qt's various UI technologies best.
Threading
A QRhi instance and the associated resources can be created and used on any thread but all usage must be limited to that one single thread. When rendering to multiple QWindows in an application, having a dedicated thread and QRhi instance for each window is often advisable, as this can eliminate issues with unexpected throttling caused by presenting to multiple windows. Conceptually that is then the same as how Qt Quick scene graph's threaded render loop operates when working directly with OpenGL: one thread for each window, one QOpenGLContext for each thread. When moving onto QRhi, QOpenGLContext is replaced by QRhi, making the migration straightforward.
When it comes to externally created native objects, such as OpenGL contexts passed in via QRhiGles2NativeHandles, it is up to the application to ensure they are not misused by other threads.
Resources are not shareable between QRhi instances. This is an intentional choice since QRhi hides most queue, command buffer, and resource synchronization related tasks, and provides no API for them. Safe and efficient concurrent use of graphics resources from multiple threads is tied to those concepts, however, and is thus a topic that is currently out of scope, but may be introduced in the future.
Resource synchronization
QRhi does not expose APIs for resource barriers or image layout transitions. Such synchronization is done implicitly by the backends, where applicable (for example, Vulkan), by tracking resource usage as necessary.
Note: Resources within a render or compute pass are expected to be bound to a single usage during that pass. For example, a buffer can be used as vertex, index, uniform, or storage buffer, but not a combination of them within a single pass. However, it is perfectly fine to use a buffer as a storage buffer in a compute pass, and then as a vertex buffer in a render pass, for example, assuming the buffer declared both usages upon creation.
Note: Textures have this rule relaxed in certain cases, because using two subresources (typically two different mip levels) of the same texture for different access (one for load, one for store) is supported even within the same pass.
Resource reuse
From the user's point of view a QRhiResource is reusable immediately after calling QRhiResource::release(). With the exception of swapchains, calling build()
on an already built object does an implicit release()
. This provides a handy shortcut to reuse a QRhiResource instance with different parameters, with a new native graphics object underneath.
The importance of reusing the same object lies in the fact that some objects reference other objects: for example, a QRhiShaderResourceBindings can reference QRhiBuffer, QRhiTexture, and QRhiSampler instances. If in a later frame one of these buffers need to be resized or a sampler parameter needs changing, destroying and creating a whole new QRhiBuffer or QRhiSampler would invalidate all references to the old instance. By just changing the appropriate parameters via QRhiBuffer::setSize() or similar and then calling QRhiBuffer::build(), everything works as expected and there is no need to touch the QRhiShaderResourceBindings at all, even though there is a good chance that under the hood the QRhiBuffer is now backed by a whole new native buffer.
ubuf = rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 256); ubuf->build(); srb = rhi->newShaderResourceBindings() srb->setBindings({ QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, ubuf) }); srb->build(); ... // now in a later frame we need to grow the buffer to a larger size ubuf->setSize(512); ubuf->build(); // same as ubuf->release(); ubuf->build(); // that's it, srb needs no changes whatsoever
Pooled objects
In addition to resources, there are pooled objects as well, such as, QRhiResourceUpdateBatch. An instance is retrieved via a next
function, such as, nextResourceUpdateBatch(). The caller does not own the returned instance in this case. The only valid way of operating here is calling functions on the QRhiResourceUpdateBatch and then passing it to QRhiCommandBuffer::beginPass() or QRhiCommandBuffer::endPass(). These functions take care of returning the batch to the pool. Alternatively, a batch can be "canceled" and returned to the pool without processing by calling QRhiResourceUpdateBatch::release().
A typical pattern is thus:
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(); cb->beginPass(swapchain->currentFrameRenderTarget(), clearCol, clearDs, resUpdates);
Swapchain specifics
QRhiSwapChain features some special semantics due to the peculiar nature of swapchains.
- It has no
build
but rather a QRhiSwapChain::buildOrResize(). Repeatedly calling this function is not the same as calling QRhiSwapChain::release() followed by QRhiSwapChain::buildOrResize(). This is because swapchains often have ways to handle the case where buffers need to be resized in a manner that is more efficient than a brute force destroying and recreating from scratch. - An active QRhiSwapChain must be released by calling release(), or by destroying the object, before the QWindow's underlying QPlatformWindow, and so the associated native window object, is destroyed. It should not be postponed because releasing the swapchain may become problematic (and with some APIs, like Vulkan, is explicitly disallowed) when the native window is not around anymore, for example because the QPlatformWindow got destroyed upon getting a QWindow::close(). Therefore, releasing the swapchain must happen whenever the targeted QWindow sends the QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed event. If the event does not arrive before the destruction of the QWindow - this can happen when using QCoreApplication::quit() -, then check QWindow::handle() after the event loop exits and invoke the swapchain release when non-null (meaning the underlying native window is still around).
Ownership
The general rule is no ownership transfer. Creating a QRhi with an already existing graphics device does not mean the QRhi takes ownership of the device object. Similarly, ownership is not given away when a device or texture object is "exported" via QRhi::nativeHandles() or QRhiTexture::nativeHandles(). Most importantly, passing pointers in structs and via setters does not transfer ownership.
Member Type Documentation
enum QRhi::BeginFrameFlag
flags QRhi::BeginFrameFlags
Flag values for QRhi::beginFrame()
The BeginFrameFlags type is a typedef for QFlags<BeginFrameFlag>. It stores an OR combination of BeginFrameFlag values.
typedef QRhi::CleanupCallback
enum QRhi::EndFrameFlag
flags QRhi::EndFrameFlags
Flag values for QRhi::endFrame()
Constant | Value | Description |
---|---|---|
QRhi::SkipPresent | 1 << 0 | Specifies that no present command is to be queued or no swapBuffers call is to be made. This way no image is presented. Generating multiple frames with all having this flag set is not recommended (except, for example, for benchmarking purposes - but keep in mind that backends may behave differently when it comes to waiting for command completion without presenting so the results are not comparable between them) |
The EndFrameFlags type is a typedef for QFlags<EndFrameFlag>. It stores an OR combination of EndFrameFlag values.
enum QRhi::Feature
Flag values to indicate what features are supported by the backend currently in use.
Constant | Value | Description |
---|---|---|
QRhi::MultisampleTexture | 1 | Indicates that textures with a sample count larger than 1 are supported. |
QRhi::MultisampleRenderBuffer | 2 | Indicates that renderbuffers with a sample count larger than 1 are supported. |
QRhi::DebugMarkers | 3 | Indicates that debug marker groups (and so QRhiCommandBuffer::debugMarkBegin()) are supported. |
QRhi::Timestamps | 4 | Indicates that command buffer timestamps are supported. Relevant for QRhiProfiler::gpuFrameTimes(). |
QRhi::Instancing | 5 | Indicates that instanced drawing is supported. |
QRhi::CustomInstanceStepRate | 6 | Indicates that instance step rates other than 1 are supported. |
QRhi::PrimitiveRestart | 7 | Indicates that restarting the assembly of primitives when encountering an index value of 0xFFFF (IndexUInt16) or 0xFFFFFFFF (IndexUInt32) is enabled, for certain primitive topologies at least. QRhi will try to enable this with all backends, but in some cases it will not be supported. Dynamically controlling primitive restart is not possible since with some APIs primitive restart with a fixed index is always on. Applications must assume that whenever this feature is reported as supported, the above mentioned index values may be treated specially, depending on the topology. The only two topologies where primitive restart is guaranteed to behave identically across backends, as long as this feature is reported as supported, are LineStrip and TriangleStrip. |
QRhi::NonDynamicUniformBuffers | 8 | Indicates that creating buffers with the usage UniformBuffer and the types Immutable or Static is supported. When reported as unsupported, uniform (constant) buffers must be created as Dynamic. (which is recommended regardless) |
QRhi::NonFourAlignedEffectiveIndexBufferOffset | 9 | Indicates that effective index buffer offsets (indexOffset + firstIndex * indexComponentSize ) that are not 4 byte aligned are supported. When not supported, attempting to issue a drawIndexed() with a non-aligned effective offset may lead to unspecified behavior. |
QRhi::NPOTTextureRepeat | 10 | Indicates that the Repeat mode is supported for textures with a non-power-of-two size. |
QRhi::RedOrAlpha8IsRed | 11 | Indicates that the RED_OR_ALPHA8 format maps to a one component 8-bit red format. This is the case for all backends except OpenGL, where GL_ALPHA , a one component 8-bit alpha format, is used instead. This is relevant for shader code that samples from the texture. |
QRhi::ElementIndexUint | 12 | Indicates that 32-bit unsigned integer elements are supported in the index buffer. In practice this is true everywhere except when running on plain OpenGL ES 2.0 implementations without the necessary extension. When false, only 16-bit unsigned elements are supported in the index buffer. |
QRhi::Compute | 13 | Indicates that compute shaders are supported. |
enum QRhi::Flag
flags QRhi::Flags
Describes what special features to enable.
Constant | Value | Description |
---|---|---|
QRhi::EnableProfiling | 1 << 0 | Enables gathering timing (CPU, GPU) and resource (QRhiBuffer, QRhiTexture, etc.) information and additional metadata. See QRhiProfiler. Avoid enabling in production builds as it may involve a performance penalty. |
QRhi::EnableDebugMarkers | 1 << 1 | Enables debug marker groups. Without this frame debugging features like making debug groups and custom resource name visible in external GPU debugging tools will not be available and functions like QRhiCommandBuffer::debugMarkBegin() will become a no-op. Avoid enabling in production builds as it may involve a performance penalty. |
The Flags type is a typedef for QFlags<Flag>. It stores an OR combination of Flag values.
enum QRhi::FrameOpResult
Describes the result of operations that can have a soft failure.
Constant | Value | Description |
---|---|---|
QRhi::FrameOpSuccess | 0 | Success |
QRhi::FrameOpError | 1 | Unspecified error |
QRhi::FrameOpSwapChainOutOfDate | 2 | The swapchain is in an inconsistent state internally. This can be recoverable by attempting to repeat the operation (such as, beginFrame()) later. |
QRhi::FrameOpDeviceLost | 3 | The graphics device was lost. This can be recoverable by attempting to repeat the operation (such as, beginFrame()) and releasing and reinitializing all objects backed by native graphics resources. |
enum QRhi::Implementation
Describes which graphics API-specific backend gets used by a QRhi instance.
Constant | Value |
---|---|
QRhi::Null | 0 |
QRhi::Vulkan | 1 |
QRhi::OpenGLES2 | 2 |
QRhi::D3D11 | 3 |
QRhi::Metal | 4 |
enum QRhi::ResourceLimit
Describes the resource limit to query.
Constant | Value | Description |
---|---|---|
QRhi::TextureSizeMin | 1 | Minimum texture width and height. This is typically 1. The minimum texture size is handled gracefully, meaning attempting to create a texture with an empty size will instead create a texture with the minimum size. |
QRhi::TextureSizeMax | 2 | Maximum texture width and height. This depends on the graphics API and sometimes the platform or implementation as well. Typically the value is in the range 4096 - 16384. Attempting to create textures larger than this is expected to fail. |
QRhi::MaxColorAttachments | 3 | The maximum number of color attachments for a QRhiTextureRenderTarget, in case multiple render targets are supported. When MRT is not supported, the value is 1. Otherwise this is typically 8, but watch out for the fact that OpenGL only mandates 4 as the minimum, and that is what some OpenGL ES implementations provide. |
QRhi::FramesInFlight | 4 | The number of frames the backend may keep "in flight". The value has no relevance, and is unspecified, with backends like OpenGL and Direct3D 11. With backends like Vulkan or Metal, it is the responsibility of QRhi to block whenever starting a new frame and finding the CPU is already N - 1 frames ahead of the GPU (because the command buffer submitted in frame no. current - N has not yet completed). The value N is what is returned from here, and is typically 2. This can be relevant to applications that integrate rendering done directly with the graphics API, as such rendering code may want to perform double (if the value is 2) buffering for resources, such as, buffers, similarly to the QRhi backends themselves. The current frame slot index (a value running 0, 1, .., N-1, then wrapping around) is retrievable from QRhi::currentFrameSlot(). |
Property Documentation
Member Function Documentation
QRhi::~QRhi()
Destructor. Destroys the backend and releases resources.
void QRhi::addCleanupCallback(const QRhi::CleanupCallback &callback)
Registers a callback that is invoked either when the QRhi is destroyed, or when runCleanup() is called.
The callback will run with the graphics resource still available, so this provides an opportunity for the application to cleanly release QRhiResource instances belonging to the QRhi. This is particularly useful for managing the lifetime of resources stored in cache
type of objects, where the cache holds QRhiResources or objects containing QRhiResources.
See also runCleanup() and ~QRhi().
QRhi::Implementation QRhi::backend() const
Returns the backend type for this QRhi.
QRhi::FrameOpResult QRhi::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags = ...)
Starts a new frame targeting the next available buffer of swapChain.
The high level pattern of rendering into a QWindow using a swapchain:
- Create a swapchain.
- Call QRhiSwapChain::buildOrResize() whenever the surface size is different than before.
- Call QRhiSwapChain::release() on QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed.
- Then on every frame:
beginFrame(sc); updates = nextResourceUpdateBatch(); updates->... QRhiCommandBuffer *cb = sc->currentFrameCommandBuffer(); cb->beginPass(sc->currentFrameRenderTarget(), colorClear, dsClear, updates); ... cb->endPass(); ... // more passes as necessary endFrame(sc);
flags is currently unused.
See also endFrame().
QRhi::FrameOpResult QRhi::beginOffscreenFrame(QRhiCommandBuffer **cb)
Starts a new offscreen frame. Provides a command buffer suitable for recording rendering commands in cb.
Note: The QRhiCommandBuffer stored to *cb is not owned by the caller.
Rendering without a swapchain is possible as well. The typical use case is to use it in completely offscreen applications, e.g. to generate image sequences by rendering and reading back without ever showing a window.
Usage in on-screen applications (so beginFrame, endFrame, beginOffscreenFrame, endOffscreenFrame, beginFrame, ...) is possible too but it does reduce parallelism so it should be done only infrequently.
Offscreen frames do not let the CPU - potentially - generate another frame while the GPU is still processing the previous one. This has the side effect that if readbacks are scheduled, the results are guaranteed to be available once endOffscreenFrame() returns. That is not the case with frames targeting a swapchain.
The skeleton of rendering a frame without a swapchain and then reading the frame contents back could look like the following:
QRhiReadbackResult rbResult; QRhiCommandBuffer *cb; beginOffscreenFrame(&cb); beginPass ... u = nextResourceUpdateBatch(); u->readBackTexture(rb, &rbResult); endPass(u); endOffscreenFrame(); // image data available in rbResult
See also endOffscreenFrame().
QMatrix4x4 QRhi::clipSpaceCorrMatrix() const
Returns a matrix that can be used to allow applications keep using OpenGL-targeted vertex data and perspective projection matrices (such as, the ones generated by QMatrix4x4::perspective()), regardless of the backend. Once this_matrix * mvp
is used instead of just mvp
, vertex data with Y up and viewports with depth range 0 - 1 can be used without considering what backend and so graphics API is going to be used at run time.
See this page for a discussion of the topic from Vulkan perspective.
[static]
QRhi *QRhi::create(QRhi::Implementation impl, QRhiInitParams *params, QRhi::Flags flags = ..., QRhiNativeHandles *importDevice = nullptr)
Returns a new QRhi instance with a backend for the graphics API specified by impl.
params must point to an instance of one of the backend-specific subclasses of QRhiInitParams, such as, QRhiVulkanInitParams, QRhiMetalInitParams, QRhiD3D11InitParams, QRhiGles2InitParams. See these classes for examples on creating a QRhi.
flags is optional. It is used to enable profile and debug related features that are potentially expensive and should only be used during development.
int QRhi::currentFrameSlot() const
Returns the current frame slot index while recording a frame. Unspecified when called outside an active frame (that is, when isRecordingFrame() is false
).
With backends like Vulkan or Metal, it is the responsibility of the QRhi backend to block whenever starting a new frame and finding the CPU is already FramesInFlight - 1
frames ahead of the GPU (because the command buffer submitted in frame no. current
- FramesInFlight
has not yet completed).
Resources that tend to change between frames (such as, the native buffer object backing a QRhiBuffer with type QRhiBuffer::Dynamic) exist in multiple versions, so that each frame, that can be submitted while a previous one is still being processed, works with its own copy, thus avoiding the need to stall the pipeline when preparing the frame. (The contents of a resource that may still be in use in the GPU should not be touched, but simply always waiting for the previous frame to finish would reduce GPU utilization and ultimately, performance and efficiency.)
Conceptually this is somewhat similar to copy-on-write schemes used by some C++ containers and other types. It may also be similar to what an OpenGL or Direct 3D 11 implementation performs internally for certain type of objects.
In practice, such double (or tripple) buffering resources is realized in the Vulkan, Metal, and similar QRhi backends by having a fixed number of native resource (such as, VkBuffer) slots
behind a QRhiResource. That can then be indexed by a frame slot index running 0, 1, .., FramesInFlight-1, and then wrapping around.
All this is managed transparently to the users of QRhi. However, applications that integrate rendering done directly with the graphics API may want to perform a similar double or tripple buffering of their own graphics resources. That is then most easily achieved by knowing the values of the maximum number of in-flight frames (retrievable via resourceLimit()) and the current frame (slot) index (returned by this function).
See also isRecordingFrame(), beginFrame(), and endFrame().
QRhi::FrameOpResult QRhi::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags = ...)
Ends, commits, and presents a frame that was started in the last beginFrame() on swapChain.
Double (or triple) buffering is managed internally by the QRhiSwapChain and QRhi.
flags can optionally be used to change the behavior in certain ways. Passing QRhi::SkipPresent skips queuing the Present command or calling swapBuffers.
See also beginFrame().
QRhi::FrameOpResult QRhi::endOffscreenFrame()
Ends and waits for the offscreen frame.
See also beginOffscreenFrame().
QRhi::FrameOpResult QRhi::finish()
Waits for any work on the graphics queue (where applicable) to complete, then executes all deferred operations, like completing readbacks and resource releases. Can be called inside and outside of a frame, but not inside a pass. Inside a frame it implies submitting any work on the command buffer.
Note: Avoid this function. One case where it may be needed is when the results of an enqueued readback in a swapchain-based frame are needed at a fixed given point and so waiting for the results is desired.
bool QRhi::isClipDepthZeroToOne() const
Returns true
if the underlying graphics API uses depth 0 - 1 in clip space.
In practice this is false
for OpenGL only.
Note: clipSpaceCorrMatrix() includes the corresponding adjustment in its returned matrix.
bool QRhi::isFeatureSupported(QRhi::Feature feature) const
Returns true
if the specified feature is supported
bool QRhi::isRecordingFrame() const
Returns true when there is an active frame, meaning there was a beginFrame() (or beginOffscreenFrame()) with no corresponding endFrame() (or endOffscreenFrame()) yet.
See also currentFrameSlot(), beginFrame(), and endFrame().
bool QRhi::isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags = ...) const
Returns true
if the specified texture format modified by flags is supported.
The query is supported both for uncompressed and compressed formats.
bool QRhi::isYUpInFramebuffer() const
Returns true
if the underlying graphics API has the Y axis pointing up in framebuffers and images.
In practice this is true
for OpenGL only.
bool QRhi::isYUpInNDC() const
Returns true
if the underlying graphics API has the Y axis pointing up in its normalized device coordinate system.
In practice this is false
for Vulkan only.
Note: clipSpaceCorrMatrix() includes the corresponding adjustment (to make Y point up) in its returned matrix.
int QRhi::mipLevelsForSize(const QSize &size) const
Returns the number of mip levels for a given size.
const QRhiNativeHandles *QRhi::nativeHandles()
Returns a pointer to the backend-specific collection of native objects for the device, context, and similar concepts used by the backend.
Cast to QRhiVulkanNativeHandles, QRhiD3D11NativeHandles, QRhiGles2NativeHandles, QRhiMetalNativeHandles as appropriate.
Note: No ownership is transferred, neither for the returned pointer nor for any native objects.
QRhiBuffer *QRhi::newBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, int size)
Returns a new buffer with the specified type, usage, and size.
Note: Some usage and type combinations may not be supported by all backends. See UsageFlags and the feature flags.
See also QRhiResource::release().
QRhiComputePipeline *QRhi::newComputePipeline()
Returns a new compute pipeline resource.
Note: Compute is only available when the Compute feature is reported as supported.
See also QRhiResource::release().
QRhiGraphicsPipeline *QRhi::newGraphicsPipeline()
Returns a new graphics pipeline resource.
See also QRhiResource::release().
QRhiRenderBuffer *QRhi::newRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize, int sampleCount = 1, QRhiRenderBuffer::Flags flags = ...)
Returns a new renderbuffer with the specified type, pixelSize, sampleCount, and flags.
See also QRhiResource::release().
QRhiSampler *QRhi::newSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter, QRhiSampler::Filter mipmapMode, QRhiSampler::AddressMode u, QRhiSampler::AddressMode v)
Returns a new sampler with the specified magnification filter magFilter, minification filter minFilter, mipmapping mode mipmapMpde, and S/T addressing modes u and v.
See also QRhiResource::release().
QRhiShaderResourceBindings *QRhi::newShaderResourceBindings()
Returns a new shader resource binding collection resource.
See also QRhiResource::release().
QRhiSwapChain *QRhi::newSwapChain()
Returns a new swapchain.
See also QRhiResource::release() and QRhiSwapChain::buildOrResize().
QRhiTexture *QRhi::newTexture(QRhiTexture::Format format, const QSize &pixelSize, int sampleCount = 1, QRhiTexture::Flags flags = ...)
Returns a new texture with the specified format, pixelSize, sampleCount, and flags.
Note: format specifies the requested internal and external format, meaning the data to be uploaded to the texture will need to be in a compatible format, while the native texture may (but is not guaranteed to, in case of OpenGL at least) use this format internally.
See also QRhiResource::release().
QRhiTextureRenderTarget *QRhi::newTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc, QRhiTextureRenderTarget::Flags flags = ...)
Returns a new texture render target with color and depth/stencil attachments given in desc, and with the specified flags.
See also QRhiResource::release().
QRhiResourceUpdateBatch *QRhi::nextResourceUpdateBatch()
Returns an available, empty batch to which copy type of operations can be recorded.
Note: the return value is not owned by the caller and must never be destroyed. Instead, the batch is returned the the pool for reuse by passing it to QRhiCommandBuffer::beginPass(), QRhiCommandBuffer::endPass(), or QRhiCommandBuffer::resourceUpdate(), or by calling QRhiResourceUpdateBatch::release() on it.
Note: Can be called outside beginFrame() - endFrame() as well since a batch instance just collects data on its own, it does not perform any operations.
QRhiProfiler *QRhi::profiler()
Returns the associated QRhiProfiler instance.
An instance is always available for each QRhi, but it is not very useful without EnableProfiling because no data is collected without setting the flag upon creation.
int QRhi::resourceLimit(QRhi::ResourceLimit limit) const
Returns the value for the specified resource limit.
The values are expected to be queried by the backends upon initialization, meaning calling this function is a light operation.
void QRhi::runCleanup()
Invokes all registered cleanup functions. The list of cleanup callbacks it then cleared. Normally destroying the QRhi does this automatically, but sometimes it can be useful to trigger cleanup in order to release all cached, non-essential resources.
See also addCleanupCallback().
QSize QRhi::sizeForMipLevel(int mipLevel, const QSize &baseLevelSize) const
Returns the texture image size for a given mipLevel, calculated based on the level 0 size given in baseLevelSize.
QVector<int> QRhi::supportedSampleCounts() const
Returns the list of supported sample counts.
A typical example would be (1, 2, 4, 8).
With some backend this list of supported values is fixed in advance, while with some others the (physical) device properties indicate what is supported at run time.
QThread *QRhi::thread() const
Returns the thread on which the QRhi was initialized.
int QRhi::ubufAligned(int v) const
Returns the value (typically an offset) v aligned to the uniform buffer alignment given by by ubufAlignment().
int QRhi::ubufAlignment() const
Returns the minimum uniform buffer offset alignment in bytes. This is typically 256.
Attempting to bind a uniform buffer region with an offset not aligned to this value will lead to failures depending on the backend and the underlying graphics API.
See also ubufAligned().