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 | |
85 | QT_BEGIN_NAMESPACE |
86 | |
87 | namespace Qt3DRender { |
88 | namespace Render { |
89 | namespace OpenGL { |
90 | |
91 | namespace { |
92 | |
93 | // register our QNodeId's as a metatype during program loading |
94 | const int Q_DECL_UNUSED qNodeIdTypeId = qMetaTypeId<Qt3DCore::QNodeId>(); |
95 | |
96 | std::atomic_bool wasInitialized{}; |
97 | |
98 | } // anonymous namespace |
99 | |
100 | RenderView::StandardUniformsNameToTypeHash RenderView::ms_standardUniformSetters; |
101 | |
102 | |
103 | RenderView::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 |
135 | static 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 | |
143 | static Matrix4x4 getProjectionMatrix(const CameraLens *lens) |
144 | { |
145 | return lens ? lens->projection() : Matrix4x4(); |
146 | } |
147 | |
148 | UniformValue 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 | |
221 | RenderView::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 | |
231 | RenderView::~RenderView() |
232 | { |
233 | } |
234 | |
235 | namespace { |
236 | |
237 | template<int SortType> |
238 | struct AdjacentSubRangeFinder |
239 | { |
240 | static bool adjacentSubRange(const RenderCommand &, const RenderCommand &) |
241 | { |
242 | Q_UNREACHABLE(); |
243 | return false; |
244 | } |
245 | }; |
246 | |
247 | template<> |
248 | struct 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 | |
256 | template<> |
257 | struct 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 | |
265 | template<> |
266 | struct 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 | |
274 | template<> |
275 | struct 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 | |
283 | template<> |
284 | struct 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 | |
306 | template<typename Predicate> |
307 | int 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 | |
326 | template<int SortType> |
327 | struct 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 | |
338 | template<> |
339 | struct 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 | |
354 | template<> |
355 | struct 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 | |
370 | template<> |
371 | struct 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 | |
386 | template<> |
387 | struct 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 | |
402 | template<> |
403 | struct 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 | |
434 | int 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 | |
457 | void 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 | |
477 | void 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 | |
520 | void 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 | |
583 | void RenderView::setRenderer(Renderer *renderer) |
584 | { |
585 | m_renderer = renderer; |
586 | m_manager = renderer->nodeManagers(); |
587 | } |
588 | |
589 | RenderStateSet *RenderView::getOrCreateStateSet() |
590 | { |
591 | if (!m_stateSet) |
592 | m_stateSet.reset(other: new RenderStateSet()); |
593 | return m_stateSet.data(); |
594 | } |
595 | |
596 | void 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 |
631 | EntityRenderCommandData 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 | |
760 | EntityRenderCommandData 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 | |
824 | void 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 | |
865 | void 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 | |
886 | void 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 | |
923 | void 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 | |
930 | void 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 | |
948 | void 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 | |
965 | void 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 | |
992 | void 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 | |
1028 | void RenderView::setShaderAndUniforms(RenderCommand *command, |
1029 | const ParameterInfoList ¶meters, |
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 | |
1114 | void 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 | |
1228 | bool RenderView::hasBlitFramebufferInfo() const |
1229 | { |
1230 | return m_hasBlitFramebufferInfo; |
1231 | } |
1232 | |
1233 | void RenderView::setHasBlitFramebufferInfo(bool hasBlitFramebufferInfo) |
1234 | { |
1235 | m_hasBlitFramebufferInfo = hasBlitFramebufferInfo; |
1236 | } |
1237 | |
1238 | bool 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 | |
1264 | BlitFramebufferInfo RenderView::blitFrameBufferInfo() const |
1265 | { |
1266 | return m_blitFrameBufferInfo; |
1267 | } |
1268 | |
1269 | void RenderView::setBlitFrameBufferInfo(const BlitFramebufferInfo &blitFrameBufferInfo) |
1270 | { |
1271 | m_blitFrameBufferInfo = blitFrameBufferInfo; |
1272 | } |
1273 | |
1274 | bool RenderView::isDownloadBuffersEnable() const |
1275 | { |
1276 | return m_isDownloadBuffersEnable; |
1277 | } |
1278 | |
1279 | void RenderView::setIsDownloadBuffersEnable(bool isDownloadBuffersEnable) |
1280 | { |
1281 | m_isDownloadBuffersEnable = isDownloadBuffersEnable; |
1282 | } |
1283 | |
1284 | } // namespace OpenGL |
1285 | } // namespace Render |
1286 | } // namespace Qt3DRender |
1287 | |
1288 | QT_END_NAMESPACE |
1289 | |