summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Lemire <paul.lemire@kdab.com>2019-07-05 09:06:35 +0200
committerPaul Lemire <paul.lemire@kdab.com>2019-08-07 12:08:32 +0200
commit6631bf0983250e1a86cb9d27628c44b6e607609b (patch)
tree5ea8ee81d2119df25b0f2a87532f7d7d2dcf03a8
parentda2c94c13c163922467e6d88e19442fc12aea523 (diff)
Make Scene3D rendering use the Manual Qt3D drive mode
This now makes Scene3D rendering fully synchronous and blocking: - All Qt3D jobs have to be executed before we can render the GL commands This makes the blocking mode that could be activated with SCENE3D_BLOCKING_RENDERMODE the default behavior now and therefore we could remove references to it in the code. This now means that Qt3D and QtQuick will be rendering at the same refresh rate, which in most cases won't be noticeable and will ensure that content from Qt3D scenes matches content from QtQuick scenes. The only downside is if the Qt3D rendering takes longer than the time expected by QtQuick, the whole QtQuick rendering will be slowed down. Previously the QtQuick rendering might have still run at 60fps while the Qt3D rendering at a lower fps. Now the QtQuick fps would be the same as the Qt3D fps. That being said, the old behavior also meant that if Qt3D didn't catch up, the delay between QtQuick and Qt3D would only increase frame after frame. This change allow to simplify the internals by making Scene3D and regular Qt3D use the same code paths internally. Please note that Scene3D relies on QQuickWindow::afterAnimating being called each frame. When using a QQuickRenderControl this means you might have to call it manually as part of your rendering code. Task-number: QTBUG-72385 Change-Id: I887daf6df632e296a892b844e738a67e973fee7f Reviewed-by: Mike Krus <mike.krus@kdab.com>
-rw-r--r--src/quick3d/imports/scene3d/scene3ditem.cpp79
-rw-r--r--src/quick3d/imports/scene3d/scene3ditem_p.h3
-rw-r--r--src/quick3d/imports/scene3d/scene3drenderer.cpp54
-rw-r--r--src/quick3d/imports/scene3d/scene3drenderer_p.h15
-rw-r--r--src/quick3d/imports/scene3d/scene3dsgmaterialshader.cpp28
-rw-r--r--src/render/backend/abstractrenderer_p.h2
-rw-r--r--src/render/frontend/qrenderaspect.cpp4
-rw-r--r--src/render/frontend/qrenderaspect_p.h2
-rw-r--r--src/render/renderers/opengl/renderer/renderer.cpp58
-rw-r--r--src/render/renderers/opengl/renderer/renderer_p.h2
-rw-r--r--src/render/services/vsyncframeadvanceservice.cpp11
-rw-r--r--tests/auto/render/commons/testrenderer.h2
-rw-r--r--tests/manual/manual.pro3
-rw-r--r--tests/manual/scene3d-in-sync/main.cpp66
-rw-r--r--tests/manual/scene3d-in-sync/main.qml174
-rw-r--r--tests/manual/scene3d-in-sync/scene3d-in-sync.pro10
-rw-r--r--tests/manual/scene3d-in-sync/scene3d-in-sync.qrc5
17 files changed, 412 insertions, 106 deletions
diff --git a/src/quick3d/imports/scene3d/scene3ditem.cpp b/src/quick3d/imports/scene3d/scene3ditem.cpp
index f5173497b..9da35a34f 100644
--- a/src/quick3d/imports/scene3d/scene3ditem.cpp
+++ b/src/quick3d/imports/scene3d/scene3ditem.cpp
@@ -66,11 +66,17 @@
#include <QtQuick/qquickrendercontrol.h>
#include <Qt3DRender/private/qrendersurfaceselector_p.h>
+#include <Qt3DRender/private/qrenderaspect_p.h>
+#include <Qt3DRender/private/rendersettings_p.h>
#include <scene3dcleaner_p.h>
#include <scene3dlogging_p.h>
#include <scene3drenderer_p.h>
#include <scene3dsgnode_p.h>
+#include <Qt3DCore/private/qaspectengine_p.h>
+#include <Qt3DCore/private/qaspectmanager_p.h>
+#include <QThread>
+
QT_BEGIN_NAMESPACE
namespace Qt3DRender {
@@ -125,11 +131,15 @@ Scene3DItem::Scene3DItem(QQuickItem *parent)
, m_renderer(nullptr)
, m_rendererCleaner(new Scene3DCleaner())
, m_multisample(true)
+ , m_dirty(true)
, m_cameraAspectRatioMode(AutomaticAspectRatio)
{
setFlag(QQuickItem::ItemHasContents, true);
setAcceptedMouseButtons(Qt::MouseButtonMask);
// TO DO: register the event source in the main thread
+
+ // Use manual drive mode when using Scene3D
+ m_aspectEngine->setRunMode(Qt3DCore::QAspectEngine::Manual);
}
Scene3DItem::~Scene3DItem()
@@ -285,6 +295,65 @@ void Scene3DItem::applyRootEntityChange()
}
}
+bool Scene3DItem::needsRender()
+{
+ auto renderAspectPriv = static_cast<QRenderAspectPrivate*>(QRenderAspectPrivate::get(m_renderAspect));
+ const bool dirty = m_dirty
+ || (renderAspectPriv
+ && renderAspectPriv->m_renderer
+ && renderAspectPriv->m_renderer->settings()
+ && renderAspectPriv->m_renderer->settings()->renderPolicy() == QRenderSettings::Always);
+ m_dirty = false;
+ return dirty;
+}
+
+// This function is triggered in the context of the Main Thread
+// when afterAnimating is emitted
+
+// The QtQuick SG proceeds like indicated below:
+// afterAnimating (Main Thread)
+// beforeSynchronizing (SG Thread and MainThread locked)
+// afterSynchronizing (SG Thread)
+// beforeRendering (SG Thread)
+
+// Note: we connect to afterAnimating rather than beforeSynchronizing as a
+// direct connection on beforeSynchronizing is executed within the SG Render
+// Thread context
+void Scene3DItem::onBeforeSync()
+{
+ // Has anything in the 3D scene actually changed that requires us to render?
+ if (!needsRender())
+ return;
+
+ Q_ASSERT(QThread::currentThread() == thread());
+
+ // Since we are in manual mode, trigger jobs for the next frame
+ Qt3DCore::QAspectEnginePrivate *aspectEnginePriv = static_cast<Qt3DCore::QAspectEnginePrivate *>(QObjectPrivate::get(m_aspectEngine));
+ if (!aspectEnginePriv->m_initialized)
+ return;
+
+ Q_ASSERT(m_aspectEngine->runMode() == Qt3DCore::QAspectEngine::Manual);
+ m_aspectEngine->processFrame();
+ // The above essentially sets the number of RV for the RenderQueue and
+ // processes the jobs for the frame (it's blocking) When
+ // Scene3DRender::updatePaintNode is called, following this step, we know
+ // that the RenderQueue target count has been set and that everything
+ // should be ready for rendering
+
+
+ // processFrame() must absolutely be followed by a single call to
+ // render
+ // At startup, we have no garantee that the QtQuick Render Thread doesn't
+ // start rendering before this function has been called
+ // We add in a safety to skip such frames as this could otherwise
+ // make Qt3D enter a locked state
+ if (m_renderer)
+ m_renderer->allowRender();
+
+ // Request refresh for next frame
+ QQuickItem::update();
+}
+
void Scene3DItem::setWindowSurface(QObject *rootObject)
{
Qt3DRender::QRenderSurfaceSelector *surfaceSelector = Qt3DRender::QRenderSurfaceSelectorPrivate::find(rootObject);
@@ -389,6 +458,16 @@ QSGNode *Scene3DItem::updatePaintNode(QSGNode *node, QQuickItem::UpdatePaintNode
if (m_renderAspect == nullptr) {
m_renderAspect = new QRenderAspect(QRenderAspect::Synchronous);
m_aspectEngine->registerAspect(m_renderAspect);
+
+ // Before Synchronizing is in the SG Thread, we want beforeSync to be triggered
+ // in the context of the main thread
+ QObject::connect(window(), &QQuickWindow::afterAnimating,
+ this, &Scene3DItem::onBeforeSync, Qt::DirectConnection);
+ auto renderAspectPriv = static_cast<QRenderAspectPrivate*>(QRenderAspectPrivate::get(m_renderAspect));
+ QObject::connect(renderAspectPriv->m_aspectManager->changeArbiter(), &Qt3DCore::QChangeArbiter::receivedChange,
+ this, [this] { m_dirty = true; }, Qt::DirectConnection);
+ QObject::connect(renderAspectPriv->m_aspectManager->changeArbiter(), &Qt3DCore::QChangeArbiter::receivedChange,
+ this, &QQuickItem::update, Qt::AutoConnection);
}
if (m_renderer == nullptr) {
diff --git a/src/quick3d/imports/scene3d/scene3ditem_p.h b/src/quick3d/imports/scene3d/scene3ditem_p.h
index 4106ff459..a23bb9e5b 100644
--- a/src/quick3d/imports/scene3d/scene3ditem_p.h
+++ b/src/quick3d/imports/scene3d/scene3ditem_p.h
@@ -113,6 +113,7 @@ Q_SIGNALS:
private Q_SLOTS:
void applyRootEntityChange();
+ void onBeforeSync();
private:
QSGNode *updatePaintNode(QSGNode *node, UpdatePaintNodeData *nodeData) override;
@@ -120,6 +121,7 @@ private:
void setCameraAspectModeHelper();
void updateCameraAspectRatio();
void mousePressEvent(QMouseEvent *event) override;
+ bool needsRender();
QStringList m_aspects;
Qt3DCore::QEntity *m_entity;
@@ -130,6 +132,7 @@ private:
Scene3DCleaner *m_rendererCleaner;
bool m_multisample;
+ bool m_dirty;
QPointer<Qt3DRender::QCamera> m_camera;
CameraAspectRatioMode m_cameraAspectRatioMode;
diff --git a/src/quick3d/imports/scene3d/scene3drenderer.cpp b/src/quick3d/imports/scene3d/scene3drenderer.cpp
index bda8e7343..596ad0b84 100644
--- a/src/quick3d/imports/scene3d/scene3drenderer.cpp
+++ b/src/quick3d/imports/scene3d/scene3drenderer.cpp
@@ -48,7 +48,6 @@
#include <Qt3DRender/private/qrenderaspect_p.h>
#include <Qt3DRender/private/abstractrenderer_p.h>
-#include <Qt3DRender/private/rendersettings_p.h>
#include <Qt3DCore/private/qaspectengine_p.h>
#include <Qt3DCore/private/qaspectmanager_p.h>
#include <Qt3DCore/private/qchangearbiter_p.h>
@@ -158,9 +157,9 @@ Scene3DRenderer::Scene3DRenderer(Scene3DItem *item, Qt3DCore::QAspectEngine *asp
, m_multisample(false) // this value is not used, will be synced from the Scene3DItem instead
, m_lastMultisample(false)
, m_needsShutdown(true)
- , m_blocking(false)
, m_forceRecreate(false)
- , m_dirty(true) // we want to render at least once
+ , m_shouldRender(false)
+ , m_allowRendering(0)
{
Q_CHECK_PTR(m_item);
Q_CHECK_PTR(m_item->window());
@@ -177,19 +176,10 @@ Scene3DRenderer::Scene3DRenderer(Scene3DItem *item, Qt3DCore::QAspectEngine *asp
m_window = w;
});
- auto renderAspectPriv = static_cast<QRenderAspectPrivate*>(QRenderAspectPrivate::get(m_renderAspect));
- QObject::connect(renderAspectPriv->m_aspectManager->changeArbiter(), &Qt3DCore::QChangeArbiter::receivedChange,
- this, [this] { m_dirty = true; }, Qt::DirectConnection);
- QObject::connect(renderAspectPriv->m_aspectManager->changeArbiter(), &Qt3DCore::QChangeArbiter::receivedChange,
- m_item, &QQuickItem::update, Qt::AutoConnection);
-
Q_ASSERT(QOpenGLContext::currentContext());
ContextSaver saver;
static_cast<QRenderAspectPrivate*>(QRenderAspectPrivate::get(m_renderAspect))->renderInitialize(saver.context());
scheduleRootEntityChange();
-
- const bool blockingRendermode = !qgetenv("SCENE3D_BLOCKING_RENDERMODE").isEmpty();
- m_blocking = blockingRendermode;
}
Scene3DRenderer::~Scene3DRenderer()
@@ -197,16 +187,6 @@ Scene3DRenderer::~Scene3DRenderer()
qCDebug(Scene3D) << Q_FUNC_INFO << QThread::currentThread();
}
-bool Scene3DRenderer::shouldRender() const
-{
- auto renderAspectPriv = static_cast<QRenderAspectPrivate*>(QRenderAspectPrivate::get(m_renderAspect));
- return m_dirty
- || (renderAspectPriv
- && renderAspectPriv->m_renderer
- && renderAspectPriv->m_renderer->settings()
- && renderAspectPriv->m_renderer->settings()->renderPolicy() == QRenderSettings::Always);
-}
-
QOpenGLFramebufferObject *Scene3DRenderer::createMultisampledFramebufferObject(const QSize &size)
{
@@ -288,7 +268,15 @@ void Scene3DRenderer::onWindowChanged(QQuickWindow *w)
void Scene3DRenderer::synchronize()
{
- if (shouldRender() && m_item && m_window) {
+ if (m_item && m_window) {
+
+ // Only render if we are sure aspectManager->processFrame was called prior
+ // We could otherwise enter a deadlock state
+ if (!m_allowRendering.tryAcquire(std::max(m_allowRendering.available(), 1)))
+ return;
+
+ m_shouldRender = true;
+
m_multisample = m_item->multisample();
if (m_aspectEngine->rootEntity() != m_item->entity()) {
@@ -299,7 +287,7 @@ void Scene3DRenderer::synchronize()
const QSize currentSize = boundingRectSize * m_window->effectiveDevicePixelRatio();
const bool sizeHasChanged = currentSize != m_lastSize;
const bool multisampleHasChanged = m_multisample != m_lastMultisample;
- m_forceRecreate = sizeHasChanged || multisampleHasChanged;
+ m_forceRecreate |= sizeHasChanged || multisampleHasChanged;
if (sizeHasChanged) {
static const QMetaMethod setItemAreaAndDevicePixelRatio = setItemAreaAndDevicePixelRatioMethod();
@@ -312,26 +300,30 @@ void Scene3DRenderer::synchronize()
m_lastSize = currentSize;
m_lastMultisample = m_multisample;
- m_node->markDirty(QSGNode::DirtyMaterial);
+ if (m_node)
+ m_node->markDirty(QSGNode::DirtyMaterial);
m_item->update();
}
}
+void Scene3DRenderer::allowRender()
+{
+ m_allowRendering.release(1);
+}
+
void Scene3DRenderer::setSGNode(Scene3DSGNode *node)
{
m_node = node;
- if (!m_texture.isNull())
- node->setTexture(m_texture.data());
}
void Scene3DRenderer::render()
{
QMutexLocker l(&m_windowMutex);
// Lock to ensure the window doesn't change while we are rendering
- if (!m_window || !shouldRender())
+ if (!m_window || !m_shouldRender)
return;
- m_dirty = false;
+ m_shouldRender = false;
ContextSaver saver;
@@ -354,6 +346,8 @@ void Scene3DRenderer::render()
m_node->setTexture(m_texture.data());
}
+ m_forceRecreate = false;
+
// Bind FBO
if (m_multisample) //Only try to use MSAA when available
m_multisampledFBO->bind();
@@ -361,7 +355,7 @@ void Scene3DRenderer::render()
m_finalFBO->bind();
// Render Qt3D Scene
- static_cast<QRenderAspectPrivate*>(QRenderAspectPrivate::get(m_renderAspect))->renderSynchronous(m_blocking);
+ static_cast<QRenderAspectPrivate*>(QRenderAspectPrivate::get(m_renderAspect))->renderSynchronous();
// We may have called doneCurrent() so restore the context if the rendering surface was changed
// Note: keep in mind that the ContextSave also restores the surface when destroyed
diff --git a/src/quick3d/imports/scene3d/scene3drenderer_p.h b/src/quick3d/imports/scene3d/scene3drenderer_p.h
index cbf11b12e..cfb7f23d2 100644
--- a/src/quick3d/imports/scene3d/scene3drenderer_p.h
+++ b/src/quick3d/imports/scene3d/scene3drenderer_p.h
@@ -54,6 +54,7 @@
#include <QtCore/QObject>
#include <QtCore/qsize.h>
#include <QtCore/QMutex>
+#include <QtCore/QSemaphore>
QT_BEGIN_NAMESPACE
@@ -81,12 +82,9 @@ public:
QRenderAspect *renderAspect);
~Scene3DRenderer();
- QOpenGLFramebufferObject *createMultisampledFramebufferObject(const QSize &size);
- QOpenGLFramebufferObject *createFramebufferObject(const QSize &size);
- void scheduleRootEntityChange();
void setSGNode(Scene3DSGNode *node);
void setCleanerHelper(Scene3DCleaner *cleaner);
- void synchronize();
+ void allowRender();
public Q_SLOTS:
void render();
@@ -95,7 +93,10 @@ public Q_SLOTS:
void onWindowChanged(QQuickWindow *w);
private:
- bool shouldRender() const;
+ QOpenGLFramebufferObject *createMultisampledFramebufferObject(const QSize &size);
+ QOpenGLFramebufferObject *createFramebufferObject(const QSize &size);
+ void synchronize();
+ void scheduleRootEntityChange();
Scene3DItem *m_item; // Will be released by the QQuickWindow/QML Engine
Qt3DCore::QAspectEngine *m_aspectEngine; // Will be released by the Scene3DRendererCleaner
@@ -111,9 +112,9 @@ private:
bool m_multisample;
bool m_lastMultisample;
bool m_needsShutdown;
- bool m_blocking;
bool m_forceRecreate;
- bool m_dirty;
+ bool m_shouldRender;
+ QSemaphore m_allowRendering;
friend class Scene3DCleaner;
};
diff --git a/src/quick3d/imports/scene3d/scene3dsgmaterialshader.cpp b/src/quick3d/imports/scene3d/scene3dsgmaterialshader.cpp
index 3fab345e1..d25c6a7f7 100644
--- a/src/quick3d/imports/scene3d/scene3dsgmaterialshader.cpp
+++ b/src/quick3d/imports/scene3d/scene3dsgmaterialshader.cpp
@@ -154,21 +154,23 @@ void Scene3DSGMaterialShader::updateState(const RenderState &state, QSGMaterial
QSGTexture *t = tx->texture();
- bool npotSupported = const_cast<QOpenGLContext *>(state.context())
- ->functions()->hasOpenGLFeature(QOpenGLFunctions::NPOTTextureRepeat);
- if (!npotSupported) {
- QSize size = t->textureSize();
- const bool isNpot = !isPowerOfTwo(size.width()) || !isPowerOfTwo(size.height());
- if (isNpot) {
- t->setHorizontalWrapMode(QSGTexture::ClampToEdge);
- t->setVerticalWrapMode(QSGTexture::ClampToEdge);
+ if (t != nullptr) {
+ bool npotSupported = const_cast<QOpenGLContext *>(state.context())
+ ->functions()->hasOpenGLFeature(QOpenGLFunctions::NPOTTextureRepeat);
+ if (!npotSupported) {
+ QSize size = t->textureSize();
+ const bool isNpot = !isPowerOfTwo(size.width()) || !isPowerOfTwo(size.height());
+ if (isNpot) {
+ t->setHorizontalWrapMode(QSGTexture::ClampToEdge);
+ t->setVerticalWrapMode(QSGTexture::ClampToEdge);
+ }
}
- }
- if (oldTx == 0 || oldTx->texture()->textureId() != t->textureId())
- t->bind();
- else
- t->updateBindOptions();
+ if (oldTx == 0 || oldTx->texture()->textureId() != t->textureId())
+ t->bind();
+ else
+ t->updateBindOptions();
+ }
if (state.isMatrixDirty())
program()->setUniformValue(m_matrixId, state.combinedMatrix());
diff --git a/src/render/backend/abstractrenderer_p.h b/src/render/backend/abstractrenderer_p.h
index 8aa994eb2..5891f966d 100644
--- a/src/render/backend/abstractrenderer_p.h
+++ b/src/render/backend/abstractrenderer_p.h
@@ -141,7 +141,7 @@ public:
// Threaded renderer
virtual void render() = 0;
// Synchronous renderer
- virtual void doRender(bool scene3dBlocking = false) = 0;
+ virtual void doRender() = 0;
virtual void cleanGraphicsResources() = 0;
diff --git a/src/render/frontend/qrenderaspect.cpp b/src/render/frontend/qrenderaspect.cpp
index 190b01f11..08ec0b732 100644
--- a/src/render/frontend/qrenderaspect.cpp
+++ b/src/render/frontend/qrenderaspect.cpp
@@ -437,9 +437,9 @@ void QRenderAspectPrivate::renderInitialize(QOpenGLContext *context)
}
/*! \internal */
-void QRenderAspectPrivate::renderSynchronous(bool blocking)
+void QRenderAspectPrivate::renderSynchronous()
{
- m_renderer->doRender(blocking);
+ m_renderer->doRender();
}
/*
diff --git a/src/render/frontend/qrenderaspect_p.h b/src/render/frontend/qrenderaspect_p.h
index 4a091e164..2147ee35c 100644
--- a/src/render/frontend/qrenderaspect_p.h
+++ b/src/render/frontend/qrenderaspect_p.h
@@ -90,7 +90,7 @@ public:
void loadSceneParsers();
void loadRenderPlugin(const QString &pluginName);
void renderInitialize(QOpenGLContext *context);
- void renderSynchronous(bool blocking = false);
+ void renderSynchronous();
void renderShutdown();
void registerBackendType(const QMetaObject &, const Qt3DCore::QBackendNodeMapperPtr &functor);
QVector<Qt3DCore::QAspectJobPtr> createGeometryRendererJobs();
diff --git a/src/render/renderers/opengl/renderer/renderer.cpp b/src/render/renderers/opengl/renderer/renderer.cpp
index b60eea33d..88dd77f39 100644
--- a/src/render/renderers/opengl/renderer/renderer.cpp
+++ b/src/render/renderers/opengl/renderer/renderer.cpp
@@ -627,34 +627,20 @@ void Renderer::render()
}
}
-void Renderer::doRender(bool scene3dBlocking)
+void Renderer::doRender()
{
Renderer::ViewSubmissionResultData submissionData;
bool hasCleanedQueueAndProceeded = false;
bool preprocessingComplete = false;
bool beganDrawing = false;
+
+ // Blocking until RenderQueue is full
const bool canSubmit = isReadyToSubmit();
// Lock the mutex to protect access to the renderQueue while we look for its state
QMutexLocker locker(m_renderQueue->mutex());
- bool queueIsComplete = m_renderQueue->isFrameQueueComplete();
- bool queueIsEmpty = m_renderQueue->targetRenderViewCount() == 0;
-
- // Scene3D Blocking Mode
- if (scene3dBlocking && !queueIsComplete && !queueIsEmpty) {
- int i = 0;
- // We wait at most 10ms to avoid a case we could never recover from
- while (!queueIsComplete && !queueIsEmpty && i++ < 10) {
- qCDebug(Backend) << Q_FUNC_INFO << "Waiting for ready queue (try:" << i << "/ 10)";
- locker.unlock();
- // Give worker threads a chance to complete the queue
- QThread::msleep(1);
- locker.relock();
- queueIsComplete = m_renderQueue->isFrameQueueComplete();
- // This could become true if we've tried to shutdown
- queueIsEmpty = m_renderQueue->targetRenderViewCount() == 0;
- }
- }
+ const bool queueIsComplete = m_renderQueue->isFrameQueueComplete();
+ const bool queueIsEmpty = m_renderQueue->targetRenderViewCount() == 0;
// When using synchronous rendering (QtQuick)
// We are not sure that the frame queue is actually complete
@@ -751,16 +737,12 @@ void Renderer::doRender(bool scene3dBlocking)
#endif
}
- // Only reset renderQueue and proceed to next frame if the submission
- // succeeded or if we are using a render thread and that is wasn't performed
- // already
-
// If hasCleanedQueueAndProceeded isn't true this implies that something went wrong
// with the rendering and/or the renderqueue is incomplete from some reason
- // (in the case of scene3d the render jobs may be taking too long ....)
// or alternatively it could be complete but empty (RenderQueue of size 0)
- if (!hasCleanedQueueAndProceeded &&
- (m_renderThread || queueIsComplete || queueIsEmpty)) {
+
+
+ if (!hasCleanedQueueAndProceeded) {
// RenderQueue was full but something bad happened when
// trying to render it and therefore proceedToNextFrame was not called
// Note: in this case the renderQueue mutex is still locked
@@ -825,21 +807,19 @@ bool Renderer::canRender() const
bool Renderer::isReadyToSubmit()
{
- // If we are using a render thread, make sure that
- // we've been told to render before rendering
- if (m_renderThread) { // Prevent ouf of order execution
- m_submitRenderViewsSemaphore.acquire(1);
+ // Make sure that we've been told to render before rendering
+ // Prevent ouf of order execution
+ m_submitRenderViewsSemaphore.acquire(1);
- // Check if shutdown has been requested
- if (m_running.load() == 0)
- return false;
+ // Check if shutdown has been requested
+ if (m_running.load() == 0)
+ return false;
- // When using Thread rendering, the semaphore should only
- // be released when the frame queue is complete and there's
- // something to render
- // The case of shutdown should have been handled just before
- Q_ASSERT(m_renderQueue->isFrameQueueComplete());
- }
+ // The semaphore should only
+ // be released when the frame queue is complete and there's
+ // something to render
+ // The case of shutdown should have been handled just before
+ Q_ASSERT(m_renderQueue->isFrameQueueComplete());
return true;
}
diff --git a/src/render/renderers/opengl/renderer/renderer_p.h b/src/render/renderers/opengl/renderer/renderer_p.h
index e82d18f77..7c1df79cd 100644
--- a/src/render/renderers/opengl/renderer/renderer_p.h
+++ b/src/render/renderers/opengl/renderer/renderer_p.h
@@ -182,7 +182,7 @@ public:
void releaseGraphicsResources() override;
void render() override;
- void doRender(bool scene3dBlocking = false) override;
+ void doRender() override;
void cleanGraphicsResources() override;
bool isRunning() const override { return m_running.load(); }
diff --git a/src/render/services/vsyncframeadvanceservice.cpp b/src/render/services/vsyncframeadvanceservice.cpp
index 8749a54ab..b49870e68 100644
--- a/src/render/services/vsyncframeadvanceservice.cpp
+++ b/src/render/services/vsyncframeadvanceservice.cpp
@@ -80,16 +80,7 @@ qint64 VSyncFrameAdvanceService::waitForNextFrame()
{
Q_D(VSyncFrameAdvanceService);
- // When rendering with Scene3D, we always want to acquire the available
- // amount + 1 to handle the cases where for some reason proceedToNextFrame
- // is being called more than once between calls to waitForNextFrame This
- // could be the case when resizing the window
-
- // When Qt3D is driving rendering however, this shouldn't happen
- if (d->m_drivenByRenderThread)
- d->m_semaphore.acquire(1);
- else
- d->m_semaphore.acquire(d->m_semaphore.available() + 1);
+ d->m_semaphore.acquire(std::max(d->m_semaphore.available(), 1));
const quint64 currentTime = d->m_elapsed.nsecsElapsed();
qCDebug(VSyncAdvanceService) << "Elapsed nsecs since last call " << currentTime - d->m_elapsedTimeSincePreviousFrame;
diff --git a/tests/auto/render/commons/testrenderer.h b/tests/auto/render/commons/testrenderer.h
index 124e30492..c4c1ed92b 100644
--- a/tests/auto/render/commons/testrenderer.h
+++ b/tests/auto/render/commons/testrenderer.h
@@ -52,7 +52,7 @@ public:
void shutdown() override {}
void releaseGraphicsResources() override {}
void render() override {}
- void doRender(bool scene3dBlocking = false) override { Q_UNUSED(scene3dBlocking); }
+ void doRender() override {}
void cleanGraphicsResources() override {}
bool isRunning() const override { return true; }
bool shouldRender() override { return true; }
diff --git a/tests/manual/manual.pro b/tests/manual/manual.pro
index 07f7f9132..644c9ecf9 100644
--- a/tests/manual/manual.pro
+++ b/tests/manual/manual.pro
@@ -65,7 +65,8 @@ SUBDIRS += \
raster-qml \
qtbug-72236 \
qtbug-76766 \
- shader-image-qml
+ shader-image-qml \
+ scene3d-in-sync
qtHaveModule(multimedia): {
SUBDIRS += \
diff --git a/tests/manual/scene3d-in-sync/main.cpp b/tests/manual/scene3d-in-sync/main.cpp
new file mode 100644
index 000000000..8886263f4
--- /dev/null
+++ b/tests/manual/scene3d-in-sync/main.cpp
@@ -0,0 +1,66 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QGuiApplication>
+#include <QQuickView>
+
+int main(int argc, char **argv)
+{
+ QGuiApplication app(argc, argv);
+
+ QQuickView view;
+
+ view.resize(500, 500);
+ view.setResizeMode(QQuickView::SizeRootObjectToView);
+ view.setSource(QUrl("qrc:/main.qml"));
+ view.show();
+
+ return app.exec();
+}
diff --git a/tests/manual/scene3d-in-sync/main.qml b/tests/manual/scene3d-in-sync/main.qml
new file mode 100644
index 000000000..31d4096a7
--- /dev/null
+++ b/tests/manual/scene3d-in-sync/main.qml
@@ -0,0 +1,174 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+import Qt3D.Core 2.9
+import Qt3D.Render 2.9
+import Qt3D.Input 2.0
+import Qt3D.Extras 2.9
+import QtQuick.Scene3D 2.0
+
+Item {
+ id: root
+ property int _trackingPosition : 0
+ readonly property int squareSize: 100
+
+ Timer {
+ running: true
+ interval: 16
+ repeat: true
+ onTriggered: {
+ _trackingPosition += 1
+ if ((_trackingPosition + squareSize) >= root.width)
+ _trackingPosition = 0
+ }
+ }
+
+ // Scene3D + Qt3D content
+ Scene3D {
+ id: sceneThreeD
+ readonly property double halfWidth: width * 0.5
+
+ focus: true
+ anchors.fill: parent
+ // anchors.margins: -15
+ // Make sure to define the input aspect if we want to handle inputs
+ aspects: ["render", "input"]
+ multisample: false
+
+ Entity { // Root
+ id: sceneRoot
+ components: [
+ RenderSettings {
+ activeFrameGraph: ForwardRenderer {
+ id: forwardRenderer
+ camera: planeCamera
+ clearColor: "yellow"
+ }
+ },
+ // Event Source is the Scene3D in that case
+ InputSettings { }
+ ]
+
+ Camera {
+ id: planeCamera
+ left: -sceneThreeD.halfWidth
+ right: sceneThreeD.halfWidth
+ bottom: -(sceneThreeD.height * 0.5)
+ top: (sceneThreeD.height * 0.5)
+ nearPlane: -100
+ farPlane: 100
+ projectionType: CameraLens.OrthographicProjection
+ position: Qt.vector3d(0, 0, 10)
+ viewCenter: Qt.vector3d(0, 0, 0)
+ }
+
+ Entity {
+ id: trackingCube
+ components: [
+ CuboidMesh {
+ xExtent: 100
+ yExtent: 100
+ zExtent: 2
+ },
+
+ PhongMaterial {
+ diffuse: "orange"
+ ambient: "orange"
+ },
+
+ Transform {
+ translation: Qt.vector3d(-sceneThreeD.halfWidth + squareSize * 0.5 + _trackingPosition, -50, 0)
+ }
+ ]
+ }
+ }
+ }
+
+ // QtQuick Content
+ Rectangle {
+ id: qtQuickTracer
+ x: _trackingPosition
+ onXChanged: {
+ console.info("Tracking position is now:" + _trackingPosition);
+ }
+
+ y: root.height * 0.5 - height
+ width: squareSize; height: squareSize
+ color: "red"
+ //transformOrigin: Item.Center
+ Text {
+ color: "white"
+ text: "Rendered with QtQuick"
+ anchors.fill: parent
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ wrapMode: Text.WrapAnywhere
+ }
+
+ Text {
+ color: "white"
+ text: "Rendered with Qt3D"
+ width: parent.width
+ height: parent.height
+ anchors.top: parent.bottom
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ wrapMode: Text.WrapAnywhere
+ }
+
+ Rectangle {
+ width: 1
+ height: 200
+ x: 100
+ color: "black"
+ }
+ }
+}
diff --git a/tests/manual/scene3d-in-sync/scene3d-in-sync.pro b/tests/manual/scene3d-in-sync/scene3d-in-sync.pro
new file mode 100644
index 000000000..521bf2a44
--- /dev/null
+++ b/tests/manual/scene3d-in-sync/scene3d-in-sync.pro
@@ -0,0 +1,10 @@
+QT += qml quick 3dinput
+
+SOURCES += \
+ main.cpp
+
+OTHER_FILES += \
+ main.qml
+
+RESOURCES += \
+ scene3d-in-sync.qrc
diff --git a/tests/manual/scene3d-in-sync/scene3d-in-sync.qrc b/tests/manual/scene3d-in-sync/scene3d-in-sync.qrc
new file mode 100644
index 000000000..5f6483ac3
--- /dev/null
+++ b/tests/manual/scene3d-in-sync/scene3d-in-sync.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/">
+ <file>main.qml</file>
+ </qresource>
+</RCC>