QRhiSwapChain Class
Swapchain resource. More...
Header: | #include <QRhiSwapChain> |
qmake: | QT += rhi |
Inherits: | QRhiResource |
Public Types
enum | Flag { SurfaceHasPreMulAlpha, SurfaceHasNonPreMulAlpha, sRGB, UsedAsTransferSource, NoVSync, MinimalBufferCount } |
flags | Flags |
Public Functions
virtual bool | buildOrResize() = 0 |
virtual QRhiCommandBuffer * | currentFrameCommandBuffer() = 0 |
virtual QRhiRenderTarget * | currentFrameRenderTarget() = 0 |
QSize | currentPixelSize() const |
QRhiRenderBuffer * | depthStencil() const |
QRhiSwapChain::Flags | flags() const |
virtual QRhiRenderPassDescriptor * | newCompatibleRenderPassDescriptor() = 0 |
QRhiRenderPassDescriptor * | renderPassDescriptor() const |
int | sampleCount() const |
void | setDepthStencil(QRhiRenderBuffer *ds) |
void | setFlags(QRhiSwapChain::Flags f) |
void | setRenderPassDescriptor(QRhiRenderPassDescriptor *desc) |
void | setSampleCount(int samples) |
void | setWindow(QWindow *window) |
virtual QSize | surfacePixelSize() = 0 |
QWindow * | window() const |
Reimplemented Public Functions
virtual QRhiResource::Type | resourceType() const override |
- 6 public functions inherited from QRhiResource
Protected Variables
QSize | m_currentPixelSize |
QRhiRenderBuffer * | m_depthStencil |
QRhiSwapChain::Flags | m_flags |
QRhiRenderPassDescriptor * | m_renderPassDesc |
int | m_sampleCount |
QWindow * | m_window |
- 3 protected variables inherited from QRhiResource
Additional Inherited Members
- 1 protected function inherited from QRhiResource
Detailed Description
Swapchain resource.
A swapchain enables presenting rendering results to a surface. A swapchain is typically backed by a set of color buffers. Of these, one is displayed at a time.
Below is a typical pattern for creating and managing a swapchain and some associated resources in order to render onto a QWindow:
void init() { sc = rhi->newSwapChain(); ds = rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, QSize(), // no need to set the size yet 1, QRhiRenderBuffer::UsedWithSwapChainOnly); sc->setWindow(window); sc->setDepthStencil(ds); rp = sc->newCompatibleRenderPassDescriptor(); sc->setRenderPassDescriptor(rp); resizeSwapChain(); } void resizeSwapChain() { const QSize outputSize = sc->surfacePixelSize(); ds->setPixelSize(outputSize); ds->build(); hasSwapChain = sc->buildOrResize(); } void render() { if (!hasSwapChain || notExposed) return; if (sc->currentPixelSize() != sc->surfacePixelSize() || newlyExposed) { resizeSwapChain(); if (!hasSwapChain) return; newlyExposed = false; } rhi->beginFrame(sc); // ... rhi->endFrame(sc); }
Avoid relying on QWindow resize events to resize swapchains, especially considering that surface sizes may not always fully match the QWindow reported dimensions. The safe, cross-platform approach is to do the check via surfacePixelSize() whenever starting a new frame.
Releasing the swapchain must happen while the QWindow and the underlying native window is fully up and running. Building on the previous example:
void releaseSwapChain() { if (hasSwapChain) { sc->release(); hasSwapChain = false; } } // assuming Window is our QWindow subclass bool Window::event(QEvent *e) { switch (e->type()) { case QEvent::UpdateRequest: // for QWindow::requestUpdate() render(); break; case QEvent::PlatformSurface: if (static_cast<QPlatformSurfaceEvent *>(e)->surfaceEventType() == QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed) releaseSwapChain(); break; default: break; } return QWindow::event(e); }
Initializing the swapchain and starting to render the first frame cannot start at any time. The safe, cross-platform approach is to rely on expose events. QExposeEvent is a loosely specified event that is sent whenever a window gets mapped, obscured, and resized, depending on the platform.
void Window::exposeEvent(QExposeEvent *) { // initialize and start rendering when the window becomes usable for graphics purposes if (isExposed() && !running) { running = true; init(); } // stop pushing frames when not exposed or size becomes 0 if ((!isExposed() || (hasSwapChain && sc->surfacePixelSize().isEmpty())) && running) notExposed = true; // continue when exposed again and the surface has a valid size if (isExposed() && running && notExposed && !sc->surfacePixelSize().isEmpty()) { notExposed = false; newlyExposed = true; } if (isExposed() && !sc->surfacePixelSize().isEmpty()) render(); }
Once the rendering has started, a simple way to request a new frame is QWindow::requestUpdate(). While on some platforms this is merely a small timer, on others it has a specific implementation: for instance on macOS or iOS it may be backed by CVDisplayLink. The example above is already prepared for update requests by handling QEvent::UpdateRequest.
While acting as a QRhiRenderTarget, QRhiSwapChain also manages a QRhiCommandBuffer. Calling QRhi::endFrame() submits the recorded commands and also enqueues a present
request. The default behavior is to do this with a swap interval of 1, meaning synchronizing to the display's vertical refresh is enabled. Thus the rendering thread calling beginFrame() and endFrame() will get throttled to vsync. On some backends this can be disabled by passing QRhiSwapChain:NoVSync in flags().
Multisampling (MSAA) is handled transparently to the applications when requested via setSampleCount(). Where applicable, QRhiSwapChain will take care of creating additional color buffers and issuing a multisample resolve command at the end of a frame. For OpenGL, it is necessary to request the appropriate sample count also via QSurfaceFormat, by calling QSurfaceFormat::setDefaultFormat() before initializing the QRhi.
Member Type Documentation
enum QRhiSwapChain::Flag
flags QRhiSwapChain::Flags
Flag values to describe swapchain properties
Constant | Value | Description |
---|---|---|
QRhiSwapChain::SurfaceHasPreMulAlpha | 1 << 0 | Indicates that the target surface has transparency with premultiplied alpha. |
QRhiSwapChain::SurfaceHasNonPreMulAlpha | 1 << 1 | Indicates the target surface has transparencyt with non-premultiplied alpha. |
QRhiSwapChain::sRGB | 1 << 2 | Requests to pick an sRGB format for the swapchain and/or its render target views, where applicable. Note that this implies that sRGB framebuffer update and blending will get enabled for all content targeting this swapchain, and opting out is not possible. For OpenGL, set sRGBColorSpace on the QSurfaceFormat of the QWindow in addition. |
QRhiSwapChain::UsedAsTransferSource | 1 << 3 | Indicates the the swapchain will be used as the source of a readback in QRhiResourceUpdateBatch::readBackTexture(). |
QRhiSwapChain::NoVSync | 1 << 4 | Requests disabling waiting for vertical sync, also avoiding throttling the rendering thread. The behavior is backend specific and applicable only where it is possible to control this. Some may ignore the request altogether. For OpenGL, try instead setting the swap interval to 0 on the QWindow via QSurfaceFormat::setSwapInterval(). |
QRhiSwapChain::MinimalBufferCount | 1 << 5 | Requests creating the swapchain with the minimum number of buffers, which is in practice 2, unless the graphics implementation has a higher minimum number than that. Only applicable with backends where such control is available via the graphics API, for example, Vulkan. By default it is up to the backend to decide what number of buffers it requests (in practice this is almost always either 2 or 3), and it is not the applications' concern. However, on Vulkan for instance the backend will likely prefer the higher number (3), for example to avoid odd performance issues with some Vulkan implementations on mobile devices. It could be that on some platforms it can prove to be beneficial to force the lower buffer count (2), so this flag allows forcing that. Note that all this has no effect on the number of frames kept in flight, so the CPU (QRhi) will still prepare frames at most N - 1 frames ahead of the GPU, even when the swapchain image buffer count larger than N . (N = QRhi::FramesInFlight and typically 2). |
The Flags type is a typedef for QFlags<Flag>. It stores an OR combination of Flag values.
Property Documentation
Member Function Documentation
[pure virtual]
bool QRhiSwapChain::buildOrResize()
Creates the swapchain if not already done and resizes the swapchain buffers to match the current size of the targeted surface. Call this whenever the size of the target surface is different than before.
Note: call release() only when the swapchain needs to be released completely, typically upon QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed. To perform resizing, just call buildOrResize().
Returns true
when successful, false
when a graphics operation failed. Regardless of the return value, calling release() is always safe.
[pure virtual]
QRhiCommandBuffer *QRhiSwapChain::currentFrameCommandBuffer()
Returns a command buffer on which rendering commands can be recorded. Only valid within a QRhi::beginFrame() - QRhi::endFrame() block where beginFrame() was called with this swapchain.
Note: the value must not be cached and reused between frames
[pure virtual]
QRhiRenderTarget *QRhiSwapChain::currentFrameRenderTarget()
Returns a render target that can used with beginPass() in order to render the the swapchain's current backbuffer. Only valid within a QRhi::beginFrame() - QRhi::endFrame() block where beginFrame() was called with this swapchain.
Note: the value must not be cached and reused between frames
QSize QRhiSwapChain::currentPixelSize() const
Returns the size with which the swapchain was last successfully built. Use this to decide if buildOrResize() needs to be called again: if currentPixelSize() != surfacePixelSize()
then the swapchain needs to be resized.
See also surfacePixelSize().
QRhiRenderBuffer *QRhiSwapChain::depthStencil() const
See also setDepthStencil().
QRhiSwapChain::Flags QRhiSwapChain::flags() const
See also setFlags().
[pure virtual]
QRhiRenderPassDescriptor *QRhiSwapChain::newCompatibleRenderPassDescriptor()
QRhiRenderPassDescriptor *QRhiSwapChain::renderPassDescriptor() const
See also setRenderPassDescriptor().
[override virtual]
QRhiResource::Type QRhiSwapChain::resourceType() const
Reimplemented from QRhiResource::resourceType().
Returns the resource type.
int QRhiSwapChain::sampleCount() const
See also setSampleCount().
void QRhiSwapChain::setDepthStencil(QRhiRenderBuffer *ds)
See also depthStencil().
void QRhiSwapChain::setFlags(QRhiSwapChain::Flags f)
See also flags().
void QRhiSwapChain::setRenderPassDescriptor(QRhiRenderPassDescriptor *desc)
See also renderPassDescriptor().
void QRhiSwapChain::setSampleCount(int samples)
See also sampleCount().
void QRhiSwapChain::setWindow(QWindow *window)
See also window().
[pure virtual]
QSize QRhiSwapChain::surfacePixelSize()
Returns The size of the window's associated surface or layer. Do not assume this is the same as QWindow::size() * QWindow::devicePixelRatio().
Can be called before buildOrResize() (but with window() already set), which allows setting the correct size for the depth-stencil buffer that is then used together with the swapchain's color buffers. Also used in combination with currentPixelSize() to detect size changes.
See also currentPixelSize().
QWindow *QRhiSwapChain::window() const
See also setWindow().