From d7a8c65ffd5acec9940f37e5f260584f7fbcfb0c Mon Sep 17 00:00:00 2001 From: Jianzhong Zhao Date: Mon, 1 Jul 2024 10:38:31 -0700 Subject: [PATCH] Revert "Imported fixes from Qt to remediate issues with RHI." This reverts commit 6d86ab7813629464b01c19e32ce9490225e288eb. --- src/gui/painting/qbackingstorerhisupport.cpp | 7 - src/gui/painting/qplatformbackingstore.cpp | 84 +++----- src/gui/painting/qplatformbackingstore.h | 11 +- src/gui/painting/qrhibackingstore.cpp | 20 +- src/opengl/qopenglcompositorbackingstore.cpp | 12 +- src/openglwidgets/qopenglwidget.cpp | 13 +- src/widgets/kernel/qwidget.cpp | 123 ++++-------- src/widgets/kernel/qwidget_p.h | 4 - src/widgets/kernel/qwidgetrepaintmanager.cpp | 81 +++++--- src/widgets/kernel/qwidgetrepaintmanager_p.h | 4 + src/widgets/kernel/qwidgetwindow.cpp | 4 +- .../tst_qwidgetrepaintmanager.cpp | 184 ------------------ 12 files changed, 157 insertions(+), 390 deletions(-) diff --git a/src/gui/painting/qbackingstorerhisupport.cpp b/src/gui/painting/qbackingstorerhisupport.cpp index ebf71f8073f8..530de60e88b9 100644 --- a/src/gui/painting/qbackingstorerhisupport.cpp +++ b/src/gui/painting/qbackingstorerhisupport.cpp @@ -24,8 +24,6 @@ #include #endif -#include - QT_BEGIN_NAMESPACE Q_DECLARE_LOGGING_CATEGORY(lcQpaBackingStore) @@ -68,11 +66,6 @@ bool QBackingStoreRhiSupport::create() QOffscreenSurface *surface = nullptr; QRhi::Flags flags; - if (m_config.api() == QPlatformBackingStoreRhiConfig::Null) { - QRhiNullInitParams params; - rhi = QRhi::create(QRhi::Null, ¶ms, flags); - } - #if QT_CONFIG(opengl) if (!rhi && m_config.api() == QPlatformBackingStoreRhiConfig::OpenGL) { surface = QRhiGles2InitParams::newFallbackSurface(m_format); diff --git a/src/gui/painting/qplatformbackingstore.cpp b/src/gui/painting/qplatformbackingstore.cpp index 189f3a329500..82e7778b86a4 100644 --- a/src/gui/painting/qplatformbackingstore.cpp +++ b/src/gui/painting/qplatformbackingstore.cpp @@ -10,8 +10,6 @@ #include -#include - QT_BEGIN_NAMESPACE Q_LOGGING_CATEGORY(lcQpaBackingStore, "qt.qpa.backingstore", QtWarningMsg); @@ -28,14 +26,11 @@ class QPlatformBackingStorePrivate QWindow *window; QBackingStore *backingStore; - 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; + // 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 QBackingstoreTextureInfo @@ -215,12 +210,8 @@ QPlatformBackingStore::FlushResult QPlatformBackingStore::rhiFlush(QWindow *wind QPlatformTextureList *textures, bool 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); + return d_ptr->compositor.flush(this, d_ptr->rhiSupport.rhi(), d_ptr->rhiSupport.swapChainForWindow(window), + window, sourceDevicePixelRatio, region, offset, textures, translucentBackground); } /*! @@ -270,10 +261,7 @@ QRhiTexture *QPlatformBackingStore::toTexture(QRhiResourceUpdateBatch *resourceU const QRegion &dirtyRegion, TextureFlags *flags) const { - auto &surfaceSupport = d_ptr->surfaceSupport[window()->surfaceType()]; - return surfaceSupport.compositor.toTexture(this, - surfaceSupport.rhiSupport.rhi(), resourceUpdates, - dirtyRegion, flags); + return d_ptr->compositor.toTexture(this, d_ptr->rhiSupport.rhi(), resourceUpdates, dirtyRegion, flags); } /*! @@ -368,56 +356,32 @@ bool QPlatformBackingStore::scroll(const QRegion &area, int dx, int dy) return false; } -void QPlatformBackingStore::createRhi(QWindow *window, QPlatformBackingStoreRhiConfig config) -{ - auto &support = d_ptr->surfaceSupport[window->surfaceType()]; - if (!support.rhiSupport.rhi()) { - qCDebug(lcQpaBackingStore) << "Setting up RHI support in" << this - << "for" << window << "with" << window->surfaceType() - << "and requested API" << config.api(); - if (config.api() == QPlatformBackingStoreRhiConfig::Null) { - // Auto detect based on window's surface type - switch (window->surfaceType()) { - case QSurface::OpenGLSurface: - config.setApi(QPlatformBackingStoreRhiConfig::OpenGL); - break; - case QSurface::MetalSurface: - config.setApi(QPlatformBackingStoreRhiConfig::Metal); - break; - case QSurface::Direct3DSurface: - config.setApi(QPlatformBackingStoreRhiConfig::D3D11); - break; - case QSurface::VulkanSurface: - config.setApi(QPlatformBackingStoreRhiConfig::Vulkan); - break; - default: - ; // Default null-configuration - } - } - support.rhiSupport.setConfig(config); - support.rhiSupport.setWindow(window); - support.rhiSupport.setFormat(window->format()); - support.rhiSupport.create(); - } +void QPlatformBackingStore::setRhiConfig(const 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(); } -QRhi *QPlatformBackingStore::rhi(QWindow *window) const +QRhi *QPlatformBackingStore::rhi() const { // Returning null is valid, and means this is not a QRhi-capable backingstore. - return d_ptr->surfaceSupport[window->surfaceType()].rhiSupport.rhi(); + return d_ptr->rhiSupport.rhi(); } -void QPlatformBackingStore::graphicsDeviceReportedLost(QWindow *window) +void QPlatformBackingStore::graphicsDeviceReportedLost() { - auto &surfaceSupport = d_ptr->surfaceSupport[window->surfaceType()]; - if (!surfaceSupport.rhiSupport.rhi()) + if (!d_ptr->rhiSupport.rhi()) return; qWarning("Rhi backingstore: graphics device lost, attempting to reinitialize"); - surfaceSupport.compositor.reset(); - surfaceSupport.rhiSupport.reset(); - surfaceSupport.rhiSupport.create(); - if (!surfaceSupport.rhiSupport.rhi()) + d_ptr->rhiSupport.reset(); + d_ptr->rhiSupport.create(); + if (!d_ptr->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 235af228357f..40453574aaf1 100644 --- a/src/gui/painting/qplatformbackingstore.h +++ b/src/gui/painting/qplatformbackingstore.h @@ -40,8 +40,6 @@ class QRhiSwapChain; struct Q_GUI_EXPORT QPlatformBackingStoreRhiConfig { - Q_GADGET -public: enum Api { OpenGL, Metal, @@ -49,7 +47,6 @@ struct Q_GUI_EXPORT QPlatformBackingStoreRhiConfig D3D11, Null }; - Q_ENUM(Api) QPlatformBackingStoreRhiConfig() : m_enable(false) @@ -173,11 +170,11 @@ class Q_GUI_EXPORT QPlatformBackingStore virtual void beginPaint(const QRegion &); virtual void endPaint(); - void createRhi(QWindow *window, QPlatformBackingStoreRhiConfig config = {}); - QRhi *rhi(QWindow *window) const; - + void setRhiConfig(const QPlatformBackingStoreRhiConfig &config); + QRhi *rhi() const; + QRhiSwapChain *rhiSwapChain() const; void surfaceAboutToBeDestroyed(); - void graphicsDeviceReportedLost(QWindow *window); + void graphicsDeviceReportedLost(); private: QPlatformBackingStorePrivate *d_ptr; diff --git a/src/gui/painting/qrhibackingstore.cpp b/src/gui/painting/qrhibackingstore.cpp index 80528f9536e8..fd7045e3f630 100644 --- a/src/gui/painting/qrhibackingstore.cpp +++ b/src/gui/painting/qrhibackingstore.cpp @@ -19,8 +19,24 @@ void QRhiBackingStore::flush(QWindow *window, const QRegion ®ion, const QPoin Q_UNUSED(region); Q_UNUSED(offset); - if (!rhi(window)) - createRhi(window); + if (window != this->window()) + return; + + if (!rhi()) { + QPlatformBackingStoreRhiConfig rhiConfig; + switch (window->surfaceType()) { + case QSurface::OpenGLSurface: + rhiConfig.setApi(QPlatformBackingStoreRhiConfig::OpenGL); + break; + case QSurface::MetalSurface: + rhiConfig.setApi(QPlatformBackingStoreRhiConfig::Metal); + break; + default: + Q_UNREACHABLE(); + } + rhiConfig.setEnabled(true); + setRhiConfig(rhiConfig); + } static QPlatformTextureList emptyTextureList; bool translucentBackground = m_image.hasAlphaChannel(); diff --git a/src/opengl/qopenglcompositorbackingstore.cpp b/src/opengl/qopenglcompositorbackingstore.cpp index 7ae788dab5a0..a5cc391e48f7 100644 --- a/src/opengl/qopenglcompositorbackingstore.cpp +++ b/src/opengl/qopenglcompositorbackingstore.cpp @@ -141,7 +141,11 @@ void QOpenGLCompositorBackingStore::flush(QWindow *window, const QRegion ®ion Q_UNUSED(region); Q_UNUSED(offset); - m_rhi = rhi(window); + m_rhi = rhi(); + if (!m_rhi) { + setRhiConfig(QPlatformBackingStoreRhiConfig(QPlatformBackingStoreRhiConfig::OpenGL)); + m_rhi = rhi(); + } Q_ASSERT(m_rhi); QOpenGLCompositor *compositor = QOpenGLCompositor::instance(); @@ -177,7 +181,11 @@ QPlatformBackingStore::FlushResult QOpenGLCompositorBackingStore::rhiFlush(QWind Q_UNUSED(translucentBackground); Q_UNUSED(sourceDevicePixelRatio); - m_rhi = rhi(window); + m_rhi = rhi(); + if (!m_rhi) { + setRhiConfig(QPlatformBackingStoreRhiConfig(QPlatformBackingStoreRhiConfig::OpenGL)); + m_rhi = rhi(); + } Q_ASSERT(m_rhi); QOpenGLCompositor *compositor = QOpenGLCompositor::instance(); diff --git a/src/openglwidgets/qopenglwidget.cpp b/src/openglwidgets/qopenglwidget.cpp index 49f54b56bfbf..cbc0b751e857 100644 --- a/src/openglwidgets/qopenglwidget.cpp +++ b/src/openglwidgets/qopenglwidget.cpp @@ -780,7 +780,9 @@ void QOpenGLWidgetPrivate::ensureRhiDependentResources() { Q_Q(QOpenGLWidget); - QRhi *rhi = QWidgetPrivate::rhi(); + QRhi *rhi = nullptr; + if (QWidgetRepaintManager *repaintManager = QWidgetPrivate::get(q->window())->maybeRepaintManager()) + rhi = repaintManager->rhi(); // If there is no rhi, because we are completely offscreen, then there's no wrapperTexture either if (rhi && rhi->backend() == QRhi::OpenGLES2) { @@ -826,6 +828,7 @@ void QOpenGLWidgetPrivate::initialize() // If no global shared context get our toplevel's context with which we // will share in order to make the texture usable by the underlying window's backingstore. QWidget *tlw = q->window(); + QWidgetPrivate *tlwd = get(tlw); // Do not include the sample count. Requesting a multisampled context is not necessary // since we render into an FBO, never to an actual surface. What's more, attempting to @@ -834,7 +837,9 @@ void QOpenGLWidgetPrivate::initialize() requestedSamples = requestedFormat.samples(); requestedFormat.setSamples(0); - QRhi *rhi = QWidgetPrivate::rhi(); + QRhi *rhi = nullptr; + if (QWidgetRepaintManager *repaintManager = tlwd->maybeRepaintManager()) + rhi = repaintManager->rhi(); // Could be that something else already initialized the window with some // other graphics API for the QRhi, that's not good. @@ -1682,8 +1687,8 @@ bool QOpenGLWidget::event(QEvent *e) if (!QCoreApplication::testAttribute(Qt::AA_ShareOpenGLContexts)) d->reset(); } - if (d->rhi()) { - if (!d->initialized && !size().isEmpty()) { + if (QWidgetRepaintManager *repaintManager = QWidgetPrivate::get(window())->maybeRepaintManager()) { + if (!d->initialized && !size().isEmpty() && repaintManager->rhi()) { d->initialize(); if (d->initialized) { d->recreateFbos(); diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp index d609e5900742..83e072a941d7 100644 --- a/src/widgets/kernel/qwidget.cpp +++ b/src/widgets/kernel/qwidget.cpp @@ -1026,34 +1026,6 @@ void QWidgetPrivate::createRecursively() } } -QRhi *QWidgetPrivate::rhi() const -{ - Q_Q(const QWidget); - if (auto *backingStore = q->backingStore()) { - auto *window = windowHandle(WindowHandleMode::Closest); - return backingStore->handle()->rhi(window); - } else { - return nullptr; - } -} - -/*! - \internal - Returns the closest parent widget that has a QWindow window handle - - \note This behavior is different from nativeParentWidget(), which - returns the closest parent that has a QWindow window handle with - a created QPlatformWindow, and hence native window (winId). -*/ -QWidget *QWidgetPrivate::closestParentWidgetWithWindowHandle() const -{ - Q_Q(const QWidget); - QWidget *parent = q->parentWidget(); - while (parent && !parent->windowHandle()) - parent = parent->parentWidget(); - return parent; -} - QWindow *QWidgetPrivate::windowHandle(WindowHandleMode mode) const { if (mode == WindowHandleMode::Direct || mode == WindowHandleMode::Closest) { @@ -1113,12 +1085,8 @@ 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)) { - // Native child widgets will not trigger RHI for the top level, - // but will still flush the native child using RHI. - if (!childWidget->testAttribute(Qt::WA_NativeWindow)) - return true; - } + if (q_evaluateRhiConfigRecursive(childWidget, outConfig, outType)) + return true; } } return false; @@ -1363,19 +1331,19 @@ void QWidgetPrivate::create() QBackingStore *store = q->backingStore(); usesRhiFlush = false; - 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); + 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); } + } else if (win->handle()) { + usesRhiFlush = q_evaluateRhiConfig(q, nullptr, nullptr); } setWindowModified_helper(); @@ -10705,7 +10673,6 @@ void QWidget::setParent(QWidget *parent, Qt::WindowFlags f) const bool resized = testAttribute(Qt::WA_Resized); const bool wasCreated = testAttribute(Qt::WA_WState_Created); QWidget *oldtlw = window(); - QWidget *oldParentWithWindow = d->closestParentWidgetWithWindowHandle(); if (f & Qt::Window) // Frame geometry likely changes, refresh. d->data.fstrut_dirty = true; @@ -10844,45 +10811,27 @@ void QWidget::setParent(QWidget *parent, Qt::WindowFlags f) if (d->extra && d->extra->hasWindowContainer) QWindowContainer::parentWasChanged(this); - 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; + QWidget *newtlw = window(); + if (oldtlw != newtlw) { QSurface::SurfaceType surfaceType = QSurface::RasterSurface; - - // 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 + // Only evaluate the reparented subtree. While it might be tempting to + // do it on newtlw instead, the performance implications of that are // problematic when it comes to large widget trees. - 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); - } + if (q_evaluateRhiConfig(this, nullptr, &surfaceType)) { + newtlw->d_func()->usesRhiFlush = true; + bool recreate = false; + if (QWindow *w = newtlw->windowHandle()) { + if (w->surfaceType() != surfaceType) + 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) { + newtlw->destroy(); + newtlw->create(); } } } @@ -12289,10 +12238,8 @@ QBackingStore *QWidget::backingStore() const if (extra && extra->backingStore) return extra->backingStore; - if (!isWindow()) - return window()->backingStore(); - - return nullptr; + QWidgetRepaintManager *repaintManager = d->maybeRepaintManager(); + return repaintManager ? repaintManager->backingStore() : nullptr; } void QWidgetPrivate::getLayoutItemMargins(int *left, int *top, int *right, int *bottom) const diff --git a/src/widgets/kernel/qwidget_p.h b/src/widgets/kernel/qwidget_p.h index 74fcfa4f6ee1..cf0618bca614 100644 --- a/src/widgets/kernel/qwidget_p.h +++ b/src/widgets/kernel/qwidget_p.h @@ -220,8 +220,6 @@ class Q_WIDGETS_EXPORT QWidgetPrivate : public QObjectPrivate void setSharedPainter(QPainter *painter); QWidgetRepaintManager *maybeRepaintManager() const; - QRhi *rhi() const; - enum class WindowHandleMode { Direct, Closest, @@ -635,8 +633,6 @@ class Q_WIDGETS_EXPORT QWidgetPrivate : public QObjectPrivate std::string flagsForDumping() const override; - QWidget *closestParentWidgetWithWindowHandle() const; - // Variables. // Regular pointers (keep them together to avoid gaps on 64 bit architectures). std::unique_ptr extra; diff --git a/src/widgets/kernel/qwidgetrepaintmanager.cpp b/src/widgets/kernel/qwidgetrepaintmanager.cpp index 02b50bd9950c..1a0fda859fba 100644 --- a/src/widgets/kernel/qwidgetrepaintmanager.cpp +++ b/src/widgets/kernel/qwidgetrepaintmanager.cpp @@ -1012,13 +1012,11 @@ 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 (window->type() == Qt::ForeignWindow) - return; + if (QWindow *widgetWindow = widget->windowHandle()) { + if (widgetWindow->type() == Qt::ForeignWindow) + return; + } static bool fpsDebug = qEnvironmentVariableIntValue("QT_DEBUG_FPS"); if (fpsDebug) { @@ -1035,48 +1033,66 @@ void QWidgetRepaintManager::flush(QWidget *widget, const QRegion ®ion, QPlatf if (widget != tlw) offset += widget->mapTo(tlw, QPoint()); - // 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) { + // 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(); if (!widgetTextures) widgetTextures = qt_dummy_platformTextureList; - // We only need to let the widget sub-hierarchy that - // we are flushing know that we're compositing. - auto *widgetPrivate = QWidgetPrivate::get(widget); - qt_window_private(widgetPrivate->windowHandle())->compositing = true; - widgetPrivate->sendComposeStatus(widget, false); - + qt_window_private(tlw->windowHandle())->compositing = true; + QWidgetPrivate *widgetWindowPrivate = widget->window()->d_func(); + widgetWindowPrivate->sendComposeStatus(widget->window(), 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(window, + flushResult = store->handle()->rhiFlush(widget->windowHandle(), widget->devicePixelRatio(), region, offset, widgetTextures, translucentBackground); - - widgetPrivate->sendComposeStatus(widget, true); - + widgetWindowPrivate->sendComposeStatus(widget->window(), true); if (flushResult == QPlatformBackingStore::FlushFailedDueToLostDevice) { - store->handle()->graphicsDeviceReportedLost(window); + store->handle()->graphicsDeviceReportedLost(); widget->update(); } } else { - store->flush(region, window, offset); + qCInfo(lcWidgetPainting) << "Flushing" << region << "of" << widget; + store->flush(region, widget->windowHandle(), offset); } } @@ -1289,6 +1305,11 @@ 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 13190a0bb0e5..43789746101c 100644 --- a/src/widgets/kernel/qwidgetrepaintmanager_p.h +++ b/src/widgets/kernel/qwidgetrepaintmanager_p.h @@ -26,6 +26,8 @@ QT_BEGIN_NAMESPACE class QPlatformTextureList; class QPlatformTextureListWatcher; class QWidgetRepaintManager; +class QRhi; +class QRhiSwapChain; class Q_WIDGETS_EXPORT QWidgetRepaintManager { @@ -70,6 +72,8 @@ class Q_WIDGETS_EXPORT QWidgetRepaintManager 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 78ff190f9567..76c77e6e27ea 100644 --- a/src/widgets/kernel/qwidgetwindow.cpp +++ b/src/widgets/kernel/qwidgetwindow.cpp @@ -124,8 +124,8 @@ QWidgetWindow::QWidgetWindow(QWidget *widget) updateObjectName(); if (!QCoreApplication::testAttribute(Qt::AA_ForceRasterWidgets)) { QSurface::SurfaceType type = QSurface::RasterSurface; - if (q_evaluateRhiConfig(m_widget, nullptr, &type)) - setSurfaceType(type); + 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 bce6c92f01b1..f53d5aeb0508 100644 --- a/tests/auto/widgets/kernel/qwidgetrepaintmanager/tst_qwidgetrepaintmanager.cpp +++ b/tests/auto/widgets/kernel/qwidgetrepaintmanager/tst_qwidgetrepaintmanager.cpp @@ -12,8 +12,6 @@ #include #include #include -#include -#include //#define MANUAL_DEBUG @@ -256,8 +254,6 @@ private slots: void opaqueChildren(); void staticContents(); void scroll(); - void evaluateRhi(); - #if defined(QT_BUILD_INTERNAL) void scrollWithOverlap(); void overlappedRegion(); @@ -484,186 +480,6 @@ void tst_QWidgetRepaintManager::scroll() } -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); - }); - - { - // Plain QWidget doesn't enable RHI - QWidget regularWidget; - regularWidget.show(); - QVERIFY(QTest::qWaitForWindowExposed(®ularWidget)); - QVERIFY(!QWidgetPrivate::get(®ularWidget)->usesRhiFlush); - 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(), QSurface::RasterSurface); - QVERIFY(!QWidgetPrivate::get(&topLevel)->usesRhiFlush); - 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(), QSurface::RasterSurface); - QVERIFY(!QWidgetPrivate::get(&topLevel)->usesRhiFlush); - 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()); - } - - { - // 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 defined(Q_OS_APPLE) - { - // 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(), QSurface::RasterSurface); - QVERIFY(!QWidgetPrivate::get(&topLevel)->usesRhiFlush); - 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 // Q_OS_APPLE - -#endif // QT_CONFIG(opengl) -} - #if defined(QT_BUILD_INTERNAL) /*!