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();
317 d->sendPaintEvent(QRect(QPoint(0, 0), size()));
322 return QWidget::event(e);
372QImage QRhiWidgetPrivate::grabFramebuffer()
385 offscreenRenderer.setConfig(config);
387 offscreenRenderer.create();
388 rhi = offscreenRenderer.rhi();
390 qWarning(
"QRhiWidget: Failed to create dedicated QRhi for grabbing");
391 emit q->renderFailed();
396 QRhiCommandBuffer *cb =
nullptr;
397 if (rhi->beginOffscreenFrame(&cb) != QRhi::FrameOpSuccess)
400 QRhiReadbackResult readResult;
401 bool readCompleted =
false;
402 bool needsInit =
false;
403 ensureTexture(&needsInit);
405 if (colorTexture || msaaColorBuffer) {
406 bool canRender =
true;
408 canRender = invokeInitialize(cb);
412 QRhiResourceUpdateBatch *readbackBatch = rhi->nextResourceUpdateBatch();
413 readResult.completed = [&readCompleted] { readCompleted =
true; };
414 readbackBatch->readBackTexture(resolveTexture ? resolveTexture : colorTexture, &readResult);
415 cb->resourceUpdate(readbackBatch);
418 rhi->endOffscreenFrame();
421 QImage::Format imageFormat = QImage::Format_RGBA8888;
422 switch (widgetTextureFormat) {
423 case QRhiWidget::TextureFormat::RGBA8:
425 case QRhiWidget::TextureFormat::RGBA16F:
426 imageFormat = QImage::Format_RGBA16FPx4;
428 case QRhiWidget::TextureFormat::RGBA32F:
429 imageFormat = QImage::Format_RGBA32FPx4;
431 case QRhiWidget::TextureFormat::RGB10A2:
432 imageFormat = QImage::Format_BGR30;
435 QImage wrapperImage(
reinterpret_cast<
const uchar *>(readResult.data.constData()),
436 readResult.pixelSize.width(), readResult.pixelSize.height(),
439 if (rhi->isYUpInFramebuffer())
440 result = wrapperImage.flipped();
442 result = wrapperImage.copy();
443 result.setDevicePixelRatio(q->devicePixelRatio());
492void QRhiWidgetPrivate::ensureRhi()
495 QRhi *currentRhi = QWidgetPrivate::rhi();
496 if (currentRhi && currentRhi->backend() != QBackingStoreRhiSupport::apiToRhiBackend(config.api())) {
497 qWarning(
"The top-level window is already using another graphics API for composition, "
498 "'%s' is not compatible with this widget",
499 currentRhi->backendName());
503 if (currentRhi && rhi != currentRhi) {
505 rhi->removeCleanupCallback(q);
508 if (rhi == offscreenRenderer.rhi()) {
509 q->releaseResources();
511 offscreenRenderer.reset();
516 resetRenderTargetObjects();
517 resetColorBufferObjects();
526 currentRhi->addCleanupCallback(q, [q,
this](QRhi *regRhi) {
527 if (!QWidgetPrivate::get(q)->data.in_destructor &&
this->rhi == regRhi) {
528 q->releaseResources();
539void QRhiWidgetPrivate::ensureTexture(
bool *changed)
543 QSize newSize = fixedSize;
544 if (newSize.isEmpty())
545 newSize = q->size() * q->devicePixelRatio();
547 const int minTexSize = rhi->resourceLimit(QRhi::TextureSizeMin);
548 const int maxTexSize = rhi->resourceLimit(QRhi::TextureSizeMax);
549 newSize.setWidth(qMin(maxTexSize, qMax(minTexSize, newSize.width())));
550 newSize.setHeight(qMin(maxTexSize, qMax(minTexSize, newSize.height())));
553 if (colorTexture->format() != rhiTextureFormat || colorTexture->sampleCount() != samples) {
554 resetColorBufferObjects();
558 resetRenderTargetObjects();
562 if (msaaColorBuffer) {
563 if (msaaColorBuffer->backingFormat() != rhiTextureFormat || msaaColorBuffer->sampleCount() != samples) {
564 resetColorBufferObjects();
568 resetRenderTargetObjects();
572 if (!colorTexture && samples <= 1) {
575 if (!rhi->isTextureFormatSupported(rhiTextureFormat)) {
576 qWarning(
"QRhiWidget: The requested texture format (%d) is not supported by the "
577 "underlying 3D graphics API implementation",
int(rhiTextureFormat));
579 colorTexture = rhi->newTexture(rhiTextureFormat, newSize, samples, QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource);
580 if (!colorTexture->create()) {
581 qWarning(
"Failed to create backing texture for QRhiWidget");
583 colorTexture =
nullptr;
589 if (!msaaColorBuffer) {
592 if (!rhi->isFeatureSupported(QRhi::MultisampleRenderBuffer)) {
593 qWarning(
"QRhiWidget: Multisample renderbuffers are reported as unsupported; "
594 "sample count %d will not work as expected", samples);
596 if (!rhi->isTextureFormatSupported(rhiTextureFormat)) {
597 qWarning(
"QRhiWidget: The requested texture format (%d) is not supported by the "
598 "underlying 3D graphics API implementation",
int(rhiTextureFormat));
600 msaaColorBuffer = rhi->newRenderBuffer(QRhiRenderBuffer::Color, newSize, samples, {}, rhiTextureFormat);
601 if (!msaaColorBuffer->create()) {
602 qWarning(
"Failed to create multisample color buffer for QRhiWidget");
603 delete msaaColorBuffer;
604 msaaColorBuffer =
nullptr;
608 if (!resolveTexture) {
611 resolveTexture = rhi->newTexture(rhiTextureFormat, newSize, 1, QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource);
612 if (!resolveTexture->create()) {
613 qWarning(
"Failed to create resolve texture for QRhiWidget");
614 delete resolveTexture;
615 resolveTexture =
nullptr;
619 }
else if (resolveTexture) {
620 resolveTexture->deleteLater();
621 resolveTexture =
nullptr;
624 if (colorTexture && colorTexture->pixelSize() != newSize) {
627 colorTexture->setPixelSize(newSize);
628 if (!colorTexture->create())
629 qWarning(
"Failed to rebuild texture for QRhiWidget after resizing");
632 if (msaaColorBuffer && msaaColorBuffer->pixelSize() != newSize) {
635 msaaColorBuffer->setPixelSize(newSize);
636 if (!msaaColorBuffer->create())
637 qWarning(
"Failed to rebuild multisample color buffer for QRhiWidget after resizing");
640 if (resolveTexture && resolveTexture->pixelSize() != newSize) {
643 resolveTexture->setPixelSize(newSize);
644 if (!resolveTexture->create())
645 qWarning(
"Failed to rebuild resolve texture for QRhiWidget after resizing");
648 textureInvalid =
false;
651bool QRhiWidgetPrivate::invokeInitialize(QRhiCommandBuffer *cb)
654 if (!colorTexture && !msaaColorBuffer)
657 if (autoRenderTarget) {
658 const QSize pixelSize = colorTexture ? colorTexture->pixelSize() : msaaColorBuffer->pixelSize();
659 if (!depthStencilBuffer) {
660 depthStencilBuffer = rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, pixelSize, samples);
661 if (!depthStencilBuffer->create()) {
662 qWarning(
"Failed to create depth-stencil buffer for QRhiWidget");
663 resetRenderTargetObjects();
666 }
else if (depthStencilBuffer->pixelSize() != pixelSize) {
667 depthStencilBuffer->setPixelSize(pixelSize);
668 if (!depthStencilBuffer->create()) {
669 qWarning(
"Failed to rebuild depth-stencil buffer for QRhiWidget with new size");
675 QRhiColorAttachment color0;
677 color0.setTexture(colorTexture);
679 color0.setRenderBuffer(msaaColorBuffer);
681 color0.setResolveTexture(resolveTexture);
682 QRhiTextureRenderTargetDescription rtDesc(color0, depthStencilBuffer);
683 renderTarget = rhi->newTextureRenderTarget(rtDesc);
684 renderPassDescriptor = renderTarget->newCompatibleRenderPassDescriptor();
685 renderTarget->setRenderPassDescriptor(renderPassDescriptor);
686 if (!renderTarget->create()) {
687 qWarning(
"Failed to create render target for QRhiWidget");
688 resetRenderTargetObjects();
693 resetRenderTargetObjects();
706QRhiWidget::Api QRhiWidget::api()
const
708 Q_D(
const QRhiWidget);
709 switch (d->config.api()) {
710 case QPlatformBackingStoreRhiConfig::OpenGL:
712 case QPlatformBackingStoreRhiConfig::Metal:
714 case QPlatformBackingStoreRhiConfig::Vulkan:
716 case QPlatformBackingStoreRhiConfig::D3D11:
717 return Api::Direct3D11;
718 case QPlatformBackingStoreRhiConfig::D3D12:
719 return Api::Direct3D12;
720 case QPlatformBackingStoreRhiConfig::Null:
723 Q_UNREACHABLE_RETURN(Api::Null);
744void QRhiWidget::setApi(Api api)
749 d->config.setApi(QPlatformBackingStoreRhiConfig::OpenGL);
752 d->config.setApi(QPlatformBackingStoreRhiConfig::Metal);
755 d->config.setApi(QPlatformBackingStoreRhiConfig::Vulkan);
757 case Api::Direct3D11:
758 d->config.setApi(QPlatformBackingStoreRhiConfig::D3D11);
760 case Api::Direct3D12:
761 d->config.setApi(QPlatformBackingStoreRhiConfig::D3D12);
764 d->config.setApi(QPlatformBackingStoreRhiConfig::Null);
827void QRhiWidget::setColorBufferFormat(TextureFormat format)
830 if (d->widgetTextureFormat != format) {
831 d->widgetTextureFormat = format;
833 case TextureFormat::RGBA8:
834 d->rhiTextureFormat = QRhiTexture::RGBA8;
836 case TextureFormat::RGBA16F:
837 d->rhiTextureFormat = QRhiTexture::RGBA16F;
839 case TextureFormat::RGBA32F:
840 d->rhiTextureFormat = QRhiTexture::RGBA32F;
842 case TextureFormat::RGB10A2:
843 d->rhiTextureFormat = QRhiTexture::RGB10A2;
846 emit colorBufferFormatChanged(format);