aboutsummaryrefslogtreecommitdiffstats
path: root/src/quickwidgets
diff options
context:
space:
mode:
authorLaszlo Agocs <laszlo.agocs@qt.io>2020-04-30 17:37:27 +0200
committerEskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io>2020-05-27 10:45:45 +0200
commit0d80dbd8c2cfc2a7d2a4d970b7acfc7fb5fb97a0 (patch)
treea6f487a8604e6212d27184b69debac87e0f3aca8 /src/quickwidgets
parent6d3aae3cf47fbf21bd27eda7a249c2a23308156e (diff)
Enable QQuickWidget with OpenGL over RHI
Also adapts to new NativeTexture format, where the actual handle is passed around instead of a pointer to it. [ChangeLog][QQuickWidget][Important Behavioral Changes] In earlier versions, the returned value from QQuickWidget::quickWindow() would persist for the life time of the widget. This is no longer the case, so if you are connecting to its signals, make sure you also connect to its destroyed() signal and update the connections when it is destroyed. Fixes: QTBUG-78638 Change-Id: I33cee8543ef1ff5d31555ed3ac18ba78c9c45102 Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Laszlo Agocs <laszlo.agocs@qt.io>
Diffstat (limited to 'src/quickwidgets')
-rw-r--r--src/quickwidgets/qquickwidget.cpp120
-rw-r--r--src/quickwidgets/qquickwidget_p.h1
2 files changed, 82 insertions, 39 deletions
diff --git a/src/quickwidgets/qquickwidget.cpp b/src/quickwidgets/qquickwidget.cpp
index f3d26e4a73..221bdfaee6 100644
--- a/src/quickwidgets/qquickwidget.cpp
+++ b/src/quickwidgets/qquickwidget.cpp
@@ -44,6 +44,7 @@
#include "private/qquickitem_p.h"
#include "private/qquickitemchangelistener_p.h"
#include "private/qquickrendercontrol_p.h"
+#include "private/qsgrhisupport_p.h"
#include "private/qsgsoftwarerenderer_p.h"
@@ -75,6 +76,9 @@
# include <QtCore/qt_windows.h>
#endif
+#include <QtQuick/qquickgraphicsdevice.h>
+#include <QtQuick/qquickrendertarget.h>
+
QT_BEGIN_NAMESPACE
// override setVisble to prevent accidental offscreen window being created
@@ -102,16 +106,25 @@ private:
QQuickWidget *m_quickWidget;
};
-void QQuickWidgetPrivate::init(QQmlEngine* e)
+void QQuickWidgetPrivate::initOffscreenWindow()
{
Q_Q(QQuickWidget);
-
- renderControl = new QQuickWidgetRenderControl(q);
- offscreenWindow = new QQuickWindow(*new QQuickOffcreenWindowPrivate(),renderControl);
+ offscreenWindow = new QQuickWindow(*new QQuickOffcreenWindowPrivate(), renderControl);
offscreenWindow->setTitle(QString::fromLatin1("Offscreen"));
offscreenWindow->setObjectName(QString::fromLatin1("QQuickOffScreenWindow"));
// Do not call create() on offscreenWindow.
+ QWidget::connect(offscreenWindow, SIGNAL(sceneGraphInitialized()), q, SLOT(createFramebufferObject()));
+ QWidget::connect(offscreenWindow, SIGNAL(sceneGraphInvalidated()), q, SLOT(destroyFramebufferObject()));
+}
+
+void QQuickWidgetPrivate::init(QQmlEngine* e)
+{
+ Q_Q(QQuickWidget);
+
+ renderControl = new QQuickWidgetRenderControl(q);
+ initOffscreenWindow();
+
// Check if the Software Adaptation is being used
auto sgRendererInterface = offscreenWindow->rendererInterface();
if (sgRendererInterface && sgRendererInterface->graphicsApi() == QSGRendererInterface::Software)
@@ -126,6 +139,9 @@ void QQuickWidgetPrivate::init(QQmlEngine* e)
qWarning("QQuickWidget is not supported on this platform.");
}
+ if (QSGRhiSupport::instance()->rhiBackend() != QRhi::OpenGLES2)
+ qWarning("QQuickWidget is only supported on OpenGL. Use QQuickWindow::setSceneGraphBackend() to override the default.");
+
engine = e;
if (!engine.isNull() && !engine.data()->incubationController())
@@ -135,8 +151,6 @@ void QQuickWidgetPrivate::init(QQmlEngine* e)
q->setAcceptDrops(true);
#endif
- QWidget::connect(offscreenWindow, SIGNAL(sceneGraphInitialized()), q, SLOT(createFramebufferObject()));
- QWidget::connect(offscreenWindow, SIGNAL(sceneGraphInvalidated()), q, SLOT(destroyFramebufferObject()));
QObject::connect(renderControl, SIGNAL(renderRequested()), q, SLOT(triggerUpdate()));
QObject::connect(renderControl, SIGNAL(sceneChanged()), q, SLOT(triggerUpdate()));
}
@@ -181,18 +195,28 @@ void QQuickWidgetPrivate::invalidateRenderControl()
void QQuickWidgetPrivate::handleWindowChange()
{
+ Q_Q(QQuickWidget);
+
if (offscreenWindow->isPersistentSceneGraph() && qGuiApp->testAttribute(Qt::AA_ShareOpenGLContexts))
return;
// In case of !isPersistentSceneGraph or when we need a new context due to
// the need to share resources with the new window's context, we must both
- // invalidate the scenegraph and destroy the context. With
- // QQuickRenderControl destroying the context must be preceded by an
- // invalidate to prevent being left with dangling context references in the
- // rendercontrol.
+ // invalidate the scenegraph and destroy the context. QQuickRenderControl
+ // must be recreated because its RHI will contain a dangling pointer to
+ // the context.
- invalidateRenderControl();
+ delete offscreenWindow;
+ offscreenWindow = nullptr;
+ delete renderControl;
+ renderControl = new QQuickWidgetRenderControl(q);
+ initOffscreenWindow();
+
+ QObject::connect(renderControl, SIGNAL(renderRequested()), q, SLOT(triggerUpdate()));
+ QObject::connect(renderControl, SIGNAL(sceneChanged()), q, SLOT(triggerUpdate()));
+
+ execute();
if (!useSoftwareRenderer)
destroyContext();
}
@@ -230,10 +254,10 @@ QQuickWidgetPrivate::~QQuickWidgetPrivate()
#if QT_CONFIG(opengl)
// context and offscreenSurface are current at this stage, if the context was created.
Q_ASSERT(!context || (QOpenGLContext::currentContext() == context && context->surface() == offscreenSurface));
- delete renderControl; // always delete the rendercontrol first
- delete offscreenWindow;
delete resolvedFbo;
delete fbo;
+ delete offscreenWindow;
+ delete renderControl;
destroyContext();
#endif
@@ -302,6 +326,8 @@ void QQuickWidgetPrivate::render(bool needsSync)
QOpenGLContextPrivate::get(context)->defaultFboRedirect = fbo->handle();
+ renderControl->beginFrame();
+
if (needsSync) {
renderControl->polishItems();
renderControl->sync();
@@ -309,6 +335,9 @@ void QQuickWidgetPrivate::render(bool needsSync)
renderControl->render();
+ renderControl->endFrame();
+
+ context->makeCurrent(offscreenSurface);
if (resolvedFbo) {
QRect rect(QPoint(0, 0), fbo->size());
QOpenGLFramebufferObject::blitFramebuffer(resolvedFbo, rect, fbo, rect);
@@ -349,7 +378,6 @@ void QQuickWidgetPrivate::renderSceneGraph()
return;
if (!useSoftwareRenderer) {
- QOpenGLContext *context = offscreenWindow->openglContext();
if (!context) {
qWarning("QQuickWidget: Attempted to render scene with no context");
return;
@@ -383,7 +411,13 @@ QImage QQuickWidgetPrivate::grabFramebuffer()
context->makeCurrent(offscreenSurface);
#endif
}
- return offscreenWindow->grabWindow();
+
+
+ // grabWindow() does not work for the OpenGL + render control case, so we
+ // prefer the FBO's toImage() if available. When the software renderer
+ // is in use, however, there will be no FBO and we fall back to grabWindow()
+ // instead.
+ return fbo != nullptr ? fbo->toImage() : offscreenWindow->grabWindow();
}
// Intentionally not overriding the QQuickWindow's focusObject.
@@ -894,7 +928,7 @@ void QQuickWidgetPrivate::createContext()
// On hide-show we may invalidate() (when !isPersistentSceneGraph) but our
// context is kept. We may need to initialize() again, though.
- const bool reinit = context && !offscreenWindow->openglContext();
+ const bool reinit = context && !offscreenWindow->isSceneGraphInitialized();
if (!reinit) {
if (context)
@@ -929,8 +963,10 @@ void QQuickWidgetPrivate::createContext()
}
if (context->makeCurrent(offscreenSurface)) {
- if (!offscreenWindow->openglContext())
- renderControl->initialize(context);
+ if (!offscreenWindow->isSceneGraphInitialized()) {
+ offscreenWindow->setGraphicsDevice(QQuickGraphicsDevice::fromOpenGLContext(context));
+ renderControl->initialize();
+ }
} else
#endif
qWarning("QQuickWidget: Failed to make context current");
@@ -971,30 +1007,28 @@ void QQuickWidget::createFramebufferObject()
}
#if QT_CONFIG(opengl)
- QOpenGLContext *context = d->offscreenWindow->openglContext();
-
- if (!context) {
+ if (!d->context) {
qWarning("QQuickWidget: Attempted to create FBO with no context");
return;
}
QOpenGLContext *shareWindowContext = QWidgetPrivate::get(window())->shareContext();
- if (shareWindowContext && context->shareContext() != shareWindowContext && !qGuiApp->testAttribute(Qt::AA_ShareOpenGLContexts)) {
- context->setShareContext(shareWindowContext);
- context->setScreen(shareWindowContext->screen());
- if (!context->create())
+ if (shareWindowContext && d->context->shareContext() != shareWindowContext && !qGuiApp->testAttribute(Qt::AA_ShareOpenGLContexts)) {
+ d->context->setShareContext(shareWindowContext);
+ d->context->setScreen(shareWindowContext->screen());
+ if (!d->context->create())
qWarning("QQuickWidget: Failed to recreate context");
// The screen may be different so we must recreate the offscreen surface too.
// Unlike QOpenGLContext, QOffscreenSurface's create() does not recreate so have to destroy() first.
d->offscreenSurface->destroy();
- d->offscreenSurface->setScreen(context->screen());
+ d->offscreenSurface->setScreen(d->context->screen());
d->offscreenSurface->create();
}
- context->makeCurrent(d->offscreenSurface);
+ d->context->makeCurrent(d->offscreenSurface);
int samples = d->requestedSamples;
- if (!QOpenGLExtensions(context).hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample))
+ if (!QOpenGLExtensions(d->context).hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample))
samples = 0;
QOpenGLFramebufferObjectFormat format;
@@ -1004,11 +1038,12 @@ void QQuickWidget::createFramebufferObject()
// The default framebuffer for normal windows have sRGB support on OS X which leads to the Qt Quick text item
// utilizing sRGB blending. To get identical results with QQuickWidget we have to have our framebuffer backed
// by an sRGB texture.
-#ifdef Q_OS_OSX
- if (context->hasExtension("GL_ARB_framebuffer_sRGB")
- && context->hasExtension("GL_EXT_texture_sRGB")
- && context->hasExtension("GL_EXT_texture_sRGB_decode"))
+#ifdef Q_OS_MACOS
+ if (d->context->hasExtension("GL_ARB_framebuffer_sRGB")
+ && d->context->hasExtension("GL_EXT_texture_sRGB")
+ && d->context->hasExtension("GL_EXT_texture_sRGB_decode")) {
format.setInternalTextureFormat(GL_SRGB8_ALPHA8_EXT);
+ }
#endif
const QSize fboSize = size() * devicePixelRatioF();
@@ -1022,15 +1057,18 @@ void QQuickWidget::createFramebufferObject()
// When compositing in the backingstore, sampling the sRGB texture would perform an
// sRGB-linear conversion which is not what we want when the final framebuffer (the window's)
// is sRGB too. Disable the conversion.
-#ifdef Q_OS_OSX
+#ifdef Q_OS_MACOS
if (format.internalTextureFormat() == GL_SRGB8_ALPHA8_EXT) {
- QOpenGLFunctions *funcs = context->functions();
+ QOpenGLFunctions *funcs = d->context->functions();
funcs->glBindTexture(GL_TEXTURE_2D, d->fbo->texture());
funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SRGB_DECODE_EXT, GL_SKIP_DECODE_EXT);
}
#endif
- d->offscreenWindow->setRenderTarget(d->fbo);
+ GLuint textureId = d->fbo->texture();
+ d->offscreenWindow->setRenderTarget( QQuickRenderTarget::fromNativeTexture({ textureId, 0 }, fboSize, samples));
+
+ d->renderControl->setSamples(samples);
if (samples > 0)
d->resolvedFbo = new QOpenGLFramebufferObject(fboSize);
@@ -1247,7 +1285,7 @@ void QQuickWidget::resizeEvent(QResizeEvent *e)
if (d->context) {
// Bail out when receiving a resize after scenegraph invalidation. This can happen
// during hide - resize - show sequences and also during application exit.
- if (!d->fbo && !d->offscreenWindow->openglContext())
+ if (!d->fbo && !d->offscreenWindow->isSceneGraphInitialized())
return;
if (!d->fbo || d->fbo->size() != size() * devicePixelRatioF()) {
needsSync = true;
@@ -1260,8 +1298,7 @@ void QQuickWidget::resizeEvent(QResizeEvent *e)
d->createContext();
}
- QOpenGLContext *context = d->offscreenWindow->openglContext();
- if (!context) {
+ if (!d->context) {
qWarning("QQuickWidget::resizeEvent() no OpenGL context");
return;
}
@@ -1351,7 +1388,7 @@ void QQuickWidget::showEvent(QShowEvent *)
if (!d->useSoftwareRenderer) {
d->createContext();
- if (d->offscreenWindow->openglContext()) {
+ if (d->offscreenWindow->isSceneGraphInitialized()) {
shouldTriggerUpdate = false;
d->render(true);
// render() may have led to a QQuickWindow::update() call (for
@@ -1707,6 +1744,11 @@ void QQuickWidget::setClearColor(const QColor &color)
\warning Use the return value of this function with caution. In
particular, do not ever attempt to show the QQuickWindow, and be
very careful when using other QWindow-only APIs.
+
+ \warning The offscreen window may be deleted (and recreated) during
+ the life time of the QQuickWidget, particularly when the widget is
+ moved to another QQuickWindow. If you need to know when the window
+ has been replaced, connect to its destroyed() signal.
*/
QQuickWindow *QQuickWidget::quickWindow() const
{
diff --git a/src/quickwidgets/qquickwidget_p.h b/src/quickwidgets/qquickwidget_p.h
index 881f7f9220..0517a772e1 100644
--- a/src/quickwidgets/qquickwidget_p.h
+++ b/src/quickwidgets/qquickwidget_p.h
@@ -108,6 +108,7 @@ public:
#endif
void init(QQmlEngine* e = 0);
+ void initOffscreenWindow();
void ensureEngine() const;
void handleWindowChange();
void invalidateRenderControl();