From e4fd19c59f10dd85edea67c30b465886f582018b Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Mon, 10 Feb 2025 16:12:53 +0200 Subject: [PATCH] QmlDesigner: Fix qml-renderer puppet Fixed various issues with qml-renderer puppet mode: - Delete objects properly - Use smooth scaling - Remove unnecessary code - Fix brief flash on screen in case of Window root in qml - Display error if qml fails to load Task-number: QDS-14691 Change-Id: Ic8944a024ab5eb3c1ee0f0e7310f71af03fdb9e2 Reviewed-by: Marco Bubke --- .../qmlpuppet/renderer/qmlrenderer.cpp | 98 +++++++++---------- .../qmlpuppet/renderer/qmlrenderer.h | 31 +++--- 2 files changed, 66 insertions(+), 63 deletions(-) diff --git a/src/tools/qmlpuppet/qmlpuppet/renderer/qmlrenderer.cpp b/src/tools/qmlpuppet/qmlpuppet/renderer/qmlrenderer.cpp index bd67246271b..2fd9487276a 100644 --- a/src/tools/qmlpuppet/qmlpuppet/renderer/qmlrenderer.cpp +++ b/src/tools/qmlpuppet/qmlpuppet/renderer/qmlrenderer.cpp @@ -12,13 +12,9 @@ #endif #include -#include -#include -#include #include #include -#include void QmlRenderer::initCoreApp() { @@ -32,24 +28,25 @@ void QmlRenderer::initCoreApp() void QmlRenderer::populateParser() { m_argParser.addOptions({ - {QStringList() << "i" << "importpath", + {QStringList{"i", "importpath"}, "Prepend the given path to the import paths.", "path"}, - {QStringList() << "o" << "outfile", + {QStringList{"o", "outfile"}, "Output image file path.", "path"}, // "h" is reserved arg for help, so use capital letters for height/width - {QStringList() << "H" << "height", + {QStringList{"H", "height"}, "Height of the final rendered image.", "pixels"}, - {QStringList() << "W" << "width", + {QStringList{"W", "width"}, "Width of the final rendered image.", "pixels"}, - {QStringList() << "v" << "verbose", "Display additional output."} + {QStringList{"v", "verbose"}, + "Display additional output."} }); m_argParser.addPositionalArgument("file", "QML file to render.", "file"); @@ -104,29 +101,37 @@ bool QmlRenderer::setupRenderer() QQuickDesignerSupport::activateDesignerMode(); - QQmlEngine *engine = new QQmlEngine; + m_engine = std::make_unique(); for (const QString &path : std::as_const(m_importPaths)) - engine->addImportPath(path); + m_engine->addImportPath(path); - m_renderControl = new QQuickRenderControl; - m_window = new QQuickWindow(m_renderControl); + m_renderControl = std::make_unique(); + m_window = std::make_unique(m_renderControl.get()); m_window->setDefaultAlphaBuffer(true); m_window->setColor(Qt::transparent); m_renderControl->initialize(); - QQmlComponent component(engine); + QQmlComponent component(m_engine.get()); component.loadUrl(QUrl::fromLocalFile(m_sourceFile)); + + if (component.isError()) { + error(QString("Failed to load url: %1").arg(m_sourceFile)); + error(component.errorString()); + return false; + } + QObject *renderObj = component.create(); if (renderObj) { #ifdef QUICK3D_MODULE QQuickItem *contentItem3D = nullptr; + renderObj->setParent(m_window->contentItem()); if (qobject_cast(renderObj)) { - auto helper = new QmlDesigner::Internal::GeneralHelper(); - engine->rootContext()->setContextProperty("_generalHelper", helper); + m_helper = std::make_unique(); + m_engine->rootContext()->setContextProperty("_generalHelper", m_helper.get()); - QQmlComponent component(engine); + QQmlComponent component(m_engine.get()); component.loadUrl(QUrl("qrc:/qtquickplugin/mockfiles/qt6/ModelNode3DImageView.qml")); m_containerItem = qobject_cast(component.create()); if (!m_containerItem) { @@ -167,14 +172,22 @@ bool QmlRenderer::setupRenderer() } else if (auto renderWindow = qobject_cast(renderObj)) { // Hack to render Window items: reparent window content to m_window->contentItem() m_renderSize = renderWindow->size(); - renderWindow->setVisible(false); m_containerItem = m_window->contentItem(); + // Suppress the original window. + // Offscreen position ensures we don't get even brief flash of it. + renderWindow->setPosition(-100000, -100000); + renderWindow->setVisible(false); const QList childItems = renderWindow->contentItem()->childItems(); - for (QQuickItem *item : childItems) + for (QQuickItem *item : childItems) { + item->setParent(m_window->contentItem()); item->setParentItem(m_window->contentItem()); + } + } else { + error("Invalid root object type."); + return false; } - if ((m_containerItem) && (contentItem3D || !m_is3D)) { + if (m_containerItem && (contentItem3D || !m_is3D)) { m_window->setGeometry(0, 0, m_renderSize.width(), m_renderSize.height()); m_window->contentItem()->setSize(m_renderSize); m_containerItem->setSize(m_renderSize); @@ -196,7 +209,7 @@ bool QmlRenderer::setupRenderer() bool QmlRenderer::initRhi() { if (!m_rhi) { - QQuickRenderControlPrivate *rd = QQuickRenderControlPrivate::get(m_renderControl); + QQuickRenderControlPrivate *rd = QQuickRenderControlPrivate::get(m_renderControl.get()); m_rhi = rd->rhi; if (!m_rhi) { error("Rhi is null."); @@ -204,36 +217,31 @@ bool QmlRenderer::initRhi() } } - m_texTarget = nullptr; - m_rpDesc = nullptr; - m_buffer = nullptr; - m_texture = nullptr; - - m_texture = m_rhi->newTexture(QRhiTexture::RGBA8, m_renderSize, 1, - QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource); + m_texture.reset(m_rhi->newTexture(QRhiTexture::RGBA8, m_renderSize, 1, + QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource)); if (!m_texture->create()) { error("QRhiTexture creation failed."); return false; } - m_buffer = m_rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, m_renderSize, 1); + m_buffer.reset(m_rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, m_renderSize, 1)); if (!m_buffer->create()) { error("Depth/stencil buffer creation failed."); return false; } - QRhiTextureRenderTargetDescription rtDesc {QRhiColorAttachment(m_texture)}; - rtDesc.setDepthStencilBuffer(m_buffer); - m_texTarget = m_rhi->newTextureRenderTarget(rtDesc); - m_rpDesc = m_texTarget->newCompatibleRenderPassDescriptor(); - m_texTarget->setRenderPassDescriptor(m_rpDesc); + QRhiTextureRenderTargetDescription rtDesc {QRhiColorAttachment(m_texture.get())}; + rtDesc.setDepthStencilBuffer(m_buffer.get()); + m_texTarget.reset(m_rhi->newTextureRenderTarget(rtDesc)); + m_rpDesc.reset(m_texTarget->newCompatibleRenderPassDescriptor()); + m_texTarget->setRenderPassDescriptor(m_rpDesc.get()); if (!m_texTarget->create()) { error("Texture render target creation failed."); return false; } // redirect Qt Quick rendering into our texture - m_window->setRenderTarget(QQuickRenderTarget::fromRhiRenderTarget(m_texTarget)); + m_window->setRenderTarget(QQuickRenderTarget::fromRhiRenderTarget(m_texTarget.get())); return true; } @@ -242,15 +250,6 @@ void QmlRenderer::render() { info(QString("Rendering: %1").arg(m_sourceFile)); - std::function updateNodesRecursive; - updateNodesRecursive = [&updateNodesRecursive](QQuickItem *item) { - const auto childItems = item->childItems(); - for (QQuickItem *childItem : childItems) - updateNodesRecursive(childItem); - if (item->flags() & QQuickItem::ItemHasContents) - item->update(); - }; - QImage renderImage; // Need to render fitted 3D views twice, first render updates spatial node geometries @@ -259,7 +258,6 @@ void QmlRenderer::render() if (m_fit3D && i == 1) QMetaObject::invokeMethod(m_containerItem, "fitToViewPort", Qt::DirectConnection); - updateNodesRecursive(m_containerItem); m_renderControl->polishItems(); m_renderControl->beginFrame(); m_renderControl->sync(); @@ -277,14 +275,16 @@ void QmlRenderer::render() readResult.pixelSize.width(), readResult.pixelSize.height(), QImage::Format_RGBA8888_Premultiplied); if (m_rhi->isYUpInFramebuffer()) - renderImage = wrapperImage.mirrored().scaled(m_requestedSize); + renderImage = wrapperImage.mirrored().scaled(m_requestedSize, Qt::IgnoreAspectRatio, + Qt::SmoothTransformation); else - renderImage = wrapperImage.copy().scaled(m_requestedSize); + renderImage = wrapperImage.copy().scaled(m_requestedSize, Qt::IgnoreAspectRatio, + Qt::SmoothTransformation); }; QRhiResourceUpdateBatch *readbackBatch = m_rhi->nextResourceUpdateBatch(); - readbackBatch->readBackTexture(m_texture, &readResult); + readbackBatch->readBackTexture(m_texture.get(), &readResult); - QQuickRenderControlPrivate *rd = QQuickRenderControlPrivate::get(m_renderControl); + QQuickRenderControlPrivate *rd = QQuickRenderControlPrivate::get(m_renderControl.get()); rd->cb->resourceUpdate(readbackBatch); m_renderControl->endFrame(); diff --git a/src/tools/qmlpuppet/qmlpuppet/renderer/qmlrenderer.h b/src/tools/qmlpuppet/qmlpuppet/renderer/qmlrenderer.h index 2a0fe3642a5..212f57074d3 100644 --- a/src/tools/qmlpuppet/qmlpuppet/renderer/qmlrenderer.h +++ b/src/tools/qmlpuppet/qmlpuppet/renderer/qmlrenderer.h @@ -5,15 +5,16 @@ #include "../qmlbase.h" +#include + +#include +#include + +#include +#include + QT_BEGIN_NAMESPACE class QQuickItem; -class QQuickRenderControl; -class QQuickWindow; -class QRhi; -class QRhiRenderBuffer; -class QRhiRenderPassDescriptor; -class QRhiTexture; -class QRhiTextureRenderTarget; QT_END_NAMESPACE class QmlRenderer : public QmlBase @@ -42,13 +43,15 @@ private: bool m_is3D = false; bool m_fit3D = false; - QQuickWindow *m_window = nullptr; QQuickItem *m_containerItem = nullptr; - - QQuickRenderControl *m_renderControl = nullptr; QRhi *m_rhi = nullptr; - QRhiTexture *m_texture = nullptr; - QRhiRenderBuffer *m_buffer = nullptr; - QRhiTextureRenderTarget *m_texTarget = nullptr; - QRhiRenderPassDescriptor *m_rpDesc = nullptr; + + std::unique_ptr m_engine; + std::unique_ptr m_renderControl; + std::unique_ptr m_window; + std::unique_ptr m_helper; + std::unique_ptr m_texture; + std::unique_ptr m_buffer; + std::unique_ptr m_texTarget; + std::unique_ptr m_rpDesc; };