| 1 | // Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). |
| 2 | // Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies). |
| 3 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
| 4 | |
| 5 | #include "renderview_p.h" |
| 6 | #include <Qt3DRender/qmaterial.h> |
| 7 | #include <Qt3DRender/qrenderaspect.h> |
| 8 | #include <Qt3DRender/qrendertarget.h> |
| 9 | #include <Qt3DRender/qabstractlight.h> |
| 10 | #include <Qt3DRender/private/sphere_p.h> |
| 11 | |
| 12 | #include <Qt3DRender/private/cameraselectornode_p.h> |
| 13 | #include <Qt3DRender/private/framegraphnode_p.h> |
| 14 | #include <Qt3DRender/private/layerfilternode_p.h> |
| 15 | #include <Qt3DRender/private/qparameter_p.h> |
| 16 | #include <Qt3DRender/private/cameralens_p.h> |
| 17 | #include <Qt3DRender/private/effect_p.h> |
| 18 | #include <Qt3DRender/private/entity_p.h> |
| 19 | #include <Qt3DRender/private/nodemanagers_p.h> |
| 20 | #include <Qt3DRender/private/layer_p.h> |
| 21 | #include <Qt3DRender/private/renderlogging_p.h> |
| 22 | #include <Qt3DRender/private/renderpassfilternode_p.h> |
| 23 | #include <Qt3DRender/private/renderpass_p.h> |
| 24 | #include <Qt3DRender/private/geometryrenderer_p.h> |
| 25 | #include <Qt3DRender/private/techniquefilternode_p.h> |
| 26 | #include <Qt3DRender/private/viewportnode_p.h> |
| 27 | #include <Qt3DRender/private/buffermanager_p.h> |
| 28 | #include <Qt3DRender/private/geometryrenderermanager_p.h> |
| 29 | #include <Qt3DRender/private/rendercapture_p.h> |
| 30 | #include <Qt3DRender/private/buffercapture_p.h> |
| 31 | #include <Qt3DRender/private/stringtoint_p.h> |
| 32 | #include <Qt3DRender/private/renderlogging_p.h> |
| 33 | #include <Qt3DRender/private/renderstateset_p.h> |
| 34 | #include <Qt3DRender/private/renderviewjobutils_p.h> |
| 35 | #include <Qt3DRender/private/uniformblockbuilder_p.h> |
| 36 | #include <Qt3DRender/private/clearbuffers_p.h> |
| 37 | #include <Qt3DRender/private/rendertargetselectornode_p.h> |
| 38 | #include <Qt3DRender/private/sortpolicy_p.h> |
| 39 | #include <Qt3DRender/private/techniquefilternode_p.h> |
| 40 | #include <Qt3DRender/private/managers_p.h> |
| 41 | #include <Qt3DRender/private/shaderdata_p.h> |
| 42 | #include <Qt3DRender/private/statesetnode_p.h> |
| 43 | #include <Qt3DRender/private/dispatchcompute_p.h> |
| 44 | #include <Qt3DRender/private/rendersurfaceselector_p.h> |
| 45 | #include <Qt3DRender/private/rendercapture_p.h> |
| 46 | #include <Qt3DRender/private/buffercapture_p.h> |
| 47 | #include <Qt3DRender/private/techniquemanager_p.h> |
| 48 | #include <Qt3DRender/private/memorybarrier_p.h> |
| 49 | #include <Qt3DRender/private/blitframebuffer_p.h> |
| 50 | #include <Qt3DRender/private/waitfence_p.h> |
| 51 | #include <Qt3DRender/private/rendercapture_p.h> |
| 52 | |
| 53 | #include <rendercommand_p.h> |
| 54 | #include <renderer_p.h> |
| 55 | #include <graphicscontext_p.h> |
| 56 | #include <submissioncontext_p.h> |
| 57 | #include <glresourcemanagers_p.h> |
| 58 | #include <Qt3DCore/qentity.h> |
| 59 | #include <QtGui/qsurface.h> |
| 60 | #include <algorithm> |
| 61 | #include <atomic> |
| 62 | #include <gllights_p.h> |
| 63 | #include <QDebug> |
| 64 | #if defined(QT3D_RENDER_VIEW_JOB_TIMINGS) |
| 65 | #include <QElapsedTimer> |
| 66 | #endif |
| 67 | |
| 68 | QT_BEGIN_NAMESPACE |
| 69 | |
| 70 | namespace Qt3DRender { |
| 71 | namespace Render { |
| 72 | namespace OpenGL { |
| 73 | |
| 74 | namespace { |
| 75 | |
| 76 | // register our QNodeId's as a metatype during program loading |
| 77 | Q_DECL_UNUSED const int qNodeIdTypeId = qMetaTypeId<Qt3DCore::QNodeId>(); |
| 78 | |
| 79 | std::atomic_bool wasInitialized{}; |
| 80 | |
| 81 | } // anonymous namespace |
| 82 | |
| 83 | RenderView::StandardUniformsNameToTypeHash RenderView::ms_standardUniformSetters; |
| 84 | |
| 85 | |
| 86 | RenderView::StandardUniformsNameToTypeHash RenderView::initializeStandardUniformSetters() |
| 87 | { |
| 88 | RenderView::StandardUniformsNameToTypeHash setters; |
| 89 | |
| 90 | setters.insert(key: Shader::modelMatrixNameId, value: ModelMatrix); |
| 91 | setters.insert(key: Shader::viewMatrixNameId, value: ViewMatrix); |
| 92 | setters.insert(key: Shader::projectionMatrixNameId, value: ProjectionMatrix); |
| 93 | setters.insert(key: Shader::modelViewMatrixNameId, value: ModelViewMatrix); |
| 94 | setters.insert(key: Shader::viewProjectionMatrixNameId, value: ViewProjectionMatrix); |
| 95 | setters.insert(key: Shader::modelViewProjectionNameId, value: ModelViewProjectionMatrix); |
| 96 | setters.insert(key: Shader::mvpNameId, value: ModelViewProjectionMatrix); |
| 97 | setters.insert(key: Shader::inverseModelMatrixNameId, value: InverseModelMatrix); |
| 98 | setters.insert(key: Shader::inverseViewMatrixNameId, value: InverseViewMatrix); |
| 99 | setters.insert(key: Shader::inverseProjectionMatrixNameId, value: InverseProjectionMatrix); |
| 100 | setters.insert(key: Shader::inverseModelViewNameId, value: InverseModelViewMatrix); |
| 101 | setters.insert(key: Shader::inverseViewProjectionMatrixNameId, value: InverseViewProjectionMatrix); |
| 102 | setters.insert(key: Shader::inverseModelViewProjectionNameId, value: InverseModelViewProjectionMatrix); |
| 103 | setters.insert(key: Shader::modelNormalMatrixNameId, value: ModelNormalMatrix); |
| 104 | setters.insert(key: Shader::modelViewNormalNameId, value: ModelViewNormalMatrix); |
| 105 | setters.insert(key: Shader::viewportMatrixNameId, value: ViewportMatrix); |
| 106 | setters.insert(key: Shader::inverseViewportMatrixNameId, value: InverseViewportMatrix); |
| 107 | setters.insert(key: Shader::aspectRatioNameId, value: AspectRatio); |
| 108 | setters.insert(key: Shader::exposureNameId, value: Exposure); |
| 109 | setters.insert(key: Shader::gammaNameId, value: Gamma); |
| 110 | setters.insert(key: Shader::timeNameId, value: Time); |
| 111 | setters.insert(key: Shader::eyePositionNameId, value: EyePosition); |
| 112 | setters.insert(key: Shader::skinningPaletteNameId, value: SkinningPalette); |
| 113 | setters.insert(key: Shader::yUpInFBOId, value: YUpInFBO); |
| 114 | setters.insert(key: Shader::yUpInNDCId, value: YUpInNDC); |
| 115 | |
| 116 | return setters; |
| 117 | } |
| 118 | |
| 119 | // TODO: Move this somewhere global where GraphicsContext::setViewport() can use it too |
| 120 | static QRectF resolveViewport(const QRectF &fractionalViewport, const QSize &surfaceSize) |
| 121 | { |
| 122 | return QRectF(fractionalViewport.x() * surfaceSize.width(), |
| 123 | (1.0 - fractionalViewport.y() - fractionalViewport.height()) * surfaceSize.height(), |
| 124 | fractionalViewport.width() * surfaceSize.width(), |
| 125 | fractionalViewport.height() * surfaceSize.height()); |
| 126 | } |
| 127 | |
| 128 | static Matrix4x4 getProjectionMatrix(const CameraLens *lens) |
| 129 | { |
| 130 | return lens ? lens->projection() : Matrix4x4(); |
| 131 | } |
| 132 | |
| 133 | UniformValue RenderView::standardUniformValue(RenderView::StandardUniform standardUniformType, |
| 134 | const Entity *entity) const |
| 135 | { |
| 136 | const Matrix4x4 &model = *(entity->worldTransform()); |
| 137 | |
| 138 | switch (standardUniformType) { |
| 139 | case ModelMatrix: |
| 140 | return UniformValue(model); |
| 141 | case ViewMatrix: |
| 142 | return UniformValue(m_viewMatrix); |
| 143 | case ProjectionMatrix: |
| 144 | return UniformValue(getProjectionMatrix(lens: m_renderCameraLens)); |
| 145 | case ModelViewMatrix: |
| 146 | return UniformValue(m_viewMatrix * model); |
| 147 | case ViewProjectionMatrix: |
| 148 | return UniformValue(getProjectionMatrix(lens: m_renderCameraLens) * m_viewMatrix); |
| 149 | case ModelViewProjectionMatrix: |
| 150 | return UniformValue(m_viewProjectionMatrix * model); |
| 151 | case InverseModelMatrix: |
| 152 | return UniformValue(model.inverted()); |
| 153 | case InverseViewMatrix: |
| 154 | return UniformValue(m_viewMatrix.inverted()); |
| 155 | case InverseProjectionMatrix: { |
| 156 | return UniformValue(getProjectionMatrix(lens: m_renderCameraLens).inverted()); |
| 157 | } |
| 158 | case InverseModelViewMatrix: |
| 159 | return UniformValue((m_viewMatrix * model).inverted()); |
| 160 | case InverseViewProjectionMatrix: { |
| 161 | const Matrix4x4 viewProjectionMatrix = getProjectionMatrix(lens: m_renderCameraLens) * m_viewMatrix; |
| 162 | return UniformValue(viewProjectionMatrix.inverted()); |
| 163 | } |
| 164 | case InverseModelViewProjectionMatrix: |
| 165 | return UniformValue((m_viewProjectionMatrix * model).inverted()); |
| 166 | case ModelNormalMatrix: |
| 167 | return UniformValue(convertToQMatrix4x4(v: model).normalMatrix()); |
| 168 | case ModelViewNormalMatrix: |
| 169 | return UniformValue(convertToQMatrix4x4(v: m_viewMatrix * model).normalMatrix()); |
| 170 | case ViewportMatrix: { |
| 171 | QMatrix4x4 viewportMatrix; |
| 172 | // TO DO: Implement on Matrix4x4 |
| 173 | viewportMatrix.viewport(rect: resolveViewport(fractionalViewport: m_viewport, surfaceSize: m_surfaceSize)); |
| 174 | return UniformValue(Matrix4x4(viewportMatrix)); |
| 175 | } |
| 176 | case InverseViewportMatrix: { |
| 177 | QMatrix4x4 viewportMatrix; |
| 178 | // TO DO: Implement on Matrix4x4 |
| 179 | viewportMatrix.viewport(rect: resolveViewport(fractionalViewport: m_viewport, surfaceSize: m_surfaceSize)); |
| 180 | return UniformValue(Matrix4x4(viewportMatrix.inverted())); |
| 181 | } |
| 182 | case AspectRatio: |
| 183 | return float(m_surfaceSize.width()) / std::max(a: 1.f, b: float(m_surfaceSize.height())); |
| 184 | case Exposure: |
| 185 | return UniformValue(m_renderCameraLens ? m_renderCameraLens->exposure() : 0.0f); |
| 186 | case Gamma: |
| 187 | return UniformValue(m_gamma); |
| 188 | case Time: |
| 189 | return UniformValue(float(m_renderer->time() / 1000000000.0f)); |
| 190 | case EyePosition: |
| 191 | return UniformValue(m_eyePos); |
| 192 | case SkinningPalette: { |
| 193 | const Armature *armature = entity->renderComponent<Armature>(); |
| 194 | if (!armature) { |
| 195 | qCWarning(Jobs, "Requesting skinningPalette uniform but no armature set on entity" ); |
| 196 | return UniformValue(); |
| 197 | } |
| 198 | return armature->skinningPaletteUniform(); |
| 199 | } |
| 200 | case YUpInNDC: |
| 201 | return UniformValue(0.0f); |
| 202 | case YUpInFBO: |
| 203 | return UniformValue(0.0f); |
| 204 | default: |
| 205 | Q_UNREACHABLE_RETURN(UniformValue()); |
| 206 | } |
| 207 | } |
| 208 | |
| 209 | /*! |
| 210 | \internal |
| 211 | Walks up the framegraph tree from \a fgLeaf and builds up as much state |
| 212 | as possible and populates \a rv. For cases where we can't get the specific state |
| 213 | (e.g. because it depends upon more than just the framegraph) we store the data from |
| 214 | the framegraph that will be needed to later when the rest of the data becomes available |
| 215 | */ |
| 216 | void RenderView::setRenderViewConfigFromFrameGraphLeafNode(RenderView *rv, const FrameGraphNode *fgLeaf) |
| 217 | { |
| 218 | // The specific RenderPass to be used is also dependent upon the Effect and TechniqueFilter |
| 219 | // which is referenced by the Material which is referenced by the RenderMesh. So we can |
| 220 | // only store the filter info in the RenderView structure and use it to do the resolving |
| 221 | // when we build the RenderCommand list. |
| 222 | const NodeManagers *manager = rv->nodeManagers(); |
| 223 | const FrameGraphNode *node = fgLeaf; |
| 224 | |
| 225 | while (node) { |
| 226 | FrameGraphNode::FrameGraphNodeType type = node->nodeType(); |
| 227 | if (node->isEnabled()) |
| 228 | switch (type) { |
| 229 | case FrameGraphNode::InvalidNodeType: |
| 230 | // A base FrameGraphNode, can be used for grouping purposes |
| 231 | break; |
| 232 | case FrameGraphNode::CameraSelector: |
| 233 | // Can be set only once and we take camera nearest to the leaf node |
| 234 | if (!rv->renderCameraLens()) { |
| 235 | const CameraSelector *cameraSelector = static_cast<const CameraSelector *>(node); |
| 236 | Entity *camNode = manager->renderNodesManager()->lookupResource(id: cameraSelector->cameraUuid()); |
| 237 | if (camNode) { |
| 238 | CameraLens *lens = camNode->renderComponent<CameraLens>(); |
| 239 | rv->setRenderCameraEntity(camNode); |
| 240 | if (lens && lens->isEnabled()) { |
| 241 | rv->setRenderCameraLens(lens); |
| 242 | // ViewMatrix and ProjectionMatrix are computed |
| 243 | // later in updateMatrices() |
| 244 | // since at this point the transformation matrices |
| 245 | // may not yet have been updated |
| 246 | } |
| 247 | } |
| 248 | } |
| 249 | break; |
| 250 | |
| 251 | case FrameGraphNode::LayerFilter: // Can be set multiple times in the tree |
| 252 | rv->appendLayerFilter(layerFilterId: static_cast<const LayerFilterNode *>(node)->peerId()); |
| 253 | break; |
| 254 | |
| 255 | case FrameGraphNode::ProximityFilter: // Can be set multiple times in the tree |
| 256 | rv->appendProximityFilterId(proximityFilterId: node->peerId()); |
| 257 | break; |
| 258 | |
| 259 | case FrameGraphNode::RenderPassFilter: |
| 260 | // Can be set once |
| 261 | // TODO: Amalgamate all render pass filters from leaf to root |
| 262 | if (!rv->renderPassFilter()) |
| 263 | rv->setRenderPassFilter(static_cast<const RenderPassFilter *>(node)); |
| 264 | break; |
| 265 | |
| 266 | case FrameGraphNode::RenderTarget: { |
| 267 | // Can be set once and we take render target nearest to the leaf node |
| 268 | const RenderTargetSelector *targetSelector = static_cast<const RenderTargetSelector *>(node); |
| 269 | Qt3DCore::QNodeId renderTargetUid = targetSelector->renderTargetUuid(); |
| 270 | HTarget renderTargetHandle = manager->renderTargetManager()->lookupHandle(id: renderTargetUid); |
| 271 | |
| 272 | // Add renderTarget Handle and build renderCommand AttachmentPack |
| 273 | if (!rv->renderTargetId()) { |
| 274 | rv->setRenderTargetId(renderTargetUid); |
| 275 | |
| 276 | RenderTarget *renderTarget = manager->renderTargetManager()->data(handle: renderTargetHandle); |
| 277 | if (renderTarget) |
| 278 | rv->setAttachmentPack(AttachmentPack(renderTarget, manager->attachmentManager())); |
| 279 | } |
| 280 | break; |
| 281 | } |
| 282 | |
| 283 | case FrameGraphNode::ClearBuffers: { |
| 284 | const ClearBuffers *cbNode = static_cast<const ClearBuffers *>(node); |
| 285 | rv->addClearBuffers(cb: cbNode); |
| 286 | break; |
| 287 | } |
| 288 | |
| 289 | case FrameGraphNode::TechniqueFilter: |
| 290 | // Can be set once |
| 291 | // TODO Amalgamate all technique filters from leaf to root |
| 292 | if (!rv->techniqueFilter()) |
| 293 | rv->setTechniqueFilter(static_cast<const TechniqueFilter *>(node)); |
| 294 | break; |
| 295 | |
| 296 | case FrameGraphNode::Viewport: { |
| 297 | // If the Viewport has already been set in a lower node |
| 298 | // Make it so that the new viewport is actually |
| 299 | // a subregion relative to that of the parent viewport |
| 300 | const ViewportNode *vpNode = static_cast<const ViewportNode *>(node); |
| 301 | rv->setViewport(ViewportNode::computeViewport(childViewport: rv->viewport(), parentViewport: vpNode)); |
| 302 | rv->setGamma(vpNode->gamma()); |
| 303 | break; |
| 304 | } |
| 305 | |
| 306 | case FrameGraphNode::SortMethod: { |
| 307 | const Render::SortPolicy *sortPolicy = static_cast<const Render::SortPolicy *>(node); |
| 308 | rv->addSortType(sortTypes: sortPolicy->sortTypes()); |
| 309 | break; |
| 310 | } |
| 311 | |
| 312 | case FrameGraphNode::SubtreeEnabler: |
| 313 | // Has no meaning here. SubtreeEnabler was used |
| 314 | // in a prior step to filter the list of RenderViewJobs |
| 315 | break; |
| 316 | |
| 317 | case FrameGraphNode::StateSet: { |
| 318 | const Render::StateSetNode *rStateSet = static_cast<const Render::StateSetNode *>(node); |
| 319 | // Add states from new stateSet we might be missing |
| 320 | // but don' t override existing states (lower StateSetNode always has priority) |
| 321 | if (rStateSet->hasRenderStates()) { |
| 322 | // Create global RenderStateSet for renderView if no stateSet was set before |
| 323 | RenderStateSet *stateSet = rv->getOrCreateStateSet(); |
| 324 | addStatesToRenderStateSet(stateSet, stateIds: rStateSet->renderStates(), manager: manager->renderStateManager()); |
| 325 | } |
| 326 | break; |
| 327 | } |
| 328 | |
| 329 | case FrameGraphNode::NoDraw: { |
| 330 | rv->setNoDraw(true); |
| 331 | break; |
| 332 | } |
| 333 | |
| 334 | case FrameGraphNode::FrustumCulling: { |
| 335 | rv->setFrustumCulling(true); |
| 336 | break; |
| 337 | } |
| 338 | |
| 339 | case FrameGraphNode::ComputeDispatch: { |
| 340 | const Render::DispatchCompute *dispatchCompute = static_cast<const Render::DispatchCompute *>(node); |
| 341 | rv->setCompute(true); |
| 342 | rv->setComputeWorkgroups(x: dispatchCompute->x(), |
| 343 | y: dispatchCompute->y(), |
| 344 | z: dispatchCompute->z()); |
| 345 | break; |
| 346 | } |
| 347 | |
| 348 | case FrameGraphNode::Lighting: { |
| 349 | // TODO |
| 350 | break; |
| 351 | } |
| 352 | |
| 353 | case FrameGraphNode::Surface: { |
| 354 | // Use the surface closest to leaf node |
| 355 | if (rv->surface() == nullptr) { |
| 356 | const Render::RenderSurfaceSelector *surfaceSelector |
| 357 | = static_cast<const Render::RenderSurfaceSelector *>(node); |
| 358 | rv->setSurface(surfaceSelector->surface()); |
| 359 | rv->setSurfaceSize(surfaceSelector->renderTargetSize() * surfaceSelector->devicePixelRatio()); |
| 360 | rv->setDevicePixelRatio(surfaceSelector->devicePixelRatio()); |
| 361 | } |
| 362 | break; |
| 363 | } |
| 364 | |
| 365 | case FrameGraphNode::DebugOverlay: |
| 366 | rv->setShowDebugOverlay(true); |
| 367 | break; |
| 368 | |
| 369 | case FrameGraphNode::RenderCapture: { |
| 370 | auto *renderCapture = const_cast<Render::RenderCapture *>( |
| 371 | static_cast<const Render::RenderCapture *>(node)); |
| 372 | if (rv->renderCaptureNodeId().isNull() && renderCapture->wasCaptureRequested()) { |
| 373 | rv->setRenderCaptureNodeId(renderCapture->peerId()); |
| 374 | rv->setRenderCaptureRequest(renderCapture->takeCaptureRequest()); |
| 375 | } |
| 376 | break; |
| 377 | } |
| 378 | |
| 379 | case FrameGraphNode::MemoryBarrier: { |
| 380 | const Render::MemoryBarrier *barrier = static_cast<const Render::MemoryBarrier *>(node); |
| 381 | rv->setMemoryBarrier(barrier->waitOperations()|rv->memoryBarrier()); |
| 382 | break; |
| 383 | } |
| 384 | |
| 385 | case FrameGraphNode::BufferCapture: { |
| 386 | auto *bufferCapture = const_cast<Render::BufferCapture *>( |
| 387 | static_cast<const Render::BufferCapture *>(node)); |
| 388 | if (bufferCapture != nullptr) |
| 389 | rv->setIsDownloadBuffersEnable(bufferCapture->isEnabled()); |
| 390 | break; |
| 391 | } |
| 392 | |
| 393 | case FrameGraphNode::BlitFramebuffer: { |
| 394 | const Render::BlitFramebuffer *blitFramebufferNode = |
| 395 | static_cast<const Render::BlitFramebuffer *>(node); |
| 396 | rv->setHasBlitFramebufferInfo(true); |
| 397 | BlitFramebufferInfo bfbInfo; |
| 398 | bfbInfo.sourceRenderTargetId = blitFramebufferNode->sourceRenderTargetId(); |
| 399 | bfbInfo.destinationRenderTargetId = blitFramebufferNode->destinationRenderTargetId(); |
| 400 | bfbInfo.sourceRect = blitFramebufferNode->sourceRect(); |
| 401 | bfbInfo.destinationRect = blitFramebufferNode->destinationRect(); |
| 402 | bfbInfo.sourceAttachmentPoint = blitFramebufferNode->sourceAttachmentPoint(); |
| 403 | bfbInfo.destinationAttachmentPoint = blitFramebufferNode->destinationAttachmentPoint(); |
| 404 | bfbInfo.interpolationMethod = blitFramebufferNode->interpolationMethod(); |
| 405 | rv->setBlitFrameBufferInfo(bfbInfo); |
| 406 | break; |
| 407 | } |
| 408 | |
| 409 | case FrameGraphNode::WaitFence: { |
| 410 | const Render::WaitFence *waitFence = static_cast<const Render::WaitFence *>(node); |
| 411 | rv->appendWaitFence(data: waitFence->data()); |
| 412 | break; |
| 413 | } |
| 414 | |
| 415 | case FrameGraphNode::SetFence: { |
| 416 | rv->appendInsertFenceId(setFenceId: node->peerId()); |
| 417 | break; |
| 418 | } |
| 419 | |
| 420 | case FrameGraphNode::NoPicking: |
| 421 | // Nothing to do RenderView wise for NoPicking |
| 422 | break; |
| 423 | |
| 424 | default: |
| 425 | // Should never get here |
| 426 | qCWarning(Backend) << "Unhandled FrameGraphNode type" ; |
| 427 | } |
| 428 | |
| 429 | node = node->parent(); |
| 430 | } |
| 431 | } |
| 432 | |
| 433 | |
| 434 | RenderView::RenderView() |
| 435 | { |
| 436 | if (Q_UNLIKELY(!wasInitialized.exchange(true))) { |
| 437 | // Needed as we can control the init order of static/global variables across compile units |
| 438 | // and this hash relies on the static StringToInt class |
| 439 | |
| 440 | RenderView::ms_standardUniformSetters = RenderView::initializeStandardUniformSetters(); |
| 441 | } |
| 442 | } |
| 443 | |
| 444 | RenderView::~RenderView() |
| 445 | { |
| 446 | } |
| 447 | |
| 448 | namespace { |
| 449 | |
| 450 | template<int SortType> |
| 451 | struct AdjacentSubRangeFinder |
| 452 | { |
| 453 | static bool adjacentSubRange(const RenderCommand &, const RenderCommand &) |
| 454 | { |
| 455 | Q_UNREACHABLE_RETURN(false); |
| 456 | } |
| 457 | }; |
| 458 | |
| 459 | template<> |
| 460 | struct AdjacentSubRangeFinder<QSortPolicy::StateChangeCost> |
| 461 | { |
| 462 | static bool adjacentSubRange(const RenderCommand &a, const RenderCommand &b) |
| 463 | { |
| 464 | return a.m_changeCost == b.m_changeCost; |
| 465 | } |
| 466 | }; |
| 467 | |
| 468 | template<> |
| 469 | struct AdjacentSubRangeFinder<QSortPolicy::BackToFront> |
| 470 | { |
| 471 | static bool adjacentSubRange(const RenderCommand &a, const RenderCommand &b) |
| 472 | { |
| 473 | return qFuzzyCompare(p1: a.m_depth, p2: b.m_depth); |
| 474 | } |
| 475 | }; |
| 476 | |
| 477 | template<> |
| 478 | struct AdjacentSubRangeFinder<QSortPolicy::Material> |
| 479 | { |
| 480 | static bool adjacentSubRange(const RenderCommand &a, const RenderCommand &b) |
| 481 | { |
| 482 | return a.m_glShader == b.m_glShader; |
| 483 | } |
| 484 | }; |
| 485 | |
| 486 | template<> |
| 487 | struct AdjacentSubRangeFinder<QSortPolicy::FrontToBack> |
| 488 | { |
| 489 | static bool adjacentSubRange(const RenderCommand &a, const RenderCommand &b) |
| 490 | { |
| 491 | return qFuzzyCompare(p1: a.m_depth, p2: b.m_depth); |
| 492 | } |
| 493 | }; |
| 494 | |
| 495 | template<> |
| 496 | struct AdjacentSubRangeFinder<QSortPolicy::Texture> |
| 497 | { |
| 498 | static bool adjacentSubRange(const RenderCommand &a, const RenderCommand &b) |
| 499 | { |
| 500 | // Two renderCommands are adjacent if one contains all the other command's textures |
| 501 | const std::vector<ShaderParameterPack::NamedResource> &texturesA = a.m_parameterPack.textures(); |
| 502 | const std::vector<ShaderParameterPack::NamedResource> &texturesB = b.m_parameterPack.textures(); |
| 503 | |
| 504 | const bool bBigger = texturesB.size() > texturesA.size(); |
| 505 | const std::vector<ShaderParameterPack::NamedResource> &smallestVector = bBigger ? texturesA : texturesB; |
| 506 | const std::vector<ShaderParameterPack::NamedResource> &biggestVector = bBigger ? texturesB : texturesA; |
| 507 | |
| 508 | const auto e = biggestVector.cend(); |
| 509 | for (const ShaderParameterPack::NamedResource &tex : smallestVector) { |
| 510 | if (std::find(first: biggestVector.begin(), last: e, val: tex) == e) |
| 511 | return false; |
| 512 | } |
| 513 | |
| 514 | return true; |
| 515 | } |
| 516 | }; |
| 517 | |
| 518 | template<typename Predicate> |
| 519 | int advanceUntilNonAdjacent(const EntityRenderCommandDataView *view, |
| 520 | const size_t beg, const size_t end, Predicate pred) |
| 521 | { |
| 522 | const std::vector<size_t> &commandIndices = view->indices; |
| 523 | const std::vector<RenderCommand> &commands = view->data.commands; |
| 524 | size_t i = beg + 1; |
| 525 | if (i < end) { |
| 526 | const size_t startIdx = commandIndices[beg]; |
| 527 | while (i < end) { |
| 528 | const size_t targetIdx = commandIndices[i]; |
| 529 | if (!pred(commands[startIdx], commands[targetIdx])) |
| 530 | break; |
| 531 | ++i; |
| 532 | } |
| 533 | } |
| 534 | return int(i); |
| 535 | } |
| 536 | |
| 537 | |
| 538 | template<int SortType> |
| 539 | struct SubRangeSorter |
| 540 | { |
| 541 | static void sortSubRange(EntityRenderCommandDataView *view, size_t begin, const size_t end) |
| 542 | { |
| 543 | Q_UNUSED(view); |
| 544 | Q_UNUSED(begin); |
| 545 | Q_UNUSED(end); |
| 546 | Q_UNREACHABLE(); |
| 547 | } |
| 548 | }; |
| 549 | |
| 550 | template<> |
| 551 | struct SubRangeSorter<QSortPolicy::StateChangeCost> |
| 552 | { |
| 553 | static void sortSubRange(EntityRenderCommandDataView *view, size_t begin, const size_t end) |
| 554 | { |
| 555 | std::vector<size_t> &commandIndices = view->indices; |
| 556 | const std::vector<RenderCommand> &commands = view->data.commands; |
| 557 | std::stable_sort(first: commandIndices.begin() + begin, last: commandIndices.begin() + end, |
| 558 | comp: [&commands] (const size_t &iA, const size_t &iB) { |
| 559 | const RenderCommand &a = commands[iA]; |
| 560 | const RenderCommand &b = commands[iB]; |
| 561 | return a.m_changeCost > b.m_changeCost; |
| 562 | }); |
| 563 | } |
| 564 | }; |
| 565 | |
| 566 | template<> |
| 567 | struct SubRangeSorter<QSortPolicy::BackToFront> |
| 568 | { |
| 569 | static void sortSubRange(EntityRenderCommandDataView *view, size_t begin, const size_t end) |
| 570 | { |
| 571 | std::vector<size_t> &commandIndices = view->indices; |
| 572 | const std::vector<RenderCommand> &commands = view->data.commands; |
| 573 | std::stable_sort(first: commandIndices.begin() + begin, last: commandIndices.begin() + end, |
| 574 | comp: [&commands] (const size_t &iA, const size_t &iB) { |
| 575 | const RenderCommand &a = commands[iA]; |
| 576 | const RenderCommand &b = commands[iB]; |
| 577 | return a.m_depth > b.m_depth; |
| 578 | }); |
| 579 | } |
| 580 | }; |
| 581 | |
| 582 | template<> |
| 583 | struct SubRangeSorter<QSortPolicy::Material> |
| 584 | { |
| 585 | static void sortSubRange(EntityRenderCommandDataView *view, size_t begin, const size_t end) |
| 586 | { |
| 587 | std::vector<size_t> &commandIndices = view->indices; |
| 588 | const std::vector<RenderCommand> &commands = view->data.commands; |
| 589 | std::stable_sort(first: commandIndices.begin() + begin, last: commandIndices.begin() + end, |
| 590 | comp: [&commands] (const size_t &iA, const size_t &iB) { |
| 591 | const RenderCommand &a = commands[iA]; |
| 592 | const RenderCommand &b = commands[iB]; |
| 593 | return a.m_glShader > b.m_glShader; |
| 594 | }); |
| 595 | } |
| 596 | }; |
| 597 | |
| 598 | template<> |
| 599 | struct SubRangeSorter<QSortPolicy::FrontToBack> |
| 600 | { |
| 601 | static void sortSubRange(EntityRenderCommandDataView *view, size_t begin, const size_t end) |
| 602 | { |
| 603 | std::vector<size_t> &commandIndices = view->indices; |
| 604 | const std::vector<RenderCommand> &commands = view->data.commands; |
| 605 | std::stable_sort(first: commandIndices.begin() + begin, last: commandIndices.begin() + end, |
| 606 | comp: [&commands] (const size_t &iA, const size_t &iB) { |
| 607 | const RenderCommand &a = commands[iA]; |
| 608 | const RenderCommand &b = commands[iB]; |
| 609 | return a.m_depth < b.m_depth; |
| 610 | }); |
| 611 | } |
| 612 | }; |
| 613 | |
| 614 | template<> |
| 615 | struct SubRangeSorter<QSortPolicy::Texture> |
| 616 | { |
| 617 | static void sortSubRange(EntityRenderCommandDataView *view, size_t begin, const size_t end) |
| 618 | { |
| 619 | #ifndef Q_OS_WIN |
| 620 | std::vector<size_t> &commandIndices = view->indices; |
| 621 | const std::vector<RenderCommand> &commands = view->data.commands; |
| 622 | std::stable_sort(first: commandIndices.begin() + begin, last: commandIndices.begin() + end, |
| 623 | comp: [&commands] (const int &iA, const int &iB) { |
| 624 | const RenderCommand &a = commands[iA]; |
| 625 | const RenderCommand &b = commands[iB]; |
| 626 | const std::vector<ShaderParameterPack::NamedResource> &texturesA = a.m_parameterPack.textures(); |
| 627 | const std::vector<ShaderParameterPack::NamedResource> &texturesB = b.m_parameterPack.textures(); |
| 628 | |
| 629 | const bool bBigger = texturesB.size() > texturesA.size(); |
| 630 | const std::vector<ShaderParameterPack::NamedResource> &smallestVector = bBigger ? texturesA : texturesB; |
| 631 | const std::vector<ShaderParameterPack::NamedResource> &biggestVector = bBigger ? texturesB : texturesA; |
| 632 | |
| 633 | size_t identicalTextureCount = 0; |
| 634 | const auto e = biggestVector.cend(); |
| 635 | for (const ShaderParameterPack::NamedResource &tex : smallestVector) { |
| 636 | if (std::find(first: biggestVector.begin(), last: e, val: tex) != e) |
| 637 | ++identicalTextureCount; |
| 638 | } |
| 639 | |
| 640 | return identicalTextureCount < smallestVector.size(); |
| 641 | }); |
| 642 | #else |
| 643 | Q_UNUSED(view); |
| 644 | Q_UNUSED(begin); |
| 645 | Q_UNUSED(end); |
| 646 | #endif |
| 647 | } |
| 648 | }; |
| 649 | |
| 650 | int findSubRange(const EntityRenderCommandDataView *view, |
| 651 | const int begin, const int end, |
| 652 | const QSortPolicy::SortType sortType) |
| 653 | { |
| 654 | switch (sortType) { |
| 655 | case QSortPolicy::StateChangeCost: |
| 656 | return advanceUntilNonAdjacent(view, beg: begin, end, pred: AdjacentSubRangeFinder<QSortPolicy::StateChangeCost>::adjacentSubRange); |
| 657 | case QSortPolicy::BackToFront: |
| 658 | return advanceUntilNonAdjacent(view, beg: begin, end, pred: AdjacentSubRangeFinder<QSortPolicy::BackToFront>::adjacentSubRange); |
| 659 | case QSortPolicy::Material: |
| 660 | return advanceUntilNonAdjacent(view, beg: begin, end, pred: AdjacentSubRangeFinder<QSortPolicy::Material>::adjacentSubRange); |
| 661 | case QSortPolicy::FrontToBack: |
| 662 | return advanceUntilNonAdjacent(view, beg: begin, end, pred: AdjacentSubRangeFinder<QSortPolicy::FrontToBack>::adjacentSubRange); |
| 663 | case QSortPolicy::Texture: |
| 664 | return advanceUntilNonAdjacent(view, beg: begin, end, pred: AdjacentSubRangeFinder<QSortPolicy::Texture>::adjacentSubRange); |
| 665 | case QSortPolicy::Uniform: |
| 666 | return end; |
| 667 | default: |
| 668 | Q_UNREACHABLE_RETURN(end); |
| 669 | } |
| 670 | } |
| 671 | |
| 672 | void sortByMaterial(EntityRenderCommandDataView *view, int begin, const int end) |
| 673 | { |
| 674 | // We try to arrange elements so that their rendering cost is minimized for a given shader |
| 675 | std::vector<size_t> &commandIndices = view->indices; |
| 676 | const std::vector<RenderCommand> &commands = view->data.commands; |
| 677 | int rangeEnd = advanceUntilNonAdjacent(view, beg: begin, end, pred: AdjacentSubRangeFinder<QSortPolicy::Material>::adjacentSubRange); |
| 678 | while (begin != end) { |
| 679 | if (begin + 1 < rangeEnd) { |
| 680 | std::stable_sort(first: commandIndices.begin() + begin + 1, last: commandIndices.begin() + rangeEnd, |
| 681 | comp: [&commands] (size_t iA, size_t iB) { |
| 682 | const RenderCommand &a = commands[iA]; |
| 683 | const RenderCommand &b = commands[iB]; |
| 684 | return a.m_material.handle() < b.m_material.handle(); |
| 685 | }); |
| 686 | } |
| 687 | begin = rangeEnd; |
| 688 | rangeEnd = advanceUntilNonAdjacent(view, beg: begin, end, pred: AdjacentSubRangeFinder<QSortPolicy::Material>::adjacentSubRange); |
| 689 | } |
| 690 | } |
| 691 | |
| 692 | void sortCommandRange(EntityRenderCommandDataView *view, int begin, int end, const int level, |
| 693 | const QList<Qt3DRender::QSortPolicy::SortType> &sortingTypes) |
| 694 | { |
| 695 | if (level >= sortingTypes.size()) |
| 696 | return; |
| 697 | |
| 698 | switch (sortingTypes.at(i: level)) { |
| 699 | case QSortPolicy::StateChangeCost: |
| 700 | SubRangeSorter<QSortPolicy::StateChangeCost>::sortSubRange(view, begin, end); |
| 701 | break; |
| 702 | case QSortPolicy::BackToFront: |
| 703 | SubRangeSorter<QSortPolicy::BackToFront>::sortSubRange(view, begin, end); |
| 704 | break; |
| 705 | case QSortPolicy::Material: |
| 706 | // Groups all same shader DNA together |
| 707 | SubRangeSorter<QSortPolicy::Material>::sortSubRange(view, begin, end); |
| 708 | // Group all same material together (same parameters most likely) |
| 709 | sortByMaterial(view, begin, end); |
| 710 | break; |
| 711 | case QSortPolicy::FrontToBack: |
| 712 | SubRangeSorter<QSortPolicy::FrontToBack>::sortSubRange(view, begin, end); |
| 713 | break; |
| 714 | case QSortPolicy::Texture: |
| 715 | SubRangeSorter<QSortPolicy::Texture>::sortSubRange(view, begin, end); |
| 716 | break; |
| 717 | case QSortPolicy::Uniform: |
| 718 | break; |
| 719 | default: |
| 720 | Q_UNREACHABLE(); |
| 721 | } |
| 722 | |
| 723 | // For all sub ranges of adjacent item for sortType[i] |
| 724 | // Perform filtering with sortType[i + 1] |
| 725 | int rangeEnd = findSubRange(view, begin, end, sortType: sortingTypes.at(i: level)); |
| 726 | while (begin != end) { |
| 727 | sortCommandRange(view, begin, end: rangeEnd, level: level + 1, sortingTypes); |
| 728 | begin = rangeEnd; |
| 729 | rangeEnd = findSubRange(view, begin, end, sortType: sortingTypes.at(i: level)); |
| 730 | } |
| 731 | } |
| 732 | |
| 733 | } // anonymous |
| 734 | |
| 735 | void RenderView::sort() |
| 736 | { |
| 737 | assert(m_renderCommandDataView); |
| 738 | // Compares the bitsetKey of the RenderCommands |
| 739 | // Key[Depth | StateCost | Shader] |
| 740 | sortCommandRange(view: m_renderCommandDataView.data(), begin: 0, end: int(m_renderCommandDataView->size()), level: 0, sortingTypes: m_sortingTypes); |
| 741 | |
| 742 | // For RenderCommand with the same shader |
| 743 | // We compute the adjacent change cost |
| 744 | |
| 745 | // Only perform uniform minimization if we explicitly asked for it |
| 746 | if (!m_sortingTypes.contains(t: QSortPolicy::Uniform)) |
| 747 | return; |
| 748 | |
| 749 | // Minimize uniform changes |
| 750 | size_t i = 0; |
| 751 | std::vector<RenderCommand> &commands = m_renderCommandDataView->data.commands; |
| 752 | const std::vector<size_t> &indices = m_renderCommandDataView->indices; |
| 753 | const size_t commandSize = indices.size(); |
| 754 | |
| 755 | while (i < commandSize) { |
| 756 | size_t j = i; |
| 757 | |
| 758 | // Advance while commands share the same shader |
| 759 | while (i < commandSize && |
| 760 | commands[indices[j]].m_glShader == commands[indices[i]].m_glShader) |
| 761 | ++i; |
| 762 | |
| 763 | if (i - j > 0) { // Several commands have the same shader, so we minimize uniform changes |
| 764 | PackUniformHash cachedUniforms = commands[indices[j++]].m_parameterPack.uniforms(); |
| 765 | |
| 766 | while (j < i) { |
| 767 | // We need the reference here as we are modifying the original container |
| 768 | // not the copy |
| 769 | PackUniformHash &uniforms = commands[indices[j]].m_parameterPack.m_uniforms; |
| 770 | |
| 771 | for (size_t u = 0; u < uniforms.keys.size();) { |
| 772 | // We are comparing the values: |
| 773 | // - raw uniform values |
| 774 | // - the texture Node id if the uniform represents a texture |
| 775 | // since all textures are assigned texture units before the RenderCommands |
| 776 | // sharing the same material (shader) are rendered, we can't have the case |
| 777 | // where two uniforms, referencing the same texture eventually have 2 different |
| 778 | // texture unit values |
| 779 | const int uniformNameId = uniforms.keys.at(n: u); |
| 780 | const UniformValue &refValue = cachedUniforms.value(key: uniformNameId); |
| 781 | const UniformValue &newValue = uniforms.values.at(n: u); |
| 782 | if (newValue == refValue) { |
| 783 | uniforms.erase(idx: int(u)); |
| 784 | } else { |
| 785 | // Record updated value so that subsequent comparison |
| 786 | // for the next command will be made againts latest |
| 787 | // uniform value |
| 788 | cachedUniforms.insert(key: uniformNameId, value: newValue); |
| 789 | ++u; |
| 790 | } |
| 791 | } |
| 792 | ++j; |
| 793 | } |
| 794 | } |
| 795 | } |
| 796 | } |
| 797 | |
| 798 | void RenderView::setRenderer(Renderer *renderer) |
| 799 | { |
| 800 | m_renderer = renderer; |
| 801 | m_manager = renderer->nodeManagers(); |
| 802 | } |
| 803 | |
| 804 | RenderStateSet *RenderView::getOrCreateStateSet() |
| 805 | { |
| 806 | if (!m_stateSet) |
| 807 | m_stateSet.reset(other: new RenderStateSet()); |
| 808 | return m_stateSet.data(); |
| 809 | } |
| 810 | |
| 811 | void RenderView::addClearBuffers(const ClearBuffers *cb) { |
| 812 | QClearBuffers::BufferTypeFlags type = cb->type(); |
| 813 | |
| 814 | if (type & QClearBuffers::StencilBuffer) { |
| 815 | m_clearStencilValue = cb->clearStencilValue(); |
| 816 | m_clearBuffer |= QClearBuffers::StencilBuffer; |
| 817 | } |
| 818 | if (type & QClearBuffers::DepthBuffer) { |
| 819 | m_clearDepthValue = cb->clearDepthValue(); |
| 820 | m_clearBuffer |= QClearBuffers::DepthBuffer; |
| 821 | } |
| 822 | // keep track of global ClearColor (if set) and collect all DrawBuffer-specific |
| 823 | // ClearColors |
| 824 | if (type & QClearBuffers::ColorBuffer) { |
| 825 | ClearBufferInfo clearBufferInfo; |
| 826 | clearBufferInfo.clearColor = cb->clearColor(); |
| 827 | |
| 828 | if (cb->clearsAllColorBuffers()) { |
| 829 | m_globalClearColorBuffer = clearBufferInfo; |
| 830 | m_clearBuffer |= QClearBuffers::ColorBuffer; |
| 831 | } else { |
| 832 | if (cb->bufferId()) { |
| 833 | const RenderTargetOutput *targetOutput = m_manager->attachmentManager()->lookupResource(id: cb->bufferId()); |
| 834 | if (targetOutput) { |
| 835 | clearBufferInfo.attchmentPoint = targetOutput->point(); |
| 836 | // Note: a job is later performed to find the drawIndex from the buffer attachment point |
| 837 | // using the AttachmentPack |
| 838 | m_specificClearColorBuffers.push_back(x: clearBufferInfo); |
| 839 | } |
| 840 | } |
| 841 | } |
| 842 | } |
| 843 | } |
| 844 | |
| 845 | // If we are there, we know that entity had a GeometryRenderer + Material |
| 846 | EntityRenderCommandData RenderView::buildDrawRenderCommands(const Entity **entities, |
| 847 | int offset, int count) const |
| 848 | { |
| 849 | GLShaderManager *glShaderManager = m_renderer->glResourceManagers()->glShaderManager(); |
| 850 | EntityRenderCommandData commands; |
| 851 | |
| 852 | commands.reserve(size: count); |
| 853 | |
| 854 | for (int i = 0; i < count; ++i) { |
| 855 | const int idx = offset + i; |
| 856 | const Entity *entity = entities[idx]; |
| 857 | GeometryRenderer *geometryRenderer = nullptr; |
| 858 | HGeometryRenderer geometryRendererHandle = entity->componentHandle<GeometryRenderer>(); |
| 859 | |
| 860 | // There is a geometry renderer with geometry |
| 861 | if ((geometryRenderer = m_manager->geometryRendererManager()->data(handle: geometryRendererHandle)) != nullptr |
| 862 | && geometryRenderer->isEnabled() |
| 863 | && !geometryRenderer->geometryId().isNull()) { |
| 864 | |
| 865 | const Qt3DCore::QNodeId materialComponentId = entity->componentUuid<Material>(); |
| 866 | const HMaterial materialHandle = entity->componentHandle<Material>(); |
| 867 | const std::vector<RenderPassParameterData> &renderPassData = m_parameters.value(key: materialComponentId); |
| 868 | |
| 869 | HGeometry geometryHandle = m_manager->geometryManager()->lookupHandle(id: geometryRenderer->geometryId()); |
| 870 | Geometry *geometry = m_manager->geometryManager()->data(handle: geometryHandle); |
| 871 | |
| 872 | // 1 RenderCommand per RenderPass pass on an Entity with a Mesh |
| 873 | for (const RenderPassParameterData &passData : renderPassData) { |
| 874 | // Add the RenderPass Parameters |
| 875 | RenderCommand command = {}; |
| 876 | command.m_geometryRenderer = geometryRendererHandle; |
| 877 | command.m_geometry = geometryHandle; |
| 878 | |
| 879 | command.m_material = materialHandle; |
| 880 | // For RenderPass based states we use the globally set RenderState |
| 881 | // if no renderstates are defined as part of the pass. That means: |
| 882 | // RenderPass { renderStates: [] } will use the states defined by |
| 883 | // StateSet in the FrameGraph |
| 884 | RenderPass *pass = passData.pass; |
| 885 | if (pass->hasRenderStates()) { |
| 886 | command.m_stateSet = RenderStateSetPtr::create(); |
| 887 | addStatesToRenderStateSet(stateSet: command.m_stateSet.data(), stateIds: pass->renderStates(), manager: m_manager->renderStateManager()); |
| 888 | if (m_stateSet) |
| 889 | command.m_stateSet->merge(other: m_stateSet.data()); |
| 890 | command.m_changeCost = m_renderer->defaultRenderState()->changeCost(previousState: command.m_stateSet.data()); |
| 891 | } |
| 892 | command.m_shaderId = pass->shaderProgram(); |
| 893 | |
| 894 | // We try to resolve the m_glShader here. If the shader exist, |
| 895 | // it won't be null and will allow us to full process the |
| 896 | // command over a single frame. Otherwise, the shader will be |
| 897 | // loaded at the next submission time and the command will only |
| 898 | // be fully valid on the next frame. Additionally, that way, if |
| 899 | // a commands keeps being rebuilt, frame after frame, it will |
| 900 | // still be visible on screen as long as the shader exists |
| 901 | command.m_glShader = glShaderManager->lookupResource(shaderId: command.m_shaderId); |
| 902 | |
| 903 | // It takes two frames to have a valid command as we can only |
| 904 | // reference a glShader at frame n if it has been loaded at frame n - 1 |
| 905 | if (!command.m_glShader) |
| 906 | continue; |
| 907 | |
| 908 | { // Scoped to show extent |
| 909 | |
| 910 | // Update the draw command with what's going to be needed for the drawing |
| 911 | int primitiveCount = geometryRenderer->vertexCount(); |
| 912 | int estimatedCount = 0; |
| 913 | Attribute *indexAttribute = nullptr; |
| 914 | Attribute *indirectAttribute = nullptr; |
| 915 | |
| 916 | const QList<Qt3DCore::QNodeId> attributeIds = geometry->attributes(); |
| 917 | for (Qt3DCore::QNodeId attributeId : attributeIds) { |
| 918 | Attribute *attribute = m_manager->attributeManager()->lookupResource(id: attributeId); |
| 919 | switch (attribute->attributeType()) { |
| 920 | case Qt3DCore::QAttribute::IndexAttribute: |
| 921 | indexAttribute = attribute; |
| 922 | break; |
| 923 | case Qt3DCore::QAttribute::DrawIndirectAttribute: |
| 924 | indirectAttribute = attribute; |
| 925 | break; |
| 926 | case Qt3DCore::QAttribute::VertexAttribute: |
| 927 | estimatedCount = std::max(a: int(attribute->count()), b: estimatedCount); |
| 928 | break; |
| 929 | default: |
| 930 | Q_UNREACHABLE(); |
| 931 | break; |
| 932 | } |
| 933 | } |
| 934 | |
| 935 | command.m_drawIndexed = (indexAttribute != nullptr); |
| 936 | command.m_drawIndirect = (indirectAttribute != nullptr); |
| 937 | |
| 938 | // Update the draw command with all the information required for the drawing |
| 939 | if (command.m_drawIndexed) { |
| 940 | command.m_indexAttributeDataType = GraphicsContext::glDataTypeFromAttributeDataType(dataType: indexAttribute->vertexBaseType()); |
| 941 | command.m_indexAttributeByteOffset = indexAttribute->byteOffset() + geometryRenderer->indexBufferByteOffset(); |
| 942 | } |
| 943 | |
| 944 | // Note: we only care about the primitiveCount when using direct draw calls |
| 945 | // For indirect draw calls it is assumed the buffer was properly set already |
| 946 | if (command.m_drawIndirect) { |
| 947 | command.m_indirectAttributeByteOffset = indirectAttribute->byteOffset(); |
| 948 | command.m_indirectDrawBuffer = m_manager->bufferManager()->lookupHandle(id: indirectAttribute->bufferId()); |
| 949 | } else { |
| 950 | // Use the count specified by the GeometryRender |
| 951 | // If not specify use the indexAttribute count if present |
| 952 | // Otherwise tries to use the count from the attribute with the highest count |
| 953 | if (primitiveCount == 0) { |
| 954 | if (indexAttribute) |
| 955 | primitiveCount = indexAttribute->count(); |
| 956 | else |
| 957 | primitiveCount = estimatedCount; |
| 958 | } |
| 959 | } |
| 960 | |
| 961 | command.m_primitiveCount = primitiveCount; |
| 962 | command.m_primitiveType = geometryRenderer->primitiveType(); |
| 963 | command.m_primitiveRestartEnabled = geometryRenderer->primitiveRestartEnabled(); |
| 964 | command.m_restartIndexValue = geometryRenderer->restartIndexValue(); |
| 965 | command.m_firstInstance = geometryRenderer->firstInstance(); |
| 966 | command.m_instanceCount = geometryRenderer->instanceCount(); |
| 967 | command.m_firstVertex = geometryRenderer->firstVertex(); |
| 968 | command.m_indexOffset = geometryRenderer->indexOffset(); |
| 969 | command.m_verticesPerPatch = geometryRenderer->verticesPerPatch(); |
| 970 | } // scope |
| 971 | |
| 972 | |
| 973 | commands.push_back(e: entity, |
| 974 | c: std::move(command), |
| 975 | p: std::move(passData)); |
| 976 | } |
| 977 | } |
| 978 | } |
| 979 | |
| 980 | return commands; |
| 981 | } |
| 982 | |
| 983 | EntityRenderCommandData RenderView::buildComputeRenderCommands(const Entity **entities, |
| 984 | int offset, int count) const |
| 985 | { |
| 986 | // If the RenderView contains only a ComputeDispatch then it cares about |
| 987 | // A ComputeDispatch is also implicitely a NoDraw operation |
| 988 | // enabled flag |
| 989 | // layer component |
| 990 | // material/effect/technique/parameters/filters/ |
| 991 | EntityRenderCommandData commands; |
| 992 | GLShaderManager *glShaderManager = m_renderer->glResourceManagers()->glShaderManager(); |
| 993 | |
| 994 | commands.reserve(size: count); |
| 995 | |
| 996 | for (int i = 0; i < count; ++i) { |
| 997 | const int idx = offset + i; |
| 998 | const Entity *entity = entities[idx]; |
| 999 | ComputeCommand *computeJob = nullptr; |
| 1000 | HComputeCommand computeCommandHandle = entity->componentHandle<ComputeCommand>(); |
| 1001 | if ((computeJob = nodeManagers()->computeJobManager()->data(handle: computeCommandHandle)) != nullptr |
| 1002 | && computeJob->isEnabled()) { |
| 1003 | |
| 1004 | const Qt3DCore::QNodeId materialComponentId = entity->componentUuid<Material>(); |
| 1005 | const std::vector<RenderPassParameterData> &renderPassData = m_parameters.value(key: materialComponentId); |
| 1006 | |
| 1007 | // 1 RenderCommand per RenderPass pass on an Entity with a Mesh |
| 1008 | for (const RenderPassParameterData &passData : renderPassData) { |
| 1009 | // Add the RenderPass Parameters |
| 1010 | RenderCommand command = {}; |
| 1011 | RenderPass *pass = passData.pass; |
| 1012 | |
| 1013 | if (pass->hasRenderStates()) { |
| 1014 | command.m_stateSet = RenderStateSetPtr::create(); |
| 1015 | addStatesToRenderStateSet(stateSet: command.m_stateSet.data(), stateIds: pass->renderStates(), manager: m_manager->renderStateManager()); |
| 1016 | |
| 1017 | // Merge per pass stateset with global stateset |
| 1018 | // so that the local stateset only overrides |
| 1019 | if (m_stateSet != nullptr) |
| 1020 | command.m_stateSet->merge(other: m_stateSet.data()); |
| 1021 | command.m_changeCost = m_renderer->defaultRenderState()->changeCost(previousState: command.m_stateSet.data()); |
| 1022 | } |
| 1023 | command.m_shaderId = pass->shaderProgram(); |
| 1024 | command.m_glShader = glShaderManager->lookupResource(shaderId: command.m_shaderId); |
| 1025 | |
| 1026 | // It takes two frames to have a valid command as we can only |
| 1027 | // reference a glShader at frame n if it has been loaded at frame n - 1 |
| 1028 | if (!command.m_glShader) |
| 1029 | continue; |
| 1030 | |
| 1031 | command.m_computeCommand = computeCommandHandle; |
| 1032 | command.m_type = RenderCommand::Compute; |
| 1033 | command.m_workGroups[0] = std::max(a: m_workGroups[0], b: computeJob->x()); |
| 1034 | command.m_workGroups[1] = std::max(a: m_workGroups[1], b: computeJob->y()); |
| 1035 | command.m_workGroups[2] = std::max(a: m_workGroups[2], b: computeJob->z()); |
| 1036 | |
| 1037 | commands.push_back(e: entity, |
| 1038 | c: std::move(command), |
| 1039 | p: std::move(passData)); |
| 1040 | } |
| 1041 | } |
| 1042 | } |
| 1043 | |
| 1044 | return commands; |
| 1045 | } |
| 1046 | |
| 1047 | void RenderView::updateRenderCommand(const EntityRenderCommandDataSubView &subView) |
| 1048 | { |
| 1049 | subView.forEach(func: [this] (const Entity *entity, |
| 1050 | const RenderPassParameterData &passData, |
| 1051 | RenderCommand &command) { |
| 1052 | if (command.m_type == RenderCommand::Draw) { |
| 1053 | // Project the camera-to-object-center vector onto the camera |
| 1054 | // view vector. This gives a depth value suitable as the key |
| 1055 | // for BackToFront sorting. |
| 1056 | command.m_depth = Vector3D::dotProduct(a: entity->worldBoundingVolume()->center() - m_eyePos, b: m_eyeViewDir); |
| 1057 | |
| 1058 | auto geometryRenderer = m_manager->geometryRendererManager()->data(handle: command.m_geometryRenderer); |
| 1059 | if (geometryRenderer && !qFuzzyCompare(p1: geometryRenderer->sortIndex(), p2: -1.f)) |
| 1060 | command.m_depth = geometryRenderer->sortIndex(); |
| 1061 | } else { // Compute |
| 1062 | // Note: if frameCount has reached 0 in the previous frame, isEnabled |
| 1063 | // would be false |
| 1064 | ComputeCommand *computeJob = m_manager->computeJobManager()->data(handle: command.m_computeCommand); |
| 1065 | if (computeJob->runType() == QComputeCommand::Manual) |
| 1066 | computeJob->updateFrameCount(); |
| 1067 | } |
| 1068 | |
| 1069 | // setShaderAndUniforms can initialize a localData |
| 1070 | // make sure this is cleared before we leave this function |
| 1071 | setShaderAndUniforms(command: &command, |
| 1072 | parameters: passData.parameterInfo, |
| 1073 | entity); |
| 1074 | }); |
| 1075 | } |
| 1076 | |
| 1077 | void RenderView::updateMatrices() |
| 1078 | { |
| 1079 | if (m_renderCameraNode && m_renderCameraLens && m_renderCameraLens->isEnabled()) { |
| 1080 | auto transform = m_renderCameraNode->renderComponent<Transform>(); |
| 1081 | if (m_renderCameraNode->isParentLessTransform() && transform && transform->hasViewMatrix()) { |
| 1082 | // optimization: if the entity is a QCamera and it doesn't have a parent with a transform component, |
| 1083 | // then we use the frontend version of the viewMatrix to avoid extra calculations that may introduce |
| 1084 | // rounding errors |
| 1085 | setViewMatrix(transform->viewMatrix()); |
| 1086 | } else { |
| 1087 | const Matrix4x4 cameraWorld = *(m_renderCameraNode->worldTransform()); |
| 1088 | setViewMatrix(m_renderCameraLens->viewMatrix(worldTransform: cameraWorld)); |
| 1089 | } |
| 1090 | |
| 1091 | setViewProjectionMatrix(m_renderCameraLens->projection() * viewMatrix()); |
| 1092 | //To get the eyePosition of the camera, we need to use the inverse of the |
| 1093 | //camera's worldTransform matrix. |
| 1094 | const Matrix4x4 inverseWorldTransform = viewMatrix().inverted(); |
| 1095 | const Vector3D eyePosition(inverseWorldTransform.column(index: 3)); |
| 1096 | setEyePosition(eyePosition); |
| 1097 | |
| 1098 | // Get the viewing direction of the camera. Use the normal matrix to |
| 1099 | // ensure non-uniform scale works too. |
| 1100 | const QMatrix3x3 normalMat = convertToQMatrix4x4(v: m_viewMatrix).normalMatrix(); |
| 1101 | // dir = normalize(QVector3D(0, 0, -1) * normalMat) |
| 1102 | setEyeViewDirection(Vector3D(-normalMat(2, 0), -normalMat(2, 1), -normalMat(2, 2)).normalized()); |
| 1103 | } |
| 1104 | } |
| 1105 | |
| 1106 | void RenderView::setUniformValue(ShaderParameterPack &uniformPack, int nameId, const UniformValue &value) const |
| 1107 | { |
| 1108 | // At this point a uniform value can only be a scalar type |
| 1109 | // or a Qt3DCore::QNodeId corresponding to a Texture or Image |
| 1110 | // ShaderData/Buffers would be handled as UBO/SSBO and would therefore |
| 1111 | // not be in the default uniform block |
| 1112 | if (value.valueType() == UniformValue::NodeId) { |
| 1113 | const Qt3DCore::QNodeId *nodeIds = value.constData<Qt3DCore::QNodeId>(); |
| 1114 | |
| 1115 | const int uniformArraySize = value.byteSize() / sizeof(Qt3DCore::QNodeId); |
| 1116 | UniformValue::ValueType resourceType = UniformValue::TextureValue; |
| 1117 | |
| 1118 | for (int i = 0; i < uniformArraySize; ++i) { |
| 1119 | const Qt3DCore::QNodeId resourceId = nodeIds[i]; |
| 1120 | |
| 1121 | const Texture *tex = m_manager->textureManager()->lookupResource(id: resourceId); |
| 1122 | if (tex != nullptr) { |
| 1123 | uniformPack.setTexture(glslNameId: nameId, uniformArrayIndex: i, id: resourceId); |
| 1124 | } else { |
| 1125 | const ShaderImage *img = m_manager->shaderImageManager()->lookupResource(id: resourceId); |
| 1126 | if (img != nullptr) { |
| 1127 | resourceType = UniformValue::ShaderImageValue; |
| 1128 | uniformPack.setImage(glslNameId: nameId, uniformArrayIndex: i, id: resourceId); |
| 1129 | } |
| 1130 | } |
| 1131 | } |
| 1132 | |
| 1133 | // This uniform will be overridden in SubmissionContext::setParameters |
| 1134 | // and -1 values will be replaced by valid Texture or Image units |
| 1135 | UniformValue uniformValue(uniformArraySize * sizeof(int), resourceType); |
| 1136 | std::fill(first: uniformValue.data<int>(), last: uniformValue.data<int>() + uniformArraySize, value: -1); |
| 1137 | uniformPack.setUniform(glslNameId: nameId, val: uniformValue); |
| 1138 | } else { |
| 1139 | uniformPack.setUniform(glslNameId: nameId, val: value); |
| 1140 | } |
| 1141 | } |
| 1142 | |
| 1143 | void RenderView::setStandardUniformValue(ShaderParameterPack &uniformPack, |
| 1144 | int nameId, |
| 1145 | const Entity *entity) const |
| 1146 | { |
| 1147 | uniformPack.setUniform(glslNameId: nameId, val: standardUniformValue(standardUniformType: ms_standardUniformSetters[nameId], entity)); |
| 1148 | } |
| 1149 | |
| 1150 | void RenderView::setUniformBlockValue(ShaderParameterPack &uniformPack, |
| 1151 | const ShaderUniformBlock &block, |
| 1152 | const UniformValue &value) const |
| 1153 | { |
| 1154 | if (value.valueType() == UniformValue::NodeId) { |
| 1155 | |
| 1156 | Buffer *buffer = nullptr; |
| 1157 | if ((buffer = m_manager->bufferManager()->lookupResource(id: *value.constData<Qt3DCore::QNodeId>())) != nullptr) { |
| 1158 | BlockToUBO uniformBlockUBO; |
| 1159 | uniformBlockUBO.m_blockIndex = block.m_index; |
| 1160 | uniformBlockUBO.m_bufferID = buffer->peerId(); |
| 1161 | uniformBlockUBO.m_needsUpdate = false; |
| 1162 | uniformPack.setUniformBuffer(std::move(uniformBlockUBO)); |
| 1163 | // Buffer update to GL buffer will be done at render time |
| 1164 | } |
| 1165 | } |
| 1166 | } |
| 1167 | |
| 1168 | void RenderView::setShaderStorageValue(ShaderParameterPack &uniformPack, |
| 1169 | const ShaderStorageBlock &block, |
| 1170 | const UniformValue &value) const |
| 1171 | { |
| 1172 | if (value.valueType() == UniformValue::NodeId) { |
| 1173 | Buffer *buffer = nullptr; |
| 1174 | if ((buffer = m_manager->bufferManager()->lookupResource(id: *value.constData<Qt3DCore::QNodeId>())) != nullptr) { |
| 1175 | BlockToSSBO shaderStorageBlock; |
| 1176 | shaderStorageBlock.m_blockIndex = block.m_index; |
| 1177 | shaderStorageBlock.m_bufferID = buffer->peerId(); |
| 1178 | shaderStorageBlock.m_bindingIndex = block.m_binding; |
| 1179 | uniformPack.setShaderStorageBuffer(shaderStorageBlock); |
| 1180 | // Buffer update to GL buffer will be done at render time |
| 1181 | } |
| 1182 | } |
| 1183 | } |
| 1184 | |
| 1185 | void RenderView::setDefaultUniformBlockShaderDataValue(ShaderParameterPack &uniformPack, |
| 1186 | const std::vector<int> &uniformsNamesIds, |
| 1187 | ShaderData *shaderData, |
| 1188 | const QString &structName) const |
| 1189 | { |
| 1190 | UniformBlockValueBuilder builder(uniformsNamesIds, |
| 1191 | m_manager->shaderDataManager(), |
| 1192 | m_manager->textureManager(), |
| 1193 | m_viewMatrix); |
| 1194 | |
| 1195 | // Build name-value map for the block |
| 1196 | builder.buildActiveUniformNameValueMapStructHelper(rShaderData: shaderData, blockName: structName); |
| 1197 | // Set uniform values for each entrie of the block name-value map |
| 1198 | QHash<int, QVariant>::const_iterator activeValuesIt = builder.activeUniformNamesToValue.constBegin(); |
| 1199 | const QHash<int, QVariant>::const_iterator activeValuesEnd = builder.activeUniformNamesToValue.constEnd(); |
| 1200 | |
| 1201 | // TO DO: Make the ShaderData store UniformValue |
| 1202 | while (activeValuesIt != activeValuesEnd) { |
| 1203 | setUniformValue(uniformPack, nameId: activeValuesIt.key(), value: UniformValue::fromVariant(variant: activeValuesIt.value())); |
| 1204 | ++activeValuesIt; |
| 1205 | } |
| 1206 | } |
| 1207 | |
| 1208 | void RenderView::applyParameter(const Parameter *param, |
| 1209 | RenderCommand *command, |
| 1210 | const GLShader *shader) const noexcept |
| 1211 | { |
| 1212 | const int nameId = param->nameId(); |
| 1213 | const UniformValue &uniformValue = param->uniformValue(); |
| 1214 | const GLShader::ParameterKind kind = shader->categorizeVariable(nameId); |
| 1215 | |
| 1216 | switch (kind) { |
| 1217 | case GLShader::Uniform: { |
| 1218 | setUniformValue(uniformPack&: command->m_parameterPack, nameId, value: uniformValue); |
| 1219 | break; |
| 1220 | } |
| 1221 | case GLShader::UBO: { |
| 1222 | setUniformBlockValue(uniformPack&: command->m_parameterPack, block: shader->uniformBlockForBlockNameId(blockIndex: nameId), value: uniformValue); |
| 1223 | break; |
| 1224 | } |
| 1225 | case GLShader::SSBO: { |
| 1226 | setShaderStorageValue(uniformPack&: command->m_parameterPack, block: shader->storageBlockForBlockNameId(blockNameId: nameId), value: uniformValue); |
| 1227 | break; |
| 1228 | } |
| 1229 | case GLShader::Struct: { |
| 1230 | ShaderData *shaderData = nullptr; |
| 1231 | if (uniformValue.valueType() == UniformValue::NodeId && |
| 1232 | (shaderData = m_manager->shaderDataManager()->lookupResource(id: *uniformValue.constData<Qt3DCore::QNodeId>())) != nullptr) { |
| 1233 | // Try to check if we have a struct or array matching a QShaderData parameter |
| 1234 | setDefaultUniformBlockShaderDataValue(uniformPack&: command->m_parameterPack, uniformsNamesIds: shader->uniformsNamesIds(), shaderData, structName: StringToInt::lookupString(idx: nameId)); |
| 1235 | } |
| 1236 | break; |
| 1237 | } |
| 1238 | default: |
| 1239 | break; |
| 1240 | } |
| 1241 | } |
| 1242 | |
| 1243 | |
| 1244 | void RenderView::setShaderAndUniforms(RenderCommand *command, |
| 1245 | const ParameterInfoList ¶meters, |
| 1246 | const Entity *entity) const |
| 1247 | { |
| 1248 | // The VAO Handle is set directly in the renderer thread so as to avoid having to use a mutex here |
| 1249 | // Set shader, technique, and effect by basically doing : |
| 1250 | // ShaderProgramManager[MaterialManager[frontentEntity->id()]->Effect->Techniques[TechniqueFilter->name]->RenderPasses[RenderPassFilter->name]]; |
| 1251 | // The Renderer knows that if one of those is null, a default material / technique / effect as to be used |
| 1252 | |
| 1253 | // Find all RenderPasses (in order) matching values set in the RenderPassFilter |
| 1254 | // Get list of parameters for the Material, Effect, and Technique |
| 1255 | // For each ParameterBinder in the RenderPass -> create a QUniformPack |
| 1256 | // Once that works, improve that to try and minimize QUniformPack updates |
| 1257 | |
| 1258 | GLShader *shader = command->m_glShader; |
| 1259 | if (shader == nullptr || !shader->isLoaded()) |
| 1260 | return; |
| 1261 | |
| 1262 | // If we have already build the uniforms previously, we should |
| 1263 | // only update values of uniforms that have changed |
| 1264 | // If parameters add been added/removed, the command would have been rebuild |
| 1265 | // and the parameter pack would be empty |
| 1266 | const bool updateUniformsOnly = command->m_parameterPack.submissionUniformIndices().size() > 0; |
| 1267 | |
| 1268 | if (!updateUniformsOnly) { |
| 1269 | // Builds the QUniformPack, sets shader standard uniforms and store attributes name / glname bindings |
| 1270 | // 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 |
| 1271 | // equals to the parameter name |
| 1272 | |
| 1273 | // Set fragData Name and index |
| 1274 | // Later on we might want to relink the shader if attachments have changed |
| 1275 | // But for now we set them once and for all |
| 1276 | if (!m_renderTarget.isNull() && !shader->isLoaded()) { |
| 1277 | QHash<QString, int> fragOutputs; |
| 1278 | const auto atts = m_attachmentPack.attachments(); |
| 1279 | for (const Attachment &att : atts) { |
| 1280 | if (att.m_point <= QRenderTargetOutput::Color15) |
| 1281 | fragOutputs.insert(key: att.m_name, value: att.m_point); |
| 1282 | } |
| 1283 | // Set frag outputs in the shaders if hash not empty |
| 1284 | if (!fragOutputs.isEmpty()) |
| 1285 | shader->setFragOutputs(fragOutputs); |
| 1286 | } |
| 1287 | |
| 1288 | // Set default attributes |
| 1289 | command->m_activeAttributes = shader->attributeNamesIds(); |
| 1290 | |
| 1291 | // At this point we know whether the command is a valid draw command or not |
| 1292 | // We still need to process the uniforms as the command could be a compute command |
| 1293 | command->m_isValid = !command->m_activeAttributes.empty(); |
| 1294 | |
| 1295 | // Reserve amount of uniforms we are going to need |
| 1296 | command->m_parameterPack.reserve(uniformCount: shader->parameterPackSize()); |
| 1297 | } |
| 1298 | |
| 1299 | const size_t previousUniformCount = command->m_parameterPack.uniforms().size(); |
| 1300 | if (shader->hasActiveVariables()) { |
| 1301 | const std::vector<int> &standardUniformNamesIds = shader->standardUniformNameIds(); |
| 1302 | |
| 1303 | // It only makes sense to update the standard uniforms if: |
| 1304 | // - Camera changed |
| 1305 | // - Entity transform changed |
| 1306 | // - Viewport/Surface changed |
| 1307 | |
| 1308 | for (const int uniformNameId : standardUniformNamesIds) |
| 1309 | setStandardUniformValue(uniformPack&: command->m_parameterPack, nameId: uniformNameId, entity); |
| 1310 | |
| 1311 | ParameterInfoList::const_iterator it = parameters.cbegin(); |
| 1312 | const ParameterInfoList::const_iterator parametersEnd = parameters.cend(); |
| 1313 | |
| 1314 | while (it != parametersEnd) { |
| 1315 | const Parameter *param = m_manager->data<Parameter, ParameterManager>(handle: it->handle); |
| 1316 | applyParameter(param, command, shader); |
| 1317 | ++it; |
| 1318 | } |
| 1319 | |
| 1320 | // Lights |
| 1321 | updateLightUniforms(command, entity); |
| 1322 | } |
| 1323 | |
| 1324 | const size_t actualUniformCount = command->m_parameterPack.uniforms().size(); |
| 1325 | // Prepare the ShaderParameterPack based on the active uniforms of the shader |
| 1326 | if (!updateUniformsOnly || previousUniformCount != actualUniformCount) |
| 1327 | shader->prepareUniforms(pack&: command->m_parameterPack); |
| 1328 | } |
| 1329 | |
| 1330 | void RenderView::updateLightUniforms(RenderCommand *command, const Entity *entity) const |
| 1331 | { |
| 1332 | GLShader *shader = command->m_glShader; |
| 1333 | const std::vector<int> &lightUniformNamesIds = shader->lightUniformsNamesIds(); |
| 1334 | if (!lightUniformNamesIds.empty()) { |
| 1335 | // Pick which lights to take in to account. |
| 1336 | // For now decide based on the distance by taking the MAX_LIGHTS closest lights. |
| 1337 | // Replace with more sophisticated mechanisms later. |
| 1338 | // Copy vector so that we can sort it concurrently and we only want to sort the one for the current command |
| 1339 | std::vector<LightSource> lightSources = m_lightSources; |
| 1340 | |
| 1341 | if (lightSources.size() > 1) { |
| 1342 | const Vector3D entityCenter = entity->worldBoundingVolume()->center(); |
| 1343 | std::sort(first: lightSources.begin(), last: lightSources.end(), |
| 1344 | comp: [&] (const LightSource &a, const LightSource &b) { |
| 1345 | const float distA = entityCenter.distanceToPoint(point: a.entity->worldBoundingVolume()->center()); |
| 1346 | const float distB = entityCenter.distanceToPoint(point: b.entity->worldBoundingVolume()->center()); |
| 1347 | return distA < distB; |
| 1348 | }); |
| 1349 | m_lightSources = {lightSources.begin(), lightSources.begin() + std::min(a: lightSources.size(), b: size_t(MAX_LIGHTS)) }; |
| 1350 | } |
| 1351 | |
| 1352 | int lightIdx = 0; |
| 1353 | for (const LightSource &lightSource : m_lightSources) { |
| 1354 | if (lightIdx == MAX_LIGHTS) |
| 1355 | break; |
| 1356 | const Entity *lightEntity = lightSource.entity; |
| 1357 | const Matrix4x4 lightWorldTransform = *(lightEntity->worldTransform()); |
| 1358 | const Vector3D worldPos = lightWorldTransform.map(point: Vector3D(0.0f, 0.0f, 0.0f)); |
| 1359 | for (Light *light : lightSource.lights) { |
| 1360 | if (!light->isEnabled()) |
| 1361 | continue; |
| 1362 | |
| 1363 | ShaderData *shaderData = m_manager->shaderDataManager()->lookupResource(id: light->shaderData()); |
| 1364 | if (!shaderData) |
| 1365 | continue; |
| 1366 | |
| 1367 | if (lightIdx == MAX_LIGHTS) |
| 1368 | break; |
| 1369 | |
| 1370 | // Note: implicit conversion of values to UniformValue |
| 1371 | if (Qt3DCore::contains(destination: lightUniformNamesIds, element: GLLights::LIGHT_TYPE_NAMES[lightIdx])) { |
| 1372 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: GLLights::LIGHT_POSITION_NAMES[lightIdx], value: worldPos); |
| 1373 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: GLLights::LIGHT_TYPE_NAMES[lightIdx], value: int(QAbstractLight::PointLight)); |
| 1374 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: GLLights::LIGHT_COLOR_NAMES[lightIdx], value: Vector3D(1.0f, 1.0f, 1.0f)); |
| 1375 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: GLLights::LIGHT_INTENSITY_NAMES[lightIdx], value: 0.5f); |
| 1376 | } else if (Qt3DCore::contains(destination: lightUniformNamesIds, element: GLLights::LIGHT_TYPE_UNROLL_NAMES[lightIdx])) { |
| 1377 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: GLLights::LIGHT_POSITION_UNROLL_NAMES[lightIdx], value: worldPos); |
| 1378 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: GLLights::LIGHT_TYPE_UNROLL_NAMES[lightIdx], value: int(QAbstractLight::PointLight)); |
| 1379 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: GLLights::LIGHT_COLOR_UNROLL_NAMES[lightIdx], value: Vector3D(1.0f, 1.0f, 1.0f)); |
| 1380 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: GLLights::LIGHT_INTENSITY_UNROLL_NAMES[lightIdx], value: 0.5f); |
| 1381 | } |
| 1382 | |
| 1383 | // There is no risk in doing that even if multithreaded |
| 1384 | // since we are sure that a shaderData is unique for a given light |
| 1385 | // and won't ever be referenced as a Component either |
| 1386 | const Matrix4x4 *worldTransform = lightEntity->worldTransform(); |
| 1387 | if (worldTransform) |
| 1388 | shaderData->updateWorldTransform(worldMatrix: *worldTransform); |
| 1389 | |
| 1390 | setDefaultUniformBlockShaderDataValue(uniformPack&: command->m_parameterPack, uniformsNamesIds: shader->lightUniformsNamesIds(), shaderData, structName: GLLights::LIGHT_STRUCT_NAMES[lightIdx]); |
| 1391 | setDefaultUniformBlockShaderDataValue(uniformPack&: command->m_parameterPack, uniformsNamesIds: shader->lightUniformsNamesIds(), shaderData, structName: GLLights::LIGHT_STRUCT_UNROLL_NAMES[lightIdx]); |
| 1392 | ++lightIdx; |
| 1393 | } |
| 1394 | } |
| 1395 | |
| 1396 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: GLLights::LIGHT_COUNT_NAME_ID, value: UniformValue(qMax(a: (m_environmentLight ? 0 : 1), b: lightIdx))); |
| 1397 | |
| 1398 | // If no active light sources and no environment light, add a default light |
| 1399 | if (m_lightSources.empty() && !m_environmentLight) { |
| 1400 | // Note: implicit conversion of values to UniformValue |
| 1401 | if (Qt3DCore::contains(destination: lightUniformNamesIds, element: GLLights::LIGHT_TYPE_NAMES[lightIdx])) { |
| 1402 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: GLLights::LIGHT_POSITION_NAMES[0], value: Vector3D(10.0f, 10.0f, 0.0f)); |
| 1403 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: GLLights::LIGHT_TYPE_NAMES[0], value: int(QAbstractLight::PointLight)); |
| 1404 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: GLLights::LIGHT_COLOR_NAMES[0], value: Vector3D(1.0f, 1.0f, 1.0f)); |
| 1405 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: GLLights::LIGHT_INTENSITY_NAMES[0], value: 0.5f); |
| 1406 | } else if (Qt3DCore::contains(destination: lightUniformNamesIds, element: GLLights::LIGHT_TYPE_UNROLL_NAMES[lightIdx])) { |
| 1407 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: GLLights::LIGHT_POSITION_UNROLL_NAMES[0], value: Vector3D(10.0f, 10.0f, 0.0f)); |
| 1408 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: GLLights::LIGHT_TYPE_UNROLL_NAMES[0], value: int(QAbstractLight::PointLight)); |
| 1409 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: GLLights::LIGHT_COLOR_UNROLL_NAMES[0], value: Vector3D(1.0f, 1.0f, 1.0f)); |
| 1410 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: GLLights::LIGHT_INTENSITY_UNROLL_NAMES[0], value: 0.5f); |
| 1411 | } |
| 1412 | } |
| 1413 | } |
| 1414 | |
| 1415 | // Environment Light |
| 1416 | int envLightCount = 0; |
| 1417 | static const int irradianceStructId = StringToInt::lookupId(str: QLatin1String("envLight.irradiance" )); |
| 1418 | static const int specularStructId = StringToInt::lookupId(str: QLatin1String("envLight.specular" )); |
| 1419 | static const int irradianceId = StringToInt::lookupId(str: QLatin1String("envLightIrradiance" )); |
| 1420 | static const int specularId = StringToInt::lookupId(str: QLatin1String("envLightSpecular" )); |
| 1421 | if (m_environmentLight && m_environmentLight->isEnabled()) { |
| 1422 | ShaderData *shaderData = m_manager->shaderDataManager()->lookupResource(id: m_environmentLight->shaderData()); |
| 1423 | if (shaderData) { |
| 1424 | // EnvLight isn't part of the light uniform name ids |
| 1425 | setDefaultUniformBlockShaderDataValue(uniformPack&: command->m_parameterPack, uniformsNamesIds: shader->uniformsNamesIds(), shaderData, QStringLiteral("envLight" )); |
| 1426 | auto irr = |
| 1427 | shaderData->properties()["irradiance" ].value.value<Qt3DCore::QNodeId>(); |
| 1428 | auto spec = |
| 1429 | shaderData->properties()["specular" ].value.value<Qt3DCore::QNodeId>(); |
| 1430 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: irradianceId, value: irr); |
| 1431 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: specularId, value: spec); |
| 1432 | envLightCount = 1; |
| 1433 | } |
| 1434 | } else { |
| 1435 | // with some drivers, samplers (like the envbox sampler) need to be bound even though |
| 1436 | // they may not be actually used, otherwise draw calls can fail |
| 1437 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: irradianceId, value: m_renderer->submissionContext()->maxTextureUnitsCount()); |
| 1438 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: irradianceStructId, value: m_renderer->submissionContext()->maxTextureUnitsCount()); |
| 1439 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: specularId, value: m_renderer->submissionContext()->maxTextureUnitsCount()); |
| 1440 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: specularStructId, value: m_renderer->submissionContext()->maxTextureUnitsCount()); |
| 1441 | } |
| 1442 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: StringToInt::lookupId(QStringLiteral("envLightCount" )), value: envLightCount); |
| 1443 | } |
| 1444 | |
| 1445 | bool RenderView::hasBlitFramebufferInfo() const |
| 1446 | { |
| 1447 | return m_hasBlitFramebufferInfo; |
| 1448 | } |
| 1449 | |
| 1450 | void RenderView::setHasBlitFramebufferInfo(bool hasBlitFramebufferInfo) |
| 1451 | { |
| 1452 | m_hasBlitFramebufferInfo = hasBlitFramebufferInfo; |
| 1453 | } |
| 1454 | |
| 1455 | bool RenderView::shouldSkipSubmission() const |
| 1456 | { |
| 1457 | if (commandCount() > 0) |
| 1458 | return false; |
| 1459 | |
| 1460 | if (m_hasBlitFramebufferInfo) |
| 1461 | return false; |
| 1462 | |
| 1463 | if (m_isDownloadBuffersEnable) |
| 1464 | return false; |
| 1465 | |
| 1466 | if (m_showDebugOverlay) |
| 1467 | return false; |
| 1468 | |
| 1469 | if (!m_waitFences.empty() || !m_insertFenceIds.empty()) |
| 1470 | return false; |
| 1471 | |
| 1472 | if (m_clearBuffer != QClearBuffers::None) |
| 1473 | return false; |
| 1474 | |
| 1475 | if (!m_renderCaptureNodeId.isNull()) |
| 1476 | return false; |
| 1477 | |
| 1478 | return true; |
| 1479 | } |
| 1480 | |
| 1481 | BlitFramebufferInfo RenderView::blitFrameBufferInfo() const |
| 1482 | { |
| 1483 | return m_blitFrameBufferInfo; |
| 1484 | } |
| 1485 | |
| 1486 | void RenderView::setBlitFrameBufferInfo(const BlitFramebufferInfo &blitFrameBufferInfo) |
| 1487 | { |
| 1488 | m_blitFrameBufferInfo = blitFrameBufferInfo; |
| 1489 | } |
| 1490 | |
| 1491 | bool RenderView::isDownloadBuffersEnable() const |
| 1492 | { |
| 1493 | return m_isDownloadBuffersEnable; |
| 1494 | } |
| 1495 | |
| 1496 | void RenderView::setIsDownloadBuffersEnable(bool isDownloadBuffersEnable) |
| 1497 | { |
| 1498 | m_isDownloadBuffersEnable = isDownloadBuffersEnable; |
| 1499 | } |
| 1500 | |
| 1501 | } // namespace OpenGL |
| 1502 | } // namespace Render |
| 1503 | } // namespace Qt3DRender |
| 1504 | |
| 1505 | QT_END_NAMESPACE |
| 1506 | |