summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTor Arne Vestbø <[email protected]>2024-02-20 13:54:47 +0100
committerTor Arne Vestbø <[email protected]>2024-06-01 00:24:34 +0200
commiteb4cb719257d3b57cd801273d4011579d8c81714 (patch)
tree4cb2128bd66b97c59ea63b0034229aba6cccba26
parente4ef0f03e6f1fddc397980fd7fbf6f6b829f16d9 (diff)
widgets: Use per-surface-format RHI support and compositor
The RHI support and compositor in QPlatformBackingStore were tied to the surface format of the top level window owning the backing store. This meant that inserting an RHI-enabled widget (QRhiWidget, QOpenGLWidget, QQuickWidget, QWebEngineView) into the widget hierarchy required recreating the top level window with a matching surface format that could support the RHI composition. It also meant that we could not have two RHI enabled widgets with different surface format requirements (Metal and OpenGL for example) in the same top level widget hierarchy. The recreation of the window had various visual side effects, such as temporarily switching out of full screen state, or the widget rendering a frame of black, as well as more serious problems such as not correctly restoring the window geometry. In addition, if client code had pulled out the winId() of the window, and did not invalidate these references on window destruction via QEvent::WinIdChange or QEvent::PlatformSurface, the client would reference stale window handles. Although this is a programming error (QWidget::winId() specifically mentions this requirement), we should avoid recreation if we can. We were already supporting flushing the backingstore to individual native child widgets, but always did so via a single RHI managed by the platform backingstore. By expanding QPlatformBackingStore to keep one set of RHI support and compositor per surface format, we can refine the logic in QWidget and QWidgetRepaintManager to not require recreating the top level. Native child widgets are then flushed independently, including any RHI textures and raster content that overlaps with the widget. We still assume that a single RHI support and compositor can be be used for multiple windows, as long as those windows have the same surface format. In the future, if needed, we can refine this to use one set per surface format e.g. Fixes: QTBUG-119221 Fixes: QTBUG-121181 Fixes: QTBUG-120096 Task-number: QTBUG-115652 Task-number: QTBUG-108344 Task-number: QTBUG-113557 Task-number: QTBUG-119309 Change-Id: I2635ed3d20c2fb76eab3b8130007dd656a0b93e5 Reviewed-by: Laszlo Agocs <[email protected]>
-rw-r--r--src/gui/painting/qplatformbackingstore.cpp65
-rw-r--r--src/gui/painting/qplatformbackingstore.h9
-rw-r--r--src/gui/painting/qrhibackingstore.cpp23
-rw-r--r--src/opengl/qopenglcompositorbackingstore.cpp12
-rw-r--r--src/widgets/kernel/qwidget.cpp118
-rw-r--r--src/widgets/kernel/qwidgetrepaintmanager.cpp79
-rw-r--r--src/widgets/kernel/qwidgetrepaintmanager_p.h4
-rw-r--r--src/widgets/kernel/qwidgetwindow.cpp4
-rw-r--r--tests/auto/widgets/kernel/qwidgetrepaintmanager/tst_qwidgetrepaintmanager.cpp217
-rw-r--r--tests/auto/widgets/widgets/qopenglwidget/tst_qopenglwidget.cpp2
10 files changed, 384 insertions, 149 deletions
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 <QtCore/private/qobject_p.h>
+#include <unordered_map>
+
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<QSurface::SurfaceType, SurfaceSupport> 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 &region, 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 &region
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<const QWidget *>(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 &region, 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 &region, 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(&regularWidget));
+ QVERIFY(!QWidgetPrivate::get(&regularWidget)->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(&regularWidget)->rhi();
+ else
+ QVERIFY(!QWidgetPrivate::get(&regularWidget)->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<const QRhiGles2NativeHandles *>(rhi->nativeHandles());