189void QRhiWidgetPrivate::init()
191 if (Q_UNLIKELY(!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::RhiBasedRendering)))
192 qWarning(
"QRhiWidget: QRhi is not supported on this platform.");
194 setRenderToTexture();
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);
202 config.setApi(QPlatformBackingStoreRhiConfig::OpenGL);
209QRhiWidget::~QRhiWidget()
214 d->rhi->removeCleanupCallback(
this);
217 d->resetRenderTargetObjects();
218 d->resetColorBufferObjects();
219 qDeleteAll(d->pendingDeletes);
222 d->offscreenRenderer.reset();
257void QRhiWidget::paintEvent(QPaintEvent *)
260 if (!updatesEnabled() || d->noSize)
265 qWarning(
"QRhiWidget: No QRhi");
270 QRhiCommandBuffer *cb =
nullptr;
271 if (d->rhi->beginOffscreenFrame(&cb) != QRhi::FrameOpSuccess)
274 bool needsInit =
false;
275 d->ensureTexture(&needsInit);
276 if (d->colorTexture || d->msaaColorBuffer) {
277 bool canRender =
true;
279 canRender = d->invokeInitialize(cb);
284 d->rhi->endOffscreenFrame();
290bool QRhiWidget::event(QEvent *e)
294 case QEvent::WindowAboutToChangeInternal:
297 d->textureInvalid =
true;
299 if (d->rhi && d->rhi != d->offscreenRenderer.rhi()) {
304 d->rhi->removeCleanupCallback(
this);
306 d->releaseResources();
314 d->sendPaintEvent(QRect(QPoint(0, 0), size()));
319 return QWidget::event(e);
369QImage QRhiWidgetPrivate::grabFramebuffer()
382 offscreenRenderer.setConfig(config);
384 offscreenRenderer.create();
385 rhi = offscreenRenderer.rhi();
387 qWarning(
"QRhiWidget: Failed to create dedicated QRhi for grabbing");
388 emit q->renderFailed();
393 QRhiCommandBuffer *cb =
nullptr;
394 if (rhi->beginOffscreenFrame(&cb) != QRhi::FrameOpSuccess)
397 QRhiReadbackResult readResult;
398 bool readCompleted =
false;
399 bool needsInit =
false;
400 ensureTexture(&needsInit);
402 if (colorTexture || msaaColorBuffer) {
403 bool canRender =
true;
405 canRender = invokeInitialize(cb);
409 QRhiResourceUpdateBatch *readbackBatch = rhi->nextResourceUpdateBatch();
410 readResult.completed = [&readCompleted] { readCompleted =
true; };
411 readbackBatch->readBackTexture(resolveTexture ? resolveTexture : colorTexture, &readResult);
412 cb->resourceUpdate(readbackBatch);
415 rhi->endOffscreenFrame();
418 QImage::Format imageFormat = QImage::Format_RGBA8888;
419 switch (widgetTextureFormat) {
420 case QRhiWidget::TextureFormat::RGBA8:
422 case QRhiWidget::TextureFormat::RGBA16F:
423 imageFormat = QImage::Format_RGBA16FPx4;
425 case QRhiWidget::TextureFormat::RGBA32F:
426 imageFormat = QImage::Format_RGBA32FPx4;
428 case QRhiWidget::TextureFormat::RGB10A2:
429 imageFormat = QImage::Format_BGR30;
432 QImage wrapperImage(
reinterpret_cast<
const uchar *>(readResult.data.constData()),
433 readResult.pixelSize.width(), readResult.pixelSize.height(),
436 if (rhi->isYUpInFramebuffer())
437 result = wrapperImage.flipped();
439 result = wrapperImage.copy();
440 result.setDevicePixelRatio(q->devicePixelRatio());
489void QRhiWidgetPrivate::ensureRhi()
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());
501 if (currentRhi && rhi != currentRhi) {
505 if (rhi == offscreenRenderer.rhi()) {
506 q->releaseResources();
508 offscreenRenderer.reset();
513 resetRenderTargetObjects();
514 resetColorBufferObjects();
523 currentRhi->addCleanupCallback(q, [q,
this](QRhi *regRhi) {
524 if (!QWidgetPrivate::get(q)->data.in_destructor &&
this->rhi == regRhi) {
525 q->releaseResources();
536void QRhiWidgetPrivate::ensureTexture(
bool *changed)
540 QSize newSize = fixedSize;
541 if (newSize.isEmpty())
542 newSize = q->size() * q->devicePixelRatio();
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())));
550 if (colorTexture->format() != rhiTextureFormat || colorTexture->sampleCount() != samples) {
551 resetColorBufferObjects();
555 resetRenderTargetObjects();
559 if (msaaColorBuffer) {
560 if (msaaColorBuffer->backingFormat() != rhiTextureFormat || msaaColorBuffer->sampleCount() != samples) {
561 resetColorBufferObjects();
565 resetRenderTargetObjects();
569 if (!colorTexture && samples <= 1) {
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));
576 colorTexture = rhi->newTexture(rhiTextureFormat, newSize, samples, QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource);
577 if (!colorTexture->create()) {
578 qWarning(
"Failed to create backing texture for QRhiWidget");
580 colorTexture =
nullptr;
586 if (!msaaColorBuffer) {
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);
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));
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;
605 if (!resolveTexture) {
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;
616 }
else if (resolveTexture) {
617 resolveTexture->deleteLater();
618 resolveTexture =
nullptr;
621 if (colorTexture && colorTexture->pixelSize() != newSize) {
624 colorTexture->setPixelSize(newSize);
625 if (!colorTexture->create())
626 qWarning(
"Failed to rebuild texture for QRhiWidget after resizing");
629 if (msaaColorBuffer && msaaColorBuffer->pixelSize() != newSize) {
632 msaaColorBuffer->setPixelSize(newSize);
633 if (!msaaColorBuffer->create())
634 qWarning(
"Failed to rebuild multisample color buffer for QRhiWidget after resizing");
637 if (resolveTexture && resolveTexture->pixelSize() != newSize) {
640 resolveTexture->setPixelSize(newSize);
641 if (!resolveTexture->create())
642 qWarning(
"Failed to rebuild resolve texture for QRhiWidget after resizing");
645 textureInvalid =
false;
648bool QRhiWidgetPrivate::invokeInitialize(QRhiCommandBuffer *cb)
651 if (!colorTexture && !msaaColorBuffer)
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();
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");
672 QRhiColorAttachment color0;
674 color0.setTexture(colorTexture);
676 color0.setRenderBuffer(msaaColorBuffer);
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();
690 resetRenderTargetObjects();
703QRhiWidget::Api QRhiWidget::api()
const
705 Q_D(
const QRhiWidget);
706 switch (d->config.api()) {
707 case QPlatformBackingStoreRhiConfig::OpenGL:
709 case QPlatformBackingStoreRhiConfig::Metal:
711 case QPlatformBackingStoreRhiConfig::Vulkan:
713 case QPlatformBackingStoreRhiConfig::D3D11:
714 return Api::Direct3D11;
715 case QPlatformBackingStoreRhiConfig::D3D12:
716 return Api::Direct3D12;
717 case QPlatformBackingStoreRhiConfig::Null:
720 Q_UNREACHABLE_RETURN(Api::Null);
741void QRhiWidget::setApi(Api api)
746 d->config.setApi(QPlatformBackingStoreRhiConfig::OpenGL);
749 d->config.setApi(QPlatformBackingStoreRhiConfig::Metal);
752 d->config.setApi(QPlatformBackingStoreRhiConfig::Vulkan);
754 case Api::Direct3D11:
755 d->config.setApi(QPlatformBackingStoreRhiConfig::D3D11);
757 case Api::Direct3D12:
758 d->config.setApi(QPlatformBackingStoreRhiConfig::D3D12);
761 d->config.setApi(QPlatformBackingStoreRhiConfig::Null);
824void QRhiWidget::setColorBufferFormat(TextureFormat format)
827 if (d->widgetTextureFormat != format) {
828 d->widgetTextureFormat = format;
830 case TextureFormat::RGBA8:
831 d->rhiTextureFormat = QRhiTexture::RGBA8;
833 case TextureFormat::RGBA16F:
834 d->rhiTextureFormat = QRhiTexture::RGBA16F;
836 case TextureFormat::RGBA32F:
837 d->rhiTextureFormat = QRhiTexture::RGBA32F;
839 case TextureFormat::RGB10A2:
840 d->rhiTextureFormat = QRhiTexture::RGB10A2;
843 emit colorBufferFormatChanged(format);