Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qrhiwidget.cpp
Go to the documentation of this file.
1// Copyright (C) 2023 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5#include "qrhiwidget_p.h"
6#include <private/qguiapplication_p.h>
7#include <qpa/qplatformintegration.h>
8#include <private/qwidgetrepaintmanager_p.h>
9
11
12/*!
13 \class QRhiWidget
14 \inmodule QtWidgets
15 \since 6.7
16
17 \brief The QRhiWidget class is a widget for rendering 3D graphics via an
18 accelerated grapics API, such as Vulkan, Metal, or Direct 3D.
19
20 QRhiWidget provides functionality for displaying 3D content rendered
21 through the \l QRhi APIs within a QWidget-based application. In many ways
22 it is the portable equivalent of \l QOpenGLWidget that is not tied to a
23 single 3D graphics API, but rather can function with all the APIs QRhi
24 supports (such as, Direct 3D 11/12, Vulkan, Metal, and OpenGL).
25
26 QRhiWidget is expected to be subclassed. To render into the 2D texture that
27 is implicitly created and managed by the QRhiWidget, subclasses should
28 reimplement the virtual functions initialize() and render().
29
30 The size of the texture will by default adapt to the size of the widget. If
31 a fixed size is preferred, set a fixed size specified in pixels by calling
32 setFixedColorBufferSize().
33
34 In addition to the texture serving as the color buffer, a depth/stencil
35 buffer and a render target binding these together is maintained implicitly
36 as well.
37
38 The QRhi for the widget's top-level window is configured to use a
39 platform-specific backend and graphics API by default: Metal on macOS and
40 iOS, Direct 3D 11 on Windows, OpenGL otherwise. Call setApi() to override
41 this.
42
43 \note A single widget window can only use one QRhi backend, and so one
44 single 3D graphics API. If two QRhiWidget or QQuickWidget widgets in the
45 window's widget hierarchy request different APIs, only one of them will
46 function correctly.
47
48 \note While QRhiWidget is a public Qt API, the QRhi family of classes in
49 the Qt Gui module, including QRhi, QShader and QShaderDescription, offer
50 limited compatibility guarantees. There are no source or binary
51 compatibility guarantees for these classes, meaning the API is only
52 guaranteed to work with the Qt version the application was developed
53 against. Source incompatible changes are however aimed to be kept at a
54 minimum and will only be made in minor releases (6.7, 6.8, and so on).
55 \c{qrhiwidget.h} does not directly include any QRhi-related headers. To use
56 those classes when implementing a QRhiWidget subclass, link to
57 \c{Qt::GuiPrivate} (if using CMake), and include the appropriate headers
58 with the \c rhi prefix, for example \c{#include <rhi/qrhi.h>}.
59
60 An example of a simple QRhiWidget subclass rendering a triangle is the
61 following:
62
63 \snippet qrhiwidget/rhiwidgetintro.cpp 0
64
65 This is a widget that continuously requests updates, throttled by the
66 presentation rate (vsync, depending on the screen refresh rate). If
67 rendering continuously is not desired, the update() call in render() should
68 be removed, and rather issued only when updating the rendered content is
69 necessary. For example, if the rotation of the cube should be tied to the
70 value of a QSlider, then connecting the slider's value change signal to a
71 slot or lambda that forwards the new value and calls update() is
72 sufficient.
73
74 The vertex and fragment shaders are provided as Vulkan-style GLSL and must
75 be processed first by the Qt shader infrastructure first. This is achieved
76 either by running the \c qsb command-line tool manually, or by using the
77 \l{qt_add_shaders}{qt_add_shaders()} function in
78 CMake. The QRhiWidget implementation loads these pre-processed \c{.qsb}
79 files that are shipped with the application. See \l{Qt Shader Tools} for
80 more information about Qt's shader translation infrastructure.
81
82 The source code for these shaders could be the following:
83
84 \c{color.vert}
85
86 \snippet qrhiwidget/rhiwidgetintro.vert 0
87
88 \c{color.frag}
89
90 \snippet qrhiwidget/rhiwidgetintro.frag 0
91
92 The result is a widget that shows the following:
93
94 \image qrhiwidget-intro.jpg {Multicolored triangle on a green background}
95
96 For a complete, minimal, introductory example check out the \l{Simple RHI
97 Widget Example}.
98
99 For an example with more functionality and demonstration of further
100 concepts, see the \l{Cube RHI Widget Example}.
101
102 QRhiWidget always involves rendering into a backing texture, not
103 directly to the window (the surface or layer provided by the windowing
104 system for the native window). This allows properly compositing the content
105 with the rest of the widget-based UI, and offering a simple and compact
106 API, making it easy to get started. All this comes at the expense of
107 additional resources and a potential effect on performance. This is often
108 perfectly acceptable in practice, but advanced users should keep in mind
109 the pros and cons of the different approaches. Refer to the \l{RHI Window
110 Example} and compare it with the \l{Simple RHI Widget Example} for details
111 about the two approaches.
112
113 Reparenting a QRhiWidget into a widget hierarchy that belongs to a
114 different window (top-level widget), or making the QRhiWidget itself a
115 top-level (by setting the parent to \nullptr), involves changing the
116 associated QRhi (and potentially destroying the old one) while the
117 QRhiWidget continues to stay alive and well. To support this, robust
118 QRhiWidget implementations are expected to reimplement the
119 releaseResources() virtual function as well, and drop their QRhi resources
120 just as they do in the destructor. The \l{Cube RHI Widget Example}
121 demonstrates this in practice.
122
123 While not a primary use case, QRhiWidget also allows incorporating
124 rendering code that directly uses a 3D graphics API such as Vulkan, Metal,
125 Direct 3D, or OpenGL. See \l QRhiCommandBuffer::beginExternal() for details
126 on recording native commands within a QRhi render pass, as well as
127 \l QRhiTexture::createFrom() for a way to wrap an existing native texture and
128 then use it with QRhi in a subsequent render pass. Note however that the
129 configurability of the underlying graphics API (its device or context
130 features, layers, extensions, etc.) is going to be limited since
131 QRhiWidget's primary goal is to provide an environment suitable for
132 QRhi-based rendering code, not to enable arbitrary, potentially complex,
133 foreign rendering engines.
134
135 \since 6.7
136
137 \sa QRhi, QShader, QOpenGLWidget, {Simple RHI Widget Example}, {Cube RHI Widget Example}
138 */
139
140/*!
141 \enum QRhiWidget::Api
142 Specifies the 3D API and QRhi backend to use
143
144 \value Null
145 \value OpenGL
146 \value Metal
147 \value Vulkan
148 \value Direct3D11
149 \value Direct3D12
150
151 \sa QRhi
152 */
153
154/*!
155 \enum QRhiWidget::TextureFormat
156 Specifies the format of the texture to which the QRhiWidget renders.
157
158 \value RGBA8 See QRhiTexture::RGBA8.
159 \value RGBA16F See QRhiTexture::RGBA16F.
160 \value RGBA32F See QRhiTexture::RGBA32F.
161 \value RGB10A2 See QRhiTexture::RGB10A2.
162
163 \sa QRhiTexture
164 */
165
166/*!
167 Constructs a widget which is a child of \a parent, with widget flags set to \a f.
168 */
169QRhiWidget::QRhiWidget(QWidget *parent, Qt::WindowFlags f)
170 : QWidget(*(new QRhiWidgetPrivate), parent, f)
171{
172 Q_D(QRhiWidget);
173 d->init();
174}
175
176/*!
177 * \internal
178 */
179QRhiWidget::QRhiWidget(QRhiWidgetPrivate &dd, QWidget *parent, Qt::WindowFlags f)
180 : QWidget(dd, parent, f)
181{
182 Q_D(QRhiWidget);
183 d->init();
184}
185
186/*!
187 * \internal
188 */
189void QRhiWidgetPrivate::init()
190{
191 if (Q_UNLIKELY(!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::RhiBasedRendering)))
192 qWarning("QRhiWidget: QRhi is not supported on this platform.");
193 else
194 setRenderToTexture();
195
196 config.setEnabled(true);
197#if defined(Q_OS_DARWIN)
198 config.setApi(QPlatformBackingStoreRhiConfig::Metal);
199#elif defined(Q_OS_WIN)
200 config.setApi(QPlatformBackingStoreRhiConfig::D3D11);
201#else
202 config.setApi(QPlatformBackingStoreRhiConfig::OpenGL);
203#endif
204}
205
206/*!
207 Destructor.
208 */
209QRhiWidget::~QRhiWidget()
210{
211 Q_D(QRhiWidget);
212
213 if (d->rhi) {
214 d->rhi->removeCleanupCallback(this);
215 // rhi resources must be destroyed here, due to how QWidget teardown works;
216 // it should not be left to the private object's destruction.
217 d->resetRenderTargetObjects();
218 d->resetColorBufferObjects();
219 qDeleteAll(d->pendingDeletes);
220 }
221
222 d->offscreenRenderer.reset();
223}
224
225/*!
226 Handles resize events that are passed in the \a e event parameter. Calls
227 the virtual function initialize().
228
229 \note Avoid overriding this function in derived classes. If that is not
230 feasible, make sure that QRhiWidget's implementation is invoked too.
231 Otherwise the underlying texture object and related resources will not get
232 resized properly and will lead to incorrect rendering.
233 */
234void QRhiWidget::resizeEvent(QResizeEvent *e)
235{
236 Q_D(QRhiWidget);
237
238 if (e->size().isEmpty()) {
239 d->noSize = true;
240 return;
241 }
242 d->noSize = false;
243
244 d->sendPaintEvent(QRect(QPoint(0, 0), size()));
245}
246
247/*!
248 Handles paint events.
249
250 Calling QWidget::update() will lead to sending a paint event \a e, and thus
251 invoking this function. The sending of the event is asynchronous and will
252 happen at some point after returning from update(). This function will
253 then, after some preparation, call the virtual render() to update the
254 contents of the QRhiWidget's associated texture. The widget's top-level
255 window will then composite the texture with the rest of the window.
256 */
257void QRhiWidget::paintEvent(QPaintEvent *)
258{
259 Q_D(QRhiWidget);
260 if (!updatesEnabled() || d->noSize)
261 return;
262
263 d->ensureRhi();
264 if (!d->rhi) {
265 qWarning("QRhiWidget: No QRhi");
266 emit renderFailed();
267 return;
268 }
269
270 QRhiCommandBuffer *cb = nullptr;
271 if (d->rhi->beginOffscreenFrame(&cb) != QRhi::FrameOpSuccess)
272 return;
273
274 bool needsInit = false;
275 d->ensureTexture(&needsInit);
276 if (d->colorTexture || d->msaaColorBuffer) {
277 bool canRender = true;
278 if (needsInit)
279 canRender = d->invokeInitialize(cb);
280 if (canRender)
281 render(cb);
282 }
283
284 d->rhi->endOffscreenFrame();
285}
286
287/*!
288 \reimp
289*/
290bool QRhiWidget::event(QEvent *e)
291{
292 Q_D(QRhiWidget);
293 switch (e->type()) {
294 case QEvent::WindowAboutToChangeInternal:
295 // The QRhi will almost certainly change, prevent texture() from
296 // returning the existing QRhiTexture in the meantime.
297 d->textureInvalid = true;
298
299 if (d->rhi && d->rhi != d->offscreenRenderer.rhi()) {
300 // Drop the cleanup callback registered to the toplevel's rhi and
301 // do the early-release, there may not be another chance to do
302 // this, and the QRhi we have currently set may be destroyed by the
303 // time we get to ensureRhi() again.
304 d->rhi->removeCleanupCallback(this);
305 releaseResources(); // notify the user code about the early-release
306 d->releaseResources();
307 // The old QRhi may be destroyed before the next paint, reparent,
308 // or widget destruction. ensureRhi() will register a new callback
309 // even if we end up using the same QRhi again.
310 d->rhi = nullptr;
311 }
312
313 break;
314
315 case QEvent::Show:
316 if (isVisible())
317 d->sendPaintEvent(QRect(QPoint(0, 0), size()));
318 break;
319 default:
320 break;
321 }
322 return QWidget::event(e);
323}
324
325QWidgetPrivate::TextureData QRhiWidgetPrivate::texture() const
326{
327 // This is the only safe place to clear pendingDeletes, due to the
328 // possibility of the texture returned in the previous invocation of this
329 // function having been added to pendingDeletes, meaning the object then
330 // needs to be valid until the next (this) invocation of this function.
331 // (the exact object lifetime requirements depend on the
332 // QWidget/RepaintManager internal implementation; for now avoid relying on
333 // such details by clearing pendingDeletes only here, not in endCompose())
334 qDeleteAll(pendingDeletes);
335 pendingDeletes.clear();
336
337 TextureData td;
338 if (!textureInvalid)
339 td.textureLeft = resolveTexture ? resolveTexture : colorTexture;
340 return td;
341}
342
343QPlatformTextureList::Flags QRhiWidgetPrivate::textureListFlags()
344{
345 QPlatformTextureList::Flags flags = QWidgetPrivate::textureListFlags();
346 if (mirrorVertically)
347 flags |= QPlatformTextureList::MirrorVertically;
348 return flags;
349}
350
351QPlatformBackingStoreRhiConfig QRhiWidgetPrivate::rhiConfig() const
352{
353 return config;
354}
355
356void QRhiWidgetPrivate::endCompose()
357{
358 // This function is called by QWidgetRepaintManager right after the
359 // backingstore's QRhi-based flush returns. In practice that means after
360 // the begin-endFrame() on the top-level window's swapchain.
361
362 if (rhi) {
363 Q_Q(QRhiWidget);
364 emit q->frameSubmitted();
365 }
366}
367
368// This is reimplemented to enable calling QWidget::grab() on the widget or an
369// ancestor of it. At the same time, QRhiWidget provides its own
370// grabFramebuffer() as well, mirroring QQuickWidget and QOpenGLWidget for
371// consistency. In both types of grabs we end up in here.
372QImage QRhiWidgetPrivate::grabFramebuffer()
373{
374 Q_Q(QRhiWidget);
375 if (noSize)
376 return QImage();
377
378 ensureRhi();
379 if (!rhi) {
380 // The widget (and its parent chain, if any) may not be shown at
381 // all, yet one may still want to use it for grabs. This is
382 // ridiculous of course because the rendering infrastructure is
383 // tied to the top-level widget that initializes upon expose, but
384 // it has to be supported.
385 offscreenRenderer.setConfig(config);
386 // no window passed in, so no swapchain, but we get a functional QRhi which we own
387 offscreenRenderer.create();
388 rhi = offscreenRenderer.rhi();
389 if (!rhi) {
390 qWarning("QRhiWidget: Failed to create dedicated QRhi for grabbing");
391 emit q->renderFailed();
392 return QImage();
393 }
394 }
395
396 QRhiCommandBuffer *cb = nullptr;
397 if (rhi->beginOffscreenFrame(&cb) != QRhi::FrameOpSuccess)
398 return QImage();
399
400 QRhiReadbackResult readResult;
401 bool readCompleted = false;
402 bool needsInit = false;
403 ensureTexture(&needsInit);
404
405 if (colorTexture || msaaColorBuffer) {
406 bool canRender = true;
407 if (needsInit)
408 canRender = invokeInitialize(cb);
409 if (canRender)
410 q->render(cb);
411
412 QRhiResourceUpdateBatch *readbackBatch = rhi->nextResourceUpdateBatch();
413 readResult.completed = [&readCompleted] { readCompleted = true; };
414 readbackBatch->readBackTexture(resolveTexture ? resolveTexture : colorTexture, &readResult);
415 cb->resourceUpdate(readbackBatch);
416 }
417
418 rhi->endOffscreenFrame();
419
420 if (readCompleted) {
421 QImage::Format imageFormat = QImage::Format_RGBA8888;
422 switch (widgetTextureFormat) {
423 case QRhiWidget::TextureFormat::RGBA8:
424 break;
425 case QRhiWidget::TextureFormat::RGBA16F:
426 imageFormat = QImage::Format_RGBA16FPx4;
427 break;
428 case QRhiWidget::TextureFormat::RGBA32F:
429 imageFormat = QImage::Format_RGBA32FPx4;
430 break;
431 case QRhiWidget::TextureFormat::RGB10A2:
432 imageFormat = QImage::Format_BGR30;
433 break;
434 }
435 QImage wrapperImage(reinterpret_cast<const uchar *>(readResult.data.constData()),
436 readResult.pixelSize.width(), readResult.pixelSize.height(),
437 imageFormat);
438 QImage result;
439 if (rhi->isYUpInFramebuffer())
440 result = wrapperImage.flipped();
441 else
442 result = wrapperImage.copy();
443 result.setDevicePixelRatio(q->devicePixelRatio());
444 return result;
445 } else {
446 Q_UNREACHABLE();
447 }
448
449 return QImage();
450}
451
452void QRhiWidgetPrivate::resetColorBufferObjects()
453{
454 if (colorTexture) {
455 pendingDeletes.append(colorTexture);
456 colorTexture = nullptr;
457 }
458 if (msaaColorBuffer) {
459 pendingDeletes.append(msaaColorBuffer);
460 msaaColorBuffer = nullptr;
461 }
462 if (resolveTexture) {
463 pendingDeletes.append(resolveTexture);
464 resolveTexture = nullptr;
465 }
466}
467
468void QRhiWidgetPrivate::resetRenderTargetObjects()
469{
470 if (renderTarget) {
471 renderTarget->deleteLater();
472 renderTarget = nullptr;
473 }
474 if (renderPassDescriptor) {
475 renderPassDescriptor->deleteLater();
476 renderPassDescriptor = nullptr;
477 }
478 if (depthStencilBuffer) {
479 depthStencilBuffer->deleteLater();
480 depthStencilBuffer = nullptr;
481 }
482}
483
484void QRhiWidgetPrivate::releaseResources()
485{
486 resetRenderTargetObjects();
487 resetColorBufferObjects();
488 qDeleteAll(pendingDeletes);
489 pendingDeletes.clear();
490}
491
492void QRhiWidgetPrivate::ensureRhi()
493{
494 Q_Q(QRhiWidget);
495 QRhi *currentRhi = QWidgetPrivate::rhi();
496 if (currentRhi && currentRhi->backend() != QBackingStoreRhiSupport::apiToRhiBackend(config.api())) {
497 qWarning("The top-level window is already using another graphics API for composition, "
498 "'%s' is not compatible with this widget",
499 currentRhi->backendName());
500 return;
501 }
502
503 if (currentRhi && rhi != currentRhi) {
504 if (rhi) {
505 rhi->removeCleanupCallback(q);
506 // if previously we created our own but now get a QRhi from the
507 // top-level, then drop what we have and start using the top-level's
508 if (rhi == offscreenRenderer.rhi()) {
509 q->releaseResources(); // notify the user code about the early-release
510 releaseResources();
511 offscreenRenderer.reset();
512 } else {
513 // rhi resources created by us all belong to the old rhi, drop them;
514 // due to nulling out colorTexture this is also what ensures that
515 // initialize() is going to be called again eventually
516 resetRenderTargetObjects();
517 resetColorBufferObjects();
518 }
519 }
520
521 // Normally the widget gets destroyed before the QRhi (which is managed by
522 // the top-level's backingstore). When reparenting between top-levels is
523 // involved, that is not always the case. Therefore we use a per-widget rhi
524 // cleanup callback to get notified when the QRhi is about to be destroyed
525 // while the QRhiWidget is still around.
526 currentRhi->addCleanupCallback(q, [q, this](QRhi *regRhi) {
527 if (!QWidgetPrivate::get(q)->data.in_destructor && this->rhi == regRhi) {
528 q->releaseResources(); // notify the user code about the early-release
529 releaseResources();
530 // must null out our ref, the QRhi object is going to be invalid
531 this->rhi = nullptr;
532 }
533 });
534 }
535
536 rhi = currentRhi;
537}
538
539void QRhiWidgetPrivate::ensureTexture(bool *changed)
540{
541 Q_Q(QRhiWidget);
542
543 QSize newSize = fixedSize;
544 if (newSize.isEmpty())
545 newSize = q->size() * q->devicePixelRatio();
546
547 const int minTexSize = rhi->resourceLimit(QRhi::TextureSizeMin);
548 const int maxTexSize = rhi->resourceLimit(QRhi::TextureSizeMax);
549 newSize.setWidth(qMin(maxTexSize, qMax(minTexSize, newSize.width())));
550 newSize.setHeight(qMin(maxTexSize, qMax(minTexSize, newSize.height())));
551
552 if (colorTexture) {
553 if (colorTexture->format() != rhiTextureFormat || colorTexture->sampleCount() != samples) {
554 resetColorBufferObjects();
555 // sample count change needs new depth-stencil, possibly a new
556 // render target; format change needs new renderpassdescriptor;
557 // therefore must drop the rest too
558 resetRenderTargetObjects();
559 }
560 }
561
562 if (msaaColorBuffer) {
563 if (msaaColorBuffer->backingFormat() != rhiTextureFormat || msaaColorBuffer->sampleCount() != samples) {
564 resetColorBufferObjects();
565 // sample count change needs new depth-stencil, possibly a new
566 // render target; format change needs new renderpassdescriptor;
567 // therefore must drop the rest too
568 resetRenderTargetObjects();
569 }
570 }
571
572 if (!colorTexture && samples <= 1) {
573 if (changed)
574 *changed = true;
575 if (!rhi->isTextureFormatSupported(rhiTextureFormat)) {
576 qWarning("QRhiWidget: The requested texture format (%d) is not supported by the "
577 "underlying 3D graphics API implementation", int(rhiTextureFormat));
578 }
579 colorTexture = rhi->newTexture(rhiTextureFormat, newSize, samples, QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource);
580 if (!colorTexture->create()) {
581 qWarning("Failed to create backing texture for QRhiWidget");
582 delete colorTexture;
583 colorTexture = nullptr;
584 return;
585 }
586 }
587
588 if (samples > 1) {
589 if (!msaaColorBuffer) {
590 if (changed)
591 *changed = true;
592 if (!rhi->isFeatureSupported(QRhi::MultisampleRenderBuffer)) {
593 qWarning("QRhiWidget: Multisample renderbuffers are reported as unsupported; "
594 "sample count %d will not work as expected", samples);
595 }
596 if (!rhi->isTextureFormatSupported(rhiTextureFormat)) {
597 qWarning("QRhiWidget: The requested texture format (%d) is not supported by the "
598 "underlying 3D graphics API implementation", int(rhiTextureFormat));
599 }
600 msaaColorBuffer = rhi->newRenderBuffer(QRhiRenderBuffer::Color, newSize, samples, {}, rhiTextureFormat);
601 if (!msaaColorBuffer->create()) {
602 qWarning("Failed to create multisample color buffer for QRhiWidget");
603 delete msaaColorBuffer;
604 msaaColorBuffer = nullptr;
605 return;
606 }
607 }
608 if (!resolveTexture) {
609 if (changed)
610 *changed = true;
611 resolveTexture = rhi->newTexture(rhiTextureFormat, newSize, 1, QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource);
612 if (!resolveTexture->create()) {
613 qWarning("Failed to create resolve texture for QRhiWidget");
614 delete resolveTexture;
615 resolveTexture = nullptr;
616 return;
617 }
618 }
619 } else if (resolveTexture) {
620 resolveTexture->deleteLater();
621 resolveTexture = nullptr;
622 }
623
624 if (colorTexture && colorTexture->pixelSize() != newSize) {
625 if (changed)
626 *changed = true;
627 colorTexture->setPixelSize(newSize);
628 if (!colorTexture->create())
629 qWarning("Failed to rebuild texture for QRhiWidget after resizing");
630 }
631
632 if (msaaColorBuffer && msaaColorBuffer->pixelSize() != newSize) {
633 if (changed)
634 *changed = true;
635 msaaColorBuffer->setPixelSize(newSize);
636 if (!msaaColorBuffer->create())
637 qWarning("Failed to rebuild multisample color buffer for QRhiWidget after resizing");
638 }
639
640 if (resolveTexture && resolveTexture->pixelSize() != newSize) {
641 if (changed)
642 *changed = true;
643 resolveTexture->setPixelSize(newSize);
644 if (!resolveTexture->create())
645 qWarning("Failed to rebuild resolve texture for QRhiWidget after resizing");
646 }
647
648 textureInvalid = false;
649}
650
651bool QRhiWidgetPrivate::invokeInitialize(QRhiCommandBuffer *cb)
652{
653 Q_Q(QRhiWidget);
654 if (!colorTexture && !msaaColorBuffer)
655 return false;
656
657 if (autoRenderTarget) {
658 const QSize pixelSize = colorTexture ? colorTexture->pixelSize() : msaaColorBuffer->pixelSize();
659 if (!depthStencilBuffer) {
660 depthStencilBuffer = rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, pixelSize, samples);
661 if (!depthStencilBuffer->create()) {
662 qWarning("Failed to create depth-stencil buffer for QRhiWidget");
663 resetRenderTargetObjects();
664 return false;
665 }
666 } else if (depthStencilBuffer->pixelSize() != pixelSize) {
667 depthStencilBuffer->setPixelSize(pixelSize);
668 if (!depthStencilBuffer->create()) {
669 qWarning("Failed to rebuild depth-stencil buffer for QRhiWidget with new size");
670 return false;
671 }
672 }
673
674 if (!renderTarget) {
675 QRhiColorAttachment color0;
676 if (colorTexture)
677 color0.setTexture(colorTexture);
678 else
679 color0.setRenderBuffer(msaaColorBuffer);
680 if (samples > 1)
681 color0.setResolveTexture(resolveTexture);
682 QRhiTextureRenderTargetDescription rtDesc(color0, depthStencilBuffer);
683 renderTarget = rhi->newTextureRenderTarget(rtDesc);
684 renderPassDescriptor = renderTarget->newCompatibleRenderPassDescriptor();
685 renderTarget->setRenderPassDescriptor(renderPassDescriptor);
686 if (!renderTarget->create()) {
687 qWarning("Failed to create render target for QRhiWidget");
688 resetRenderTargetObjects();
689 return false;
690 }
691 }
692 } else {
693 resetRenderTargetObjects();
694 }
695
696 q->initialize(cb);
697
698 return true;
699}
700
701/*!
702 \return the currently set graphics API (QRhi backend).
703
704 \sa setApi()
705 */
706QRhiWidget::Api QRhiWidget::api() const
707{
708 Q_D(const QRhiWidget);
709 switch (d->config.api()) {
710 case QPlatformBackingStoreRhiConfig::OpenGL:
711 return Api::OpenGL;
712 case QPlatformBackingStoreRhiConfig::Metal:
713 return Api::Metal;
714 case QPlatformBackingStoreRhiConfig::Vulkan:
715 return Api::Vulkan;
716 case QPlatformBackingStoreRhiConfig::D3D11:
717 return Api::Direct3D11;
718 case QPlatformBackingStoreRhiConfig::D3D12:
719 return Api::Direct3D12;
720 case QPlatformBackingStoreRhiConfig::Null:
721 return Api::Null;
722 }
723 Q_UNREACHABLE_RETURN(Api::Null);
724}
725
726/*!
727 Sets the graphics API and QRhi backend to use to \a api.
728
729 \warning This function must be called early enough, before the widget is
730 added to a widget hierarchy and displayed on screen. For example, aim to
731 call the function for the subclass constructor. If called too late, the
732 function will have no effect.
733
734 The default value depends on the platform: Metal on macOS and iOS, Direct
735 3D 11 on Windows, OpenGL otherwise.
736
737 The \a api can only be set once for the widget and its top-level window,
738 once it is done and takes effect, the window can only use that API and QRhi
739 backend to render. Attempting to set another value, or to add another
740 QRhiWidget with a different \a api will not function as expected.
741
742 \sa setColorBufferFormat(), setDebugLayerEnabled(), api()
743 */
744void QRhiWidget::setApi(Api api)
745{
746 Q_D(QRhiWidget);
747 switch (api) {
748 case Api::OpenGL:
749 d->config.setApi(QPlatformBackingStoreRhiConfig::OpenGL);
750 break;
751 case Api::Metal:
752 d->config.setApi(QPlatformBackingStoreRhiConfig::Metal);
753 break;
754 case Api::Vulkan:
755 d->config.setApi(QPlatformBackingStoreRhiConfig::Vulkan);
756 break;
757 case Api::Direct3D11:
758 d->config.setApi(QPlatformBackingStoreRhiConfig::D3D11);
759 break;
760 case Api::Direct3D12:
761 d->config.setApi(QPlatformBackingStoreRhiConfig::D3D12);
762 break;
763 case Api::Null:
764 d->config.setApi(QPlatformBackingStoreRhiConfig::Null);
765 break;
766 }
767}
768
769/*!
770 \return true if a debug or validation layer will be requested if applicable
771 to the graphics API in use.
772
773 \sa setDebugLayerEnabled()
774 */
775bool QRhiWidget::isDebugLayerEnabled() const
776{
777 Q_D(const QRhiWidget);
778 return d->config.isDebugLayerEnabled();
779}
780
781/*!
782 Requests the debug or validation layer of the underlying graphics API
783 when \a enable is true.
784
785 \warning This function must be called early enough, before the widget is added
786 to a widget hierarchy and displayed on screen. For example, aim to call the
787 function for the subclass constructor. If called too late, the function
788 will have no effect.
789
790 Applicable for Vulkan and Direct 3D.
791
792 By default this is disabled.
793
794 \sa setApi(), isDebugLayerEnabled()
795 */
796void QRhiWidget::setDebugLayerEnabled(bool enable)
797{
798 Q_D(QRhiWidget);
799 d->config.setDebugLayer(enable);
800}
801
802/*!
803 \property QRhiWidget::colorBufferFormat
804
805 This property controls the texture format of the texture (or renderbuffer)
806 used as the color buffer. The default value is TextureFormat::RGBA8.
807 QRhiWidget supports rendering to a subset of the formats supported by \l
808 QRhiTexture. Only formats that are reported as supported from \l
809 QRhi::isTextureFormatSupported() should be specified, rendering will not be
810 functional otherwise.
811
812 \note Setting a new format when the widget is already initialized and has
813 rendered implies that all QRhiGraphicsPipeline objects created by the
814 renderer may become unusable, if the associated QRhiRenderPassDescriptor is
815 now incompatible due to the different texture format. Similarly to changing
816 \l sampleCount dynamically, this means that initialize() or render()
817 implementations must then take care of releasing the existing pipelines and
818 creating new ones.
819 */
820
821QRhiWidget::TextureFormat QRhiWidget::colorBufferFormat() const
822{
823 Q_D(const QRhiWidget);
824 return d->widgetTextureFormat;
825}
826
827void QRhiWidget::setColorBufferFormat(TextureFormat format)
828{
829 Q_D(QRhiWidget);
830 if (d->widgetTextureFormat != format) {
831 d->widgetTextureFormat = format;
832 switch (format) {
833 case TextureFormat::RGBA8:
834 d->rhiTextureFormat = QRhiTexture::RGBA8;
835 break;
836 case TextureFormat::RGBA16F:
837 d->rhiTextureFormat = QRhiTexture::RGBA16F;
838 break;
839 case TextureFormat::RGBA32F:
840 d->rhiTextureFormat = QRhiTexture::RGBA32F;
841 break;
842 case TextureFormat::RGB10A2:
843 d->rhiTextureFormat = QRhiTexture::RGB10A2;
844 break;
845 }
846 emit colorBufferFormatChanged(format);
847 update();
848 }
849}
850
851/*!
852 \property QRhiWidget::sampleCount
853
854 This property controls for sample count for multisample antialiasing.
855 By default the value is \c 1 which means MSAA is disabled.
856
857 Valid values are 1, 4, 8, and sometimes 16 and 32.
858 \l QRhi::supportedSampleCounts() can be used to query the supported sample
859 counts at run time, but typically applications should request 1 (no MSAA),
860 4x (normal MSAA) or 8x (high MSAA).
861
862 \note Setting a new value implies that all QRhiGraphicsPipeline objects
863 created by the renderer must use the same sample count from then on.
864 Existing QRhiGraphicsPipeline objects created with a different sample count
865 must not be used anymore. When the value changes, all color and
866 depth-stencil buffers are destroyed and recreated automatically, and
867 initialize() is invoked again. However, when
868 \l autoRenderTarget is \c false, it will be up to the application to
869 manage this with regards to the depth-stencil buffer or additional color
870 buffers.
871
872 Changing the sample count from the default 1 to a higher value implies that
873 colorTexture() becomes \nullptr and msaaColorBuffer() starts returning a
874 valid object. Switching back to 1 (or 0), implies the opposite: in the next
875 call to initialize() msaaColorBuffer() is going to return \nullptr, whereas
876 colorTexture() becomes once again valid. In addition, resolveTexture()
877 returns a valid (non-multisample) QRhiTexture whenever the sample count is
878 greater than 1 (i.e., MSAA is in use).
879
880 \sa msaaColorBuffer(), resolveTexture()
881 */
882
883int QRhiWidget::sampleCount() const
884{
885 Q_D(const QRhiWidget);
886 return d->samples;
887}
888
889void QRhiWidget::setSampleCount(int samples)
890{
891 Q_D(QRhiWidget);
892 if (d->samples != samples) {
893 d->samples = samples;
894 emit sampleCountChanged(samples);
895 update();
896 }
897}
898
899/*!
900 \property QRhiWidget::fixedColorBufferSize
901
902 The fixed size, in pixels, of the QRhiWidget's associated texture. Relevant
903 when a fixed texture size is desired that does not depend on the widget's
904 size. This size has no effect on the geometry of the widget (its size and
905 placement within the top-level window), which means the texture's content
906 will appear stretched (scaled up) or scaled down onto the widget's area.
907
908 For example, setting a size that is exactly twice the widget's (pixel) size
909 effectively performs 2x supersampling (rendering at twice the resolution and
910 then implicitly scaling down when texturing the quad corresponding to the
911 widget in the window). On the other hand, setting a size that is half of the
912 widget's effectively achieves rendering at half resolution and then
913 upscaling the results.
914
915 By default the value is a null QSize. A null or empty QSize means that the
916 texture's size follows the QRhiWidget's size. (\c{texture size} = \c{widget
917 size} * \c{device pixel ratio}).
918
919 \note The device pixel ratio (the system compositor's scale factor) can have
920 a big impact on performance, since a scale factor of 2 (200%) means
921 rendering at twice the resolution, so twice of what the developer and UI
922 designer perceives as the widget's size, and then effectively downscaling
923 the content, similarly to what happens when setting this property to twice
924 the widget's pixel size on a system where the device pixel ratio is 1.
925 Therefore, this property is expected to be rarely used with sizes bigger
926 than the widget's pixel size, since many modern desktop systems have neither
927 have the need nor the performance budget for it, when a larger than 1 device
928 pixel ratio is used anyway by the system. Instead, the main use case for
929 this property is to set a smaller size, in order to render at a reasonable
930 smaller resolution instead of blindly following the window geometry, however
931 big that may be.
932 */
933
934QSize QRhiWidget::fixedColorBufferSize() const
935{
936 Q_D(const QRhiWidget);
937 return d->fixedSize;
938}
939
940void QRhiWidget::setFixedColorBufferSize(QSize pixelSize)
941{
942 Q_D(QRhiWidget);
943 if (d->fixedSize != pixelSize) {
944 d->fixedSize = pixelSize;
945 emit fixedColorBufferSizeChanged(pixelSize);
946 update();
947 }
948}
949
950/*!
951 \property QRhiWidget::mirrorVertically
952
953 When enabled, flips the image around the X axis when compositing the
954 QRhiWidget's backing texture with the rest of the widget content in the
955 top-level window.
956
957 The default value is \c false.
958 */
959
960bool QRhiWidget::isMirrorVerticallyEnabled() const
961{
962 Q_D(const QRhiWidget);
963 return d->mirrorVertically;
964}
965
966void QRhiWidget::setMirrorVertically(bool enabled)
967{
968 Q_D(QRhiWidget);
969 if (d->mirrorVertically != enabled) {
970 d->mirrorVertically = enabled;
971 emit mirrorVerticallyChanged(enabled);
972 update();
973 }
974}
975
976/*!
977 \property QRhiWidget::autoRenderTarget
978
979 The current setting for automatic depth-stencil buffer and render
980 target maintenance.
981
982 By default the value is \c true.
983 */
984bool QRhiWidget::isAutoRenderTargetEnabled() const
985{
986 Q_D(const QRhiWidget);
987 return d->autoRenderTarget;
988}
989
990/*!
991 Controls if a depth-stencil QRhiRenderBuffer and a QRhiTextureRenderTarget
992 is created and maintained automatically by the widget. The default value is
993 \c true.
994
995 In automatic mode, the size and sample count of the depth-stencil buffer
996 follows the color buffer texture's settings. In non-automatic mode,
997 renderTarget() and depthStencilBuffer() always return \nullptr and it is
998 then up to the application's implementation of initialize() to take care of
999 setting up and managing these objects.
1000
1001 Call this function with \a enabled set to \c false early on, for example in
1002 the derived class' constructor, to disable the automatic mode.
1003 */
1004void QRhiWidget::setAutoRenderTarget(bool enabled)
1005{
1006 Q_D(QRhiWidget);
1007 if (d->autoRenderTarget != enabled) {
1008 d->autoRenderTarget = enabled;
1009 update();
1010 }
1011}
1012
1013/*!
1014 Renders a new frame, reads the contents of the texture back, and returns it
1015 as a QImage.
1016
1017 When an error occurs, a null QImage is returned.
1018
1019 The returned QImage will have a format of QImage::Format_RGBA8888,
1020 QImage::Format_RGBA16FPx4, QImage::Format_RGBA32FPx4, or
1021 QImage::Format_BGR30, depending on colorBufferFormat().
1022
1023 QRhiWidget does not know the renderer's approach to blending and
1024 composition, and therefore cannot know if the output has alpha
1025 premultiplied in the RGB color values. Thus \c{_Premultiplied} QImage
1026 formats are never used for the returned QImage, even when it would be
1027 appropriate. It is up to the caller to reinterpret the resulting data as it
1028 sees fit.
1029
1030 \note This function can also be called when the QRhiWidget is not added to
1031 a widget hierarchy belonging to an on-screen top-level window. This allows
1032 generating an image from a 3D rendering off-screen.
1033
1034 The function is named grabFramebuffer() for consistency with QOpenGLWidget
1035 and QQuickWidget. It is not the only way to get CPU-side image data out of
1036 the QRhiWidget's content: calling \l QWidget::grab() on a QRhiWidget, or an
1037 ancestor of it, is functional as well (returning a QPixmap). Besides
1038 working directly with QImage, another advantage of grabFramebuffer() is
1039 that it may be slightly more performant, simply because it does not have to
1040 go through the rest of QWidget infrastructure but can right away trigger
1041 rendering a new frame and then do the readback.
1042
1043 \sa setColorBufferFormat()
1044 */
1045QImage QRhiWidget::grabFramebuffer() const
1046{
1047 return const_cast<QRhiWidgetPrivate *>(d_func())->grabFramebuffer();
1048}
1049
1050/*!
1051 Called when the widget is initialized for the first time, when the
1052 associated texture's size, format, or sample count changes, or when the
1053 QRhi and texture change for any reason. The function is expected to
1054 maintain (create if not yet created, adjust and rebuild if the size has
1055 changed) the graphics resources used by the rendering code in render().
1056
1057 To query the QRhi, QRhiTexture, and other related objects, call rhi(),
1058 colorTexture(), depthStencilBuffer(), and renderTarget().
1059
1060 When the widget size changes, the QRhi object, the color buffer texture,
1061 and the depth stencil buffer objects are all the same instances (so the
1062 getters return the same pointers) as before, but the color and
1063 depth/stencil buffers will likely have been rebuilt, meaning the
1064 \l{QRhiTexture::pixelSize()}{size} and the underlying native texture
1065 resource may be different than in the last invocation.
1066
1067 Reimplementations should also be prepared that the QRhi object and the
1068 color buffer texture may change between invocations of this function. One
1069 special case where the objects will be different is when performing a
1070 grabFramebuffer() with a widget that is not yet shown, and then making the
1071 widget visible on-screen within a top-level widget. There the grab will
1072 happen with a dedicated QRhi that is then replaced with the top-level
1073 window's associated QRhi in subsequent initialize() and render()
1074 invocations. Another, more common case is when the widget is reparented so
1075 that it belongs to a new top-level window. In this case the QRhi and all
1076 related resources managed by the QRhiWidget will be different instances
1077 than before in the subsequent call to this function. Is is then important
1078 that all existing QRhi resources previously created by the subclass are
1079 destroyed because they belong to the previous QRhi that should not be used
1080 by the widget anymore.
1081
1082 When \l autoRenderTarget is \c true, which is the default, a
1083 depth-stencil QRhiRenderBuffer and a QRhiTextureRenderTarget associated
1084 with colorTexture() (or msaaColorBuffer()) and the depth-stencil buffer are
1085 created and managed automatically. Reimplementations of initialize() and
1086 render() can query those objects via depthStencilBuffer() and
1087 renderTarget(). When \l autoRenderTarget is set to \c false, these
1088 objects are no longer created and managed automatically. Rather, it will be
1089 up the the initialize() implementation to create buffers and set up the
1090 render target as it sees fit. When manually managing additional color or
1091 depth-stencil attachments for the render target, their size and sample
1092 count must always follow the size and sample count of colorTexture() /
1093 msaaColorBuffer(), otherwise rendering or 3D API validation errors may
1094 occur.
1095
1096 The subclass-created graphics resources are expected to be released in the
1097 destructor implementation of the subclass.
1098
1099 \a cb is the QRhiCommandBuffer for the current frame of the widget. The
1100 function is called with a frame being recorded, but without an active
1101 render pass. The command buffer is provided primarily to allow enqueuing
1102 \l{QRhiCommandBuffer::resourceUpdate()}{resource updates} without deferring
1103 to render().
1104
1105 \sa render()
1106 */
1107void QRhiWidget::initialize(QRhiCommandBuffer *cb)
1108{
1109 Q_UNUSED(cb);
1110}
1111
1112/*!
1113 Called when the widget contents (i.e. the contents of the texture) need
1114 updating.
1115
1116 There is always at least one call to initialize() before this function is
1117 called.
1118
1119 To request updates, call QWidget::update(). Calling update() from within
1120 render() will lead to updating continuously, throttled by vsync.
1121
1122 \a cb is the QRhiCommandBuffer for the current frame of the widget. The
1123 function is called with a frame being recorded, but without an active
1124 render pass.
1125
1126 \sa initialize()
1127 */
1128void QRhiWidget::render(QRhiCommandBuffer *cb)
1129{
1130 Q_UNUSED(cb);
1131}
1132
1133/*!
1134 Called when the need to early-release the graphics resources arises.
1135
1136 This normally does not happen for a QRhiWidget that is added to a top-level
1137 widget's child hierarchy and it then stays there for the rest of its and
1138 the top-level's lifetime. Thus in many cases there is no need to
1139 reimplement this function, e.g. because the application only ever has a
1140 single top-level widget (native window). However, when reparenting of the
1141 widget (or an ancestor of it) is involved, reimplementing this function
1142 will become necessary in robust, well-written QRhiWidget subclasses.
1143
1144 When this function is called, the implementation is expected to destroy all
1145 QRhi resources (QRhiBuffer, QRhiTexture, etc. objects), similarly to how it
1146 is expected to do this in the destructor. Nulling out, using a smart
1147 pointer, or setting a \c{resources-invalid} flag is going to be required as
1148 well, because initialize() will eventually get called afterwards. Note
1149 however that deferring the releasing of resources to the subsequent
1150 initialize() is wrong. If this function is called, the resource must be
1151 dropped before returning. Also note that implementing this function does
1152 not replace the class destructor (or smart pointers): the graphics
1153 resources must still be released in both.
1154
1155 See the \l{Cube RHI Widget Example} for an example of this in action. There
1156 the button that toggles the QRhiWidget between being a child widget (due to
1157 having a parent widget) and being a top-level widget (due to having no
1158 parent widget), will trigger invoking this function since the associated
1159 top-level widget, native window, and QRhi all change during the lifetime of
1160 the QRhiWidget, with the previously used QRhi getting destroyed which
1161 implies an early-release of the associated resources managed by the
1162 still-alive QRhiWidget.
1163
1164 Another case when this function is called is when grabFramebuffer() is used
1165 with a QRhiWidget that is not added to a visible window, i.e. the rendering
1166 is performed offscreen. If later on this QRhiWidget is made visible, or
1167 added to a visible widget hierarchy, the associated QRhi will change from
1168 the temporary one used for offscreen rendering to the window's dedicated
1169 one, thus triggering this function as well.
1170
1171 \sa initialize()
1172 */
1173void QRhiWidget::releaseResources()
1174{
1175}
1176
1177/*!
1178 \return the current QRhi object.
1179
1180 Must only be called from initialize() and render().
1181 */
1182QRhi *QRhiWidget::rhi() const
1183{
1184 Q_D(const QRhiWidget);
1185 return d->rhi;
1186}
1187
1188/*!
1189 \return the texture serving as the color buffer for the widget.
1190
1191 Must only be called from initialize() and render().
1192
1193 Unlike the depth-stencil buffer and the QRhiRenderTarget, this texture is
1194 always available and is managed by the QRhiWidget, independent of the value
1195 of \l autoRenderTarget.
1196
1197 \note When \l sampleCount is larger than 1, and so multisample antialiasing
1198 is enabled, the return value is \nullptr. Instead, query the
1199 \l QRhiRenderBuffer by calling msaaColorBuffer().
1200
1201 \note The backing texture size and sample count can also be queried via the
1202 QRhiRenderTarget returned from renderTarget(). This can be more convenient
1203 and compact than querying from the QRhiTexture or QRhiRenderBuffer, because
1204 it works regardless of multisampling is in use or not.
1205
1206 \sa msaaColorBuffer(), depthStencilBuffer(), renderTarget(), resolveTexture()
1207 */
1208QRhiTexture *QRhiWidget::colorTexture() const
1209{
1210 Q_D(const QRhiWidget);
1211 return d->colorTexture;
1212}
1213
1214/*!
1215 \return the renderbuffer serving as the multisample color buffer for the widget.
1216
1217 Must only be called from initialize() and render().
1218
1219 When \l sampleCount is larger than 1, and so multisample antialising is
1220 enabled, the returned QRhiRenderBuffer has a matching sample count and
1221 serves as the color buffer. Graphics pipelines used to render into this
1222 buffer must be created with the same sample count, and the depth-stencil
1223 buffer's sample count must match as well. The multisample content is
1224 expected to be resolved into the texture returned from resolveTexture().
1225 When \l autoRenderTarget is
1226 \c true, renderTarget() is set up automatically to do this, by setting up
1227 msaaColorBuffer() as the \l{QRhiColorAttachment::renderBuffer()}{renderbuffer} of
1228 color attachment 0 and resolveTexture() as its
1229 \l{QRhiColorAttachment::resolveTexture()}{resolveTexture}.
1230
1231 When MSAA is not in use, the return value is \nullptr. Use colorTexture()
1232 instead then.
1233
1234 Depending on the underlying 3D graphics API, there may be no practical
1235 difference between multisample textures and color renderbuffers with a
1236 sample count larger than 1 (QRhi may just map both to the same native
1237 resource type). Some older APIs however may differentiate between textures
1238 and renderbuffers. In order to support OpenGL ES 3.0, where multisample
1239 renderbuffers are available, but multisample textures are not, QRhiWidget
1240 always performs MSAA by using a multisample QRhiRenderBuffer as the color
1241 attachment (and never a multisample QRhiTexture).
1242
1243 \note The backing texture size and sample count can also be queried via the
1244 QRhiRenderTarget returned from renderTarget(). This can be more convenient
1245 and compact than querying from the QRhiTexture or QRhiRenderBuffer, because
1246 it works regardless of multisampling is in use or not.
1247
1248 \sa colorTexture(), depthStencilBuffer(), renderTarget(), resolveTexture()
1249 */
1250QRhiRenderBuffer *QRhiWidget::msaaColorBuffer() const
1251{
1252 Q_D(const QRhiWidget);
1253 return d->msaaColorBuffer;
1254}
1255
1256/*!
1257 \return the non-multisample texture to which the multisample content is resolved.
1258
1259 The result is \nullptr when multisample antialiasing is not enabled.
1260
1261 Must only be called from initialize() and render().
1262
1263 With MSAA enabled, this is the texture that gets composited with the rest
1264 of the QWidget content on-screen. However, the QRhiWidget's rendering must
1265 target the (multisample) QRhiRenderBuffer returned from
1266 msaaColorBuffer(). When
1267 \l autoRenderTarget is \c true, this is taken care of by the
1268 QRhiRenderTarget returned from renderTarget(). Otherwise, it is up to the
1269 subclass code to correctly configure a render target object with both the
1270 color buffer and resolve textures.
1271
1272 \sa colorTexture()
1273 */
1274QRhiTexture *QRhiWidget::resolveTexture() const
1275{
1276 Q_D(const QRhiWidget);
1277 return d->resolveTexture;
1278}
1279
1280/*!
1281 \return the depth-stencil buffer used by the widget's rendering.
1282
1283 Must only be called from initialize() and render().
1284
1285 Available only when \l autoRenderTarget is \c true. Otherwise the
1286 returned value is \nullptr and it is up the reimplementation of
1287 initialize() to create and manage a depth-stencil buffer and a
1288 QRhiTextureRenderTarget.
1289
1290 \sa colorTexture(), renderTarget()
1291 */
1292QRhiRenderBuffer *QRhiWidget::depthStencilBuffer() const
1293{
1294 Q_D(const QRhiWidget);
1295 return d->depthStencilBuffer;
1296}
1297
1298/*!
1299 \return the render target object that must be used with
1300 \l QRhiCommandBuffer::beginPass() in reimplementations of render().
1301
1302 Must only be called from initialize() and render().
1303
1304 Available only when \l autoRenderTarget is \c true. Otherwise the
1305 returned value is \nullptr and it is up the reimplementation of
1306 initialize() to create and manage a depth-stencil buffer and a
1307 QRhiTextureRenderTarget.
1308
1309 When creating \l{QRhiGraphicsPipeline}{graphics pipelines}, a
1310 QRhiRenderPassDescriptor is needed. This can be queried from the returned
1311 QRhiTextureRenderTarget by calling
1312 \l{QRhiTextureRenderTarget::renderPassDescriptor()}{renderPassDescriptor()}.
1313
1314 \sa colorTexture(), depthStencilBuffer()
1315 */
1316QRhiRenderTarget *QRhiWidget::renderTarget() const
1317{
1318 Q_D(const QRhiWidget);
1319 return d->renderTarget;
1320}
1321
1322/*!
1323 \fn void QRhiWidget::frameSubmitted()
1324
1325 This signal is emitted after the widget's top-level window has finished
1326 composition and has \l{QRhi::endFrame()}{submitted a frame}.
1327*/
1328
1329/*!
1330 \fn void QRhiWidget::renderFailed()
1331
1332 This signal is emitted whenever the widget is supposed to render to its
1333 backing texture (either due to a \l{QWidget::update()}{widget update} or
1334 due to a call to grabFramebuffer()), but there is no \l QRhi for the widget to
1335 use, likely due to issues related to graphics configuration.
1336
1337 This signal may be emitted multiple times when a problem arises. Do not
1338 assume it is emitted only once. Connect with Qt::SingleShotConnection if
1339 the error handling code is to be notified only once.
1340*/
1341
1342QT_END_NAMESPACE
Combined button and popup list for selecting options.