diff options
34 files changed, 516 insertions, 246 deletions
diff --git a/src/core/jobs/qthreadpooler.cpp b/src/core/jobs/qthreadpooler.cpp index a9f4e7a31..3ab321542 100644 --- a/src/core/jobs/qthreadpooler.cpp +++ b/src/core/jobs/qthreadpooler.cpp @@ -66,16 +66,19 @@ QThreadPooler::QThreadPooler(QObject *parent) , m_futureInterface(nullptr) , m_mutex() , m_taskCount(0) + , m_threadPool(QThreadPool::globalInstance()) { const QByteArray maxThreadCount = qgetenv("QT3D_MAX_THREAD_COUNT"); if (!maxThreadCount.isEmpty()) { bool conversionOK = false; const int maxThreadCountValue = maxThreadCount.toInt(&conversionOK); if (conversionOK) - m_threadPool.setMaxThreadCount(maxThreadCountValue); + m_threadPool->setMaxThreadCount(maxThreadCountValue); } + + // Ensures that threads will never be recycled - m_threadPool.setExpiryTimeout(-1); + m_threadPool->setExpiryTimeout(-1); #if QT_CONFIG(qt3d_profile_jobs) QThreadPooler::m_jobsStatTimer.start(); #endif @@ -105,7 +108,7 @@ void QThreadPooler::enqueueTasks(const QVector<RunnableInterface *> &tasks) if (!hasDependencies(*it) && !(*it)->reserved()) { (*it)->setReserved(true); (*it)->setPooler(this); - m_threadPool.start((*it)); + m_threadPool->start((*it)); } } } @@ -125,7 +128,7 @@ void QThreadPooler::taskFinished(RunnableInterface *task) if (!aspectTask->reserved()) { aspectTask->setReserved(true); aspectTask->setPooler(this); - m_threadPool.start(aspectTask); + m_threadPool->start(aspectTask); } } } @@ -188,7 +191,7 @@ int QThreadPooler::currentCount() const int QThreadPooler::maxThreadCount() const { - return m_threadPool.maxThreadCount(); + return m_threadPool->maxThreadCount(); } #if QT_CONFIG(qt3d_profile_jobs) diff --git a/src/core/jobs/qthreadpooler_p.h b/src/core/jobs/qthreadpooler_p.h index 65459efba..3e17cbd6d 100644 --- a/src/core/jobs/qthreadpooler_p.h +++ b/src/core/jobs/qthreadpooler_p.h @@ -103,7 +103,7 @@ private: QFutureInterface<void> *m_futureInterface; QMutex m_mutex; QAtomicInt m_taskCount; - QThreadPool m_threadPool; + QThreadPool *m_threadPool; }; } // namespace Qt3DCore diff --git a/src/extras/defaults/qt3dwindow.cpp b/src/extras/defaults/qt3dwindow.cpp index ace40c3c1..04b8554f1 100644 --- a/src/extras/defaults/qt3dwindow.cpp +++ b/src/extras/defaults/qt3dwindow.cpp @@ -237,7 +237,7 @@ void Qt3DWindow::showEvent(QShowEvent *e) void Qt3DWindow::resizeEvent(QResizeEvent *) { Q_D(Qt3DWindow); - d->m_defaultCamera->setAspectRatio(float(width()) / float(height())); + d->m_defaultCamera->setAspectRatio(float(width()) / std::max(1.f, static_cast<float>(height()))); } /*! diff --git a/src/extras/shaders/es2/distancefieldtext.frag b/src/extras/shaders/es2/distancefieldtext.frag index d2db2e306..b7563e397 100644 --- a/src/extras/shaders/es2/distancefieldtext.frag +++ b/src/extras/shaders/es2/distancefieldtext.frag @@ -33,5 +33,5 @@ void main() FP float maxAlpha = threshold + range; FP float distVal = texture2D(distanceFieldTexture, texCoord).r; - gl_FragColor = color * smoothstep(minAlpha, maxAlpha, distVal); + gl_FragColor = vec4(color.rgb, color.a * smoothstep(minAlpha, maxAlpha, distVal)); } diff --git a/src/extras/shaders/gl3/distancefieldtext.frag b/src/extras/shaders/gl3/distancefieldtext.frag index 23dff8e0f..8e0684adc 100644 --- a/src/extras/shaders/gl3/distancefieldtext.frag +++ b/src/extras/shaders/gl3/distancefieldtext.frag @@ -34,6 +34,6 @@ void main() float maxAlpha = threshold + range; float distVal = texture(distanceFieldTexture, texCoord).r; - fragColor = color * smoothstep(minAlpha, maxAlpha, distVal); + fragColor = vec4(color.rgb, color.a * smoothstep(minAlpha, maxAlpha, distVal)); gl_FragDepth = gl_FragCoord.z - zValue * 0.00001; } diff --git a/src/quick3d/imports/scene3d/scene3ditem.cpp b/src/quick3d/imports/scene3d/scene3ditem.cpp index f0ebfbc38..f824d2c4e 100644 --- a/src/quick3d/imports/scene3d/scene3ditem.cpp +++ b/src/quick3d/imports/scene3d/scene3ditem.cpp @@ -474,10 +474,12 @@ bool Scene3DItem::needsRender() // processFrame will block and wait for renderer to have been finished void Scene3DItem::onBeforeSync() { + static bool dontRenderWhenHidden = !qgetenv("QT3D_SCENE3D_STOP_RENDER_HIDDEN").isEmpty(); + // If we are not visible, don't processFrame changes as we would end up // waiting forever for the scene to be rendered which won't happen // if the Scene3D item is not visible - if (!isVisible()) + if (!isVisible() && dontRenderWhenHidden) return; Q_ASSERT(QThread::currentThread() == thread()); diff --git a/src/quick3d/quick3d/qquaternionanimation.cpp b/src/quick3d/quick3d/qquaternionanimation.cpp index 22cc905f7..933a08ee4 100644 --- a/src/quick3d/quick3d/qquaternionanimation.cpp +++ b/src/quick3d/quick3d/qquaternionanimation.cpp @@ -144,7 +144,7 @@ void QQuaternionAnimation::setType(Type type) switch (type) { case Nlerp: QT_WARNING_PUSH -QT_WARNING_DISABLE_GCC(-Wcast-function-type) +QT_WARNING_DISABLE_GCC("-Wcast-function-type") d->interpolator = reinterpret_cast<QVariantAnimation::Interpolator>(&q_quaternionNlerpInterpolator); QT_WARNING_POP break; diff --git a/src/quick3d/quick3dextras/qt3dquickwindow.cpp b/src/quick3d/quick3dextras/qt3dquickwindow.cpp index d67dfdc71..043aca8b9 100644 --- a/src/quick3d/quick3dextras/qt3dquickwindow.cpp +++ b/src/quick3d/quick3dextras/qt3dquickwindow.cpp @@ -276,7 +276,7 @@ void Qt3DQuickWindow::updateCameraAspectRatio() Q_D(Qt3DQuickWindow); if (d->m_camera) { d->m_camera->setAspectRatio(static_cast<float>(width()) / - static_cast<float>(height())); + std::max(1.f, static_cast<float>(height()))); } } diff --git a/src/render/backend/bufferutils_p.h b/src/render/backend/bufferutils_p.h index 2bb35fac6..ea783df0d 100644 --- a/src/render/backend/bufferutils_p.h +++ b/src/render/backend/bufferutils_p.h @@ -74,6 +74,8 @@ struct BufferInfo , count(0) , byteStride(0) , byteOffset(0) + , restartEnabled(false) + , restartIndexValue(-1) {} QByteArray data; @@ -82,6 +84,8 @@ struct BufferInfo uint count; uint byteStride; uint byteOffset; + bool restartEnabled; + int restartIndexValue; }; diff --git a/src/render/backend/segmentsvisitor.cpp b/src/render/backend/segmentsvisitor.cpp index a3a5d059c..d9f2d79ec 100644 --- a/src/render/backend/segmentsvisitor.cpp +++ b/src/render/backend/segmentsvisitor.cpp @@ -135,34 +135,44 @@ void traverseSegmentStripIndexed(Index *indices, bool loop) { uint i = 0; + uint stripStartIndex = 0; + const uint verticesStride = vertexInfo.byteStride / sizeof(Vertex); const uint maxVerticesDataSize = qMin(vertexInfo.dataSize, 3U); uint ndx[2]; Vector3D abc[2]; - ndx[0] = indices[0]; - uint idx = ndx[0] * verticesStride; - for (uint j = 0; j < maxVerticesDataSize; ++j) - abc[0][j] = vertices[idx + j]; - while (i < indexInfo.count - 1) { - ndx[1] = indices[i + 1]; - if (ndx[0] != ndx[1]) { - idx = ndx[1] * verticesStride; - for (uint j = 0; j < maxVerticesDataSize; ++j) - abc[1][j] = vertices[idx + j]; - visitor->visit(ndx[0], abc[0], ndx[1], abc[1]); + while (i < indexInfo.count) { + if (indexInfo.restartEnabled && indexInfo.restartIndexValue == static_cast<int>(indices[i])) { + ++i; + continue; } + stripStartIndex = i; + ndx[0] = indices[stripStartIndex]; + uint idx = ndx[0] * verticesStride; + for (uint j = 0; j < maxVerticesDataSize; ++j) + abc[0][j] = vertices[idx + j]; ++i; - ndx[0] = ndx[1]; - abc[0] = abc[1]; - } - if (loop) { - ndx[1] = indices[0]; - if (ndx[0] != ndx[1]) { - idx = ndx[1] * verticesStride; - for (uint j = 0; j < maxVerticesDataSize; ++j) - abc[1][j] = vertices[idx + j]; - visitor->visit(ndx[0], abc[0], ndx[1], abc[1]); + while (i < indexInfo.count && (!indexInfo.restartEnabled || indexInfo.restartIndexValue != static_cast<int>(indices[i]))) { + ndx[1] = indices[i]; + if (ndx[0] != ndx[1]) { + idx = ndx[1] * verticesStride; + for (uint j = 0; j < maxVerticesDataSize; ++j) + abc[1][j] = vertices[idx + j]; + visitor->visit(ndx[0], abc[0], ndx[1], abc[1]); + } + ++i; + ndx[0] = ndx[1]; + abc[0] = abc[1]; + } + if (loop) { + ndx[1] = indices[stripStartIndex]; + if (ndx[0] != ndx[1]) { + idx = ndx[1] * verticesStride; + for (uint j = 0; j < maxVerticesDataSize; ++j) + abc[1][j] = vertices[idx + j]; + visitor->visit(ndx[0], abc[0], ndx[1], abc[1]); + } } } } diff --git a/src/render/backend/stringtoint.cpp b/src/render/backend/stringtoint.cpp index 5659da394..0e0d38c9c 100644 --- a/src/render/backend/stringtoint.cpp +++ b/src/render/backend/stringtoint.cpp @@ -50,9 +50,18 @@ namespace Render { namespace { -QReadWriteLock lock; -QHash<QString, int> map = QHash<QString, int>(); -QVector<QString> reverseMap = QVector<QString>(); +struct StringToIntCache +{ + QReadWriteLock lock; + QHash<QString, int> map = QHash<QString, int>(); + QVector<QString> reverseMap = QVector<QString>(); + + static StringToIntCache& instance() + { + static StringToIntCache c; + return c; + } +}; } // anonymous @@ -64,20 +73,21 @@ int StringToInt::lookupId(QLatin1String str) int StringToInt::lookupId(const QString &str) { + auto& cache = StringToIntCache::instance(); int idx; { - QReadLocker readLocker(&lock); - idx = map.value(str, -1); + QReadLocker readLocker(&cache.lock); + idx = cache.map.value(str, -1); } if (Q_UNLIKELY(idx < 0)) { - QWriteLocker writeLocker(&lock); - idx = map.value(str, -1); + QWriteLocker writeLocker(&cache.lock); + idx = cache.map.value(str, -1); if (idx < 0) { - idx = reverseMap.size(); - Q_ASSERT(map.size() == reverseMap.size()); - map.insert(str, idx); - reverseMap.append(str); + idx = cache.reverseMap.size(); + Q_ASSERT(cache.map.size() == cache.reverseMap.size()); + cache.map.insert(str, idx); + cache.reverseMap.append(str); } } return idx; @@ -85,9 +95,10 @@ int StringToInt::lookupId(const QString &str) QString StringToInt::lookupString(int idx) { - QReadLocker readLocker(&lock); - if (Q_LIKELY(reverseMap.size() > idx)) - return reverseMap.at(idx); + auto& cache = StringToIntCache::instance(); + QReadLocker readLocker(&cache.lock); + if (Q_LIKELY(cache.reverseMap.size() > idx)) + return cache.reverseMap.at(idx); return QString(); } diff --git a/src/render/backend/trianglesvisitor.cpp b/src/render/backend/trianglesvisitor.cpp index 87ba7bde9..a58f2d20b 100644 --- a/src/render/backend/trianglesvisitor.cpp +++ b/src/render/backend/trianglesvisitor.cpp @@ -153,6 +153,10 @@ void traverseTriangleStripIndexed(index *indices, uint ndx[3]; Vector3D abc[3]; while (i < indexInfo.count - 2) { + if (indexInfo.restartEnabled && indexInfo.restartIndexValue == static_cast<int>(indices[i + 2])) { + i += 3; + continue; + } bool degenerate = false; for (uint u = 0; u < 3; ++u) { ndx[u] = indices[i + u]; @@ -216,6 +220,11 @@ void traverseTriangleFanIndexed(index *indices, ndx[0] = indices[0]; uint i = 1; while (i < indexInfo.count - 1) { + if (indexInfo.restartEnabled && indexInfo.restartIndexValue == static_cast<int>(indices[i + 1])) { + ndx[0] = indices[i + 2]; + i += 3; + continue; + } for (uint u = 0; u < 2; ++u) { ndx[u + 1] = indices[i + u]; uint idx = ndx[u + 1] * verticesStride; @@ -224,7 +233,7 @@ void traverseTriangleFanIndexed(index *indices, } } visitor->visit(ndx[2], abc[2], ndx[1], abc[1], ndx[0], abc[0]); - i += 1; + ++i; } } diff --git a/src/render/backend/visitorutils_p.h b/src/render/backend/visitorutils_p.h index 6a5c7b4ff..14183e11b 100644 --- a/src/render/backend/visitorutils_p.h +++ b/src/render/backend/visitorutils_p.h @@ -149,6 +149,8 @@ void visitPrimitives(NodeManagers *manager, const GeometryRenderer *renderer, Vi indexBufferInfo.byteOffset = indexAttribute->byteOffset(); indexBufferInfo.byteStride = indexAttribute->byteStride(); indexBufferInfo.count = indexAttribute->count(); + indexBufferInfo.restartEnabled = renderer->primitiveRestartEnabled(); + indexBufferInfo.restartIndexValue = renderer->restartIndexValue(); IndexExecutor executor; executor.m_vertexBufferInfo = vertexBufferInfo; diff --git a/src/render/jobs/expandboundingvolumejob.cpp b/src/render/jobs/expandboundingvolumejob.cpp index d63934b54..641a5c272 100644 --- a/src/render/jobs/expandboundingvolumejob.cpp +++ b/src/render/jobs/expandboundingvolumejob.cpp @@ -62,7 +62,7 @@ void expandWorldBoundingVolume(NodeManagers *manager, Entity *node) const auto childrenHandles = node->childrenHandles(); for (const HEntity &handle : childrenHandles) { Entity *c = manager->renderNodesManager()->data(handle); - if (c) + if (c && c->isEnabled()) expandWorldBoundingVolume(manager, c); } @@ -72,7 +72,7 @@ void expandWorldBoundingVolume(NodeManagers *manager, Entity *node) Qt3DRender::Render::Sphere *parentBoundingVolume = node->worldBoundingVolumeWithChildren(); for (const HEntity &handle : childrenHandles) { Entity *c = manager->renderNodesManager()->data(handle); - if (c) + if (c && c->isEnabled()) parentBoundingVolume->expandToContain(*c->worldBoundingVolumeWithChildren()); } } diff --git a/src/render/jobs/updateworldboundingvolumejob.cpp b/src/render/jobs/updateworldboundingvolumejob.cpp index 40dd919bc..65a3ec75d 100644 --- a/src/render/jobs/updateworldboundingvolumejob.cpp +++ b/src/render/jobs/updateworldboundingvolumejob.cpp @@ -52,7 +52,7 @@ UpdateWorldBoundingVolumeJob::UpdateWorldBoundingVolumeJob() : Qt3DCore::QAspectJob() , m_manager(nullptr) { - SET_JOB_RUN_STAT_TYPE(this, JobTypes::UpdateWorldBoundingVolume, 0); + SET_JOB_RUN_STAT_TYPE(this, JobTypes::UpdateWorldBoundingVolume, 0) } void UpdateWorldBoundingVolumeJob::run() @@ -61,6 +61,8 @@ void UpdateWorldBoundingVolumeJob::run() for (const HEntity &handle : handles) { Entity *node = m_manager->data(handle); + if (!node->isEnabled()) + continue; *(node->worldBoundingVolume()) = node->localBoundingVolume()->transformed(*(node->worldTransform())); *(node->worldBoundingVolumeWithChildren()) = *(node->worldBoundingVolume()); // expanded in UpdateBoundingVolumeJob } diff --git a/src/render/jobs/updateworldtransformjob.cpp b/src/render/jobs/updateworldtransformjob.cpp index e3487e68b..ea9aa778c 100644 --- a/src/render/jobs/updateworldtransformjob.cpp +++ b/src/render/jobs/updateworldtransformjob.cpp @@ -67,6 +67,9 @@ struct TransformUpdate void updateWorldTransformAndBounds(NodeManagers *manager, Entity *node, const Matrix4x4 &parentTransform, QVector<TransformUpdate> &updatedTransforms) { + if (!node->isEnabled()) + return; + Matrix4x4 worldTransform(parentTransform); Transform *nodeTransform = node->renderComponent<Transform>(); diff --git a/src/render/lights/qdirectionallight.cpp b/src/render/lights/qdirectionallight.cpp index 13fb78843..51827b644 100644 --- a/src/render/lights/qdirectionallight.cpp +++ b/src/render/lights/qdirectionallight.cpp @@ -115,12 +115,18 @@ QDirectionalLight::QDirectionalLight(QDirectionalLightPrivate &dd, QNode *parent /*! \qmlproperty vector3d Qt3D.Render::DirectionalLight::worldDirection - Specifies the world direction of the directional light + Specifies the world direction of the directional light. + + \note The exact meaning and use of this property is up to the + material implementation. */ /*! \property Qt3DRender::QDirectionalLight::worldDirection - Specifies the world direction of the directional light + Specifies the world direction of the directional light. + + \note The exact meaning and use of this property is up to the + material implementation. */ void QDirectionalLight::setWorldDirection(const QVector3D &direction) { diff --git a/src/render/lights/qenvironmentlight.cpp b/src/render/lights/qenvironmentlight.cpp index 86ef04f95..977e117db 100644 --- a/src/render/lights/qenvironmentlight.cpp +++ b/src/render/lights/qenvironmentlight.cpp @@ -158,6 +158,9 @@ QEnvironmentLight::~QEnvironmentLight() Holds the current environment irradiance map texture. By default, the environment irradiance texture is null. + + \note The exact meaning and use of this property is up to the + material implementation. */ /*! @@ -166,6 +169,9 @@ QEnvironmentLight::~QEnvironmentLight() Holds the current environment irradiance map texture. By default, the environment irradiance texture is null. + + \note The exact meaning and use of this property is up to the + material implementation. */ QAbstractTexture *QEnvironmentLight::irradiance() const { @@ -179,6 +185,9 @@ QAbstractTexture *QEnvironmentLight::irradiance() const Holds the current environment specular map texture. By default, the environment specular texture is null. + + \note The exact meaning and use of this property is up to the + material implementation. */ /*! @@ -187,6 +196,9 @@ QAbstractTexture *QEnvironmentLight::irradiance() const Holds the current environment specular map texture. By default, the environment specular texture is null. + + \note The exact meaning and use of this property is up to the + material implementation. */ QAbstractTexture *QEnvironmentLight::specular() const { diff --git a/src/render/lights/qpointlight.cpp b/src/render/lights/qpointlight.cpp index 2b042c91d..c16291709 100644 --- a/src/render/lights/qpointlight.cpp +++ b/src/render/lights/qpointlight.cpp @@ -135,12 +135,18 @@ QPointLight::QPointLight(QPointLightPrivate &dd, QNode *parent) /*! \qmlproperty float Qt3D.Render::PointLight::constantAttenuation - Specifies the constant attenuation of the point light + Specifies the constant attenuation of the point light. + + \note The exact meaning and use of this property is up to the + material implementation. */ /*! \property Qt3DRender::QPointLight::constantAttenuation - Specifies the constant attenuation of the point light + Specifies the constant attenuation of the point light. + + \note The exact meaning and use of this property is up to the + material implementation. */ float QPointLight::constantAttenuation() const { @@ -159,12 +165,18 @@ void QPointLight::setConstantAttenuation(float value) /*! \qmlproperty float Qt3D.Render::PointLight::linearAttenuation - Specifies the linear attenuation of the point light + Specifies the linear attenuation of the point light. + + \note The exact meaning and use of this property is up to the + material implementation. */ /*! \property Qt3DRender::QPointLight::linearAttenuation - Specifies the linear attenuation of the point light + Specifies the linear attenuation of the point light. + + \note The exact meaning and use of this property is up to the + material implementation. */ float QPointLight::linearAttenuation() const { @@ -183,12 +195,18 @@ void QPointLight::setLinearAttenuation(float value) /*! \qmlproperty float Qt3D.Render::PointLight::quadraticAttenuation - Specifies the quadratic attenuation of the point light + Specifies the quadratic attenuation of the point light. + + \note The exact meaning and use of this property is up to the + material implementation. */ /*! \property Qt3DRender::QPointLight::quadraticAttenuation - Specifies the quadratic attenuation of the point light + Specifies the quadratic attenuation of the point light. + + \note The exact meaning and use of this property is up to the + material implementation. */ float QPointLight::quadraticAttenuation() const { diff --git a/src/render/lights/qspotlight.cpp b/src/render/lights/qspotlight.cpp index eddafbe61..c725a6baf 100644 --- a/src/render/lights/qspotlight.cpp +++ b/src/render/lights/qspotlight.cpp @@ -140,12 +140,18 @@ QSpotLight::QSpotLight(QSpotLightPrivate &dd, QNode *parent) /*! \qmlproperty float Qt3D.Render::SpotLight::constantAttenuation - Specifies the constant attenuation of the spot light + Specifies the constant attenuation of the spot light. + + \note The exact meaning and use of this property is up to the + material implementation. */ /*! \property Qt3DRender::QSpotLight::constantAttenuation - Specifies the constant attenuation of the spot light + Specifies the constant attenuation of the spot light. + + \note The exact meaning and use of this property is up to the + material implementation. */ float QSpotLight::constantAttenuation() const { @@ -164,12 +170,18 @@ void QSpotLight::setConstantAttenuation(float value) /*! \qmlproperty float Qt3D.Render::SpotLight::linearAttenuation - Specifies the linear attenuation of the spot light + Specifies the linear attenuation of the spot light. + + \note The exact meaning and use of this property is up to the + material implementation. */ /*! \property Qt3DRender::QSpotLight::linearAttenuation - Specifies the linear attenuation of the spot light + Specifies the linear attenuation of the spot light. + + \note The exact meaning and use of this property is up to the + material implementation. */ float QSpotLight::linearAttenuation() const { @@ -188,12 +200,18 @@ void QSpotLight::setLinearAttenuation(float value) /*! \qmlproperty float Qt3D.Render::SpotLight::quadraticAttenuation - Specifies the quadratic attenuation of the spot light + Specifies the quadratic attenuation of the spot light. + + \note The exact meaning and use of this property is up to the + material implementation. */ /*! \property Qt3DRender::QSpotLight::quadraticAttenuation - Specifies the quadratic attenuation of the spot light + Specifies the quadratic attenuation of the spot light. + + \note The exact meaning and use of this property is up to the + material implementation. */ float QSpotLight::quadraticAttenuation() const { @@ -212,12 +230,18 @@ void QSpotLight::setQuadraticAttenuation(float value) /*! \qmlproperty vector3d Qt3D.Render::SpotLight::localDirection - Specifies the local direction of the spot light + Specifies the local direction of the spot light. + + \note The exact meaning and use of this property is up to the + material implementation. */ /*! \property Qt3DRender::QSpotLight::localDirection - Specifies the local direction of the spot light + Specifies the local direction of the spot light. + + \note The exact meaning and use of this property is up to the + material implementation. */ QVector3D QSpotLight::localDirection() const { @@ -227,12 +251,18 @@ QVector3D QSpotLight::localDirection() const /*! \qmlproperty float Qt3D.Render::SpotLight::cutOffAngle - Specifies the cut off angle of the spot light + Specifies the cut off angle of the spot light. + + \note The exact meaning and use of this property is up to the + material implementation. */ /*! \property Qt3DRender::QSpotLight::cutOffAngle - Specifies the cut off angle of the spot light + Specifies the cut off angle of the spot light. + + \note The exact meaning and use of this property is up to the + material implementation. */ float QSpotLight::cutOffAngle() const { diff --git a/src/render/materialsystem/shader.cpp b/src/render/materialsystem/shader.cpp index 300a71b84..0d4b5edba 100644 --- a/src/render/materialsystem/shader.cpp +++ b/src/render/materialsystem/shader.cpp @@ -57,6 +57,29 @@ using namespace Qt3DCore; namespace Qt3DRender { namespace Render { +const int Shader::modelMatrixNameId = StringToInt::lookupId(QLatin1String("modelMatrix")); +const int Shader::viewMatrixNameId = StringToInt::lookupId(QLatin1String("viewMatrix")); +const int Shader::projectionMatrixNameId = StringToInt::lookupId(QLatin1String("projectionMatrix")); +const int Shader::modelViewMatrixNameId = StringToInt::lookupId(QLatin1String("modelView")); +const int Shader::viewProjectionMatrixNameId = StringToInt::lookupId(QLatin1String("viewProjectionMatrix")); +const int Shader::modelViewProjectionNameId = StringToInt::lookupId(QLatin1String("modelViewProjection")); +const int Shader::mvpNameId = StringToInt::lookupId(QLatin1String("mvp")); +const int Shader::inverseModelMatrixNameId = StringToInt::lookupId(QLatin1String("inverseModelMatrix")); +const int Shader::inverseViewMatrixNameId = StringToInt::lookupId(QLatin1String("inverseViewMatrix")); +const int Shader::inverseProjectionMatrixNameId = StringToInt::lookupId(QLatin1String("inverseProjectionMatrix")); +const int Shader::inverseModelViewNameId = StringToInt::lookupId(QLatin1String("inverseModelView")); +const int Shader::inverseViewProjectionMatrixNameId = StringToInt::lookupId(QLatin1String("inverseViewProjectionMatrix")); +const int Shader::inverseModelViewProjectionNameId = StringToInt::lookupId(QLatin1String("inverseModelViewProjection")); +const int Shader::modelNormalMatrixNameId = StringToInt::lookupId(QLatin1String("modelNormalMatrix")); +const int Shader::modelViewNormalNameId = StringToInt::lookupId(QLatin1String("modelViewNormal")); +const int Shader::viewportMatrixNameId = StringToInt::lookupId(QLatin1String("viewportMatrix")); +const int Shader::inverseViewportMatrixNameId = StringToInt::lookupId(QLatin1String("inverseViewportMatrix")); +const int Shader::aspectRatioNameId = StringToInt::lookupId(QLatin1String("aspectRatio")); +const int Shader::exposureNameId = StringToInt::lookupId(QLatin1String("exposure")); +const int Shader::gammaNameId = StringToInt::lookupId(QLatin1String("gamma")); +const int Shader::timeNameId = StringToInt::lookupId(QLatin1String("time")); +const int Shader::eyePositionNameId = StringToInt::lookupId(QLatin1String("eyePosition")); +const int Shader::skinningPaletteNameId = StringToInt::lookupId(QLatin1String("skinningPalette[0]")); Shader::Shader() : BackendNode(ReadWrite) @@ -308,13 +331,47 @@ void Shader::initializeUniforms(const QVector<ShaderUniform> &uniformsDescriptio { m_uniforms = uniformsDescription; m_uniformsNames.resize(uniformsDescription.size()); - m_uniformsNamesIds.resize(uniformsDescription.size()); + m_uniformsNamesIds.reserve(uniformsDescription.size()); + m_standardUniformNamesIds.reserve(5); QHash<QString, ShaderUniform> activeUniformsInDefaultBlock; + static const QVector<int> standardUniformNameIds = { + modelMatrixNameId, + viewMatrixNameId, + projectionMatrixNameId, + modelViewMatrixNameId, + viewProjectionMatrixNameId, + modelViewProjectionNameId, + mvpNameId, + inverseModelMatrixNameId, + inverseViewMatrixNameId, + inverseProjectionMatrixNameId, + inverseModelViewNameId, + inverseViewProjectionMatrixNameId, + inverseModelViewProjectionNameId, + modelNormalMatrixNameId, + modelViewNormalNameId, + viewportMatrixNameId, + inverseViewportMatrixNameId, + aspectRatioNameId, + exposureNameId, + gammaNameId, + timeNameId, + eyePositionNameId, + skinningPaletteNameId, + }; + for (int i = 0, m = uniformsDescription.size(); i < m; i++) { m_uniformsNames[i] = m_uniforms[i].m_name; - m_uniforms[i].m_nameId = StringToInt::lookupId(m_uniformsNames[i]); - m_uniformsNamesIds[i] = m_uniforms[i].m_nameId; + const int nameId = StringToInt::lookupId(m_uniformsNames[i]); + m_uniforms[i].m_nameId = nameId; + + // Is the uniform a Qt3D "Standard" uniform or a user defined one? + if (standardUniformNameIds.contains(nameId)) + m_standardUniformNamesIds.push_back(nameId); + else + m_uniformsNamesIds.push_back(nameId); + if (uniformsDescription[i].m_blockIndex == -1) { // Uniform is in default block qCDebug(Shaders) << "Active Uniform in Default Block " << uniformsDescription[i].m_name << uniformsDescription[i].m_blockIndex; activeUniformsInDefaultBlock.insert(uniformsDescription[i].m_name, uniformsDescription[i]); @@ -394,6 +451,7 @@ void Shader::initializeFromReference(const Shader &other) { Q_ASSERT(m_dna == other.m_dna); m_uniformsNamesIds = other.m_uniformsNamesIds; + m_standardUniformNamesIds = other.m_standardUniformNamesIds; m_uniformsNames = other.m_uniformsNames; m_uniforms = other.m_uniforms; m_attributesNames = other.m_attributesNames; diff --git a/src/render/materialsystem/shader_p.h b/src/render/materialsystem/shader_p.h index fe1a401d9..298b09c6c 100644 --- a/src/render/materialsystem/shader_p.h +++ b/src/render/materialsystem/shader_p.h @@ -75,6 +75,30 @@ typedef uint ProgramDNA; class Q_AUTOTEST_EXPORT Shader : public BackendNode { public: + static const int modelMatrixNameId; + static const int viewMatrixNameId; + static const int projectionMatrixNameId; + static const int modelViewMatrixNameId; + static const int viewProjectionMatrixNameId; + static const int modelViewProjectionNameId; + static const int mvpNameId; + static const int inverseModelMatrixNameId; + static const int inverseViewMatrixNameId; + static const int inverseProjectionMatrixNameId; + static const int inverseModelViewNameId; + static const int inverseViewProjectionMatrixNameId; + static const int inverseModelViewProjectionNameId; + static const int modelNormalMatrixNameId; + static const int modelViewNormalNameId; + static const int viewportMatrixNameId; + static const int inverseViewportMatrixNameId; + static const int aspectRatioNameId; + static const int exposureNameId; + static const int gammaNameId; + static const int timeNameId; + static const int eyePositionNameId; + static const int skinningPaletteNameId; + Shader(); ~Shader(); @@ -88,6 +112,7 @@ public: const QHash<QString, int> fragOutputs() const; inline QVector<int> uniformsNamesIds() const { return m_uniformsNamesIds; } + inline QVector<int> standardUniformNameIds() const { return m_standardUniformNamesIds; } inline QVector<int> uniformBlockNamesIds() const { return m_uniformBlockNamesIds; } inline QVector<int> storageBlockNamesIds() const { return m_shaderStorageBlockNamesIds; } inline QVector<int> attributeNamesIds() const { return m_attributeNamesIds; } @@ -128,6 +153,7 @@ public: private: QVector<QString> m_uniformsNames; QVector<int> m_uniformsNamesIds; + QVector<int> m_standardUniformNamesIds; QVector<ShaderUniform> m_uniforms; QVector<QString> m_attributesNames; diff --git a/src/render/renderers/opengl/graphicshelpers/graphicscontext.cpp b/src/render/renderers/opengl/graphicshelpers/graphicscontext.cpp index 333453ac7..71edc1c74 100644 --- a/src/render/renderers/opengl/graphicshelpers/graphicscontext.cpp +++ b/src/render/renderers/opengl/graphicshelpers/graphicscontext.cpp @@ -265,17 +265,15 @@ QOpenGLShaderProgram *GraphicsContext::createShaderProgram(Shader *shaderNode) return shaderProgram.take(); } -// Called by GL Command Thread (can't use global glHelpers) // That assumes that the shaderProgram in Shader stays the same void GraphicsContext::introspectShaderInterface(Shader *shader, QOpenGLShaderProgram *shaderProgram) { - QScopedPointer<GraphicsHelperInterface> glHelper(resolveHighestOpenGLFunctions()); - shader->initializeUniforms(glHelper->programUniformsAndLocations(shaderProgram->programId())); - shader->initializeAttributes(glHelper->programAttributesAndLocations(shaderProgram->programId())); + shader->initializeUniforms(m_glHelper->programUniformsAndLocations(shaderProgram->programId())); + shader->initializeAttributes(m_glHelper->programAttributesAndLocations(shaderProgram->programId())); if (m_glHelper->supportsFeature(GraphicsHelperInterface::UniformBufferObject)) - shader->initializeUniformBlocks(glHelper->programUniformBlocks(shaderProgram->programId())); + shader->initializeUniformBlocks(m_glHelper->programUniformBlocks(shaderProgram->programId())); if (m_glHelper->supportsFeature(GraphicsHelperInterface::ShaderStorageObject)) - shader->initializeShaderStorageBlocks(glHelper->programShaderStorageBlocks(shaderProgram->programId())); + shader->initializeShaderStorageBlocks(m_glHelper->programShaderStorageBlocks(shaderProgram->programId())); } diff --git a/src/render/renderers/opengl/jobs/renderviewcommandbuilderjob.cpp b/src/render/renderers/opengl/jobs/renderviewcommandbuilderjob.cpp index c6bc20423..091d49ef5 100644 --- a/src/render/renderers/opengl/jobs/renderviewcommandbuilderjob.cpp +++ b/src/render/renderers/opengl/jobs/renderviewcommandbuilderjob.cpp @@ -53,6 +53,8 @@ int renderViewInstanceCounter = 0; RenderViewCommandBuilderJob::RenderViewCommandBuilderJob() : Qt3DCore::QAspectJob() + , m_offset(0) + , m_count(0) , m_renderView(nullptr) { SET_JOB_RUN_STAT_TYPE(this, JobTypes::RenderViewCommandBuilder, renderViewInstanceCounter++) @@ -61,11 +63,14 @@ RenderViewCommandBuilderJob::RenderViewCommandBuilderJob() void RenderViewCommandBuilderJob::run() { if (!m_renderView->noDraw()) { + if (m_count == 0) + return; + const bool isDraw = !m_renderView->isCompute(); if (isDraw) - m_commandData = m_renderView->buildDrawRenderCommands(m_entities); + m_commandData = m_renderView->buildDrawRenderCommands(m_entities, m_offset, m_count); else - m_commandData = m_renderView->buildComputeRenderCommands(m_entities); + m_commandData = m_renderView->buildComputeRenderCommands(m_entities, m_offset, m_count); } } diff --git a/src/render/renderers/opengl/jobs/renderviewcommandbuilderjob_p.h b/src/render/renderers/opengl/jobs/renderviewcommandbuilderjob_p.h index 9f45a8005..556c7f241 100644 --- a/src/render/renderers/opengl/jobs/renderviewcommandbuilderjob_p.h +++ b/src/render/renderers/opengl/jobs/renderviewcommandbuilderjob_p.h @@ -67,12 +67,19 @@ public: RenderViewCommandBuilderJob(); inline void setRenderView(RenderView *rv) Q_DECL_NOTHROW { m_renderView = rv; } - inline void setEntities(const QVector<Entity *> &entities) { m_entities = entities; } + inline void setEntities(const QVector<Entity *> &entities, int offset, int count) + { + m_offset = offset; + m_count = count; + m_entities = entities; + } inline EntityRenderCommandData &commandData() { return m_commandData; } void run() final; private: + int m_offset; + int m_count; RenderView *m_renderView; QVector<Entity *> m_entities; EntityRenderCommandData m_commandData; diff --git a/src/render/renderers/opengl/jobs/renderviewcommandupdaterjob.cpp b/src/render/renderers/opengl/jobs/renderviewcommandupdaterjob.cpp index af1d545ed..6d6ae7853 100644 --- a/src/render/renderers/opengl/jobs/renderviewcommandupdaterjob.cpp +++ b/src/render/renderers/opengl/jobs/renderviewcommandupdaterjob.cpp @@ -54,8 +54,11 @@ int renderViewInstanceCounter = 0; RenderViewCommandUpdaterJob::RenderViewCommandUpdaterJob() : Qt3DCore::QAspectJob() + , m_offset(0) + , m_count(0) , m_renderView(nullptr) , m_renderer(nullptr) + , m_renderables(nullptr) { SET_JOB_RUN_STAT_TYPE(this, JobTypes::RenderCommandUpdater, renderViewInstanceCounter++); } @@ -65,12 +68,10 @@ void RenderViewCommandUpdaterJob::run() // Build RenderCommand should perform the culling as we have no way to determine // if a child has a mesh in the view frustum while its parent isn't contained in it. if (!m_renderView->noDraw()) { + if (m_count == 0) + return; // Update Render Commands (Uniform Change, Depth Change) - m_renderView->updateRenderCommand(m_renderables); - - // Copy commands out of cached -> ensures we can submit them for rendering - // while cache is rebuilt or modified for next frame - m_commands = m_renderables.commands; + m_renderView->updateRenderCommand(m_renderables, m_offset, m_count); } } diff --git a/src/render/renderers/opengl/jobs/renderviewcommandupdaterjob_p.h b/src/render/renderers/opengl/jobs/renderviewcommandupdaterjob_p.h index 72caef6cf..e6df3f3b3 100644 --- a/src/render/renderers/opengl/jobs/renderviewcommandupdaterjob_p.h +++ b/src/render/renderers/opengl/jobs/renderviewcommandupdaterjob_p.h @@ -71,16 +71,24 @@ public: inline void setRenderView(RenderView *rv) Q_DECL_NOTHROW { m_renderView = rv; } inline void setRenderer(Renderer *renderer) Q_DECL_NOTHROW { m_renderer = renderer; } - inline void setRenderables(const EntityRenderCommandData &renderables) Q_DECL_NOTHROW { m_renderables = renderables; } + inline void setRenderables(EntityRenderCommandData *renderables, int offset, int count) Q_DECL_NOTHROW + { + m_offset = offset; + m_count = count; + m_renderables = renderables; + } + EntityRenderCommandData *renderables() const { return m_renderables; } QVector<RenderCommand> &commands() Q_DECL_NOTHROW { return m_commands; } void run() final; private: + int m_offset; + int m_count; RenderView *m_renderView; Renderer *m_renderer; - EntityRenderCommandData m_renderables; + EntityRenderCommandData *m_renderables; QVector<RenderCommand> m_commands; }; diff --git a/src/render/renderers/opengl/renderer/renderer.cpp b/src/render/renderers/opengl/renderer/renderer.cpp index bf9230079..d61d1d8ac 100644 --- a/src/render/renderers/opengl/renderer/renderer.cpp +++ b/src/render/renderers/opengl/renderer/renderer.cpp @@ -1019,74 +1019,6 @@ void Renderer::prepareCommandsSubmission(const QVector<RenderView *> &renderView // Prepare the ShaderParameterPack based on the active uniforms of the shader shader->prepareUniforms(command.m_parameterPack); - { // Scoped to show extent - command.m_isValid = !command.m_activeAttributes.empty(); - if (!command.m_isValid) - continue; - - // Update the draw command with what's going to be needed for the drawing - uint primitiveCount = rGeometryRenderer->vertexCount(); - uint estimatedCount = 0; - Attribute *indexAttribute = nullptr; - Attribute *indirectAttribute = nullptr; - - const QVector<Qt3DCore::QNodeId> attributeIds = rGeometry->attributes(); - for (Qt3DCore::QNodeId attributeId : attributeIds) { - Attribute *attribute = m_nodesManager->attributeManager()->lookupResource(attributeId); - switch (attribute->attributeType()) { - case QAttribute::IndexAttribute: - indexAttribute = attribute; - break; - case QAttribute::DrawIndirectAttribute: - indirectAttribute = attribute; - break; - case QAttribute::VertexAttribute: { - if (command.m_activeAttributes.contains(attribute->nameId())) - estimatedCount = qMax(attribute->count(), estimatedCount); - break; - } - default: - Q_UNREACHABLE(); - break; - } - } - - command.m_drawIndexed = (indexAttribute != nullptr); - command.m_drawIndirect = (indirectAttribute != nullptr); - - // Update the draw command with all the information required for the drawing - if (command.m_drawIndexed) { - command.m_indexAttributeDataType = GraphicsContext::glDataTypeFromAttributeDataType(indexAttribute->vertexBaseType()); - command.m_indexAttributeByteOffset = indexAttribute->byteOffset() + rGeometryRenderer->indexBufferByteOffset(); - } - - // Note: we only care about the primitiveCount when using direct draw calls - // For indirect draw calls it is assumed the buffer was properly set already - if (command.m_drawIndirect) { - command.m_indirectAttributeByteOffset = indirectAttribute->byteOffset(); - command.m_indirectDrawBuffer = m_nodesManager->bufferManager()->lookupHandle(indirectAttribute->bufferId()); - } else { - // Use the count specified by the GeometryRender - // If not specify use the indexAttribute count if present - // Otherwise tries to use the count from the attribute with the highest count - if (primitiveCount == 0) { - if (indexAttribute) - primitiveCount = indexAttribute->count(); - else - primitiveCount = estimatedCount; - } - } - - command.m_primitiveCount = primitiveCount; - command.m_primitiveType = rGeometryRenderer->primitiveType(); - command.m_primitiveRestartEnabled = rGeometryRenderer->primitiveRestartEnabled(); - command.m_restartIndexValue = rGeometryRenderer->restartIndexValue(); - command.m_firstInstance = rGeometryRenderer->firstInstance(); - command.m_instanceCount = rGeometryRenderer->instanceCount(); - command.m_firstVertex = rGeometryRenderer->firstVertex(); - command.m_indexOffset = rGeometryRenderer->indexOffset(); - command.m_verticesPerPatch = rGeometryRenderer->verticesPerPatch(); - } // scope } else if (command.m_type == RenderCommand::Compute) { Shader *shader = m_nodesManager->data<Shader, ShaderManager>(command.m_shader); Q_ASSERT(shader); diff --git a/src/render/renderers/opengl/renderer/renderview.cpp b/src/render/renderers/opengl/renderer/renderview.cpp index 0ac4f876f..d7e34e16c 100644 --- a/src/render/renderers/opengl/renderer/renderview.cpp +++ b/src/render/renderers/opengl/renderer/renderview.cpp @@ -119,29 +119,29 @@ RenderView::StandardUniformsNameToTypeHash RenderView::initializeStandardUniform { RenderView::StandardUniformsNameToTypeHash setters; - setters.insert(StringToInt::lookupId(QLatin1String("modelMatrix")), ModelMatrix); - setters.insert(StringToInt::lookupId(QLatin1String("viewMatrix")), ViewMatrix); - setters.insert(StringToInt::lookupId(QLatin1String("projectionMatrix")), ProjectionMatrix); - setters.insert(StringToInt::lookupId(QLatin1String("modelView")), ModelViewMatrix); - setters.insert(StringToInt::lookupId(QLatin1String("viewProjectionMatrix")), ViewProjectionMatrix); - setters.insert(StringToInt::lookupId(QLatin1String("modelViewProjection")), ModelViewProjectionMatrix); - setters.insert(StringToInt::lookupId(QLatin1String("mvp")), ModelViewProjectionMatrix); - setters.insert(StringToInt::lookupId(QLatin1String("inverseModelMatrix")), InverseModelMatrix); - setters.insert(StringToInt::lookupId(QLatin1String("inverseViewMatrix")), InverseViewMatrix); - setters.insert(StringToInt::lookupId(QLatin1String("inverseProjectionMatrix")), InverseProjectionMatrix); - setters.insert(StringToInt::lookupId(QLatin1String("inverseModelView")), InverseModelViewMatrix); - setters.insert(StringToInt::lookupId(QLatin1String("inverseViewProjectionMatrix")), InverseViewProjectionMatrix); - setters.insert(StringToInt::lookupId(QLatin1String("inverseModelViewProjection")), InverseModelViewProjectionMatrix); - setters.insert(StringToInt::lookupId(QLatin1String("modelNormalMatrix")), ModelNormalMatrix); - setters.insert(StringToInt::lookupId(QLatin1String("modelViewNormal")), ModelViewNormalMatrix); - setters.insert(StringToInt::lookupId(QLatin1String("viewportMatrix")), ViewportMatrix); - setters.insert(StringToInt::lookupId(QLatin1String("inverseViewportMatrix")), InverseViewportMatrix); - setters.insert(StringToInt::lookupId(QLatin1String("aspectRatio")), AspectRatio); - setters.insert(StringToInt::lookupId(QLatin1String("exposure")), Exposure); - setters.insert(StringToInt::lookupId(QLatin1String("gamma")), Gamma); - setters.insert(StringToInt::lookupId(QLatin1String("time")), Time); - setters.insert(StringToInt::lookupId(QLatin1String("eyePosition")), EyePosition); - setters.insert(StringToInt::lookupId(QLatin1String("skinningPalette[0]")), SkinningPalette); + setters.insert(Shader::modelMatrixNameId, ModelMatrix); + setters.insert(Shader::viewMatrixNameId, ViewMatrix); + setters.insert(Shader::projectionMatrixNameId, ProjectionMatrix); + setters.insert(Shader::modelViewMatrixNameId, ModelViewMatrix); + setters.insert(Shader::viewProjectionMatrixNameId, ViewProjectionMatrix); + setters.insert(Shader::modelViewProjectionNameId, ModelViewProjectionMatrix); + setters.insert(Shader::mvpNameId, ModelViewProjectionMatrix); + setters.insert(Shader::inverseModelMatrixNameId, InverseModelMatrix); + setters.insert(Shader::inverseViewMatrixNameId, InverseViewMatrix); + setters.insert(Shader::inverseProjectionMatrixNameId, InverseProjectionMatrix); + setters.insert(Shader::inverseModelViewNameId, InverseModelViewMatrix); + setters.insert(Shader::inverseViewProjectionMatrixNameId, InverseViewProjectionMatrix); + setters.insert(Shader::inverseModelViewProjectionNameId, InverseModelViewProjectionMatrix); + setters.insert(Shader::modelNormalMatrixNameId, ModelNormalMatrix); + setters.insert(Shader::modelViewNormalNameId, ModelViewNormalMatrix); + setters.insert(Shader::viewportMatrixNameId, ViewportMatrix); + setters.insert(Shader::inverseViewportMatrixNameId, InverseViewportMatrix); + setters.insert(Shader::aspectRatioNameId, AspectRatio); + setters.insert(Shader::exposureNameId, Exposure); + setters.insert(Shader::gammaNameId, Gamma); + setters.insert(Shader::timeNameId, Time); + setters.insert(Shader::eyePositionNameId, EyePosition); + setters.insert(Shader::skinningPaletteNameId, SkinningPalette); return setters; } @@ -211,7 +211,7 @@ UniformValue RenderView::standardUniformValue(RenderView::StandardUniform standa return UniformValue(Matrix4x4(viewportMatrix.inverted())); } case AspectRatio: - return float(m_surfaceSize.width()) / float(m_surfaceSize.height()); + return float(m_surfaceSize.width()) / std::max(1.f, float(m_surfaceSize.height())); case Exposure: return UniformValue(m_data.m_renderCameraLens ? m_data.m_renderCameraLens->exposure() : 0.0f); case Gamma: @@ -621,13 +621,16 @@ void RenderView::addClearBuffers(const ClearBuffers *cb) { } // If we are there, we know that entity had a GeometryRenderer + Material -EntityRenderCommandData RenderView::buildDrawRenderCommands(const QVector<Entity *> &entities) const +EntityRenderCommandData RenderView::buildDrawRenderCommands(const QVector<Entity *> &entities, + int offset, int count) const { EntityRenderCommandData commands; - commands.reserve(entities.size()); + commands.reserve(count); - for (Entity *entity : entities) { + for (int i = 0; i < count; ++i) { + const int idx = offset + i; + Entity *entity = entities.at(idx); GeometryRenderer *geometryRenderer = nullptr; HGeometryRenderer geometryRendererHandle = entity->componentHandle<GeometryRenderer>(); @@ -640,12 +643,15 @@ EntityRenderCommandData RenderView::buildDrawRenderCommands(const QVector<Entity const HMaterial materialHandle = entity->componentHandle<Material>(); const QVector<RenderPassParameterData> renderPassData = m_parameters.value(materialComponentId); + HGeometry geometryHandle = m_manager->geometryManager()->lookupHandle(geometryRenderer->geometryId()); + Geometry *geometry = m_manager->geometryManager()->data(geometryHandle); + // 1 RenderCommand per RenderPass pass on an Entity with a Mesh for (const RenderPassParameterData &passData : renderPassData) { // Add the RenderPass Parameters RenderCommand command = {}; command.m_geometryRenderer = geometryRendererHandle; - command.m_geometry = m_manager->geometryManager()->lookupHandle(geometryRenderer->geometryId()); + command.m_geometry = geometryHandle; command.m_material = materialHandle; // For RenderPass based states we use the globally set RenderState @@ -662,6 +668,71 @@ EntityRenderCommandData RenderView::buildDrawRenderCommands(const QVector<Entity } command.m_shader = m_manager->lookupHandle<Shader, ShaderManager, HShader>(pass->shaderProgram()); + { // Scoped to show extent + + // Update the draw command with what's going to be needed for the drawing + int primitiveCount = geometryRenderer->vertexCount(); + int estimatedCount = 0; + Attribute *indexAttribute = nullptr; + Attribute *indirectAttribute = nullptr; + + const QVector<Qt3DCore::QNodeId> attributeIds = geometry->attributes(); + for (Qt3DCore::QNodeId attributeId : attributeIds) { + Attribute *attribute = m_manager->attributeManager()->lookupResource(attributeId); + switch (attribute->attributeType()) { + case QAttribute::IndexAttribute: + indexAttribute = attribute; + break; + case QAttribute::DrawIndirectAttribute: + indirectAttribute = attribute; + break; + case QAttribute::VertexAttribute: + estimatedCount = std::max(int(attribute->count()), estimatedCount); + break; + default: + Q_UNREACHABLE(); + break; + } + } + + command.m_drawIndexed = (indexAttribute != nullptr); + command.m_drawIndirect = (indirectAttribute != nullptr); + + // Update the draw command with all the information required for the drawing + if (command.m_drawIndexed) { + command.m_indexAttributeDataType = GraphicsContext::glDataTypeFromAttributeDataType(indexAttribute->vertexBaseType()); + command.m_indexAttributeByteOffset = indexAttribute->byteOffset() + geometryRenderer->indexBufferByteOffset(); + } + + // Note: we only care about the primitiveCount when using direct draw calls + // For indirect draw calls it is assumed the buffer was properly set already + if (command.m_drawIndirect) { + command.m_indirectAttributeByteOffset = indirectAttribute->byteOffset(); + command.m_indirectDrawBuffer = m_manager->bufferManager()->lookupHandle(indirectAttribute->bufferId()); + } else { + // Use the count specified by the GeometryRender + // If not specify use the indexAttribute count if present + // Otherwise tries to use the count from the attribute with the highest count + if (primitiveCount == 0) { + if (indexAttribute) + primitiveCount = indexAttribute->count(); + else + primitiveCount = estimatedCount; + } + } + + command.m_primitiveCount = primitiveCount; + command.m_primitiveType = geometryRenderer->primitiveType(); + command.m_primitiveRestartEnabled = geometryRenderer->primitiveRestartEnabled(); + command.m_restartIndexValue = geometryRenderer->restartIndexValue(); + command.m_firstInstance = geometryRenderer->firstInstance(); + command.m_instanceCount = geometryRenderer->instanceCount(); + command.m_firstVertex = geometryRenderer->firstVertex(); + command.m_indexOffset = geometryRenderer->indexOffset(); + command.m_verticesPerPatch = geometryRenderer->verticesPerPatch(); + } // scope + + commands.push_back(entity, std::move(command), std::move(passData)); @@ -672,7 +743,8 @@ EntityRenderCommandData RenderView::buildDrawRenderCommands(const QVector<Entity return commands; } -EntityRenderCommandData RenderView::buildComputeRenderCommands(const QVector<Entity *> &entities) const +EntityRenderCommandData RenderView::buildComputeRenderCommands(const QVector<Entity *> &entities, + int offset, int count) const { // If the RenderView contains only a ComputeDispatch then it cares about // A ComputeDispatch is also implicitely a NoDraw operation @@ -681,9 +753,11 @@ EntityRenderCommandData RenderView::buildComputeRenderCommands(const QVector<Ent // material/effect/technique/parameters/filters/ EntityRenderCommandData commands; - commands.reserve(entities.size()); + commands.reserve(count); - for (Entity *entity : entities) { + for (int i = 0; i < count; ++i) { + const int idx = offset + i; + Entity *entity = entities.at(idx); ComputeCommand *computeJob = nullptr; HComputeCommand computeCommandHandle = entity->componentHandle<ComputeCommand>(); if ((computeJob = nodeManagers()->computeJobManager()->data(computeCommandHandle)) != nullptr @@ -725,7 +799,9 @@ EntityRenderCommandData RenderView::buildComputeRenderCommands(const QVector<Ent return commands; } -void RenderView::updateRenderCommand(EntityRenderCommandData &renderCommandData) +void RenderView::updateRenderCommand(EntityRenderCommandData *renderCommandData, + int offset, + int count) { // Note: since many threads can be building render commands // we need to ensure that the UniformBlockValueBuilder they are using @@ -735,10 +811,11 @@ void RenderView::updateRenderCommand(EntityRenderCommandData &renderCommandData) builder->textureManager = m_manager->textureManager(); m_localData.setLocalData(builder); - for (int i = 0, m = renderCommandData.size(); i < m; ++i) { - Entity *entity = renderCommandData.entities.at(i); - const RenderPassParameterData passData = renderCommandData.passesData.at(i); - RenderCommand &command = renderCommandData.commands[i]; + for (int i = 0, m = count; i < m; ++i) { + const int idx = offset + i; + Entity *entity = renderCommandData->entities.at(idx); + const RenderPassParameterData passData = renderCommandData->passesData.at(idx); + RenderCommand &command = renderCommandData->commands[idx]; // Pick which lights to take in to account. // For now decide based on the distance by taking the MAX_LIGHTS closest lights. @@ -944,6 +1021,7 @@ void RenderView::setShaderAndUniforms(RenderCommand *command, // If a parameter is defined and not found in the bindings it is assumed to be a binding of Uniform type with the glsl name // equals to the parameter name const QVector<int> uniformNamesIds = shader->uniformsNamesIds(); + const QVector<int> standardUniformNamesIds = shader->standardUniformNameIds(); const QVector<int> uniformBlockNamesIds = shader->uniformBlockNamesIds(); const QVector<int> shaderStorageBlockNamesIds = shader->storageBlockNamesIds(); const QVector<int> attributeNamesIds = shader->attributeNamesIds(); @@ -963,20 +1041,23 @@ void RenderView::setShaderAndUniforms(RenderCommand *command, shader->setFragOutputs(fragOutputs); } - if (!uniformNamesIds.isEmpty() || !attributeNamesIds.isEmpty() || + if (!uniformNamesIds.isEmpty() || !standardUniformNamesIds.isEmpty() || + !attributeNamesIds.isEmpty() || !shaderStorageBlockNamesIds.isEmpty() || !attributeNamesIds.isEmpty()) { // Set default standard uniforms without bindings const Matrix4x4 worldTransform = *(entity->worldTransform()); - for (const int uniformNameId : uniformNamesIds) { - if (ms_standardUniformSetters.contains(uniformNameId)) + for (const int uniformNameId : standardUniformNamesIds) setStandardUniformValue(command->m_parameterPack, uniformNameId, uniformNameId, entity, worldTransform); - } // Set default attributes command->m_activeAttributes = attributeNamesIds; + // At this point we know whether the command is a valid draw command or not + // We still need to process the uniforms as the command could be a compute command + command->m_isValid = !command->m_activeAttributes.empty(); + // Parameters remaining could be // -> uniform scalar / vector // -> uniform struct / arrays diff --git a/src/render/renderers/opengl/renderer/renderview_p.h b/src/render/renderers/opengl/renderer/renderview_p.h index 1221e7a59..c7dc37a2c 100644 --- a/src/render/renderers/opengl/renderer/renderview_p.h +++ b/src/render/renderers/opengl/renderer/renderview_p.h @@ -227,11 +227,14 @@ public: RenderPassList passesAndParameters(ParameterInfoList *parameter, Entity *node, bool useDefaultMaterials = true); - EntityRenderCommandData buildDrawRenderCommands(const QVector<Entity *> &entities) const; - EntityRenderCommandData buildComputeRenderCommands(const QVector<Entity *> &entities) const; + EntityRenderCommandData buildDrawRenderCommands(const QVector<Entity *> &entities, + int offset, int count) const; + EntityRenderCommandData buildComputeRenderCommands(const QVector<Entity *> &entities, + int offset, int count) const; - void updateRenderCommand(EntityRenderCommandData &renderCommandData); + void updateRenderCommand(EntityRenderCommandData *renderCommandData, + int offset, int count); void setCommands(const QVector<RenderCommand> &commands) Q_DECL_NOTHROW { m_commands = commands; } diff --git a/src/render/renderers/opengl/renderer/renderviewbuilder.cpp b/src/render/renderers/opengl/renderer/renderviewbuilder.cpp index 96fa55c47..9f7589ecc 100644 --- a/src/render/renderers/opengl/renderer/renderviewbuilder.cpp +++ b/src/render/renderers/opengl/renderer/renderviewbuilder.cpp @@ -50,10 +50,18 @@ namespace Render { // In some cases having less jobs is better (especially on fast cpus where // splitting just adds more overhead). Ideally, we should try to set the value // depending on the platform/CPU/nbr of cores -const int RenderViewBuilder::m_optimalParallelJobCount = std::max(std::min(4, QThread::idealThreadCount()), 2); +const int RenderViewBuilder::m_optimalParallelJobCount = QThread::idealThreadCount(); namespace { +int findIdealNumberOfWorkers(int elementCount, int packetSize = 100) +{ + if (elementCount == 0 || packetSize == 0) + return 0; + return std::min(std::max(elementCount / packetSize, 1), RenderViewBuilder::optimalJobCount()); +} + + class SyncPreCommandBuilding { public: @@ -82,16 +90,16 @@ public: lock.unlock(); - // Split among the number of command builders - int i = 0; - const int m = RenderViewBuilder::optimalJobCount() - 1; - const int packetSize = entities.size() / RenderViewBuilder::optimalJobCount(); - while (i < m) { + // Split among the ideal number of command builders + const int idealPacketSize = std::min(std::max(100, entities.size() / RenderViewBuilder::optimalJobCount()), entities.size()); + // Try to split work into an ideal number of workers + const int m = findIdealNumberOfWorkers(entities.size(), idealPacketSize); + + for (int i = 0; i < m; ++i) { const RenderViewCommandBuilderJobPtr renderViewCommandBuilder = m_renderViewCommandBuilderJobs.at(i); - renderViewCommandBuilder->setEntities(entities.mid(i * packetSize, packetSize)); - ++i; + const int count = (i == m - 1) ? entities.size() - (i * idealPacketSize) : idealPacketSize; + renderViewCommandBuilder->setEntities(entities, i * idealPacketSize, count); } - m_renderViewCommandBuilderJobs.at(i)->setEntities(entities.mid(i * packetSize, packetSize + entities.size() % (m + 1))); } private: @@ -117,22 +125,20 @@ public: // Append all the commands and sort them RenderView *rv = m_renderViewJob->renderView(); - int totalCommandCount = 0; - for (const auto &renderViewCommandBuilder : qAsConst(m_renderViewCommandUpdaterJobs)) - totalCommandCount += renderViewCommandBuilder->commands().size(); + const EntityRenderCommandData *commandData = m_renderViewCommandUpdaterJobs.first()->renderables(); - QVector<RenderCommand> commands; - commands.reserve(totalCommandCount); + if (commandData != nullptr) { + const QVector<RenderCommand> commands = std::move(commandData->commands); - // Reduction - for (const auto &renderViewCommandBuilder : qAsConst(m_renderViewCommandUpdaterJobs)) - commands += std::move(renderViewCommandBuilder->commands()); + // TO DO: Cache the EntityRenderCommandData so that it can be reused if nothing in the scene has changed + delete commandData; - rv->setCommands(commands); + rv->setCommands(commands); - // TO DO: Find way to store commands once or at least only when required - // Sort the commands - rv->sort(); + // TO DO: Find way to store commands once or at least only when required + // Sort the commands + rv->sort(); + } // Enqueue our fully populated RenderView with the RenderThread m_renderer->enqueueRenderView(rv, m_renderViewJob->submitOrderIndex()); @@ -279,12 +285,12 @@ public: commandData += std::move(renderViewCommandBuilder->commandData()); } + // Store new cache RendererCache::LeafNodeData &writableCacheForLeaf = cache->leafNodeCache[m_leafNode]; writableCacheForLeaf.renderCommandData = std::move(commandData); } const EntityRenderCommandData commandData = dataCacheForLeaf.renderCommandData; - const QVector<Entity *> filteredEntities = dataCacheForLeaf.filterEntitiesByLayer; QVector<Entity *> renderableEntities = isDraw ? cache->renderableEntities : cache->computeEntities; QVector<LightSource> lightSources = cache->gatheredLights; @@ -316,8 +322,8 @@ public: // Filter out Render commands for which the Entity wasn't selected because // of frustum, proximity or layer filtering - EntityRenderCommandData filteredCommandData; - filteredCommandData.reserve(renderableEntities.size()); + EntityRenderCommandData *filteredCommandData = new EntityRenderCommandData(); + filteredCommandData->reserve(renderableEntities.size()); // Because dataCacheForLeaf.renderableEntities or computeEntities are sorted // What we get out of EntityRenderCommandData is also sorted by Entity auto eIt = std::cbegin(renderableEntities); @@ -335,24 +341,24 @@ public: // Push pointers to command data for all commands that match the // entity while (cIt != cEnd && commandData.entities.at(cIt) == targetEntity) { - filteredCommandData.push_back(commandData.entities.at(cIt), - commandData.commands.at(cIt), - commandData.passesData.at(cIt)); + filteredCommandData->push_back(commandData.entities.at(cIt), + commandData.commands.at(cIt), + commandData.passesData.at(cIt)); ++cIt; } ++eIt; } // Split among the number of command builders - int i = 0; - const int m = RenderViewBuilder::optimalJobCount() - 1; - const int packetSize = filteredCommandData.size() / RenderViewBuilder::optimalJobCount(); - while (i < m) { + // The idealPacketSize is at least 100 entities per worker + const int idealPacketSize = std::min(std::max(100, filteredCommandData->size() / RenderViewBuilder::optimalJobCount()), filteredCommandData->size()); + const int m = findIdealNumberOfWorkers(filteredCommandData->size(), idealPacketSize); + + for (int i = 0; i < m; ++i) { const RenderViewCommandUpdaterJobPtr renderViewCommandBuilder = m_renderViewCommandUpdaterJobs.at(i); - renderViewCommandBuilder->setRenderables(filteredCommandData.mid(i * packetSize, packetSize)); - ++i; + const int count = (i == m - 1) ? filteredCommandData->size() - (i * idealPacketSize) : idealPacketSize; + renderViewCommandBuilder->setRenderables(filteredCommandData, i * idealPacketSize, count); } - m_renderViewCommandUpdaterJobs.at(i)->setRenderables(filteredCommandData.mid(i * packetSize, packetSize + filteredCommandData.size() % (m + 1))); } } diff --git a/tests/auto/core/qaspectengine/tst_qaspectengine.cpp b/tests/auto/core/qaspectengine/tst_qaspectengine.cpp index eb20536e6..2f16bf7c6 100644 --- a/tests/auto/core/qaspectengine/tst_qaspectengine.cpp +++ b/tests/auto/core/qaspectengine/tst_qaspectengine.cpp @@ -146,6 +146,10 @@ private Q_SLOTS: void shouldNotCrashInNormalStartupShutdownSequence() { +#ifdef Q_OS_MACOS + QSKIP("Test frequently times out. See QTBUG-80660."); +#endif + // GIVEN // An initialized aspect engine... QAspectEngine engine; @@ -168,7 +172,7 @@ private Q_SLOTS: engine.setRootEntity(entity); QEventLoop eventLoop; - QTimer::singleShot(100, &eventLoop, SLOT(quit())); + QTimer::singleShot(1000, &eventLoop, SLOT(quit())); eventLoop.exec(); // THEN diff --git a/tests/auto/render/segmentvisitor/tst_segmentvisitor.cpp b/tests/auto/render/segmentvisitor/tst_segmentvisitor.cpp index 4db12136a..fc65d0854 100644 --- a/tests/auto/render/segmentvisitor/tst_segmentvisitor.cpp +++ b/tests/auto/render/segmentvisitor/tst_segmentvisitor.cpp @@ -426,12 +426,15 @@ private Q_SLOTS: simulateInitializationSync(dataBuffer.data(), backendBuffer); QByteArray indexData; - indexData.resize(sizeof(uint) * 2 * 4); + indexData.resize(sizeof(uint) * 7); uint *iDataPtr = reinterpret_cast<uint *>(indexData.data()); iDataPtr[0] = 0; iDataPtr[1] = 1; iDataPtr[2] = 2; iDataPtr[3] = 3; + iDataPtr[4] = static_cast<uint>(-1); + iDataPtr[5] = 0; + iDataPtr[6] = 1; indexDataBuffer->setData(indexData); Buffer *backendIndexBuffer = nodeManagers->bufferManager()->getOrCreateResource(indexDataBuffer->id()); @@ -450,7 +453,7 @@ private Q_SLOTS: indexAttribute->setBuffer(indexDataBuffer.data()); indexAttribute->setVertexBaseType(Qt3DRender::QAttribute::UnsignedInt); - indexAttribute->setCount(4); + indexAttribute->setCount(7); indexAttribute->setAttributeType(Qt3DRender::QAttribute::IndexAttribute); geometry->addAttribute(positionAttribute.data()); @@ -458,6 +461,8 @@ private Q_SLOTS: geometryRenderer->setGeometry(geometry); geometryRenderer->setPrimitiveType(Qt3DRender::QGeometryRenderer::LineStrip); + geometryRenderer->setPrimitiveRestartEnabled(true); + geometryRenderer->setRestartIndexValue(-1); Attribute *backendAttribute = nodeManagers->attributeManager()->getOrCreateResource(positionAttribute->id()); backendAttribute->setRenderer(&renderer); @@ -480,10 +485,11 @@ private Q_SLOTS: visitor.apply(backendRenderer, Qt3DCore::QNodeId()); // THEN - QCOMPARE(visitor.segmentCount(), uint(3)); + QCOMPARE(visitor.segmentCount(), uint(4)); QVERIFY(visitor.verifySegment(0, 0,1, Vector3D(0,0,0), Vector3D(1,0,0))); QVERIFY(visitor.verifySegment(1, 1,2, Vector3D(1,0,0), Vector3D(1,1,0))); QVERIFY(visitor.verifySegment(2, 2,3, Vector3D(1,1,0), Vector3D(0,1,0))); + QVERIFY(visitor.verifySegment(3, 0,1, Vector3D(0,0,0), Vector3D(1,0,0))); } void testVisitLineLoop() @@ -588,12 +594,16 @@ private Q_SLOTS: simulateInitializationSync(dataBuffer.data(), backendBuffer); QByteArray indexData; - indexData.resize(sizeof(uint) * 2 * 4); + indexData.resize(sizeof(uint) * 8); uint *iDataPtr = reinterpret_cast<uint *>(indexData.data()); iDataPtr[0] = 0; iDataPtr[1] = 1; iDataPtr[2] = 2; iDataPtr[3] = 3; + iDataPtr[4] = static_cast<uint>(-1); + iDataPtr[5] = 0; + iDataPtr[6] = 1; + iDataPtr[7] = 2; indexDataBuffer->setData(indexData); Buffer *backendIndexBuffer = nodeManagers->bufferManager()->getOrCreateResource(indexDataBuffer->id()); @@ -612,7 +622,7 @@ private Q_SLOTS: indexAttribute->setBuffer(indexDataBuffer.data()); indexAttribute->setVertexBaseType(Qt3DRender::QAttribute::UnsignedInt); - indexAttribute->setCount(4); + indexAttribute->setCount(8); indexAttribute->setAttributeType(Qt3DRender::QAttribute::IndexAttribute); geometry->addAttribute(positionAttribute.data()); @@ -620,6 +630,8 @@ private Q_SLOTS: geometryRenderer->setGeometry(geometry); geometryRenderer->setPrimitiveType(Qt3DRender::QGeometryRenderer::LineLoop); + geometryRenderer->setPrimitiveRestartEnabled(true); + geometryRenderer->setRestartIndexValue(-1); Attribute *backendAttribute = nodeManagers->attributeManager()->getOrCreateResource(positionAttribute->id()); backendAttribute->setRenderer(&renderer); @@ -642,11 +654,14 @@ private Q_SLOTS: visitor.apply(backendRenderer, Qt3DCore::QNodeId()); // THEN - QCOMPARE(visitor.segmentCount(), uint(4)); + QCOMPARE(visitor.segmentCount(), uint(7)); QVERIFY(visitor.verifySegment(0, 0,1, Vector3D(0,0,0), Vector3D(1,0,0))); QVERIFY(visitor.verifySegment(1, 1,2, Vector3D(1,0,0), Vector3D(1,1,0))); QVERIFY(visitor.verifySegment(2, 2,3, Vector3D(1,1,0), Vector3D(0,1,0))); QVERIFY(visitor.verifySegment(3, 3,0, Vector3D(0,1,0), Vector3D(0,0,0))); + QVERIFY(visitor.verifySegment(4, 0,1, Vector3D(0,0,0), Vector3D(1,0,0))); + QVERIFY(visitor.verifySegment(5, 1,2, Vector3D(1,0,0), Vector3D(1,1,0))); + QVERIFY(visitor.verifySegment(6, 2,0, Vector3D(1,1,0), Vector3D(0,0,0))); } void testVisitLineAdjacency() diff --git a/tests/auto/render/trianglevisitor/tst_trianglevisitor.cpp b/tests/auto/render/trianglevisitor/tst_trianglevisitor.cpp index 8dfda0eea..66f67e08a 100644 --- a/tests/auto/render/trianglevisitor/tst_trianglevisitor.cpp +++ b/tests/auto/render/trianglevisitor/tst_trianglevisitor.cpp @@ -454,7 +454,7 @@ private Q_SLOTS: simulateInitializationSync(dataBuffer.data(), backendBuffer); QByteArray indexData; - indexData.resize(sizeof(uint) * 3 * 4); + indexData.resize(sizeof(uint) * 4 * 4); uint *iDataPtr = reinterpret_cast<uint *>(indexData.data()); iDataPtr[0] = 0; iDataPtr[1] = 1; @@ -468,6 +468,10 @@ private Q_SLOTS: iDataPtr[9] = 4; iDataPtr[10] = 3; iDataPtr[11] = 2; + iDataPtr[12] = static_cast<uint>(-1); + iDataPtr[13] = 0; + iDataPtr[14] = 1; + iDataPtr[15] = 2; indexDataBuffer->setData(indexData); Buffer *backendIndexBuffer = nodeManagers->bufferManager()->getOrCreateResource(indexDataBuffer->id()); @@ -486,7 +490,7 @@ private Q_SLOTS: indexAttribute->setBuffer(indexDataBuffer.data()); indexAttribute->setVertexBaseType(Qt3DRender::QAttribute::UnsignedInt); - indexAttribute->setCount(3*4); + indexAttribute->setCount(4*4); indexAttribute->setAttributeType(Qt3DRender::QAttribute::IndexAttribute); geometry->addAttribute(positionAttribute.data()); @@ -494,6 +498,8 @@ private Q_SLOTS: geometryRenderer->setGeometry(geometry); geometryRenderer->setPrimitiveType(Qt3DRender::QGeometryRenderer::TriangleStrip); + geometryRenderer->setPrimitiveRestartEnabled(true); + geometryRenderer->setRestartIndexValue(-1); Attribute *backendAttribute = nodeManagers->attributeManager()->getOrCreateResource(positionAttribute->id()); backendAttribute->setRenderer(&renderer); @@ -516,7 +522,7 @@ private Q_SLOTS: visitor.apply(backendRenderer, Qt3DCore::QNodeId()); // THEN - QVERIFY(visitor.triangleCount() == 8); + QCOMPARE(visitor.triangleCount(), 9U); QVERIFY(visitor.verifyTriangle(0, 2,1,0, Vector3D(0,1,0), Vector3D(1,0,0), Vector3D(0,0,1))); QVERIFY(visitor.verifyTriangle(1, 3,2,1, Vector3D(0,0,1), Vector3D(0,1,0), Vector3D(1,0,0))); QVERIFY(visitor.verifyTriangle(2, 4,3,2, Vector3D(1,0,0), Vector3D(0,0,1), Vector3D(0,1,0))); @@ -525,6 +531,7 @@ private Q_SLOTS: QVERIFY(visitor.verifyTriangle(5, 4,0,1, Vector3D(1,0,0), Vector3D(0,0,1), Vector3D(1,0,0))); QVERIFY(visitor.verifyTriangle(6, 3,4,0, Vector3D(0,0,1), Vector3D(1,0,0), Vector3D(0,0,1))); QVERIFY(visitor.verifyTriangle(7, 2,3,4, Vector3D(0,1,0), Vector3D(0,0,1), Vector3D(1,0,0))); + QVERIFY(visitor.verifyTriangle(8, 2,1,0, Vector3D(0,1,0), Vector3D(1,0,0), Vector3D(0,0,1))); } void testVisitTriangleFan() @@ -643,7 +650,7 @@ private Q_SLOTS: simulateInitializationSync(dataBuffer.data(), backendBuffer); QByteArray indexData; - indexData.resize(sizeof(uint) * 3 * 2); + indexData.resize(sizeof(uint) * 10); uint *iDataPtr = reinterpret_cast<uint *>(indexData.data()); iDataPtr[0] = 0; iDataPtr[1] = 1; @@ -651,6 +658,10 @@ private Q_SLOTS: iDataPtr[3] = 3; iDataPtr[4] = 4; iDataPtr[5] = 5; + iDataPtr[6] = static_cast<uint>(-1); + iDataPtr[7] = 0; + iDataPtr[8] = 1; + iDataPtr[9] = 2; indexDataBuffer->setData(indexData); Buffer *backendIndexBuffer = nodeManagers->bufferManager()->getOrCreateResource(indexDataBuffer->id()); @@ -669,7 +680,7 @@ private Q_SLOTS: indexAttribute->setBuffer(indexDataBuffer.data()); indexAttribute->setVertexBaseType(Qt3DRender::QAttribute::UnsignedInt); - indexAttribute->setCount(3*2); + indexAttribute->setCount(10); indexAttribute->setAttributeType(Qt3DRender::QAttribute::IndexAttribute); geometry->addAttribute(positionAttribute.data()); @@ -677,6 +688,8 @@ private Q_SLOTS: geometryRenderer->setGeometry(geometry); geometryRenderer->setPrimitiveType(Qt3DRender::QGeometryRenderer::TriangleFan); + geometryRenderer->setPrimitiveRestartEnabled(true); + geometryRenderer->setRestartIndexValue(-1); Attribute *backendAttribute = nodeManagers->attributeManager()->getOrCreateResource(positionAttribute->id()); backendAttribute->setRenderer(&renderer); @@ -699,11 +712,12 @@ private Q_SLOTS: visitor.apply(backendRenderer, Qt3DCore::QNodeId()); // THEN - QVERIFY(visitor.triangleCount() == 4); + QCOMPARE(visitor.triangleCount(), 5U); QVERIFY(visitor.verifyTriangle(0, 2,1,0, Vector3D(0,1,0), Vector3D(1,0,0), Vector3D(0,0,1))); QVERIFY(visitor.verifyTriangle(1, 3,2,0, Vector3D(0,0,1), Vector3D(0,1,0), Vector3D(0,0,1))); QVERIFY(visitor.verifyTriangle(2, 4,3,0, Vector3D(1,0,0), Vector3D(0,0,1), Vector3D(0,0,1))); QVERIFY(visitor.verifyTriangle(3, 5,4,0, Vector3D(0,1,0), Vector3D(1,0,0), Vector3D(0,0,1))); + QVERIFY(visitor.verifyTriangle(4, 2,1,0, Vector3D(0,1,0), Vector3D(1,0,0), Vector3D(0,0,1))); } void testVisitTrianglesAdjacency() |