QQuickWidget: Enable use with software renderer backend

Change-Id: Ic7a550e55cc4ece4c3a5547dae5bf0dbb3f5b0ac
Reviewed-by: Laszlo Agocs <laszlo.agocs@theqtcompany.com>
This commit is contained in:
Andy Nichols 2016-04-19 16:19:23 +02:00 committed by Laszlo Agocs
parent 8139a219dd
commit 62198ab448
4 changed files with 181 additions and 93 deletions

View File

@ -57,7 +57,7 @@ QT_BEGIN_NAMESPACE
class QPaintDevice;
class QSGSoftwareRenderer : public QSGAbstractSoftwareRenderer
class Q_QUICK_PRIVATE_EXPORT QSGSoftwareRenderer : public QSGAbstractSoftwareRenderer
{
public:
QSGSoftwareRenderer(QSGRenderContext *context);

View File

@ -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>
@ -60,6 +62,9 @@
#include <QtGui/QOpenGLContext>
#include <QtGui/QOpenGLFunctions>
#include <QtGui/private/qopenglextensions_p.h>
#include <QtGui/QPainter>
#include <QtQuick/QSGRendererInterface>
#ifdef Q_OS_WIN
# include <QtWidgets/QMessageBox>
@ -93,10 +98,17 @@ 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) {
if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::RasterGLSurface))
setRenderToTexture();
else
qWarning("QQuickWidget is not supported on this platform.");
}
engine = e;
@ -118,13 +130,15 @@ void QQuickWidgetPrivate::init(QQmlEngine* e)
void QQuickWidgetPrivate::invalidateRenderControl()
{
if (!context) // this is not an error, could be called before creating the context, or multiple times
return;
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;
}
}
renderControl->invalidate();
@ -133,7 +147,8 @@ void QQuickWidgetPrivate::invalidateRenderControl()
void QQuickWidgetPrivate::handleWindowChange()
{
invalidateRenderControl();
destroyContext();
if (!useSoftwareRenderer)
destroyContext();
}
QQuickWidgetPrivate::QQuickWidgetPrivate()
@ -151,6 +166,7 @@ QQuickWidgetPrivate::QQuickWidgetPrivate()
, updatePending(false)
, fakeHidden(false)
, requestedSamples(0)
, useSoftwareRenderer(false)
{
}
@ -158,14 +174,19 @@ 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;
if (useSoftwareRenderer) {
delete renderControl;
delete offscreenWindow;
} else {
// 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();
destroyContext();
}
}
void QQuickWidgetPrivate::execute()
@ -206,37 +227,53 @@ void QQuickWidgetPrivate::itemGeometryChanged(QQuickItem *resizeItem, const QRec
QQuickItemChangeListener::itemGeometryChanged(resizeItem, newGeometry, oldGeometry);
}
// Is never called when using the software renderer
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) {
// 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();
if (needsSync) {
renderControl->polishItems();
renderControl->sync();
}
renderControl->render();
if (resolvedFbo) {
QRect rect(QPoint(0, 0), fbo->size());
QOpenGLFramebufferObject::blitFramebuffer(resolvedFbo, rect, fbo, rect);
}
static_cast<QOpenGLExtensions *>(context->functions())->flushShared();
QOpenGLContextPrivate::get(context)->defaultFboRedirect = 0;
} 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 = fbo->handle();
if (needsSync) {
renderControl->polishItems();
renderControl->sync();
}
renderControl->render();
if (resolvedFbo) {
QRect rect(QPoint(0, 0), fbo->size());
QOpenGLFramebufferObject::blitFramebuffer(resolvedFbo, rect, fbo, rect);
}
static_cast<QOpenGLExtensions *>(context->functions())->flushShared();
QOpenGLContextPrivate::get(context)->defaultFboRedirect = 0;
}
void QQuickWidgetPrivate::renderSceneGraph()
@ -247,13 +284,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);
@ -267,10 +306,12 @@ void QQuickWidgetPrivate::renderSceneGraph()
QImage QQuickWidgetPrivate::grabFramebuffer()
{
if (!context)
return QImage();
if (!useSoftwareRenderer) {
if (!context)
return QImage();
context->makeCurrent(offscreenSurface);
context->makeCurrent(offscreenSurface);
}
return renderControl->grab();
}
@ -377,6 +418,7 @@ QQuickWidget::QQuickWidget(QWidget *parent)
{
setMouseTracking(true);
setFocusPolicy(Qt::StrongFocus);
setAttribute(Qt::WA_OpaquePaintEvent, true);
d_func()->init();
}
@ -390,6 +432,7 @@ QQuickWidget::QQuickWidget(const QUrl &source, QWidget *parent)
{
setMouseTracking(true);
setFocusPolicy(Qt::StrongFocus);
setAttribute(Qt::WA_OpaquePaintEvent, true);
d_func()->init();
setSource(source);
}
@ -408,6 +451,7 @@ QQuickWidget::QQuickWidget(QQmlEngine* engine, QWidget *parent)
{
setMouseTracking(true);
setFocusPolicy(Qt::StrongFocus);
setAttribute(Qt::WA_OpaquePaintEvent, true);
Q_ASSERT(engine);
d_func()->init(engine);
}
@ -712,6 +756,7 @@ void QQuickWidgetPrivate::handleContextCreationFailure(const QSurfaceFormat &for
qFatal("%s", qPrintable(untranslatedMessage));
}
// Never called by Software Rendering backend
void QQuickWidgetPrivate::createContext()
{
Q_Q(QQuickWidget);
@ -756,6 +801,7 @@ void QQuickWidgetPrivate::createContext()
qWarning("QQuickWidget: Failed to make context current");
}
// Never called by Software Rendering backend
void QQuickWidgetPrivate::destroyContext()
{
delete offscreenSurface;
@ -773,6 +819,13 @@ void QQuickWidget::createFramebufferObject()
if (size().isEmpty())
return;
if (d->useSoftwareRenderer) {
const QSize imageSize = size() * devicePixelRatio();
d->softwareImage = QImage(imageSize, QImage::Format_ARGB32_Premultiplied);
d->softwareImage.setDevicePixelRatio(devicePixelRatio());
return;
}
QOpenGLContext *context = d->offscreenWindow->openglContext();
if (!context) {
@ -850,6 +903,12 @@ void QQuickWidget::createFramebufferObject()
void QQuickWidget::destroyFramebufferObject()
{
Q_D(QQuickWidget);
if (d->useSoftwareRenderer) {
d->softwareImage = QImage();
return;
}
delete d->fbo;
d->fbo = 0;
delete d->resolvedFbo;
@ -1024,26 +1083,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();
}
QOpenGLContext *context = d->offscreenWindow->openglContext();
if (!context) {
qWarning("QQuickWidget::resizeEvent() no OpenGL context");
return;
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;
}
}
d->render(needsSync);
@ -1108,11 +1177,16 @@ void QQuickWidget::showEvent(QShowEvent *)
{
Q_D(QQuickWidget);
d->updatePending = false;
d->createContext();
if (d->offscreenWindow->openglContext())
d->render(true);
else
if (!d->useSoftwareRenderer) {
d->createContext();
if (d->offscreenWindow->openglContext())
d->render(true);
else
triggerUpdate();
} else {
triggerUpdate();
}
QWindowPrivate *offscreenPrivate = QWindowPrivate::get(d->offscreenWindow);
if (!offscreenPrivate->visible) {
offscreenPrivate->visible = true;
@ -1228,7 +1302,7 @@ bool QQuickWidget::event(QEvent *e)
break;
case QEvent::ScreenChangeInternal:
if (d->fbo) {
if (d->fbo || d->useSoftwareRenderer) {
// This will check the size taking the devicePixelRatio into account
// and recreate if needed.
createFramebufferObject();
@ -1400,3 +1474,13 @@ QQuickWindow *QQuickWidget::quickWindow() const
}
QT_END_NAMESPACE
void QQuickWidget::paintEvent(QPaintEvent *event)
{
Q_D(QQuickWidget);
if (d->useSoftwareRenderer) {
QPainter painter(this);
painter.drawImage(rect(), d->softwareImage);
}
}

View File

@ -115,34 +115,35 @@ private Q_SLOTS:
void triggerUpdate();
protected:
virtual void resizeEvent(QResizeEvent *);
virtual void timerEvent(QTimerEvent*);
void resizeEvent(QResizeEvent *) override;
void timerEvent(QTimerEvent*) override;
virtual void keyPressEvent(QKeyEvent *);
virtual void keyReleaseEvent(QKeyEvent *);
virtual void mousePressEvent(QMouseEvent *);
virtual void mouseReleaseEvent(QMouseEvent *);
virtual void mouseMoveEvent(QMouseEvent *);
virtual void mouseDoubleClickEvent(QMouseEvent *);
void keyPressEvent(QKeyEvent *) override;
void keyReleaseEvent(QKeyEvent *) override;
void mousePressEvent(QMouseEvent *) override;
void mouseReleaseEvent(QMouseEvent *) override;
void mouseMoveEvent(QMouseEvent *) override;
void mouseDoubleClickEvent(QMouseEvent *) override;
virtual void showEvent(QShowEvent *);
virtual void hideEvent(QHideEvent *);
void showEvent(QShowEvent *) override;
void hideEvent(QHideEvent *) override;
virtual void focusInEvent(QFocusEvent * event);
virtual void focusOutEvent(QFocusEvent * event);
void focusInEvent(QFocusEvent * event) override;
void focusOutEvent(QFocusEvent * event) override;
#ifndef QT_NO_WHEELEVENT
virtual void wheelEvent(QWheelEvent *);
void wheelEvent(QWheelEvent *) override;
#endif
#ifndef QT_NO_DRAGANDDROP
virtual void dragEnterEvent(QDragEnterEvent *);
virtual void dragMoveEvent(QDragMoveEvent *);
virtual void dragLeaveEvent(QDragLeaveEvent *);
virtual void dropEvent(QDropEvent *);
void dragEnterEvent(QDragEnterEvent *) override;
void dragMoveEvent(QDragMoveEvent *) override;
void dragLeaveEvent(QDragLeaveEvent *) override;
void dropEvent(QDropEvent *) override;
#endif
bool event(QEvent *);
bool event(QEvent *) override;
void paintEvent(QPaintEvent *event) override;
private:
Q_DISABLE_COPY(QQuickWidget)

View File

@ -134,6 +134,9 @@ public:
bool fakeHidden;
int requestedSamples;
bool useSoftwareRenderer;
QImage softwareImage;
};
QT_END_NAMESPACE