diff options
Diffstat (limited to 'src/render')
33 files changed, 702 insertions, 388 deletions
diff --git a/src/render/backend/platformsurfacefilter.cpp b/src/render/backend/platformsurfacefilter.cpp index 891e30c44..7458f607d 100644 --- a/src/render/backend/platformsurfacefilter.cpp +++ b/src/render/backend/platformsurfacefilter.cpp @@ -107,6 +107,10 @@ bool PlatformSurfaceFilter::eventFilter(QObject *obj, QEvent *e) // If we remove it, the call to isSurfaceValid will // implicitely return false PlatformSurfaceFilter::m_surfacesValidity.remove(m_surface); + if (m_obj) { + m_obj->removeEventFilter(this); + m_obj = nullptr; + } break; } diff --git a/src/render/backend/renderer.cpp b/src/render/backend/renderer.cpp index 0fda6e5b4..bd8687d5e 100644 --- a/src/render/backend/renderer.cpp +++ b/src/render/backend/renderer.cpp @@ -86,6 +86,7 @@ #include <Qt3DRender/private/updatelevelofdetailjob_p.h> #include <Qt3DRender/private/buffercapture_p.h> #include <Qt3DRender/private/offscreensurfacehelper_p.h> +#include <Qt3DRender/private/renderviewbuilder_p.h> #include <Qt3DRender/qcameralens.h> #include <Qt3DCore/qt3dcore-config.h> @@ -535,17 +536,26 @@ void Renderer::render() void Renderer::doRender() { - bool submissionSucceeded = false; - bool hasCleanedQueueAndProceeded = false; Renderer::ViewSubmissionResultData submissionData; + bool hasCleanedQueueAndProceeded = false; bool preprocessingComplete = false; - - if (isReadyToSubmit()) { - - // Lock the mutex to protect access to m_surface and check if we are still set - // to the running state and that we have a valid surface on which to draw - // TO DO: Is that still needed given the surface changes - QMutexLocker locker(&m_mutex); + bool beganDrawing = false; + const bool canSubmit = isReadyToSubmit(); + + // Lock the mutex to protect access to the renderQueue while we look for its state + QMutexLocker locker(&m_renderQueueMutex); + 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 + // Since a call to render may not be synched with the completions + // of the RenderViewJobs + // In such a case we return early, waiting for a next call with + // the frame queue complete at this point + + // RenderQueue is complete (but that means it may be of size 0) + if (canSubmit && (queueIsComplete && !queueIsEmpty)) { const QVector<Render::RenderView *> renderViews = m_renderQueue->nextFrameQueue(); #ifdef QT3D_JOBS_RUN_STATS @@ -560,8 +570,7 @@ void Renderer::doRender() submissionStatsPart2.jobId.typeAndInstance[1] = 0; submissionStatsPart2.threadId = reinterpret_cast<quint64>(QThread::currentThreadId()); #endif - - if (canRender() && (submissionSucceeded = renderViews.size() > 0) == true) { + if (canRender()) { // Clear all dirty flags but Compute so that // we still render every frame when a compute shader is used in a scene BackendNodeDirtySet changesToUnset = m_changeSet; @@ -583,7 +592,8 @@ void Renderer::doRender() // Reset state for each draw if we don't have complete control of the context if (!m_ownedContext) m_graphicsContext->setCurrentStateSet(nullptr); - if (m_graphicsContext->beginDrawing(surface)) { + beganDrawing = m_graphicsContext->beginDrawing(surface); + if (beganDrawing) { // 1) Execute commands for buffer uploads, texture updates, shader loading first updateGLResources(); // 2) Update VAO and copy data into commands to allow concurrent submission @@ -594,6 +604,7 @@ void Renderer::doRender() } // 2) Proceed to next frame and start preparing frame n + 1 m_renderQueue->reset(); + locker.unlock(); // Done protecting RenderQueue m_vsyncFrameAdvanceService->proceedToNextFrame(); hasCleanedQueueAndProceeded = true; @@ -637,61 +648,57 @@ void Renderer::doRender() #endif } - // Note: submissionSucceeded is false when - // * we cannot render because a shutdown has been scheduled - // * the renderqueue is incomplete (only when rendering with a Scene3D) - // Otherwise returns true even for cases like - // * No render view - // * No surface set - // * OpenGLContext failed to be set current - // This behavior is important as we need to - // call proceedToNextFrame despite rendering errors that aren't fatal - // Only reset renderQueue and proceed to next frame if the submission - // succeeded or it we are using a render thread and that is wasn't performed + // succeeded or if we are using a render thread and that is wasn't performed // already - // If submissionSucceeded isn't true this implies that something went wrong + // 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 ....) - if (m_renderThread || submissionSucceeded) { - - if (!hasCleanedQueueAndProceeded) { - // Reset the m_renderQueue so that we won't try to render - // with a queue used by a previous frame with corrupted content - // if the current queue was correctly submitted - m_renderQueue->reset(); - - // We allow the RenderTickClock service to proceed to the next frame - // In turn this will allow the aspect manager to request a new set of jobs - // to be performed for each aspect - m_vsyncFrameAdvanceService->proceedToNextFrame(); - } + // or alternatively it could be complete but empty (RenderQueue of size 0) + if (!hasCleanedQueueAndProceeded && + (m_renderThread || queueIsComplete || queueIsEmpty)) { + // 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 + + // Reset the m_renderQueue so that we won't try to render + // with a queue used by a previous frame with corrupted content + // if the current queue was correctly submitted + m_renderQueue->reset(); + + // We allow the RenderTickClock service to proceed to the next frame + // In turn this will allow the aspect manager to request a new set of jobs + // to be performed for each aspect + m_vsyncFrameAdvanceService->proceedToNextFrame(); + } - // Perform the last swapBuffers calls after the proceedToNextFrame - // as this allows us to gain a bit of time for the preparation of the - // next frame + // Perform the last swapBuffers calls after the proceedToNextFrame + // as this allows us to gain a bit of time for the preparation of the + // next frame + // Finish up with last surface used in the list of RenderViews + if (beganDrawing) { + SurfaceLocker surfaceLock(submissionData.surface); // Finish up with last surface used in the list of RenderViews - if (submissionSucceeded) { - SurfaceLocker surfaceLock(submissionData.surface); - // Finish up with last surface used in the list of RenderViews - m_graphicsContext->endDrawing(submissionData.lastBoundFBOId == m_graphicsContext->defaultFBO() && surfaceLock.isSurfaceValid()); - } + m_graphicsContext->endDrawing(submissionData.lastBoundFBOId == m_graphicsContext->defaultFBO() && surfaceLock.isSurfaceValid()); } } // Called by RenderViewJobs +// When the frameQueue is complete and we are using a renderThread +// we allow the render thread to proceed void Renderer::enqueueRenderView(Render::RenderView *renderView, int submitOrder) { - QMutexLocker locker(&m_mutex); // Prevent out of order execution + QMutexLocker locker(&m_renderQueueMutex); // Prevent out of order execution // We cannot use a lock free primitive here because: // - QVector is not thread safe // - Even if the insert is made correctly, the isFrameComplete call // could be invalid since depending on the order of execution // the counter could be complete but the renderview not yet added to the // buffer depending on whichever order the cpu decides to process this - - if (m_renderQueue->queueRenderView(renderView, submitOrder)) { + const bool isQueueComplete = m_renderQueue->queueRenderView(renderView, submitOrder); + locker.unlock(); // We're done protecting the queue at this point + if (isQueueComplete) { if (m_renderThread && m_running.load()) Q_ASSERT(m_submitRenderViewsSemaphore.available() == 0); m_submitRenderViewsSemaphore.release(1); @@ -730,16 +737,6 @@ bool Renderer::isReadyToSubmit() // something to render // The case of shutdown should have been handled just before Q_ASSERT(m_renderQueue->isFrameQueueComplete()); - } else { - // When using synchronous rendering (QtQuick) - // We are not sure that the frame queue is actually complete - // Since a call to render may not be synched with the completions - // of the RenderViewJobs - // In such a case we return early, waiting for a next call with - // the frame queue complete at this point - QMutexLocker locker(&m_mutex); - if (!m_renderQueue->isFrameQueueComplete()) - return false; } return true; } @@ -1016,12 +1013,11 @@ void Renderer::updateGLResources() const QVector<HBuffer> dirtyBufferHandles = std::move(m_dirtyBuffers); for (HBuffer handle: dirtyBufferHandles) { Buffer *buffer = m_nodesManager->bufferManager()->data(handle); - // Perform data upload // Forces creation if it doesn't exit if (!m_graphicsContext->hasGLBufferForBuffer(buffer)) m_graphicsContext->glBufferForRenderBuffer(buffer); - else if (buffer->isDirty()) // Otherwise update the glBuffer - m_graphicsContext->updateBuffer(buffer); + // Update the glBuffer data + m_graphicsContext->updateBuffer(buffer); buffer->unsetDirty(); } } @@ -1061,6 +1057,11 @@ void Renderer::updateGLResources() // Render Thread void Renderer::updateTexture(Texture *texture) { + // Check that the current texture images are still in place, if not, do not update + const bool isValid = texture->isValid(); + if (!isValid) + return; + // For implementing unique, non-shared, non-cached textures. // for now, every texture is shared by default @@ -1425,16 +1426,25 @@ QVector<Qt3DCore::QAspectJobPtr> Renderer::renderBinJobs() renderBinJobs.push_back(m_textureGathererJob); renderBinJobs.push_back(m_shaderGathererJob); - // Traverse the current framegraph. For each leaf node create a - // RenderView and set its configuration then create a job to - // populate the RenderView with a set of RenderCommands that get - // their details from the RenderNodes that are visible to the - // Camera selected by the framegraph configuration - FrameGraphVisitor visitor(this, m_nodesManager->frameGraphManager()); - visitor.traverse(frameGraphRoot(), &renderBinJobs); + QMutexLocker lock(&m_renderQueueMutex); + if (m_renderQueue->wasReset()) { // Have we rendered yet? (Scene3D case) + // Traverse the current framegraph. For each leaf node create a + // RenderView and set its configuration then create a job to + // populate the RenderView with a set of RenderCommands that get + // their details from the RenderNodes that are visible to the + // Camera selected by the framegraph configuration + FrameGraphVisitor visitor(m_nodesManager->frameGraphManager()); + const QVector<FrameGraphNode *> fgLeaves = visitor.traverse(frameGraphRoot()); + + const int fgBranchCount = fgLeaves.size(); + for (int i = 0; i < fgBranchCount; ++i) { + RenderViewBuilder builder(fgLeaves.at(i), i, this); + renderBinJobs.append(builder.buildJobHierachy()); + } - // Set target number of RenderViews - m_renderQueue->setTargetRenderViewCount(visitor.leafNodeCount()); + // Set target number of RenderViews + m_renderQueue->setTargetRenderViewCount(fgBranchCount); + } return renderBinJobs; } diff --git a/src/render/backend/renderer_p.h b/src/render/backend/renderer_p.h index 9df24ef2c..af10108e9 100644 --- a/src/render/backend/renderer_p.h +++ b/src/render/backend/renderer_p.h @@ -259,7 +259,7 @@ public: ViewSubmissionResultData submitRenderViews(const QVector<Render::RenderView *> &renderViews); - QMutex* mutex() { return &m_mutex; } + QMutex* mutex() { return &m_renderQueueMutex; } #ifdef QT3D_RENDER_UNIT_TESTS @@ -290,7 +290,7 @@ private: QScopedPointer<RenderThread> m_renderThread; QScopedPointer<VSyncFrameAdvanceService> m_vsyncFrameAdvanceService; - QMutex m_mutex; + QMutex m_renderQueueMutex; QSemaphore m_submitRenderViewsSemaphore; QSemaphore m_waitForInitializationToBeCompleted; diff --git a/src/render/backend/renderqueue.cpp b/src/render/backend/renderqueue.cpp index 6ec7da464..2fa1cb7d2 100644 --- a/src/render/backend/renderqueue.cpp +++ b/src/render/backend/renderqueue.cpp @@ -49,6 +49,7 @@ namespace Render { RenderQueue::RenderQueue() : m_noRender(false) + , m_wasReset(true) , m_targetRenderViewCount(0) , m_currentRenderViewCount(0) , m_currentWorkQueue(1) @@ -70,6 +71,7 @@ void RenderQueue::reset() m_targetRenderViewCount = 0; m_currentWorkQueue.clear(); m_noRender = false; + m_wasReset = true; } void RenderQueue::setNoRender() @@ -88,6 +90,7 @@ bool RenderQueue::queueRenderView(RenderView *renderView, uint submissionOrderIn Q_ASSERT(!m_noRender); m_currentWorkQueue[submissionOrderIndex] = renderView; ++m_currentRenderViewCount; + Q_ASSERT(m_currentRenderViewCount <= m_targetRenderViewCount); return isFrameQueueComplete(); } @@ -109,6 +112,7 @@ void RenderQueue::setTargetRenderViewCount(int targetRenderViewCount) Q_ASSERT(!m_noRender); m_targetRenderViewCount = targetRenderViewCount; m_currentWorkQueue.resize(targetRenderViewCount); + m_wasReset = false; } /*! @@ -119,7 +123,7 @@ void RenderQueue::setTargetRenderViewCount(int targetRenderViewCount) bool RenderQueue::isFrameQueueComplete() const { return (m_noRender - || (m_targetRenderViewCount && m_targetRenderViewCount == currentRenderViewCount())); + || (m_targetRenderViewCount > 0 && m_targetRenderViewCount == m_currentRenderViewCount)); } } // namespace Render diff --git a/src/render/backend/renderqueue_p.h b/src/render/backend/renderqueue_p.h index 49316049b..611f5849a 100644 --- a/src/render/backend/renderqueue_p.h +++ b/src/render/backend/renderqueue_p.h @@ -77,9 +77,13 @@ public: void reset(); void setNoRender(); + inline bool isNoRender() const { return m_noRender; } + + inline bool wasReset() const { return m_wasReset; } private: bool m_noRender; + bool m_wasReset; int m_targetRenderViewCount; int m_currentRenderViewCount; QVector<RenderView *> m_currentWorkQueue; diff --git a/src/render/backend/renderviewbuilder.cpp b/src/render/backend/renderviewbuilder.cpp index f1355d27e..f47c6f419 100644 --- a/src/render/backend/renderviewbuilder.cpp +++ b/src/render/backend/renderviewbuilder.cpp @@ -66,14 +66,14 @@ public: RenderView *rv = m_renderViewJob->renderView(); int totalCommandCount = 0; - for (const auto renderViewCommandBuilder : qAsConst(m_renderViewBuilderJobs)) + for (const auto &renderViewCommandBuilder : qAsConst(m_renderViewBuilderJobs)) totalCommandCount += renderViewCommandBuilder->commands().size(); QVector<RenderCommand *> commands; commands.reserve(totalCommandCount); // Reduction - for (const auto renderViewCommandBuilder : qAsConst(m_renderViewBuilderJobs)) + for (const auto &renderViewCommandBuilder : qAsConst(m_renderViewBuilderJobs)) commands += std::move(renderViewCommandBuilder->commands()); rv->setCommands(commands); @@ -139,13 +139,13 @@ public: m_filterEntityByLayerJob->setLayers(rv->layerFilter()); // Material Parameter building - for (const auto materialGatherer : qAsConst(m_materialGathererJobs)) { + for (const auto &materialGatherer : qAsConst(m_materialGathererJobs)) { materialGatherer->setRenderPassFilter(const_cast<RenderPassFilter *>(rv->renderPassFilter())); materialGatherer->setTechniqueFilter(const_cast<TechniqueFilter *>(rv->techniqueFilter())); } // Command builders - for (const auto renderViewCommandBuilder : qAsConst(m_renderViewBuilderJobs)) + for (const auto &renderViewCommandBuilder : qAsConst(m_renderViewBuilderJobs)) renderViewCommandBuilder->setRenderView(rv); // Set whether frustum culling is enabled or not @@ -188,8 +188,6 @@ public: RenderView *rv = m_renderViewJob->renderView(); if (!rv->noDraw()) { - // Set the light sources - rv->setLightSources(std::move(m_lightGathererJob->lights())); rv->setEnvironmentLight(m_lightGathererJob->takeEnvironmentLight()); // We sort the vector so that the removal can then be performed linearly @@ -208,6 +206,14 @@ public: QVector<Entity *> filteredEntities = m_filterEntityByLayerJob->filteredEntities(); RenderViewBuilder::removeEntitiesNotInSubset(renderableEntities, filteredEntities); + // Set the light sources, with layer filters applied. + QVector<LightSource> lightSources = m_lightGathererJob->lights(); + for (int i = 0; i < lightSources.count(); ++i) { + if (!filteredEntities.contains(lightSources[i].entity)) + lightSources.removeAt(i--); + } + rv->setLightSources(lightSources); + // Filter out frustum culled entity for drawable entities if (isDraw && rv->frustumCulling()) RenderViewBuilder::removeEntitiesNotInSubset(renderableEntities, m_frustumCullingJob->visibleEntities()); @@ -225,7 +231,7 @@ public: // Reduction QHash<Qt3DCore::QNodeId, QVector<RenderPassParameterData>> params; - for (const auto materialGatherer : qAsConst(m_materialGathererJobs)) + for (const auto &materialGatherer : qAsConst(m_materialGathererJobs)) params.unite(materialGatherer->materialToPassAndParameter()); // Set all required data on the RenderView for final processing rv->setMaterialParameterTable(std::move(params)); @@ -425,7 +431,7 @@ QVector<Qt3DCore::QAspectJobPtr> RenderViewBuilder::buildJobHierachy() const m_filterEntityByLayerJob->addDependency(m_renderer->updateTreeEnabledJob()); m_syncRenderCommandBuildingJob->addDependency(m_syncRenderViewInitializationJob); - for (const auto materialGatherer : qAsConst(m_materialGathererJobs)) { + for (const auto &materialGatherer : qAsConst(m_materialGathererJobs)) { materialGatherer->addDependency(m_syncRenderViewInitializationJob); materialGatherer->addDependency(m_renderer->filterCompatibleTechniqueJob()); m_syncRenderCommandBuildingJob->addDependency(materialGatherer); @@ -436,7 +442,7 @@ QVector<Qt3DCore::QAspectJobPtr> RenderViewBuilder::buildJobHierachy() const m_syncRenderCommandBuildingJob->addDependency(m_lightGathererJob); m_syncRenderCommandBuildingJob->addDependency(m_frustumCullingJob); - for (const auto renderViewCommandBuilder : qAsConst(m_renderViewBuilderJobs)) { + for (const auto &renderViewCommandBuilder : qAsConst(m_renderViewBuilderJobs)) { renderViewCommandBuilder->addDependency(m_syncRenderCommandBuildingJob); m_syncRenderViewCommandBuildersJob->addDependency(renderViewCommandBuilder); } @@ -458,13 +464,13 @@ QVector<Qt3DCore::QAspectJobPtr> RenderViewBuilder::buildJobHierachy() const jobs.push_back(m_filterEntityByLayerJob); // Step 3 jobs.push_back(m_setClearDrawBufferIndexJob); // Step 3 - for (const auto materialGatherer : qAsConst(m_materialGathererJobs)) // Step3 + for (const auto &materialGatherer : qAsConst(m_materialGathererJobs)) // Step3 jobs.push_back(materialGatherer); jobs.push_back(m_frustumCullingJob); // Step 4 jobs.push_back(m_syncRenderCommandBuildingJob); // Step 4 - for (const auto renderViewCommandBuilder : qAsConst(m_renderViewBuilderJobs)) // Step 5 + for (const auto &renderViewCommandBuilder : qAsConst(m_renderViewBuilderJobs)) // Step 5 jobs.push_back(renderViewCommandBuilder); jobs.push_back(m_syncRenderViewCommandBuildersJob); // Step 6 diff --git a/src/render/framegraph/framegraphvisitor.cpp b/src/render/framegraph/framegraphvisitor.cpp index d31e9cddd..cd8b08219 100644 --- a/src/render/framegraph/framegraphvisitor.cpp +++ b/src/render/framegraph/framegraphvisitor.cpp @@ -43,7 +43,6 @@ #include "framegraphnode_p.h" #include <Qt3DRender/private/renderer_p.h> #include <Qt3DRender/private/managers_p.h> -#include <Qt3DRender/private/renderviewbuilder_p.h> #include <QThreadPool> QT_BEGIN_NAMESPACE @@ -53,24 +52,16 @@ using namespace Qt3DCore; namespace Qt3DRender { namespace Render { -FrameGraphVisitor::FrameGraphVisitor(Renderer *renderer, - const FrameGraphManager *manager) - : m_renderer(renderer) - , m_manager(manager) - , m_jobs(nullptr) - , m_renderviewIndex(0) - +FrameGraphVisitor::FrameGraphVisitor(const FrameGraphManager *manager) + : m_manager(manager) { + m_leaves.reserve(8); } -void FrameGraphVisitor::traverse(FrameGraphNode *root, - QVector<Qt3DCore::QAspectJobPtr> *jobs) +QVector<FrameGraphNode *> FrameGraphVisitor::traverse(FrameGraphNode *root) { - m_jobs = jobs; - m_renderviewIndex = 0; + m_leaves.clear(); - Q_ASSERT(m_renderer); - Q_ASSERT(m_jobs); Q_ASSERT_X(root, Q_FUNC_INFO, "The FrameGraphRoot is null"); // Kick off the traversal @@ -78,6 +69,7 @@ void FrameGraphVisitor::traverse(FrameGraphNode *root, if (node == nullptr) qCritical() << Q_FUNC_INFO << "FrameGraph is null"; visit(node); + return m_leaves; } void FrameGraphVisitor::visit(Render::FrameGraphNode *node) @@ -97,10 +89,8 @@ void FrameGraphVisitor::visit(Render::FrameGraphNode *node) // Leaf node - create a RenderView ready to be populated // TODO: Pass in only framegraph config that has changed from previous // index RenderViewJob. - if (fgChildIds.empty()) { - RenderViewBuilder builder(node, m_renderviewIndex++, m_renderer); - m_jobs->append(builder.buildJobHierachy()); - } + if (fgChildIds.empty()) + m_leaves.push_back(node); } } // namespace Render diff --git a/src/render/framegraph/framegraphvisitor_p.h b/src/render/framegraph/framegraphvisitor_p.h index af8f4caab..f4c0d7796 100644 --- a/src/render/framegraph/framegraphvisitor_p.h +++ b/src/render/framegraph/framegraphvisitor_p.h @@ -66,24 +66,18 @@ class FrameGraphNode; class Renderer; class FrameGraphManager; -class FrameGraphVisitor +class Q_AUTOTEST_EXPORT FrameGraphVisitor { public: - explicit FrameGraphVisitor(Renderer *renderer, - const FrameGraphManager *nodeManager); + explicit FrameGraphVisitor(const FrameGraphManager *nodeManager); - void traverse(FrameGraphNode *root, - QVector<Qt3DCore::QAspectJobPtr> *jobs); - - inline int leafNodeCount() Q_DECL_NOTHROW { return m_renderviewIndex; } + QVector<FrameGraphNode *> traverse(FrameGraphNode *root); private: void visit(Render::FrameGraphNode *node); - Renderer *m_renderer; const FrameGraphManager *m_manager; - QVector<Qt3DCore::QAspectJobPtr> *m_jobs; - int m_renderviewIndex; + QVector<FrameGraphNode *> m_leaves; }; } // namespace Render diff --git a/src/render/framegraph/qsortcriterion.cpp b/src/render/framegraph/qsortcriterion.cpp deleted file mode 100644 index f5252b1f7..000000000 --- a/src/render/framegraph/qsortcriterion.cpp +++ /dev/null @@ -1,82 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 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:LGPL$ -** 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. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qsortcriterion.h" -#include "qsortcriterion_p.h" -#include <Qt3DCore/qnodepropertychange.h> - -QT_BEGIN_NAMESPACE - -namespace Qt3DRender { - -QSortCriterionPrivate::QSortCriterionPrivate() - : QNodePrivate() - , m_sort(QSortCriterion::StateChangeCost) -{ -} - -QSortCriterion::QSortCriterion(QNode *parent) - : QNode(*new QSortCriterionPrivate, parent) -{ -} - -QSortCriterion::SortType QSortCriterion::sort() const -{ - Q_D(const QSortCriterion); - return d->m_sort; -} - -void QSortCriterion::setSort(QSortCriterion::SortType sort) -{ - Q_D(QSortCriterion); - if (d->m_sort != sort) { - d->m_sort = sort; - emit sortChanged(sort); - } -} - -/*! \internal */ -QSortCriterion::QSortCriterion(QSortCriterionPrivate &dd, QNode *parent) - : QNode(dd, parent) -{ -} - -} // namespace Qt3DRender - -QT_END_NAMESPACE diff --git a/src/render/framegraph/qsortcriterion.h b/src/render/framegraph/qsortcriterion.h deleted file mode 100644 index 230f111f9..000000000 --- a/src/render/framegraph/qsortcriterion.h +++ /dev/null @@ -1,85 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 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:LGPL$ -** 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. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QT3DRENDER_QSORTCRITERION_H -#define QT3DRENDER_QSORTCRITERION_H - -#include <Qt3DCore/qnode.h> -#include <Qt3DRender/qt3drender_global.h> - -QT_BEGIN_NAMESPACE - -namespace Qt3DRender { - -class QSortCriterionPrivate; - -class QT3DRENDERSHARED_EXPORT QSortCriterion : public Qt3DCore::QNode -{ - Q_OBJECT - Q_PROPERTY(Qt3DRender::QSortCriterion::SortType sort READ sort WRITE setSort NOTIFY sortChanged) -public: - explicit QSortCriterion(Qt3DCore::QNode *parent = Q_NULLPTR); - - enum SortType { - StateChangeCost = (1 << 0), - BackToFront = (1 << 1), - Material = (1 << 2) - }; - Q_ENUM(SortType) // LCOV_EXCL_LINE - - SortType sort() const; - -public Q_SLOTS: - void setSort(SortType sort); - -Q_SIGNALS: - void sortChanged(SortType sort); - -protected: - QSortCriterion(QSortCriterionPrivate &dd, Qt3DCore::QNode *parent = Q_NULLPTR); - -private: - Q_DECLARE_PRIVATE(QSortCriterion) -}; - -} // namespace Qt3DRender - -QT_END_NAMESPACE - -#endif // QSORTCRITERION_H diff --git a/src/render/framegraph/rendercapture.cpp b/src/render/framegraph/rendercapture.cpp index 1d3117c0d..dea1cf565 100644 --- a/src/render/framegraph/rendercapture.cpp +++ b/src/render/framegraph/rendercapture.cpp @@ -92,7 +92,7 @@ void RenderCapture::sendRenderCaptures() { QMutexLocker lock(&m_mutex); - for (const RenderCaptureDataPtr data : qAsConst(m_renderCaptureData)) { + for (const RenderCaptureDataPtr &data : qAsConst(m_renderCaptureData)) { auto e = Qt3DCore::QPropertyUpdatedChangePtr::create(peerId()); e->setDeliveryFlags(Qt3DCore::QSceneChange::DeliverToAll); e->setPropertyName("renderCaptureData"); diff --git a/src/render/framegraph/viewportnode.cpp b/src/render/framegraph/viewportnode.cpp index c16a660b0..b68f7b55b 100644 --- a/src/render/framegraph/viewportnode.cpp +++ b/src/render/framegraph/viewportnode.cpp @@ -123,7 +123,7 @@ void ViewportNode::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e) if (e->type() == PropertyUpdated) { QPropertyUpdatedChangePtr propertyChange = qSharedPointerCast<QPropertyUpdatedChange>(e); if (propertyChange->propertyName() == QByteArrayLiteral("normalizedRect")) { - QRectF normalizedRect = propertyChange->value().value<QRectF>(); + QRectF normalizedRect = propertyChange->value().toRectF(); setXMin(normalizedRect.x()); setYMin(normalizedRect.y()); setXMax(normalizedRect.width()); diff --git a/src/render/frontend/qabstractfunctor.h b/src/render/frontend/qabstractfunctor.h index c05c04743..95f6aee51 100644 --- a/src/render/frontend/qabstractfunctor.h +++ b/src/render/frontend/qabstractfunctor.h @@ -80,6 +80,14 @@ public: virtual ~QAbstractFunctor(); virtual qintptr id() const = 0; + // TODO: Remove when moving a copy of this to Qt3DCore + template<class T> + const T *functor_cast(const QAbstractFunctor *other) const + { + if (other->id() == functorTypeId<T>()) + return static_cast<const T *>(other); + return nullptr; + } private: Q_DISABLE_COPY(QAbstractFunctor) }; diff --git a/src/render/frontend/qlevelofdetail.cpp b/src/render/frontend/qlevelofdetail.cpp index df169876e..03df4e6a8 100644 --- a/src/render/frontend/qlevelofdetail.cpp +++ b/src/render/frontend/qlevelofdetail.cpp @@ -77,7 +77,7 @@ QLevelOfDetailPrivate::QLevelOfDetailPrivate() The currentIndex property can then be used, for example, to enable or disable entities, change material, etc. - The LevelOfDetail component is not shareable between multiple Entity's. + The LevelOfDetail component is not shareable between multiple \l [QML]{Entity}{entities}. \code #include <Qt3DCore/QEntity> @@ -185,35 +185,6 @@ QLevelOfDetailPrivate::QLevelOfDetailPrivate() * \sa Qt3DRender::QLevelOfDetail::ThresholdType */ - -/*! - * \enum Qt3DRender::QLevelOfDetail::SizeProxyMode - * - * Specifies what is used as a proxy for the entity when computing distance - * or size. - * - * \value LevelOfDetailBoundingSphere use the bounding sphere specified by the center - * and radius properties. - * \value Children LevelOfDetailBoundingSphere use the bounding sphere of the entity the - * component is attached to. - */ - -/*! - * \qmlproperty enumeration LevelOfDetail::SizeProxyMode - * - * Specifies what is used as a proxy for the entity when computing distance - * or size. - * - * \list - * \li LevelOfDetailBoundingSphere use the bounding sphere specified by the center - * and radius properties. - * \li Children LevelOfDetailBoundingSphere use the bounding sphere of the entity the - * component is attached to. - * \endlist - * \sa Qt3DRender::QLevelOfDetail::SizeProxyMode - */ - - /*! * \qmlproperty Camera LevelOfDetail::camera * @@ -442,7 +413,7 @@ void QLevelOfDetail::setThresholds(const QVector<qreal> &thresholds) Q_D(QLevelOfDetail); if (d->m_thresholds != thresholds) { d->m_thresholds = thresholds; - thresholdsChanged(d->m_thresholds); + emit thresholdsChanged(d->m_thresholds); } } diff --git a/src/render/frontend/qlevelofdetailswitch.cpp b/src/render/frontend/qlevelofdetailswitch.cpp index a6d2b1530..845fdd5a6 100644 --- a/src/render/frontend/qlevelofdetailswitch.cpp +++ b/src/render/frontend/qlevelofdetailswitch.cpp @@ -69,11 +69,17 @@ namespace Qt3DRender { This component is assigned to an entity. When the entity changes distance relative to the camera, the LevelOfDetailSwitch will disable all the child entities except - the one matching index \l LevelOfDetailSwitch::currentIndex. + the one matching index \l currentIndex. \sa LevelOfDetail */ +/*! + \qmlproperty int LevelOfDetailSwitch::currentIndex + + The index of the presently selected child entity. +*/ + /*! \fn Qt3DRender::QLevelOfDetailSwitch::QLevelOfDetailSwitch(Qt3DCore::QNode *parent) Constructs a new QLevelOfDetailSwitch with the specified \a parent. */ diff --git a/src/render/frontend/qrenderaspect.cpp b/src/render/frontend/qrenderaspect.cpp index e962f3700..11c03e7db 100644 --- a/src/render/frontend/qrenderaspect.cpp +++ b/src/render/frontend/qrenderaspect.cpp @@ -267,7 +267,7 @@ void QRenderAspectPrivate::registerBackendTypes() q->registerBackendType<QObjectPicker>(QSharedPointer<Render::NodeFunctor<Render::ObjectPicker, Render::ObjectPickerManager> >::create(m_renderer)); // Plugins - for (QString plugin : m_pluginConfig) + for (const QString &plugin : m_pluginConfig) loadRenderPlugin(plugin); } @@ -608,7 +608,7 @@ void QRenderAspectPrivate::configurePlugin(const QString &plugin) if (!m_pluginConfig.contains(plugin)) { m_pluginConfig.append(plugin); - for (QRenderAspectPrivate *instance : m_instances) + for (QRenderAspectPrivate *instance : qAsConst(m_instances)) instance->loadRenderPlugin(plugin); } } diff --git a/src/render/geometry/attribute.cpp b/src/render/geometry/attribute.cpp index 7e1ea79dd..6c8c0113a 100644 --- a/src/render/geometry/attribute.cpp +++ b/src/render/geometry/attribute.cpp @@ -108,7 +108,7 @@ void Attribute::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e) QByteArray propertyName = propertyChange->propertyName(); if (propertyName == QByteArrayLiteral("name")) { - m_name = propertyChange->value().value<QString>(); + m_name = propertyChange->value().toString(); m_nameId = StringToInt::lookupId(m_name); m_attributeDirty = true; } else if (propertyName == QByteArrayLiteral("vertexBaseType")) { diff --git a/src/render/geometry/buffer.cpp b/src/render/geometry/buffer.cpp index 1133b8e5e..55c86910f 100644 --- a/src/render/geometry/buffer.cpp +++ b/src/render/geometry/buffer.cpp @@ -88,6 +88,9 @@ void Buffer::executeFunctor() { Q_ASSERT(m_functor); m_data = (*m_functor)(); + // Request data to be loaded + forceDataUpload(); + if (m_syncData) { // Send data back to the frontend auto e = Qt3DCore::QPropertyUpdatedChangePtr::create(peerId()); @@ -101,6 +104,8 @@ void Buffer::executeFunctor() //Called from th sendBufferJob void Buffer::updateDataFromGPUToCPU(QByteArray data) { + // Note: when this is called, data is what's currently in GPU memory + // so m_data shouldn't be reuploaded m_data = data; // Send data back to the frontend auto e = Qt3DCore::QPropertyUpdatedChangePtr::create(peerId()); @@ -121,21 +126,37 @@ void Buffer::initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr &chang m_access = data.access; m_bufferDirty = true; + if (!m_data.isEmpty()) + forceDataUpload(); + m_functor = data.functor; Q_ASSERT(m_manager); if (m_functor) m_manager->addDirtyBuffer(peerId()); } +void Buffer::forceDataUpload() +{ + // We push back an update with offset = -1 + // As this is the way to force data to be loaded + QBufferUpdate updateNewData; + updateNewData.offset = -1; + m_bufferUpdates.clear(); //previous updates are pointless + m_bufferUpdates.push_back(updateNewData); +} + void Buffer::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e) { if (e->type() == PropertyUpdated) { QPropertyUpdatedChangePtr propertyChange = qSharedPointerCast<QPropertyUpdatedChange>(e); QByteArray propertyName = propertyChange->propertyName(); if (propertyName == QByteArrayLiteral("data")) { - QByteArray newData = propertyChange->value().value<QByteArray>(); - m_bufferDirty |= m_data != newData; + QByteArray newData = propertyChange->value().toByteArray(); + bool dirty = m_data != newData; + m_bufferDirty |= dirty; m_data = newData; + if (dirty) + forceDataUpload(); } else if (propertyName == QByteArrayLiteral("updateData")) { Qt3DRender::QBufferUpdate updateData = propertyChange->value().value<Qt3DRender::QBufferUpdate>(); m_bufferUpdates.push_back(updateData); diff --git a/src/render/geometry/buffer_p.h b/src/render/geometry/buffer_p.h index 9d9606eb0..691d6cc60 100644 --- a/src/render/geometry/buffer_p.h +++ b/src/render/geometry/buffer_p.h @@ -90,6 +90,7 @@ public: private: void initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr &change) Q_DECL_FINAL; + void forceDataUpload(); QBuffer::BufferType m_type; QBuffer::UsageType m_usage; diff --git a/src/render/geometry/geometryrenderer.cpp b/src/render/geometry/geometryrenderer.cpp index 54fe61831..4f5432e1d 100644 --- a/src/render/geometry/geometryrenderer.cpp +++ b/src/render/geometry/geometryrenderer.cpp @@ -153,7 +153,7 @@ void GeometryRenderer::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e) m_verticesPerPatch = propertyChange->value().value<int>(); m_dirty = true; } else if (propertyName == QByteArrayLiteral("primitiveRestartEnabled")) { - m_primitiveRestartEnabled = propertyChange->value().value<bool>(); + m_primitiveRestartEnabled = propertyChange->value().toBool(); m_dirty = true; } else if (propertyName == QByteArrayLiteral("primitiveType")) { m_primitiveType = static_cast<QGeometryRenderer::PrimitiveType>(propertyChange->value().value<int>()); diff --git a/src/render/graphicshelpers/graphicscontext.cpp b/src/render/graphicshelpers/graphicscontext.cpp index 6fa081552..80e8267da 100644 --- a/src/render/graphicshelpers/graphicscontext.cpp +++ b/src/render/graphicshelpers/graphicscontext.cpp @@ -1320,16 +1320,16 @@ void GraphicsContext::applyUniform(const ShaderUniform &description, const Unifo break; case UniformType::UInt: - applyUniformHelper<UniformType::Int>(description.m_location, description.m_size, v); + applyUniformHelper<UniformType::UInt>(description.m_location, description.m_size, v); break; case UniformType::UIVec2: - applyUniformHelper<UniformType::IVec2>(description.m_location, description.m_size, v); + applyUniformHelper<UniformType::UIVec2>(description.m_location, description.m_size, v); break; case UniformType::UIVec3: - applyUniformHelper<UniformType::IVec3>(description.m_location, description.m_size, v); + applyUniformHelper<UniformType::UIVec3>(description.m_location, description.m_size, v); break; case UniformType::UIVec4: - applyUniformHelper<UniformType::IVec4>(description.m_location, description.m_size, v); + applyUniformHelper<UniformType::UIVec4>(description.m_location, description.m_size, v); break; case UniformType::Bool: @@ -1501,8 +1501,6 @@ HGLBuffer GraphicsContext::createGLBufferFor(Buffer *buffer) if (!bindGLBuffer(b, bufferTypeToGLBufferType(buffer->type()))) qCWarning(Render::Io) << Q_FUNC_INFO << "buffer binding failed"; - // TO DO: Handle usage pattern - b->allocate(this, buffer->data().constData(), buffer->data().size(), false); return m_renderer->nodeManagers()->glBufferManager()->lookupHandle(buffer->peerId()); } @@ -1528,21 +1526,39 @@ void GraphicsContext::uploadDataToGLBuffer(Buffer *buffer, GLBuffer *b, bool rel // * setData was called changing the whole data or functor (or the usage pattern) // * partial buffer updates where received - // Note: we assume the case where both setData/functor and updates are called to be a misuse - // with unexpected behavior - const QVector<Qt3DRender::QBufferUpdate> updates = std::move(buffer->pendingBufferUpdates()); - if (!updates.empty()) { - for (const Qt3DRender::QBufferUpdate &update : updates) { + // TO DO: Handle usage pattern + QVector<Qt3DRender::QBufferUpdate> updates = std::move(buffer->pendingBufferUpdates()); + for (auto it = updates.begin(); it != updates.end(); ++it) { + auto update = it; + // We have a partial update + if (update->offset >= 0) { + //accumulate sequential updates as single one + int bufferSize = update->data.size(); + auto it2 = it + 1; + while ((it2 != updates.end()) + && (it2->offset - update->offset == bufferSize)) { + bufferSize += it2->data.size(); + ++it2; + } + update->data.resize(bufferSize); + while (it + 1 != it2) { + ++it; + update->data.replace(it->offset - update->offset, it->data.size(), it->data); + it->data.clear(); + } // TO DO: based on the number of updates .., it might make sense to // sometime use glMapBuffer rather than glBufferSubData - b->update(this, update.data.constData(), update.data.size(), update.offset); + b->update(this, update->data.constData(), update->data.size(), update->offset); + } else { + // We have an update that was done by calling QBuffer::setData + // which is used to resize or entirely clear the buffer + // Note: we use the buffer data directly in that case + const int bufferSize = buffer->data().size(); + b->allocate(this, bufferSize, false); // orphan the buffer + b->allocate(this, buffer->data().constData(), bufferSize, false); } - } else { - const int bufferSize = buffer->data().size(); - // TO DO: Handle usage pattern - b->allocate(this, bufferSize, false); // orphan the buffer - b->allocate(this, buffer->data().constData(), bufferSize, false); } + if (releaseBuffer) { b->release(this); if (bufferTypeToGLBufferType(buffer->type()) == GLBuffer::ArrayBuffer) diff --git a/src/render/jobs/pickboundingvolumejob.cpp b/src/render/jobs/pickboundingvolumejob.cpp index 7bd45587c..f9c7a390c 100644 --- a/src/render/jobs/pickboundingvolumejob.cpp +++ b/src/render/jobs/pickboundingvolumejob.cpp @@ -371,14 +371,14 @@ void PickBoundingVolumeJob::dispatchPickEvents(const QMouseEvent &event, } break; } - +#if QT_CONFIG(gestures) case Qt::TapGesture: { objectPicker->onClicked(pickEvent); break; } - +#endif case QEvent::MouseMove: { - if (objectPicker->isPressed() && objectPicker->isDragEnabled()) { + if ((objectPicker->isPressed() || objectPicker->isHoverEnabled()) && objectPicker->isDragEnabled()) { objectPicker->onMoved(pickEvent); } // fallthrough diff --git a/src/render/materialsystem/qeffect.cpp b/src/render/materialsystem/qeffect.cpp index 7778b5621..b611657c4 100644 --- a/src/render/materialsystem/qeffect.cpp +++ b/src/render/materialsystem/qeffect.cpp @@ -67,6 +67,35 @@ QEffectPrivate::QEffectPrivate() The QEffect class combines a set of techniques and parameters used by those techniques to produce a rendering effect for a material. + An QEffect instance should be shared among several QMaterial instances when possible. + + \code + QEffect *effect = new QEffect(); + + // Create technique, render pass and shader + QTechnique *gl3Technique = new QTechnique(); + QRenderPass *gl3Pass = new QRenderPass(); + QShaderProgram *glShader = new QShaderProgram(); + + // Set the shader on the render pass + gl3Pass->setShaderProgram(glShader); + + // Add the pass to the technique + gl3Technique->addRenderPass(gl3Pass); + + // Set the targeted GL version for the technique + gl3Technique->graphicsApiFilter()->setApi(QGraphicsApiFilter::OpenGL); + gl3Technique->graphicsApiFilter()->setMajorVersion(3); + gl3Technique->graphicsApiFilter()->setMinorVersion(1); + gl3Technique->graphicsApiFilter()->setProfile(QGraphicsApiFilter::CoreProfile); + + // Add the technique to the effect + effect->addTechnique(gl3Technique); + \endcode + + A QParameter defined on an Effect is overridden by a QParameter (of the same + name) defined in a QMaterial, QTechniqueFilter, QRenderPassFilter. + \sa QMaterial, QTechnique, QParameter */ @@ -81,6 +110,37 @@ QEffectPrivate::QEffectPrivate() The Effect type combines a set of techniques and parameters used by those techniques to produce a rendering effect for a material. + An Effect instance should be shared among several Material instances when possible. + + A Parameter defined on an Effect is overridden by a QParameter (of the same + name) defined in a Material, TechniqueFilter, RenderPassFilter. + + \code + Effect { + id: effect + + technique: [ + Technique { + id: gl3Technique + graphicsApiFilter { + api: GraphicsApiFilter.OpenGL + profile: GraphicsApiFilter.CoreProfile + majorVersion: 3 + minorVersion: 1 + } + renderPasses: [ + RenderPass { + id: gl3Pass + shaderProgram: ShaderProgram { + ... + } + } + ] + } + ] + } + \endcode + \sa Material, Technique, Parameter */ diff --git a/src/render/materialsystem/qmaterial.cpp b/src/render/materialsystem/qmaterial.cpp index ca0f86463..c6913441e 100644 --- a/src/render/materialsystem/qmaterial.cpp +++ b/src/render/materialsystem/qmaterial.cpp @@ -60,7 +60,58 @@ sound should reflect off an element, the temperature of a surface, and so on. - \sa Effect + In itself, a Material doesn't do anything. It's only when it references an + Effect node that a Material becomes useful. + + In practice, it often happens that a single Effect is being referenced by + several Material components. This allows to only create the effect, + techniques, passes and shaders once while allowing to specify the material + by adding Parameter instances. + + A Parameter defined on a Material is overridden by a Parameter (of the same + name) defined in a TechniqueFilter or a RenderPassFilter. + + \code + Effect { + id: effect + + technique: [ + Technique { + id: gl3Technique + graphicsApiFilter { + api: GraphicsApiFilter.OpenGL + profile: GraphicsApiFilter.CoreProfile + majorVersion: 3 + minorVersion: 1 + } + renderPasses: [ + RenderPass { + id: gl3Pass + shaderProgram: ShaderProgram { + ... + } + } + ] + } + ] + } + + Material { + id: material1 + parameters: [ + Parameter { name: "color"; value: "green" } + ] + } + + Material { + id: material2 + parameters: [ + Parameter { name: "color"; value: "white" } + ] + } + \endcode + + \sa Effect, Technique, Parameter */ /*! @@ -77,7 +128,54 @@ sound should reflect off an element, the temperature of a surface, and so on. - \sa QEffect + In itself, a QMaterial doesn't do anything. It's only when it references a + QEffect node that a QMaterial becomes useful. + + In practice, it often happens that a single QEffect is being referenced by + several QMaterial components. This allows to only create the effect, + techniques, passes and shaders once while allowing to specify the material + by adding QParameter instances. + + A QParameter defined on a QMaterial is overridden by a QParameter (of the same + name) defined in a QTechniqueFilter or a QRenderPassFilter. + + \code + QMaterial *material1 = new QMaterial(); + QMaterial *material2 = new QMaterial(); + + // Create effect, technique, render pass and shader + QEffect *effect = new QEffect(); + QTechnique *gl3Technique = new QTechnique(); + QRenderPass *gl3Pass = new QRenderPass(); + QShaderProgram *glShader = new QShaderProgram(); + + // Set the shader on the render pass + gl3Pass->setShaderProgram(glShader); + + // Add the pass to the technique + gl3Technique->addRenderPass(gl3Pass); + + // Set the targeted GL version for the technique + gl3Technique->graphicsApiFilter()->setApi(QGraphicsApiFilter::OpenGL); + gl3Technique->graphicsApiFilter()->setMajorVersion(3); + gl3Technique->graphicsApiFilter()->setMinorVersion(1); + gl3Technique->graphicsApiFilter()->setProfile(QGraphicsApiFilter::CoreProfile); + + // Add the technique to the effect + effect->addTechnique(gl3Technique); + + // Set the effect on the materials + material1->setEffect(effect); + material2->setEffect(effect); + + // Set different parameters on the materials + const QString parameterName = QStringLiteral("color"); + material1->addParameter(new QParameter(parameterName, QColor::fromRgbF(0.0f, 1.0f, 0.0f, 1.0f); + material2->addParameter(new QParameter(parameterName, QColor::fromRgbF(1.0f, 1.0f, 1.0f, 1.0f); + + \endcode + + \sa QEffect, QTechnique, QParameter */ QT_BEGIN_NAMESPACE diff --git a/src/render/materialsystem/qparameter.cpp b/src/render/materialsystem/qparameter.cpp index 8f83fd02e..2ca7d176b 100644 --- a/src/render/materialsystem/qparameter.cpp +++ b/src/render/materialsystem/qparameter.cpp @@ -45,18 +45,127 @@ /*! - * \qmltype Parameter - * \instantiates Qt3DRender::QParameter - * \inqmlmodule Qt3D.Render - * \brief Provides storage for a name and value pair. + \qmltype Parameter + \instantiates Qt3DRender::QParameter + \inqmlmodule Qt3D.Render + \brief Provides storage for a name and value pair. This maps to a shader uniform. + + A Parameter can be referenced by a RenderPass, Technique, Effect, Material, + TechniqueFilter, RenderPassFilter. At runtime, depending on which shader is + selected for a given step of the rendering, the value contained in a + Parameter will be converted and uploaded if the shader contains a uniform + with a name matching that of the Parameter. + + \code + Parameter { + name: "diffuseColor" + value: "blue" + } + + // Works with the following GLSL uniform shader declarations + // uniform vec4 diffuseColor; + // uniform vec3 diffuseColor; + // uniform vec2 diffuseColor; + // uniform float diffuseColor; + \endcode + + \note some care must be taken to ensure the value wrapped by a Parameter + can actually be converted to what the real uniform expect. Giving a value + stored as an int where the actual shader uniform is of type float could + result in undefined behaviors. + + \note when the targeted uniform is an array, the name should be the name + of the uniform with [0] appended to it. + + \code + Parameter { + name: "diffuseValues[0]" + value: [0.0, 1.0. 2.0, 3.0, 4.0, 883.0, 1340.0, 1584.0] + } + + // Matching GLSL shader uniform declaration + // uniform float diffuseValues[8]; + \endcode + + When it comes to texture support, the Parameter value should be set to the + appropriate Texture subclass that matches the sampler type of the shader + uniform. + + \code + Parameter { + name: "diffuseTexture" + value: Texture2D { ... } + } + + // Works with the following GLSL uniform shader declaration + // uniform sampler2D diffuseTexture + \endcode + + \sa Texture */ /*! - * \class Qt3DRender::QParameter - * \inheaderfile Qt3DRender/QParameter - * \inmodule Qt3DRender - * - * \brief Provides storage for a name and value pair. + \class Qt3DRender::QParameter + \inheaderfile Qt3DRender/QParameter + \inmodule Qt3DRender + \brief Provides storage for a name and value pair. This maps to a shader uniform. + + A QParameter can be referenced by a QRenderPass, QTechnique, QEffect, QMaterial, + QTechniqueFilter, QRenderPassFilter. At runtime, depending on which shader is + selected for a given step of the rendering, the value contained in a + QParameter will be converted and uploaded if the shader contains a uniform + with a name matching that of the QParameter. + + \code + QParameter *param = new QParameter(); + param->setName(QStringLiteral("diffuseColor")); + param->setValue(QColor::fromRgbF(0.0f, 0.0f, 1.0f, 1.0f)); + + // Alternatively you can create and set a QParameter this way + QParameter *param2 = new QParameter(QStringLiteral("diffuseColor"), QColor::fromRgbF(0.0f, 0.0f, 1.0f, 1.0f)); + + // Such QParameters will work with the following GLSL uniform shader declarations + // uniform vec4 diffuseColor; + // uniform vec3 diffuseColor; + // uniform vec2 diffuseColor; + // uniform float diffuseColor; + \endcode + + \note some care must be taken to ensure the value wrapped by a QParameter + can actually be converted to what the real uniform expect. Giving a value + stored as an int where the actual shader uniform is of type float could + result in undefined behaviors. + + \note when the targeted uniform is an array, the name should be the name + of the uniform with [0] appended to it. + + \code + QParameter *param = new QParameter(); + QVariantList values = QVariantList() << 0.0f << 1.0f << 2.0f << 3.0f << 4.0f << 883.0f << 1340.0f << 1584.0f; + + param->setName(QStringLiteral("diffuseValues[0]")); + param->setValue(values); + + // Matching GLSL shader uniform declaration + // uniform float diffuseValues[8]; + \endcode + + When it comes to texture support, the QParameter value should be set to the + appropriate QAbstractTexture subclass that matches the sampler type of the shader + uniform. + + \code + QTexture2D *texture = new QTexture2D(); + ... + QParameter *param = new QParameter(); + param->setName(QStringLiteral("diffuseTexture")); + param->setValue(QVariant::fromValue(texture)); + + // Works with the following GLSL uniform shader declaration + // uniform sampler2D diffuseTexture + \endcode + + \sa QAbstractTexture */ QT_BEGIN_NAMESPACE diff --git a/src/render/materialsystem/qrenderpass.cpp b/src/render/materialsystem/qrenderpass.cpp index 6f5949781..61f844be3 100644 --- a/src/render/materialsystem/qrenderpass.cpp +++ b/src/render/materialsystem/qrenderpass.cpp @@ -72,9 +72,40 @@ QRenderPassPrivate::QRenderPassPrivate() a list of FilterKey objects, a list of RenderState objects and a list of \l Parameter objects. - RenderPass executes the ShaderProgram using the given render states and parameters - when its filter keys match the filter keys in RenderPassFilter or when no filter - keys are specified and no RenderPassFilter is present in the FrameGraph. + RenderPass executes the ShaderProgram using the given RenderState and + Parameter nodes when at least one of FilterKey nodes being referenced + matches any of the FilterKey nodes in RenderPassFilter or when no FilterKey + nodes are specified and no RenderPassFilter is present in the FrameGraph. + + If the RenderPass defines a Parameter, it will be overridden by a Parameter + with the same name if it exists in any of the Technique, Effect, Material, + TechniqueFilter, RenderPassFilter associated with the pass at runtime. This + still can be useful to define sane default values. + + At render time, for each leaf node of the FrameGraph a base render state is + recorded by accumulating states defined by all RenderStateSet nodes in the + FrameGraph branch. Each RenderPass can overload this base render state by + specifying its own RenderState nodes. + + \code + RenderPass { + id: pass + shaderProgram: ShaderProgram { + ... + } + parameters: [ + Parameters { name: "color"; value: "red" } + ] + filterKeys: [ + FilterKey { name: "name"; value: "zFillPass" } + ] + renderStates: [ + DepthTest { } + ] + } + \endcode + + \sa RenderPassFilter, FilterKey, Parameter, RenderState, Effect, Technique */ /*! @@ -88,10 +119,54 @@ QRenderPassPrivate::QRenderPassPrivate() of a Qt3DRender::QShaderProgram and a list of Qt3DRender::QFilterKey objects, a list of Qt3DRender::QRenderState objects and a list of Qt3DRender::QParameter objects. - QRenderPass executes the QShaderProgram using the given render states and parameters - when its filter keys match the filter keys in Qt3DRender::QRenderPassFilter or - when no filter keys are specified and no QRenderPassFilter is present - in the FrameGraph. + QRenderPass executes the QShaderProgram using the given QRenderState and + QParameter nodes when at least one of QFilterKey nodes being referenced + matches any of the QFilterKey nodes in QRenderPassFilter or when no + QFilterKey nodes are specified and no QRenderPassFilter is present in the + FrameGraph. + + If the QRenderPass defines a QParameter, it will be overridden by a + QParameter with the same name if it exists in any of the QTechnique, + QEffect, QMaterial, QTechniqueFilter, QRenderPassFilter associated with the + pass at runtime. This still can be useful to define sane default values. + + At render time, for each leaf node of the FrameGraph a base render state is + recorded by accumulating states defined by all QRenderStateSet nodes in the + FrameGraph branch. Each QRenderPass can overload this base render state by + specifying its own QRenderState nodes. + + \code + // Create the render passes + QRenderPass *pass = new QRenderPass(); + + // Create shader program + QShaderProgram *glShader = new QShaderProgram(); + + // Set the shader on the render pass + pass->setShaderProgram(glShader); + + // Create a FilterKey + QFilterKey *filterKey = new QFilterKey(); + filterKey->setName(QStringLiteral("name")); + fitlerKey->setValue(QStringLiteral("zFillPass")); + + // Add the FilterKey to the pass + pass->addFilterKey(filterKey); + + // Create a QParameter + QParameter *colorParameter = new QParameter(QStringLiteral("color"), QColor::fromRgbF(0.0f, 0.0f, 1.0f, 1.0f)); + + // Add parameter to pass + pass->addParameter(colorParameter); + + // Create a QRenderState + QDepthTest *depthTest = new QDepthTest(); + + // Add the render state to the pass + pass->addRenderState(depthTest); + \endcode + + \sa QRenderPassFilter, QFilterKey, QParameter, QRenderState, QEffect, QTechnique */ /*! \typedef ParameterList diff --git a/src/render/materialsystem/qtechnique.cpp b/src/render/materialsystem/qtechnique.cpp index 74505bfbd..df142cdee 100644 --- a/src/render/materialsystem/qtechnique.cpp +++ b/src/render/materialsystem/qtechnique.cpp @@ -68,14 +68,61 @@ QTechniquePrivate::~QTechniquePrivate() \since 5.7 \brief Encapsulates a Technique. - A Technique specifies a set of RenderPass objects, FilterKey objects, Parameter objects - and a GraphicsApiFilter, which together define a rendering technique the given - graphics API can render. The filter keys are used by TechniqueFilter - to select specific techinques at specific parts of the FrameGraph. - If the same parameter is specified both in Technique and RenderPass, the one - in Technique overrides the one used in the RenderPass. - - \sa Qt3D.Render::Effect + A Technique specifies a set of RenderPass objects, FilterKey objects, + Parameter objects and a GraphicsApiFilter, which together define a + rendering technique the given graphics API can render. The filter keys are + used by TechniqueFilter to select specific techniques at specific parts of + the FrameGraph. If two Parameter instances with the same name are specified + in a Technique and a RenderPass, the one in Technique overrides the one + used in the RenderPass. + + When creating an Effect that targets several versions of a graphics API, it + is useful to create several Technique nodes each with a graphicsApiFilter + set to match one of the targeted versions. At runtime, the Qt3D renderer + will select the most appropriate Technique based on which graphics API + versions are supported and (if specified) the FilterKey nodes that satisfy + a given TechniqueFilter in the FrameGraph. + + \note When using OpenGL as the graphics API for rendering, Qt3D relies on + the QSurfaceFormat returned by QSurfaceFormat::defaultFormat() at runtime + to decide what is the most appropriate GL version available. If you need to + customize the QSurfaceFormat, do not forget to apply it with + QSurfaceFormat::setDefaultFormat(). Setting the QSurfaceFormat on the view + will likely have no effect on Qt3D related rendering. + + \code + Technique { + id: gl3Technique + parameters: [ + Parameter { name: "color"; value: "orange" } + ] + filterKeys: [ + FilterKey { name: "name"; value: "zFillTechnique" } + ] + graphicsApiFilter { + api: GraphicsApiFilter.OpenGL + profile: GraphicsApiFilter.CoreProfile + majorVersion: 3 + minorVersion: 1 + } + renderPasses: [ + RenderPass { + id: firstPass + shaderProgram: ShaderProgram { + ... + } + }, + RenderPass { + id: secondPass + shaderProgram: ShaderProgram { + ... + } + } + ] + } + \endcode + + \sa Effect, RenderPass, TechniqueFilter */ /*! @@ -85,15 +132,62 @@ QTechniquePrivate::~QTechniquePrivate() \since 5.7 \brief Encapsulates a Technique. - A Qt3DRender::QTechnique specifies a set of Qt3DRender::QRenderPass objects, - Qt3DRender::QFilterKey objects, Qt3DRender::QParameter objects and - a Qt3DRender::QGraphicsApiFilter, which together define a rendering technique the given - graphics API can render. The filter keys are used by Qt3DRender::QTechniqueFilter - to select specific techinques at specific parts of the FrameGraph. - If the same parameter is specified both in QTechnique and QRenderPass, the one - in QTechnique overrides the one used in the QRenderPass. - - \sa Qt3DRender::QEffect + A Qt3DRender::QTechnique specifies a set of Qt3DRender::QRenderPass + objects, Qt3DRender::QFilterKey objects, Qt3DRender::QParameter objects and + a Qt3DRender::QGraphicsApiFilter, which together define a rendering + technique the given graphics API can render. The filter keys are used by + Qt3DRender::QTechniqueFilter to select specific techniques at specific + parts of the FrameGraph. If two QParameter instances with the same name are + specified in a QTechnique and a QRenderPass, the one in Technique overrides + the one used in the QRenderPass. + + When creating an QEffect that targets several versions of a graphics API, + it is useful to create several QTechnique nodes each with a + graphicsApiFilter set to match one of the targeted GL versions. At runtime, + the Qt3D renderer will select the most appropriate QTechnique based on + which graphics API versions are supported and (if specified) the QFilterKey + nodes that satisfy a given QTechniqueFilter in the FrameGraph. + + \note When using OpenGL as the graphics API for rendering, Qt3D relies on + the QSurfaceFormat returned by QSurfaceFormat::defaultFormat() at runtime + to decide what is the most appropriate GL version available. If you need to + customize the QSurfaceFormat, do not forget to apply it with + QSurfaceFormat::setDefaultFormat(). Setting the QSurfaceFormat on the view + will likely have no effect on Qt3D related rendering. + + \code + QTechnique *gl3Technique = new QTechnique(); + + // Create the render passes + QRenderPass *firstPass = new QRenderPass(); + QRenderPass *secondPass = new QRenderPass(); + + // Add the passes to the technique + gl3Technique->addRenderPass(firstPass); + gl3Technique->addRenderPass(secondPass); + + // Set the targeted GL version for the technique + gl3Technique->graphicsApiFilter()->setApi(QGraphicsApiFilter::OpenGL); + gl3Technique->graphicsApiFilter()->setMajorVersion(3); + gl3Technique->graphicsApiFilter()->setMinorVersion(1); + gl3Technique->graphicsApiFilter()->setProfile(QGraphicsApiFilter::CoreProfile); + + // Create a FilterKey + QFilterKey *filterKey = new QFilterKey(); + filterKey->setName(QStringLiteral("name")); + fitlerKey->setValue(QStringLiteral("zFillPass")); + + // Add the FilterKey to the Technique + gl3Technique->addFilterKey(filterKey); + + // Create a QParameter + QParameter *colorParameter = new QParameter(QStringLiteral("color"), QColor::fromRgbF(0.0f, 0.0f, 1.0f, 1.0f)); + + // Add parameter to technique + gl3Technique->addParameter(colorParameter); + \endcode + + \sa QEffect, QRenderPass, QTechniqueFilter */ /*! diff --git a/src/render/materialsystem/shader.cpp b/src/render/materialsystem/shader.cpp index 3ee00739d..915ca1d54 100644 --- a/src/render/materialsystem/shader.cpp +++ b/src/render/materialsystem/shader.cpp @@ -311,8 +311,8 @@ void Shader::updateDNA() QMutexLocker locker(&m_mutex); uint attachmentHash = 0; - QHash<QString, int>::const_iterator it = m_fragOutputs.begin(); - QHash<QString, int>::const_iterator end = m_fragOutputs.end(); + QHash<QString, int>::const_iterator it = m_fragOutputs.cbegin(); + QHash<QString, int>::const_iterator end = m_fragOutputs.cend(); while (it != end) { attachmentHash += ::qHash(it.value()) + ::qHash(it.key()); ++it; @@ -373,11 +373,11 @@ void Shader::initializeUniformBlocks(const QVector<ShaderUniformBlock> &uniformB qCDebug(Shaders) << "Initializing Uniform Block {" << m_uniformBlockNames[i] << "}"; // Find all active uniforms for the shader block - QVector<ShaderUniform>::const_iterator uniformsIt = m_uniforms.begin(); - const QVector<ShaderUniform>::const_iterator uniformsEnd = m_uniforms.end(); + QVector<ShaderUniform>::const_iterator uniformsIt = m_uniforms.cbegin(); + const QVector<ShaderUniform>::const_iterator uniformsEnd = m_uniforms.cend(); - QVector<QString>::const_iterator uniformNamesIt = m_uniformsNames.begin(); - const QVector<QString>::const_iterator uniformNamesEnd = m_attributesNames.end(); + QVector<QString>::const_iterator uniformNamesIt = m_uniformsNames.cbegin(); + const QVector<QString>::const_iterator uniformNamesEnd = m_attributesNames.cend(); QHash<QString, ShaderUniform> activeUniformsInBlock; diff --git a/src/render/raycasting/qabstractcollisionqueryservice_p.h b/src/render/raycasting/qabstractcollisionqueryservice_p.h index 806c33d5b..1c1261937 100644 --- a/src/render/raycasting/qabstractcollisionqueryservice_p.h +++ b/src/render/raycasting/qabstractcollisionqueryservice_p.h @@ -79,6 +79,7 @@ public: class QT3DRENDERSHARED_EXPORT QAbstractCollisionQueryService : public Qt3DCore::QAbstractServiceProvider { + Q_OBJECT public: enum QueryMode { FirstHit, diff --git a/src/render/texture/apitexturemanager_p.h b/src/render/texture/apitexturemanager_p.h index c062f0971..91747b3bc 100644 --- a/src/render/texture/apitexturemanager_p.h +++ b/src/render/texture/apitexturemanager_p.h @@ -273,7 +273,7 @@ public: if (impl->isUnique()) return false; - auto it = m_sharedTextures.find(impl); + auto it = m_sharedTextures.constFind(impl); if (it == m_sharedTextures.cend()) return false; diff --git a/src/render/texture/gltexture.cpp b/src/render/texture/gltexture.cpp index bc2a1d464..854789e94 100644 --- a/src/render/texture/gltexture.cpp +++ b/src/render/texture/gltexture.cpp @@ -188,13 +188,11 @@ QOpenGLTexture* GLTexture::getOrCreateGLTexture() if (!m_gl) { m_gl = buildGLTexture(); - if (m_gl) { - m_gl->allocateStorage(); - if (!m_gl->isStorageAllocated()) { - qWarning() << Q_FUNC_INFO << "texture storage allocation failed"; - return nullptr; - } - } else { + if (!m_gl) + return nullptr; + m_gl->allocateStorage(); + if (!m_gl->isStorageAllocated()) { + qWarning() << Q_FUNC_INFO << "texture storage allocation failed"; return nullptr; } } diff --git a/src/render/texture/texture.cpp b/src/render/texture/texture.cpp index a1f0d0f98..21f29d0b6 100644 --- a/src/render/texture/texture.cpp +++ b/src/render/texture/texture.cpp @@ -254,6 +254,16 @@ void Texture::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e) BackendNode::sceneChangeEvent(e); } +bool Texture::isValid() const +{ + for (const auto handle : m_textureImages) { + TextureImage *img = m_textureImageManager->data(handle); + if (img == nullptr) + return false; + } + return true; +} + void Texture::initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr &change) { const auto typedChange = qSharedPointerCast<Qt3DCore::QNodeCreatedChange<QAbstractTextureData>>(change); diff --git a/src/render/texture/texture_p.h b/src/render/texture/texture_p.h index 18f650686..1f3ba729c 100644 --- a/src/render/texture/texture_p.h +++ b/src/render/texture/texture_p.h @@ -157,6 +157,7 @@ public: inline const QVector<HTextureImage>& textureImages() const { return m_textureImages; } inline const QTextureGeneratorPtr& dataGenerator() const { return m_dataFunctor; } + bool isValid() const; private: void initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr &change) Q_DECL_FINAL; |