From dd279759dd5c052e7fae89f9a15ab17a8e5283d6 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Mon, 23 Nov 2020 17:47:07 +0200 Subject: QmlPuppet: Use QQuickRenderControl to render 2D views Port QQuickRenderControl rendering used for 3D edit view also for 2D views. This also fixes the issue of only partially rendered form editor view for root items with non-origin position by adjusting the position to 0,0 on the puppet side via an extra injected item. As a result of the root item always being rendered at origin, the visualization of the root item offset is no longer visible in the form editor (the checkerboard background item). Change-Id: Ide29510ef52513340d205ed35ac35c8cce66715c Fixes: QDS-3159 Fixes: QDS-3175 Reviewed-by: Mahmoud Badri Reviewed-by: Thomas Hartmann --- .../qml/qmlpuppet/mockfiles/EditView3D.qml | 23 ++- .../qml2puppet/instances/nodeinstanceserver.cpp | 20 ++- .../qml2puppet/instances/nodeinstanceserver.h | 10 +- .../qt5captureimagenodeinstanceserver.cpp | 2 +- .../qt5capturepreviewnodeinstanceserver.cpp | 2 +- .../instances/qt5informationnodeinstanceserver.cpp | 140 +-------------- .../instances/qt5informationnodeinstanceserver.h | 26 --- .../qml2puppet/instances/qt5nodeinstanceserver.cpp | 199 +++++++++++++++++++-- .../qml2puppet/instances/qt5nodeinstanceserver.h | 39 +++- .../instances/qt5previewnodeinstanceserver.cpp | 2 +- .../instances/qt5rendernodeinstanceserver.cpp | 4 +- .../instances/qt5testnodeinstanceserver.cpp | 4 +- .../qml2puppet/instances/quickitemnodeinstance.cpp | 29 ++- 13 files changed, 293 insertions(+), 207 deletions(-) diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/EditView3D.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/EditView3D.qml index 002ed7bc04..1bdc73fb22 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/EditView3D.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/EditView3D.qml @@ -57,6 +57,8 @@ Item { property var selectionBoxes: [] property rect viewPortRect: Qt.rect(0, 0, 1000, 1000) + property bool shuttingDown: false + signal selectionChanged(var selectedNodes) signal commitObjectProperty(var object, var propName) signal changeObjectProperty(var object, var propName) @@ -71,6 +73,11 @@ Item { onActiveSceneChanged: updateActiveScene() + function aboutToShutDown() + { + shuttingDown = true; + } + function createEditView() { var component = Qt.createComponent("SceneView3D.qml"); @@ -125,8 +132,12 @@ Item { } else { // When active scene is deleted, this function gets called by object deletion // handlers without going through setActiveScene, so make sure sceneId is cleared. - sceneId = ""; - storeCurrentToolStates(); + // This is skipped during application shutdown, as calling QQuickText::setText() + // during application shutdown can crash the application. + if (!shuttingDown) { + sceneId = ""; + storeCurrentToolStates(); + } } notifyActiveSceneChange(); @@ -704,6 +715,10 @@ Item { Text { id: gizmoLabelText text: { + // This is skipped during application shutdown, as calling QQuickText::setText() + // during application shutdown can crash the application. + if (shuttingDown) + return text; var l = Qt.locale(); var targetProperty; if (viewRoot.selectedNode) { @@ -738,6 +753,10 @@ Item { Text { id: rotateGizmoLabelText text: { + // This is skipped during application shutdown, as calling QQuickText::setText() + // during application shutdown can crash the application. + if (shuttingDown) + return text; var l = Qt.locale(); if (rotateGizmo.targetNode) { var degrees = rotateGizmo.currentAngle * (180 / Math.PI); diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.cpp index 552d323247..64d790c6a7 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.cpp @@ -189,9 +189,14 @@ NodeInstanceServer::NodeInstanceServer(NodeInstanceClientInterface *nodeInstance Internal::QmlPrivateGate::registerFixResourcePathsForObjectCallBack(); } +NodeInstanceServer::~NodeInstanceServer() +{ + m_objectInstanceHash.clear(); +} + QList NodeInstanceServer::createInstances(const QVector &containerVector) { - Q_ASSERT(declarativeView() || quickView()); + Q_ASSERT(declarativeView() || quickWindow()); QList instanceList; for (const InstanceContainer &instanceContainer : containerVector) { ServerNodeInstance instance; @@ -207,7 +212,6 @@ QList NodeInstanceServer::createInstances(const QVectorsetContent(fileUrl(), m_importComponent, m_rootNodeInstance.rootQuickItem()); - resizeCanvasSizeToRootItemSize(); } foreach (QQmlContext* context, allSubContextsForObject(instance.internalObject())) @@ -442,7 +446,7 @@ void NodeInstanceServer::removeSharedMemory(const RemoveSharedMemoryCommand &/*c void NodeInstanceServer::setupImports(const QVector &containerVector) { - Q_ASSERT(quickView()); + Q_ASSERT(quickWindow()); QSet importStatementSet; QString qtQuickImport; @@ -497,8 +501,9 @@ void NodeInstanceServer::setupOnlyWorkingImports(const QStringList &workingImpor QByteArray componentCode = workingImportStatementList.join("\n").toUtf8().append("\n"); m_importCode = componentCode; - m_importComponent = new QQmlComponent(engine(), quickView()); - quickView()->setContent(fileUrl(), m_importComponent, quickView()->rootObject()); + m_importComponent = new QQmlComponent(engine(), quickWindow()); + if (quickView()) + quickView()->setContent(fileUrl(), m_importComponent, quickView()->rootObject()); m_importComponent->setData(componentCode.append("\nItem {}\n"), fileUrl()); m_importComponentObject = m_importComponent->create(); @@ -942,11 +947,9 @@ void NodeInstanceServer::setInstancePropertyVariant(const PropertyValueContainer if (hasInstanceForId(valueContainer.instanceId())) { ServerNodeInstance instance = instanceForId(valueContainer.instanceId()); - const PropertyName name = valueContainer.name(); const QVariant value = valueContainer.value(); - if (activeStateInstance().isValid() && !instance.isSubclassOf("QtQuick/PropertyChanges")) { bool stateValueWasUpdated = activeStateInstance().updateStateVariant(instance, name, value); if (!stateValueWasUpdated) { @@ -962,6 +965,9 @@ void NodeInstanceServer::setInstancePropertyVariant(const PropertyValueContainer if (valueContainer.isDynamic() && valueContainer.instanceId() == 0 && engine()) rootContext()->setContextProperty(QString::fromUtf8(name), Internal::QmlPrivateGate::fixResourcePaths(value)); + + if (valueContainer.instanceId() == 0 && (name == "width" || name == "height" || name == "x" || name == "y")) + resizeCanvasToRootItem(); } } diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.h index 021a2bd6d0..f44a0047fe 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.h +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.h @@ -31,6 +31,7 @@ #include #include #include +#include #ifdef MULTILANGUAGE_TRANSLATIONPROVIDER #include @@ -90,6 +91,7 @@ QT_BEGIN_NAMESPACE class QFileSystemWatcher; class QQmlView; class QQuickView; +class QQuickWindow; class QQmlEngine; class QFileInfo; class QQmlComponent; @@ -131,6 +133,7 @@ public: }; explicit NodeInstanceServer(NodeInstanceClientInterface *nodeInstanceClient); + ~NodeInstanceServer() override; void createInstances(const CreateInstancesCommand &command) override; void changeFileUrl(const ChangeFileUrlCommand &command) override; @@ -192,6 +195,9 @@ public: virtual QQmlView *declarativeView() const = 0; virtual QQuickView *quickView() const = 0; + virtual QQuickWindow *quickWindow() const = 0; + virtual QQuickItem *rootItem() const = 0; + virtual void setRootItem(QQuickItem *item) = 0; void sendDebugOutput(DebugOutputCommand::Type type, const QString &message, qint32 instanceId = 0); void sendDebugOutput(DebugOutputCommand::Type type, @@ -211,6 +217,8 @@ public: virtual void handleInstanceLocked(const ServerNodeInstance &instance, bool enable, bool checkAncestors); virtual void handleInstanceHidden(const ServerNodeInstance &instance, bool enable, bool checkAncestors); + virtual QImage grabWindow() = 0; + public slots: void refreshLocalFileProperty(const QString &path); void refreshDummyData(const QString &path); @@ -288,7 +296,7 @@ protected: QList allSubContextsForObject(QObject *object); static QList allSubObjectsForObject(QObject *object); - virtual void resizeCanvasSizeToRootItemSize() = 0; + virtual void resizeCanvasToRootItem() = 0; void setupState(qint32 stateInstanceId); private: diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5captureimagenodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5captureimagenodeinstanceserver.cpp index d24c4e5552..4581bc6ce8 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5captureimagenodeinstanceserver.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5captureimagenodeinstanceserver.cpp @@ -70,7 +70,7 @@ void Qt5CaptureImageNodeInstanceServer::collectItemChangesAndSendChangeCommands( auto rooNodeInstance = rootNodeInstance(); rooNodeInstance.rootQuickItem()->setClip(true); - DesignerSupport::polishItems(quickView()); + DesignerSupport::polishItems(quickWindow()); QImage image = renderImage(rooNodeInstance); diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5capturepreviewnodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5capturepreviewnodeinstanceserver.cpp index 7fb87defb0..b9ba3860a3 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5capturepreviewnodeinstanceserver.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5capturepreviewnodeinstanceserver.cpp @@ -88,7 +88,7 @@ void Qt5CapturePreviewNodeInstanceServer::collectItemChangesAndSendChangeCommand if (!inFunction) { inFunction = true; - DesignerSupport::polishItems(quickView()); + DesignerSupport::polishItems(quickWindow()); QVector stateDatas; stateDatas.push_back(collectStateData(rootNodeInstance(), nodeInstances(), 0)); diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp index 8421064021..521ef40765 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp @@ -88,11 +88,7 @@ #include #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) -#include -#include -#include #include -#include #endif #ifdef QUICK3D_MODULE @@ -149,7 +145,7 @@ void Qt5InformationNodeInstanceServer::createAuxiliaryQuickView(const QUrl &url, #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) viewData.window = new QQuickView(quickView()->engine(), quickView()); viewData.window->setFormat(quickView()->format()); - DesignerSupport::createOpenGLContext(static_cast(viewData.window)); + DesignerSupport::createOpenGLContext(static_cast(viewData.window.data())); #else viewData.renderControl = new QQuickRenderControl; viewData.window = new QQuickWindow(viewData.renderControl); @@ -165,7 +161,7 @@ void Qt5InformationNodeInstanceServer::createAuxiliaryQuickView(const QUrl &url, } #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - DesignerSupport::setRootItem(static_cast(viewData.window), viewData.rootItem); + DesignerSupport::setRootItem(static_cast(viewData.window.data()), viewData.rootItem); #else viewData.window->contentItem()->setSize(viewData.rootItem->size()); viewData.window->setGeometry(0, 0, viewData.rootItem->width(), viewData.rootItem->height()); @@ -249,120 +245,6 @@ void Qt5InformationNodeInstanceServer::handleInputEvents() } } -bool Qt5InformationNodeInstanceServer::initRhi(RenderViewData &viewData) -{ -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) - if (!viewData.renderControl) { - qWarning() << __FUNCTION__ << "Render control not created"; - return false; - } - - if (!viewData.rhi) { - QQuickRenderControlPrivate *rd = QQuickRenderControlPrivate::get(viewData.renderControl); - viewData.rhi = rd->rhi; - - if (!viewData.rhi) { - qWarning() << __FUNCTION__ << "Rhi is null"; - return false; - } - } - - auto cleanRhiResources = [&viewData]() { - // Releasing cached resources is a workaround for bug QTBUG-88761 - auto renderer = QQuickWindowPrivate::get(viewData.window)->renderer; - if (renderer) - renderer->releaseCachedResources(); - - if (viewData.rpDesc) { - viewData.rpDesc->deleteLater(); - viewData.rpDesc = nullptr; - } - if (viewData.texTarget) { - viewData.texTarget->deleteLater(); - viewData.texTarget = nullptr; - } - if (viewData.buffer) { - viewData.buffer->deleteLater(); - viewData.buffer = nullptr; - } - if (viewData.texture) { - viewData.texture->deleteLater(); - viewData.texture = nullptr; - } - }; - if (viewData.bufferDirty) { - cleanRhiResources(); - viewData.bufferDirty = false; - } - - const QSize size = viewData.rootItem->size().toSize(); - viewData.texture = viewData.rhi->newTexture(QRhiTexture::RGBA8, size, 1, - QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource); - if (!viewData.texture->create()) { - qWarning() << __FUNCTION__ << "QRhiTexture creation failed"; - cleanRhiResources(); - return false; - } - - viewData.buffer = viewData.rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, size, 1); - if (!viewData.buffer->create()) { - qWarning() << __FUNCTION__ << "Depth/stencil buffer creation failed"; - cleanRhiResources(); - return false; - } - - QRhiTextureRenderTargetDescription rtDesc(QRhiColorAttachment(viewData.texture)); - rtDesc.setDepthStencilBuffer(viewData.buffer); - viewData.texTarget = viewData.rhi->newTextureRenderTarget(rtDesc); - viewData.rpDesc = viewData.texTarget->newCompatibleRenderPassDescriptor(); - viewData.texTarget->setRenderPassDescriptor(viewData.rpDesc); - if (!viewData.texTarget->create()) { - qWarning() << __FUNCTION__ << "Texture render target creation failed"; - cleanRhiResources(); - return false; - } - - // redirect Qt Quick rendering into our texture - viewData.window->setRenderTarget(QQuickRenderTarget::fromRhiRenderTarget(viewData.texTarget)); -#endif - return true; -} - -QImage Qt5InformationNodeInstanceServer::grabRenderControl(RenderViewData &viewData) -{ - QImage renderImage; -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) - if (viewData.bufferDirty && !initRhi(viewData)) - return renderImage; - - viewData.renderControl->polishItems(); - viewData.renderControl->beginFrame(); - viewData.renderControl->sync(); - viewData.renderControl->render(); - - bool readCompleted = false; - QRhiReadbackResult readResult; - readResult.completed = [&] { - readCompleted = true; - QImage wrapperImage(reinterpret_cast(readResult.data.constData()), - readResult.pixelSize.width(), readResult.pixelSize.height(), - QImage::Format_RGBA8888_Premultiplied); - if (viewData.rhi->isYUpInFramebuffer()) - renderImage = wrapperImage.mirrored(); - else - renderImage = wrapperImage.copy(); - }; - QRhiResourceUpdateBatch *readbackBatch = viewData.rhi->nextResourceUpdateBatch(); - readbackBatch->readBackTexture(viewData.texture, &readResult); - - QQuickRenderControlPrivate *rd = QQuickRenderControlPrivate::get(viewData.renderControl); - rd->cb->resourceUpdate(readbackBatch); - - viewData.renderControl->endFrame(); -#endif - return renderImage; -} - void Qt5InformationNodeInstanceServer::createEditView3D() { #ifdef QUICK3D_MODULE @@ -1000,17 +882,13 @@ Qt5InformationNodeInstanceServer::~Qt5InformationNodeInstanceServer() m_render3DEditViewTimer.stop(); m_inputEventTimer.stop(); - delete m_editView3DData.rootItem; - delete m_editView3DData.window; - delete m_modelNode3DImageViewData.rootItem; - delete m_modelNode3DImageViewData.window; - delete m_modelNode2DImageViewData.rootItem; - delete m_modelNode2DImageViewData.window; - for (auto view : qAsConst(m_view3Ds)) view->disconnect(); - for (auto scene : qAsConst(m_3DSceneMap)) - scene->disconnect(); + for (auto node : qAsConst(m_3DSceneMap)) + node->disconnect(); + + if (m_editView3DData.rootItem) + QMetaObject::invokeMethod(m_editView3DData.rootItem, "aboutToShutDown", Qt::DirectConnection); } void Qt5InformationNodeInstanceServer::sendTokenBack() @@ -1424,12 +1302,12 @@ void Qt5InformationNodeInstanceServer::collectItemChangesAndSendChangeCommands() if (!inFunction) { inFunction = true; - DesignerSupport::polishItems(quickView()); + DesignerSupport::polishItems(quickWindow()); QSet informationChangedInstanceSet; QVector propertyChangedList; - if (quickView()) { + if (quickWindow()) { foreach (QQuickItem *item, allItems()) { if (item && hasInstanceForObject(item)) { ServerNodeInstance instance = instanceForObject(item); diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.h index 47dacd45db..353f57b3fc 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.h +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.h @@ -39,15 +39,6 @@ QT_BEGIN_NAMESPACE class QDragMoveEvent; -class QQuickWindow; -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) -class QQuickRenderControl; -class QRhi; -class QRhiTexture; -class QRhiRenderBuffer; -class QRhiTextureRenderTarget; -class QRhiRenderPassDescriptor; -#endif QT_END_NAMESPACE namespace QmlDesigner { @@ -141,23 +132,6 @@ private: void updateLockedAndHiddenStates(const QSet &instances); void handleInputEvents(); - struct RenderViewData { - QQuickWindow *window = nullptr; - QQuickItem *rootItem = nullptr; - QQuickItem *contentItem = nullptr; -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) - bool bufferDirty = true; - QQuickRenderControl *renderControl = nullptr; - QRhi *rhi = nullptr; - QRhiTexture *texture = nullptr; - QRhiRenderBuffer *buffer = nullptr; - QRhiTextureRenderTarget *texTarget = nullptr; - QRhiRenderPassDescriptor *rpDesc = nullptr; -#endif - }; - - bool initRhi(RenderViewData &viewData); - QImage grabRenderControl(RenderViewData &viewData); void createAuxiliaryQuickView(const QUrl &url, RenderViewData &viewData); RenderViewData m_editView3DData; diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceserver.cpp index 90f959dd2b..620ed0352b 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceserver.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceserver.cpp @@ -31,6 +31,7 @@ #include #include +#include #include #include @@ -40,6 +41,14 @@ #include #include +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +#include +#include +#include +#include +#include +#endif + namespace QmlDesigner { Qt5NodeInstanceServer::Qt5NodeInstanceServer(NodeInstanceClientInterface *nodeInstanceClient) @@ -50,30 +59,44 @@ Qt5NodeInstanceServer::Qt5NodeInstanceServer(NodeInstanceClientInterface *nodeIn Qt5NodeInstanceServer::~Qt5NodeInstanceServer() { - delete quickView(); + delete quickWindow(); } QQuickView *Qt5NodeInstanceServer::quickView() const { - return m_quickView.data(); +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + return static_cast(m_viewData.window.data()); +#else + return nullptr; +#endif } -void Qt5NodeInstanceServer::initializeView() +QQuickWindow *Qt5NodeInstanceServer::quickWindow() const { - Q_ASSERT(!quickView()); + return m_viewData.window.data(); +} - m_quickView = new QQuickView; +void Qt5NodeInstanceServer::initializeView() +{ + Q_ASSERT(!quickWindow()); #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + auto view = new QQuickView; + m_viewData.window = view; /* enables grab window without show */ - QSurfaceFormat surfaceFormat = m_quickView->requestedFormat(); + QSurfaceFormat surfaceFormat = view->requestedFormat(); surfaceFormat.setVersion(4, 1); surfaceFormat.setProfile(QSurfaceFormat::CoreProfile); QSurfaceFormat::setDefaultFormat(surfaceFormat); - - m_quickView->setFormat(surfaceFormat); - - DesignerSupport::createOpenGLContext(m_quickView.data()); + view->setFormat(surfaceFormat); + + DesignerSupport::createOpenGLContext(view); + m_qmlEngine = view->engine(); +#else + m_viewData.renderControl = new QQuickRenderControl; + m_viewData.window = new QQuickWindow(m_viewData.renderControl); + m_viewData.renderControl->initialize(); + m_qmlEngine = new QQmlEngine; #endif if (qEnvironmentVariableIsSet("QML_FILE_SELECTORS")) { @@ -90,16 +113,39 @@ QQmlView *Qt5NodeInstanceServer::declarativeView() const return nullptr; } -QQmlEngine *Qt5NodeInstanceServer::engine() const +QQuickItem *Qt5NodeInstanceServer::rootItem() const { - if (quickView()) - return quickView()->engine(); + return m_viewData.rootItem; +} - return nullptr; +void Qt5NodeInstanceServer::setRootItem(QQuickItem *item) +{ + m_viewData.rootItem = item; +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + DesignerSupport::setRootItem(quickView(), item); +#else + quickWindow()->setGeometry(0, 0, item->width(), item->height()); + // Insert an extra item above the root to adjust root item position to 0,0 to make entire + // item to be always rendered. + if (!m_viewData.contentItem) + m_viewData.contentItem = new QQuickItem(quickWindow()->contentItem()); + m_viewData.contentItem->setPosition(-item->position()); + item->setParentItem(m_viewData.contentItem); +#endif } -void Qt5NodeInstanceServer::resizeCanvasSizeToRootItemSize() +QQmlEngine *Qt5NodeInstanceServer::engine() const { + return m_qmlEngine; +} + +void Qt5NodeInstanceServer::resizeCanvasToRootItem() +{ +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + m_viewData.bufferDirty = true; + m_viewData.contentItem->setPosition(-m_viewData.rootItem->position()); +#endif + quickWindow()->resize(rootNodeInstance().boundingRect().size().toSize()); } void Qt5NodeInstanceServer::resetAllItems() @@ -116,7 +162,7 @@ void Qt5NodeInstanceServer::setupScene(const CreateSceneCommand &command) setupDummyData(command.fileUrl); setupInstances(command); - quickView()->resize(rootNodeInstance().boundingRect().size().toSize()); + resizeCanvasToRootItem(); } QList subItems(QQuickItem *parentItem) @@ -138,6 +184,127 @@ QList Qt5NodeInstanceServer::allItems() const return QList(); } +bool Qt5NodeInstanceServer::initRhi(RenderViewData &viewData) +{ +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + if (!viewData.renderControl) { + qWarning() << __FUNCTION__ << "Render control not created"; + return false; + } + + if (!viewData.rhi) { + QQuickRenderControlPrivate *rd = QQuickRenderControlPrivate::get(viewData.renderControl); + viewData.rhi = rd->rhi; + + if (!viewData.rhi) { + qWarning() << __FUNCTION__ << "Rhi is null"; + return false; + } + } + + auto cleanRhiResources = [&viewData]() { + // Releasing cached resources is a workaround for bug QTBUG-88761 + auto renderer = QQuickWindowPrivate::get(viewData.window)->renderer; + if (renderer) + renderer->releaseCachedResources(); + + if (viewData.rpDesc) { + viewData.rpDesc->deleteLater(); + viewData.rpDesc = nullptr; + } + if (viewData.texTarget) { + viewData.texTarget->deleteLater(); + viewData.texTarget = nullptr; + } + if (viewData.buffer) { + viewData.buffer->deleteLater(); + viewData.buffer = nullptr; + } + if (viewData.texture) { + viewData.texture->deleteLater(); + viewData.texture = nullptr; + } + }; + if (viewData.bufferDirty) { + cleanRhiResources(); + viewData.bufferDirty = false; + } + + const QSize size = viewData.window->size(); + viewData.texture = viewData.rhi->newTexture(QRhiTexture::RGBA8, size, 1, + QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource); + if (!viewData.texture->create()) { + qWarning() << __FUNCTION__ << "QRhiTexture creation failed"; + cleanRhiResources(); + return false; + } + + viewData.buffer = viewData.rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, size, 1); + if (!viewData.buffer->create()) { + qWarning() << __FUNCTION__ << "Depth/stencil buffer creation failed"; + cleanRhiResources(); + return false; + } + + QRhiTextureRenderTargetDescription rtDesc(QRhiColorAttachment(viewData.texture)); + rtDesc.setDepthStencilBuffer(viewData.buffer); + viewData.texTarget = viewData.rhi->newTextureRenderTarget(rtDesc); + viewData.rpDesc = viewData.texTarget->newCompatibleRenderPassDescriptor(); + viewData.texTarget->setRenderPassDescriptor(viewData.rpDesc); + if (!viewData.texTarget->create()) { + qWarning() << __FUNCTION__ << "Texture render target creation failed"; + cleanRhiResources(); + return false; + } + + // redirect Qt Quick rendering into our texture + viewData.window->setRenderTarget(QQuickRenderTarget::fromRhiRenderTarget(viewData.texTarget)); +#endif + return true; +} + +QImage Qt5NodeInstanceServer::grabRenderControl(RenderViewData &viewData) +{ + QImage renderImage; +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + if (viewData.bufferDirty && !initRhi(viewData)) + return renderImage; + + viewData.renderControl->polishItems(); + viewData.renderControl->beginFrame(); + viewData.renderControl->sync(); + viewData.renderControl->render(); + + bool readCompleted = false; + QRhiReadbackResult readResult; + readResult.completed = [&] { + readCompleted = true; + QImage wrapperImage(reinterpret_cast(readResult.data.constData()), + readResult.pixelSize.width(), readResult.pixelSize.height(), + QImage::Format_RGBA8888_Premultiplied); + if (viewData.rhi->isYUpInFramebuffer()) + renderImage = wrapperImage.mirrored(); + else + renderImage = wrapperImage.copy(); + }; + QRhiResourceUpdateBatch *readbackBatch = viewData.rhi->nextResourceUpdateBatch(); + readbackBatch->readBackTexture(viewData.texture, &readResult); + + QQuickRenderControlPrivate *rd = QQuickRenderControlPrivate::get(viewData.renderControl); + rd->cb->resourceUpdate(readbackBatch); + + viewData.renderControl->endFrame(); +#endif + return renderImage; +} + +QImage Qt5NodeInstanceServer::grabWindow() +{ + if (m_viewData.rootItem) + return grabRenderControl(m_viewData); + return {}; +} + void Qt5NodeInstanceServer::refreshBindings() { DesignerSupport::refreshExpressions(context()); diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceserver.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceserver.h index ca43363a9a..e6a2754f99 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceserver.h +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceserver.h @@ -26,12 +26,22 @@ #pragma once #include +#include #include "nodeinstanceserver.h" #include QT_BEGIN_NAMESPACE class QQuickItem; +class QQmlEngine; +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +class QQuickRenderControl; +class QRhi; +class QRhiTexture; +class QRhiRenderBuffer; +class QRhiTextureRenderTarget; +class QRhiRenderPassDescriptor; +#endif QT_END_NAMESPACE namespace QmlDesigner { @@ -44,7 +54,11 @@ public: ~Qt5NodeInstanceServer() override; QQuickView *quickView() const override; + QQuickWindow *quickWindow() const override; QQmlView *declarativeView() const override; + QQuickItem *rootItem() const override; + void setRootItem(QQuickItem *item) override; + QQmlEngine *engine() const override; void refreshBindings() override; @@ -54,16 +68,37 @@ public: void clearScene(const ClearSceneCommand &command) override; void reparentInstances(const ReparentInstancesCommand &command) override; + QImage grabWindow() override; + protected: void initializeView() override; - void resizeCanvasSizeToRootItemSize() override; + void resizeCanvasToRootItem() override; void resetAllItems(); void setupScene(const CreateSceneCommand &command) override; QList allItems() const; + struct RenderViewData { + QPointer window = nullptr; + QQuickItem *rootItem = nullptr; + QQuickItem *contentItem = nullptr; +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + bool bufferDirty = true; + QQuickRenderControl *renderControl = nullptr; + QRhi *rhi = nullptr; + QRhiTexture *texture = nullptr; + QRhiRenderBuffer *buffer = nullptr; + QRhiTextureRenderTarget *texTarget = nullptr; + QRhiRenderPassDescriptor *rpDesc = nullptr; +#endif + }; + + virtual bool initRhi(RenderViewData &viewData); + virtual QImage grabRenderControl(RenderViewData &viewData); + private: - QPointer m_quickView; + RenderViewData m_viewData; DesignerSupport m_designerSupport; + QQmlEngine *m_qmlEngine = nullptr; }; } // QmlDesigner diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5previewnodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5previewnodeinstanceserver.cpp index ae167ad6ca..2a34864a84 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5previewnodeinstanceserver.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5previewnodeinstanceserver.cpp @@ -71,7 +71,7 @@ void Qt5PreviewNodeInstanceServer::collectItemChangesAndSendChangeCommands() if (!inFunction && nodeInstanceClient()->bytesToWrite() < 10000) { inFunction = true; - DesignerSupport::polishItems(quickView()); + DesignerSupport::polishItems(quickWindow()); QVector imageContainerVector; imageContainerVector.append(ImageContainer(0, renderPreviewImage(), -1)); diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5rendernodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5rendernodeinstanceserver.cpp index 4a45c626be..bb1aa72445 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5rendernodeinstanceserver.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5rendernodeinstanceserver.cpp @@ -74,9 +74,9 @@ void Qt5RenderNodeInstanceServer::collectItemChangesAndSendChangeCommands() if (!inFunction) { inFunction = true; - DesignerSupport::polishItems(quickView()); + DesignerSupport::polishItems(quickWindow()); - if (quickView() && nodeInstanceClient()->bytesToWrite() < 10000) { + if (quickWindow() && nodeInstanceClient()->bytesToWrite() < 10000) { foreach (QQuickItem *item, allItems()) { if (item) { if (hasInstanceForObject(item)) { diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5testnodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5testnodeinstanceserver.cpp index a6570b4a1e..9edb567d5b 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5testnodeinstanceserver.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5testnodeinstanceserver.cpp @@ -257,13 +257,13 @@ void Qt5TestNodeInstanceServer::removeSharedMemory(const RemoveSharedMemoryComma void QmlDesigner::Qt5TestNodeInstanceServer::collectItemChangesAndSendChangeCommands() { - DesignerSupport::polishItems(quickView()); + DesignerSupport::polishItems(quickWindow()); QSet informationChangedInstanceSet; QVector propertyChangedList; QSet parentChangedSet; - if (quickView()) { + if (quickWindow()) { foreach (QQuickItem *item, allItems()) { if (item && hasInstanceForObject(item)) { ServerNodeInstance instance = instanceForObject(item); diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quickitemnodeinstance.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quickitemnodeinstance.cpp index e9e6328ce9..81607cbdd6 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quickitemnodeinstance.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quickitemnodeinstance.cpp @@ -174,11 +174,10 @@ void QuickItemNodeInstance::initialize(const ObjectNodeInstance::Pointer &object InstanceContainer::NodeFlags flags) { - if (instanceId() == 0) { - DesignerSupport::setRootItem(nodeInstanceServer()->quickView(), quickItem()); - } else { - quickItem()->setParentItem(qobject_cast(nodeInstanceServer()->quickView()->rootObject())); - } + if (instanceId() == 0) + nodeInstanceServer()->setRootItem(quickItem()); + else + quickItem()->setParentItem(nodeInstanceServer()->rootItem()); if (quickItem()->window() && checkIfRefFromEffect(instanceId())) { designerSupport()->refFromEffectItem(quickItem(), @@ -420,19 +419,19 @@ QImage QuickItemNodeInstance::renderImage() const #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) if (s_unifiedRenderPath) { - renderImage = nodeInstanceServer()->quickView()->grabWindow(); + renderImage = nodeInstanceServer()->quickWindow()->grabWindow(); } else { // Fake render loop signaling to update things like QML items as 3D textures - nodeInstanceServer()->quickView()->beforeSynchronizing(); - nodeInstanceServer()->quickView()->beforeRendering(); + nodeInstanceServer()->quickWindow()->beforeSynchronizing(); + nodeInstanceServer()->quickWindow()->beforeRendering(); renderImage = designerSupport()->renderImageForItem(quickItem(), renderBoundingRect, size); - nodeInstanceServer()->quickView()->afterRendering(); + nodeInstanceServer()->quickWindow()->afterRendering(); } renderImage.setDevicePixelRatio(devicePixelRatio); #else - renderImage = nodeInstanceServer()->quickView()->grabWindow(); + renderImage = nodeInstanceServer()->grabWindow(); renderImage = renderImage.copy(renderBoundingRect.toRect()); /* When grabbing an offscren window the device pixel ratio is 1 */ renderImage.setDevicePixelRatio(1); @@ -452,20 +451,20 @@ QImage QuickItemNodeInstance::renderPreviewImage(const QSize &previewImageSize) QImage image; #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) if (s_unifiedRenderPath) { - image = nodeInstanceServer()->quickView()->grabWindow(); + image = nodeInstanceServer()->quickWindow()->grabWindow(); } else { // Fake render loop signaling to update things like QML items as 3D textures - nodeInstanceServer()->quickView()->beforeSynchronizing(); - nodeInstanceServer()->quickView()->beforeRendering(); + nodeInstanceServer()->quickWindow()->beforeSynchronizing(); + nodeInstanceServer()->quickWindow()->beforeRendering(); image = designerSupport()->renderImageForItem(quickItem(), previewItemBoundingRect, size); - nodeInstanceServer()->quickView()->afterRendering(); + nodeInstanceServer()->quickWindow()->afterRendering(); } #else - image = nodeInstanceServer()->quickView()->grabWindow(); + image = nodeInstanceServer()->grabWindow(); image = image.copy(previewItemBoundingRect.toRect()); #endif -- cgit v1.2.3