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 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
1098void 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
1135void 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
1142void 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
1160void 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
1177void 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
1200void 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
1236void RenderView::setShaderAndUniforms(RenderCommand *command,
1237 const ParameterInfoList &parameters,
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
1322void 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
1437bool RenderView::hasBlitFramebufferInfo() const
1438{
1439 return m_hasBlitFramebufferInfo;
1440}
1441
1442void RenderView::setHasBlitFramebufferInfo(bool hasBlitFramebufferInfo)
1443{
1444 m_hasBlitFramebufferInfo = hasBlitFramebufferInfo;
1445}
1446
1447bool 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
1473BlitFramebufferInfo RenderView::blitFrameBufferInfo() const
1474{
1475 return m_blitFrameBufferInfo;
1476}
1477
1478void RenderView::setBlitFrameBufferInfo(const BlitFramebufferInfo &blitFrameBufferInfo)
1479{
1480 m_blitFrameBufferInfo = blitFrameBufferInfo;
1481}
1482
1483bool RenderView::isDownloadBuffersEnable() const
1484{
1485 return m_isDownloadBuffersEnable;
1486}
1487
1488void RenderView::setIsDownloadBuffersEnable(bool isDownloadBuffersEnable)
1489{
1490 m_isDownloadBuffersEnable = isDownloadBuffersEnable;
1491}
1492
1493} // namespace OpenGL
1494} // namespace Render
1495} // namespace Qt3DRender
1496
1497QT_END_NAMESPACE
1498

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