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
68QT_BEGIN_NAMESPACE
69
70namespace Qt3DRender {
71namespace Render {
72namespace OpenGL {
73
74namespace {
75
76// register our QNodeId's as a metatype during program loading
77Q_DECL_UNUSED const int qNodeIdTypeId = qMetaTypeId<Qt3DCore::QNodeId>();
78
79std::atomic_bool wasInitialized{};
80
81} // anonymous namespace
82
83RenderView::StandardUniformsNameToTypeHash RenderView::ms_standardUniformSetters;
84
85
86RenderView::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
120static 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
128static Matrix4x4 getProjectionMatrix(const CameraLens *lens)
129{
130 return lens ? lens->projection() : Matrix4x4();
131}
132
133UniformValue 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*/
216void 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
434RenderView::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
444RenderView::~RenderView()
445{
446}
447
448namespace {
449
450template<int SortType>
451struct AdjacentSubRangeFinder
452{
453 static bool adjacentSubRange(const RenderCommand &, const RenderCommand &)
454 {
455 Q_UNREACHABLE_RETURN(false);
456 }
457};
458
459template<>
460struct 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
468template<>
469struct 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
477template<>
478struct 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
486template<>
487struct 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
495template<>
496struct 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
518template<typename Predicate>
519int 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
538template<int SortType>
539struct 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
550template<>
551struct 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
566template<>
567struct 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
582template<>
583struct 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
598template<>
599struct 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
614template<>
615struct 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
650int 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
672void 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
692void 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
735void 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
798void RenderView::setRenderer(Renderer *renderer)
799{
800 m_renderer = renderer;
801 m_manager = renderer->nodeManagers();
802}
803
804RenderStateSet *RenderView::getOrCreateStateSet()
805{
806 if (!m_stateSet)
807 m_stateSet.reset(other: new RenderStateSet());
808 return m_stateSet.data();
809}
810
811void 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
846EntityRenderCommandData 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
983EntityRenderCommandData 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
1047void 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
1077void 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
1106void 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
1143void 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
1150void 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
1168void 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
1185void 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
1208void 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
1244void RenderView::setShaderAndUniforms(RenderCommand *command,
1245 const ParameterInfoList &parameters,
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
1330void 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
1445bool RenderView::hasBlitFramebufferInfo() const
1446{
1447 return m_hasBlitFramebufferInfo;
1448}
1449
1450void RenderView::setHasBlitFramebufferInfo(bool hasBlitFramebufferInfo)
1451{
1452 m_hasBlitFramebufferInfo = hasBlitFramebufferInfo;
1453}
1454
1455bool 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
1481BlitFramebufferInfo RenderView::blitFrameBufferInfo() const
1482{
1483 return m_blitFrameBufferInfo;
1484}
1485
1486void RenderView::setBlitFrameBufferInfo(const BlitFramebufferInfo &blitFrameBufferInfo)
1487{
1488 m_blitFrameBufferInfo = blitFrameBufferInfo;
1489}
1490
1491bool RenderView::isDownloadBuffersEnable() const
1492{
1493 return m_isDownloadBuffersEnable;
1494}
1495
1496void RenderView::setIsDownloadBuffersEnable(bool isDownloadBuffersEnable)
1497{
1498 m_isDownloadBuffersEnable = isDownloadBuffersEnable;
1499}
1500
1501} // namespace OpenGL
1502} // namespace Render
1503} // namespace Qt3DRender
1504
1505QT_END_NAMESPACE
1506

source code of qt3d/src/plugins/renderers/opengl/renderer/renderview.cpp