forked from qt-creator/qt-creator
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 <marco.bubke@qt.io>
This commit is contained in:
@@ -12,13 +12,9 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <private/qquickdesignersupport_p.h>
|
#include <private/qquickdesignersupport_p.h>
|
||||||
#include <private/qquickrendercontrol_p.h>
|
|
||||||
#include <private/qquickrendertarget_p.h>
|
|
||||||
#include <private/qrhi_p.h>
|
|
||||||
|
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
#include <QQmlComponent>
|
#include <QQmlComponent>
|
||||||
#include <QQmlEngine>
|
|
||||||
|
|
||||||
void QmlRenderer::initCoreApp()
|
void QmlRenderer::initCoreApp()
|
||||||
{
|
{
|
||||||
@@ -32,24 +28,25 @@ void QmlRenderer::initCoreApp()
|
|||||||
void QmlRenderer::populateParser()
|
void QmlRenderer::populateParser()
|
||||||
{
|
{
|
||||||
m_argParser.addOptions({
|
m_argParser.addOptions({
|
||||||
{QStringList() << "i" << "importpath",
|
{QStringList{"i", "importpath"},
|
||||||
"Prepend the given path to the import paths.",
|
"Prepend the given path to the import paths.",
|
||||||
"path"},
|
"path"},
|
||||||
|
|
||||||
{QStringList() << "o" << "outfile",
|
{QStringList{"o", "outfile"},
|
||||||
"Output image file path.",
|
"Output image file path.",
|
||||||
"path"},
|
"path"},
|
||||||
|
|
||||||
// "h" is reserved arg for help, so use capital letters for height/width
|
// "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.",
|
"Height of the final rendered image.",
|
||||||
"pixels"},
|
"pixels"},
|
||||||
|
|
||||||
{QStringList() << "W" << "width",
|
{QStringList{"W", "width"},
|
||||||
"Width of the final rendered image.",
|
"Width of the final rendered image.",
|
||||||
"pixels"},
|
"pixels"},
|
||||||
|
|
||||||
{QStringList() << "v" << "verbose", "Display additional output."}
|
{QStringList{"v", "verbose"},
|
||||||
|
"Display additional output."}
|
||||||
});
|
});
|
||||||
|
|
||||||
m_argParser.addPositionalArgument("file", "QML file to render.", "file");
|
m_argParser.addPositionalArgument("file", "QML file to render.", "file");
|
||||||
@@ -104,29 +101,37 @@ bool QmlRenderer::setupRenderer()
|
|||||||
|
|
||||||
QQuickDesignerSupport::activateDesignerMode();
|
QQuickDesignerSupport::activateDesignerMode();
|
||||||
|
|
||||||
QQmlEngine *engine = new QQmlEngine;
|
m_engine = std::make_unique<QQmlEngine>();
|
||||||
|
|
||||||
for (const QString &path : std::as_const(m_importPaths))
|
for (const QString &path : std::as_const(m_importPaths))
|
||||||
engine->addImportPath(path);
|
m_engine->addImportPath(path);
|
||||||
|
|
||||||
m_renderControl = new QQuickRenderControl;
|
m_renderControl = std::make_unique<QQuickRenderControl>();
|
||||||
m_window = new QQuickWindow(m_renderControl);
|
m_window = std::make_unique<QQuickWindow>(m_renderControl.get());
|
||||||
m_window->setDefaultAlphaBuffer(true);
|
m_window->setDefaultAlphaBuffer(true);
|
||||||
m_window->setColor(Qt::transparent);
|
m_window->setColor(Qt::transparent);
|
||||||
m_renderControl->initialize();
|
m_renderControl->initialize();
|
||||||
|
|
||||||
QQmlComponent component(engine);
|
QQmlComponent component(m_engine.get());
|
||||||
component.loadUrl(QUrl::fromLocalFile(m_sourceFile));
|
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();
|
QObject *renderObj = component.create();
|
||||||
|
|
||||||
if (renderObj) {
|
if (renderObj) {
|
||||||
#ifdef QUICK3D_MODULE
|
#ifdef QUICK3D_MODULE
|
||||||
QQuickItem *contentItem3D = nullptr;
|
QQuickItem *contentItem3D = nullptr;
|
||||||
|
renderObj->setParent(m_window->contentItem());
|
||||||
if (qobject_cast<QQuick3DObject *>(renderObj)) {
|
if (qobject_cast<QQuick3DObject *>(renderObj)) {
|
||||||
auto helper = new QmlDesigner::Internal::GeneralHelper();
|
m_helper = std::make_unique<QmlDesigner::Internal::GeneralHelper>();
|
||||||
engine->rootContext()->setContextProperty("_generalHelper", helper);
|
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"));
|
component.loadUrl(QUrl("qrc:/qtquickplugin/mockfiles/qt6/ModelNode3DImageView.qml"));
|
||||||
m_containerItem = qobject_cast<QQuickItem *>(component.create());
|
m_containerItem = qobject_cast<QQuickItem *>(component.create());
|
||||||
if (!m_containerItem) {
|
if (!m_containerItem) {
|
||||||
@@ -167,14 +172,22 @@ bool QmlRenderer::setupRenderer()
|
|||||||
} else if (auto renderWindow = qobject_cast<QQuickWindow *>(renderObj)) {
|
} else if (auto renderWindow = qobject_cast<QQuickWindow *>(renderObj)) {
|
||||||
// Hack to render Window items: reparent window content to m_window->contentItem()
|
// Hack to render Window items: reparent window content to m_window->contentItem()
|
||||||
m_renderSize = renderWindow->size();
|
m_renderSize = renderWindow->size();
|
||||||
renderWindow->setVisible(false);
|
|
||||||
m_containerItem = m_window->contentItem();
|
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<QQuickItem *> childItems = renderWindow->contentItem()->childItems();
|
const QList<QQuickItem *> childItems = renderWindow->contentItem()->childItems();
|
||||||
for (QQuickItem *item : childItems)
|
for (QQuickItem *item : childItems) {
|
||||||
|
item->setParent(m_window->contentItem());
|
||||||
item->setParentItem(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->setGeometry(0, 0, m_renderSize.width(), m_renderSize.height());
|
||||||
m_window->contentItem()->setSize(m_renderSize);
|
m_window->contentItem()->setSize(m_renderSize);
|
||||||
m_containerItem->setSize(m_renderSize);
|
m_containerItem->setSize(m_renderSize);
|
||||||
@@ -196,7 +209,7 @@ bool QmlRenderer::setupRenderer()
|
|||||||
bool QmlRenderer::initRhi()
|
bool QmlRenderer::initRhi()
|
||||||
{
|
{
|
||||||
if (!m_rhi) {
|
if (!m_rhi) {
|
||||||
QQuickRenderControlPrivate *rd = QQuickRenderControlPrivate::get(m_renderControl);
|
QQuickRenderControlPrivate *rd = QQuickRenderControlPrivate::get(m_renderControl.get());
|
||||||
m_rhi = rd->rhi;
|
m_rhi = rd->rhi;
|
||||||
if (!m_rhi) {
|
if (!m_rhi) {
|
||||||
error("Rhi is null.");
|
error("Rhi is null.");
|
||||||
@@ -204,36 +217,31 @@ bool QmlRenderer::initRhi()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_texTarget = nullptr;
|
m_texture.reset(m_rhi->newTexture(QRhiTexture::RGBA8, m_renderSize, 1,
|
||||||
m_rpDesc = nullptr;
|
QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource));
|
||||||
m_buffer = nullptr;
|
|
||||||
m_texture = nullptr;
|
|
||||||
|
|
||||||
m_texture = m_rhi->newTexture(QRhiTexture::RGBA8, m_renderSize, 1,
|
|
||||||
QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource);
|
|
||||||
if (!m_texture->create()) {
|
if (!m_texture->create()) {
|
||||||
error("QRhiTexture creation failed.");
|
error("QRhiTexture creation failed.");
|
||||||
return false;
|
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()) {
|
if (!m_buffer->create()) {
|
||||||
error("Depth/stencil buffer creation failed.");
|
error("Depth/stencil buffer creation failed.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
QRhiTextureRenderTargetDescription rtDesc {QRhiColorAttachment(m_texture)};
|
QRhiTextureRenderTargetDescription rtDesc {QRhiColorAttachment(m_texture.get())};
|
||||||
rtDesc.setDepthStencilBuffer(m_buffer);
|
rtDesc.setDepthStencilBuffer(m_buffer.get());
|
||||||
m_texTarget = m_rhi->newTextureRenderTarget(rtDesc);
|
m_texTarget.reset(m_rhi->newTextureRenderTarget(rtDesc));
|
||||||
m_rpDesc = m_texTarget->newCompatibleRenderPassDescriptor();
|
m_rpDesc.reset(m_texTarget->newCompatibleRenderPassDescriptor());
|
||||||
m_texTarget->setRenderPassDescriptor(m_rpDesc);
|
m_texTarget->setRenderPassDescriptor(m_rpDesc.get());
|
||||||
if (!m_texTarget->create()) {
|
if (!m_texTarget->create()) {
|
||||||
error("Texture render target creation failed.");
|
error("Texture render target creation failed.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// redirect Qt Quick rendering into our texture
|
// redirect Qt Quick rendering into our texture
|
||||||
m_window->setRenderTarget(QQuickRenderTarget::fromRhiRenderTarget(m_texTarget));
|
m_window->setRenderTarget(QQuickRenderTarget::fromRhiRenderTarget(m_texTarget.get()));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -242,15 +250,6 @@ void QmlRenderer::render()
|
|||||||
{
|
{
|
||||||
info(QString("Rendering: %1").arg(m_sourceFile));
|
info(QString("Rendering: %1").arg(m_sourceFile));
|
||||||
|
|
||||||
std::function<void (QQuickItem *)> 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;
|
QImage renderImage;
|
||||||
|
|
||||||
// Need to render fitted 3D views twice, first render updates spatial node geometries
|
// 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)
|
if (m_fit3D && i == 1)
|
||||||
QMetaObject::invokeMethod(m_containerItem, "fitToViewPort", Qt::DirectConnection);
|
QMetaObject::invokeMethod(m_containerItem, "fitToViewPort", Qt::DirectConnection);
|
||||||
|
|
||||||
updateNodesRecursive(m_containerItem);
|
|
||||||
m_renderControl->polishItems();
|
m_renderControl->polishItems();
|
||||||
m_renderControl->beginFrame();
|
m_renderControl->beginFrame();
|
||||||
m_renderControl->sync();
|
m_renderControl->sync();
|
||||||
@@ -277,14 +275,16 @@ void QmlRenderer::render()
|
|||||||
readResult.pixelSize.width(), readResult.pixelSize.height(),
|
readResult.pixelSize.width(), readResult.pixelSize.height(),
|
||||||
QImage::Format_RGBA8888_Premultiplied);
|
QImage::Format_RGBA8888_Premultiplied);
|
||||||
if (m_rhi->isYUpInFramebuffer())
|
if (m_rhi->isYUpInFramebuffer())
|
||||||
renderImage = wrapperImage.mirrored().scaled(m_requestedSize);
|
renderImage = wrapperImage.mirrored().scaled(m_requestedSize, Qt::IgnoreAspectRatio,
|
||||||
|
Qt::SmoothTransformation);
|
||||||
else
|
else
|
||||||
renderImage = wrapperImage.copy().scaled(m_requestedSize);
|
renderImage = wrapperImage.copy().scaled(m_requestedSize, Qt::IgnoreAspectRatio,
|
||||||
|
Qt::SmoothTransformation);
|
||||||
};
|
};
|
||||||
QRhiResourceUpdateBatch *readbackBatch = m_rhi->nextResourceUpdateBatch();
|
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);
|
rd->cb->resourceUpdate(readbackBatch);
|
||||||
|
|
||||||
m_renderControl->endFrame();
|
m_renderControl->endFrame();
|
||||||
|
@@ -5,15 +5,16 @@
|
|||||||
|
|
||||||
#include "../qmlbase.h"
|
#include "../qmlbase.h"
|
||||||
|
|
||||||
|
#include <rhi/qrhi.h>
|
||||||
|
|
||||||
|
#include <private/qquickrendercontrol_p.h>
|
||||||
|
#include <private/qquickrendertarget_p.h>
|
||||||
|
|
||||||
|
#include <QQmlEngine>
|
||||||
|
#include <QQuickWindow>
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
class QQuickItem;
|
class QQuickItem;
|
||||||
class QQuickRenderControl;
|
|
||||||
class QQuickWindow;
|
|
||||||
class QRhi;
|
|
||||||
class QRhiRenderBuffer;
|
|
||||||
class QRhiRenderPassDescriptor;
|
|
||||||
class QRhiTexture;
|
|
||||||
class QRhiTextureRenderTarget;
|
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
class QmlRenderer : public QmlBase
|
class QmlRenderer : public QmlBase
|
||||||
@@ -42,13 +43,15 @@ private:
|
|||||||
bool m_is3D = false;
|
bool m_is3D = false;
|
||||||
bool m_fit3D = false;
|
bool m_fit3D = false;
|
||||||
|
|
||||||
QQuickWindow *m_window = nullptr;
|
|
||||||
QQuickItem *m_containerItem = nullptr;
|
QQuickItem *m_containerItem = nullptr;
|
||||||
|
|
||||||
QQuickRenderControl *m_renderControl = nullptr;
|
|
||||||
QRhi *m_rhi = nullptr;
|
QRhi *m_rhi = nullptr;
|
||||||
QRhiTexture *m_texture = nullptr;
|
|
||||||
QRhiRenderBuffer *m_buffer = nullptr;
|
std::unique_ptr<QQmlEngine> m_engine;
|
||||||
QRhiTextureRenderTarget *m_texTarget = nullptr;
|
std::unique_ptr<QQuickRenderControl> m_renderControl;
|
||||||
QRhiRenderPassDescriptor *m_rpDesc = nullptr;
|
std::unique_ptr<QQuickWindow> m_window;
|
||||||
|
std::unique_ptr<QObject> m_helper;
|
||||||
|
std::unique_ptr<QRhiTexture> m_texture;
|
||||||
|
std::unique_ptr<QRhiRenderBuffer> m_buffer;
|
||||||
|
std::unique_ptr<QRhiTextureRenderTarget> m_texTarget;
|
||||||
|
std::unique_ptr<QRhiRenderPassDescriptor> m_rpDesc;
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user