diff options
Diffstat (limited to 'src/render/renderers/opengl/renderer')
11 files changed, 1098 insertions, 894 deletions
diff --git a/src/render/renderers/opengl/renderer/rendercommand.cpp b/src/render/renderers/opengl/renderer/rendercommand.cpp index e60b17668..072127391 100644 --- a/src/render/renderers/opengl/renderer/rendercommand.cpp +++ b/src/render/renderers/opengl/renderer/rendercommand.cpp @@ -48,6 +48,7 @@ RenderCommand::RenderCommand() : m_stateSet(nullptr) , m_depth(0.0f) , m_changeCost(0) + , m_shaderDna(0) , m_type(RenderCommand::Draw) , m_primitiveCount(0) , m_primitiveType(QGeometryRenderer::Triangles) @@ -70,6 +71,20 @@ RenderCommand::RenderCommand() m_workGroups[2] = 0; } +bool operator==(const RenderCommand &a, const RenderCommand &b) noexcept +{ + return (a.m_vao == b.m_vao && a.m_shader == b.m_shader && a.m_material == b.m_material && + a.m_stateSet == b.m_stateSet && a.m_geometry == b.m_geometry && a.m_geometryRenderer == b.m_geometryRenderer && + a.m_indirectDrawBuffer == b.m_indirectDrawBuffer && a.m_activeAttributes == b.m_activeAttributes && + a.m_depth == b.m_depth && a.m_changeCost == b.m_changeCost && a.m_shaderDna == b.m_shaderDna && + a.m_workGroups[0] == b.m_workGroups[0] && a.m_workGroups[1] == b.m_workGroups[1] && a.m_workGroups[2] == b.m_workGroups[2] && + a.m_primitiveCount == b.m_primitiveCount && a.m_primitiveType == b.m_primitiveType && a.m_restartIndexValue == b.m_restartIndexValue && + a.m_firstInstance == b.m_firstInstance && a.m_firstVertex == b.m_firstVertex && a.m_verticesPerPatch == b.m_verticesPerPatch && + a.m_instanceCount == b.m_instanceCount && a.m_indexOffset == b.m_indexOffset && a.m_indexAttributeByteOffset == b.m_indexAttributeByteOffset && + a.m_drawIndexed == b.m_drawIndexed && a.m_drawIndirect == b.m_drawIndirect && a.m_primitiveRestartEnabled == b.m_primitiveRestartEnabled && + a.m_isValid == b.m_isValid && a.m_computeCommand == b.m_computeCommand); +} + } // namespace Render } // namespace Qt3DRender diff --git a/src/render/renderers/opengl/renderer/rendercommand_p.h b/src/render/renderers/opengl/renderer/rendercommand_p.h index 61cc6d17d..be00fb753 100644 --- a/src/render/renderers/opengl/renderer/rendercommand_p.h +++ b/src/render/renderers/opengl/renderer/rendercommand_p.h @@ -55,6 +55,7 @@ #include <qglobal.h> #include <Qt3DRender/private/shaderparameterpack_p.h> #include <Qt3DRender/private/handle_types_p.h> +#include <Qt3DRender/private/renderviewjobutils_p.h> #include <Qt3DRender/qgeometryrenderer.h> #include <QOpenGLShaderProgram> #include <QOpenGLTexture> @@ -69,6 +70,7 @@ namespace Qt3DRender { namespace Render { class RenderStateSet; +using RenderStateSetPtr = QSharedPointer<RenderStateSet>; class Q_AUTOTEST_EXPORT RenderCommand { @@ -80,12 +82,13 @@ public: HMaterial m_material; // Purely used to ease sorting (minimize stage changes, binding changes ....) ShaderParameterPack m_parameterPack; // Might need to be reworked so as to be able to destroy the // Texture while submission is happening. - RenderStateSet *m_stateSet; + RenderStateSetPtr m_stateSet; HGeometry m_geometry; HGeometryRenderer m_geometryRenderer; HBuffer m_indirectDrawBuffer; // Reference to indirect draw buffer (valid only m_drawIndirect == true) + HComputeCommand m_computeCommand; // A QAttribute pack might be interesting // This is a temporary fix in the meantime, to remove the hacked methods in Technique @@ -121,6 +124,53 @@ public: bool m_isValid; }; +Q_AUTOTEST_EXPORT bool operator==(const RenderCommand &a, const RenderCommand &b) noexcept; + +inline bool operator!=(const RenderCommand &lhs, const RenderCommand &rhs) noexcept +{ return !operator==(lhs, rhs); } + +struct EntityRenderCommandData +{ + QVector<Entity *> entities; + QVector<RenderCommand> commands; + QVector<RenderPassParameterData> passesData; + + void reserve(int size) + { + entities.reserve(size); + commands.reserve(size); + passesData.reserve(size); + } + + inline int size() const { return entities.size(); } + + inline void push_back(Entity *e, const RenderCommand &c, const RenderPassParameterData &p) + { + entities.push_back(e); + commands.push_back(c); + passesData.push_back(p); + } + + inline void push_back(Entity *e, RenderCommand &&c, RenderPassParameterData &&p) + { + entities.push_back(e); + commands.push_back(std::move(c)); + passesData.push_back(std::move(p)); + } + + EntityRenderCommandData &operator+=(EntityRenderCommandData &&t) + { + entities += std::move(t.entities); + commands += std::move(t.commands); + passesData += std::move(t.passesData); + return *this; + } + +}; + +using EntityRenderCommandDataPtr = QSharedPointer<EntityRenderCommandData>; + + } // namespace Render } // namespace Qt3DRender diff --git a/src/render/renderers/opengl/renderer/renderer.cpp b/src/render/renderers/opengl/renderer/renderer.cpp index 121a6aa8f..ddf57f4fe 100644 --- a/src/render/renderers/opengl/renderer/renderer.cpp +++ b/src/render/renderers/opengl/renderer/renderer.cpp @@ -93,15 +93,15 @@ #include <Qt3DRender/private/subtreeenabler_p.h> #include <Qt3DRender/private/qshaderprogrambuilder_p.h> #include <Qt3DRender/private/qshaderprogram_p.h> +#include <Qt3DRender/private/filterentitybycomponentjob_p.h> +#include <Qt3DRender/private/commandexecuter_p.h> #include <Qt3DRender/qcameralens.h> #include <Qt3DCore/private/qeventfilterservice_p.h> #include <Qt3DCore/private/qabstractaspectjobmanager_p.h> #include <Qt3DCore/private/qaspectmanager_p.h> - -#if QT_CONFIG(qt3d_profile_jobs) -#include <Qt3DCore/private/aspectcommanddebugger_p.h> -#endif +#include <Qt3DCore/private/qsysteminformationservice_p.h> +#include <Qt3DCore/private/qsysteminformationservice_p_p.h> #include <QStack> #include <QOffscreenSurface> @@ -114,19 +114,9 @@ #include <QUrl> #include <QOffscreenSurface> #include <QWindow> - -#include <QtGui/private/qopenglcontext_p.h> - -// For Debug purposes only #include <QThread> - -#if QT_CONFIG(qt3d_profile_jobs) -#include <Qt3DCore/private/qthreadpooler_p.h> -#include <Qt3DRender/private/job_common_p.h> -#include <Qt3DRender/private/commandexecuter_p.h> -#endif - +#include <QtGui/private/qopenglcontext_p.h> #include <Qt3DRender/private/frameprofiler_p.h> QT_BEGIN_NAMESPACE @@ -138,6 +128,82 @@ using namespace Qt3DCore; namespace Qt3DRender { namespace Render { + +namespace { + +class SyncLightsGatherer +{ +public: + explicit SyncLightsGatherer(LightGathererPtr gatherJob, + RendererCache *cache) + : m_gatherJob(gatherJob) + , m_cache(cache) + { + } + + void operator()() + { + QMutexLocker lock(m_cache->mutex()); + m_cache->gatheredLights = m_gatherJob->lights(); + m_cache->environmentLight = m_gatherJob->takeEnvironmentLight(); + } + +private: + LightGathererPtr m_gatherJob; + RendererCache *m_cache; +}; + +class SyncRenderableEntities +{ +public: + explicit SyncRenderableEntities(RenderableEntityFilterPtr gatherJob, + RendererCache *cache) + : m_gatherJob(gatherJob) + , m_cache(cache) + { + } + + void operator()() + { + QVector<Entity *> selectedEntities = m_gatherJob->filteredEntities(); + std::sort(selectedEntities.begin(), selectedEntities.end()); + + QMutexLocker lock(m_cache->mutex()); + m_cache->renderableEntities = selectedEntities; + } + +private: + RenderableEntityFilterPtr m_gatherJob; + RendererCache *m_cache; +}; + +class SyncComputableEntities +{ +public: + explicit SyncComputableEntities(ComputableEntityFilterPtr gatherJob, + RendererCache *cache) + : m_gatherJob(gatherJob) + , m_cache(cache) + { + } + + void operator()() + { + QVector<Entity *> selectedEntities = m_gatherJob->filteredEntities(); + std::sort(selectedEntities.begin(), selectedEntities.end()); + + QMutexLocker lock(m_cache->mutex()); + m_cache->computeEntities = selectedEntities; + } + +private: + ComputableEntityFilterPtr m_gatherJob; + RendererCache *m_cache; +}; + +} // anonymous + + /*! \internal @@ -186,31 +252,33 @@ Renderer::Renderer(QRenderAspect::RenderType type) , m_calculateBoundingVolumeJob(Render::CalculateBoundingVolumeJobPtr::create()) , m_updateWorldBoundingVolumeJob(Render::UpdateWorldBoundingVolumeJobPtr::create()) , m_updateTreeEnabledJob(Render::UpdateTreeEnabledJobPtr::create()) - , m_sendRenderCaptureJob(Render::SendRenderCaptureJobPtr::create()) , m_sendBufferCaptureJob(Render::SendBufferCaptureJobPtr::create()) , m_updateSkinningPaletteJob(Render::UpdateSkinningPaletteJobPtr::create()) , m_updateLevelOfDetailJob(Render::UpdateLevelOfDetailJobPtr::create()) , m_updateMeshTriangleListJob(Render::UpdateMeshTriangleListJobPtr::create()) , m_filterCompatibleTechniqueJob(Render::FilterCompatibleTechniqueJobPtr::create()) , m_updateEntityLayersJob(Render::UpdateEntityLayersJobPtr::create()) + , m_lightGathererJob(Render::LightGathererPtr::create()) + , m_renderableEntityFilterJob(Render::RenderableEntityFilterPtr::create()) + , m_computableEntityFilterJob(Render::ComputableEntityFilterPtr::create()) , m_bufferGathererJob(SynchronizerJobPtr::create([this] { lookForDirtyBuffers(); }, JobTypes::DirtyBufferGathering)) , m_vaoGathererJob(SynchronizerJobPtr::create([this] { lookForAbandonedVaos(); }, JobTypes::DirtyVaoGathering)) , m_textureGathererJob(SynchronizerJobPtr::create([this] { lookForDirtyTextures(); }, JobTypes::DirtyTextureGathering)) - , m_sendTextureChangesToFrontendJob(SynchronizerPostFramePtr::create([] {}, - [this] (Qt3DCore::QAspectManager *m) { sendTextureChangesToFrontend(m); }, - JobTypes::SendTextureChangesToFrontend)) , m_sendSetFenceHandlesToFrontendJob(SynchronizerJobPtr::create([this] { sendSetFenceHandlesToFrontend(); }, JobTypes::SendSetFenceHandlesToFrontend)) - , m_sendDisablesToFrontendJob(SynchronizerJobPtr::create([this] { sendDisablesToFrontend(); }, JobTypes::SendDisablesToFrontend)) , m_introspectShaderJob(SynchronizerPostFramePtr::create([this] { reloadDirtyShaders(); }, [this] (Qt3DCore::QAspectManager *m) { sendShaderChangesToFrontend(m); }, JobTypes::DirtyShaderGathering)) , m_syncLoadingJobs(SynchronizerJobPtr::create([] {}, JobTypes::SyncLoadingJobs)) + , m_cacheRenderableEntitiesJob(SynchronizerJobPtr::create(SyncRenderableEntities(m_renderableEntityFilterJob, &m_cache), + JobTypes::EntityComponentTypeFiltering)) + , m_cacheComputableEntitiesJob(SynchronizerJobPtr::create(SyncComputableEntities(m_computableEntityFilterJob, &m_cache), + JobTypes::EntityComponentTypeFiltering)) + , m_cacheLightsJob(SynchronizerJobPtr::create(SyncLightsGatherer(m_lightGathererJob, &m_cache), + JobTypes::EntityComponentTypeFiltering)) , m_ownedContext(false) , m_offscreenHelper(nullptr) - , m_shouldSwapBuffers(true) - #if QT_CONFIG(qt3d_profile_jobs) , m_commandExecuter(new Qt3DRender::Debug::CommandExecuter(this)) - #endif + , m_shouldSwapBuffers(true) { // Set renderer as running - it will wait in the context of the // RenderThread for RenderViews to be submitted @@ -238,6 +306,10 @@ Renderer::Renderer(QRenderAspect::RenderType type) m_introspectShaderJob->addDependency(m_filterCompatibleTechniqueJob); + m_cacheLightsJob->addDependency(m_lightGathererJob); + m_cacheRenderableEntitiesJob->addDependency(m_renderableEntityFilterJob); + m_cacheComputableEntitiesJob->addDependency(m_computableEntityFilterJob); + m_filterCompatibleTechniqueJob->setRenderer(this); m_defaultRenderStateSet = new RenderStateSet; @@ -299,13 +371,16 @@ void Renderer::setNodeManagers(NodeManagers *managers) m_pickBoundingVolumeJob->setManagers(m_nodesManager); m_rayCastingJob->setManagers(m_nodesManager); m_updateWorldBoundingVolumeJob->setManager(m_nodesManager->renderNodesManager()); - m_sendRenderCaptureJob->setManagers(m_nodesManager); m_updateLevelOfDetailJob->setManagers(m_nodesManager); m_updateSkinningPaletteJob->setManagers(m_nodesManager); m_updateMeshTriangleListJob->setManagers(m_nodesManager); m_filterCompatibleTechniqueJob->setManager(m_nodesManager->techniqueManager()); m_updateEntityLayersJob->setManager(m_nodesManager); m_updateTreeEnabledJob->setManagers(m_nodesManager); + m_sendBufferCaptureJob->setManagers(m_nodesManager); + m_lightGathererJob->setManager(m_nodesManager->renderNodesManager()); + m_renderableEntityFilterJob->setManager(m_nodesManager->renderNodesManager()); + m_computableEntityFilterJob->setManager(m_nodesManager->renderNodesManager()); } void Renderer::setServices(QServiceLocator *services) @@ -338,7 +413,7 @@ QOpenGLContext *Renderer::shareContext() const // Executed in the reloadDirtyShader job void Renderer::loadShader(Shader *shader, HShader shaderHandle) { - Q_UNUSED(shader); + Q_UNUSED(shader) m_dirtyShaders.push_back(shaderHandle); } @@ -432,6 +507,9 @@ void Renderer::initialize() m_waitForInitializationToBeCompleted.release(1); // Allow the aspect manager to proceed m_vsyncFrameAdvanceService->proceedToNextFrame(); + + // Force initial refresh + markDirty(AllDirty, nullptr); } /*! @@ -653,19 +731,12 @@ void Renderer::doRender(bool swapBuffers) // RenderQueue is complete (but that means it may be of size 0) if (canSubmit && (queueIsComplete && !queueIsEmpty)) { const QVector<Render::RenderView *> renderViews = m_renderQueue->nextFrameQueue(); - -#if QT_CONFIG(qt3d_profile_jobs) - // Save start of frame - JobRunStats submissionStatsPart1; - JobRunStats submissionStatsPart2; - submissionStatsPart1.jobId.typeAndInstance[0] = JobTypes::FrameSubmissionPart1; - submissionStatsPart1.jobId.typeAndInstance[1] = 0; - submissionStatsPart1.threadId = reinterpret_cast<quint64>(QThread::currentThreadId()); - submissionStatsPart1.startTime = QThreadPooler::m_jobsStatTimer.nsecsElapsed(); - submissionStatsPart2.jobId.typeAndInstance[0] = JobTypes::FrameSubmissionPart2; - submissionStatsPart2.jobId.typeAndInstance[1] = 0; - submissionStatsPart2.threadId = reinterpret_cast<quint64>(QThread::currentThreadId()); -#endif + QTaskLogger submissionStatsPart1(m_services->systemInformation(), + {JobTypes::FrameSubmissionPart1, 0}, + QTaskLogger::Submission); + QTaskLogger submissionStatsPart2(m_services->systemInformation(), + {JobTypes::FrameSubmissionPart2, 0}, + QTaskLogger::Submission); if (canRender()) { { // Scoped to destroy surfaceLock @@ -698,15 +769,11 @@ void Renderer::doRender(bool swapBuffers) m_vsyncFrameAdvanceService->proceedToNextFrame(); hasCleanedQueueAndProceeded = true; -#if QT_CONFIG(qt3d_profile_jobs) - if (preprocessingComplete) { - submissionStatsPart2.startTime = QThreadPooler::m_jobsStatTimer.nsecsElapsed(); - submissionStatsPart1.endTime = submissionStatsPart2.startTime; - } -#endif // Only try to submit the RenderViews if the preprocessing was successful // This part of the submission is happening in parallel to the RV building for the next frame if (preprocessingComplete) { + submissionStatsPart1.end(submissionStatsPart2.restart()); + // 3) Submit the render commands for frame n (making sure we never reference something that could be changing) // Render using current device state and renderer configuration submissionData = submitRenderViews(renderViews); @@ -716,33 +783,18 @@ void Renderer::doRender(bool swapBuffers) } } -#if QT_CONFIG(qt3d_profile_jobs) // Execute the pending shell commands m_commandExecuter->performAsynchronousCommandExecution(renderViews); -#endif // Delete all the RenderViews which will clear the allocators // that were used for their allocation qDeleteAll(renderViews); - -#if QT_CONFIG(qt3d_profile_jobs) - if (preprocessingComplete) { - // Save submission elapsed time - submissionStatsPart2.endTime = QThreadPooler::m_jobsStatTimer.nsecsElapsed(); - // Note this is safe since proceedToNextFrame is the one going to trigger - // the write to the file, and this is performed after this step - Qt3DCore::QThreadPooler::addSubmissionLogStatsEntry(submissionStatsPart1); - Qt3DCore::QThreadPooler::addSubmissionLogStatsEntry(submissionStatsPart2); - Profiling::GLTimeRecorder::writeResults(); - } -#endif } // If hasCleanedQueueAndProceeded isn't true this implies that something went wrong // with the rendering and/or the renderqueue is incomplete from some reason // or alternatively it could be complete but empty (RenderQueue of size 0) - if (!hasCleanedQueueAndProceeded) { // RenderQueue was full but something bad happened when // trying to render it and therefore proceedToNextFrame was not called @@ -830,12 +882,7 @@ bool Renderer::isReadyToSubmit() // Main thread QVariant Renderer::executeCommand(const QStringList &args) { -#if QT_CONFIG(qt3d_profile_jobs) return m_commandExecuter->executeCommand(args); -#else - Q_UNUSED(args); -#endif - return QVariant(); } /*! @@ -860,13 +907,13 @@ void Renderer::prepareCommandsSubmission(const QVector<RenderView *> &renderView QHash<HVao, bool> updatedTable; for (RenderView *rv: renderViews) { - const QVector<RenderCommand *> commands = rv->commands(); - for (RenderCommand *command : commands) { + QVector<RenderCommand> &commands = rv->commands(); + for (RenderCommand &command : commands) { // Update/Create VAO - if (command->m_type == RenderCommand::Draw) { - Geometry *rGeometry = m_nodesManager->data<Geometry, GeometryManager>(command->m_geometry); - GeometryRenderer *rGeometryRenderer = m_nodesManager->data<GeometryRenderer, GeometryRendererManager>(command->m_geometryRenderer); - Shader *shader = m_nodesManager->data<Shader, ShaderManager>(command->m_shader); + if (command.m_type == RenderCommand::Draw) { + Geometry *rGeometry = m_nodesManager->data<Geometry, GeometryManager>(command.m_geometry); + GeometryRenderer *rGeometryRenderer = m_nodesManager->data<GeometryRenderer, GeometryRendererManager>(command.m_geometryRenderer); + Shader *shader = m_nodesManager->data<Shader, ShaderManager>(command.m_shader); // We should never have inserted a command for which these are null // in the first place @@ -879,15 +926,15 @@ void Renderer::prepareCommandsSubmission(const QVector<RenderView *> &renderView // Create VAO or return already created instance associated with command shader/geometry // (VAO is emulated if not supported) - createOrUpdateVAO(command, &vaoHandle, &vao); - command->m_vao = vaoHandle; + createOrUpdateVAO(&command, &vaoHandle, &vao); + command.m_vao = vaoHandle; // Avoids redoing the same thing for the same VAO if (!updatedTable.contains(vaoHandle)) { updatedTable.insert(vaoHandle, true); // Do we have any attributes that are dirty ? - const bool requiresPartialVAOUpdate = requiresVAOAttributeUpdate(rGeometry, command); + const bool requiresPartialVAOUpdate = requiresVAOAttributeUpdate(rGeometry, &command); // If true, we need to reupload all attributes to set the VAO // Otherwise only dirty attributes will be updates @@ -898,7 +945,7 @@ void Renderer::prepareCommandsSubmission(const QVector<RenderView *> &renderView if (rGeometry->isDirty()) m_dirtyGeometry.push_back(rGeometry); - if (!command->m_activeAttributes.isEmpty() && (requiresFullVAOUpdate || requiresPartialVAOUpdate)) { + if (!command.m_activeAttributes.isEmpty() && (requiresFullVAOUpdate || requiresPartialVAOUpdate)) { Profiling::GLTimeRecorder recorder(Profiling::VAOUpload); // Activate shader m_submissionContext->activateShader(shader->dna()); @@ -906,7 +953,7 @@ void Renderer::prepareCommandsSubmission(const QVector<RenderView *> &renderView vao->bind(); // Update or set Attributes and Buffers for the given rGeometry and Command // Note: this fills m_dirtyAttributes as well - if (updateVAOWithAttributes(rGeometry, command, shader, requiresFullVAOUpdate)) + if (updateVAOWithAttributes(rGeometry, &command, shader, requiresFullVAOUpdate)) vao->setSpecified(true); } } @@ -918,82 +965,14 @@ void Renderer::prepareCommandsSubmission(const QVector<RenderView *> &renderView rGeometryRenderer->unsetDirty(); // 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; - } - } + shader->prepareUniforms(command.m_parameterPack); - 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); + } else if (command.m_type == RenderCommand::Compute) { + Shader *shader = m_nodesManager->data<Shader, ShaderManager>(command.m_shader); Q_ASSERT(shader); // Prepare the ShaderParameterPack based on the active uniforms of the shader - shader->prepareUniforms(command->m_parameterPack); + shader->prepareUniforms(command.m_parameterPack); } } } @@ -1042,6 +1021,7 @@ void Renderer::lookForDirtyBuffers() } } +// Called in prepareSubmission void Renderer::lookForDownloadableBuffers() { m_downloadableBuffers.clear(); @@ -1049,7 +1029,7 @@ void Renderer::lookForDownloadableBuffers() for (const HBuffer &handle : activeBufferHandles) { Buffer *buffer = m_nodesManager->bufferManager()->data(handle); if (buffer->access() & QBuffer::Read) - m_downloadableBuffers.push_back(handle); + m_downloadableBuffers.push_back(buffer->peerId()); } } @@ -1106,7 +1086,7 @@ void Renderer::reloadDirtyShaders() // If api of the renderer matches the one from the technique if (technique->isCompatibleWithRenderer()) { const auto passIds = technique->renderPasses(); - for (const QNodeId passId : passIds) { + for (const QNodeId &passId : passIds) { RenderPass *renderPass = m_nodesManager->renderPassManager()->lookupResource(passId); HShader shaderHandle = m_nodesManager->shaderManager()->lookupHandle(renderPass->shaderProgram()); Shader *shader = m_nodesManager->shaderManager()->data(shaderHandle); @@ -1146,7 +1126,7 @@ void Renderer::reloadDirtyShaders() } } -// Executed in job postFrame +// Executed in job (in main thread when jobs are done) void Renderer::sendShaderChangesToFrontend(Qt3DCore::QAspectManager *manager) { Q_ASSERT(isRunning()); @@ -1173,14 +1153,13 @@ void Renderer::sendShaderChangesToFrontend(Qt3DCore::QAspectManager *manager) } } -// Executed in a job (as postFrame) +// Executed in a job (in main thread when jobs are done) void Renderer::sendTextureChangesToFrontend(Qt3DCore::QAspectManager *manager) { const QVector<QPair<Texture::TextureUpdateInfo, Qt3DCore::QNodeIdVector>> updateTextureProperties = std::move(m_updatedTextureProperties); for (const auto &pair : updateTextureProperties) { const Qt3DCore::QNodeIdVector targetIds = pair.second; - for (const Qt3DCore::QNodeId targetId: targetIds) { - + for (const Qt3DCore::QNodeId &targetId: targetIds) { // Lookup texture Texture *t = m_nodesManager->textureManager()->lookupResource(targetId); // If backend texture is Dirty, some property has changed and the properties we are @@ -1226,17 +1205,24 @@ void Renderer::sendSetFenceHandlesToFrontend() } } -// Executed in a job -void Renderer::sendDisablesToFrontend() +// Executed in a job (in main thread when jobs done) +void Renderer::sendDisablesToFrontend(Qt3DCore::QAspectManager *manager) { - const auto updatedDisables = std::move(m_updatedDisables); - FrameGraphManager *fgManager = m_nodesManager->frameGraphManager(); + // SubtreeEnabled + const auto updatedDisables = std::move(m_updatedDisableSubtreeEnablers); for (const auto &nodeId : updatedDisables) { - FrameGraphNode *fgNode = fgManager->lookupNode(nodeId); - if (fgNode != nullptr) { // Node could have been deleted before we got a chance to notify it - Q_ASSERT(fgNode->nodeType() == FrameGraphNode::SubtreeEnabler); - SubtreeEnabler *enabler = static_cast<SubtreeEnabler *>(fgNode); - enabler->sendDisableToFrontend(); + QSubtreeEnabler *frontend = static_cast<decltype(frontend)>(manager->lookupNode(nodeId)); + frontend->setEnabled(false); + } + + // Compute Commands + const QVector<HComputeCommand> activeCommands = m_nodesManager->computeJobManager()->activeHandles(); + for (const HComputeCommand &handle :activeCommands) { + ComputeCommand *c = m_nodesManager->computeJobManager()->data(handle); + if (c->hasReachedFrameCount()) { + QComputeCommand *frontend = static_cast<decltype(frontend)>(manager->lookupNode(c->peerId())); + frontend->setEnabled(false); + c->resetHasReachedFrameCount(); } } } @@ -1355,6 +1341,9 @@ void Renderer::updateGLResources() // Record ids of texture to cleanup while we are still blocking the aspect thread m_textureIdsToCleanup += m_nodesManager->textureManager()->takeTexturesIdsToCleanup(); } + + // Record list of buffer that might need uploading + lookForDownloadableBuffers(); } // Render Thread @@ -1437,12 +1426,18 @@ void Renderer::cleanupTexture(Qt3DCore::QNodeId cleanedUpTextureId) // Called by SubmitRenderView void Renderer::downloadGLBuffers() { - lookForDownloadableBuffers(); - const QVector<HBuffer> downloadableHandles = std::move(m_downloadableBuffers); - for (const HBuffer &handle : downloadableHandles) { - Buffer *buffer = m_nodesManager->bufferManager()->data(handle); - QByteArray content = m_submissionContext->downloadBufferContent(buffer); - m_sendBufferCaptureJob->addRequest(QPair<Buffer*, QByteArray>(buffer, content)); + const QVector<Qt3DCore::QNodeId> downloadableHandles = std::move(m_downloadableBuffers); + for (const Qt3DCore::QNodeId &bufferId : downloadableHandles) { + BufferManager *bufferManager = m_nodesManager->bufferManager(); + BufferManager::ReadLocker locker(const_cast<const BufferManager *>(bufferManager)); + Buffer *buffer = bufferManager->lookupResource(bufferId); + // Buffer could have been destroyed at this point + if (!buffer) + continue; + // locker is protecting us from the buffer being destroy while we're looking + // up its content + const QByteArray content = m_submissionContext->downloadBufferContent(buffer); + m_sendBufferCaptureJob->addRequest(QPair<Qt3DCore::QNodeId, QByteArray>(bufferId, content)); } } @@ -1606,7 +1601,7 @@ Renderer::ViewSubmissionResultData Renderer::submitRenderViews(const QVector<Ren } // Set the Viewport - m_submissionContext->setViewport(renderView->viewport(), renderView->surfaceSize() * renderView->devicePixelRatio()); + m_submissionContext->setViewport(renderView->viewport(), renderView->surfaceSize()); // Execute the render commands if (!executeCommandsSubmission(renderView)) @@ -1617,7 +1612,7 @@ Renderer::ViewSubmissionResultData Renderer::submitRenderViews(const QVector<Ren // renderViewStateSet or m_defaultRenderStateSet) if (!renderView->renderCaptureNodeId().isNull()) { const QRenderCaptureRequest request = renderView->renderCaptureRequest(); - const QSize size = m_submissionContext->renderTargetSize(renderView->surfaceSize() * renderView->devicePixelRatio()); + const QSize size = m_submissionContext->renderTargetSize(renderView->surfaceSize()); QRect rect(QPoint(0, 0), size); if (!request.rect.isEmpty()) rect = rect.intersected(request.rect); @@ -1632,7 +1627,8 @@ Renderer::ViewSubmissionResultData Renderer::submitRenderViews(const QVector<Ren Render::RenderCapture *renderCapture = static_cast<Render::RenderCapture*>(m_nodesManager->frameGraphManager()->lookupNode(renderView->renderCaptureNodeId())); renderCapture->addRenderCapture(request.captureId, image); - addRenderCaptureSendRequest(renderView->renderCaptureNodeId()); + if (!m_pendingRenderCaptureSendRequests.contains(renderView->renderCaptureNodeId())) + m_pendingRenderCaptureSendRequests.push_back(renderView->renderCaptureNodeId()); } if (renderView->isDownloadBuffersEnable()) @@ -1688,7 +1684,7 @@ Renderer::ViewSubmissionResultData Renderer::submitRenderViews(const QVector<Ren void Renderer::markDirty(BackendNodeDirtySet changes, BackendNode *node) { - Q_UNUSED(node); + Q_UNUSED(node) m_dirtyBits.marked |= changes; } @@ -1705,12 +1701,11 @@ void Renderer::clearDirtyBits(BackendNodeDirtySet changes) } #endif -bool Renderer::shouldRender() +bool Renderer::shouldRender() const { // Only render if something changed during the last frame, or the last frame // was not rendered successfully (or render-on-demand is disabled) return (m_settings->renderPolicy() == QRenderSettings::Always - || m_renderThread == nullptr // <==> we use Scene3D || m_dirtyBits.marked != 0 || m_dirtyBits.remaining != 0 || !m_lastFrameCorrect.loadRelaxed()); @@ -1725,24 +1720,34 @@ void Renderer::skipNextFrame() m_submitRenderViewsSemaphore.release(1); } -// Jobs we may have to run even if no rendering will happen -QVector<QAspectJobPtr> Renderer::preRenderingJobs() +void Renderer::jobsDone(Qt3DCore::QAspectManager *manager) { - QVector<QAspectJobPtr> jobs; + // called in main thread once all jobs are done running + + // sync captured renders to frontend + const QVector<Qt3DCore::QNodeId> pendingCaptureIds = std::move(m_pendingRenderCaptureSendRequests); + for (const Qt3DCore::QNodeId &id : qAsConst(pendingCaptureIds)) { + auto *backend = static_cast<Qt3DRender::Render::RenderCapture *> + (m_nodesManager->frameGraphManager()->lookupNode(id)); + backend->syncRenderCapturesToFrontend(manager); + } // Do we need to notify any texture about property changes? if (m_updatedTextureProperties.size() > 0) - jobs.push_back(m_sendTextureChangesToFrontendJob); + sendTextureChangesToFrontend(manager); + + sendDisablesToFrontend(manager); +} + +// Jobs we may have to run even if no rendering will happen +QVector<QAspectJobPtr> Renderer::preRenderingJobs() +{ + QVector<QAspectJobPtr> jobs; // Do we need to notify frontend about fence change? if (m_updatedSetFences.size() > 0) jobs.push_back(m_sendSetFenceHandlesToFrontendJob); - const QVector<Qt3DCore::QNodeId> pendingCaptureIds = takePendingRenderCaptureSendRequests(); - if (pendingCaptureIds.size() > 0) { - m_sendRenderCaptureJob->setPendingCaptureRequests(pendingCaptureIds); - jobs.push_back(m_sendRenderCaptureJob); - } if (m_sendBufferCaptureJob->hasRequests()) jobs.push_back(m_sendBufferCaptureJob); @@ -1759,7 +1764,6 @@ QVector<QAspectJobPtr> Renderer::preRenderingJobs() QVector<Qt3DCore::QAspectJobPtr> Renderer::renderBinJobs() { QVector<QAspectJobPtr> renderBinJobs; - // Create the jobs to build the frame const QVector<QAspectJobPtr> bufferJobs = createRenderBufferJobs(); @@ -1774,9 +1778,9 @@ QVector<Qt3DCore::QAspectJobPtr> Renderer::renderBinJobs() m_updateLevelOfDetailJob->setFrameGraphRoot(frameGraphRoot()); const BackendNodeDirtySet dirtyBitsForFrame = m_dirtyBits.marked | m_dirtyBits.remaining; - m_dirtyBits.marked = 0; - m_dirtyBits.remaining = 0; - BackendNodeDirtySet notCleared = 0; + m_dirtyBits.marked = {}; + m_dirtyBits.remaining = {}; + BackendNodeDirtySet notCleared = {}; // Add jobs const bool entitiesEnabledDirty = dirtyBitsForFrame & AbstractRenderer::EntityEnabledDirty; @@ -1810,7 +1814,6 @@ QVector<Qt3DCore::QAspectJobPtr> Renderer::renderBinJobs() renderBinJobs.push_back(m_updateSkinningPaletteJob); renderBinJobs.push_back(m_updateLevelOfDetailJob); renderBinJobs.push_back(m_cleanupJob); - renderBinJobs.append(bufferJobs); // Jobs to prepare GL Resource upload @@ -1834,11 +1837,27 @@ QVector<Qt3DCore::QAspectJobPtr> Renderer::renderBinJobs() const bool computeableDirty = dirtyBitsForFrame & AbstractRenderer::ComputeDirty; const bool renderableDirty = dirtyBitsForFrame & AbstractRenderer::GeometryDirty; const bool materialCacheNeedsToBeRebuilt = shadersDirty || materialDirty || frameGraphDirty; + const bool renderCommandsDirty = materialCacheNeedsToBeRebuilt || renderableDirty || computeableDirty; // Rebuild Entity Layers list if layers are dirty if (layersDirty) renderBinJobs.push_back(m_updateEntityLayersJob); + if (renderableDirty) { + renderBinJobs.push_back(m_renderableEntityFilterJob); + renderBinJobs.push_back(m_cacheRenderableEntitiesJob); + } + + if (computeableDirty) { + renderBinJobs.push_back(m_computableEntityFilterJob); + renderBinJobs.push_back(m_cacheComputableEntitiesJob); + } + + if (lightsDirty) { + renderBinJobs.push_back(m_lightGathererJob); + renderBinJobs.push_back(m_cacheLightsJob); + } + QMutexLocker lock(m_renderQueue->mutex()); if (m_renderQueue->wasReset()) { // Have we rendered yet? (Scene3D case) // Traverse the current framegraph. For each leaf node create a @@ -1859,20 +1878,18 @@ QVector<Qt3DCore::QAspectJobPtr> Renderer::renderBinJobs() // Handle single shot subtree enablers const auto subtreeEnablers = visitor.takeEnablersToDisable(); for (auto *node : subtreeEnablers) - m_updatedDisables.push_back(node->peerId()); - if (m_updatedDisables.size() > 0) - renderBinJobs.push_back(m_sendDisablesToFrontendJob); + m_updatedDisableSubtreeEnablers.push_back(node->peerId()); } const int fgBranchCount = m_frameGraphLeaves.size(); for (int i = 0; i < fgBranchCount; ++i) { - RenderViewBuilder builder(m_frameGraphLeaves.at(i), i, this); - builder.setLayerCacheNeedsToBeRebuilt(layersCacheNeedsToBeRebuilt); - builder.setRenderableCacheNeedsToBeRebuilt(renderableDirty); - builder.setComputableCacheNeedsToBeRebuilt(computeableDirty); - builder.setLightGathererCacheNeedsToBeRebuilt(lightsDirty); - builder.setMaterialGathererCacheNeedsToBeRebuilt(materialCacheNeedsToBeRebuilt); - builder.setLightGathererCacheNeedsToBeRebuilt(lightsDirty); + FrameGraphNode *leaf = m_frameGraphLeaves.at(i); + RenderViewBuilder builder(leaf, i, this); + // If we have a new RV (wasn't in the cache before, then it contains no cached data) + const bool isNewRV = !m_cache.leafNodeCache.contains(leaf); + builder.setLayerCacheNeedsToBeRebuilt(layersCacheNeedsToBeRebuilt || isNewRV); + builder.setMaterialGathererCacheNeedsToBeRebuilt(materialCacheNeedsToBeRebuilt || isNewRV); + builder.setRenderCommandCacheNeedsToBeRebuilt(renderCommandsDirty || isNewRV); builder.prepareJobs(); renderBinJobs.append(builder.buildJobHierachy()); @@ -2075,7 +2092,7 @@ bool Renderer::executeCommandsSubmission(const RenderView *rv) bool allCommandsIssued = true; // Render drawing commands - const QVector<RenderCommand *> commands = rv->commands(); + QVector<RenderCommand> commands = rv->commands(); // Use the graphicscontext to submit the commands to the underlying // graphics API (OpenGL) @@ -2084,18 +2101,18 @@ bool Renderer::executeCommandsSubmission(const RenderView *rv) RenderStateSet *globalState = m_submissionContext->currentStateSet(); OpenGLVertexArrayObject *vao = nullptr; - for (RenderCommand *command : qAsConst(commands)) { + for (RenderCommand &command : commands) { - if (command->m_type == RenderCommand::Compute) { // Compute Call - performCompute(rv, command); + if (command.m_type == RenderCommand::Compute) { // Compute Call + performCompute(rv, &command); } else { // Draw Command // Check if we have a valid command that can be drawn - if (!command->m_isValid) { + if (!command.m_isValid) { allCommandsIssued = false; continue; } - vao = m_nodesManager->vaoManager()->data(command->m_vao); + vao = m_nodesManager->vaoManager()->data(command.m_vao); // something may have went wrong when initializing the VAO if (!vao->isSpecified()) { @@ -2106,7 +2123,7 @@ bool Renderer::executeCommandsSubmission(const RenderView *rv) { Profiling::GLTimeRecorder recorder(Profiling::ShaderUpdate); //// We activate the shader here - if (!m_submissionContext->activateShader(command->m_shaderDna)) { + if (!m_submissionContext->activateShader(command.m_shaderDna)) { allCommandsIssued = false; continue; } @@ -2121,7 +2138,7 @@ bool Renderer::executeCommandsSubmission(const RenderView *rv) { Profiling::GLTimeRecorder recorder(Profiling::UniformUpdate); //// Update program uniforms - if (!m_submissionContext->setParameters(command->m_parameterPack)) { + if (!m_submissionContext->setParameters(command.m_parameterPack)) { allCommandsIssued = false; // If we have failed to set uniform (e.g unable to bind a texture) // we won't perform the draw call which could show invalid content @@ -2132,7 +2149,7 @@ bool Renderer::executeCommandsSubmission(const RenderView *rv) //// OpenGL State // TO DO: Make states not dependendent on their backend node for this step // Set state - RenderStateSet *localState = command->m_stateSet; + RenderStateSet *localState = command.m_stateSet.data(); { @@ -2140,8 +2157,8 @@ bool Renderer::executeCommandsSubmission(const RenderView *rv) // Merge the RenderCommand state with the globalState of the RenderView // Or restore the globalState if no stateSet for the RenderCommand if (localState != nullptr) { - command->m_stateSet->merge(globalState); - m_submissionContext->setCurrentStateSet(command->m_stateSet); + command.m_stateSet->merge(globalState); + m_submissionContext->setCurrentStateSet(localState); } else { m_submissionContext->setCurrentStateSet(globalState); } @@ -2151,7 +2168,7 @@ bool Renderer::executeCommandsSubmission(const RenderView *rv) // at that point //// Draw Calls - performDraw(command); + performDraw(&command); } } // end of RenderCommands loop @@ -2167,7 +2184,7 @@ bool Renderer::executeCommandsSubmission(const RenderView *rv) } bool Renderer::updateVAOWithAttributes(Geometry *geometry, - RenderCommand *command, + const RenderCommand *command, Shader *shader, bool forceUpdate) { @@ -2227,7 +2244,7 @@ bool Renderer::updateVAOWithAttributes(Geometry *geometry, } bool Renderer::requiresVAOAttributeUpdate(Geometry *geometry, - RenderCommand *command) const + const RenderCommand *command) const { const auto attributeIds = geometry->attributes(); @@ -2295,17 +2312,6 @@ SubmissionContext *Renderer::submissionContext() const return m_submissionContext.data(); } -void Renderer::addRenderCaptureSendRequest(Qt3DCore::QNodeId nodeId) -{ - if (!m_pendingRenderCaptureSendRequests.contains(nodeId)) - m_pendingRenderCaptureSendRequests.push_back(nodeId); -} - -const QVector<Qt3DCore::QNodeId> Renderer::takePendingRenderCaptureSendRequests() -{ - return std::move(m_pendingRenderCaptureSendRequests); -} - // Returns a vector of jobs to be performed for dirty buffers // 1 dirty buffer == 1 job, all job can be performed in parallel QVector<Qt3DCore::QAspectJobPtr> Renderer::createRenderBufferJobs() const diff --git a/src/render/renderers/opengl/renderer/renderer_p.h b/src/render/renderers/opengl/renderer/renderer_p.h index b1cd4aac5..e770b8280 100644 --- a/src/render/renderers/opengl/renderer/renderer_p.h +++ b/src/render/renderers/opengl/renderer/renderer_p.h @@ -71,7 +71,6 @@ #include <Qt3DRender/private/updateworldboundingvolumejob_p.h> #include <Qt3DRender/private/updatetreeenabledjob_p.h> #include <Qt3DRender/private/platformsurfacefilter_p.h> -#include <Qt3DRender/private/sendrendercapturejob_p.h> #include <Qt3DRender/private/sendbuffercapturejob_p.h> #include <Qt3DRender/private/genericlambdajob_p.h> #include <Qt3DRender/private/updatemeshtrianglelistjob_p.h> @@ -82,6 +81,7 @@ #include <Qt3DRender/private/texture_p.h> #include <Qt3DRender/private/glfence_p.h> #include <Qt3DRender/private/shaderbuilder_p.h> +#include <Qt3DRender/private/lightgatherer_p.h> #include <QHash> #include <QMatrix4x4> @@ -125,11 +125,9 @@ class QAbstractShapeMesh; struct GraphicsApiFilterData; class QSceneImporter; -#if QT_CONFIG(qt3d_profile_jobs) namespace Debug { class CommandExecuter; } -#endif namespace Render { @@ -158,6 +156,13 @@ typedef QSharedPointer<UpdateLevelOfDetailJob> UpdateLevelOfDetailJobPtr; using SynchronizerJobPtr = GenericLambdaJobPtr<std::function<void()>>; using SynchronizerPostFramePtr = GenericLambdaJobAndPostFramePtr<std::function<void ()>, std::function<void (Qt3DCore::QAspectManager *)>>; +template<typename T, typename ... Ts> +class FilterEntityByComponentJob; +template<typename T, typename ... Ts> +using FilterEntityByComponentJobPtr = QSharedPointer<FilterEntityByComponentJob<T, Ts...>>; +using ComputableEntityFilterPtr = FilterEntityByComponentJobPtr<Render::ComputeCommand, Render::Material>; +using RenderableEntityFilterPtr = FilterEntityByComponentJobPtr<Render::GeometryRenderer, Render::Material>; + class Q_3DRENDERSHARED_PRIVATE_EXPORT Renderer : public AbstractRenderer { public: @@ -199,8 +204,9 @@ public: #if defined(QT_BUILD_INTERNAL) void clearDirtyBits(BackendNodeDirtySet changes) override; #endif - bool shouldRender() override; + bool shouldRender() const override; void skipNextFrame() override; + void jobsDone(Qt3DCore::QAspectManager *manager) override; QVector<Qt3DCore::QAspectJobPtr> preRenderingJobs() override; QVector<Qt3DCore::QAspectJobPtr> renderBinJobs() override; @@ -225,8 +231,13 @@ public: inline SynchronizerPostFramePtr introspectShadersJob() const { return m_introspectShaderJob; } inline Qt3DCore::QAspectJobPtr bufferGathererJob() const { return m_bufferGathererJob; } inline Qt3DCore::QAspectJobPtr textureGathererJob() const { return m_textureGathererJob; } - inline Qt3DCore::QAspectJobPtr sendTextureChangesToFrontendJob() const { return m_sendTextureChangesToFrontendJob; } inline UpdateEntityLayersJobPtr updateEntityLayersJob() const { return m_updateEntityLayersJob; } + inline LightGathererPtr lightGathererJob() const { return m_lightGathererJob; } + inline RenderableEntityFilterPtr renderableEntityFilterJob() const { return m_renderableEntityFilterJob; } + inline ComputableEntityFilterPtr computableEntityFilterJob() const { return m_computableEntityFilterJob; } + inline SynchronizerJobPtr cacheLightJob() const { return m_cacheLightsJob; } + inline SynchronizerJobPtr cacheRenderableEntitiesJob() const { return m_cacheRenderableEntitiesJob; } + inline SynchronizerJobPtr cacheComputableEntitiesJob() const { return m_cacheComputableEntitiesJob; } Qt3DCore::QAbstractFrameAdvanceService *frameAdvanceService() const override; @@ -254,12 +265,12 @@ public: void prepareCommandsSubmission(const QVector<RenderView *> &renderViews); bool executeCommandsSubmission(const RenderView *rv); bool updateVAOWithAttributes(Geometry *geometry, - RenderCommand *command, + const RenderCommand *command, Shader *shader, bool forceUpdate); bool requiresVAOAttributeUpdate(Geometry *geometry, - RenderCommand *command) const; + const RenderCommand *command) const; void setOpenGLContext(QOpenGLContext *context) override; const GraphicsApiFilterData *contextInfo() const; @@ -270,9 +281,6 @@ public: QList<QPair<QObject*, QMouseEvent>> pendingPickingEvents() const; QList<QKeyEvent> pendingKeyEvents() const; - void addRenderCaptureSendRequest(Qt3DCore::QNodeId nodeId); - const QVector<Qt3DCore::QNodeId> takePendingRenderCaptureSendRequests(); - void enqueueRenderView(RenderView *renderView, int submitOrder); bool isReadyToSubmit(); @@ -338,8 +346,8 @@ private: QAtomicInt m_exposed; struct DirtyBits { - BackendNodeDirtySet marked = 0; // marked dirty since last job build - BackendNodeDirtySet remaining = 0; // remaining dirty after jobs have finished + BackendNodeDirtySet marked; // marked dirty since last job build + BackendNodeDirtySet remaining; // remaining dirty after jobs have finished }; DirtyBits m_dirtyBits; @@ -362,13 +370,15 @@ private: CalculateBoundingVolumeJobPtr m_calculateBoundingVolumeJob; UpdateWorldBoundingVolumeJobPtr m_updateWorldBoundingVolumeJob; UpdateTreeEnabledJobPtr m_updateTreeEnabledJob; - SendRenderCaptureJobPtr m_sendRenderCaptureJob; SendBufferCaptureJobPtr m_sendBufferCaptureJob; UpdateSkinningPaletteJobPtr m_updateSkinningPaletteJob; UpdateLevelOfDetailJobPtr m_updateLevelOfDetailJob; UpdateMeshTriangleListJobPtr m_updateMeshTriangleListJob; FilterCompatibleTechniqueJobPtr m_filterCompatibleTechniqueJob; UpdateEntityLayersJobPtr m_updateEntityLayersJob; + LightGathererPtr m_lightGathererJob; + RenderableEntityFilterPtr m_renderableEntityFilterJob; + ComputableEntityFilterPtr m_computableEntityFilterJob; QVector<Qt3DCore::QNodeId> m_pendingRenderCaptureSendRequests; @@ -381,11 +391,12 @@ private: SynchronizerJobPtr m_bufferGathererJob; SynchronizerJobPtr m_vaoGathererJob; SynchronizerJobPtr m_textureGathererJob; - SynchronizerPostFramePtr m_sendTextureChangesToFrontendJob; SynchronizerJobPtr m_sendSetFenceHandlesToFrontendJob; - SynchronizerJobPtr m_sendDisablesToFrontendJob; SynchronizerPostFramePtr m_introspectShaderJob; SynchronizerJobPtr m_syncLoadingJobs; + SynchronizerJobPtr m_cacheRenderableEntitiesJob; + SynchronizerJobPtr m_cacheComputableEntitiesJob; + SynchronizerJobPtr m_cacheLightsJob; void lookForAbandonedVaos(); void lookForDirtyBuffers(); @@ -395,18 +406,18 @@ private: void sendShaderChangesToFrontend(Qt3DCore::QAspectManager *manager); void sendTextureChangesToFrontend(Qt3DCore::QAspectManager *manager); void sendSetFenceHandlesToFrontend(); - void sendDisablesToFrontend(); + void sendDisablesToFrontend(Qt3DCore::QAspectManager *manager); QMutex m_abandonedVaosMutex; QVector<HVao> m_abandonedVaos; QVector<HBuffer> m_dirtyBuffers; - QVector<HBuffer> m_downloadableBuffers; + QVector<Qt3DCore::QNodeId> m_downloadableBuffers; QVector<HShader> m_dirtyShaders; QVector<HTexture> m_dirtyTextures; QVector<QPair<Texture::TextureUpdateInfo, Qt3DCore::QNodeIdVector>> m_updatedTextureProperties; QVector<QPair<Qt3DCore::QNodeId, GLFence>> m_updatedSetFences; - QVector<Qt3DCore::QNodeId> m_updatedDisables; + QVector<Qt3DCore::QNodeId> m_updatedDisableSubtreeEnablers; Qt3DCore::QNodeIdVector m_textureIdsToCleanup; QVector<ShaderBuilderUpdate> m_shaderBuilderUpdates; @@ -415,10 +426,7 @@ private: OffscreenSurfaceHelper *m_offscreenHelper; QMutex m_offscreenSurfaceMutex; -#if QT_CONFIG(qt3d_profile_jobs) QScopedPointer<Qt3DRender::Debug::CommandExecuter> m_commandExecuter; - friend class Qt3DRender::Debug::CommandExecuter; -#endif #ifdef QT_BUILD_INTERNAL friend class ::tst_Renderer; diff --git a/src/render/renderers/opengl/renderer/renderercache_p.h b/src/render/renderers/opengl/renderer/renderercache_p.h index 0e9c5d3cd..02fe4ff41 100644 --- a/src/render/renderers/opengl/renderer/renderercache_p.h +++ b/src/render/renderers/opengl/renderer/renderercache_p.h @@ -56,6 +56,7 @@ #include <Qt3DRender/private/entity_p.h> #include <Qt3DRender/private/renderviewjobutils_p.h> #include <Qt3DRender/private/lightsource_p.h> +#include <Qt3DRender/private/rendercommand_p.h> QT_BEGIN_NAMESPACE @@ -69,12 +70,16 @@ struct RendererCache { QVector<Entity *> filterEntitiesByLayer; MaterialParameterGathererData materialParameterGatherer; - QVector<LightSource> gatheredLights; - QVector<Entity *> renderableEntities; - QVector<Entity *> computeEntities; - EnvironmentLight* environmentLight; + EntityRenderCommandData renderCommandData; }; + // Shared amongst all RV cache + QVector<Entity *> renderableEntities; + QVector<Entity *> computeEntities; + QVector<LightSource> gatheredLights; + EnvironmentLight* environmentLight; + + // Per RV cache QHash<FrameGraphNode *, LeafNodeData> leafNodeCache; QMutex *mutex() { return &m_mutex; } diff --git a/src/render/renderers/opengl/renderer/renderview.cpp b/src/render/renderers/opengl/renderer/renderview.cpp index aa9f4d1a5..c00a92629 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: @@ -238,11 +238,14 @@ RenderView::RenderView() : m_isDownloadBuffersEnable(false) , m_hasBlitFramebufferInfo(false) , m_renderer(nullptr) + , m_manager(nullptr) , m_devicePixelRatio(1.) - , m_viewport(QRectF(0.0f, 0.0f, 1.0f, 1.0f)) + , m_viewport(QRectF(0., 0., 1., 1.)) , m_gamma(2.2f) , m_surface(nullptr) , m_clearBuffer(QClearBuffers::None) + , m_clearDepthValue(1.f) + , m_clearStencilValue(0) , m_stateSet(nullptr) , m_noDraw(false) , m_compute(false) @@ -280,10 +283,6 @@ RenderView::RenderView() RenderView::~RenderView() { delete m_stateSet; - for (RenderCommand *command : qAsConst(m_commands)) { - delete command->m_stateSet; - delete command; - } } namespace { @@ -291,7 +290,7 @@ namespace { template<int SortType> struct AdjacentSubRangeFinder { - static bool adjacentSubRange(RenderCommand *, RenderCommand *) + static bool adjacentSubRange(const RenderCommand &, const RenderCommand &) { Q_UNREACHABLE(); return false; @@ -301,47 +300,47 @@ struct AdjacentSubRangeFinder template<> struct AdjacentSubRangeFinder<QSortPolicy::StateChangeCost> { - static bool adjacentSubRange(RenderCommand *a, RenderCommand *b) + static bool adjacentSubRange(const RenderCommand &a, const RenderCommand &b) { - return a->m_changeCost == b->m_changeCost; + return a.m_changeCost == b.m_changeCost; } }; template<> struct AdjacentSubRangeFinder<QSortPolicy::BackToFront> { - static bool adjacentSubRange(RenderCommand *a, RenderCommand *b) + static bool adjacentSubRange(const RenderCommand &a, const RenderCommand &b) { - return a->m_depth == b->m_depth; + return a.m_depth == b.m_depth; } }; template<> struct AdjacentSubRangeFinder<QSortPolicy::Material> { - static bool adjacentSubRange(RenderCommand *a, RenderCommand *b) + static bool adjacentSubRange(const RenderCommand &a, const RenderCommand &b) { - return a->m_shaderDna == b->m_shaderDna; + return a.m_shaderDna == b.m_shaderDna; } }; template<> struct AdjacentSubRangeFinder<QSortPolicy::FrontToBack> { - static bool adjacentSubRange(RenderCommand *a, RenderCommand *b) + static bool adjacentSubRange(const RenderCommand &a, const RenderCommand &b) { - return a->m_depth == b->m_depth; + return a.m_depth == b.m_depth; } }; template<> struct AdjacentSubRangeFinder<QSortPolicy::Texture> { - static bool adjacentSubRange(RenderCommand *a, RenderCommand *b) + static bool adjacentSubRange(const RenderCommand &a, const RenderCommand &b) { // Two renderCommands are adjacent if one contains all the other command's textures - QVector<ShaderParameterPack::NamedResource> texturesA = a->m_parameterPack.textures(); - QVector<ShaderParameterPack::NamedResource> texturesB = b->m_parameterPack.textures(); + QVector<ShaderParameterPack::NamedResource> texturesA = a.m_parameterPack.textures(); + QVector<ShaderParameterPack::NamedResource> texturesB = b.m_parameterPack.textures(); if (texturesB.size() > texturesA.size()) qSwap(texturesA, texturesB); @@ -356,7 +355,7 @@ struct AdjacentSubRangeFinder<QSortPolicy::Texture> }; template<typename Predicate> -int advanceUntilNonAdjacent(const QVector<RenderCommand *> &commands, +int advanceUntilNonAdjacent(const QVector<RenderCommand> &commands, const int beg, const int end, Predicate pred) { int i = beg + 1; @@ -369,7 +368,7 @@ int advanceUntilNonAdjacent(const QVector<RenderCommand *> &commands, } -using CommandIt = QVector<RenderCommand *>::iterator; +using CommandIt = QVector<RenderCommand>::iterator; template<int SortType> struct SubRangeSorter @@ -387,8 +386,8 @@ struct SubRangeSorter<QSortPolicy::StateChangeCost> { static void sortSubRange(CommandIt begin, const CommandIt end) { - std::stable_sort(begin, end, [] (RenderCommand *a, RenderCommand *b) { - return a->m_changeCost > b->m_changeCost; + std::stable_sort(begin, end, [] (const RenderCommand &a, const RenderCommand &b) { + return a.m_changeCost > b.m_changeCost; }); } }; @@ -398,8 +397,8 @@ struct SubRangeSorter<QSortPolicy::BackToFront> { static void sortSubRange(CommandIt begin, const CommandIt end) { - std::stable_sort(begin, end, [] (RenderCommand *a, RenderCommand *b) { - return a->m_depth > b->m_depth; + std::stable_sort(begin, end, [] (const RenderCommand &a, const RenderCommand &b) { + return a.m_depth > b.m_depth; }); } }; @@ -410,8 +409,8 @@ struct SubRangeSorter<QSortPolicy::Material> static void sortSubRange(CommandIt begin, const CommandIt end) { // First we sort by shaderDNA - std::stable_sort(begin, end, [] (RenderCommand *a, RenderCommand *b) { - return a->m_shaderDna > b->m_shaderDna; + std::stable_sort(begin, end, [] (const RenderCommand &a, const RenderCommand &b) { + return a.m_shaderDna > b.m_shaderDna; }); } }; @@ -421,8 +420,8 @@ struct SubRangeSorter<QSortPolicy::FrontToBack> { static void sortSubRange(CommandIt begin, const CommandIt end) { - std::stable_sort(begin, end, [] (RenderCommand *a, RenderCommand *b) { - return a->m_depth < b->m_depth; + std::stable_sort(begin, end, [] (const RenderCommand &a, const RenderCommand &b) { + return a.m_depth < b.m_depth; }); } }; @@ -432,9 +431,9 @@ struct SubRangeSorter<QSortPolicy::Texture> { static void sortSubRange(CommandIt begin, const CommandIt end) { - std::stable_sort(begin, end, [] (RenderCommand *a, RenderCommand *b) { - QVector<ShaderParameterPack::NamedResource> texturesA = a->m_parameterPack.textures(); - QVector<ShaderParameterPack::NamedResource> texturesB = b->m_parameterPack.textures(); + std::stable_sort(begin, end, [] (const RenderCommand &a, const RenderCommand &b) { + QVector<ShaderParameterPack::NamedResource> texturesA = a.m_parameterPack.textures(); + QVector<ShaderParameterPack::NamedResource> texturesB = b.m_parameterPack.textures(); const int originalTextureASize = texturesA.size(); @@ -453,7 +452,7 @@ struct SubRangeSorter<QSortPolicy::Texture> } }; -int findSubRange(const QVector<RenderCommand *> &commands, +int findSubRange(const QVector<RenderCommand> &commands, const int begin, const int end, const QSortPolicy::SortType sortType) { @@ -474,14 +473,14 @@ int findSubRange(const QVector<RenderCommand *> &commands, } } -void sortByMaterial(QVector<RenderCommand *> &commands, int begin, const int end) +void sortByMaterial(QVector<RenderCommand> &commands, int begin, const int end) { // We try to arrange elements so that their rendering cost is minimized for a given shader int rangeEnd = advanceUntilNonAdjacent(commands, begin, end, AdjacentSubRangeFinder<QSortPolicy::Material>::adjacentSubRange); while (begin != end) { if (begin + 1 < rangeEnd) { - std::stable_sort(commands.begin() + begin + 1, commands.begin() + rangeEnd, [] (RenderCommand *a, RenderCommand *b){ - return a->m_material.handle() < b->m_material.handle(); + std::stable_sort(commands.begin() + begin + 1, commands.begin() + rangeEnd, [] (const RenderCommand &a, const RenderCommand &b){ + return a.m_material.handle() < b.m_material.handle(); }); } begin = rangeEnd; @@ -489,7 +488,7 @@ void sortByMaterial(QVector<RenderCommand *> &commands, int begin, const int end } } -void sortCommandRange(QVector<RenderCommand *> &commands, int begin, const int end, const int level, +void sortCommandRange(QVector<RenderCommand> &commands, int begin, const int end, const int level, const QVector<Qt3DRender::QSortPolicy::SortType> &sortingTypes) { if (level >= sortingTypes.size()) @@ -539,24 +538,23 @@ void RenderView::sort() // Minimize uniform changes int i = 0; - while (i < m_commands.size()) { + const int commandSize = m_commands.size(); + while (i < commandSize) { int j = i; // Advance while commands share the same shader - while (i < m_commands.size() && m_commands[j]->m_shaderDna == m_commands[i]->m_shaderDna) + while (i < commandSize && m_commands[j].m_shaderDna == m_commands[i].m_shaderDna) ++i; if (i - j > 0) { // Several commands have the same shader, so we minimize uniform changes - PackUniformHash cachedUniforms = m_commands[j++]->m_parameterPack.uniforms(); + PackUniformHash cachedUniforms = m_commands[j++].m_parameterPack.uniforms(); while (j < i) { // We need the reference here as we are modifying the original container // not the copy - PackUniformHash &uniforms = m_commands.at(j)->m_parameterPack.m_uniforms; - PackUniformHash::iterator it = uniforms.begin(); - const PackUniformHash::iterator end = uniforms.end(); + PackUniformHash &uniforms = m_commands[j].m_parameterPack.m_uniforms; - while (it != end) { + for (int u = 0; u < uniforms.keys.size();) { // We are comparing the values: // - raw uniform values // - the texture Node id if the uniform represents a texture @@ -564,12 +562,17 @@ void RenderView::sort() // sharing the same material (shader) are rendered, we can't have the case // where two uniforms, referencing the same texture eventually have 2 different // texture unit values - const UniformValue refValue = cachedUniforms.value(it.key()); - if (it.value() == refValue) { - it = uniforms.erase(it); + const int uniformNameId = uniforms.keys.at(u); + const UniformValue &refValue = cachedUniforms.value(uniformNameId); + const UniformValue &newValue = uniforms.values.at(u); + if (newValue == refValue) { + uniforms.erase(u); } else { - cachedUniforms.insert(it.key(), it.value()); - ++it; + // Record updated value so that subsequent comparison + // for the next command will be made againts latest + // uniform value + cachedUniforms.insert(uniformNameId, newValue); + ++u; } } ++j; @@ -619,20 +622,16 @@ void RenderView::addClearBuffers(const ClearBuffers *cb) { } // If we are there, we know that entity had a GeometryRenderer + Material -QVector<RenderCommand *> RenderView::buildDrawRenderCommands(const QVector<Entity *> &entities) const +EntityRenderCommandData RenderView::buildDrawRenderCommands(const QVector<Entity *> &entities, + int offset, int count) const { - // Note: since many threads can be building render commands - // we need to ensure that the UniformBlockValueBuilder they are using - // is only accessed from the same thread - UniformBlockValueBuilder *builder = new UniformBlockValueBuilder(); - builder->shaderDataManager = m_manager->shaderDataManager(); - builder->textureManager = m_manager->textureManager(); - m_localData.setLocalData(builder); + EntityRenderCommandData commands; - QVector<RenderCommand *> 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>(); @@ -645,135 +644,226 @@ QVector<RenderCommand *> RenderView::buildDrawRenderCommands(const QVector<Entit 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 = new RenderCommand(); - command->m_geometryRenderer = geometryRendererHandle; - command->m_geometry = m_manager->geometryManager()->lookupHandle(geometryRenderer->geometryId()); - - // Project the camera-to-object-center vector onto the camera - // view vector. This gives a depth value suitable as the key - // for BackToFront sorting. - command->m_depth = Vector3D::dotProduct(entity->worldBoundingVolume()->center() - m_data.m_eyePos, m_data.m_eyeViewDir); - command->m_material = materialHandle; + RenderCommand command = {}; + command.m_geometryRenderer = geometryRendererHandle; + command.m_geometry = geometryHandle; + + command.m_material = materialHandle; // For RenderPass based states we use the globally set RenderState // if no renderstates are defined as part of the pass. That means: // RenderPass { renderStates: [] } will use the states defined by // StateSet in the FrameGraph RenderPass *pass = passData.pass; if (pass->hasRenderStates()) { - command->m_stateSet = new RenderStateSet(); - addStatesToRenderStateSet(command->m_stateSet, pass->renderStates(), m_manager->renderStateManager()); + command.m_stateSet = RenderStateSetPtr::create(); + addStatesToRenderStateSet(command.m_stateSet.data(), pass->renderStates(), m_manager->renderStateManager()); if (m_stateSet != nullptr) - command->m_stateSet->merge(m_stateSet); - command->m_changeCost = m_renderer->defaultRenderState()->changeCost(command->m_stateSet); + command.m_stateSet->merge(m_stateSet); + command.m_changeCost = m_renderer->defaultRenderState()->changeCost(command.m_stateSet.data()); } + 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; + } + } - // Pick which lights to take in to account. - // For now decide based on the distance by taking the MAX_LIGHTS closest lights. - // Replace with more sophisticated mechanisms later. - // Copy vector so that we can sort it concurrently and we only want to sort the one for the current command - QVector<LightSource> lightSources = m_lightSources; - if (lightSources.size() > 1) { - const Vector3D entityCenter = entity->worldBoundingVolume()->center(); - std::sort(lightSources.begin(), lightSources.end(), - [&] (const LightSource &a, const LightSource &b) { - const float distA = entityCenter.distanceToPoint(a.entity->worldBoundingVolume()->center()); - const float distB = entityCenter.distanceToPoint(b.entity->worldBoundingVolume()->center()); - return distA < distB; - }); - } + 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; + } + } - ParameterInfoList globalParameters = passData.parameterInfo; - // setShaderAndUniforms can initialize a localData - // make sure this is cleared before we leave this function - setShaderAndUniforms(command, - pass, - globalParameters, - entity, - lightSources.mid(0, std::max(lightSources.size(), MAX_LIGHTS)), - m_environmentLight); - - commands.append(command); + 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)); } } } - // We reset the local data once we are done with it - m_localData.setLocalData(nullptr); - return commands; } -QVector<RenderCommand *> RenderView::buildComputeRenderCommands(const QVector<Entity *> &entities) const +EntityRenderCommandData RenderView::buildComputeRenderCommands(const QVector<Entity *> &entities, + int offset, int count) const { - // Note: since many threads can be building render commands - // we need to ensure that the UniformBlockValueBuilder they are using - // is only accessed from the same thread - UniformBlockValueBuilder *builder = new UniformBlockValueBuilder(); - builder->shaderDataManager = m_manager->shaderDataManager(); - builder->textureManager = m_manager->textureManager(); - m_localData.setLocalData(builder); - // If the RenderView contains only a ComputeDispatch then it cares about // A ComputeDispatch is also implicitely a NoDraw operation // enabled flag // layer component // material/effect/technique/parameters/filters/ - QVector<RenderCommand *> commands; - commands.reserve(entities.size()); - for (Entity *entity : entities) { + EntityRenderCommandData commands; + + commands.reserve(count); + + for (int i = 0; i < count; ++i) { + const int idx = offset + i; + Entity *entity = entities.at(idx); ComputeCommand *computeJob = nullptr; - if ((computeJob = entity->renderComponent<ComputeCommand>()) != nullptr + HComputeCommand computeCommandHandle = entity->componentHandle<ComputeCommand>(); + if ((computeJob = nodeManagers()->computeJobManager()->data(computeCommandHandle)) != nullptr && computeJob->isEnabled()) { - // Note: if frameCount has reached 0 in the previous frame, isEnabled - // would be false - if (computeJob->runType() == QComputeCommand::Manual) - computeJob->updateFrameCount(); - const Qt3DCore::QNodeId materialComponentId = entity->componentUuid<Material>(); const QVector<RenderPassParameterData> renderPassData = m_parameters.value(materialComponentId); // 1 RenderCommand per RenderPass pass on an Entity with a Mesh for (const RenderPassParameterData &passData : renderPassData) { // Add the RenderPass Parameters - RenderCommand *command = new RenderCommand(); + RenderCommand command = {}; RenderPass *pass = passData.pass; if (pass->hasRenderStates()) { - command->m_stateSet = new RenderStateSet(); - addStatesToRenderStateSet(command->m_stateSet, pass->renderStates(), m_manager->renderStateManager()); + command.m_stateSet = RenderStateSetPtr::create(); + addStatesToRenderStateSet(command.m_stateSet.data(), pass->renderStates(), m_manager->renderStateManager()); // Merge per pass stateset with global stateset // so that the local stateset only overrides if (m_stateSet != nullptr) - command->m_stateSet->merge(m_stateSet); - command->m_changeCost = m_renderer->defaultRenderState()->changeCost(command->m_stateSet); + command.m_stateSet->merge(m_stateSet); + command.m_changeCost = m_renderer->defaultRenderState()->changeCost(command.m_stateSet.data()); } + command.m_shader = m_manager->lookupHandle<Shader, ShaderManager, HShader>(pass->shaderProgram()); + command.m_computeCommand = computeCommandHandle; + command.m_type = RenderCommand::Compute; + command.m_workGroups[0] = std::max(m_workGroups[0], computeJob->x()); + command.m_workGroups[1] = std::max(m_workGroups[1], computeJob->y()); + command.m_workGroups[2] = std::max(m_workGroups[2], computeJob->z()); + + commands.push_back(entity, + std::move(command), + std::move(passData)); + } + } + } - command->m_type = RenderCommand::Compute; - command->m_workGroups[0] = std::max(m_workGroups[0], computeJob->x()); - command->m_workGroups[1] = std::max(m_workGroups[1], computeJob->y()); - command->m_workGroups[2] = std::max(m_workGroups[2], computeJob->z()); - - ParameterInfoList globalParameters = passData.parameterInfo; - setShaderAndUniforms(command, - pass, - globalParameters, - entity, - QVector<LightSource>(), - nullptr); - commands.append(command); + return commands; +} + +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 + // is only accessed from the same thread + UniformBlockValueBuilder *builder = new UniformBlockValueBuilder(); + builder->shaderDataManager = m_manager->shaderDataManager(); + builder->textureManager = m_manager->textureManager(); + m_localData.setLocalData(builder); + + 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. + // Replace with more sophisticated mechanisms later. + // Copy vector so that we can sort it concurrently and we only want to sort the one for the current command + QVector<LightSource> lightSources; + EnvironmentLight *environmentLight = nullptr; + + if (command.m_type == RenderCommand::Draw) { + // Project the camera-to-object-center vector onto the camera + // view vector. This gives a depth value suitable as the key + // for BackToFront sorting. + command.m_depth = Vector3D::dotProduct(entity->worldBoundingVolume()->center() - m_data.m_eyePos, m_data.m_eyeViewDir); + + environmentLight = m_environmentLight; + lightSources = m_lightSources; + + if (lightSources.size() > 1) { + const Vector3D entityCenter = entity->worldBoundingVolume()->center(); + std::sort(lightSources.begin(), lightSources.end(), + [&] (const LightSource &a, const LightSource &b) { + const float distA = entityCenter.distanceToPoint(a.entity->worldBoundingVolume()->center()); + const float distB = entityCenter.distanceToPoint(b.entity->worldBoundingVolume()->center()); + return distA < distB; + }); } + lightSources = lightSources.mid(0, std::max(lightSources.size(), MAX_LIGHTS)); + } else { // Compute + // Note: if frameCount has reached 0 in the previous frame, isEnabled + // would be false + ComputeCommand *computeJob = m_manager->computeJobManager()->data(command.m_computeCommand); + if (computeJob->runType() == QComputeCommand::Manual) + computeJob->updateFrameCount(); } + + ParameterInfoList globalParameters = passData.parameterInfo; + // setShaderAndUniforms can initialize a localData + // make sure this is cleared before we leave this function + setShaderAndUniforms(&command, + globalParameters, + entity, + lightSources, + environmentLight); } // We reset the local data once we are done with it m_localData.setLocalData(nullptr); - - return commands; } void RenderView::updateMatrices() @@ -908,7 +998,6 @@ void RenderView::setDefaultUniformBlockShaderDataValue(ShaderParameterPack &unif } void RenderView::setShaderAndUniforms(RenderCommand *command, - RenderPass *rPass, ParameterInfoList ¶meters, Entity *entity, const QVector<LightSource> &activeLightSources, @@ -924,163 +1013,163 @@ void RenderView::setShaderAndUniforms(RenderCommand *command, // For each ParameterBinder in the RenderPass -> create a QUniformPack // Once that works, improve that to try and minimize QUniformPack updates - if (rPass != nullptr) { - // Index Shader by Shader UUID - command->m_shader = m_manager->lookupHandle<Shader, ShaderManager, HShader>(rPass->shaderProgram()); - Shader *shader = m_manager->data<Shader, ShaderManager>(command->m_shader); - if (shader != nullptr && shader->isLoaded()) { - command->m_shaderDna = shader->dna(); - - // Builds the QUniformPack, sets shader standard uniforms and store attributes name / glname bindings - // 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> uniformBlockNamesIds = shader->uniformBlockNamesIds(); - const QVector<int> shaderStorageBlockNamesIds = shader->storageBlockNamesIds(); - const QVector<int> attributeNamesIds = shader->attributeNamesIds(); - - // Set fragData Name and index - // Later on we might want to relink the shader if attachments have changed - // But for now we set them once and for all + // Index Shader by Shader UUID + Shader *shader = m_manager->data<Shader, ShaderManager>(command->m_shader); + if (shader != nullptr && shader->isLoaded()) { + command->m_shaderDna = shader->dna(); + + // Builds the QUniformPack, sets shader standard uniforms and store attributes name / glname bindings + // 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(); + + // Set fragData Name and index + // Later on we might want to relink the shader if attachments have changed + // But for now we set them once and for all + if (!m_renderTarget.isNull() && !shader->isLoaded()) { QHash<QString, int> fragOutputs; - if (!m_renderTarget.isNull() && !shader->isLoaded()) { - const auto atts = m_attachmentPack.attachments(); - for (const Attachment &att : atts) { - if (att.m_point <= QRenderTargetOutput::Color15) - fragOutputs.insert(att.m_name, att.m_point); - } + const auto atts = m_attachmentPack.attachments(); + for (const Attachment &att : atts) { + if (att.m_point <= QRenderTargetOutput::Color15) + fragOutputs.insert(att.m_name, att.m_point); } + // Set frag outputs in the shaders if hash not empty + if (!fragOutputs.isEmpty()) + shader->setFragOutputs(fragOutputs); + } - if (!uniformNamesIds.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)) - setStandardUniformValue(command->m_parameterPack, uniformNameId, uniformNameId, entity, worldTransform); - } - - // Set default attributes - for (const int attributeNameId : attributeNamesIds) - command->m_activeAttributes.push_back(attributeNameId); - - // Parameters remaining could be - // -> uniform scalar / vector - // -> uniform struct / arrays - // -> uniform block / array (4.3) - // -> ssbo block / array (4.3) - - ParameterInfoList::const_iterator it = parameters.cbegin(); - const ParameterInfoList::const_iterator parametersEnd = parameters.cend(); - - while (it != parametersEnd) { - Parameter *param = m_manager->data<Parameter, ParameterManager>(it->handle); - const UniformValue &uniformValue = param->uniformValue(); - if (uniformNamesIds.contains(it->nameId)) { // Parameter is a regular uniform - setUniformValue(command->m_parameterPack, it->nameId, uniformValue); - } else if (uniformBlockNamesIds.indexOf(it->nameId) != -1) { // Parameter is a uniform block - setUniformBlockValue(command->m_parameterPack, shader, shader->uniformBlockForBlockNameId(it->nameId), uniformValue); - } else if (shaderStorageBlockNamesIds.indexOf(it->nameId) != -1) { // Parameters is a SSBO - setShaderStorageValue(command->m_parameterPack, shader, shader->storageBlockForBlockNameId(it->nameId), uniformValue); - } else { // Parameter is a struct - ShaderData *shaderData = nullptr; - if (uniformValue.valueType() == UniformValue::NodeId && - (shaderData = m_manager->shaderDataManager()->lookupResource(*uniformValue.constData<Qt3DCore::QNodeId>())) != nullptr) { - // Try to check if we have a struct or array matching a QShaderData parameter - setDefaultUniformBlockShaderDataValue(command->m_parameterPack, shader, shaderData, StringToInt::lookupString(it->nameId)); - } - // Otherwise: param unused by current shader + 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 : 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 + // -> uniform block / array (4.3) + // -> ssbo block / array (4.3) + + ParameterInfoList::const_iterator it = parameters.cbegin(); + const ParameterInfoList::const_iterator parametersEnd = parameters.cend(); + + while (it != parametersEnd) { + Parameter *param = m_manager->data<Parameter, ParameterManager>(it->handle); + const UniformValue &uniformValue = param->uniformValue(); + if (uniformNamesIds.contains(it->nameId)) { // Parameter is a regular uniform + setUniformValue(command->m_parameterPack, it->nameId, uniformValue); + } else if (uniformBlockNamesIds.indexOf(it->nameId) != -1) { // Parameter is a uniform block + setUniformBlockValue(command->m_parameterPack, shader, shader->uniformBlockForBlockNameId(it->nameId), uniformValue); + } else if (shaderStorageBlockNamesIds.indexOf(it->nameId) != -1) { // Parameters is a SSBO + setShaderStorageValue(command->m_parameterPack, shader, shader->storageBlockForBlockNameId(it->nameId), uniformValue); + } else { // Parameter is a struct + ShaderData *shaderData = nullptr; + if (uniformValue.valueType() == UniformValue::NodeId && + (shaderData = m_manager->shaderDataManager()->lookupResource(*uniformValue.constData<Qt3DCore::QNodeId>())) != nullptr) { + // Try to check if we have a struct or array matching a QShaderData parameter + setDefaultUniformBlockShaderDataValue(command->m_parameterPack, shader, shaderData, StringToInt::lookupString(it->nameId)); } - ++it; + // Otherwise: param unused by current shader } + ++it; + } - // Lights - - int lightIdx = 0; - for (const LightSource &lightSource : activeLightSources) { - if (lightIdx == MAX_LIGHTS) - break; - Entity *lightEntity = lightSource.entity; - const Vector3D worldPos = lightEntity->worldBoundingVolume()->center(); - for (Light *light : lightSource.lights) { - if (!light->isEnabled()) - continue; - - ShaderData *shaderData = m_manager->shaderDataManager()->lookupResource(light->shaderData()); - if (!shaderData) - continue; + // Lights - if (lightIdx == MAX_LIGHTS) - break; + int lightIdx = 0; + for (const LightSource &lightSource : activeLightSources) { + if (lightIdx == MAX_LIGHTS) + break; + Entity *lightEntity = lightSource.entity; + const Matrix4x4 lightWorldTransform = *(lightEntity->worldTransform()); + const Vector3D worldPos = lightWorldTransform * Vector3D(0.0f, 0.0f, 0.0f); + for (Light *light : lightSource.lights) { + if (!light->isEnabled()) + continue; - // Note: implicit conversion of values to UniformValue - setUniformValue(command->m_parameterPack, LIGHT_POSITION_NAMES[lightIdx], worldPos); - setUniformValue(command->m_parameterPack, LIGHT_TYPE_NAMES[lightIdx], int(QAbstractLight::PointLight)); - setUniformValue(command->m_parameterPack, LIGHT_COLOR_NAMES[lightIdx], Vector3D(1.0f, 1.0f, 1.0f)); - setUniformValue(command->m_parameterPack, LIGHT_INTENSITY_NAMES[lightIdx], 0.5f); - - setUniformValue(command->m_parameterPack, LIGHT_POSITION_UNROLL_NAMES[lightIdx], worldPos); - setUniformValue(command->m_parameterPack, LIGHT_TYPE_UNROLL_NAMES[lightIdx], int(QAbstractLight::PointLight)); - setUniformValue(command->m_parameterPack, LIGHT_COLOR_UNROLL_NAMES[lightIdx], Vector3D(1.0f, 1.0f, 1.0f)); - setUniformValue(command->m_parameterPack, LIGHT_INTENSITY_UNROLL_NAMES[lightIdx], 0.5f); - - // There is no risk in doing that even if multithreaded - // since we are sure that a shaderData is unique for a given light - // and won't ever be referenced as a Component either - Matrix4x4 *worldTransform = lightEntity->worldTransform(); - if (worldTransform) - shaderData->updateWorldTransform(*worldTransform); - - setDefaultUniformBlockShaderDataValue(command->m_parameterPack, shader, shaderData, LIGHT_STRUCT_NAMES[lightIdx]); - setDefaultUniformBlockShaderDataValue(command->m_parameterPack, shader, shaderData, LIGHT_STRUCT_UNROLL_NAMES[lightIdx]); - ++lightIdx; - } - } + ShaderData *shaderData = m_manager->shaderDataManager()->lookupResource(light->shaderData()); + if (!shaderData) + continue; - if (uniformNamesIds.contains(LIGHT_COUNT_NAME_ID)) - setUniformValue(command->m_parameterPack, LIGHT_COUNT_NAME_ID, UniformValue(qMax((environmentLight ? 0 : 1), lightIdx))); + if (lightIdx == MAX_LIGHTS) + break; - // If no active light sources and no environment light, add a default light - if (activeLightSources.isEmpty() && !environmentLight) { // Note: implicit conversion of values to UniformValue - setUniformValue(command->m_parameterPack, LIGHT_POSITION_NAMES[0], Vector3D(10.0f, 10.0f, 0.0f)); - setUniformValue(command->m_parameterPack, LIGHT_TYPE_NAMES[0], int(QAbstractLight::PointLight)); - setUniformValue(command->m_parameterPack, LIGHT_COLOR_NAMES[0], Vector3D(1.0f, 1.0f, 1.0f)); - setUniformValue(command->m_parameterPack, LIGHT_INTENSITY_NAMES[0], 0.5f); - - setUniformValue(command->m_parameterPack, LIGHT_POSITION_UNROLL_NAMES[0], Vector3D(10.0f, 10.0f, 0.0f)); - setUniformValue(command->m_parameterPack, LIGHT_TYPE_UNROLL_NAMES[0], int(QAbstractLight::PointLight)); - setUniformValue(command->m_parameterPack, LIGHT_COLOR_UNROLL_NAMES[0], Vector3D(1.0f, 1.0f, 1.0f)); - setUniformValue(command->m_parameterPack, LIGHT_INTENSITY_UNROLL_NAMES[0], 0.5f); + setUniformValue(command->m_parameterPack, LIGHT_POSITION_NAMES[lightIdx], worldPos); + setUniformValue(command->m_parameterPack, LIGHT_TYPE_NAMES[lightIdx], int(QAbstractLight::PointLight)); + setUniformValue(command->m_parameterPack, LIGHT_COLOR_NAMES[lightIdx], Vector3D(1.0f, 1.0f, 1.0f)); + setUniformValue(command->m_parameterPack, LIGHT_INTENSITY_NAMES[lightIdx], 0.5f); + + setUniformValue(command->m_parameterPack, LIGHT_POSITION_UNROLL_NAMES[lightIdx], worldPos); + setUniformValue(command->m_parameterPack, LIGHT_TYPE_UNROLL_NAMES[lightIdx], int(QAbstractLight::PointLight)); + setUniformValue(command->m_parameterPack, LIGHT_COLOR_UNROLL_NAMES[lightIdx], Vector3D(1.0f, 1.0f, 1.0f)); + setUniformValue(command->m_parameterPack, LIGHT_INTENSITY_UNROLL_NAMES[lightIdx], 0.5f); + + + // There is no risk in doing that even if multithreaded + // since we are sure that a shaderData is unique for a given light + // and won't ever be referenced as a Component either + Matrix4x4 *worldTransform = lightEntity->worldTransform(); + if (worldTransform) + shaderData->updateWorldTransform(*worldTransform); + + setDefaultUniformBlockShaderDataValue(command->m_parameterPack, shader, shaderData, LIGHT_STRUCT_NAMES[lightIdx]); + setDefaultUniformBlockShaderDataValue(command->m_parameterPack, shader, shaderData, LIGHT_STRUCT_UNROLL_NAMES[lightIdx]); + ++lightIdx; } + } - // Environment Light - int envLightCount = 0; - if (environmentLight && environmentLight->isEnabled()) { - ShaderData *shaderData = m_manager->shaderDataManager()->lookupResource(environmentLight->shaderData()); - if (shaderData) { - setDefaultUniformBlockShaderDataValue(command->m_parameterPack, shader, shaderData, QStringLiteral("envLight")); - envLightCount = 1; - } - } else { - // with some drivers, samplers (like the envbox sampler) need to be bound even though - // they may not be actually used, otherwise draw calls can fail - static const int irradianceId = StringToInt::lookupId(QLatin1String("envLight.irradiance")); - static const int specularId = StringToInt::lookupId(QLatin1String("envLight.specular")); - setUniformValue(command->m_parameterPack, irradianceId, m_renderer->submissionContext()->maxTextureUnitsCount()); - setUniformValue(command->m_parameterPack, specularId, m_renderer->submissionContext()->maxTextureUnitsCount()); + if (uniformNamesIds.contains(LIGHT_COUNT_NAME_ID)) + setUniformValue(command->m_parameterPack, LIGHT_COUNT_NAME_ID, UniformValue(qMax((environmentLight ? 0 : 1), lightIdx))); + + // If no active light sources and no environment light, add a default light + if (activeLightSources.isEmpty() && !environmentLight) { + // Note: implicit conversion of values to UniformValue + setUniformValue(command->m_parameterPack, LIGHT_POSITION_NAMES[0], Vector3D(10.0f, 10.0f, 0.0f)); + setUniformValue(command->m_parameterPack, LIGHT_TYPE_NAMES[0], int(QAbstractLight::PointLight)); + setUniformValue(command->m_parameterPack, LIGHT_COLOR_NAMES[0], Vector3D(1.0f, 1.0f, 1.0f)); + setUniformValue(command->m_parameterPack, LIGHT_INTENSITY_NAMES[0], 0.5f); + + setUniformValue(command->m_parameterPack, LIGHT_POSITION_UNROLL_NAMES[0], Vector3D(10.0f, 10.0f, 0.0f)); + setUniformValue(command->m_parameterPack, LIGHT_TYPE_UNROLL_NAMES[0], int(QAbstractLight::PointLight)); + setUniformValue(command->m_parameterPack, LIGHT_COLOR_UNROLL_NAMES[0], Vector3D(1.0f, 1.0f, 1.0f)); + setUniformValue(command->m_parameterPack, LIGHT_INTENSITY_UNROLL_NAMES[0], 0.5f); + } + + // Environment Light + int envLightCount = 0; + if (environmentLight && environmentLight->isEnabled()) { + ShaderData *shaderData = m_manager->shaderDataManager()->lookupResource(environmentLight->shaderData()); + if (shaderData) { + setDefaultUniformBlockShaderDataValue(command->m_parameterPack, shader, shaderData, QStringLiteral("envLight")); + envLightCount = 1; } - setUniformValue(command->m_parameterPack, StringToInt::lookupId(QStringLiteral("envLightCount")), envLightCount); + } else { + // with some drivers, samplers (like the envbox sampler) need to be bound even though + // they may not be actually used, otherwise draw calls can fail + static const int irradianceId = StringToInt::lookupId(QLatin1String("envLight.irradiance")); + static const int specularId = StringToInt::lookupId(QLatin1String("envLight.specular")); + setUniformValue(command->m_parameterPack, irradianceId, m_renderer->submissionContext()->maxTextureUnitsCount()); + setUniformValue(command->m_parameterPack, specularId, m_renderer->submissionContext()->maxTextureUnitsCount()); } - // Set frag outputs in the shaders if hash not empty - if (!fragOutputs.isEmpty()) - shader->setFragOutputs(fragOutputs); + setUniformValue(command->m_parameterPack, StringToInt::lookupId(QStringLiteral("envLightCount")), envLightCount); } } - else { - qCWarning(Render::Backend) << Q_FUNC_INFO << "Using default effect as none was provided"; - } } bool RenderView::hasBlitFramebufferInfo() const diff --git a/src/render/renderers/opengl/renderer/renderview_p.h b/src/render/renderers/opengl/renderer/renderview_p.h index 7ebcdb6bd..c7dc37a2c 100644 --- a/src/render/renderers/opengl/renderer/renderview_p.h +++ b/src/render/renderers/opengl/renderer/renderview_p.h @@ -227,10 +227,19 @@ public: RenderPassList passesAndParameters(ParameterInfoList *parameter, Entity *node, bool useDefaultMaterials = true); - QVector<RenderCommand *> buildDrawRenderCommands(const QVector<Entity *> &entities) const; - QVector<RenderCommand *> buildComputeRenderCommands(const QVector<Entity *> &entities) const; - void setCommands(QVector<RenderCommand *> &commands) Q_DECL_NOTHROW { m_commands = commands; } - QVector<RenderCommand *> commands() const Q_DECL_NOTHROW { return m_commands; } + 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, + int offset, int count); + + + void setCommands(const QVector<RenderCommand> &commands) Q_DECL_NOTHROW { m_commands = commands; } + QVector<RenderCommand> &commands() { return m_commands; } + QVector<RenderCommand> commands() const { return m_commands; } void setAttachmentPack(const AttachmentPack &pack) { m_attachmentPack = pack; } const AttachmentPack &attachmentPack() const { return m_attachmentPack; } @@ -291,7 +300,6 @@ public: private: void setShaderAndUniforms(RenderCommand *command, - RenderPass *pass, ParameterInfoList ¶meters, Entity *entity, const QVector<LightSource> &activeLightSources, @@ -331,10 +339,7 @@ private: QVector<Qt3DCore::QNodeId> m_insertFenceIds; QVector<QWaitFenceData> m_waitFences; - // We do not use pointers to RenderNodes or Drawable's here so that the - // render aspect is free to change the drawables on the next frame whilst - // the render thread is submitting these commands. - QVector<RenderCommand *> m_commands; + QVector<RenderCommand> m_commands; mutable QVector<LightSource> m_lightSources; EnvironmentLight *m_environmentLight; diff --git a/src/render/renderers/opengl/renderer/renderviewbuilder.cpp b/src/render/renderers/opengl/renderer/renderviewbuilder.cpp index 83fab301a..8f1b17119 100644 --- a/src/render/renderers/opengl/renderer/renderviewbuilder.cpp +++ b/src/render/renderers/opengl/renderer/renderviewbuilder.cpp @@ -50,18 +50,73 @@ 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 { -class SyncRenderViewCommandBuilders +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: - explicit SyncRenderViewCommandBuilders(const RenderViewInitializerJobPtr &renderViewJob, - const QVector<RenderViewBuilderJobPtr> &renderViewBuilderJobs, - Renderer *renderer) + explicit SyncPreCommandBuilding(RenderViewInitializerJobPtr renderViewInitializerJob, + const QVector<RenderViewCommandBuilderJobPtr> &renderViewCommandBuilderJobs, + Renderer *renderer, + FrameGraphNode *leafNode) + : m_renderViewInitializer(renderViewInitializerJob) + , m_renderViewCommandBuilderJobs(renderViewCommandBuilderJobs) + , m_renderer(renderer) + , m_leafNode(leafNode) + { + } + + void operator()() + { + // Split commands to build among jobs + QMutexLocker lock(m_renderer->cache()->mutex()); + // Rebuild RenderCommands for all entities in RV (ignoring filtering) + RendererCache *cache = m_renderer->cache(); + const RendererCache::LeafNodeData &dataCacheForLeaf = cache->leafNodeCache[m_leafNode]; + RenderView *rv = m_renderViewInitializer->renderView(); + const auto entities = !rv->isCompute() ? cache->renderableEntities : cache->computeEntities; + + rv->setMaterialParameterTable(dataCacheForLeaf.materialParameterGatherer); + + lock.unlock(); + + // 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); + const int count = (i == m - 1) ? entities.size() - (i * idealPacketSize) : idealPacketSize; + renderViewCommandBuilder->setEntities(entities, i * idealPacketSize, count); + } + } + +private: + RenderViewInitializerJobPtr m_renderViewInitializer; + QVector<RenderViewCommandBuilderJobPtr> m_renderViewCommandBuilderJobs; + Renderer *m_renderer; + FrameGraphNode *m_leafNode; +}; + +class SyncRenderViewPostCommandUpdate +{ +public: + explicit SyncRenderViewPostCommandUpdate(const RenderViewInitializerJobPtr &renderViewJob, + const QVector<RenderViewCommandUpdaterJobPtr> &renderViewCommandUpdateJobs, + Renderer *renderer) : m_renderViewJob(renderViewJob) - , m_renderViewBuilderJobs(renderViewBuilderJobs) + , m_renderViewCommandUpdaterJobs(renderViewCommandUpdateJobs) , m_renderer(renderer) {} @@ -70,20 +125,16 @@ public: // Append all the commands and sort them RenderView *rv = m_renderViewJob->renderView(); - int totalCommandCount = 0; - for (const auto &renderViewCommandBuilder : qAsConst(m_renderViewBuilderJobs)) - totalCommandCount += renderViewCommandBuilder->commands().size(); - - QVector<RenderCommand *> commands; - commands.reserve(totalCommandCount); + const EntityRenderCommandDataPtr commandData = m_renderViewCommandUpdaterJobs.first()->renderables(); - // Reduction - for (const auto &renderViewCommandBuilder : qAsConst(m_renderViewBuilderJobs)) - commands += std::move(renderViewCommandBuilder->commands()); - rv->setCommands(commands); + if (commandData) { + const QVector<RenderCommand> commands = std::move(commandData->commands); + rv->setCommands(commands); - // 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()); @@ -91,15 +142,15 @@ public: private: RenderViewInitializerJobPtr m_renderViewJob; - QVector<RenderViewBuilderJobPtr> m_renderViewBuilderJobs; + QVector<RenderViewCommandUpdaterJobPtr> m_renderViewCommandUpdaterJobs; Renderer *m_renderer; }; -class SyncFrustumCulling +class SyncPreFrustumCulling { public: - explicit SyncFrustumCulling(const RenderViewInitializerJobPtr &renderViewJob, - const FrustumCullingJobPtr &frustumCulling) + explicit SyncPreFrustumCulling(const RenderViewInitializerJobPtr &renderViewJob, + const FrustumCullingJobPtr &frustumCulling) : m_renderViewJob(renderViewJob) , m_frustumCullingJob(frustumCulling) {} @@ -120,21 +171,23 @@ private: FrustumCullingJobPtr m_frustumCullingJob; }; -class SyncRenderViewInitialization +class SyncRenderViewPostInitialization { public: - explicit SyncRenderViewInitialization(const RenderViewInitializerJobPtr &renderViewJob, - const FrustumCullingJobPtr &frustumCullingJob, - const FilterLayerEntityJobPtr &filterEntityByLayerJob, - const FilterProximityDistanceJobPtr &filterProximityJob, - const QVector<MaterialParameterGathererJobPtr> &materialGathererJobs, - const QVector<RenderViewBuilderJobPtr> &renderViewBuilderJobs) + explicit SyncRenderViewPostInitialization(const RenderViewInitializerJobPtr &renderViewJob, + const FrustumCullingJobPtr &frustumCullingJob, + const FilterLayerEntityJobPtr &filterEntityByLayerJob, + const FilterProximityDistanceJobPtr &filterProximityJob, + const QVector<MaterialParameterGathererJobPtr> &materialGathererJobs, + const QVector<RenderViewCommandUpdaterJobPtr> &renderViewCommandUpdaterJobs, + const QVector<RenderViewCommandBuilderJobPtr> &renderViewCommandBuilderJobs) : m_renderViewJob(renderViewJob) , m_frustumCullingJob(frustumCullingJob) , m_filterEntityByLayerJob(filterEntityByLayerJob) , m_filterProximityJob(filterProximityJob) , m_materialGathererJobs(materialGathererJobs) - , m_renderViewBuilderJobs(renderViewBuilderJobs) + , m_renderViewCommandUpdaterJobs(renderViewCommandUpdaterJobs) + , m_renderViewCommandBuilderJobs(renderViewCommandBuilderJobs) {} void operator()() @@ -154,8 +207,10 @@ public: materialGatherer->setTechniqueFilter(const_cast<TechniqueFilter *>(rv->techniqueFilter())); } - // Command builders - for (const auto &renderViewCommandBuilder : qAsConst(m_renderViewBuilderJobs)) + // Command builders and updates + for (const auto &renderViewCommandUpdater : qAsConst(m_renderViewCommandUpdaterJobs)) + renderViewCommandUpdater->setRenderView(rv); + for (const auto &renderViewCommandBuilder : qAsConst(m_renderViewCommandBuilderJobs)) renderViewCommandBuilder->setRenderView(rv); // Set whether frustum culling is enabled or not @@ -168,26 +223,31 @@ private: FilterLayerEntityJobPtr m_filterEntityByLayerJob; FilterProximityDistanceJobPtr m_filterProximityJob; QVector<MaterialParameterGathererJobPtr> m_materialGathererJobs; - QVector<RenderViewBuilderJobPtr> m_renderViewBuilderJobs; + QVector<RenderViewCommandUpdaterJobPtr> m_renderViewCommandUpdaterJobs; + QVector<RenderViewCommandBuilderJobPtr> m_renderViewCommandBuilderJobs; }; -class SyncRenderCommandBuilding +class SyncRenderViewPreCommandUpdate { public: - explicit SyncRenderCommandBuilding(const RenderViewInitializerJobPtr &renderViewJob, - const FrustumCullingJobPtr &frustumCullingJob, - const FilterProximityDistanceJobPtr &filterProximityJob, - const QVector<MaterialParameterGathererJobPtr> &materialGathererJobs, - const QVector<RenderViewBuilderJobPtr> &renderViewBuilderJobs, - Renderer *renderer, - FrameGraphNode *leafNode) + explicit SyncRenderViewPreCommandUpdate(const RenderViewInitializerJobPtr &renderViewJob, + const FrustumCullingJobPtr &frustumCullingJob, + const FilterProximityDistanceJobPtr &filterProximityJob, + const QVector<MaterialParameterGathererJobPtr> &materialGathererJobs, + const QVector<RenderViewCommandUpdaterJobPtr> &renderViewCommandUpdaterJobs, + const QVector<RenderViewCommandBuilderJobPtr> &renderViewCommandBuilderJobs, + Renderer *renderer, + FrameGraphNode *leafNode, + bool fullCommandRebuild) : m_renderViewJob(renderViewJob) , m_frustumCullingJob(frustumCullingJob) , m_filterProximityJob(filterProximityJob) , m_materialGathererJobs(materialGathererJobs) - , m_renderViewBuilderJobs(renderViewBuilderJobs) + , m_renderViewCommandUpdaterJobs(renderViewCommandUpdaterJobs) + , m_renderViewCommandBuilderJobs(renderViewCommandBuilderJobs) , m_renderer(renderer) , m_leafNode(leafNode) + , m_fullRebuild(fullCommandRebuild) {} void operator()() @@ -197,21 +257,44 @@ public: RenderView *rv = m_renderViewJob->renderView(); if (!rv->noDraw()) { - QVector<Entity *> renderableEntities; - const bool isDraw = !rv->isCompute(); - QMutexLocker lock(m_renderer->cache()->mutex()); - const auto& cacheData = m_renderer->cache()->leafNodeCache.value(m_leafNode); + ///////// CACHE LOCKED //////////// + // Retrieve Data from Cache + RendererCache *cache = m_renderer->cache(); + QMutexLocker lock(cache->mutex()); + Q_ASSERT(cache->leafNodeCache.contains(m_leafNode)); - if (isDraw) - renderableEntities = cacheData.renderableEntities; - else - renderableEntities = cacheData.computeEntities; - - const QVector<Entity *> filteredEntities = cacheData.filterEntitiesByLayer; - QVector<LightSource> lightSources = cacheData.gatheredLights; - rv->setEnvironmentLight(cacheData.environmentLight); + const bool isDraw = !rv->isCompute(); + const RendererCache::LeafNodeData &dataCacheForLeaf = cache->leafNodeCache[m_leafNode]; + + // Rebuild RenderCommands if required + // This should happen fairly infrequently (FrameGraph Change, Geometry/Material change) + // and allow to skip that step most of the time + if (m_fullRebuild) { + EntityRenderCommandData commandData; + // Reduction + { + int totalCommandCount = 0; + for (const RenderViewCommandBuilderJobPtr &renderViewCommandBuilder : qAsConst(m_renderViewCommandBuilderJobs)) + totalCommandCount += renderViewCommandBuilder->commandData().size(); + commandData.reserve(totalCommandCount); + for (const RenderViewCommandBuilderJobPtr &renderViewCommandBuilder : qAsConst(m_renderViewCommandBuilderJobs)) + 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; + rv->setMaterialParameterTable(dataCacheForLeaf.materialParameterGatherer); + rv->setEnvironmentLight(cache->environmentLight); lock.unlock(); + ///////// END OF CACHE LOCKED //////////// // Filter out entities that weren't selected by the layer filters // Remove all entities from the compute and renderable vectors that aren't in the filtered layer vector @@ -229,22 +312,52 @@ public: if (rv->frustumCulling()) renderableEntities = RenderViewBuilder::entitiesInSubset(renderableEntities, m_frustumCullingJob->visibleEntities()); // Filter out entities which didn't satisfy proximity filtering - renderableEntities = RenderViewBuilder::entitiesInSubset(renderableEntities, m_filterProximityJob->filteredEntities()); + if (!rv->proximityFilterIds().empty()) + renderableEntities = RenderViewBuilder::entitiesInSubset(renderableEntities, m_filterProximityJob->filteredEntities()); } - // Split among the number of command builders - int i = 0; - const int m = RenderViewBuilder::optimalJobCount() - 1; - const int packetSize = renderableEntities.size() / RenderViewBuilder::optimalJobCount(); - while (i < m) { - const RenderViewBuilderJobPtr renderViewCommandBuilder = m_renderViewBuilderJobs.at(i); - renderViewCommandBuilder->setRenderables(renderableEntities.mid(i * packetSize, packetSize)); - ++i; + // Early return in case we have nothing to filter + if (renderableEntities.size() == 0) + return; + + // Filter out Render commands for which the Entity wasn't selected because + // of frustum, proximity or layer filtering + EntityRenderCommandDataPtr filteredCommandData = EntityRenderCommandDataPtr::create(); + 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); + const auto eEnd = std::cend(renderableEntities); + int cIt = 0; + const int cEnd = commandData.size(); + + while (eIt != eEnd) { + const Entity *targetEntity = *eIt; + // Advance until we have commands whose Entity has a lower address + // than the selected filtered entity + while (cIt != cEnd && commandData.entities.at(cIt) < targetEntity) + ++cIt; + + // 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)); + ++cIt; + } + ++eIt; } - m_renderViewBuilderJobs.at(i)->setRenderables(renderableEntities.mid(i * packetSize, packetSize + renderableEntities.size() % (m + 1))); - { - QMutexLocker rendererCacheLock(m_renderer->cache()->mutex()); - rv->setMaterialParameterTable(m_renderer->cache()->leafNodeCache.value(m_leafNode).materialParameterGatherer); + + // Split among the number of command builders + // 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); + const int count = (i == m - 1) ? filteredCommandData->size() - (i * idealPacketSize) : idealPacketSize; + renderViewCommandBuilder->setRenderables(filteredCommandData, i * idealPacketSize, count); } } } @@ -254,9 +367,11 @@ private: FrustumCullingJobPtr m_frustumCullingJob; FilterProximityDistanceJobPtr m_filterProximityJob; QVector<MaterialParameterGathererJobPtr> m_materialGathererJobs; - QVector<RenderViewBuilderJobPtr> m_renderViewBuilderJobs; + QVector<RenderViewCommandUpdaterJobPtr> m_renderViewCommandUpdaterJobs; + QVector<RenderViewCommandBuilderJobPtr> m_renderViewCommandBuilderJobs; Renderer *m_renderer; FrameGraphNode *m_leafNode; + bool m_fullRebuild; }; class SetClearDrawBufferIndex @@ -335,84 +450,6 @@ private: FrameGraphNode *m_leafNode; }; -class SyncLightsGatherer -{ -public: - explicit SyncLightsGatherer(LightGathererPtr gatherJob, - Renderer *renderer, - FrameGraphNode *leafNode) - : m_gatherJob(gatherJob) - , m_renderer(renderer) - , m_leafNode(leafNode) - { - } - - void operator()() - { - QMutexLocker lock(m_renderer->cache()->mutex()); - RendererCache::LeafNodeData &dataCacheForLeaf = m_renderer->cache()->leafNodeCache[m_leafNode]; - dataCacheForLeaf.gatheredLights = m_gatherJob->lights(); - dataCacheForLeaf.environmentLight = m_gatherJob->takeEnvironmentLight(); - } - -private: - LightGathererPtr m_gatherJob; - Renderer *m_renderer; - FrameGraphNode *m_leafNode; -}; - -class SyncRenderableEntities -{ -public: - explicit SyncRenderableEntities(RenderableEntityFilterPtr gatherJob, - Renderer *renderer, - FrameGraphNode *leafNode) - : m_gatherJob(gatherJob) - , m_renderer(renderer) - , m_leafNode(leafNode) - { - } - - void operator()() - { - QMutexLocker lock(m_renderer->cache()->mutex()); - RendererCache::LeafNodeData &dataCacheForLeaf = m_renderer->cache()->leafNodeCache[m_leafNode]; - dataCacheForLeaf.renderableEntities = m_gatherJob->filteredEntities(); - std::sort(dataCacheForLeaf.renderableEntities.begin(), dataCacheForLeaf.renderableEntities.end()); - } - -private: - RenderableEntityFilterPtr m_gatherJob; - Renderer *m_renderer; - FrameGraphNode *m_leafNode; -}; - -class SyncComputableEntities -{ -public: - explicit SyncComputableEntities(ComputableEntityFilterPtr gatherJob, - Renderer *renderer, - FrameGraphNode *leafNode) - : m_gatherJob(gatherJob) - , m_renderer(renderer) - , m_leafNode(leafNode) - { - } - - void operator()() - { - QMutexLocker lock(m_renderer->cache()->mutex()); - RendererCache::LeafNodeData &dataCacheForLeaf = m_renderer->cache()->leafNodeCache[m_leafNode]; - dataCacheForLeaf.computeEntities = m_gatherJob->filteredEntities(); - std::sort(dataCacheForLeaf.computeEntities.begin(), dataCacheForLeaf.computeEntities.end()); - } - -private: - ComputableEntityFilterPtr m_gatherJob; - Renderer *m_renderer; - FrameGraphNode *m_leafNode; -}; - } // anonymous RenderViewBuilder::RenderViewBuilder(Render::FrameGraphNode *leafNode, int renderViewIndex, Renderer *renderer) @@ -421,13 +458,11 @@ RenderViewBuilder::RenderViewBuilder(Render::FrameGraphNode *leafNode, int rende , m_renderer(renderer) , m_layerCacheNeedsToBeRebuilt(false) , m_materialGathererCacheNeedsToBeRebuilt(false) - , m_lightsCacheNeedsToBeRebuilt(false) - , m_renderableCacheNeedsToBeRebuilt(false) - , m_computableCacheNeedsToBeRebuilt(false) + , m_renderCommandCacheNeedsToBeRebuilt(false) , m_renderViewJob(RenderViewInitializerJobPtr::create()) , m_filterEntityByLayerJob() , m_frustumCullingJob(new Render::FrustumCullingJob()) - , m_syncFrustumCullingJob(SynchronizerJobPtr::create(SyncFrustumCulling(m_renderViewJob, m_frustumCullingJob), JobTypes::SyncFrustumCulling)) + , m_syncPreFrustumCullingJob(SynchronizerJobPtr::create(SyncPreFrustumCulling(m_renderViewJob, m_frustumCullingJob), JobTypes::SyncFrustumCulling)) , m_setClearDrawBufferIndexJob(SynchronizerJobPtr::create(SetClearDrawBufferIndex(m_renderViewJob), JobTypes::ClearBufferDrawIndex)) , m_syncFilterEntityByLayerJob() , m_filterProximityJob(Render::FilterProximityDistanceJobPtr::create()) @@ -444,54 +479,49 @@ FilterLayerEntityJobPtr RenderViewBuilder::filterEntityByLayerJob() const return m_filterEntityByLayerJob; } -LightGathererPtr RenderViewBuilder::lightGathererJob() const -{ - return m_lightGathererJob; -} - -RenderableEntityFilterPtr RenderViewBuilder::renderableEntityFilterJob() const +FrustumCullingJobPtr RenderViewBuilder::frustumCullingJob() const { - return m_renderableEntityFilterJob; + return m_frustumCullingJob; } -ComputableEntityFilterPtr RenderViewBuilder::computableEntityFilterJob() const +QVector<RenderViewCommandUpdaterJobPtr> RenderViewBuilder::renderViewCommandUpdaterJobs() const { - return m_computableEntityFilterJob; + return m_renderViewCommandUpdaterJobs; } -FrustumCullingJobPtr RenderViewBuilder::frustumCullingJob() const +QVector<RenderViewCommandBuilderJobPtr> RenderViewBuilder::renderViewCommandBuilderJobs() const { - return m_frustumCullingJob; + return m_renderViewCommandBuilderJobs; } -QVector<RenderViewBuilderJobPtr> RenderViewBuilder::renderViewBuilderJobs() const +QVector<MaterialParameterGathererJobPtr> RenderViewBuilder::materialGathererJobs() const { - return m_renderViewBuilderJobs; + return m_materialGathererJobs; } -QVector<MaterialParameterGathererJobPtr> RenderViewBuilder::materialGathererJobs() const +SynchronizerJobPtr RenderViewBuilder::syncRenderViewPostInitializationJob() const { - return m_materialGathererJobs; + return m_syncRenderViewPostInitializationJob; } -SynchronizerJobPtr RenderViewBuilder::syncRenderViewInitializationJob() const +SynchronizerJobPtr RenderViewBuilder::syncPreFrustumCullingJob() const { - return m_syncRenderViewInitializationJob; + return m_syncPreFrustumCullingJob; } -SynchronizerJobPtr RenderViewBuilder::syncFrustumCullingJob() const +SynchronizerJobPtr RenderViewBuilder::syncRenderViewPreCommandBuildingJob() const { - return m_syncFrustumCullingJob; + return m_syncRenderViewPreCommandBuildingJob; } -SynchronizerJobPtr RenderViewBuilder::syncRenderCommandBuildingJob() const +SynchronizerJobPtr RenderViewBuilder::syncRenderViewPreCommandUpdateJob() const { - return m_syncRenderCommandBuildingJob; + return m_syncRenderViewPreCommandUpdateJob; } -SynchronizerJobPtr RenderViewBuilder::syncRenderViewCommandBuildersJob() const +SynchronizerJobPtr RenderViewBuilder::syncRenderViewPostCommandUpdateJob() const { - return m_syncRenderViewCommandBuildersJob; + return m_syncRenderViewPostCommandUpdateJob; } SynchronizerJobPtr RenderViewBuilder::setClearDrawBufferIndexJob() const @@ -517,37 +547,21 @@ FilterProximityDistanceJobPtr RenderViewBuilder::filterProximityJob() const void RenderViewBuilder::prepareJobs() { // Init what we can here - EntityManager *entityManager = m_renderer->nodeManagers()->renderNodesManager(); m_filterProximityJob->setManager(m_renderer->nodeManagers()); m_frustumCullingJob->setRoot(m_renderer->sceneRoot()); - if (m_lightsCacheNeedsToBeRebuilt) { - m_lightGathererJob = Render::LightGathererPtr::create(); - m_lightGathererJob->setManager(entityManager); - - m_cacheLightsJob = SynchronizerJobPtr::create(SyncLightsGatherer(m_lightGathererJob, m_renderer, m_leafNode), - JobTypes::EntityComponentTypeFiltering); - m_cacheLightsJob->addDependency(m_lightGathererJob); - } - - if (m_renderableCacheNeedsToBeRebuilt) { - m_renderableEntityFilterJob = RenderableEntityFilterPtr::create(); - m_renderableEntityFilterJob->setManager(entityManager); - - m_cacheRenderableEntitiesJob = SynchronizerJobPtr::create( - SyncRenderableEntities(m_renderableEntityFilterJob, m_renderer, m_leafNode), - JobTypes::EntityComponentTypeFiltering); - m_cacheRenderableEntitiesJob->addDependency(m_renderableEntityFilterJob); - } - - if (m_computableCacheNeedsToBeRebuilt) { - m_computableEntityFilterJob = ComputableEntityFilterPtr::create(); - m_computableEntityFilterJob->setManager(entityManager); + if (m_renderCommandCacheNeedsToBeRebuilt) { - m_cacheComputableEntitiesJob = SynchronizerJobPtr::create( - SyncComputableEntities(m_computableEntityFilterJob, m_renderer, m_leafNode), - JobTypes::EntityComponentTypeFiltering); - m_cacheComputableEntitiesJob->addDependency(m_computableEntityFilterJob); + m_renderViewCommandBuilderJobs.reserve(RenderViewBuilder::m_optimalParallelJobCount); + for (auto i = 0; i < RenderViewBuilder::m_optimalParallelJobCount; ++i) { + auto renderViewCommandBuilder = Render::RenderViewCommandBuilderJobPtr::create(); + m_renderViewCommandBuilderJobs.push_back(renderViewCommandBuilder); + } + m_syncRenderViewPreCommandBuildingJob = SynchronizerJobPtr::create(SyncPreCommandBuilding(m_renderViewJob, + m_renderViewCommandBuilderJobs, + m_renderer, + m_leafNode), + JobTypes::SyncRenderViewPreCommandBuilding); } m_renderViewJob->setRenderer(m_renderer); @@ -556,12 +570,11 @@ void RenderViewBuilder::prepareJobs() // RenderCommand building is the most consuming task -> split it // Estimate the number of jobs to create based on the number of entities - m_renderViewBuilderJobs.reserve(RenderViewBuilder::m_optimalParallelJobCount); + m_renderViewCommandUpdaterJobs.reserve(RenderViewBuilder::m_optimalParallelJobCount); for (auto i = 0; i < RenderViewBuilder::m_optimalParallelJobCount; ++i) { - auto renderViewCommandBuilder = Render::RenderViewBuilderJobPtr::create(); - renderViewCommandBuilder->setIndex(m_renderViewIndex); - renderViewCommandBuilder->setRenderer(m_renderer); - m_renderViewBuilderJobs.push_back(renderViewCommandBuilder); + auto renderViewCommandUpdater = Render::RenderViewCommandUpdaterJobPtr::create(); + renderViewCommandUpdater->setRenderer(m_renderer); + m_renderViewCommandUpdaterJobs.push_back(renderViewCommandUpdater); } if (m_materialGathererCacheNeedsToBeRebuilt) { @@ -582,7 +595,7 @@ void RenderViewBuilder::prepareJobs() m_syncMaterialGathererJob = SynchronizerJobPtr::create(SyncMaterialParameterGatherer(m_materialGathererJobs, m_renderer, m_leafNode), - JobTypes::SyncMaterialGatherer); + JobTypes::SyncMaterialGatherer); } if (m_layerCacheNeedsToBeRebuilt) { @@ -594,34 +607,37 @@ void RenderViewBuilder::prepareJobs() JobTypes::SyncFilterEntityByLayer); } - m_syncRenderCommandBuildingJob = SynchronizerJobPtr::create(SyncRenderCommandBuilding(m_renderViewJob, - m_frustumCullingJob, - m_filterProximityJob, - m_materialGathererJobs, - m_renderViewBuilderJobs, - m_renderer, - m_leafNode), - JobTypes::SyncRenderViewCommandBuilding); - - m_syncRenderViewCommandBuildersJob = SynchronizerJobPtr::create(SyncRenderViewCommandBuilders(m_renderViewJob, - m_renderViewBuilderJobs, - m_renderer), - JobTypes::SyncRenderViewCommandBuilder); - - m_syncRenderViewInitializationJob = SynchronizerJobPtr::create(SyncRenderViewInitialization(m_renderViewJob, - m_frustumCullingJob, - m_filterEntityByLayerJob, - m_filterProximityJob, - m_materialGathererJobs, - m_renderViewBuilderJobs), - JobTypes::SyncRenderViewInitialization); + m_syncRenderViewPreCommandUpdateJob = SynchronizerJobPtr::create(SyncRenderViewPreCommandUpdate(m_renderViewJob, + m_frustumCullingJob, + m_filterProximityJob, + m_materialGathererJobs, + m_renderViewCommandUpdaterJobs, + m_renderViewCommandBuilderJobs, + m_renderer, + m_leafNode, + m_renderCommandCacheNeedsToBeRebuilt), + JobTypes::SyncRenderViewPreCommandUpdate); + + m_syncRenderViewPostCommandUpdateJob = SynchronizerJobPtr::create(SyncRenderViewPostCommandUpdate(m_renderViewJob, + m_renderViewCommandUpdaterJobs, + m_renderer), + JobTypes::SyncRenderViewPostCommandUpdate); + + m_syncRenderViewPostInitializationJob = SynchronizerJobPtr::create(SyncRenderViewPostInitialization(m_renderViewJob, + m_frustumCullingJob, + m_filterEntityByLayerJob, + m_filterProximityJob, + m_materialGathererJobs, + m_renderViewCommandUpdaterJobs, + m_renderViewCommandBuilderJobs), + JobTypes::SyncRenderViewInitialization); } QVector<Qt3DCore::QAspectJobPtr> RenderViewBuilder::buildJobHierachy() const { QVector<Qt3DCore::QAspectJobPtr> jobs; - jobs.reserve(m_materialGathererJobs.size() + m_renderViewBuilderJobs.size() + 11); + jobs.reserve(m_materialGathererJobs.size() + m_renderViewCommandUpdaterJobs.size() + 11); // Set dependencies @@ -629,97 +645,97 @@ QVector<Qt3DCore::QAspectJobPtr> RenderViewBuilder::buildJobHierachy() const // TODO: Maybe only update skinning palettes for non-culled entities m_renderViewJob->addDependency(m_renderer->updateSkinningPaletteJob()); - m_syncFrustumCullingJob->addDependency(m_renderer->updateWorldTransformJob()); - m_syncFrustumCullingJob->addDependency(m_renderer->updateShaderDataTransformJob()); - m_syncFrustumCullingJob->addDependency(m_syncRenderViewInitializationJob); + m_syncPreFrustumCullingJob->addDependency(m_renderer->updateWorldTransformJob()); + m_syncPreFrustumCullingJob->addDependency(m_renderer->updateShaderDataTransformJob()); + m_syncPreFrustumCullingJob->addDependency(m_syncRenderViewPostInitializationJob); m_frustumCullingJob->addDependency(m_renderer->expandBoundingVolumeJob()); - m_frustumCullingJob->addDependency(m_syncFrustumCullingJob); + m_frustumCullingJob->addDependency(m_syncPreFrustumCullingJob); - m_setClearDrawBufferIndexJob->addDependency(m_syncRenderViewInitializationJob); + m_setClearDrawBufferIndexJob->addDependency(m_syncRenderViewPostInitializationJob); - m_syncRenderViewInitializationJob->addDependency(m_renderViewJob); + m_syncRenderViewPostInitializationJob->addDependency(m_renderViewJob); m_filterProximityJob->addDependency(m_renderer->expandBoundingVolumeJob()); - m_filterProximityJob->addDependency(m_syncRenderViewInitializationJob); + m_filterProximityJob->addDependency(m_syncRenderViewPostInitializationJob); - m_syncRenderCommandBuildingJob->addDependency(m_syncRenderViewInitializationJob); - m_syncRenderCommandBuildingJob->addDependency(m_filterProximityJob); - m_syncRenderCommandBuildingJob->addDependency(m_frustumCullingJob); + m_syncRenderViewPreCommandUpdateJob->addDependency(m_syncRenderViewPostInitializationJob); + m_syncRenderViewPreCommandUpdateJob->addDependency(m_filterProximityJob); + m_syncRenderViewPreCommandUpdateJob->addDependency(m_frustumCullingJob); // Ensure the RenderThread won't be able to process dirtyResources // before they have been completely gathered - m_syncRenderCommandBuildingJob->addDependency(m_renderer->introspectShadersJob()); - m_syncRenderCommandBuildingJob->addDependency(m_renderer->bufferGathererJob()); - m_syncRenderCommandBuildingJob->addDependency(m_renderer->textureGathererJob()); - - for (const auto &renderViewCommandBuilder : qAsConst(m_renderViewBuilderJobs)) { - renderViewCommandBuilder->addDependency(m_syncRenderCommandBuildingJob); - m_syncRenderViewCommandBuildersJob->addDependency(renderViewCommandBuilder); + m_syncRenderViewPreCommandUpdateJob->addDependency(m_renderer->introspectShadersJob()); + m_syncRenderViewPreCommandUpdateJob->addDependency(m_renderer->bufferGathererJob()); + m_syncRenderViewPreCommandUpdateJob->addDependency(m_renderer->textureGathererJob()); + m_syncRenderViewPreCommandUpdateJob->addDependency(m_renderer->cacheLightJob()); + + for (const auto &renderViewCommandUpdater : qAsConst(m_renderViewCommandUpdaterJobs)) { + renderViewCommandUpdater->addDependency(m_syncRenderViewPreCommandUpdateJob); + m_syncRenderViewPostCommandUpdateJob->addDependency(renderViewCommandUpdater); } - m_renderer->frameCleanupJob()->addDependency(m_syncRenderViewCommandBuildersJob); + m_renderer->frameCleanupJob()->addDependency(m_syncRenderViewPostCommandUpdateJob); m_renderer->frameCleanupJob()->addDependency(m_setClearDrawBufferIndexJob); // Add jobs jobs.push_back(m_renderViewJob); // Step 1 - if (m_lightsCacheNeedsToBeRebuilt) { - jobs.push_back(m_lightGathererJob); // Step 1 - jobs.push_back(m_cacheLightsJob); - m_syncRenderCommandBuildingJob->addDependency(m_cacheLightsJob); - } + jobs.push_back(m_syncRenderViewPostInitializationJob); // Step 2 - if (m_renderableCacheNeedsToBeRebuilt) { - jobs.push_back(m_renderableEntityFilterJob); // Step 1 - jobs.push_back(m_cacheRenderableEntitiesJob); - m_syncRenderCommandBuildingJob->addDependency(m_cacheRenderableEntitiesJob); - } + if (m_renderCommandCacheNeedsToBeRebuilt) { // Step 3 + m_syncRenderViewPreCommandBuildingJob->addDependency(m_renderer->cacheComputableEntitiesJob()); + m_syncRenderViewPreCommandBuildingJob->addDependency(m_renderer->cacheRenderableEntitiesJob()); + m_syncRenderViewPreCommandBuildingJob->addDependency(m_syncRenderViewPostInitializationJob); - if (m_computableCacheNeedsToBeRebuilt) { - // Note: do it only if OpenGL 4.3+ available - jobs.push_back(m_computableEntityFilterJob); // Step 1 - jobs.push_back(m_cacheComputableEntitiesJob); - m_syncRenderCommandBuildingJob->addDependency(m_cacheComputableEntitiesJob); - } + if (m_materialGathererCacheNeedsToBeRebuilt) + m_syncRenderViewPreCommandBuildingJob->addDependency(m_syncMaterialGathererJob); + + jobs.push_back(m_syncRenderViewPreCommandBuildingJob); - jobs.push_back(m_syncRenderViewInitializationJob); // Step 2 + for (const auto &renderViewCommandBuilder : qAsConst(m_renderViewCommandBuilderJobs)) { + renderViewCommandBuilder->addDependency(m_syncRenderViewPreCommandBuildingJob); + m_syncRenderViewPreCommandUpdateJob->addDependency(renderViewCommandBuilder); + jobs.push_back(renderViewCommandBuilder); + } + } if (m_layerCacheNeedsToBeRebuilt) { m_filterEntityByLayerJob->addDependency(m_renderer->updateEntityLayersJob()); - m_filterEntityByLayerJob->addDependency(m_syncRenderViewInitializationJob); + m_filterEntityByLayerJob->addDependency(m_syncRenderViewPostInitializationJob); m_filterEntityByLayerJob->addDependency(m_renderer->updateTreeEnabledJob()); m_syncFilterEntityByLayerJob->addDependency(m_filterEntityByLayerJob); - m_syncRenderCommandBuildingJob->addDependency(m_syncFilterEntityByLayerJob); + m_syncRenderViewPreCommandUpdateJob->addDependency(m_syncFilterEntityByLayerJob); jobs.push_back(m_filterEntityByLayerJob); // Step 3 jobs.push_back(m_syncFilterEntityByLayerJob); // Step 4 } - jobs.push_back(m_syncFrustumCullingJob); // Step 3 + jobs.push_back(m_syncPreFrustumCullingJob); // Step 3 jobs.push_back(m_filterProximityJob); // Step 3 jobs.push_back(m_setClearDrawBufferIndexJob); // Step 3 if (m_materialGathererCacheNeedsToBeRebuilt) { for (const auto &materialGatherer : qAsConst(m_materialGathererJobs)) { - materialGatherer->addDependency(m_syncRenderViewInitializationJob); + materialGatherer->addDependency(m_syncRenderViewPostInitializationJob); materialGatherer->addDependency(m_renderer->introspectShadersJob()); materialGatherer->addDependency(m_renderer->filterCompatibleTechniqueJob()); jobs.push_back(materialGatherer); // Step3 m_syncMaterialGathererJob->addDependency(materialGatherer); } - m_syncRenderCommandBuildingJob->addDependency(m_syncMaterialGathererJob); + m_syncRenderViewPreCommandUpdateJob->addDependency(m_syncMaterialGathererJob); jobs.push_back(m_syncMaterialGathererJob); // Step 3 } jobs.push_back(m_frustumCullingJob); // Step 4 - jobs.push_back(m_syncRenderCommandBuildingJob); // Step 5 + jobs.push_back(m_syncRenderViewPreCommandUpdateJob); // Step 5 - for (const auto &renderViewCommandBuilder : qAsConst(m_renderViewBuilderJobs)) // Step 6 + // Build RenderCommands or Update RenderCommand Uniforms + for (const auto &renderViewCommandBuilder : qAsConst(m_renderViewCommandUpdaterJobs)) // Step 6 jobs.push_back(renderViewCommandBuilder); - jobs.push_back(m_syncRenderViewCommandBuildersJob); // Step 7 + jobs.push_back(m_syncRenderViewPostCommandUpdateJob); // Step 7 return jobs; } @@ -754,34 +770,14 @@ bool RenderViewBuilder::materialGathererCacheNeedsToBeRebuilt() const return m_materialGathererCacheNeedsToBeRebuilt; } -void RenderViewBuilder::setRenderableCacheNeedsToBeRebuilt(bool needsToBeRebuilt) -{ - m_renderableCacheNeedsToBeRebuilt = needsToBeRebuilt; -} - -bool RenderViewBuilder::renderableCacheNeedsToBeRebuilt() const -{ - return m_renderableCacheNeedsToBeRebuilt; -} - -void RenderViewBuilder::setComputableCacheNeedsToBeRebuilt(bool needsToBeRebuilt) -{ - m_computableCacheNeedsToBeRebuilt = needsToBeRebuilt; -} - -bool RenderViewBuilder::computableCacheNeedsToBeRebuilt() const -{ - return m_computableCacheNeedsToBeRebuilt; -} - -void RenderViewBuilder::setLightGathererCacheNeedsToBeRebuilt(bool needsToBeRebuilt) +void RenderViewBuilder::setRenderCommandCacheNeedsToBeRebuilt(bool needsToBeRebuilt) { - m_lightsCacheNeedsToBeRebuilt = needsToBeRebuilt; + m_renderCommandCacheNeedsToBeRebuilt = needsToBeRebuilt; } -bool RenderViewBuilder::lightGathererCacheNeedsToBeRebuilt() const +bool RenderViewBuilder::renderCommandCacheNeedsToBeRebuilt() const { - return m_lightsCacheNeedsToBeRebuilt; + return m_renderCommandCacheNeedsToBeRebuilt; } int RenderViewBuilder::optimalJobCount() diff --git a/src/render/renderers/opengl/renderer/renderviewbuilder_p.h b/src/render/renderers/opengl/renderer/renderviewbuilder_p.h index e223a5f1e..a2ab80e7e 100644 --- a/src/render/renderers/opengl/renderer/renderviewbuilder_p.h +++ b/src/render/renderers/opengl/renderer/renderviewbuilder_p.h @@ -53,15 +53,14 @@ #include <functional> #include <Qt3DCore/qaspectjob.h> -#include <Qt3DRender/private/filterentitybycomponentjob_p.h> #include <Qt3DRender/private/filterlayerentityjob_p.h> #include <Qt3DRender/private/genericlambdajob_p.h> #include <Qt3DRender/private/materialparametergathererjob_p.h> #include <Qt3DRender/private/nodemanagers_p.h> -#include <Qt3DRender/private/renderviewbuilderjob_p.h> +#include <Qt3DRender/private/renderviewcommandbuilderjob_p.h> +#include <Qt3DRender/private/renderviewcommandupdaterjob_p.h> #include <Qt3DRender/private/renderview_p.h> #include <Qt3DRender/private/frustumcullingjob_p.h> -#include <Qt3DRender/private/lightgatherer_p.h> #include <Qt3DRender/private/filterproximitydistancejob_p.h> QT_BEGIN_NAMESPACE @@ -73,8 +72,6 @@ namespace Render { class Renderer; using SynchronizerJobPtr = GenericLambdaJobPtr<std::function<void()>>; -using ComputableEntityFilterPtr = FilterEntityByComponentJobPtr<Render::ComputeCommand, Render::Material>; -using RenderableEntityFilterPtr = FilterEntityByComponentJobPtr<Render::GeometryRenderer, Render::Material>; class Q_AUTOTEST_EXPORT RenderViewBuilder { @@ -83,16 +80,15 @@ public: RenderViewInitializerJobPtr renderViewJob() const; FilterLayerEntityJobPtr filterEntityByLayerJob() const; - LightGathererPtr lightGathererJob() const; - RenderableEntityFilterPtr renderableEntityFilterJob() const; - ComputableEntityFilterPtr computableEntityFilterJob() const; FrustumCullingJobPtr frustumCullingJob() const; - QVector<RenderViewBuilderJobPtr> renderViewBuilderJobs() const; + QVector<RenderViewCommandBuilderJobPtr> renderViewCommandBuilderJobs() const; + QVector<RenderViewCommandUpdaterJobPtr> renderViewCommandUpdaterJobs() const; QVector<MaterialParameterGathererJobPtr> materialGathererJobs() const; - SynchronizerJobPtr syncRenderViewInitializationJob() const; - SynchronizerJobPtr syncFrustumCullingJob() const; - SynchronizerJobPtr syncRenderCommandBuildingJob() const; - SynchronizerJobPtr syncRenderViewCommandBuildersJob() const; + SynchronizerJobPtr syncRenderViewPostInitializationJob() const; + SynchronizerJobPtr syncPreFrustumCullingJob() const; + SynchronizerJobPtr syncRenderViewPreCommandBuildingJob() const; + SynchronizerJobPtr syncRenderViewPreCommandUpdateJob() const; + SynchronizerJobPtr syncRenderViewPostCommandUpdateJob() const; SynchronizerJobPtr setClearDrawBufferIndexJob() const; SynchronizerJobPtr syncFilterEntityByLayerJob() const; FilterProximityDistanceJobPtr filterProximityJob() const; @@ -108,15 +104,8 @@ public: bool layerCacheNeedsToBeRebuilt() const; void setMaterialGathererCacheNeedsToBeRebuilt(bool needsToBeRebuilt); bool materialGathererCacheNeedsToBeRebuilt() const; - - void setRenderableCacheNeedsToBeRebuilt(bool needsToBeRebuilt); - bool renderableCacheNeedsToBeRebuilt() const; - - void setComputableCacheNeedsToBeRebuilt(bool needsToBeRebuilt); - bool computableCacheNeedsToBeRebuilt() const; - - void setLightGathererCacheNeedsToBeRebuilt(bool needsToBeRebuilt); - bool lightGathererCacheNeedsToBeRebuilt() const; + void setRenderCommandCacheNeedsToBeRebuilt(bool needsToBeRebuilt); + bool renderCommandCacheNeedsToBeRebuilt() const; static int optimalJobCount(); static QVector<Entity *> entitiesInSubset(const QVector<Entity *> &entities, const QVector<Entity *> &subset); @@ -127,32 +116,25 @@ private: Renderer *m_renderer; bool m_layerCacheNeedsToBeRebuilt; bool m_materialGathererCacheNeedsToBeRebuilt; - bool m_lightsCacheNeedsToBeRebuilt; - bool m_renderableCacheNeedsToBeRebuilt; - bool m_computableCacheNeedsToBeRebuilt; + bool m_renderCommandCacheNeedsToBeRebuilt; RenderViewInitializerJobPtr m_renderViewJob; FilterLayerEntityJobPtr m_filterEntityByLayerJob; - LightGathererPtr m_lightGathererJob; - RenderableEntityFilterPtr m_renderableEntityFilterJob; - ComputableEntityFilterPtr m_computableEntityFilterJob; FrustumCullingJobPtr m_frustumCullingJob; - QVector<RenderViewBuilderJobPtr> m_renderViewBuilderJobs; + QVector<RenderViewCommandBuilderJobPtr> m_renderViewCommandBuilderJobs; + QVector<RenderViewCommandUpdaterJobPtr> m_renderViewCommandUpdaterJobs; QVector<MaterialParameterGathererJobPtr> m_materialGathererJobs; - SynchronizerJobPtr m_syncRenderViewInitializationJob; - SynchronizerJobPtr m_syncFrustumCullingJob; - SynchronizerJobPtr m_syncRenderCommandBuildingJob; - SynchronizerJobPtr m_syncRenderViewCommandBuildersJob; + SynchronizerJobPtr m_syncRenderViewPostInitializationJob; + SynchronizerJobPtr m_syncPreFrustumCullingJob; + SynchronizerJobPtr m_syncRenderViewPreCommandBuildingJob; + SynchronizerJobPtr m_syncRenderViewPreCommandUpdateJob; + SynchronizerJobPtr m_syncRenderViewPostCommandUpdateJob; SynchronizerJobPtr m_setClearDrawBufferIndexJob; SynchronizerJobPtr m_syncFilterEntityByLayerJob; SynchronizerJobPtr m_syncMaterialGathererJob; FilterProximityDistanceJobPtr m_filterProximityJob; - SynchronizerJobPtr m_cacheRenderableEntitiesJob; - SynchronizerJobPtr m_cacheComputableEntitiesJob; - SynchronizerJobPtr m_cacheLightsJob; - static const int m_optimalParallelJobCount; }; diff --git a/src/render/renderers/opengl/renderer/shaderparameterpack.cpp b/src/render/renderers/opengl/renderer/shaderparameterpack.cpp index 13d05cac1..1cfb59343 100644 --- a/src/render/renderers/opengl/renderer/shaderparameterpack.cpp +++ b/src/render/renderers/opengl/renderer/shaderparameterpack.cpp @@ -57,7 +57,6 @@ namespace Render { ShaderParameterPack::~ShaderParameterPack() { - m_uniforms.clear(); } void ShaderParameterPack::setUniform(const int glslNameId, const UniformValue &val) diff --git a/src/render/renderers/opengl/renderer/shaderparameterpack_p.h b/src/render/renderers/opengl/renderer/shaderparameterpack_p.h index a5aee6ac4..cb599124c 100644 --- a/src/render/renderers/opengl/renderer/shaderparameterpack_p.h +++ b/src/render/renderers/opengl/renderer/shaderparameterpack_p.h @@ -89,7 +89,56 @@ struct BlockToSSBO { QT3D_DECLARE_TYPEINFO_2(Qt3DRender, Render, BlockToSSBO, Q_PRIMITIVE_TYPE) -typedef QHash<int, UniformValue> PackUniformHash; +struct PackUniformHash +{ + QVector<int> keys; + QVector<UniformValue> values; + + PackUniformHash() + { + keys.reserve(10); + values.reserve(10); + } + + void insert(int key, const UniformValue &value) + { + const int idx = keys.indexOf(key); + if (idx != -1) { + values[idx] = value; + } else { + keys.push_back(key); + values.push_back(value); + } + } + + UniformValue value(int key) const + { + const int idx = keys.indexOf(key); + if (idx != -1) + return values.at(idx); + return UniformValue(); + } + + UniformValue& value(int key) + { + const int idx = keys.indexOf(key); + if (idx != -1) + return values[idx]; + insert(key, UniformValue()); + return value(key); + } + + void erase(int idx) + { + keys.removeAt(idx); + values.removeAt(idx); + } + + bool contains(int key) const + { + return keys.contains(key); + } +}; class Q_AUTOTEST_EXPORT ShaderParameterPack { |