summaryrefslogtreecommitdiffstats
path: root/src/widgets/kernel/qwidget.cpp
diff options
context:
space:
mode:
authorLaszlo Agocs <laszlo.agocs@qt.io>2021-10-25 15:21:31 +0200
committerTor Arne Vestbø <tor.arne.vestbo@qt.io>2022-03-11 21:25:00 +0100
commit68a4c5da9a080101cccd8a3b2edb1c908da0ca8e (patch)
treee89a8c02539147d30cb7529441d1a9e055163760 /src/widgets/kernel/qwidget.cpp
parent8ccf1080fcb131940939369271e6828b178b8e9e (diff)
Compose render-to-texture widgets through QRhi
QPlatformTextureList holds a QRhiTexture instead of GLuint. A QPlatformBackingStore now optionally can own a QRhi and a QRhiSwapChain for the associated window. Non-GL rendering must use this QRhi everywhere, whereas GL (QOpenGLWidget) can choose to still rely on resource sharing between contexts. A widget tells that it wants QRhi and the desired configuration in a new virtual function in QWidgetPrivate returning a QPlatformBackingStoreRhiConfig. This is evaluated (among a top-level's all children) upon create() before creating the repaint manager and the QWidgetWindow. In QOpenGLWidget what do request is obvious: it will request an OpenGL-based QRhi. QQuickWidget (or a potential future QRhiWidget) will be more interesting: it needs to honor the standard Qt Quick env.vars. and QQuickWindow APIs (or, in whatever way the user configured the QRhiWidget), and so will set up the config struct accordingly. In addition, the rhiconfig and surface type is (re)evaluated when (re)parenting a widget to a new tlw. If needed, this will now trigger a destroy - create on the tlw. This should be be safe to do in setParent. When multiple child widgets report an enabled rhiconfig, the first one (the first child encountered) wins. So e.g. attempting to have a QOpenGLWidget and a Vulkan-based QQuickWidget in the same top-level window will fail one of the widgets (it likely won't render). RasterGLSurface is no longer used by widgets. Rather, the appropriate surface type is chosen. The rhi support in the backingstore is usable without widgets as well. To make rhiFlush() functional, one needs to call setRhiConfig() after creating the QBackingStore. (like QWidget does to top-level windows) Most of the QT_NO_OPENGL ifdefs are eliminated all over the place. Everything with QRhi is unconditional code at compile time, except the actual initialization. Having to plumb the widget tlw's shareContext (or, now, the QRhi) through QWindowPrivate is no longer needed. The old approach does not scale: to implement composeAndFlush (now rhiFlush) we need more than just a QRhi object, and this way we no longer pollute everything starting from the widget level (QWidget's topextra -> QWidgetWindow -> QWindowPrivate) just to send data around. The BackingStoreOpenGLSupport interface and the QtGui - QtOpenGL split is all gone. Instead, there is a QBackingStoreDefaultCompositor in QtGui which is what the default implementations of composeAndFlush and toTexture call. (overriding composeAndFlush and co. f.ex. in eglfs should continue working mostly as-is, apart from adapting to the texture list changes and getting the native OpenGL texture id out of the QRhiTexture) As QQuickWidget is way too complicated to just port as-is, an rhi manual test (rhiwidget) is introduced as a first step, in ordewr to exercise a simple, custom render-to-texture widget that does something using a (not necessarily OpenGL-backed) QRhi and acts as fully functional QWidget (modeled after QOpenGLWidget). This can also form the foundation of a potential future QRhiWidget. It is also possible to force the QRhi-based flushing always, regardless of the presence of render-to-texture widgets. To exercise this, set the env.var. QT_WIDGETS_RHI=1. This picks a platform-specific default, and can be overridden with QT_WIDGETS_RHI_BACKEND. (in sync with Qt Quick) This can eventually be extended to query the platform plugin as well to check if the platform plugin prefers to always do flushes with a 3D API. QOpenGLWidget should work like before from the user's perspective, while internally it has to do some things differently to play nice and prevent regressions with the new rendering architecture. To exercise this better, the qopenglwidget example gets a new tab-based view (that could perhaps replace the example's main window later on?). The openglwidget manual test is made compatible with Qt 6, and gets a counterpart in form of the dockedopenglwidget manual test, which is a modified version of the cube example that features dock widgets. This is relevant in particular because render-to-texture widgets within a QDockWidget has its own specific quirks, with logic taking this into account, hence testing is essential. For existing applications there are two important consequences with this patch in place: - Once the rhi-based composition is enabled, it stays active for the lifetime of the top-level window. - Dynamically creating and parenting the first render-to-texture widget to an already created tlw will destroy and recreate the tlw (and the underlying window). The visible effects of this depend on the platform. (e.g. the window may disappear and reappear on some, whereas with other windowing systems it is not noticeable at all - this is not really different from similar situtions with reparenting or when moving windows between screens, so should be acceptable in practice) - On iOS raster windows are flushed with Metal (and rhi) from now on (previously this was through OpenGL by making flush() call composeAndFlush(). Change-Id: Id05bd0f7a26fa845f8b7ad8eedda3b0e78ab7a4e Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
Diffstat (limited to 'src/widgets/kernel/qwidget.cpp')
-rw-r--r--src/widgets/kernel/qwidget.cpp141
1 files changed, 80 insertions, 61 deletions
diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp
index 0a0957bf5e..1527ec679f 100644
--- a/src/widgets/kernel/qwidget.cpp
+++ b/src/widgets/kernel/qwidget.cpp
@@ -77,9 +77,6 @@
#include "qscopeguard.h"
#include <QtGui/private/qhighdpiscaling_p.h>
#include <QtGui/qinputmethod.h>
-#include <QtGui/qopenglcontext.h>
-#include <QtGui/private/qopenglcontext_p.h>
-#include <QtGui/qoffscreensurface.h>
#if QT_CONFIG(graphicseffect)
#include <private/qgraphicseffect_p.h>
@@ -178,10 +175,8 @@ QWidgetPrivate::QWidgetPrivate(int version)
#ifndef QT_NO_IM
, inheritsInputMethodHints(0)
#endif
-#ifndef QT_NO_OPENGL
, renderToTextureReallyDirty(1)
- , renderToTextureComposeActive(0)
-#endif
+ , usesRhiFlush(0)
, childrenHiddenByWState(0)
, childrenShownByExpose(0)
#if defined(Q_OS_WIN)
@@ -1114,6 +1109,47 @@ QScreen *QWidgetPrivate::associatedScreen() const
return nullptr;
}
+// finds the first rhiconfig in the hierarchy that has enable==true
+static bool q_evaluateRhiConfigRecursive(const QWidget *w, QPlatformBackingStoreRhiConfig *outConfig, QSurface::SurfaceType *outType)
+{
+ QPlatformBackingStoreRhiConfig config = QWidgetPrivate::get(w)->rhiConfig();
+ if (config.isEnabled()) {
+ if (outConfig)
+ *outConfig = config;
+ if (outType)
+ *outType = QBackingStoreRhiSupport::surfaceTypeForConfig(config);
+ return true;
+ }
+ QObjectList children = w->children();
+ for (int i = 0; i < children.size(); i++) {
+ if (children.at(i)->isWidgetType()) {
+ const QWidget *childWidget = qobject_cast<const QWidget *>(children.at(i));
+ if (childWidget) {
+ if (q_evaluateRhiConfigRecursive(childWidget, outConfig, outType))
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+// First tries q_evaluateRhiConfigRecursive, then if that did not indicate that rhi is wanted,
+// then checks env.vars or something else to see if we need to force using rhi-based composition.
+bool q_evaluateRhiConfig(const QWidget *w, QPlatformBackingStoreRhiConfig *outConfig, QSurface::SurfaceType *outType)
+{
+ if (q_evaluateRhiConfigRecursive(w, outConfig, outType)) {
+ qCDebug(lcWidgetPainting) << "Tree with root" << w << "evaluates to flushing with QRhi";
+ return true;
+ }
+
+ if (QBackingStoreRhiSupport::checkForceRhi(outConfig, outType)) {
+ qCDebug(lcWidgetPainting) << "Tree with root" << w << "evaluated to forced flushing with QRhi";
+ return true;
+ }
+
+ return false;
+}
+
// ### fixme: Qt 6: Remove parameter window from QWidget::create()
/*!
@@ -1336,14 +1372,21 @@ void QWidgetPrivate::create()
#endif
QBackingStore *store = q->backingStore();
+ usesRhiFlush = false;
if (!store) {
if (q->windowType() != Qt::Desktop) {
- if (q->isWindow())
+ 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();
@@ -1685,10 +1728,7 @@ void QWidgetPrivate::deleteTLSysExtra()
extra->topextra->repaintManager.reset(nullptr);
deleteBackingStore(this);
-#ifndef QT_NO_OPENGL
extra->topextra->widgetTextures.clear();
- extra->topextra->shareContext.reset();
-#endif
//the toplevel might have a context with a "qglcontext associated with it. We need to
//delete the qglcontext before we delete the qplatformopenglcontext.
@@ -5538,30 +5578,22 @@ void QWidgetPrivate::drawWidget(QPaintDevice *pdev, const QRegion &rgn, const QP
//paint the background
if ((asRoot || q->autoFillBackground() || onScreen || q->testAttribute(Qt::WA_StyledBackground))
&& !q->testAttribute(Qt::WA_OpaquePaintEvent) && !q->testAttribute(Qt::WA_NoSystemBackground)) {
-#ifndef QT_NO_OPENGL
beginBackingStorePainting();
-#endif
QPainter p(q);
paintBackground(&p, toBePainted, (asRoot || onScreen) ? (flags | DrawAsRoot) : DrawWidgetFlags());
-#ifndef QT_NO_OPENGL
endBackingStorePainting();
-#endif
}
if (!sharedPainter)
setSystemClip(pdev->paintEngine(), pdev->devicePixelRatio(), toBePainted.translated(offset));
if (!onScreen && !asRoot && !isOpaque && q->testAttribute(Qt::WA_TintedBackground)) {
-#ifndef QT_NO_OPENGL
beginBackingStorePainting();
-#endif
QPainter p(q);
QColor tint = q->palette().window().color();
tint.setAlphaF(.6f);
p.fillRect(toBePainted.boundingRect(), tint);
-#ifndef QT_NO_OPENGL
endBackingStorePainting();
-#endif
}
}
@@ -5572,7 +5604,6 @@ void QWidgetPrivate::drawWidget(QPaintDevice *pdev, const QRegion &rgn, const QP
#endif
bool skipPaintEvent = false;
-#ifndef QT_NO_OPENGL
if (renderToTexture) {
// This widget renders into a texture which is composed later. We just need to
// punch a hole in the backingstore, so the texture will be visible.
@@ -5598,7 +5629,6 @@ void QWidgetPrivate::drawWidget(QPaintDevice *pdev, const QRegion &rgn, const QP
else
skipPaintEvent = true;
}
-#endif // QT_NO_OPENGL
if (!skipPaintEvent) {
//actually send the paint event
@@ -5654,10 +5684,8 @@ void QWidgetPrivate::sendPaintEvent(const QRegion &toBePainted)
QPaintEvent e(toBePainted);
QCoreApplication::sendSpontaneousEvent(q, &e);
-#ifndef QT_NO_OPENGL
if (renderToTexture)
resolveSamples();
-#endif // QT_NO_OPENGL
}
void QWidgetPrivate::render(QPaintDevice *target, const QPoint &targetOffset,
@@ -9232,9 +9260,7 @@ bool QWidget::event(QEvent *event)
}
if (d->data.fnt.d->dpi != logicalDpiY())
d->updateFont(d->data.fnt);
-#ifndef QT_NO_OPENGL
d->renderToTextureReallyDirty = 1;
-#endif
break;
case QEvent::DynamicPropertyChange: {
const QByteArray &propName = static_cast<QDynamicPropertyChangeEvent *>(event)->propertyName();
@@ -10511,22 +10537,20 @@ void QWidget::setParent(QWidget *parent)
setParent((QWidget*)parent, windowFlags() & ~Qt::WindowType_Mask);
}
-#ifndef QT_NO_OPENGL
-static void sendWindowChangeToTextureChildrenRecursively(QWidget *widget)
+static void sendWindowChangeToTextureChildrenRecursively(QWidget *widget, QEvent::Type eventType)
{
QWidgetPrivate *d = QWidgetPrivate::get(widget);
if (d->renderToTexture) {
- QEvent e(QEvent::WindowChangeInternal);
+ QEvent e(eventType);
QCoreApplication::sendEvent(widget, &e);
}
for (int i = 0; i < d->children.size(); ++i) {
QWidget *w = qobject_cast<QWidget *>(d->children.at(i));
if (w && !w->isWindow() && QWidgetPrivate::get(w)->textureChildSeen)
- sendWindowChangeToTextureChildrenRecursively(w);
+ sendWindowChangeToTextureChildrenRecursively(w, eventType);
}
}
-#endif
/*!
\overload
@@ -10582,6 +10606,12 @@ void QWidget::setParent(QWidget *parent, Qt::WindowFlags f)
QCoreApplication::sendEvent(this, &e);
}
}
+
+ // 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 (d->textureChildSeen && ((!parent && parentWidget()) || (parent && parent->window() != oldtlw)))
+ sendWindowChangeToTextureChildrenRecursively(this, QEvent::WindowAboutToChangeInternal);
+
// If we get parented into another window, children will be folded
// into the new parent's focus chain, so clear focus now.
if (newParent && isAncestorOf(focusWidget()) && !(f & Qt::Window))
@@ -10592,12 +10622,10 @@ void QWidget::setParent(QWidget *parent, Qt::WindowFlags f)
if (desktopWidget)
parent = nullptr;
-#ifndef QT_NO_OPENGL
if (d->textureChildSeen && parent) {
// set the textureChildSeen flag up the whole parent chain
QWidgetPrivate::get(parent)->setTextureChildSeen();
}
-#endif
if (QWidgetRepaintManager *oldPaintManager = oldtlw->d_func()->maybeRepaintManager()) {
if (newParent)
@@ -10659,12 +10687,11 @@ void QWidget::setParent(QWidget *parent, Qt::WindowFlags f)
QEvent e(QEvent::ParentChange);
QCoreApplication::sendEvent(this, &e);
}
-#ifndef QT_NO_OPENGL
- //renderToTexture widgets also need to know when their top-level window changes
- if (d->textureChildSeen && oldtlw != window()) {
- sendWindowChangeToTextureChildrenRecursively(this);
- }
-#endif
+
+ // texture-based widgets need another event when their top-level window
+ // changes (more precisely, has already changed at this point)
+ if (d->textureChildSeen && oldtlw != window())
+ sendWindowChangeToTextureChildrenRecursively(this, QEvent::WindowChangeInternal);
if (!wasCreated) {
if (isWindow() || parentWidget()->isVisible())
@@ -10690,6 +10717,20 @@ void QWidget::setParent(QWidget *parent, Qt::WindowFlags f)
if (d->extra && d->extra->hasWindowContainer)
QWindowContainer::parentWasChanged(this);
+
+ QWidget *newtlw = window();
+ if (oldtlw != newtlw) {
+ QSurface::SurfaceType surfaceType = QSurface::RasterSurface;
+ if (q_evaluateRhiConfig(this, nullptr, &surfaceType)) {
+ newtlw->d_func()->usesRhiFlush = true;
+ if (QWindow *w = newtlw->windowHandle()) {
+ if (w->surfaceType() != surfaceType) {
+ newtlw->destroy();
+ newtlw->create();
+ }
+ }
+ }
+ }
}
void QWidgetPrivate::setParent_sys(QWidget *newparent, Qt::WindowFlags f)
@@ -10947,7 +10988,7 @@ void QWidgetPrivate::repaint(T r)
return;
QTLWExtra *tlwExtra = q->window()->d_func()->maybeTopData();
- if (tlwExtra && tlwExtra->backingStore)
+ if (tlwExtra && tlwExtra->backingStore && tlwExtra->repaintManager)
tlwExtra->repaintManager->markDirty(r, q, QWidgetRepaintManager::UpdateNow);
}
@@ -11022,7 +11063,7 @@ void QWidgetPrivate::update(T r)
}
QTLWExtra *tlwExtra = q->window()->d_func()->maybeTopData();
- if (tlwExtra && tlwExtra->backingStore)
+ if (tlwExtra && tlwExtra->backingStore && tlwExtra->repaintManager)
tlwExtra->repaintManager->markDirty(clipped, q);
}
@@ -12158,27 +12199,6 @@ void QWidgetPrivate::adjustQuitOnCloseAttribute()
}
}
-QOpenGLContext *QWidgetPrivate::shareContext() const
-{
-#ifdef QT_NO_OPENGL
- return nullptr;
-#else
- if (!extra || !extra->topextra || !extra->topextra->window)
- return nullptr;
-
- if (!extra->topextra->shareContext) {
- auto ctx = std::make_unique<QOpenGLContext>();
- ctx->setShareContext(qt_gl_global_share_context());
- ctx->setFormat(extra->topextra->window->format());
- ctx->setScreen(extra->topextra->window->screen());
- ctx->create();
- extra->topextra->shareContext = std::move(ctx);
- }
- return extra->topextra->shareContext.get();
-#endif // QT_NO_OPENGL
-}
-
-#ifndef QT_NO_OPENGL
void QWidgetPrivate::sendComposeStatus(QWidget *w, bool end)
{
QWidgetPrivate *wd = QWidgetPrivate::get(w);
@@ -12194,7 +12214,6 @@ void QWidgetPrivate::sendComposeStatus(QWidget *w, bool end)
sendComposeStatus(w, end);
}
}
-#endif // QT_NO_OPENGL
Q_WIDGETS_EXPORT QWidgetData *qt_qwidget_data(QWidget *widget)
{