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 Shader Tools Build System Integration}{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 // must _not_ null out d->rhi here, for proper interaction with ensureRhi()
308 }
309
310 break;
311
312 case QEvent::Show:
313 if (isVisible())
314 d->sendPaintEvent(QRect(QPoint(0, 0), size()));
315 break;
316 default:
317 break;
318 }
319 return QWidget::event(e);
320}
321
322QWidgetPrivate::TextureData QRhiWidgetPrivate::texture() const
323{
324 // This is the only safe place to clear pendingDeletes, due to the
325 // possibility of the texture returned in the previous invocation of this
326 // function having been added to pendingDeletes, meaning the object then
327 // needs to be valid until the next (this) invocation of this function.
328 // (the exact object lifetime requirements depend on the
329 // QWidget/RepaintManager internal implementation; for now avoid relying on
330 // such details by clearing pendingDeletes only here, not in endCompose())
331 qDeleteAll(pendingDeletes);
332 pendingDeletes.clear();
333
334 TextureData td;
335 if (!textureInvalid)
336 td.textureLeft = resolveTexture ? resolveTexture : colorTexture;
337 return td;
338}
339
340QPlatformTextureList::Flags QRhiWidgetPrivate::textureListFlags()
341{
342 QPlatformTextureList::Flags flags = QWidgetPrivate::textureListFlags();
343 if (mirrorVertically)
344 flags |= QPlatformTextureList::MirrorVertically;
345 return flags;
346}
347
348QPlatformBackingStoreRhiConfig QRhiWidgetPrivate::rhiConfig() const
349{
350 return config;
351}
352
353void QRhiWidgetPrivate::endCompose()
354{
355 // This function is called by QWidgetRepaintManager right after the
356 // backingstore's QRhi-based flush returns. In practice that means after
357 // the begin-endFrame() on the top-level window's swapchain.
358
359 if (rhi) {
360 Q_Q(QRhiWidget);
361 emit q->frameSubmitted();
362 }
363}
364
365// This is reimplemented to enable calling QWidget::grab() on the widget or an
366// ancestor of it. At the same time, QRhiWidget provides its own
367// grabFramebuffer() as well, mirroring QQuickWidget and QOpenGLWidget for
368// consistency. In both types of grabs we end up in here.
369QImage QRhiWidgetPrivate::grabFramebuffer()
370{
371 Q_Q(QRhiWidget);
372 if (noSize)
373 return QImage();
374
375 ensureRhi();
376 if (!rhi) {
377 // The widget (and its parent chain, if any) may not be shown at
378 // all, yet one may still want to use it for grabs. This is
379 // ridiculous of course because the rendering infrastructure is
380 // tied to the top-level widget that initializes upon expose, but
381 // it has to be supported.
382 offscreenRenderer.setConfig(config);
383 // no window passed in, so no swapchain, but we get a functional QRhi which we own
384 offscreenRenderer.create();
385 rhi = offscreenRenderer.rhi();
386 if (!rhi) {
387 qWarning("QRhiWidget: Failed to create dedicated QRhi for grabbing");
388 emit q->renderFailed();
389 return QImage();
390 }
391 }
392
393 QRhiCommandBuffer *cb = nullptr;
394 if (rhi->beginOffscreenFrame(&cb) != QRhi::FrameOpSuccess)
395 return QImage();
396
397 QRhiReadbackResult readResult;
398 bool readCompleted = false;
399 bool needsInit = false;
400 ensureTexture(&needsInit);
401
402 if (colorTexture || msaaColorBuffer) {
403 bool canRender = true;
404 if (needsInit)
405 canRender = invokeInitialize(cb);
406 if (canRender)
407 q->render(cb);
408
409 QRhiResourceUpdateBatch *readbackBatch = rhi->nextResourceUpdateBatch();
410 readResult.completed = [&readCompleted] { readCompleted = true; };
411 readbackBatch->readBackTexture(resolveTexture ? resolveTexture : colorTexture, &readResult);
412 cb->resourceUpdate(readbackBatch);
413 }
414
415 rhi->endOffscreenFrame();
416
417 if (readCompleted) {
418 QImage::Format imageFormat = QImage::Format_RGBA8888;
419 switch (widgetTextureFormat) {
420 case QRhiWidget::TextureFormat::RGBA8:
421 break;
422 case QRhiWidget::TextureFormat::RGBA16F:
423 imageFormat = QImage::Format_RGBA16FPx4;
424 break;
425 case QRhiWidget::TextureFormat::RGBA32F:
426 imageFormat = QImage::Format_RGBA32FPx4;
427 break;
428 case QRhiWidget::TextureFormat::RGB10A2:
429 imageFormat = QImage::Format_BGR30;
430 break;
431 }
432 QImage wrapperImage(reinterpret_cast<const uchar *>(readResult.data.constData()),
433 readResult.pixelSize.width(), readResult.pixelSize.height(),
434 imageFormat);
435 QImage result;
436 if (rhi->isYUpInFramebuffer())
437 result = wrapperImage.flipped();
438 else
439 result = wrapperImage.copy();
440 result.setDevicePixelRatio(q->devicePixelRatio());
441 return result;
442 } else {
443 Q_UNREACHABLE();
444 }
445
446 return QImage();
447}
448
449void QRhiWidgetPrivate::resetColorBufferObjects()
450{
451 if (colorTexture) {
452 pendingDeletes.append(colorTexture);
453 colorTexture = nullptr;
454 }
455 if (msaaColorBuffer) {
456 pendingDeletes.append(msaaColorBuffer);
457 msaaColorBuffer = nullptr;
458 }
459 if (resolveTexture) {
460 pendingDeletes.append(resolveTexture);
461 resolveTexture = nullptr;
462 }
463}
464
465void QRhiWidgetPrivate::resetRenderTargetObjects()
466{
467 if (renderTarget) {
468 renderTarget->deleteLater();
469 renderTarget = nullptr;
470 }
471 if (renderPassDescriptor) {
472 renderPassDescriptor->deleteLater();
473 renderPassDescriptor = nullptr;
474 }
475 if (depthStencilBuffer) {
476 depthStencilBuffer->deleteLater();
477 depthStencilBuffer = nullptr;
478 }
479}
480
481void QRhiWidgetPrivate::releaseResources()
482{
483 resetRenderTargetObjects();
484 resetColorBufferObjects();
485 qDeleteAll(pendingDeletes);
486 pendingDeletes.clear();
487}
488
489void QRhiWidgetPrivate::ensureRhi()
490{
491 Q_Q(QRhiWidget);
492 QRhi *currentRhi = QWidgetPrivate::rhi();
493 if (currentRhi && currentRhi->backend() != QBackingStoreRhiSupport::apiToRhiBackend(config.api())) {
494 qWarning("The top-level window is already using another graphics API for composition, "
495 "'%s' is not compatible with this widget",
496 currentRhi->backendName());
497 return;
498 }
499
500 // NB the rhi member may be an invalid object, the pointer can be used, but no deref
501 if (currentRhi && rhi != currentRhi) {
502 if (rhi) {
503 // if previously we created our own but now get a QRhi from the
504 // top-level, then drop what we have and start using the top-level's
505 if (rhi == offscreenRenderer.rhi()) {
506 q->releaseResources(); // notify the user code about the early-release
507 releaseResources();
508 offscreenRenderer.reset();
509 } else {
510 // rhi resources created by us all belong to the old rhi, drop them;
511 // due to nulling out colorTexture this is also what ensures that
512 // initialize() is going to be called again eventually
513 resetRenderTargetObjects();
514 resetColorBufferObjects();
515 }
516 }
517
518 // Normally the widget gets destroyed before the QRhi (which is managed by
519 // the top-level's backingstore). When reparenting between top-levels is
520 // involved, that is not always the case. Therefore we use a per-widget rhi
521 // cleanup callback to get notified when the QRhi is about to be destroyed
522 // while the QRhiWidget is still around.
523 currentRhi->addCleanupCallback(q, [q, this](QRhi *regRhi) {
524 if (!QWidgetPrivate::get(q)->data.in_destructor && this->rhi == regRhi) {
525 q->releaseResources(); // notify the user code about the early-release
526 releaseResources();
527 // must null out our ref, the QRhi object is going to be invalid
528 this->rhi = nullptr;
529 }
530 });
531 }
532
533 rhi = currentRhi;
534}
535
536void QRhiWidgetPrivate::ensureTexture(bool *changed)
537{
538 Q_Q(QRhiWidget);
539
540 QSize newSize = fixedSize;
541 if (newSize.isEmpty())
542 newSize = q->size() * q->devicePixelRatio();
543
544 const int minTexSize = rhi->resourceLimit(QRhi::TextureSizeMin);
545 const int maxTexSize = rhi->resourceLimit(QRhi::TextureSizeMax);
546 newSize.setWidth(qMin(maxTexSize, qMax(minTexSize, newSize.width())));
547 newSize.setHeight(qMin(maxTexSize, qMax(minTexSize, newSize.height())));
548
549 if (colorTexture) {
550 if (colorTexture->format() != rhiTextureFormat || colorTexture->sampleCount() != samples) {
551 resetColorBufferObjects();
552 // sample count change needs new depth-stencil, possibly a new
553 // render target; format change needs new renderpassdescriptor;
554 // therefore must drop the rest too
555 resetRenderTargetObjects();
556 }
557 }
558
559 if (msaaColorBuffer) {
560 if (msaaColorBuffer->backingFormat() != rhiTextureFormat || msaaColorBuffer->sampleCount() != samples) {
561 resetColorBufferObjects();
562 // sample count change needs new depth-stencil, possibly a new
563 // render target; format change needs new renderpassdescriptor;
564 // therefore must drop the rest too
565 resetRenderTargetObjects();
566 }
567 }
568
569 if (!colorTexture && samples <= 1) {
570 if (changed)
571 *changed = true;
572 if (!rhi->isTextureFormatSupported(rhiTextureFormat)) {
573 qWarning("QRhiWidget: The requested texture format (%d) is not supported by the "
574 "underlying 3D graphics API implementation", int(rhiTextureFormat));
575 }
576 colorTexture = rhi->newTexture(rhiTextureFormat, newSize, samples, QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource);
577 if (!colorTexture->create()) {
578 qWarning("Failed to create backing texture for QRhiWidget");
579 delete colorTexture;
580 colorTexture = nullptr;
581 return;
582 }
583 }
584
585 if (samples > 1) {
586 if (!msaaColorBuffer) {
587 if (changed)
588 *changed = true;
589 if (!rhi->isFeatureSupported(QRhi::MultisampleRenderBuffer)) {
590 qWarning("QRhiWidget: Multisample renderbuffers are reported as unsupported; "
591 "sample count %d will not work as expected", samples);
592 }
593 if (!rhi->isTextureFormatSupported(rhiTextureFormat)) {
594 qWarning("QRhiWidget: The requested texture format (%d) is not supported by the "
595 "underlying 3D graphics API implementation", int(rhiTextureFormat));
596 }
597 msaaColorBuffer = rhi->newRenderBuffer(QRhiRenderBuffer::Color, newSize, samples, {}, rhiTextureFormat);
598 if (!msaaColorBuffer->create()) {
599 qWarning("Failed to create multisample color buffer for QRhiWidget");
600 delete msaaColorBuffer;
601 msaaColorBuffer = nullptr;
602 return;
603 }
604 }
605 if (!resolveTexture) {
606 if (changed)
607 *changed = true;
608 resolveTexture = rhi->newTexture(rhiTextureFormat, newSize, 1, QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource);
609 if (!resolveTexture->create()) {
610 qWarning("Failed to create resolve texture for QRhiWidget");
611 delete resolveTexture;
612 resolveTexture = nullptr;
613 return;
614 }
615 }
616 } else if (resolveTexture) {
617 resolveTexture->deleteLater();
618 resolveTexture = nullptr;
619 }
620
621 if (colorTexture && colorTexture->pixelSize() != newSize) {
622 if (changed)
623 *changed = true;
624 colorTexture->setPixelSize(newSize);
625 if (!colorTexture->create())
626 qWarning("Failed to rebuild texture for QRhiWidget after resizing");
627 }
628
629 if (msaaColorBuffer && msaaColorBuffer->pixelSize() != newSize) {
630 if (changed)
631 *changed = true;
632 msaaColorBuffer->setPixelSize(newSize);
633 if (!msaaColorBuffer->create())
634 qWarning("Failed to rebuild multisample color buffer for QRhiWidget after resizing");
635 }
636
637 if (resolveTexture && resolveTexture->pixelSize() != newSize) {
638 if (changed)
639 *changed = true;
640 resolveTexture->setPixelSize(newSize);
641 if (!resolveTexture->create())
642 qWarning("Failed to rebuild resolve texture for QRhiWidget after resizing");
643 }
644
645 textureInvalid = false;
646}
647
648bool QRhiWidgetPrivate::invokeInitialize(QRhiCommandBuffer *cb)
649{
650 Q_Q(QRhiWidget);
651 if (!colorTexture && !msaaColorBuffer)
652 return false;
653
654 if (autoRenderTarget) {
655 const QSize pixelSize = colorTexture ? colorTexture->pixelSize() : msaaColorBuffer->pixelSize();
656 if (!depthStencilBuffer) {
657 depthStencilBuffer = rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, pixelSize, samples);
658 if (!depthStencilBuffer->create()) {
659 qWarning("Failed to create depth-stencil buffer for QRhiWidget");
660 resetRenderTargetObjects();
661 return false;
662 }
663 } else if (depthStencilBuffer->pixelSize() != pixelSize) {
664 depthStencilBuffer->setPixelSize(pixelSize);
665 if (!depthStencilBuffer->create()) {
666 qWarning("Failed to rebuild depth-stencil buffer for QRhiWidget with new size");
667 return false;
668 }
669 }
670
671 if (!renderTarget) {
672 QRhiColorAttachment color0;
673 if (colorTexture)
674 color0.setTexture(colorTexture);
675 else
676 color0.setRenderBuffer(msaaColorBuffer);
677 if (samples > 1)
678 color0.setResolveTexture(resolveTexture);
679 QRhiTextureRenderTargetDescription rtDesc(color0, depthStencilBuffer);
680 renderTarget = rhi->newTextureRenderTarget(rtDesc);
681 renderPassDescriptor = renderTarget->newCompatibleRenderPassDescriptor();
682 renderTarget->setRenderPassDescriptor(renderPassDescriptor);
683 if (!renderTarget->create()) {
684 qWarning("Failed to create render target for QRhiWidget");
685 resetRenderTargetObjects();
686 return false;
687 }
688 }
689 } else {
690 resetRenderTargetObjects();
691 }
692
693 q->initialize(cb);
694
695 return true;
696}
697
698/*!
699 \return the currently set graphics API (QRhi backend).
700
701 \sa setApi()
702 */
703QRhiWidget::Api QRhiWidget::api() const
704{
705 Q_D(const QRhiWidget);
706 switch (d->config.api()) {
707 case QPlatformBackingStoreRhiConfig::OpenGL:
708 return Api::OpenGL;
709 case QPlatformBackingStoreRhiConfig::Metal:
710 return Api::Metal;
711 case QPlatformBackingStoreRhiConfig::Vulkan:
712 return Api::Vulkan;
713 case QPlatformBackingStoreRhiConfig::D3D11:
714 return Api::Direct3D11;
715 case QPlatformBackingStoreRhiConfig::D3D12:
716 return Api::Direct3D12;
717 case QPlatformBackingStoreRhiConfig::Null:
718 return Api::Null;
719 }
720 Q_UNREACHABLE_RETURN(Api::Null);
721}
722
723/*!
724 Sets the graphics API and QRhi backend to use to \a api.
725
726 \warning This function must be called early enough, before the widget is
727 added to a widget hierarchy and displayed on screen. For example, aim to
728 call the function for the subclass constructor. If called too late, the
729 function will have no effect.
730
731 The default value depends on the platform: Metal on macOS and iOS, Direct
732 3D 11 on Windows, OpenGL otherwise.
733
734 The \a api can only be set once for the widget and its top-level window,
735 once it is done and takes effect, the window can only use that API and QRhi
736 backend to render. Attempting to set another value, or to add another
737 QRhiWidget with a different \a api will not function as expected.
738
739 \sa setColorBufferFormat(), setDebugLayerEnabled(), api()
740 */
741void QRhiWidget::setApi(Api api)
742{
743 Q_D(QRhiWidget);
744 switch (api) {
745 case Api::OpenGL:
746 d->config.setApi(QPlatformBackingStoreRhiConfig::OpenGL);
747 break;
748 case Api::Metal:
749 d->config.setApi(QPlatformBackingStoreRhiConfig::Metal);
750 break;
751 case Api::Vulkan:
752 d->config.setApi(QPlatformBackingStoreRhiConfig::Vulkan);
753 break;
754 case Api::Direct3D11:
755 d->config.setApi(QPlatformBackingStoreRhiConfig::D3D11);
756 break;
757 case Api::Direct3D12:
758 d->config.setApi(QPlatformBackingStoreRhiConfig::D3D12);
759 break;
760 case Api::Null:
761 d->config.setApi(QPlatformBackingStoreRhiConfig::Null);
762 break;
763 }
764}
765
766/*!
767 \return true if a debug or validation layer will be requested if applicable
768 to the graphics API in use.
769
770 \sa setDebugLayerEnabled()
771 */
772bool QRhiWidget::isDebugLayerEnabled() const
773{
774 Q_D(const QRhiWidget);
775 return d->config.isDebugLayerEnabled();
776}
777
778/*!
779 Requests the debug or validation layer of the underlying graphics API
780 when \a enable is true.
781
782 \warning This function must be called early enough, before the widget is added
783 to a widget hierarchy and displayed on screen. For example, aim to call the
784 function for the subclass constructor. If called too late, the function
785 will have no effect.
786
787 Applicable for Vulkan and Direct 3D.
788
789 By default this is disabled.
790
791 \sa setApi(), isDebugLayerEnabled()
792 */
793void QRhiWidget::setDebugLayerEnabled(bool enable)
794{
795 Q_D(QRhiWidget);
796 d->config.setDebugLayer(enable);
797}
798
799/*!
800 \property QRhiWidget::colorBufferFormat
801
802 This property controls the texture format of the texture (or renderbuffer)
803 used as the color buffer. The default value is TextureFormat::RGBA8.
804 QRhiWidget supports rendering to a subset of the formats supported by \l
805 QRhiTexture. Only formats that are reported as supported from \l
806 QRhi::isTextureFormatSupported() should be specified, rendering will not be
807 functional otherwise.
808
809 \note Setting a new format when the widget is already initialized and has
810 rendered implies that all QRhiGraphicsPipeline objects created by the
811 renderer may become unusable, if the associated QRhiRenderPassDescriptor is
812 now incompatible due to the different texture format. Similarly to changing
813 \l sampleCount dynamically, this means that initialize() or render()
814 implementations must then take care of releasing the existing pipelines and
815 creating new ones.
816 */
817
818QRhiWidget::TextureFormat QRhiWidget::colorBufferFormat() const
819{
820 Q_D(const QRhiWidget);
821 return d->widgetTextureFormat;
822}
823
824void QRhiWidget::setColorBufferFormat(TextureFormat format)
825{
826 Q_D(QRhiWidget);
827 if (d->widgetTextureFormat != format) {
828 d->widgetTextureFormat = format;
829 switch (format) {
830 case TextureFormat::RGBA8:
831 d->rhiTextureFormat = QRhiTexture::RGBA8;
832 break;
833 case TextureFormat::RGBA16F:
834 d->rhiTextureFormat = QRhiTexture::RGBA16F;
835 break;
836 case TextureFormat::RGBA32F:
837 d->rhiTextureFormat = QRhiTexture::RGBA32F;
838 break;
839 case TextureFormat::RGB10A2:
840 d->rhiTextureFormat = QRhiTexture::RGB10A2;
841 break;
842 }
843 emit colorBufferFormatChanged(format);
844 update();
845 }
846}
847
848/*!
849 \property QRhiWidget::sampleCount
850
851 This property controls for sample count for multisample antialiasing.
852 By default the value is \c 1 which means MSAA is disabled.
853
854 Valid values are 1, 4, 8, and sometimes 16 and 32.
855 \l QRhi::supportedSampleCounts() can be used to query the supported sample
856 counts at run time, but typically applications should request 1 (no MSAA),
857 4x (normal MSAA) or 8x (high MSAA).
858
859 \note Setting a new value implies that all QRhiGraphicsPipeline objects
860 created by the renderer must use the same sample count from then on.
861 Existing QRhiGraphicsPipeline objects created with a different sample count
862 must not be used anymore. When the value changes, all color and
863 depth-stencil buffers are destroyed and recreated automatically, and
864 initialize() is invoked again. However, when
865 \l autoRenderTarget is \c false, it will be up to the application to
866 manage this with regards to the depth-stencil buffer or additional color
867 buffers.
868
869 Changing the sample count from the default 1 to a higher value implies that
870 colorTexture() becomes \nullptr and msaaColorBuffer() starts returning a
871 valid object. Switching back to 1 (or 0), implies the opposite: in the next
872 call to initialize() msaaColorBuffer() is going to return \nullptr, whereas
873 colorTexture() becomes once again valid. In addition, resolveTexture()
874 returns a valid (non-multisample) QRhiTexture whenever the sample count is
875 greater than 1 (i.e., MSAA is in use).
876
877 \sa msaaColorBuffer(), resolveTexture()
878 */
879
880int QRhiWidget::sampleCount() const
881{
882 Q_D(const QRhiWidget);
883 return d->samples;
884}
885
886void QRhiWidget::setSampleCount(int samples)
887{
888 Q_D(QRhiWidget);
889 if (d->samples != samples) {
890 d->samples = samples;
891 emit sampleCountChanged(samples);
892 update();
893 }
894}
895
896/*!
897 \property QRhiWidget::fixedColorBufferSize
898
899 The fixed size, in pixels, of the QRhiWidget's associated texture. Relevant
900 when a fixed texture size is desired that does not depend on the widget's
901 size. This size has no effect on the geometry of the widget (its size and
902 placement within the top-level window), which means the texture's content
903 will appear stretched (scaled up) or scaled down onto the widget's area.
904
905 For example, setting a size that is exactly twice the widget's (pixel) size
906 effectively performs 2x supersampling (rendering at twice the resolution and
907 then implicitly scaling down when texturing the quad corresponding to the
908 widget in the window). On the other hand, setting a size that is half of the
909 widget's effectively achieves rendering at half resolution and then
910 upscaling the results.
911
912 By default the value is a null QSize. A null or empty QSize means that the
913 texture's size follows the QRhiWidget's size. (\c{texture size} = \c{widget
914 size} * \c{device pixel ratio}).
915
916 \note The device pixel ratio (the system compositor's scale factor) can have
917 a big impact on performance, since a scale factor of 2 (200%) means
918 rendering at twice the resolution, so twice of what the developer and UI
919 designer perceives as the widget's size, and then effectively downscaling
920 the content, similarly to what happens when setting this property to twice
921 the widget's pixel size on a system where the device pixel ratio is 1.
922 Therefore, this property is expected to be rarely used with sizes bigger
923 than the widget's pixel size, since many modern desktop systems have neither
924 have the need nor the performance budget for it, when a larger than 1 device
925 pixel ratio is used anyway by the system. Instead, the main use case for
926 this property is to set a smaller size, in order to render at a reasonable
927 smaller resolution instead of blindly following the window geometry, however
928 big that may be.
929 */
930
931QSize QRhiWidget::fixedColorBufferSize() const
932{
933 Q_D(const QRhiWidget);
934 return d->fixedSize;
935}
936
937void QRhiWidget::setFixedColorBufferSize(QSize pixelSize)
938{
939 Q_D(QRhiWidget);
940 if (d->fixedSize != pixelSize) {
941 d->fixedSize = pixelSize;
942 emit fixedColorBufferSizeChanged(pixelSize);
943 update();
944 }
945}
946
947/*!
948 \property QRhiWidget::mirrorVertically
949
950 When enabled, flips the image around the X axis when compositing the
951 QRhiWidget's backing texture with the rest of the widget content in the
952 top-level window.
953
954 The default value is \c false.
955 */
956
957bool QRhiWidget::isMirrorVerticallyEnabled() const
958{
959 Q_D(const QRhiWidget);
960 return d->mirrorVertically;
961}
962
963void QRhiWidget::setMirrorVertically(bool enabled)
964{
965 Q_D(QRhiWidget);
966 if (d->mirrorVertically != enabled) {
967 d->mirrorVertically = enabled;
968 emit mirrorVerticallyChanged(enabled);
969 update();
970 }
971}
972
973/*!
974 \property QRhiWidget::autoRenderTarget
975
976 The current setting for automatic depth-stencil buffer and render
977 target maintenance.
978
979 By default the value is \c true.
980 */
981bool QRhiWidget::isAutoRenderTargetEnabled() const
982{
983 Q_D(const QRhiWidget);
984 return d->autoRenderTarget;
985}
986
987/*!
988 Controls if a depth-stencil QRhiRenderBuffer and a QRhiTextureRenderTarget
989 is created and maintained automatically by the widget. The default value is
990 \c true.
991
992 In automatic mode, the size and sample count of the depth-stencil buffer
993 follows the color buffer texture's settings. In non-automatic mode,
994 renderTarget() and depthStencilBuffer() always return \nullptr and it is
995 then up to the application's implementation of initialize() to take care of
996 setting up and managing these objects.
997
998 Call this function with \a enabled set to \c false early on, for example in
999 the derived class' constructor, to disable the automatic mode.
1000 */
1001void QRhiWidget::setAutoRenderTarget(bool enabled)
1002{
1003 Q_D(QRhiWidget);
1004 if (d->autoRenderTarget != enabled) {
1005 d->autoRenderTarget = enabled;
1006 update();
1007 }
1008}
1009
1010/*!
1011 Renders a new frame, reads the contents of the texture back, and returns it
1012 as a QImage.
1013
1014 When an error occurs, a null QImage is returned.
1015
1016 The returned QImage will have a format of QImage::Format_RGBA8888,
1017 QImage::Format_RGBA16FPx4, QImage::Format_RGBA32FPx4, or
1018 QImage::Format_BGR30, depending on colorBufferFormat().
1019
1020 QRhiWidget does not know the renderer's approach to blending and
1021 composition, and therefore cannot know if the output has alpha
1022 premultiplied in the RGB color values. Thus \c{_Premultiplied} QImage
1023 formats are never used for the returned QImage, even when it would be
1024 appropriate. It is up to the caller to reinterpret the resulting data as it
1025 sees fit.
1026
1027 \note This function can also be called when the QRhiWidget is not added to
1028 a widget hierarchy belonging to an on-screen top-level window. This allows
1029 generating an image from a 3D rendering off-screen.
1030
1031 The function is named grabFramebuffer() for consistency with QOpenGLWidget
1032 and QQuickWidget. It is not the only way to get CPU-side image data out of
1033 the QRhiWidget's content: calling \l QWidget::grab() on a QRhiWidget, or an
1034 ancestor of it, is functional as well (returning a QPixmap). Besides
1035 working directly with QImage, another advantage of grabFramebuffer() is
1036 that it may be slightly more performant, simply because it does not have to
1037 go through the rest of QWidget infrastructure but can right away trigger
1038 rendering a new frame and then do the readback.
1039
1040 \sa setColorBufferFormat()
1041 */
1042QImage QRhiWidget::grabFramebuffer() const
1043{
1044 return const_cast<QRhiWidgetPrivate *>(d_func())->grabFramebuffer();
1045}
1046
1047/*!
1048 Called when the widget is initialized for the first time, when the
1049 associated texture's size, format, or sample count changes, or when the
1050 QRhi and texture change for any reason. The function is expected to
1051 maintain (create if not yet created, adjust and rebuild if the size has
1052 changed) the graphics resources used by the rendering code in render().
1053
1054 To query the QRhi, QRhiTexture, and other related objects, call rhi(),
1055 colorTexture(), depthStencilBuffer(), and renderTarget().
1056
1057 When the widget size changes, the QRhi object, the color buffer texture,
1058 and the depth stencil buffer objects are all the same instances (so the
1059 getters return the same pointers) as before, but the color and
1060 depth/stencil buffers will likely have been rebuilt, meaning the
1061 \l{QRhiTexture::pixelSize()}{size} and the underlying native texture
1062 resource may be different than in the last invocation.
1063
1064 Reimplementations should also be prepared that the QRhi object and the
1065 color buffer texture may change between invocations of this function. One
1066 special case where the objects will be different is when performing a
1067 grabFramebuffer() with a widget that is not yet shown, and then making the
1068 widget visible on-screen within a top-level widget. There the grab will
1069 happen with a dedicated QRhi that is then replaced with the top-level
1070 window's associated QRhi in subsequent initialize() and render()
1071 invocations. Another, more common case is when the widget is reparented so
1072 that it belongs to a new top-level window. In this case the QRhi and all
1073 related resources managed by the QRhiWidget will be different instances
1074 than before in the subsequent call to this function. Is is then important
1075 that all existing QRhi resources previously created by the subclass are
1076 destroyed because they belong to the previous QRhi that should not be used
1077 by the widget anymore.
1078
1079 When \l autoRenderTarget is \c true, which is the default, a
1080 depth-stencil QRhiRenderBuffer and a QRhiTextureRenderTarget associated
1081 with colorTexture() (or msaaColorBuffer()) and the depth-stencil buffer are
1082 created and managed automatically. Reimplementations of initialize() and
1083 render() can query those objects via depthStencilBuffer() and
1084 renderTarget(). When \l autoRenderTarget is set to \c false, these
1085 objects are no longer created and managed automatically. Rather, it will be
1086 up the the initialize() implementation to create buffers and set up the
1087 render target as it sees fit. When manually managing additional color or
1088 depth-stencil attachments for the render target, their size and sample
1089 count must always follow the size and sample count of colorTexture() /
1090 msaaColorBuffer(), otherwise rendering or 3D API validation errors may
1091 occur.
1092
1093 The subclass-created graphics resources are expected to be released in the
1094 destructor implementation of the subclass.
1095
1096 \a cb is the QRhiCommandBuffer for the current frame of the widget. The
1097 function is called with a frame being recorded, but without an active
1098 render pass. The command buffer is provided primarily to allow enqueuing
1099 \l{QRhiCommandBuffer::resourceUpdate()}{resource updates} without deferring
1100 to render().
1101
1102 \sa render()
1103 */
1104void QRhiWidget::initialize(QRhiCommandBuffer *cb)
1105{
1106 Q_UNUSED(cb);
1107}
1108
1109/*!
1110 Called when the widget contents (i.e. the contents of the texture) need
1111 updating.
1112
1113 There is always at least one call to initialize() before this function is
1114 called.
1115
1116 To request updates, call QWidget::update(). Calling update() from within
1117 render() will lead to updating continuously, throttled by vsync.
1118
1119 \a cb is the QRhiCommandBuffer for the current frame of the widget. The
1120 function is called with a frame being recorded, but without an active
1121 render pass.
1122
1123 \sa initialize()
1124 */
1125void QRhiWidget::render(QRhiCommandBuffer *cb)
1126{
1127 Q_UNUSED(cb);
1128}
1129
1130/*!
1131 Called when the need to early-release the graphics resources arises.
1132
1133 This normally does not happen for a QRhiWidget that is added to a top-level
1134 widget's child hierarchy and it then stays there for the rest of its and
1135 the top-level's lifetime. Thus in many cases there is no need to
1136 reimplement this function, e.g. because the application only ever has a
1137 single top-level widget (native window). However, when reparenting of the
1138 widget (or an ancestor of it) is involved, reimplementing this function
1139 will become necessary in robust, well-written QRhiWidget subclasses.
1140
1141 When this function is called, the implementation is expected to destroy all
1142 QRhi resources (QRhiBuffer, QRhiTexture, etc. objects), similarly to how it
1143 is expected to do this in the destructor. Nulling out, using a smart
1144 pointer, or setting a \c{resources-invalid} flag is going to be required as
1145 well, because initialize() will eventually get called afterwards. Note
1146 however that deferring the releasing of resources to the subsequent
1147 initialize() is wrong. If this function is called, the resource must be
1148 dropped before returning. Also note that implementing this function does
1149 not replace the class destructor (or smart pointers): the graphics
1150 resources must still be released in both.
1151
1152 See the \l{Cube RHI Widget Example} for an example of this in action. There
1153 the button that toggles the QRhiWidget between being a child widget (due to
1154 having a parent widget) and being a top-level widget (due to having no
1155 parent widget), will trigger invoking this function since the associated
1156 top-level widget, native window, and QRhi all change during the lifetime of
1157 the QRhiWidget, with the previously used QRhi getting destroyed which
1158 implies an early-release of the associated resources managed by the
1159 still-alive QRhiWidget.
1160
1161 Another case when this function is called is when grabFramebuffer() is used
1162 with a QRhiWidget that is not added to a visible window, i.e. the rendering
1163 is performed offscreen. If later on this QRhiWidget is made visible, or
1164 added to a visible widget hierarchy, the associated QRhi will change from
1165 the temporary one used for offscreen rendering to the window's dedicated
1166 one, thus triggering this function as well.
1167
1168 \sa initialize()
1169 */
1170void QRhiWidget::releaseResources()
1171{
1172}
1173
1174/*!
1175 \return the current QRhi object.
1176
1177 Must only be called from initialize() and render().
1178 */
1179QRhi *QRhiWidget::rhi() const
1180{
1181 Q_D(const QRhiWidget);
1182 return d->rhi;
1183}
1184
1185/*!
1186 \return the texture serving as the color buffer for the widget.
1187
1188 Must only be called from initialize() and render().
1189
1190 Unlike the depth-stencil buffer and the QRhiRenderTarget, this texture is
1191 always available and is managed by the QRhiWidget, independent of the value
1192 of \l autoRenderTarget.
1193
1194 \note When \l sampleCount is larger than 1, and so multisample antialiasing
1195 is enabled, the return value is \nullptr. Instead, query the
1196 \l QRhiRenderBuffer by calling msaaColorBuffer().
1197
1198 \note The backing texture size and sample count can also be queried via the
1199 QRhiRenderTarget returned from renderTarget(). This can be more convenient
1200 and compact than querying from the QRhiTexture or QRhiRenderBuffer, because
1201 it works regardless of multisampling is in use or not.
1202
1203 \sa msaaColorBuffer(), depthStencilBuffer(), renderTarget(), resolveTexture()
1204 */
1205QRhiTexture *QRhiWidget::colorTexture() const
1206{
1207 Q_D(const QRhiWidget);
1208 return d->colorTexture;
1209}
1210
1211/*!
1212 \return the renderbuffer serving as the multisample color buffer for the widget.
1213
1214 Must only be called from initialize() and render().
1215
1216 When \l sampleCount is larger than 1, and so multisample antialising is
1217 enabled, the returned QRhiRenderBuffer has a matching sample count and
1218 serves as the color buffer. Graphics pipelines used to render into this
1219 buffer must be created with the same sample count, and the depth-stencil
1220 buffer's sample count must match as well. The multisample content is
1221 expected to be resolved into the texture returned from resolveTexture().
1222 When \l autoRenderTarget is
1223 \c true, renderTarget() is set up automatically to do this, by setting up
1224 msaaColorBuffer() as the \l{QRhiColorAttachment::renderBuffer()}{renderbuffer} of
1225 color attachment 0 and resolveTexture() as its
1226 \l{QRhiColorAttachment::resolveTexture()}{resolveTexture}.
1227
1228 When MSAA is not in use, the return value is \nullptr. Use colorTexture()
1229 instead then.
1230
1231 Depending on the underlying 3D graphics API, there may be no practical
1232 difference between multisample textures and color renderbuffers with a
1233 sample count larger than 1 (QRhi may just map both to the same native
1234 resource type). Some older APIs however may differentiate between textures
1235 and renderbuffers. In order to support OpenGL ES 3.0, where multisample
1236 renderbuffers are available, but multisample textures are not, QRhiWidget
1237 always performs MSAA by using a multisample QRhiRenderBuffer as the color
1238 attachment (and never a multisample QRhiTexture).
1239
1240 \note The backing texture size and sample count can also be queried via the
1241 QRhiRenderTarget returned from renderTarget(). This can be more convenient
1242 and compact than querying from the QRhiTexture or QRhiRenderBuffer, because
1243 it works regardless of multisampling is in use or not.
1244
1245 \sa colorTexture(), depthStencilBuffer(), renderTarget(), resolveTexture()
1246 */
1247QRhiRenderBuffer *QRhiWidget::msaaColorBuffer() const
1248{
1249 Q_D(const QRhiWidget);
1250 return d->msaaColorBuffer;
1251}
1252
1253/*!
1254 \return the non-multisample texture to which the multisample content is resolved.
1255
1256 The result is \nullptr when multisample antialiasing is not enabled.
1257
1258 Must only be called from initialize() and render().
1259
1260 With MSAA enabled, this is the texture that gets composited with the rest
1261 of the QWidget content on-screen. However, the QRhiWidget's rendering must
1262 target the (multisample) QRhiRenderBuffer returned from
1263 msaaColorBuffer(). When
1264 \l autoRenderTarget is \c true, this is taken care of by the
1265 QRhiRenderTarget returned from renderTarget(). Otherwise, it is up to the
1266 subclass code to correctly configure a render target object with both the
1267 color buffer and resolve textures.
1268
1269 \sa colorTexture()
1270 */
1271QRhiTexture *QRhiWidget::resolveTexture() const
1272{
1273 Q_D(const QRhiWidget);
1274 return d->resolveTexture;
1275}
1276
1277/*!
1278 \return the depth-stencil buffer used by the widget's rendering.
1279
1280 Must only be called from initialize() and render().
1281
1282 Available only when \l autoRenderTarget is \c true. Otherwise the
1283 returned value is \nullptr and it is up the reimplementation of
1284 initialize() to create and manage a depth-stencil buffer and a
1285 QRhiTextureRenderTarget.
1286
1287 \sa colorTexture(), renderTarget()
1288 */
1289QRhiRenderBuffer *QRhiWidget::depthStencilBuffer() const
1290{
1291 Q_D(const QRhiWidget);
1292 return d->depthStencilBuffer;
1293}
1294
1295/*!
1296 \return the render target object that must be used with
1297 \l QRhiCommandBuffer::beginPass() in reimplementations of render().
1298
1299 Must only be called from initialize() and render().
1300
1301 Available only when \l autoRenderTarget is \c true. Otherwise the
1302 returned value is \nullptr and it is up the reimplementation of
1303 initialize() to create and manage a depth-stencil buffer and a
1304 QRhiTextureRenderTarget.
1305
1306 When creating \l{QRhiGraphicsPipeline}{graphics pipelines}, a
1307 QRhiRenderPassDescriptor is needed. This can be queried from the returned
1308 QRhiTextureRenderTarget by calling
1309 \l{QRhiTextureRenderTarget::renderPassDescriptor()}{renderPassDescriptor()}.
1310
1311 \sa colorTexture(), depthStencilBuffer()
1312 */
1313QRhiRenderTarget *QRhiWidget::renderTarget() const
1314{
1315 Q_D(const QRhiWidget);
1316 return d->renderTarget;
1317}
1318
1319/*!
1320 \fn void QRhiWidget::frameSubmitted()
1321
1322 This signal is emitted after the widget's top-level window has finished
1323 composition and has \l{QRhi::endFrame()}{submitted a frame}.
1324*/
1325
1326/*!
1327 \fn void QRhiWidget::renderFailed()
1328
1329 This signal is emitted whenever the widget is supposed to render to its
1330 backing texture (either due to a \l{QWidget::update()}{widget update} or
1331 due to a call to grabFramebuffer()), but there is no \l QRhi for the widget to
1332 use, likely due to issues related to graphics configuration.
1333
1334 This signal may be emitted multiple times when a problem arises. Do not
1335 assume it is emitted only once. Connect with Qt::SingleShotConnection if
1336 the error handling code is to be notified only once.
1337*/
1338
1339QT_END_NAMESPACE