aboutsummaryrefslogtreecommitdiffstats
path: root/src/quickwidgets/qquickwidget.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/quickwidgets/qquickwidget.cpp')
-rw-r--r--src/quickwidgets/qquickwidget.cpp392
1 files changed, 289 insertions, 103 deletions
diff --git a/src/quickwidgets/qquickwidget.cpp b/src/quickwidgets/qquickwidget.cpp
index b0838e5dc4..ad04c76783 100644
--- a/src/quickwidgets/qquickwidget.cpp
+++ b/src/quickwidgets/qquickwidget.cpp
@@ -45,6 +45,8 @@
#include "private/qquickitemchangelistener_p.h"
#include "private/qquickrendercontrol_p.h"
+#include "private/qsgsoftwarerenderer_p.h"
+
#include <private/qqmldebugconnector_p.h>
#include <private/qquickprofiler_p.h>
#include <private/qqmldebugserviceinterfaces_p.h>
@@ -57,6 +59,15 @@
#include <QtGui/private/qguiapplication_p.h>
#include <QtGui/qpa/qplatformintegration.h>
+#ifndef QT_NO_OPENGL
+#include <QtGui/QOpenGLContext>
+#include <QtGui/QOpenGLFunctions>
+#include <QtGui/private/qopenglextensions_p.h>
+#endif
+#include <QtGui/QPainter>
+
+#include <QtQuick/QSGRendererInterface>
+
#ifdef Q_OS_WIN
# include <QtWidgets/QMessageBox>
# include <QtCore/QLibraryInfo>
@@ -65,7 +76,9 @@
QT_BEGIN_NAMESPACE
+#ifndef QT_NO_OPENGL
extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha);
+#endif
class QQuickWidgetRenderControl : public QQuickRenderControl
{
@@ -89,10 +102,19 @@ void QQuickWidgetPrivate::init(QQmlEngine* e)
offscreenWindow->setTitle(QString::fromLatin1("Offscreen"));
// Do not call create() on offscreenWindow.
- if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::RasterGLSurface))
- setRenderToTexture();
- else
- qWarning("QQuickWidget is not supported on this platform.");
+ // Check if the Software Adaptation is being used
+ auto sgRendererInterface = offscreenWindow->rendererInterface();
+ if (sgRendererInterface && sgRendererInterface->graphicsApi() == QSGRendererInterface::Software)
+ useSoftwareRenderer = true;
+
+ if (!useSoftwareRenderer) {
+#ifndef QT_NO_OPENGL
+ if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::RasterGLSurface))
+ setRenderToTexture();
+ else
+#endif
+ qWarning("QQuickWidget is not supported on this platform.");
+ }
engine = e;
@@ -121,22 +143,38 @@ void QQuickWidgetPrivate::ensureEngine() const
void QQuickWidgetPrivate::invalidateRenderControl()
{
- if (!context) // this is not an error, could be called before creating the context, or multiple times
- return;
+#ifndef QT_NO_OPENGL
+ if (!useSoftwareRenderer) {
+ if (!context) // this is not an error, could be called before creating the context, or multiple times
+ return;
- bool success = context->makeCurrent(offscreenSurface);
- if (!success) {
- qWarning("QQuickWidget::invalidateRenderControl could not make context current");
- return;
+ bool success = context->makeCurrent(offscreenSurface);
+ if (!success) {
+ qWarning("QQuickWidget::invalidateRenderControl could not make context current");
+ return;
+ }
}
+#endif
renderControl->invalidate();
}
void QQuickWidgetPrivate::handleWindowChange()
{
+ 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.
+
invalidateRenderControl();
- destroyContext();
+
+ if (!useSoftwareRenderer)
+ destroyContext();
}
QQuickWidgetPrivate::QQuickWidgetPrivate()
@@ -145,15 +183,18 @@ QQuickWidgetPrivate::QQuickWidgetPrivate()
, offscreenWindow(0)
, offscreenSurface(0)
, renderControl(0)
+#ifndef QT_NO_OPENGL
, fbo(0)
, resolvedFbo(0)
, context(0)
+#endif
, resizeMode(QQuickWidget::SizeViewToRootObject)
, initialSize(0,0)
, eventPending(false)
, updatePending(false)
, fakeHidden(false)
, requestedSamples(0)
+ , useSoftwareRenderer(false)
{
}
@@ -161,14 +202,21 @@ QQuickWidgetPrivate::~QQuickWidgetPrivate()
{
invalidateRenderControl();
- // 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;
-
- destroyContext();
+ if (useSoftwareRenderer) {
+ delete renderControl;
+ delete offscreenWindow;
+ } else {
+#ifndef QT_NO_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;
+
+ destroyContext();
+#endif
+ }
}
void QQuickWidgetPrivate::execute()
@@ -196,47 +244,67 @@ void QQuickWidgetPrivate::execute()
}
}
-void QQuickWidgetPrivate::itemGeometryChanged(QQuickItem *resizeItem, const QRectF &newGeometry, const QRectF &oldGeometry)
+void QQuickWidgetPrivate::itemGeometryChanged(QQuickItem *resizeItem, QQuickGeometryChange change,
+ const QRectF &diff)
{
Q_Q(QQuickWidget);
if (resizeItem == root && resizeMode == QQuickWidget::SizeViewToRootObject) {
// wait for both width and height to be changed
resizetimer.start(0,q);
}
- QQuickItemChangeListener::itemGeometryChanged(resizeItem, newGeometry, oldGeometry);
+ QQuickItemChangeListener::itemGeometryChanged(resizeItem, change, diff);
}
void QQuickWidgetPrivate::render(bool needsSync)
{
- // createFramebufferObject() bails out when the size is empty. In this case
- // we cannot render either.
- if (!fbo)
- return;
+ if (!useSoftwareRenderer) {
+#ifndef QT_NO_OPENGL
+ // createFramebufferObject() bails out when the size is empty. In this case
+ // we cannot render either.
+ if (!fbo)
+ return;
- Q_ASSERT(context);
+ Q_ASSERT(context);
- if (!context->makeCurrent(offscreenSurface)) {
- qWarning("QQuickWidget: Cannot render due to failing makeCurrent()");
- return;
- }
+ if (!context->makeCurrent(offscreenSurface)) {
+ qWarning("QQuickWidget: Cannot render due to failing makeCurrent()");
+ return;
+ }
- QOpenGLContextPrivate::get(context)->defaultFboRedirect = fbo->handle();
+ QOpenGLContextPrivate::get(context)->defaultFboRedirect = fbo->handle();
- if (needsSync) {
- renderControl->polishItems();
- renderControl->sync();
- }
+ if (needsSync) {
+ renderControl->polishItems();
+ renderControl->sync();
+ }
- renderControl->render();
+ renderControl->render();
- if (resolvedFbo) {
- QRect rect(QPoint(0, 0), fbo->size());
- QOpenGLFramebufferObject::blitFramebuffer(resolvedFbo, rect, fbo, rect);
- }
+ if (resolvedFbo) {
+ QRect rect(QPoint(0, 0), fbo->size());
+ QOpenGLFramebufferObject::blitFramebuffer(resolvedFbo, rect, fbo, rect);
+ }
+
+ static_cast<QOpenGLExtensions *>(context->functions())->flushShared();
- static_cast<QOpenGLExtensions *>(context->functions())->flushShared();
+ QOpenGLContextPrivate::get(context)->defaultFboRedirect = 0;
+#endif
+ } else {
+ //Software Renderer
+ if (needsSync) {
+ renderControl->polishItems();
+ renderControl->sync();
+ }
+
+ QQuickWindowPrivate *cd = QQuickWindowPrivate::get(offscreenWindow);
+ auto softwareRenderer = static_cast<QSGSoftwareRenderer*>(cd->renderer);
+ if (softwareRenderer) {
+ softwareRenderer->setCurrentPaintDevice(&softwareImage);
+ renderControl->render();
- QOpenGLContextPrivate::get(context)->defaultFboRedirect = 0;
+ updateRegion += softwareRenderer->flushRegion();
+ }
+ }
}
void QQuickWidgetPrivate::renderSceneGraph()
@@ -247,13 +315,15 @@ void QQuickWidgetPrivate::renderSceneGraph()
if (!q->isVisible() || fakeHidden)
return;
- QOpenGLContext *context = offscreenWindow->openglContext();
- if (!context) {
- qWarning("QQuickWidget: Attempted to render scene with no context");
- return;
- }
+ if (!useSoftwareRenderer) {
+ QOpenGLContext *context = offscreenWindow->openglContext();
+ if (!context) {
+ qWarning("QQuickWidget: Attempted to render scene with no context");
+ return;
+ }
- Q_ASSERT(offscreenSurface);
+ Q_ASSERT(offscreenSurface);
+ }
render(true);
@@ -262,15 +332,24 @@ void QQuickWidgetPrivate::renderSceneGraph()
QWidgetPrivate::nearestGraphicsProxyWidget(q)->update();
else
#endif
- q->update(); // schedule composition
+ {
+ if (!useSoftwareRenderer)
+ q->update(); // schedule composition
+ else if (!updateRegion.isEmpty())
+ q->update(updateRegion);
+ }
}
QImage QQuickWidgetPrivate::grabFramebuffer()
{
- if (!context)
- return QImage();
+ if (!useSoftwareRenderer) {
+#ifndef QT_NO_OPENGL
+ if (!context)
+ return QImage();
- context->makeCurrent(offscreenSurface);
+ context->makeCurrent(offscreenSurface);
+#endif
+ }
return renderControl->grab();
}
@@ -339,6 +418,36 @@ QObject *QQuickWidgetPrivate::focusObject()
entire purpose of QQuickWidget is to render Quick scenes without a separate native
window, hence making it a native widget should always be avoided.
+ \section1 Scene graph and context persistency
+
+ QQuickWidget honors QQuickWindow::isPersistentSceneGraph(), meaning that
+ applications can decide - by calling
+ QQuickWindow::setPersistentSceneGraph() on the window returned from the
+ quickWindow() function - to let scenegraph nodes and other Qt Quick scene
+ related resources be released whenever the widget becomes hidden. By default
+ persistency is enabled, just like with QQuickWindow.
+
+ When running with the OpenGL backend of the scene graph, QQuickWindow
+ offers the possibility to disable persistent OpenGL contexts as well. This
+ setting is currently ignored by QQuickWidget and the context is always
+ persistent. The OpenGL context is thus not destroyed when hiding the
+ widget. The context is destroyed only when the widget is destroyed or when
+ the widget gets reparented into another top-level widget's child hierarchy.
+ However, some applications, in particular those that have their own
+ graphics resources due to performing custom OpenGL rendering in the Qt
+ Quick scene, may wish to disable the latter since they may not be prepared
+ to handle the loss of the context when moving a QQuickWidget into another
+ window. Such applications can set the
+ QCoreApplication::AA_ShareOpenGLContexts attribute. For a discussion on the
+ details of resource initialization and cleanup, refer to the QOpenGLWidget
+ documentation.
+
+ \note QQuickWidget offers less fine-grained control over its internal
+ OpenGL context than QOpenGLWidget, and there are subtle differences, most
+ notably that disabling the persistent scene graph will lead to destroying
+ the context on a window change regardless of the presence of
+ QCoreApplication::AA_ShareOpenGLContexts.
+
\section1 Limitations
Putting other widgets underneath and making the QQuickWidget transparent will not lead
@@ -359,6 +468,13 @@ QObject *QQuickWidgetPrivate::focusObject()
Qt::WA_TranslucentBackground on the top-level window, request an alpha channel, and
change the Qt Quick Scenegraph's clear color to Qt::transparent via setClearColor().
+ \section1 Support when not using OpenGL
+
+ In addition to OpenGL, the \c software backend of Qt Quick also supports
+ QQuickWidget. Other backends, for example the Direct 3D 12 one, are not
+ compatible however and attempting to construct a QQuickWidget will lead to
+ problems.
+
\sa {Exposing Attributes of C++ Types to QML}, {Qt Quick Widgets Example}, QQuickView
*/
@@ -386,11 +502,8 @@ QQuickWidget::QQuickWidget(QWidget *parent)
*/
QQuickWidget::QQuickWidget(const QUrl &source, QWidget *parent)
-: QWidget(*(new QQuickWidgetPrivate), parent, 0)
+ : QQuickWidget(parent)
{
- setMouseTracking(true);
- setFocusPolicy(Qt::StrongFocus);
- d_func()->init();
setSource(source);
}
@@ -652,9 +765,14 @@ void QQuickWidgetPrivate::updateSize()
q->resize(newSize);
}
} else if (resizeMode == QQuickWidget::SizeRootObjectToView) {
- if (!qFuzzyCompare(q->width(), root->width()))
+ bool needToUpdateWidth = !qFuzzyCompare(q->width(), root->width());
+ bool needToUpdateHeight = !qFuzzyCompare(q->height(), root->height());
+
+ if (needToUpdateWidth && needToUpdateHeight)
+ root->setSize(QSizeF(q->width(), q->height()));
+ else if (needToUpdateWidth)
root->setWidth(q->width());
- if (!qFuzzyCompare(q->height(), root->height()))
+ else if (needToUpdateHeight)
root->setHeight(q->height());
}
}
@@ -706,19 +824,22 @@ void QQuickWidgetPrivate::handleContextCreationFailure(const QSurfaceFormat &for
if (signalConnected)
emit q->sceneGraphError(QQuickWindow::ContextNotAvailable, translatedMessage);
-#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
if (!signalConnected && !QLibraryInfo::isDebugBuild() && !GetConsoleWindow())
QMessageBox::critical(q, QCoreApplication::applicationName(), translatedMessage);
-#endif // Q_OS_WIN && !Q_OS_WINCE && !Q_OS_WINRT
+#endif // Q_OS_WIN && !Q_OS_WINRT
if (!signalConnected)
qFatal("%s", qPrintable(untranslatedMessage));
}
+// Never called by Software Rendering backend
void QQuickWidgetPrivate::createContext()
{
+#ifndef QT_NO_OPENGL
Q_Q(QQuickWidget);
- // On hide-show we invalidate() but our context is kept.
- // We nonetheless need to initialize() again.
+
+ // 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();
if (!reinit) {
@@ -752,18 +873,23 @@ void QQuickWidgetPrivate::createContext()
offscreenSurface->create();
}
- if (context->makeCurrent(offscreenSurface))
- renderControl->initialize(context);
- else
+ if (context->makeCurrent(offscreenSurface)) {
+ if (!offscreenWindow->openglContext())
+ renderControl->initialize(context);
+ } else
+#endif
qWarning("QQuickWidget: Failed to make context current");
}
+// Never called by Software Rendering backend
void QQuickWidgetPrivate::destroyContext()
{
delete offscreenSurface;
offscreenSurface = 0;
+#ifndef QT_NO_OPENGL
delete context;
context = 0;
+#endif
}
void QQuickWidget::createFramebufferObject()
@@ -775,6 +901,20 @@ void QQuickWidget::createFramebufferObject()
if (size().isEmpty())
return;
+ // Even though this is just an offscreen window we should set the position on it, as it might be
+ // useful for an item to know the actual position of the scene.
+ // Note: The position will be update when we get a move event (see: updatePosition()).
+ const QPoint &globalPos = mapToGlobal(QPoint(0, 0));
+ d->offscreenWindow->setGeometry(globalPos.x(), globalPos.y(), width(), height());
+
+ if (d->useSoftwareRenderer) {
+ const QSize imageSize = size() * devicePixelRatio();
+ d->softwareImage = QImage(imageSize, QImage::Format_ARGB32_Premultiplied);
+ d->softwareImage.setDevicePixelRatio(devicePixelRatio());
+ return;
+ }
+
+#ifndef QT_NO_OPENGL
QOpenGLContext *context = d->offscreenWindow->openglContext();
if (!context) {
@@ -834,11 +974,6 @@ void QQuickWidget::createFramebufferObject()
}
#endif
- // Even though this is just an offscreen window we should set the position on it, as it might be
- // useful for an item to know the actual position of the scene.
- // Note: The position will be update when we get a move event (see: updatePosition()).
- const QPoint &globalPos = mapToGlobal(QPoint(0, 0));
- d->offscreenWindow->setGeometry(globalPos.x(), globalPos.y(), width(), height());
d->offscreenWindow->setRenderTarget(d->fbo);
if (samples > 0)
@@ -848,15 +983,24 @@ void QQuickWidget::createFramebufferObject()
// Having one would mean create() was called and platforms that only support
// a single native window were in trouble.
Q_ASSERT(!d->offscreenWindow->handle());
+#endif
}
void QQuickWidget::destroyFramebufferObject()
{
Q_D(QQuickWidget);
+
+ if (d->useSoftwareRenderer) {
+ d->softwareImage = QImage();
+ return;
+ }
+
+#ifndef QT_NO_OPENGL
delete d->fbo;
d->fbo = 0;
delete d->resolvedFbo;
d->resolvedFbo = 0;
+#endif
}
QQuickWidget::ResizeMode QQuickWidget::resizeMode() const
@@ -934,6 +1078,7 @@ void QQuickWidgetPrivate::setRootObject(QObject *obj)
}
}
+#ifndef QT_NO_OPENGL
GLuint QQuickWidgetPrivate::textureId() const
{
Q_Q(const QQuickWidget);
@@ -945,6 +1090,7 @@ GLuint QQuickWidgetPrivate::textureId() const
return resolvedFbo ? resolvedFbo->texture()
: (fbo ? fbo->texture() : 0);
}
+#endif
/*!
\internal
@@ -1027,26 +1173,36 @@ void QQuickWidget::resizeEvent(QResizeEvent *e)
needsSync = true;
}
- 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())
- return;
- if (!d->fbo || d->fbo->size() != size() * devicePixelRatio()) {
- needsSync = true;
+ // Software Renderer
+ if (d->useSoftwareRenderer) {
+ needsSync = true;
+ if (d->softwareImage.size() != size() * devicePixelRatio()) {
createFramebufferObject();
}
} else {
- // This will result in a scenegraphInitialized() signal which
- // is connected to createFramebufferObject().
- needsSync = true;
- d->createContext();
- }
+#ifndef QT_NO_OPENGL
+ 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())
+ return;
+ if (!d->fbo || d->fbo->size() != size() * devicePixelRatio()) {
+ needsSync = true;
+ createFramebufferObject();
+ }
+ } else {
+ // This will result in a scenegraphInitialized() signal which
+ // is connected to createFramebufferObject().
+ needsSync = true;
+ d->createContext();
+ }
- QOpenGLContext *context = d->offscreenWindow->openglContext();
- if (!context) {
- qWarning("QQuickWidget::resizeEvent() no OpenGL context");
- return;
+ QOpenGLContext *context = d->offscreenWindow->openglContext();
+ if (!context) {
+ qWarning("QQuickWidget::resizeEvent() no OpenGL context");
+ return;
+ }
+#endif
}
d->render(needsSync);
@@ -1110,22 +1266,24 @@ void QQuickWidget::mouseDoubleClickEvent(QMouseEvent *e)
void QQuickWidget::showEvent(QShowEvent *)
{
Q_D(QQuickWidget);
- d->createContext();
- if (d->offscreenWindow->openglContext()) {
- d->render(true);
- // render() may have led to a QQuickWindow::update() call (for
- // example, having a scene with a QQuickFramebufferObject::Renderer
- // calling update() in its render()) which in turn results in
- // renderRequested in the rendercontrol, ending up in
- // triggerUpdate. In this case just calling update() is not
- // acceptable, we need the full renderSceneGraph issued from
- // timerEvent().
- if (!d->eventPending && d->updatePending) {
- d->updatePending = false;
- update();
+ if (!d->useSoftwareRenderer) {
+ d->createContext();
+ if (d->offscreenWindow->openglContext()) {
+ d->render(true);
+ // render() may have led to a QQuickWindow::update() call (for
+ // example, having a scene with a QQuickFramebufferObject::Renderer
+ // calling update() in its render()) which in turn results in
+ // renderRequested in the rendercontrol, ending up in
+ // triggerUpdate. In this case just calling update() is not
+ // acceptable, we need the full renderSceneGraph issued from
+ // timerEvent().
+ if (!d->eventPending && d->updatePending) {
+ d->updatePending = false;
+ update();
+ }
+ } else {
+ triggerUpdate();
}
- } else {
- triggerUpdate();
}
QWindowPrivate *offscreenPrivate = QWindowPrivate::get(d->offscreenWindow);
if (!offscreenPrivate->visible) {
@@ -1141,7 +1299,8 @@ void QQuickWidget::showEvent(QShowEvent *)
void QQuickWidget::hideEvent(QHideEvent *)
{
Q_D(QQuickWidget);
- d->invalidateRenderControl();
+ if (!d->offscreenWindow->isPersistentSceneGraph())
+ d->invalidateRenderControl();
QWindowPrivate *offscreenPrivate = QWindowPrivate::get(d->offscreenWindow);
if (offscreenPrivate->visible) {
offscreenPrivate->visible = false;
@@ -1249,11 +1408,17 @@ bool QQuickWidget::event(QEvent *e)
d->offscreenWindow->setScreen(newScreen);
if (d->offscreenSurface)
d->offscreenSurface->setScreen(newScreen);
+#ifndef QT_NO_OPENGL
if (d->context)
d->context->setScreen(newScreen);
+#endif
}
- if (d->fbo) {
+ if (d->useSoftwareRenderer
+#ifndef QT_NO_OPENGL
+ || d->fbo
+#endif
+ ) {
// This will check the size taking the devicePixelRatio into account
// and recreate if needed.
createFramebufferObject();
@@ -1425,3 +1590,24 @@ QQuickWindow *QQuickWidget::quickWindow() const
}
QT_END_NAMESPACE
+
+
+void QQuickWidget::paintEvent(QPaintEvent *event)
+{
+ Q_UNUSED(event)
+ Q_D(QQuickWidget);
+ if (d->useSoftwareRenderer) {
+ QPainter painter(this);
+ if (d->updateRegion.isNull()) {
+ //Paint everything
+ painter.drawImage(rect(), d->softwareImage);
+ } else {
+ //Paint only the updated areas
+ for (auto targetRect : d->updateRegion.rects()) {
+ auto sourceRect = QRect(targetRect.topLeft() * devicePixelRatio(), targetRect.size() * devicePixelRatio());
+ painter.drawImage(targetRect, d->softwareImage, sourceRect);
+ }
+ d->updateRegion = QRegion();
+ }
+ }
+}