diff options
author | Laszlo Agocs <laszlo.agocs@qt.io> | 2018-01-29 12:08:00 +0100 |
---|---|---|
committer | Laszlo Agocs <laszlo.agocs@qt.io> | 2018-02-01 12:51:23 +0000 |
commit | 0e04f0d4dad213e323d11f4814581c8afc2b7e9d (patch) | |
tree | 0be19e577f689e9d0bb23730728b24ad5a1e8728 /src/imports/studio3d/q3dsstudio3ditem.cpp | |
parent | fa1cc116705dfef96afb17bef995f9000c2355be (diff) |
Studio3D: add scenegraph node and renderer object
Not functional yet, although the implementation is mostly complete.
The API is obviously not there, for now we'll just have a source property
to allow testing the internals.
Change-Id: I6b189aa30a0bc486edb041879e094641e0e51ffc
Reviewed-by: Andy Nichols <andy.nichols@qt.io>
Diffstat (limited to 'src/imports/studio3d/q3dsstudio3ditem.cpp')
-rw-r--r-- | src/imports/studio3d/q3dsstudio3ditem.cpp | 186 |
1 files changed, 186 insertions, 0 deletions
diff --git a/src/imports/studio3d/q3dsstudio3ditem.cpp b/src/imports/studio3d/q3dsstudio3ditem.cpp index 2f710a6..ff642ea 100644 --- a/src/imports/studio3d/q3dsstudio3ditem.cpp +++ b/src/imports/studio3d/q3dsstudio3ditem.cpp @@ -28,18 +28,204 @@ ****************************************************************************/ #include "q3dsstudio3ditem_p.h" +#include "q3dsstudio3drenderer_p.h" +#include "q3dsstudio3dnode_p.h" +#include <QSGNode> +#include <QLoggingCategory> +#include <QRunnable> +#include <QQuickWindow> +#include <QQuickRenderControl> +#include <QOffscreenSurface> +#include <QQmlFile> +#include <QGuiApplication> +#include <Q3DSEngine> +#include <private/q3dsutils_p.h> +#include <Qt3DCore/QEntity> +#include <Qt3DRender/QRenderSurfaceSelector> +#include <Qt3DRender/private/qrendersurfaceselector_p.h> QT_BEGIN_NAMESPACE +Q_LOGGING_CATEGORY(lcStudio3D, "q3ds.studio3d") + Q3DSStudio3DItem::Q3DSStudio3DItem(QQuickItem *parent) : QQuickItem(parent) { setFlag(QQuickItem::ItemHasContents, true); setAcceptedMouseButtons(Qt::MouseButtonMask); + Q3DSUtils::setDialogsEnabled(false); } Q3DSStudio3DItem::~Q3DSStudio3DItem() { } +QUrl Q3DSStudio3DItem::source() const +{ + return m_source; +} + +void Q3DSStudio3DItem::setSource(const QUrl &newSource) +{ + if (m_source == newSource) + return; + + m_source = newSource; + emit sourceChanged(); + + if (window()) // else defer to itemChange() + createEngine(); +} + +void Q3DSStudio3DItem::createEngine() +{ + // note: cannot have an engine without the source set + // (since once we assume m_engine!=nullptr, m_engine->renderAspect() must be valid as well) + + if (!m_engine) { + qCDebug(lcStudio3D, "creating engine"); + QQuickWindow *w = window(); + Q_ASSERT(w); + + m_engine = new Q3DSEngine; + // Rendering will be driven manually from the Quick render thread via the QRenderAspect. + // We create the render aspect ourselves on the Quick render thread. + m_engine->setFlags(Q3DSEngine::WithoutRenderAspect); + + if (QWindow *rw = QQuickRenderControl::renderWindowFor(w)) { + // rw is the top-level window that is backed by a native window. Do + // not use that though since we must not clash with e.g. the widget + // backingstore compositor in the gui thread. + QOffscreenSurface *dummySurface = new QOffscreenSurface; + dummySurface->setParent(qGuiApp); // parent to something suitably long-living + dummySurface->setFormat(rw->format()); + dummySurface->create(); + m_engine->setSurface(dummySurface); + } else { + m_engine->setSurface(w); + } + } + + const QString fn = QQmlFile::urlToLocalFileOrQrc(m_source); + qCDebug(lcStudio3D) << "source is now" << fn; + const QSize sz(width(), height()); + if (!sz.isEmpty()) + sendResize(sz); + + m_engine->setSource(fn); + + // cannot start() here, that must be deferred + + update(); +} + +QSGNode *Q3DSStudio3DItem::updatePaintNode(QSGNode *node, QQuickItem::UpdatePaintNodeData *) +{ + // this on the render thread; the engine lives on the gui thread and should + // be ready at this point - unless there's no source set yet + + if (!m_engine) { + delete node; + return nullptr; + } + + Q3DSStudio3DNode *n = static_cast<Q3DSStudio3DNode *>(node); + if (!n) + n = new Q3DSStudio3DNode; + + n->setRect(boundingRect()); + + if (!m_renderer) + m_renderer = new Q3DSStudio3DRenderer(this, n, m_engine->aspectEngine()); + + return n; +} + +// Let's do resource handling correctly, which means handling releaseResources +// on the item and connecting to the sceneGraphInvalidated signal. Care must be +// taken to support the case of moving the item from window to another as well. + +void Q3DSStudio3DItem::itemChange(QQuickItem::ItemChange change, + const QQuickItem::ItemChangeData &changeData) +{ + if (change == QQuickItem::ItemSceneChange) { + if (changeData.window) { + connect(changeData.window, &QQuickWindow::sceneGraphInvalidated, this, [this]() { + // render thread + qCDebug(lcStudio3D, "[R] sceneGraphInvalidated"); + delete m_renderer; + m_renderer = nullptr; + QMetaObject::invokeMethod(this, "destroyEngine"); + }, Qt::DirectConnection); + + if (!m_source.isEmpty() && !m_engine) + createEngine(); + } + } +} + +void Q3DSStudio3DItem::startEngine() +{ + // set root entity + m_engine->start(); +} + +void Q3DSStudio3DItem::destroyEngine() +{ + if (m_engine) { + qCDebug(lcStudio3D, "destroying engine"); + delete m_engine; // recreate on next window change - if we are still around, that is + m_engine = nullptr; + } +} + +class Releaser : public QRunnable +{ +public: + Releaser(Q3DSStudio3DRenderer *r) : m_renderer(r) { } + void run() override { + delete m_renderer; + } +private: + Q3DSStudio3DRenderer *m_renderer; +}; + +void Q3DSStudio3DItem::releaseResources() +{ + qCDebug(lcStudio3D, "releaseResources"); + + // by the time the runnable runs we (the item) may already be gone; that's fine, just pass the renderer ref + if (window()) { + destroyEngine(); + if (m_renderer) { + window()->scheduleRenderJob(new Releaser(m_renderer), QQuickWindow::BeforeSynchronizingStage); + // this may not be an application exit; if this is just a window change then allow continuing + // by eventually creating a new renderer object + m_renderer = nullptr; + } + } +} + +void Q3DSStudio3DItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + QQuickItem::geometryChanged(newGeometry, oldGeometry); + + if (!newGeometry.isEmpty() && m_engine) + sendResize(newGeometry.size().toSize()); +} + +void Q3DSStudio3DItem::sendResize(const QSize &size) +{ + Q_ASSERT(m_engine); + m_engine->resize(size); + + Qt3DCore::QEntity *rootEntity = m_engine->rootEntity(); + Qt3DRender::QRenderSurfaceSelector *surfaceSelector = Qt3DRender::QRenderSurfaceSelectorPrivate::find(rootEntity); + qCDebug(lcStudio3D, "Setting external render target size on surface selector %p", surfaceSelector); + if (surfaceSelector) { + surfaceSelector->setExternalRenderTargetSize(size); + surfaceSelector->setSurfacePixelRatio(window()->effectiveDevicePixelRatio()); + } +} + QT_END_NAMESPACE |