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

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

source code of qtbase/src/widgets/kernel/qrhiwidget.cpp