diff --git a/src/gui/painting/qplatformbackingstore.cpp b/src/gui/painting/qplatformbackingstore.cpp index f7c4df40c84..21e89d67fd2 100644 --- a/src/gui/painting/qplatformbackingstore.cpp +++ b/src/gui/painting/qplatformbackingstore.cpp @@ -10,6 +10,8 @@ #include +#include + QT_BEGIN_NAMESPACE Q_LOGGING_CATEGORY(lcQpaBackingStore, "qt.qpa.backingstore", QtWarningMsg); @@ -26,11 +28,14 @@ public: QWindow *window; QBackingStore *backingStore; - // The order matters. if it needs to be rearranged in the future, call - // reset() explicitly from the dtor in the correct order. - // (first the compositor, then the rhiSupport) - QBackingStoreRhiSupport rhiSupport; - QBackingStoreDefaultCompositor compositor; + struct SurfaceSupport { + // The order matters. if it needs to be rearranged in the future, call + // reset() explicitly from the dtor in the correct order. + // (first the compositor, then the rhiSupport) + QBackingStoreRhiSupport rhiSupport; + QBackingStoreDefaultCompositor compositor; + }; + std::unordered_map surfaceSupport; }; struct QBackingstoreTextureInfo @@ -210,8 +215,12 @@ QPlatformBackingStore::FlushResult QPlatformBackingStore::rhiFlush(QWindow *wind QPlatformTextureList *textures, bool translucentBackground) { - return d_ptr->compositor.flush(this, d_ptr->rhiSupport.rhi(), d_ptr->rhiSupport.swapChainForWindow(window), - window, sourceDevicePixelRatio, region, offset, textures, translucentBackground); + auto &surfaceSupport = d_ptr->surfaceSupport[window->surfaceType()]; + return surfaceSupport.compositor.flush(this, + surfaceSupport.rhiSupport.rhi(), + surfaceSupport.rhiSupport.swapChainForWindow(window), + window, sourceDevicePixelRatio, region, offset, textures, + translucentBackground); } /*! @@ -261,7 +270,10 @@ QRhiTexture *QPlatformBackingStore::toTexture(QRhiResourceUpdateBatch *resourceU const QRegion &dirtyRegion, TextureFlags *flags) const { - return d_ptr->compositor.toTexture(this, d_ptr->rhiSupport.rhi(), resourceUpdates, dirtyRegion, flags); + auto &surfaceSupport = d_ptr->surfaceSupport[window()->surfaceType()]; + return surfaceSupport.compositor.toTexture(this, + surfaceSupport.rhiSupport.rhi(), resourceUpdates, + dirtyRegion, flags); } /*! @@ -356,33 +368,44 @@ bool QPlatformBackingStore::scroll(const QRegion &area, int dx, int dy) return false; } -void QPlatformBackingStore::setRhiConfig(const QPlatformBackingStoreRhiConfig &config) +void QPlatformBackingStore::createRhi(QWindow *window, QPlatformBackingStoreRhiConfig config) { if (!config.isEnabled()) return; - d_ptr->rhiSupport.setConfig(config); - d_ptr->rhiSupport.setWindow(d_ptr->window); - d_ptr->rhiSupport.setFormat(d_ptr->window->format()); - d_ptr->rhiSupport.create(); + qCDebug(lcQpaBackingStore) << "Setting up RHI support in" << this + << "for" << window << "with" << window->surfaceType() + << "and requested API" << config.api(); + + auto &support = d_ptr->surfaceSupport[window->surfaceType()]; + if (!support.rhiSupport.rhi()) { + support.rhiSupport.setConfig(config); + support.rhiSupport.setWindow(window); + support.rhiSupport.setFormat(window->format()); + support.rhiSupport.create(); + } else { + qCDebug(lcQpaBackingStore) << "Window already has RHI support" + << "with backend" << support.rhiSupport.rhi()->backendName(); + } } -QRhi *QPlatformBackingStore::rhi() const +QRhi *QPlatformBackingStore::rhi(QWindow *window) const { // Returning null is valid, and means this is not a QRhi-capable backingstore. - return d_ptr->rhiSupport.rhi(); + return d_ptr->surfaceSupport[window->surfaceType()].rhiSupport.rhi(); } -void QPlatformBackingStore::graphicsDeviceReportedLost() +void QPlatformBackingStore::graphicsDeviceReportedLost(QWindow *window) { - if (!d_ptr->rhiSupport.rhi()) + auto &surfaceSupport = d_ptr->surfaceSupport[window->surfaceType()]; + if (!surfaceSupport.rhiSupport.rhi()) return; qWarning("Rhi backingstore: graphics device lost, attempting to reinitialize"); - d_ptr->compositor.reset(); - d_ptr->rhiSupport.reset(); - d_ptr->rhiSupport.create(); - if (!d_ptr->rhiSupport.rhi()) + surfaceSupport.compositor.reset(); + surfaceSupport.rhiSupport.reset(); + surfaceSupport.rhiSupport.create(); + if (!surfaceSupport.rhiSupport.rhi()) qWarning("Rhi backingstore: failed to reinitialize after losing the device"); } diff --git a/src/gui/painting/qplatformbackingstore.h b/src/gui/painting/qplatformbackingstore.h index 1d55e9ab6a0..2f27a2aa2c1 100644 --- a/src/gui/painting/qplatformbackingstore.h +++ b/src/gui/painting/qplatformbackingstore.h @@ -39,6 +39,8 @@ class QRhiResourceUpdateBatch; struct Q_GUI_EXPORT QPlatformBackingStoreRhiConfig { + Q_GADGET +public: enum Api { OpenGL, Metal, @@ -47,6 +49,7 @@ struct Q_GUI_EXPORT QPlatformBackingStoreRhiConfig D3D12, Null }; + Q_ENUM(Api) QPlatformBackingStoreRhiConfig() : m_enable(false) @@ -171,10 +174,10 @@ public: virtual void beginPaint(const QRegion &); virtual void endPaint(); - void setRhiConfig(const QPlatformBackingStoreRhiConfig &config); - QRhi *rhi() const; + void createRhi(QWindow *window, QPlatformBackingStoreRhiConfig config); + QRhi *rhi(QWindow *window) const; void surfaceAboutToBeDestroyed(); - void graphicsDeviceReportedLost(); + void graphicsDeviceReportedLost(QWindow *window); private: QPlatformBackingStorePrivate *d_ptr; diff --git a/src/gui/painting/qrhibackingstore.cpp b/src/gui/painting/qrhibackingstore.cpp index 586dfb44a43..d59cc2d83c5 100644 --- a/src/gui/painting/qrhibackingstore.cpp +++ b/src/gui/painting/qrhibackingstore.cpp @@ -20,30 +20,27 @@ void QRhiBackingStore::flush(QWindow *flushedWindow, const QRegion ®ion, cons Q_UNUSED(region); Q_UNUSED(offset); - if (flushedWindow->surfaceType() != window()->surfaceType()) { - qWarning() << "Cannot flush child window" << flushedWindow - << "with surface type" << flushedWindow->surfaceType() << ";" - << "Must match" << window()->surfaceType() << "of" << window(); - - // FIXME: Support different surface types by not tying the - // RHI config to the backing store itself (per window config). - return; - } - - if (!rhi()) { + if (!rhi(flushedWindow)) { QPlatformBackingStoreRhiConfig rhiConfig; - switch (window()->surfaceType()) { + switch (flushedWindow->surfaceType()) { case QSurface::OpenGLSurface: rhiConfig.setApi(QPlatformBackingStoreRhiConfig::OpenGL); break; case QSurface::MetalSurface: rhiConfig.setApi(QPlatformBackingStoreRhiConfig::Metal); break; + case QSurface::Direct3DSurface: + rhiConfig.setApi(QPlatformBackingStoreRhiConfig::D3D11); + break; + case QSurface::VulkanSurface: + rhiConfig.setApi(QPlatformBackingStoreRhiConfig::Vulkan); + break; default: Q_UNREACHABLE(); } + rhiConfig.setEnabled(true); - setRhiConfig(rhiConfig); + createRhi(flushedWindow, rhiConfig); } static QPlatformTextureList emptyTextureList; diff --git a/src/opengl/qopenglcompositorbackingstore.cpp b/src/opengl/qopenglcompositorbackingstore.cpp index 931734cf604..371ca868f85 100644 --- a/src/opengl/qopenglcompositorbackingstore.cpp +++ b/src/opengl/qopenglcompositorbackingstore.cpp @@ -144,11 +144,7 @@ void QOpenGLCompositorBackingStore::flush(QWindow *window, const QRegion ®ion Q_UNUSED(region); Q_UNUSED(offset); - m_rhi = rhi(); - if (!m_rhi) { - setRhiConfig(QPlatformBackingStoreRhiConfig(QPlatformBackingStoreRhiConfig::OpenGL)); - m_rhi = rhi(); - } + m_rhi = rhi(window); Q_ASSERT(m_rhi); QOpenGLCompositor *compositor = QOpenGLCompositor::instance(); @@ -184,11 +180,7 @@ QPlatformBackingStore::FlushResult QOpenGLCompositorBackingStore::rhiFlush(QWind Q_UNUSED(translucentBackground); Q_UNUSED(sourceDevicePixelRatio); - m_rhi = rhi(); - if (!m_rhi) { - setRhiConfig(QPlatformBackingStoreRhiConfig(QPlatformBackingStoreRhiConfig::OpenGL)); - m_rhi = rhi(); - } + m_rhi = rhi(window); Q_ASSERT(m_rhi); QOpenGLCompositor *compositor = QOpenGLCompositor::instance(); diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp index 365323933b5..46e0d2c76c7 100644 --- a/src/widgets/kernel/qwidget.cpp +++ b/src/widgets/kernel/qwidget.cpp @@ -1029,10 +1029,13 @@ void QWidgetPrivate::createRecursively() QRhi *QWidgetPrivate::rhi() const { - if (QWidgetRepaintManager *repaintManager = maybeRepaintManager()) - return repaintManager->rhi(); - else + Q_Q(const QWidget); + if (auto *backingStore = q->backingStore()) { + auto *window = windowHandle(WindowHandleMode::Closest); + return backingStore->handle()->rhi(window); + } else { return nullptr; + } } /*! @@ -1112,8 +1115,15 @@ static bool q_evaluateRhiConfigRecursive(const QWidget *w, QPlatformBackingStore } for (const QObject *child : w->children()) { if (const QWidget *childWidget = qobject_cast(child)) { - if (q_evaluateRhiConfigRecursive(childWidget, outConfig, outType)) + if (q_evaluateRhiConfigRecursive(childWidget, outConfig, outType)) { + static bool optOut = qEnvironmentVariableIsSet("QT_WIDGETS_NO_CHILD_RHI"); + // Native child widgets should not trigger RHI for its parent + // hierarchy, but will still flush the native child using RHI. + if (!optOut && childWidget->testAttribute(Qt::WA_NativeWindow)) + continue; + return true; + } } } return false; @@ -1356,19 +1366,19 @@ void QWidgetPrivate::create() QBackingStore *store = q->backingStore(); usesRhiFlush = false; - if (!store) { - if (q->windowType() != Qt::Desktop) { - if (q->isWindow()) { - q->setBackingStore(new QBackingStore(win)); - QPlatformBackingStoreRhiConfig rhiConfig; - usesRhiFlush = q_evaluateRhiConfig(q, &rhiConfig, nullptr); - topData()->backingStore->handle()->setRhiConfig(rhiConfig); - } - } else { - q->setAttribute(Qt::WA_PaintOnScreen, true); + if (q->windowType() == Qt::Desktop) { + q->setAttribute(Qt::WA_PaintOnScreen, true); + } else { + if (!store && q->isWindow()) + q->setBackingStore(new QBackingStore(win)); + + QPlatformBackingStoreRhiConfig rhiConfig; + usesRhiFlush = q_evaluateRhiConfig(q, &rhiConfig, nullptr); + if (usesRhiFlush && q->backingStore()) { + // Trigger creation of support infrastructure up front, + // now that we have a specific RHI configuration. + q->backingStore()->handle()->createRhi(win, rhiConfig); } - } else if (win->handle()) { - usesRhiFlush = q_evaluateRhiConfig(q, nullptr, nullptr); } setWindowModified_helper(); @@ -10673,6 +10683,7 @@ void QWidget::setParent(QWidget *parent, Qt::WindowFlags f) const bool wasCreated = testAttribute(Qt::WA_WState_Created); QWidget *oldtlw = window(); Q_ASSERT(oldtlw); + QWidget *oldParentWithWindow = d->closestParentWidgetWithWindowHandle(); if (f & Qt::Window) // Frame geometry likely changes, refresh. d->data.fstrut_dirty = true; @@ -10715,7 +10726,8 @@ void QWidget::setParent(QWidget *parent, Qt::WindowFlags f) // texture-based widgets need a pre-notification when their associated top-level window changes // This is not under the wasCreated/newParent conditions above in order to also play nice with QDockWidget. - if (oldtlw->d_func()->usesRhiFlush && ((!parent && parentWidget()) || (parent && parent->window() != oldtlw))) + const bool oldParentUsesRhiFlush = oldParentWithWindow ? oldParentWithWindow->d_func()->usesRhiFlush : false; + if (oldParentUsesRhiFlush && ((!parent && parentWidget()) || (parent && parent->window() != oldtlw))) qSendWindowChangeToTextureChildrenRecursively(this, QEvent::WindowAboutToChangeInternal); // If we get parented into another window, children will be folded @@ -10796,7 +10808,7 @@ void QWidget::setParent(QWidget *parent, Qt::WindowFlags f) // texture-based widgets need another event when their top-level window // changes (more precisely, has already changed at this point) - if (oldtlw->d_func()->usesRhiFlush && oldtlw != window()) + if (oldParentUsesRhiFlush && oldtlw != window()) qSendWindowChangeToTextureChildrenRecursively(this, QEvent::WindowChangeInternal); if (!wasCreated) { @@ -10824,33 +10836,47 @@ void QWidget::setParent(QWidget *parent, Qt::WindowFlags f) if (d->extra && d->extra->hasWindowContainer) QWindowContainer::parentWasChanged(this); - QWidget *newtlw = window(); - if (oldtlw != newtlw) { + QWidget *newParentWithWindow = d->closestParentWidgetWithWindowHandle(); + if (newParentWithWindow && newParentWithWindow != oldParentWithWindow) { + // Check if the native parent now needs to switch to RHI + qCDebug(lcWidgetPainting) << "Evaluating whether reparenting of" << this + << "into" << parent << "requires RHI enablement for" << newParentWithWindow; + + QPlatformBackingStoreRhiConfig rhiConfig; QSurface::SurfaceType surfaceType = QSurface::RasterSurface; - // Only evaluate the reparented subtree. While it might be tempting to - // do it on newtlw instead, the performance implications of that are + + // First evaluate whether the reparented widget uses RHI. + // We do this as a separate step because the performance + // implications of always checking the native parent are // problematic when it comes to large widget trees. - if (q_evaluateRhiConfig(this, nullptr, &surfaceType)) { - const bool wasUsingRhiFlush = newtlw->d_func()->usesRhiFlush; - newtlw->d_func()->usesRhiFlush = true; - bool recreate = false; - if (QWindow *w = newtlw->windowHandle()) { - if (w->surfaceType() != surfaceType || !wasUsingRhiFlush) - recreate = true; - } - // QTBUG-115652: Besides the toplevel the nativeParentWidget()'s QWindow must be checked as well. - if (QWindow *w = d->windowHandle(QWidgetPrivate::WindowHandleMode::Closest)) { - if (w->surfaceType() != surfaceType) - recreate = true; - } - if (recreate) { - const auto windowStateBeforeDestroy = newtlw->windowState(); - const auto visibilityBeforeDestroy = newtlw->isVisible(); - newtlw->destroy(); - newtlw->create(); - Q_ASSERT(newtlw->windowHandle()); - newtlw->windowHandle()->setWindowStates(windowStateBeforeDestroy); - QWidgetPrivate::get(newtlw)->setVisible(visibilityBeforeDestroy); + if (q_evaluateRhiConfig(this, &rhiConfig, &surfaceType)) { + // Then check whether the native parent requires RHI + // as a result. It may not, if this widget is a native + // window, and can handle its own RHI flushing. + if (q_evaluateRhiConfig(newParentWithWindow, nullptr, nullptr)) { + // Finally, check whether we need to recreate the + // native parent to enable RHI flushing. + auto *existingWindow = newParentWithWindow->windowHandle(); + auto existingSurfaceType = existingWindow->surfaceType(); + if (existingSurfaceType != surfaceType) { + qCDebug(lcWidgetPainting) + << "Recreating" << existingWindow + << "with current type" << existingSurfaceType + << "to support" << surfaceType; + const auto windowStateBeforeDestroy = newParentWithWindow->windowState(); + const auto visibilityBeforeDestroy = newParentWithWindow->isVisible(); + newParentWithWindow->destroy(); + newParentWithWindow->create(); + Q_ASSERT(newParentWithWindow->windowHandle()); + newParentWithWindow->windowHandle()->setWindowStates(windowStateBeforeDestroy); + QWidgetPrivate::get(newParentWithWindow)->setVisible(visibilityBeforeDestroy); + } else if (auto *backingStore = newParentWithWindow->backingStore()) { + // If we don't recreate we still need to make sure the native parent + // widget has a RHI config that the reparented widget can use. + backingStore->handle()->createRhi(existingWindow, rhiConfig); + // And that it knows it's now flushing with RHI + QWidgetPrivate::get(newParentWithWindow)->usesRhiFlush = true; + } } } } @@ -12308,8 +12334,10 @@ QBackingStore *QWidget::backingStore() const if (extra && extra->backingStore) return extra->backingStore; - QWidgetRepaintManager *repaintManager = d->maybeRepaintManager(); - return repaintManager ? repaintManager->backingStore() : nullptr; + if (!isWindow()) + return window()->backingStore(); + + return nullptr; } void QWidgetPrivate::getLayoutItemMargins(int *left, int *top, int *right, int *bottom) const diff --git a/src/widgets/kernel/qwidgetrepaintmanager.cpp b/src/widgets/kernel/qwidgetrepaintmanager.cpp index 607a767a20a..0dee380a911 100644 --- a/src/widgets/kernel/qwidgetrepaintmanager.cpp +++ b/src/widgets/kernel/qwidgetrepaintmanager.cpp @@ -1016,11 +1016,13 @@ void QWidgetRepaintManager::flush(QWidget *widget, const QRegion ®ion, QPlatf if (tlw->testAttribute(Qt::WA_DontShowOnScreen) || widget->testAttribute(Qt::WA_DontShowOnScreen)) return; + QWindow *window = widget->windowHandle(); + // We should only be flushing to native widgets + Q_ASSERT(window); + // Foreign Windows do not have backing store content and must not be flushed - if (QWindow *widgetWindow = widget->windowHandle()) { - if (widgetWindow->type() == Qt::ForeignWindow) - return; - } + if (window->type() == Qt::ForeignWindow) + return; static bool fpsDebug = qEnvironmentVariableIntValue("QT_DEBUG_FPS"); if (fpsDebug) { @@ -1037,69 +1039,51 @@ void QWidgetRepaintManager::flush(QWidget *widget, const QRegion ®ion, QPlatf if (widget != tlw) offset += widget->mapTo(tlw, QPoint()); - // Use a condition that tries to keep both QTBUG-108344 and QTBUG-113557 - // happy, i.e. support both (A) "native rhi-based child in a rhi-based - // toplevel" and (B) "native raster child in a rhi-based toplevel". - // - // If the tlw and the backingstore are RHI-based, then there are two cases - // to consider: - // - // (1) widget is not a native child, i.e. the QWindow for widget and tlw are - // the same, - // - // (2) widget is a native child which we now attempt to flush with tlw's - // backingstore to widget's native window. This is the interesting one. - // - // Using the condition tlw->usesRhiFlush on its own is insufficient since - // it fails to capture the case of a raster-based native child widget - // within tlw. (which must hit the non-rhi flush path) - // - // Extending the condition with tlw->windowHandle() == widget->windowHandle() - // would be logical but wrong, when it comes to (A) since flushing a - // RHI-based native child with a given 3D API using a RHI-based - // tlw/backingstore with the same 3D API needs to be supported still. (this - // happens when e.g. someone calls winId() on a QOpenGLWidget) - // - // Different 3D APIs do not need to be supported since we do not allow to - // do things like having a QQuickWidget with Vulkan and a QOpenGLWidget in - // the same toplevel, regardless of the widgets being native children or - // not. Hence comparing the surfaceType() instead. This satisfies both (A) - // and (B) given that an RHI-based toplevel cannot be RasterSurface. - // - if (tlw->d_func()->usesRhiFlush && tlw->windowHandle()->surfaceType() == widget->windowHandle()->surfaceType()) { - QRhi *rhi = store->handle()->rhi(); - qCDebug(lcWidgetPainting) << "Flushing" << region << "of" << widget - << "with QRhi" << rhi - << "to window" << widget->windowHandle(); + // A widget uses RHI flush if itself, or one of its non-native children + // uses RHI for its drawing. If so, we composite the backing store raster + // data along with textures produced by the RHI widgets. + const bool flushWithRhi = widget->d_func()->usesRhiFlush; + + qCDebug(lcWidgetPainting) << "Flushing" << region << "of" << widget + << "to" << window << (flushWithRhi ? "using RHI" : ""); + + // A widget uses RHI flush if itself, or one of its non-native children + // uses RHI for its drawing. If so, we composite the backing store raster + // data along with textures produced by the RHI widgets. + if (flushWithRhi) { if (!widgetTextures) widgetTextures = qt_dummy_platformTextureList; - QWidgetPrivate *widgetWindowPrivate = widget->window()->d_func(); - widgetWindowPrivate->sendComposeStatus(widget->window(), false); + // We only need to let the widget sub-hierarchy that + // we are flushing know that we're compositing. + auto *widgetPrivate = QWidgetPrivate::get(widget); + widgetPrivate->sendComposeStatus(widget, false); + // A window may have alpha even when the app did not request // WA_TranslucentBackground. Therefore the compositor needs to know whether the app intends // to rely on translucency, in order to decide if it should clear to transparent or opaque. const bool translucentBackground = widget->testAttribute(Qt::WA_TranslucentBackground); QPlatformBackingStore::FlushResult flushResult; - flushResult = store->handle()->rhiFlush(widget->windowHandle(), + flushResult = store->handle()->rhiFlush(window, widget->devicePixelRatio(), region, offset, widgetTextures, translucentBackground); - widgetWindowPrivate->sendComposeStatus(widget->window(), true); + + widgetPrivate->sendComposeStatus(widget, true); + if (flushResult == QPlatformBackingStore::FlushFailedDueToLostDevice) { qSendWindowChangeToTextureChildrenRecursively(widget->window(), QEvent::WindowAboutToChangeInternal); - store->handle()->graphicsDeviceReportedLost(); + store->handle()->graphicsDeviceReportedLost(window); qSendWindowChangeToTextureChildrenRecursively(widget->window(), QEvent::WindowChangeInternal); widget->update(); } } else { - qCInfo(lcWidgetPainting) << "Flushing" << region << "of" << widget; - store->flush(region, widget->windowHandle(), offset); + store->flush(region, window, offset); } } @@ -1308,11 +1292,6 @@ void QWidgetPrivate::invalidateBackingStore_resizeHelper(const QPoint &oldPos, c } } -QRhi *QWidgetRepaintManager::rhi() const -{ - return store->handle()->rhi(); -} - QT_END_NAMESPACE #include "qwidgetrepaintmanager.moc" diff --git a/src/widgets/kernel/qwidgetrepaintmanager_p.h b/src/widgets/kernel/qwidgetrepaintmanager_p.h index 43789746101..13190a0bb0e 100644 --- a/src/widgets/kernel/qwidgetrepaintmanager_p.h +++ b/src/widgets/kernel/qwidgetrepaintmanager_p.h @@ -26,8 +26,6 @@ QT_BEGIN_NAMESPACE class QPlatformTextureList; class QPlatformTextureListWatcher; class QWidgetRepaintManager; -class QRhi; -class QRhiSwapChain; class Q_WIDGETS_EXPORT QWidgetRepaintManager { @@ -72,8 +70,6 @@ public: bool bltRect(const QRect &rect, int dx, int dy, QWidget *widget); - QRhi *rhi() const; - private: void updateLists(QWidget *widget); diff --git a/src/widgets/kernel/qwidgetwindow.cpp b/src/widgets/kernel/qwidgetwindow.cpp index 33e376460f8..ce9bb44b45f 100644 --- a/src/widgets/kernel/qwidgetwindow.cpp +++ b/src/widgets/kernel/qwidgetwindow.cpp @@ -160,8 +160,8 @@ QWidgetWindow::QWidgetWindow(QWidget *widget) updateObjectName(); if (!QCoreApplication::testAttribute(Qt::AA_ForceRasterWidgets)) { QSurface::SurfaceType type = QSurface::RasterSurface; - q_evaluateRhiConfig(m_widget, nullptr, &type); - setSurfaceType(type); + if (q_evaluateRhiConfig(m_widget, nullptr, &type)) + setSurfaceType(type); } connect(widget, &QObject::objectNameChanged, this, &QWidgetWindow::updateObjectName); diff --git a/tests/auto/widgets/kernel/qwidgetrepaintmanager/tst_qwidgetrepaintmanager.cpp b/tests/auto/widgets/kernel/qwidgetrepaintmanager/tst_qwidgetrepaintmanager.cpp index 9059a9262eb..64ebeb08b0d 100644 --- a/tests/auto/widgets/kernel/qwidgetrepaintmanager/tst_qwidgetrepaintmanager.cpp +++ b/tests/auto/widgets/kernel/qwidgetrepaintmanager/tst_qwidgetrepaintmanager.cpp @@ -260,6 +260,7 @@ private slots: void staticContents(); void scroll(); void paintOnScreenUpdates(); + void evaluateRhi(); #if defined(QT_BUILD_INTERNAL) void scrollWithOverlap(); @@ -581,6 +582,222 @@ void tst_QWidgetRepaintManager::paintOnScreenUpdates() } } +class RhiWidgetPrivate : public QWidgetPrivate +{ +public: + RhiWidgetPrivate(const QPlatformBackingStoreRhiConfig &config) + : config(config) + { + } + + QPlatformBackingStoreRhiConfig rhiConfig() const override + { + return config; + } + + QPlatformBackingStoreRhiConfig config = QPlatformBackingStoreRhiConfig::Null; +}; + +class RhiWidget : public QWidget +{ +public: + RhiWidget(const QPlatformBackingStoreRhiConfig &config = QPlatformBackingStoreRhiConfig::Null, QWidget *parent = nullptr) + : QWidget(*new RhiWidgetPrivate(config), parent, {}) + { + } +}; + +void tst_QWidgetRepaintManager::evaluateRhi() +{ + const auto *integration = QGuiApplicationPrivate::platformIntegration(); + if (!integration->hasCapability(QPlatformIntegration::RhiBasedRendering)) + QSKIP("Platform does not support RHI based rendering"); + + // We need full control over whether widgets are native or not + const bool nativeSiblingsOriginal = qApp->testAttribute(Qt::AA_DontCreateNativeWidgetSiblings); + qApp->setAttribute(Qt::AA_DontCreateNativeWidgetSiblings, true); + auto nativeSiblingGuard = qScopeGuard([&]{ + qApp->setAttribute(Qt::AA_DontCreateNativeWidgetSiblings, nativeSiblingsOriginal); + }); + + auto defaultSurfaceType = QSurface::RasterSurface; + bool usesRhiBackingStore = false; + + { + // Plain QWidget doesn't enable RHI + QWidget regularWidget; + regularWidget.show(); + QVERIFY(QTest::qWaitForWindowExposed(®ularWidget)); + QVERIFY(!QWidgetPrivate::get(®ularWidget)->usesRhiFlush); + + // The platform might use a non-raster surface type if it uses + // an RHI backingstore by default (e.g. Android, iOS, QNX). + defaultSurfaceType = regularWidget.windowHandle()->surfaceType(); + + // Record whether the platform uses an RHI backingstore, + // so we can opt out of some tests further down. + if (defaultSurfaceType != QSurface::RasterSurface) + usesRhiBackingStore = QWidgetPrivate::get(®ularWidget)->rhi(); + else + QVERIFY(!QWidgetPrivate::get(®ularWidget)->rhi()); + } + + { + // But a top level RHI widget does + RhiWidget rhiWidget; + rhiWidget.show(); + QVERIFY(QTest::qWaitForWindowExposed(&rhiWidget)); + QVERIFY(QWidgetPrivate::get(&rhiWidget)->usesRhiFlush); + QVERIFY(QWidgetPrivate::get(&rhiWidget)->rhi()); + } + +#if QT_CONFIG(opengl) + { + // Non-native child RHI widget enables RHI for top level regular widget + QWidget topLevel; + RhiWidget rhiWidget(QPlatformBackingStoreRhiConfig::OpenGL, &topLevel); + topLevel.show(); + QVERIFY(QTest::qWaitForWindowExposed(&topLevel)); + QCOMPARE(topLevel.windowHandle()->surfaceType(), QSurface::OpenGLSurface); + QVERIFY(QWidgetPrivate::get(&topLevel)->usesRhiFlush); + QVERIFY(QWidgetPrivate::get(&topLevel)->rhi()); + // Only the native widget that actually flushes will report usesRhiFlush + QVERIFY(!QWidgetPrivate::get(&rhiWidget)->usesRhiFlush); + // But it should have an RHI it can use + QVERIFY(QWidgetPrivate::get(&rhiWidget)->rhi()); + } + + { + // Native child RHI widget does not enable RHI for top level + QWidget topLevel; + RhiWidget nativeRhiWidget(QPlatformBackingStoreRhiConfig::OpenGL, &topLevel); + nativeRhiWidget.setAttribute(Qt::WA_NativeWindow); + topLevel.show(); + QVERIFY(QTest::qWaitForWindowExposed(&topLevel)); + QCOMPARE(nativeRhiWidget.windowHandle()->surfaceType(), QSurface::OpenGLSurface); + QVERIFY(QWidgetPrivate::get(&nativeRhiWidget)->usesRhiFlush); + QVERIFY(QWidgetPrivate::get(&nativeRhiWidget)->rhi()); + QCOMPARE(topLevel.windowHandle()->surfaceType(), defaultSurfaceType); + QVERIFY(!QWidgetPrivate::get(&topLevel)->usesRhiFlush); + + if (!usesRhiBackingStore) + QVERIFY(!QWidgetPrivate::get(&topLevel)->rhi()); + } + + { + // Non-native RHI child of native child enables RHI for native child, + // but not top level. + QWidget topLevel; + QWidget nativeChild(&topLevel); + nativeChild.setAttribute(Qt::WA_NativeWindow); + RhiWidget rhiWidget(QPlatformBackingStoreRhiConfig::OpenGL, &nativeChild); + topLevel.show(); + QVERIFY(QTest::qWaitForWindowExposed(&topLevel)); + + QCOMPARE(nativeChild.windowHandle()->surfaceType(), QSurface::OpenGLSurface); + QVERIFY(QWidgetPrivate::get(&nativeChild)->usesRhiFlush); + QVERIFY(QWidgetPrivate::get(&nativeChild)->rhi()); + QVERIFY(!QWidgetPrivate::get(&rhiWidget)->usesRhiFlush); + QVERIFY(QWidgetPrivate::get(&rhiWidget)->rhi()); + QCOMPARE(topLevel.windowHandle()->surfaceType(), defaultSurfaceType); + QVERIFY(!QWidgetPrivate::get(&topLevel)->usesRhiFlush); + if (!usesRhiBackingStore) + QVERIFY(!QWidgetPrivate::get(&topLevel)->rhi()); + } + + { + // Native child RHI widget does not prevent RHI for top level + // if non-native RHI child widget is also present. + QWidget topLevel; + RhiWidget rhiWidget(QPlatformBackingStoreRhiConfig::OpenGL, &topLevel); + RhiWidget nativeRhiWidget(QPlatformBackingStoreRhiConfig::OpenGL, &topLevel); + nativeRhiWidget.setAttribute(Qt::WA_NativeWindow); + topLevel.show(); + QVERIFY(QTest::qWaitForWindowExposed(&topLevel)); + + QCOMPARE(nativeRhiWidget.windowHandle()->surfaceType(), QSurface::OpenGLSurface); + QVERIFY(QWidgetPrivate::get(&nativeRhiWidget)->usesRhiFlush); + QVERIFY(QWidgetPrivate::get(&nativeRhiWidget)->rhi()); + QVERIFY(!QWidgetPrivate::get(&rhiWidget)->usesRhiFlush); + QVERIFY(QWidgetPrivate::get(&rhiWidget)->rhi()); + QCOMPARE(topLevel.windowHandle()->surfaceType(), QSurface::OpenGLSurface); + QVERIFY(QWidgetPrivate::get(&topLevel)->usesRhiFlush); + QVERIFY(QWidgetPrivate::get(&topLevel)->rhi()); + } + + { + // Reparenting into a window that already matches the required + // surface type should still mark the parent as flushing with RHI. + QWidget topLevel; + + RhiWidget rhiWidget(QPlatformBackingStoreRhiConfig::Null); + rhiWidget.show(); + QVERIFY(QTest::qWaitForWindowExposed(&rhiWidget)); + QVERIFY(QWidgetPrivate::get(&rhiWidget)->usesRhiFlush); + QVERIFY(QWidgetPrivate::get(&rhiWidget)->rhi()); + + topLevel.show(); + QVERIFY(QTest::qWaitForWindowExposed(&topLevel)); + rhiWidget.setParent(&topLevel); + QVERIFY(QWidgetPrivate::get(&topLevel)->usesRhiFlush); + QVERIFY(QWidgetPrivate::get(&topLevel)->rhi()); + } + + { + // Non-native RHI child of native child enables RHI for native child, + // but does not prevent top level from flushing with RHI. + QWidget topLevel; + QWidget nativeChild(&topLevel); + nativeChild.setAttribute(Qt::WA_NativeWindow); + RhiWidget rhiGranchild(QPlatformBackingStoreRhiConfig::OpenGL, &nativeChild); + RhiWidget rhiChild(QPlatformBackingStoreRhiConfig::OpenGL, &topLevel); + topLevel.show(); + QVERIFY(QTest::qWaitForWindowExposed(&topLevel)); + + QCOMPARE(nativeChild.windowHandle()->surfaceType(), QSurface::OpenGLSurface); + QVERIFY(QWidgetPrivate::get(&nativeChild)->usesRhiFlush); + QVERIFY(QWidgetPrivate::get(&nativeChild)->rhi()); + QVERIFY(!QWidgetPrivate::get(&rhiGranchild)->usesRhiFlush); + QVERIFY(QWidgetPrivate::get(&rhiGranchild)->rhi()); + QCOMPARE(topLevel.windowHandle()->surfaceType(), QSurface::OpenGLSurface); + QVERIFY(QWidgetPrivate::get(&topLevel)->usesRhiFlush); + QVERIFY(QWidgetPrivate::get(&topLevel)->rhi()); + QVERIFY(!QWidgetPrivate::get(&rhiChild)->usesRhiFlush); + QVERIFY(QWidgetPrivate::get(&rhiChild)->rhi()); + } + +#if QT_CONFIG(metal) + QRhiMetalInitParams metalParams; + if (QRhi::probe(QRhi::Metal, &metalParams)) { + // Native RHI childen allows mixing RHI backends + QWidget topLevel; + RhiWidget openglWidget(QPlatformBackingStoreRhiConfig::OpenGL, &topLevel); + openglWidget.setAttribute(Qt::WA_NativeWindow); + RhiWidget metalWidget(QPlatformBackingStoreRhiConfig::Metal, &topLevel); + metalWidget.setAttribute(Qt::WA_NativeWindow); + topLevel.show(); + QVERIFY(QTest::qWaitForWindowExposed(&topLevel)); + + QCOMPARE(topLevel.windowHandle()->surfaceType(), defaultSurfaceType); + QVERIFY(!QWidgetPrivate::get(&topLevel)->usesRhiFlush); + if (!usesRhiBackingStore) + QVERIFY(!QWidgetPrivate::get(&topLevel)->rhi()); + + QCOMPARE(openglWidget.windowHandle()->surfaceType(), QSurface::OpenGLSurface); + QVERIFY(QWidgetPrivate::get(&openglWidget)->usesRhiFlush); + QVERIFY(QWidgetPrivate::get(&openglWidget)->rhi()); + + QCOMPARE(metalWidget.windowHandle()->surfaceType(), QSurface::MetalSurface); + QVERIFY(QWidgetPrivate::get(&metalWidget)->usesRhiFlush); + QVERIFY(QWidgetPrivate::get(&metalWidget)->rhi()); + + QVERIFY(QWidgetPrivate::get(&openglWidget)->rhi() != QWidgetPrivate::get(&metalWidget)->rhi()); + } +#endif // QT_CONFIG(metal) + +#endif // QT_CONFIG(opengl) +} + #if defined(QT_BUILD_INTERNAL) /*! diff --git a/tests/auto/widgets/widgets/qopenglwidget/tst_qopenglwidget.cpp b/tests/auto/widgets/widgets/qopenglwidget/tst_qopenglwidget.cpp index 773ccd894ca..51f898c9537 100644 --- a/tests/auto/widgets/widgets/qopenglwidget/tst_qopenglwidget.cpp +++ b/tests/auto/widgets/widgets/qopenglwidget/tst_qopenglwidget.cpp @@ -203,7 +203,7 @@ void tst_QOpenGLWidget::deviceLoss() w->resize(640, 480); w->show(); - auto rhi = w->backingStore()->handle()->rhi(); + auto rhi = w->backingStore()->handle()->rhi(w->windowHandle()); QNativeInterface::QEGLContext *rhiContext = nullptr; if (rhi->backend() == QRhi::OpenGLES2) { auto rhiHandles = static_cast(rhi->nativeHandles());