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 | const Matrix4x4 cameraWorld = *(m_renderCameraNode->worldTransform()); |
1081 | setViewMatrix(m_renderCameraLens->viewMatrix(worldTransform: cameraWorld)); |
1082 | |
1083 | setViewProjectionMatrix(m_renderCameraLens->projection() * viewMatrix()); |
1084 | //To get the eyePosition of the camera, we need to use the inverse of the |
1085 | //camera's worldTransform matrix. |
1086 | const Matrix4x4 inverseWorldTransform = viewMatrix().inverted(); |
1087 | const Vector3D eyePosition(inverseWorldTransform.column(index: 3)); |
1088 | setEyePosition(eyePosition); |
1089 | |
1090 | // Get the viewing direction of the camera. Use the normal matrix to |
1091 | // ensure non-uniform scale works too. |
1092 | const QMatrix3x3 normalMat = convertToQMatrix4x4(v: m_viewMatrix).normalMatrix(); |
1093 | // dir = normalize(QVector3D(0, 0, -1) * normalMat) |
1094 | setEyeViewDirection(Vector3D(-normalMat(2, 0), -normalMat(2, 1), -normalMat(2, 2)).normalized()); |
1095 | } |
1096 | } |
1097 | |
1098 | void RenderView::setUniformValue(ShaderParameterPack &uniformPack, int nameId, const UniformValue &value) const |
1099 | { |
1100 | // At this point a uniform value can only be a scalar type |
1101 | // or a Qt3DCore::QNodeId corresponding to a Texture or Image |
1102 | // ShaderData/Buffers would be handled as UBO/SSBO and would therefore |
1103 | // not be in the default uniform block |
1104 | if (value.valueType() == UniformValue::NodeId) { |
1105 | const Qt3DCore::QNodeId *nodeIds = value.constData<Qt3DCore::QNodeId>(); |
1106 | |
1107 | const int uniformArraySize = value.byteSize() / sizeof(Qt3DCore::QNodeId); |
1108 | UniformValue::ValueType resourceType = UniformValue::TextureValue; |
1109 | |
1110 | for (int i = 0; i < uniformArraySize; ++i) { |
1111 | const Qt3DCore::QNodeId resourceId = nodeIds[i]; |
1112 | |
1113 | const Texture *tex = m_manager->textureManager()->lookupResource(id: resourceId); |
1114 | if (tex != nullptr) { |
1115 | uniformPack.setTexture(glslNameId: nameId, uniformArrayIndex: i, id: resourceId); |
1116 | } else { |
1117 | const ShaderImage *img = m_manager->shaderImageManager()->lookupResource(id: resourceId); |
1118 | if (img != nullptr) { |
1119 | resourceType = UniformValue::ShaderImageValue; |
1120 | uniformPack.setImage(glslNameId: nameId, uniformArrayIndex: i, id: resourceId); |
1121 | } |
1122 | } |
1123 | } |
1124 | |
1125 | // This uniform will be overridden in SubmissionContext::setParameters |
1126 | // and -1 values will be replaced by valid Texture or Image units |
1127 | UniformValue uniformValue(uniformArraySize * sizeof(int), resourceType); |
1128 | std::fill(first: uniformValue.data<int>(), last: uniformValue.data<int>() + uniformArraySize, value: -1); |
1129 | uniformPack.setUniform(glslNameId: nameId, val: uniformValue); |
1130 | } else { |
1131 | uniformPack.setUniform(glslNameId: nameId, val: value); |
1132 | } |
1133 | } |
1134 | |
1135 | void RenderView::setStandardUniformValue(ShaderParameterPack &uniformPack, |
1136 | int nameId, |
1137 | const Entity *entity) const |
1138 | { |
1139 | uniformPack.setUniform(glslNameId: nameId, val: standardUniformValue(standardUniformType: ms_standardUniformSetters[nameId], entity)); |
1140 | } |
1141 | |
1142 | void RenderView::setUniformBlockValue(ShaderParameterPack &uniformPack, |
1143 | const ShaderUniformBlock &block, |
1144 | const UniformValue &value) const |
1145 | { |
1146 | if (value.valueType() == UniformValue::NodeId) { |
1147 | |
1148 | Buffer *buffer = nullptr; |
1149 | if ((buffer = m_manager->bufferManager()->lookupResource(id: *value.constData<Qt3DCore::QNodeId>())) != nullptr) { |
1150 | BlockToUBO uniformBlockUBO; |
1151 | uniformBlockUBO.m_blockIndex = block.m_index; |
1152 | uniformBlockUBO.m_bufferID = buffer->peerId(); |
1153 | uniformBlockUBO.m_needsUpdate = false; |
1154 | uniformPack.setUniformBuffer(std::move(uniformBlockUBO)); |
1155 | // Buffer update to GL buffer will be done at render time |
1156 | } |
1157 | } |
1158 | } |
1159 | |
1160 | void RenderView::setShaderStorageValue(ShaderParameterPack &uniformPack, |
1161 | const ShaderStorageBlock &block, |
1162 | const UniformValue &value) const |
1163 | { |
1164 | if (value.valueType() == UniformValue::NodeId) { |
1165 | Buffer *buffer = nullptr; |
1166 | if ((buffer = m_manager->bufferManager()->lookupResource(id: *value.constData<Qt3DCore::QNodeId>())) != nullptr) { |
1167 | BlockToSSBO shaderStorageBlock; |
1168 | shaderStorageBlock.m_blockIndex = block.m_index; |
1169 | shaderStorageBlock.m_bufferID = buffer->peerId(); |
1170 | shaderStorageBlock.m_bindingIndex = block.m_binding; |
1171 | uniformPack.setShaderStorageBuffer(shaderStorageBlock); |
1172 | // Buffer update to GL buffer will be done at render time |
1173 | } |
1174 | } |
1175 | } |
1176 | |
1177 | void RenderView::setDefaultUniformBlockShaderDataValue(ShaderParameterPack &uniformPack, |
1178 | const std::vector<int> &uniformsNamesIds, |
1179 | ShaderData *shaderData, |
1180 | const QString &structName) const |
1181 | { |
1182 | UniformBlockValueBuilder builder(uniformsNamesIds, |
1183 | m_manager->shaderDataManager(), |
1184 | m_manager->textureManager(), |
1185 | m_viewMatrix); |
1186 | |
1187 | // Build name-value map for the block |
1188 | builder.buildActiveUniformNameValueMapStructHelper(rShaderData: shaderData, blockName: structName); |
1189 | // Set uniform values for each entrie of the block name-value map |
1190 | QHash<int, QVariant>::const_iterator activeValuesIt = builder.activeUniformNamesToValue.constBegin(); |
1191 | const QHash<int, QVariant>::const_iterator activeValuesEnd = builder.activeUniformNamesToValue.constEnd(); |
1192 | |
1193 | // TO DO: Make the ShaderData store UniformValue |
1194 | while (activeValuesIt != activeValuesEnd) { |
1195 | setUniformValue(uniformPack, nameId: activeValuesIt.key(), value: UniformValue::fromVariant(variant: activeValuesIt.value())); |
1196 | ++activeValuesIt; |
1197 | } |
1198 | } |
1199 | |
1200 | void RenderView::applyParameter(const Parameter *param, |
1201 | RenderCommand *command, |
1202 | const GLShader *shader) const noexcept |
1203 | { |
1204 | const int nameId = param->nameId(); |
1205 | const UniformValue &uniformValue = param->uniformValue(); |
1206 | const GLShader::ParameterKind kind = shader->categorizeVariable(nameId); |
1207 | |
1208 | switch (kind) { |
1209 | case GLShader::Uniform: { |
1210 | setUniformValue(uniformPack&: command->m_parameterPack, nameId, value: uniformValue); |
1211 | break; |
1212 | } |
1213 | case GLShader::UBO: { |
1214 | setUniformBlockValue(uniformPack&: command->m_parameterPack, block: shader->uniformBlockForBlockNameId(blockIndex: nameId), value: uniformValue); |
1215 | break; |
1216 | } |
1217 | case GLShader::SSBO: { |
1218 | setShaderStorageValue(uniformPack&: command->m_parameterPack, block: shader->storageBlockForBlockNameId(blockNameId: nameId), value: uniformValue); |
1219 | break; |
1220 | } |
1221 | case GLShader::Struct: { |
1222 | ShaderData *shaderData = nullptr; |
1223 | if (uniformValue.valueType() == UniformValue::NodeId && |
1224 | (shaderData = m_manager->shaderDataManager()->lookupResource(id: *uniformValue.constData<Qt3DCore::QNodeId>())) != nullptr) { |
1225 | // Try to check if we have a struct or array matching a QShaderData parameter |
1226 | setDefaultUniformBlockShaderDataValue(uniformPack&: command->m_parameterPack, uniformsNamesIds: shader->uniformsNamesIds(), shaderData, structName: StringToInt::lookupString(idx: nameId)); |
1227 | } |
1228 | break; |
1229 | } |
1230 | default: |
1231 | break; |
1232 | } |
1233 | } |
1234 | |
1235 | |
1236 | void RenderView::setShaderAndUniforms(RenderCommand *command, |
1237 | const ParameterInfoList ¶meters, |
1238 | const Entity *entity) const |
1239 | { |
1240 | // The VAO Handle is set directly in the renderer thread so as to avoid having to use a mutex here |
1241 | // Set shader, technique, and effect by basically doing : |
1242 | // ShaderProgramManager[MaterialManager[frontentEntity->id()]->Effect->Techniques[TechniqueFilter->name]->RenderPasses[RenderPassFilter->name]]; |
1243 | // The Renderer knows that if one of those is null, a default material / technique / effect as to be used |
1244 | |
1245 | // Find all RenderPasses (in order) matching values set in the RenderPassFilter |
1246 | // Get list of parameters for the Material, Effect, and Technique |
1247 | // For each ParameterBinder in the RenderPass -> create a QUniformPack |
1248 | // Once that works, improve that to try and minimize QUniformPack updates |
1249 | |
1250 | GLShader *shader = command->m_glShader; |
1251 | if (shader == nullptr || !shader->isLoaded()) |
1252 | return; |
1253 | |
1254 | // If we have already build the uniforms previously, we should |
1255 | // only update values of uniforms that have changed |
1256 | // If parameters add been added/removed, the command would have been rebuild |
1257 | // and the parameter pack would be empty |
1258 | const bool updateUniformsOnly = command->m_parameterPack.submissionUniformIndices().size() > 0; |
1259 | |
1260 | if (!updateUniformsOnly) { |
1261 | // Builds the QUniformPack, sets shader standard uniforms and store attributes name / glname bindings |
1262 | // 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 |
1263 | // equals to the parameter name |
1264 | |
1265 | // Set fragData Name and index |
1266 | // Later on we might want to relink the shader if attachments have changed |
1267 | // But for now we set them once and for all |
1268 | if (!m_renderTarget.isNull() && !shader->isLoaded()) { |
1269 | QHash<QString, int> fragOutputs; |
1270 | const auto atts = m_attachmentPack.attachments(); |
1271 | for (const Attachment &att : atts) { |
1272 | if (att.m_point <= QRenderTargetOutput::Color15) |
1273 | fragOutputs.insert(key: att.m_name, value: att.m_point); |
1274 | } |
1275 | // Set frag outputs in the shaders if hash not empty |
1276 | if (!fragOutputs.isEmpty()) |
1277 | shader->setFragOutputs(fragOutputs); |
1278 | } |
1279 | |
1280 | // Set default attributes |
1281 | command->m_activeAttributes = shader->attributeNamesIds(); |
1282 | |
1283 | // At this point we know whether the command is a valid draw command or not |
1284 | // We still need to process the uniforms as the command could be a compute command |
1285 | command->m_isValid = !command->m_activeAttributes.empty(); |
1286 | |
1287 | // Reserve amount of uniforms we are going to need |
1288 | command->m_parameterPack.reserve(uniformCount: shader->parameterPackSize()); |
1289 | } |
1290 | |
1291 | const size_t previousUniformCount = command->m_parameterPack.uniforms().size(); |
1292 | if (shader->hasActiveVariables()) { |
1293 | const std::vector<int> &standardUniformNamesIds = shader->standardUniformNameIds(); |
1294 | |
1295 | // It only makes sense to update the standard uniforms if: |
1296 | // - Camera changed |
1297 | // - Entity transform changed |
1298 | // - Viewport/Surface changed |
1299 | |
1300 | for (const int uniformNameId : standardUniformNamesIds) |
1301 | setStandardUniformValue(uniformPack&: command->m_parameterPack, nameId: uniformNameId, entity); |
1302 | |
1303 | ParameterInfoList::const_iterator it = parameters.cbegin(); |
1304 | const ParameterInfoList::const_iterator parametersEnd = parameters.cend(); |
1305 | |
1306 | while (it != parametersEnd) { |
1307 | const Parameter *param = m_manager->data<Parameter, ParameterManager>(handle: it->handle); |
1308 | applyParameter(param, command, shader); |
1309 | ++it; |
1310 | } |
1311 | |
1312 | // Lights |
1313 | updateLightUniforms(command, entity); |
1314 | } |
1315 | |
1316 | const size_t actualUniformCount = command->m_parameterPack.uniforms().size(); |
1317 | // Prepare the ShaderParameterPack based on the active uniforms of the shader |
1318 | if (!updateUniformsOnly || previousUniformCount != actualUniformCount) |
1319 | shader->prepareUniforms(pack&: command->m_parameterPack); |
1320 | } |
1321 | |
1322 | void RenderView::updateLightUniforms(RenderCommand *command, const Entity *entity) const |
1323 | { |
1324 | GLShader *shader = command->m_glShader; |
1325 | const std::vector<int> &lightUniformNamesIds = shader->lightUniformsNamesIds(); |
1326 | if (!lightUniformNamesIds.empty()) { |
1327 | // Pick which lights to take in to account. |
1328 | // For now decide based on the distance by taking the MAX_LIGHTS closest lights. |
1329 | // Replace with more sophisticated mechanisms later. |
1330 | // Copy vector so that we can sort it concurrently and we only want to sort the one for the current command |
1331 | std::vector<LightSource> lightSources = m_lightSources; |
1332 | |
1333 | if (lightSources.size() > 1) { |
1334 | const Vector3D entityCenter = entity->worldBoundingVolume()->center(); |
1335 | std::sort(first: lightSources.begin(), last: lightSources.end(), |
1336 | comp: [&] (const LightSource &a, const LightSource &b) { |
1337 | const float distA = entityCenter.distanceToPoint(point: a.entity->worldBoundingVolume()->center()); |
1338 | const float distB = entityCenter.distanceToPoint(point: b.entity->worldBoundingVolume()->center()); |
1339 | return distA < distB; |
1340 | }); |
1341 | m_lightSources = {lightSources.begin(), lightSources.begin() + std::min(a: lightSources.size(), b: size_t(MAX_LIGHTS)) }; |
1342 | } |
1343 | |
1344 | int lightIdx = 0; |
1345 | for (const LightSource &lightSource : m_lightSources) { |
1346 | if (lightIdx == MAX_LIGHTS) |
1347 | break; |
1348 | const Entity *lightEntity = lightSource.entity; |
1349 | const Matrix4x4 lightWorldTransform = *(lightEntity->worldTransform()); |
1350 | const Vector3D worldPos = lightWorldTransform.map(point: Vector3D(0.0f, 0.0f, 0.0f)); |
1351 | for (Light *light : lightSource.lights) { |
1352 | if (!light->isEnabled()) |
1353 | continue; |
1354 | |
1355 | ShaderData *shaderData = m_manager->shaderDataManager()->lookupResource(id: light->shaderData()); |
1356 | if (!shaderData) |
1357 | continue; |
1358 | |
1359 | if (lightIdx == MAX_LIGHTS) |
1360 | break; |
1361 | |
1362 | // Note: implicit conversion of values to UniformValue |
1363 | if (Qt3DCore::contains(destination: lightUniformNamesIds, element: GLLights::LIGHT_TYPE_NAMES[lightIdx])) { |
1364 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: GLLights::LIGHT_POSITION_NAMES[lightIdx], value: worldPos); |
1365 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: GLLights::LIGHT_TYPE_NAMES[lightIdx], value: int(QAbstractLight::PointLight)); |
1366 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: GLLights::LIGHT_COLOR_NAMES[lightIdx], value: Vector3D(1.0f, 1.0f, 1.0f)); |
1367 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: GLLights::LIGHT_INTENSITY_NAMES[lightIdx], value: 0.5f); |
1368 | } else if (Qt3DCore::contains(destination: lightUniformNamesIds, element: GLLights::LIGHT_TYPE_UNROLL_NAMES[lightIdx])) { |
1369 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: GLLights::LIGHT_POSITION_UNROLL_NAMES[lightIdx], value: worldPos); |
1370 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: GLLights::LIGHT_TYPE_UNROLL_NAMES[lightIdx], value: int(QAbstractLight::PointLight)); |
1371 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: GLLights::LIGHT_COLOR_UNROLL_NAMES[lightIdx], value: Vector3D(1.0f, 1.0f, 1.0f)); |
1372 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: GLLights::LIGHT_INTENSITY_UNROLL_NAMES[lightIdx], value: 0.5f); |
1373 | } |
1374 | |
1375 | // There is no risk in doing that even if multithreaded |
1376 | // since we are sure that a shaderData is unique for a given light |
1377 | // and won't ever be referenced as a Component either |
1378 | const Matrix4x4 *worldTransform = lightEntity->worldTransform(); |
1379 | if (worldTransform) |
1380 | shaderData->updateWorldTransform(worldMatrix: *worldTransform); |
1381 | |
1382 | setDefaultUniformBlockShaderDataValue(uniformPack&: command->m_parameterPack, uniformsNamesIds: shader->lightUniformsNamesIds(), shaderData, structName: GLLights::LIGHT_STRUCT_NAMES[lightIdx]); |
1383 | setDefaultUniformBlockShaderDataValue(uniformPack&: command->m_parameterPack, uniformsNamesIds: shader->lightUniformsNamesIds(), shaderData, structName: GLLights::LIGHT_STRUCT_UNROLL_NAMES[lightIdx]); |
1384 | ++lightIdx; |
1385 | } |
1386 | } |
1387 | |
1388 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: GLLights::LIGHT_COUNT_NAME_ID, value: UniformValue(qMax(a: (m_environmentLight ? 0 : 1), b: lightIdx))); |
1389 | |
1390 | // If no active light sources and no environment light, add a default light |
1391 | if (m_lightSources.empty() && !m_environmentLight) { |
1392 | // Note: implicit conversion of values to UniformValue |
1393 | if (Qt3DCore::contains(destination: lightUniformNamesIds, element: GLLights::LIGHT_TYPE_NAMES[lightIdx])) { |
1394 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: GLLights::LIGHT_POSITION_NAMES[0], value: Vector3D(10.0f, 10.0f, 0.0f)); |
1395 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: GLLights::LIGHT_TYPE_NAMES[0], value: int(QAbstractLight::PointLight)); |
1396 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: GLLights::LIGHT_COLOR_NAMES[0], value: Vector3D(1.0f, 1.0f, 1.0f)); |
1397 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: GLLights::LIGHT_INTENSITY_NAMES[0], value: 0.5f); |
1398 | } else if (Qt3DCore::contains(destination: lightUniformNamesIds, element: GLLights::LIGHT_TYPE_UNROLL_NAMES[lightIdx])) { |
1399 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: GLLights::LIGHT_POSITION_UNROLL_NAMES[0], value: Vector3D(10.0f, 10.0f, 0.0f)); |
1400 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: GLLights::LIGHT_TYPE_UNROLL_NAMES[0], value: int(QAbstractLight::PointLight)); |
1401 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: GLLights::LIGHT_COLOR_UNROLL_NAMES[0], value: Vector3D(1.0f, 1.0f, 1.0f)); |
1402 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: GLLights::LIGHT_INTENSITY_UNROLL_NAMES[0], value: 0.5f); |
1403 | } |
1404 | } |
1405 | } |
1406 | |
1407 | // Environment Light |
1408 | int envLightCount = 0; |
1409 | static const int irradianceStructId = StringToInt::lookupId(str: QLatin1String("envLight.irradiance" )); |
1410 | static const int specularStructId = StringToInt::lookupId(str: QLatin1String("envLight.specular" )); |
1411 | static const int irradianceId = StringToInt::lookupId(str: QLatin1String("envLightIrradiance" )); |
1412 | static const int specularId = StringToInt::lookupId(str: QLatin1String("envLightSpecular" )); |
1413 | if (m_environmentLight && m_environmentLight->isEnabled()) { |
1414 | ShaderData *shaderData = m_manager->shaderDataManager()->lookupResource(id: m_environmentLight->shaderData()); |
1415 | if (shaderData) { |
1416 | // EnvLight isn't part of the light uniform name ids |
1417 | setDefaultUniformBlockShaderDataValue(uniformPack&: command->m_parameterPack, uniformsNamesIds: shader->uniformsNamesIds(), shaderData, QStringLiteral("envLight" )); |
1418 | auto irr = |
1419 | shaderData->properties()["irradiance" ].value.value<Qt3DCore::QNodeId>(); |
1420 | auto spec = |
1421 | shaderData->properties()["specular" ].value.value<Qt3DCore::QNodeId>(); |
1422 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: irradianceId, value: irr); |
1423 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: specularId, value: spec); |
1424 | envLightCount = 1; |
1425 | } |
1426 | } else { |
1427 | // with some drivers, samplers (like the envbox sampler) need to be bound even though |
1428 | // they may not be actually used, otherwise draw calls can fail |
1429 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: irradianceId, value: m_renderer->submissionContext()->maxTextureUnitsCount()); |
1430 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: irradianceStructId, value: m_renderer->submissionContext()->maxTextureUnitsCount()); |
1431 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: specularId, value: m_renderer->submissionContext()->maxTextureUnitsCount()); |
1432 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: specularStructId, value: m_renderer->submissionContext()->maxTextureUnitsCount()); |
1433 | } |
1434 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: StringToInt::lookupId(QStringLiteral("envLightCount" )), value: envLightCount); |
1435 | } |
1436 | |
1437 | bool RenderView::hasBlitFramebufferInfo() const |
1438 | { |
1439 | return m_hasBlitFramebufferInfo; |
1440 | } |
1441 | |
1442 | void RenderView::setHasBlitFramebufferInfo(bool hasBlitFramebufferInfo) |
1443 | { |
1444 | m_hasBlitFramebufferInfo = hasBlitFramebufferInfo; |
1445 | } |
1446 | |
1447 | bool RenderView::shouldSkipSubmission() const |
1448 | { |
1449 | if (commandCount() > 0) |
1450 | return false; |
1451 | |
1452 | if (m_hasBlitFramebufferInfo) |
1453 | return false; |
1454 | |
1455 | if (m_isDownloadBuffersEnable) |
1456 | return false; |
1457 | |
1458 | if (m_showDebugOverlay) |
1459 | return false; |
1460 | |
1461 | if (!m_waitFences.empty() || !m_insertFenceIds.empty()) |
1462 | return false; |
1463 | |
1464 | if (m_clearBuffer != QClearBuffers::None) |
1465 | return false; |
1466 | |
1467 | if (!m_renderCaptureNodeId.isNull()) |
1468 | return false; |
1469 | |
1470 | return true; |
1471 | } |
1472 | |
1473 | BlitFramebufferInfo RenderView::blitFrameBufferInfo() const |
1474 | { |
1475 | return m_blitFrameBufferInfo; |
1476 | } |
1477 | |
1478 | void RenderView::setBlitFrameBufferInfo(const BlitFramebufferInfo &blitFrameBufferInfo) |
1479 | { |
1480 | m_blitFrameBufferInfo = blitFrameBufferInfo; |
1481 | } |
1482 | |
1483 | bool RenderView::isDownloadBuffersEnable() const |
1484 | { |
1485 | return m_isDownloadBuffersEnable; |
1486 | } |
1487 | |
1488 | void RenderView::setIsDownloadBuffersEnable(bool isDownloadBuffersEnable) |
1489 | { |
1490 | m_isDownloadBuffersEnable = isDownloadBuffersEnable; |
1491 | } |
1492 | |
1493 | } // namespace OpenGL |
1494 | } // namespace Render |
1495 | } // namespace Qt3DRender |
1496 | |
1497 | QT_END_NAMESPACE |
1498 | |