// Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). // Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies). // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "renderview_p.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(QT3D_RENDER_VIEW_JOB_TIMINGS) #include #endif QT_BEGIN_NAMESPACE namespace Qt3DRender { namespace Render { namespace OpenGL { namespace { // register our QNodeId's as a metatype during program loading Q_DECL_UNUSED const int qNodeIdTypeId = qMetaTypeId(); std::atomic_bool wasInitialized{}; } // anonymous namespace RenderView::StandardUniformsNameToTypeHash RenderView::ms_standardUniformSetters; RenderView::StandardUniformsNameToTypeHash RenderView::initializeStandardUniformSetters() { RenderView::StandardUniformsNameToTypeHash setters; 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); setters.insert(Shader::yUpInFBOId, YUpInFBO); setters.insert(Shader::yUpInNDCId, YUpInNDC); return setters; } // TODO: Move this somewhere global where GraphicsContext::setViewport() can use it too static QRectF resolveViewport(const QRectF &fractionalViewport, const QSize &surfaceSize) { return QRectF(fractionalViewport.x() * surfaceSize.width(), (1.0 - fractionalViewport.y() - fractionalViewport.height()) * surfaceSize.height(), fractionalViewport.width() * surfaceSize.width(), fractionalViewport.height() * surfaceSize.height()); } static Matrix4x4 getProjectionMatrix(const CameraLens *lens) { return lens ? lens->projection() : Matrix4x4(); } UniformValue RenderView::standardUniformValue(RenderView::StandardUniform standardUniformType, const Entity *entity) const { const Matrix4x4 &model = *(entity->worldTransform()); switch (standardUniformType) { case ModelMatrix: return UniformValue(model); case ViewMatrix: return UniformValue(m_viewMatrix); case ProjectionMatrix: return UniformValue(getProjectionMatrix(m_renderCameraLens)); case ModelViewMatrix: return UniformValue(m_viewMatrix * model); case ViewProjectionMatrix: return UniformValue(getProjectionMatrix(m_renderCameraLens) * m_viewMatrix); case ModelViewProjectionMatrix: return UniformValue(m_viewProjectionMatrix * model); case InverseModelMatrix: return UniformValue(model.inverted()); case InverseViewMatrix: return UniformValue(m_viewMatrix.inverted()); case InverseProjectionMatrix: { return UniformValue(getProjectionMatrix(m_renderCameraLens).inverted()); } case InverseModelViewMatrix: return UniformValue((m_viewMatrix * model).inverted()); case InverseViewProjectionMatrix: { const Matrix4x4 viewProjectionMatrix = getProjectionMatrix(m_renderCameraLens) * m_viewMatrix; return UniformValue(viewProjectionMatrix.inverted()); } case InverseModelViewProjectionMatrix: return UniformValue((m_viewProjectionMatrix * model).inverted()); case ModelNormalMatrix: return UniformValue(convertToQMatrix4x4(model).normalMatrix()); case ModelViewNormalMatrix: return UniformValue(convertToQMatrix4x4(m_viewMatrix * model).normalMatrix()); case ViewportMatrix: { QMatrix4x4 viewportMatrix; // TO DO: Implement on Matrix4x4 viewportMatrix.viewport(resolveViewport(m_viewport, m_surfaceSize)); return UniformValue(Matrix4x4(viewportMatrix)); } case InverseViewportMatrix: { QMatrix4x4 viewportMatrix; // TO DO: Implement on Matrix4x4 viewportMatrix.viewport(resolveViewport(m_viewport, m_surfaceSize)); return UniformValue(Matrix4x4(viewportMatrix.inverted())); } case AspectRatio: return float(m_surfaceSize.width()) / std::max(1.f, float(m_surfaceSize.height())); case Exposure: return UniformValue(m_renderCameraLens ? m_renderCameraLens->exposure() : 0.0f); case Gamma: return UniformValue(m_gamma); case Time: return UniformValue(float(m_renderer->time() / 1000000000.0f)); case EyePosition: return UniformValue(m_eyePos); case SkinningPalette: { const Armature *armature = entity->renderComponent(); if (!armature) { qCWarning(Jobs, "Requesting skinningPalette uniform but no armature set on entity"); return UniformValue(); } return armature->skinningPaletteUniform(); } case YUpInNDC: return UniformValue(0.0f); case YUpInFBO: return UniformValue(0.0f); default: Q_UNREACHABLE_RETURN(UniformValue()); } } /*! \internal Walks up the framegraph tree from \a fgLeaf and builds up as much state as possible and populates \a rv. For cases where we can't get the specific state (e.g. because it depends upon more than just the framegraph) we store the data from the framegraph that will be needed to later when the rest of the data becomes available */ void RenderView::setRenderViewConfigFromFrameGraphLeafNode(RenderView *rv, const FrameGraphNode *fgLeaf) { // The specific RenderPass to be used is also dependent upon the Effect and TechniqueFilter // which is referenced by the Material which is referenced by the RenderMesh. So we can // only store the filter info in the RenderView structure and use it to do the resolving // when we build the RenderCommand list. const NodeManagers *manager = rv->nodeManagers(); const FrameGraphNode *node = fgLeaf; while (node) { FrameGraphNode::FrameGraphNodeType type = node->nodeType(); if (node->isEnabled()) switch (type) { case FrameGraphNode::InvalidNodeType: // A base FrameGraphNode, can be used for grouping purposes break; case FrameGraphNode::CameraSelector: // Can be set only once and we take camera nearest to the leaf node if (!rv->renderCameraLens()) { const CameraSelector *cameraSelector = static_cast(node); Entity *camNode = manager->renderNodesManager()->lookupResource(cameraSelector->cameraUuid()); if (camNode) { CameraLens *lens = camNode->renderComponent(); rv->setRenderCameraEntity(camNode); if (lens && lens->isEnabled()) { rv->setRenderCameraLens(lens); // ViewMatrix and ProjectionMatrix are computed // later in updateMatrices() // since at this point the transformation matrices // may not yet have been updated } } } break; case FrameGraphNode::LayerFilter: // Can be set multiple times in the tree rv->appendLayerFilter(static_cast(node)->peerId()); break; case FrameGraphNode::ProximityFilter: // Can be set multiple times in the tree rv->appendProximityFilterId(node->peerId()); break; case FrameGraphNode::RenderPassFilter: // Can be set once // TODO: Amalgamate all render pass filters from leaf to root if (!rv->renderPassFilter()) rv->setRenderPassFilter(static_cast(node)); break; case FrameGraphNode::RenderTarget: { // Can be set once and we take render target nearest to the leaf node const RenderTargetSelector *targetSelector = static_cast(node); Qt3DCore::QNodeId renderTargetUid = targetSelector->renderTargetUuid(); HTarget renderTargetHandle = manager->renderTargetManager()->lookupHandle(renderTargetUid); // Add renderTarget Handle and build renderCommand AttachmentPack if (!rv->renderTargetId()) { rv->setRenderTargetId(renderTargetUid); RenderTarget *renderTarget = manager->renderTargetManager()->data(renderTargetHandle); if (renderTarget) rv->setAttachmentPack(AttachmentPack(renderTarget, manager->attachmentManager())); } break; } case FrameGraphNode::ClearBuffers: { const ClearBuffers *cbNode = static_cast(node); rv->addClearBuffers(cbNode); break; } case FrameGraphNode::TechniqueFilter: // Can be set once // TODO Amalgamate all technique filters from leaf to root if (!rv->techniqueFilter()) rv->setTechniqueFilter(static_cast(node)); break; case FrameGraphNode::Viewport: { // If the Viewport has already been set in a lower node // Make it so that the new viewport is actually // a subregion relative to that of the parent viewport const ViewportNode *vpNode = static_cast(node); rv->setViewport(ViewportNode::computeViewport(rv->viewport(), vpNode)); rv->setGamma(vpNode->gamma()); break; } case FrameGraphNode::SortMethod: { const Render::SortPolicy *sortPolicy = static_cast(node); rv->addSortType(sortPolicy->sortTypes()); break; } case FrameGraphNode::SubtreeEnabler: // Has no meaning here. SubtreeEnabler was used // in a prior step to filter the list of RenderViewJobs break; case FrameGraphNode::StateSet: { const Render::StateSetNode *rStateSet = static_cast(node); // Add states from new stateSet we might be missing // but don' t override existing states (lower StateSetNode always has priority) if (rStateSet->hasRenderStates()) { // Create global RenderStateSet for renderView if no stateSet was set before RenderStateSet *stateSet = rv->getOrCreateStateSet(); addStatesToRenderStateSet(stateSet, rStateSet->renderStates(), manager->renderStateManager()); } break; } case FrameGraphNode::NoDraw: { rv->setNoDraw(true); break; } case FrameGraphNode::FrustumCulling: { rv->setFrustumCulling(true); break; } case FrameGraphNode::ComputeDispatch: { const Render::DispatchCompute *dispatchCompute = static_cast(node); rv->setCompute(true); rv->setComputeWorkgroups(dispatchCompute->x(), dispatchCompute->y(), dispatchCompute->z()); break; } case FrameGraphNode::Lighting: { // TODO break; } case FrameGraphNode::Surface: { // Use the surface closest to leaf node if (rv->surface() == nullptr) { const Render::RenderSurfaceSelector *surfaceSelector = static_cast(node); rv->setSurface(surfaceSelector->surface()); rv->setSurfaceSize(surfaceSelector->renderTargetSize() * surfaceSelector->devicePixelRatio()); rv->setDevicePixelRatio(surfaceSelector->devicePixelRatio()); } break; } case FrameGraphNode::DebugOverlay: rv->setShowDebugOverlay(true); break; case FrameGraphNode::RenderCapture: { auto *renderCapture = const_cast( static_cast(node)); if (rv->renderCaptureNodeId().isNull() && renderCapture->wasCaptureRequested()) { rv->setRenderCaptureNodeId(renderCapture->peerId()); rv->setRenderCaptureRequest(renderCapture->takeCaptureRequest()); } break; } case FrameGraphNode::MemoryBarrier: { const Render::MemoryBarrier *barrier = static_cast(node); rv->setMemoryBarrier(barrier->waitOperations()|rv->memoryBarrier()); break; } case FrameGraphNode::BufferCapture: { auto *bufferCapture = const_cast( static_cast(node)); if (bufferCapture != nullptr) rv->setIsDownloadBuffersEnable(bufferCapture->isEnabled()); break; } case FrameGraphNode::BlitFramebuffer: { const Render::BlitFramebuffer *blitFramebufferNode = static_cast(node); rv->setHasBlitFramebufferInfo(true); BlitFramebufferInfo bfbInfo; bfbInfo.sourceRenderTargetId = blitFramebufferNode->sourceRenderTargetId(); bfbInfo.destinationRenderTargetId = blitFramebufferNode->destinationRenderTargetId(); bfbInfo.sourceRect = blitFramebufferNode->sourceRect(); bfbInfo.destinationRect = blitFramebufferNode->destinationRect(); bfbInfo.sourceAttachmentPoint = blitFramebufferNode->sourceAttachmentPoint(); bfbInfo.destinationAttachmentPoint = blitFramebufferNode->destinationAttachmentPoint(); bfbInfo.interpolationMethod = blitFramebufferNode->interpolationMethod(); rv->setBlitFrameBufferInfo(bfbInfo); break; } case FrameGraphNode::WaitFence: { const Render::WaitFence *waitFence = static_cast(node); rv->appendWaitFence(waitFence->data()); break; } case FrameGraphNode::SetFence: { rv->appendInsertFenceId(node->peerId()); break; } case FrameGraphNode::NoPicking: // Nothing to do RenderView wise for NoPicking break; default: // Should never get here qCWarning(Backend) << "Unhandled FrameGraphNode type"; } node = node->parent(); } } RenderView::RenderView() { if (Q_UNLIKELY(!wasInitialized.exchange(true))) { // Needed as we can control the init order of static/global variables across compile units // and this hash relies on the static StringToInt class RenderView::ms_standardUniformSetters = RenderView::initializeStandardUniformSetters(); } } RenderView::~RenderView() { } namespace { template struct AdjacentSubRangeFinder { static bool adjacentSubRange(const RenderCommand &, const RenderCommand &) { Q_UNREACHABLE_RETURN(false); } }; template<> struct AdjacentSubRangeFinder { static bool adjacentSubRange(const RenderCommand &a, const RenderCommand &b) { return a.m_changeCost == b.m_changeCost; } }; template<> struct AdjacentSubRangeFinder { static bool adjacentSubRange(const RenderCommand &a, const RenderCommand &b) { return qFuzzyCompare(a.m_depth, b.m_depth); } }; template<> struct AdjacentSubRangeFinder { static bool adjacentSubRange(const RenderCommand &a, const RenderCommand &b) { return a.m_glShader == b.m_glShader; } }; template<> struct AdjacentSubRangeFinder { static bool adjacentSubRange(const RenderCommand &a, const RenderCommand &b) { return qFuzzyCompare(a.m_depth, b.m_depth); } }; template<> struct AdjacentSubRangeFinder { static bool adjacentSubRange(const RenderCommand &a, const RenderCommand &b) { // Two renderCommands are adjacent if one contains all the other command's textures const std::vector &texturesA = a.m_parameterPack.textures(); const std::vector &texturesB = b.m_parameterPack.textures(); const bool bBigger = texturesB.size() > texturesA.size(); const std::vector &smallestVector = bBigger ? texturesA : texturesB; const std::vector &biggestVector = bBigger ? texturesB : texturesA; const auto e = biggestVector.cend(); for (const ShaderParameterPack::NamedResource &tex : smallestVector) { if (std::find(biggestVector.begin(), e, tex) == e) return false; } return true; } }; template int advanceUntilNonAdjacent(const EntityRenderCommandDataView *view, const size_t beg, const size_t end, Predicate pred) { const std::vector &commandIndices = view->indices; const std::vector &commands = view->data.commands; size_t i = beg + 1; if (i < end) { const size_t startIdx = commandIndices[beg]; while (i < end) { const size_t targetIdx = commandIndices[i]; if (!pred(commands[startIdx], commands[targetIdx])) break; ++i; } } return int(i); } template struct SubRangeSorter { static void sortSubRange(EntityRenderCommandDataView *view, size_t begin, const size_t end) { Q_UNUSED(view); Q_UNUSED(begin); Q_UNUSED(end); Q_UNREACHABLE(); } }; template<> struct SubRangeSorter { static void sortSubRange(EntityRenderCommandDataView *view, size_t begin, const size_t end) { std::vector &commandIndices = view->indices; const std::vector &commands = view->data.commands; std::stable_sort(commandIndices.begin() + begin, commandIndices.begin() + end, [&commands] (const size_t &iA, const size_t &iB) { const RenderCommand &a = commands[iA]; const RenderCommand &b = commands[iB]; return a.m_changeCost > b.m_changeCost; }); } }; template<> struct SubRangeSorter { static void sortSubRange(EntityRenderCommandDataView *view, size_t begin, const size_t end) { std::vector &commandIndices = view->indices; const std::vector &commands = view->data.commands; std::stable_sort(commandIndices.begin() + begin, commandIndices.begin() + end, [&commands] (const size_t &iA, const size_t &iB) { const RenderCommand &a = commands[iA]; const RenderCommand &b = commands[iB]; return a.m_depth > b.m_depth; }); } }; template<> struct SubRangeSorter { static void sortSubRange(EntityRenderCommandDataView *view, size_t begin, const size_t end) { std::vector &commandIndices = view->indices; const std::vector &commands = view->data.commands; std::stable_sort(commandIndices.begin() + begin, commandIndices.begin() + end, [&commands] (const size_t &iA, const size_t &iB) { const RenderCommand &a = commands[iA]; const RenderCommand &b = commands[iB]; return a.m_glShader > b.m_glShader; }); } }; template<> struct SubRangeSorter { static void sortSubRange(EntityRenderCommandDataView *view, size_t begin, const size_t end) { std::vector &commandIndices = view->indices; const std::vector &commands = view->data.commands; std::stable_sort(commandIndices.begin() + begin, commandIndices.begin() + end, [&commands] (const size_t &iA, const size_t &iB) { const RenderCommand &a = commands[iA]; const RenderCommand &b = commands[iB]; return a.m_depth < b.m_depth; }); } }; template<> struct SubRangeSorter { static void sortSubRange(EntityRenderCommandDataView *view, size_t begin, const size_t end) { #ifndef Q_OS_WIN std::vector &commandIndices = view->indices; const std::vector &commands = view->data.commands; std::stable_sort(commandIndices.begin() + begin, commandIndices.begin() + end, [&commands] (const int &iA, const int &iB) { const RenderCommand &a = commands[iA]; const RenderCommand &b = commands[iB]; const std::vector &texturesA = a.m_parameterPack.textures(); const std::vector &texturesB = b.m_parameterPack.textures(); const bool bBigger = texturesB.size() > texturesA.size(); const std::vector &smallestVector = bBigger ? texturesA : texturesB; const std::vector &biggestVector = bBigger ? texturesB : texturesA; size_t identicalTextureCount = 0; const auto e = biggestVector.cend(); for (const ShaderParameterPack::NamedResource &tex : smallestVector) { if (std::find(biggestVector.begin(), e, tex) != e) ++identicalTextureCount; } return identicalTextureCount < smallestVector.size(); }); #else Q_UNUSED(view); Q_UNUSED(begin); Q_UNUSED(end); #endif } }; int findSubRange(const EntityRenderCommandDataView *view, const int begin, const int end, const QSortPolicy::SortType sortType) { switch (sortType) { case QSortPolicy::StateChangeCost: return advanceUntilNonAdjacent(view, begin, end, AdjacentSubRangeFinder::adjacentSubRange); case QSortPolicy::BackToFront: return advanceUntilNonAdjacent(view, begin, end, AdjacentSubRangeFinder::adjacentSubRange); case QSortPolicy::Material: return advanceUntilNonAdjacent(view, begin, end, AdjacentSubRangeFinder::adjacentSubRange); case QSortPolicy::FrontToBack: return advanceUntilNonAdjacent(view, begin, end, AdjacentSubRangeFinder::adjacentSubRange); case QSortPolicy::Texture: return advanceUntilNonAdjacent(view, begin, end, AdjacentSubRangeFinder::adjacentSubRange); case QSortPolicy::Uniform: return end; default: Q_UNREACHABLE_RETURN(end); } } void sortByMaterial(EntityRenderCommandDataView *view, int begin, const int end) { // We try to arrange elements so that their rendering cost is minimized for a given shader std::vector &commandIndices = view->indices; const std::vector &commands = view->data.commands; int rangeEnd = advanceUntilNonAdjacent(view, begin, end, AdjacentSubRangeFinder::adjacentSubRange); while (begin != end) { if (begin + 1 < rangeEnd) { std::stable_sort(commandIndices.begin() + begin + 1, commandIndices.begin() + rangeEnd, [&commands] (size_t iA, size_t iB) { const RenderCommand &a = commands[iA]; const RenderCommand &b = commands[iB]; return a.m_material.handle() < b.m_material.handle(); }); } begin = rangeEnd; rangeEnd = advanceUntilNonAdjacent(view, begin, end, AdjacentSubRangeFinder::adjacentSubRange); } } void sortCommandRange(EntityRenderCommandDataView *view, int begin, int end, const int level, const QList &sortingTypes) { if (level >= sortingTypes.size()) return; switch (sortingTypes.at(level)) { case QSortPolicy::StateChangeCost: SubRangeSorter::sortSubRange(view, begin, end); break; case QSortPolicy::BackToFront: SubRangeSorter::sortSubRange(view, begin, end); break; case QSortPolicy::Material: // Groups all same shader DNA together SubRangeSorter::sortSubRange(view, begin, end); // Group all same material together (same parameters most likely) sortByMaterial(view, begin, end); break; case QSortPolicy::FrontToBack: SubRangeSorter::sortSubRange(view, begin, end); break; case QSortPolicy::Texture: SubRangeSorter::sortSubRange(view, begin, end); break; case QSortPolicy::Uniform: break; default: Q_UNREACHABLE(); } // For all sub ranges of adjacent item for sortType[i] // Perform filtering with sortType[i + 1] int rangeEnd = findSubRange(view, begin, end, sortingTypes.at(level)); while (begin != end) { sortCommandRange(view, begin, rangeEnd, level + 1, sortingTypes); begin = rangeEnd; rangeEnd = findSubRange(view, begin, end, sortingTypes.at(level)); } } } // anonymous void RenderView::sort() { assert(m_renderCommandDataView); // Compares the bitsetKey of the RenderCommands // Key[Depth | StateCost | Shader] sortCommandRange(m_renderCommandDataView.data(), 0, int(m_renderCommandDataView->size()), 0, m_sortingTypes); // For RenderCommand with the same shader // We compute the adjacent change cost // Only perform uniform minimization if we explicitly asked for it if (!m_sortingTypes.contains(QSortPolicy::Uniform)) return; // Minimize uniform changes size_t i = 0; std::vector &commands = m_renderCommandDataView->data.commands; const std::vector &indices = m_renderCommandDataView->indices; const size_t commandSize = indices.size(); while (i < commandSize) { size_t j = i; // Advance while commands share the same shader while (i < commandSize && commands[indices[j]].m_glShader == commands[indices[i]].m_glShader) ++i; if (i - j > 0) { // Several commands have the same shader, so we minimize uniform changes PackUniformHash cachedUniforms = commands[indices[j++]].m_parameterPack.uniforms(); while (j < i) { // We need the reference here as we are modifying the original container // not the copy PackUniformHash &uniforms = commands[indices[j]].m_parameterPack.m_uniforms; for (size_t u = 0; u < uniforms.keys.size();) { // We are comparing the values: // - raw uniform values // - the texture Node id if the uniform represents a texture // since all textures are assigned texture units before the RenderCommands // 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 int uniformNameId = uniforms.keys.at(u); const UniformValue &refValue = cachedUniforms.value(uniformNameId); const UniformValue &newValue = uniforms.values.at(u); if (newValue == refValue) { uniforms.erase(int(u)); } else { // Record updated value so that subsequent comparison // for the next command will be made againts latest // uniform value cachedUniforms.insert(uniformNameId, newValue); ++u; } } ++j; } } } } void RenderView::setRenderer(Renderer *renderer) { m_renderer = renderer; m_manager = renderer->nodeManagers(); } RenderStateSet *RenderView::getOrCreateStateSet() { if (!m_stateSet) m_stateSet.reset(new RenderStateSet()); return m_stateSet.data(); } void RenderView::addClearBuffers(const ClearBuffers *cb) { QClearBuffers::BufferTypeFlags type = cb->type(); if (type & QClearBuffers::StencilBuffer) { m_clearStencilValue = cb->clearStencilValue(); m_clearBuffer |= QClearBuffers::StencilBuffer; } if (type & QClearBuffers::DepthBuffer) { m_clearDepthValue = cb->clearDepthValue(); m_clearBuffer |= QClearBuffers::DepthBuffer; } // keep track of global ClearColor (if set) and collect all DrawBuffer-specific // ClearColors if (type & QClearBuffers::ColorBuffer) { ClearBufferInfo clearBufferInfo; clearBufferInfo.clearColor = cb->clearColor(); if (cb->clearsAllColorBuffers()) { m_globalClearColorBuffer = clearBufferInfo; m_clearBuffer |= QClearBuffers::ColorBuffer; } else { if (cb->bufferId()) { const RenderTargetOutput *targetOutput = m_manager->attachmentManager()->lookupResource(cb->bufferId()); if (targetOutput) { clearBufferInfo.attchmentPoint = targetOutput->point(); // Note: a job is later performed to find the drawIndex from the buffer attachment point // using the AttachmentPack m_specificClearColorBuffers.push_back(clearBufferInfo); } } } } } // If we are there, we know that entity had a GeometryRenderer + Material EntityRenderCommandData RenderView::buildDrawRenderCommands(const Entity **entities, int offset, int count) const { GLShaderManager *glShaderManager = m_renderer->glResourceManagers()->glShaderManager(); EntityRenderCommandData commands; commands.reserve(count); for (int i = 0; i < count; ++i) { const int idx = offset + i; const Entity *entity = entities[idx]; GeometryRenderer *geometryRenderer = nullptr; HGeometryRenderer geometryRendererHandle = entity->componentHandle(); // There is a geometry renderer with geometry if ((geometryRenderer = m_manager->geometryRendererManager()->data(geometryRendererHandle)) != nullptr && geometryRenderer->isEnabled() && !geometryRenderer->geometryId().isNull()) { const Qt3DCore::QNodeId materialComponentId = entity->componentUuid(); const HMaterial materialHandle = entity->componentHandle(); const std::vector &renderPassData = m_parameters.value(materialComponentId); HGeometry geometryHandle = m_manager->geometryManager()->lookupHandle(geometryRenderer->geometryId()); Geometry *geometry = m_manager->geometryManager()->data(geometryHandle); // 1 RenderCommand per RenderPass pass on an Entity with a Mesh for (const RenderPassParameterData &passData : renderPassData) { // Add the RenderPass Parameters RenderCommand command = {}; command.m_geometryRenderer = geometryRendererHandle; command.m_geometry = 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 = RenderStateSetPtr::create(); addStatesToRenderStateSet(command.m_stateSet.data(), pass->renderStates(), m_manager->renderStateManager()); if (m_stateSet) command.m_stateSet->merge(m_stateSet.data()); command.m_changeCost = m_renderer->defaultRenderState()->changeCost(command.m_stateSet.data()); } command.m_shaderId = pass->shaderProgram(); // We try to resolve the m_glShader here. If the shader exist, // it won't be null and will allow us to full process the // command over a single frame. Otherwise, the shader will be // loaded at the next submission time and the command will only // be fully valid on the next frame. Additionally, that way, if // a commands keeps being rebuilt, frame after frame, it will // still be visible on screen as long as the shader exists command.m_glShader = glShaderManager->lookupResource(command.m_shaderId); // It takes two frames to have a valid command as we can only // reference a glShader at frame n if it has been loaded at frame n - 1 if (!command.m_glShader) continue; { // 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 QList attributeIds = geometry->attributes(); for (Qt3DCore::QNodeId attributeId : attributeIds) { Attribute *attribute = m_manager->attributeManager()->lookupResource(attributeId); switch (attribute->attributeType()) { case Qt3DCore::QAttribute::IndexAttribute: indexAttribute = attribute; break; case Qt3DCore::QAttribute::DrawIndirectAttribute: indirectAttribute = attribute; break; case Qt3DCore::QAttribute::VertexAttribute: estimatedCount = std::max(int(attribute->count()), estimatedCount); break; default: Q_UNREACHABLE(); break; } } command.m_drawIndexed = (indexAttribute != nullptr); command.m_drawIndirect = (indirectAttribute != nullptr); // Update the draw command with all the information required for the drawing if (command.m_drawIndexed) { command.m_indexAttributeDataType = GraphicsContext::glDataTypeFromAttributeDataType(indexAttribute->vertexBaseType()); command.m_indexAttributeByteOffset = indexAttribute->byteOffset() + geometryRenderer->indexBufferByteOffset(); } // Note: we only care about the primitiveCount when using direct draw calls // For indirect draw calls it is assumed the buffer was properly set already if (command.m_drawIndirect) { command.m_indirectAttributeByteOffset = indirectAttribute->byteOffset(); command.m_indirectDrawBuffer = m_manager->bufferManager()->lookupHandle(indirectAttribute->bufferId()); } else { // Use the count specified by the GeometryRender // If not specify use the indexAttribute count if present // Otherwise tries to use the count from the attribute with the highest count if (primitiveCount == 0) { if (indexAttribute) primitiveCount = indexAttribute->count(); else primitiveCount = estimatedCount; } } command.m_primitiveCount = primitiveCount; command.m_primitiveType = geometryRenderer->primitiveType(); command.m_primitiveRestartEnabled = geometryRenderer->primitiveRestartEnabled(); command.m_restartIndexValue = geometryRenderer->restartIndexValue(); command.m_firstInstance = geometryRenderer->firstInstance(); command.m_instanceCount = geometryRenderer->instanceCount(); command.m_firstVertex = geometryRenderer->firstVertex(); command.m_indexOffset = geometryRenderer->indexOffset(); command.m_verticesPerPatch = geometryRenderer->verticesPerPatch(); } // scope commands.push_back(entity, std::move(command), std::move(passData)); } } } return commands; } EntityRenderCommandData RenderView::buildComputeRenderCommands(const Entity **entities, int offset, int count) const { // If the RenderView contains only a ComputeDispatch then it cares about // A ComputeDispatch is also implicitely a NoDraw operation // enabled flag // layer component // material/effect/technique/parameters/filters/ EntityRenderCommandData commands; GLShaderManager *glShaderManager = m_renderer->glResourceManagers()->glShaderManager(); commands.reserve(count); for (int i = 0; i < count; ++i) { const int idx = offset + i; const Entity *entity = entities[idx]; ComputeCommand *computeJob = nullptr; HComputeCommand computeCommandHandle = entity->componentHandle(); if ((computeJob = nodeManagers()->computeJobManager()->data(computeCommandHandle)) != nullptr && computeJob->isEnabled()) { const Qt3DCore::QNodeId materialComponentId = entity->componentUuid(); const std::vector &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 = {}; RenderPass *pass = passData.pass; if (pass->hasRenderStates()) { 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.data()); command.m_changeCost = m_renderer->defaultRenderState()->changeCost(command.m_stateSet.data()); } command.m_shaderId = pass->shaderProgram(); command.m_glShader = glShaderManager->lookupResource(command.m_shaderId); // It takes two frames to have a valid command as we can only // reference a glShader at frame n if it has been loaded at frame n - 1 if (!command.m_glShader) continue; 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)); } } } return commands; } void RenderView::updateRenderCommand(const EntityRenderCommandDataSubView &subView) { subView.forEach([this] (const Entity *entity, const RenderPassParameterData &passData, RenderCommand &command) { 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_eyePos, m_eyeViewDir); auto geometryRenderer = m_manager->geometryRendererManager()->data(command.m_geometryRenderer); if (geometryRenderer && !qFuzzyCompare(geometryRenderer->sortIndex(), -1.f)) command.m_depth = geometryRenderer->sortIndex(); } 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(); } // setShaderAndUniforms can initialize a localData // make sure this is cleared before we leave this function setShaderAndUniforms(&command, passData.parameterInfo, entity); }); } void RenderView::updateMatrices() { if (m_renderCameraNode && m_renderCameraLens && m_renderCameraLens->isEnabled()) { auto transform = m_renderCameraNode->renderComponent(); if (m_renderCameraNode->isParentLessTransform() && transform && transform->hasViewMatrix()) { // optimization: if the entity is a QCamera and it doesn't have a parent with a transform component, // then we use the frontend version of the viewMatrix to avoid extra calculations that may introduce // rounding errors setViewMatrix(transform->viewMatrix()); } else { const Matrix4x4 cameraWorld = *(m_renderCameraNode->worldTransform()); setViewMatrix(m_renderCameraLens->viewMatrix(cameraWorld)); } setViewProjectionMatrix(m_renderCameraLens->projection() * viewMatrix()); //To get the eyePosition of the camera, we need to use the inverse of the //camera's worldTransform matrix. const Matrix4x4 inverseWorldTransform = viewMatrix().inverted(); const Vector3D eyePosition(inverseWorldTransform.column(3)); setEyePosition(eyePosition); // Get the viewing direction of the camera. Use the normal matrix to // ensure non-uniform scale works too. const QMatrix3x3 normalMat = convertToQMatrix4x4(m_viewMatrix).normalMatrix(); // dir = normalize(QVector3D(0, 0, -1) * normalMat) setEyeViewDirection(Vector3D(-normalMat(2, 0), -normalMat(2, 1), -normalMat(2, 2)).normalized()); } } void RenderView::setUniformValue(ShaderParameterPack &uniformPack, int nameId, const UniformValue &value) const { // At this point a uniform value can only be a scalar type // or a Qt3DCore::QNodeId corresponding to a Texture or Image // ShaderData/Buffers would be handled as UBO/SSBO and would therefore // not be in the default uniform block if (value.valueType() == UniformValue::NodeId) { const Qt3DCore::QNodeId *nodeIds = value.constData(); const int uniformArraySize = value.byteSize() / sizeof(Qt3DCore::QNodeId); UniformValue::ValueType resourceType = UniformValue::TextureValue; for (int i = 0; i < uniformArraySize; ++i) { const Qt3DCore::QNodeId resourceId = nodeIds[i]; const Texture *tex = m_manager->textureManager()->lookupResource(resourceId); if (tex != nullptr) { uniformPack.setTexture(nameId, i, resourceId); } else { const ShaderImage *img = m_manager->shaderImageManager()->lookupResource(resourceId); if (img != nullptr) { resourceType = UniformValue::ShaderImageValue; uniformPack.setImage(nameId, i, resourceId); } } } // This uniform will be overridden in SubmissionContext::setParameters // and -1 values will be replaced by valid Texture or Image units UniformValue uniformValue(uniformArraySize * sizeof(int), resourceType); std::fill(uniformValue.data(), uniformValue.data() + uniformArraySize, -1); uniformPack.setUniform(nameId, uniformValue); } else { uniformPack.setUniform(nameId, value); } } void RenderView::setStandardUniformValue(ShaderParameterPack &uniformPack, int nameId, const Entity *entity) const { uniformPack.setUniform(nameId, standardUniformValue(ms_standardUniformSetters[nameId], entity)); } void RenderView::setUniformBlockValue(ShaderParameterPack &uniformPack, const ShaderUniformBlock &block, const UniformValue &value) const { if (value.valueType() == UniformValue::NodeId) { Buffer *buffer = nullptr; if ((buffer = m_manager->bufferManager()->lookupResource(*value.constData())) != nullptr) { BlockToUBO uniformBlockUBO; uniformBlockUBO.m_blockIndex = block.m_index; uniformBlockUBO.m_bufferID = buffer->peerId(); uniformBlockUBO.m_needsUpdate = false; uniformPack.setUniformBuffer(std::move(uniformBlockUBO)); // Buffer update to GL buffer will be done at render time } } } void RenderView::setShaderStorageValue(ShaderParameterPack &uniformPack, const ShaderStorageBlock &block, const UniformValue &value) const { if (value.valueType() == UniformValue::NodeId) { Buffer *buffer = nullptr; if ((buffer = m_manager->bufferManager()->lookupResource(*value.constData())) != nullptr) { BlockToSSBO shaderStorageBlock; shaderStorageBlock.m_blockIndex = block.m_index; shaderStorageBlock.m_bufferID = buffer->peerId(); shaderStorageBlock.m_bindingIndex = block.m_binding; uniformPack.setShaderStorageBuffer(shaderStorageBlock); // Buffer update to GL buffer will be done at render time } } } void RenderView::setDefaultUniformBlockShaderDataValue(ShaderParameterPack &uniformPack, const std::vector &uniformsNamesIds, ShaderData *shaderData, const QString &structName) const { UniformBlockValueBuilder builder(uniformsNamesIds, m_manager->shaderDataManager(), m_manager->textureManager(), m_viewMatrix); // Build name-value map for the block builder.buildActiveUniformNameValueMapStructHelper(shaderData, structName); // Set uniform values for each entrie of the block name-value map QHash::const_iterator activeValuesIt = builder.activeUniformNamesToValue.constBegin(); const QHash::const_iterator activeValuesEnd = builder.activeUniformNamesToValue.constEnd(); // TO DO: Make the ShaderData store UniformValue while (activeValuesIt != activeValuesEnd) { setUniformValue(uniformPack, activeValuesIt.key(), UniformValue::fromVariant(activeValuesIt.value())); ++activeValuesIt; } } void RenderView::applyParameter(const Parameter *param, RenderCommand *command, const GLShader *shader) const noexcept { const int nameId = param->nameId(); const UniformValue &uniformValue = param->uniformValue(); const GLShader::ParameterKind kind = shader->categorizeVariable(nameId); switch (kind) { case GLShader::Uniform: { setUniformValue(command->m_parameterPack, nameId, uniformValue); break; } case GLShader::UBO: { setUniformBlockValue(command->m_parameterPack, shader->uniformBlockForBlockNameId(nameId), uniformValue); break; } case GLShader::SSBO: { setShaderStorageValue(command->m_parameterPack, shader->storageBlockForBlockNameId(nameId), uniformValue); break; } case GLShader::Struct: { ShaderData *shaderData = nullptr; if (uniformValue.valueType() == UniformValue::NodeId && (shaderData = m_manager->shaderDataManager()->lookupResource(*uniformValue.constData())) != nullptr) { // Try to check if we have a struct or array matching a QShaderData parameter setDefaultUniformBlockShaderDataValue(command->m_parameterPack, shader->uniformsNamesIds(), shaderData, StringToInt::lookupString(nameId)); } break; } default: break; } } void RenderView::setShaderAndUniforms(RenderCommand *command, const ParameterInfoList ¶meters, const Entity *entity) const { // The VAO Handle is set directly in the renderer thread so as to avoid having to use a mutex here // Set shader, technique, and effect by basically doing : // ShaderProgramManager[MaterialManager[frontentEntity->id()]->Effect->Techniques[TechniqueFilter->name]->RenderPasses[RenderPassFilter->name]]; // The Renderer knows that if one of those is null, a default material / technique / effect as to be used // Find all RenderPasses (in order) matching values set in the RenderPassFilter // Get list of parameters for the Material, Effect, and Technique // For each ParameterBinder in the RenderPass -> create a QUniformPack // Once that works, improve that to try and minimize QUniformPack updates GLShader *shader = command->m_glShader; if (shader == nullptr || !shader->isLoaded()) return; // If we have already build the uniforms previously, we should // only update values of uniforms that have changed // If parameters add been added/removed, the command would have been rebuild // and the parameter pack would be empty const bool updateUniformsOnly = command->m_parameterPack.submissionUniformIndices().size() > 0; if (!updateUniformsOnly) { // 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 // 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 fragOutputs; 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); } // Set default attributes command->m_activeAttributes = shader->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(); // Reserve amount of uniforms we are going to need command->m_parameterPack.reserve(shader->parameterPackSize()); } const size_t previousUniformCount = command->m_parameterPack.uniforms().size(); if (shader->hasActiveVariables()) { const std::vector &standardUniformNamesIds = shader->standardUniformNameIds(); // It only makes sense to update the standard uniforms if: // - Camera changed // - Entity transform changed // - Viewport/Surface changed for (const int uniformNameId : standardUniformNamesIds) setStandardUniformValue(command->m_parameterPack, uniformNameId, entity); ParameterInfoList::const_iterator it = parameters.cbegin(); const ParameterInfoList::const_iterator parametersEnd = parameters.cend(); while (it != parametersEnd) { const Parameter *param = m_manager->data(it->handle); applyParameter(param, command, shader); ++it; } // Lights updateLightUniforms(command, entity); } const size_t actualUniformCount = command->m_parameterPack.uniforms().size(); // Prepare the ShaderParameterPack based on the active uniforms of the shader if (!updateUniformsOnly || previousUniformCount != actualUniformCount) shader->prepareUniforms(command->m_parameterPack); } void RenderView::updateLightUniforms(RenderCommand *command, const Entity *entity) const { GLShader *shader = command->m_glShader; const std::vector &lightUniformNamesIds = shader->lightUniformsNamesIds(); if (!lightUniformNamesIds.empty()) { // 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 std::vector 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; }); m_lightSources = {lightSources.begin(), lightSources.begin() + std::min(lightSources.size(), size_t(MAX_LIGHTS)) }; } int lightIdx = 0; for (const LightSource &lightSource : m_lightSources) { if (lightIdx == MAX_LIGHTS) break; const Entity *lightEntity = lightSource.entity; const Matrix4x4 lightWorldTransform = *(lightEntity->worldTransform()); const Vector3D worldPos = lightWorldTransform.map(Vector3D(0.0f, 0.0f, 0.0f)); for (Light *light : lightSource.lights) { if (!light->isEnabled()) continue; ShaderData *shaderData = m_manager->shaderDataManager()->lookupResource(light->shaderData()); if (!shaderData) continue; if (lightIdx == MAX_LIGHTS) break; // Note: implicit conversion of values to UniformValue if (Qt3DCore::contains(lightUniformNamesIds, GLLights::LIGHT_TYPE_NAMES[lightIdx])) { setUniformValue(command->m_parameterPack, GLLights::LIGHT_POSITION_NAMES[lightIdx], worldPos); setUniformValue(command->m_parameterPack, GLLights::LIGHT_TYPE_NAMES[lightIdx], int(QAbstractLight::PointLight)); setUniformValue(command->m_parameterPack, GLLights::LIGHT_COLOR_NAMES[lightIdx], Vector3D(1.0f, 1.0f, 1.0f)); setUniformValue(command->m_parameterPack, GLLights::LIGHT_INTENSITY_NAMES[lightIdx], 0.5f); } else if (Qt3DCore::contains(lightUniformNamesIds, GLLights::LIGHT_TYPE_UNROLL_NAMES[lightIdx])) { setUniformValue(command->m_parameterPack, GLLights::LIGHT_POSITION_UNROLL_NAMES[lightIdx], worldPos); setUniformValue(command->m_parameterPack, GLLights::LIGHT_TYPE_UNROLL_NAMES[lightIdx], int(QAbstractLight::PointLight)); setUniformValue(command->m_parameterPack, GLLights::LIGHT_COLOR_UNROLL_NAMES[lightIdx], Vector3D(1.0f, 1.0f, 1.0f)); setUniformValue(command->m_parameterPack, GLLights::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 const Matrix4x4 *worldTransform = lightEntity->worldTransform(); if (worldTransform) shaderData->updateWorldTransform(*worldTransform); setDefaultUniformBlockShaderDataValue(command->m_parameterPack, shader->lightUniformsNamesIds(), shaderData, GLLights::LIGHT_STRUCT_NAMES[lightIdx]); setDefaultUniformBlockShaderDataValue(command->m_parameterPack, shader->lightUniformsNamesIds(), shaderData, GLLights::LIGHT_STRUCT_UNROLL_NAMES[lightIdx]); ++lightIdx; } } setUniformValue(command->m_parameterPack, GLLights::LIGHT_COUNT_NAME_ID, UniformValue(qMax((m_environmentLight ? 0 : 1), lightIdx))); // If no active light sources and no environment light, add a default light if (m_lightSources.empty() && !m_environmentLight) { // Note: implicit conversion of values to UniformValue if (Qt3DCore::contains(lightUniformNamesIds, GLLights::LIGHT_TYPE_NAMES[lightIdx])) { setUniformValue(command->m_parameterPack, GLLights::LIGHT_POSITION_NAMES[0], Vector3D(10.0f, 10.0f, 0.0f)); setUniformValue(command->m_parameterPack, GLLights::LIGHT_TYPE_NAMES[0], int(QAbstractLight::PointLight)); setUniformValue(command->m_parameterPack, GLLights::LIGHT_COLOR_NAMES[0], Vector3D(1.0f, 1.0f, 1.0f)); setUniformValue(command->m_parameterPack, GLLights::LIGHT_INTENSITY_NAMES[0], 0.5f); } else if (Qt3DCore::contains(lightUniformNamesIds, GLLights::LIGHT_TYPE_UNROLL_NAMES[lightIdx])) { setUniformValue(command->m_parameterPack, GLLights::LIGHT_POSITION_UNROLL_NAMES[0], Vector3D(10.0f, 10.0f, 0.0f)); setUniformValue(command->m_parameterPack, GLLights::LIGHT_TYPE_UNROLL_NAMES[0], int(QAbstractLight::PointLight)); setUniformValue(command->m_parameterPack, GLLights::LIGHT_COLOR_UNROLL_NAMES[0], Vector3D(1.0f, 1.0f, 1.0f)); setUniformValue(command->m_parameterPack, GLLights::LIGHT_INTENSITY_UNROLL_NAMES[0], 0.5f); } } } // Environment Light int envLightCount = 0; static const int irradianceStructId = StringToInt::lookupId(QLatin1String("envLight.irradiance")); static const int specularStructId = StringToInt::lookupId(QLatin1String("envLight.specular")); static const int irradianceId = StringToInt::lookupId(QLatin1String("envLightIrradiance")); static const int specularId = StringToInt::lookupId(QLatin1String("envLightSpecular")); if (m_environmentLight && m_environmentLight->isEnabled()) { ShaderData *shaderData = m_manager->shaderDataManager()->lookupResource(m_environmentLight->shaderData()); if (shaderData) { // EnvLight isn't part of the light uniform name ids setDefaultUniformBlockShaderDataValue(command->m_parameterPack, shader->uniformsNamesIds(), shaderData, QStringLiteral("envLight")); auto irr = shaderData->properties()["irradiance"].value.value(); auto spec = shaderData->properties()["specular"].value.value(); setUniformValue(command->m_parameterPack, irradianceId, irr); setUniformValue(command->m_parameterPack, specularId, spec); 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 setUniformValue(command->m_parameterPack, irradianceId, m_renderer->submissionContext()->maxTextureUnitsCount()); setUniformValue(command->m_parameterPack, irradianceStructId, m_renderer->submissionContext()->maxTextureUnitsCount()); setUniformValue(command->m_parameterPack, specularId, m_renderer->submissionContext()->maxTextureUnitsCount()); setUniformValue(command->m_parameterPack, specularStructId, m_renderer->submissionContext()->maxTextureUnitsCount()); } setUniformValue(command->m_parameterPack, StringToInt::lookupId(QStringLiteral("envLightCount")), envLightCount); } bool RenderView::hasBlitFramebufferInfo() const { return m_hasBlitFramebufferInfo; } void RenderView::setHasBlitFramebufferInfo(bool hasBlitFramebufferInfo) { m_hasBlitFramebufferInfo = hasBlitFramebufferInfo; } bool RenderView::shouldSkipSubmission() const { if (commandCount() > 0) return false; if (m_hasBlitFramebufferInfo) return false; if (m_isDownloadBuffersEnable) return false; if (m_showDebugOverlay) return false; if (!m_waitFences.empty() || !m_insertFenceIds.empty()) return false; if (m_clearBuffer != QClearBuffers::None) return false; if (!m_renderCaptureNodeId.isNull()) return false; return true; } BlitFramebufferInfo RenderView::blitFrameBufferInfo() const { return m_blitFrameBufferInfo; } void RenderView::setBlitFrameBufferInfo(const BlitFramebufferInfo &blitFrameBufferInfo) { m_blitFrameBufferInfo = blitFrameBufferInfo; } bool RenderView::isDownloadBuffersEnable() const { return m_isDownloadBuffersEnable; } void RenderView::setIsDownloadBuffersEnable(bool isDownloadBuffersEnable) { m_isDownloadBuffersEnable = isDownloadBuffersEnable; } } // namespace OpenGL } // namespace Render } // namespace Qt3DRender QT_END_NAMESPACE