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

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