1 | // Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB). |
2 | // Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies). |
3 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
4 | |
5 | #include "renderview_p.h" |
6 | #include <Qt3DRender/qmaterial.h> |
7 | #include <Qt3DRender/qrenderaspect.h> |
8 | #include <Qt3DRender/qrendertarget.h> |
9 | #include <Qt3DRender/qabstractlight.h> |
10 | #include <Qt3DRender/private/sphere_p.h> |
11 | |
12 | #include <Qt3DRender/private/cameraselectornode_p.h> |
13 | #include <Qt3DRender/private/framegraphnode_p.h> |
14 | #include <Qt3DRender/private/layerfilternode_p.h> |
15 | #include <Qt3DRender/private/qparameter_p.h> |
16 | #include <Qt3DRender/private/cameralens_p.h> |
17 | #include <Qt3DRender/private/effect_p.h> |
18 | #include <Qt3DRender/private/entity_p.h> |
19 | #include <Qt3DRender/private/nodemanagers_p.h> |
20 | #include <Qt3DRender/private/layer_p.h> |
21 | #include <Qt3DRender/private/renderlogging_p.h> |
22 | #include <Qt3DRender/private/renderpassfilternode_p.h> |
23 | #include <Qt3DRender/private/renderpass_p.h> |
24 | #include <Qt3DRender/private/geometryrenderer_p.h> |
25 | #include <Qt3DRender/private/techniquefilternode_p.h> |
26 | #include <Qt3DRender/private/viewportnode_p.h> |
27 | #include <Qt3DRender/private/buffermanager_p.h> |
28 | #include <Qt3DRender/private/geometryrenderermanager_p.h> |
29 | #include <Qt3DRender/private/rendercapture_p.h> |
30 | #include <Qt3DRender/private/buffercapture_p.h> |
31 | #include <Qt3DRender/private/stringtoint_p.h> |
32 | #include <Qt3DRender/private/renderlogging_p.h> |
33 | #include <Qt3DRender/private/renderstateset_p.h> |
34 | #include <Qt3DRender/private/uniformblockbuilder_p.h> |
35 | #include <Qt3DRender/private/clearbuffers_p.h> |
36 | #include <Qt3DRender/private/rendertargetselectornode_p.h> |
37 | #include <Qt3DRender/private/sortpolicy_p.h> |
38 | #include <Qt3DRender/private/techniquefilternode_p.h> |
39 | #include <Qt3DRender/private/managers_p.h> |
40 | #include <Qt3DRender/private/shaderdata_p.h> |
41 | #include <Qt3DRender/private/statesetnode_p.h> |
42 | #include <Qt3DRender/private/dispatchcompute_p.h> |
43 | #include <Qt3DRender/private/rendersurfaceselector_p.h> |
44 | #include <Qt3DRender/private/rendercapture_p.h> |
45 | #include <Qt3DRender/private/buffercapture_p.h> |
46 | #include <Qt3DRender/private/techniquemanager_p.h> |
47 | #include <Qt3DRender/private/blitframebuffer_p.h> |
48 | #include <Qt3DRender/private/rendercapture_p.h> |
49 | |
50 | #include <rendercommand_p.h> |
51 | #include <renderer_p.h> |
52 | #include <submissioncontext_p.h> |
53 | #include <rhiresourcemanagers_p.h> |
54 | #include <Qt3DCore/qentity.h> |
55 | #include <QtGui/qsurface.h> |
56 | #include <algorithm> |
57 | #include <atomic> |
58 | #include <cstdlib> |
59 | #include <QDebug> |
60 | #if defined(QT3D_RENDER_VIEW_JOB_TIMINGS) |
61 | #include <QElapsedTimer> |
62 | #endif |
63 | |
64 | QT_BEGIN_NAMESPACE |
65 | |
66 | namespace Qt3DRender { |
67 | namespace Render { |
68 | namespace Rhi { |
69 | |
70 | namespace { |
71 | |
72 | // register our QNodeId's as a metatype during program loading |
73 | Q_DECL_UNUSED const int qNodeIdTypeId = qMetaTypeId<Qt3DCore::QNodeId>(); |
74 | |
75 | const int MAX_LIGHTS = 8; |
76 | |
77 | #define LIGHT_POSITION_NAME QLatin1String(".position") |
78 | #define LIGHT_TYPE_NAME QLatin1String(".type") |
79 | #define LIGHT_COLOR_NAME QLatin1String(".color") |
80 | #define LIGHT_INTENSITY_NAME QLatin1String(".intensity") |
81 | |
82 | int LIGHT_COUNT_NAME_ID = 0; |
83 | int LIGHT_POSITION_NAMES[MAX_LIGHTS]; |
84 | int LIGHT_TYPE_NAMES[MAX_LIGHTS]; |
85 | int LIGHT_COLOR_NAMES[MAX_LIGHTS]; |
86 | int LIGHT_INTENSITY_NAMES[MAX_LIGHTS]; |
87 | QString LIGHT_STRUCT_NAMES[MAX_LIGHTS]; |
88 | |
89 | int LIGHT_POSITION_UNROLL_NAMES[MAX_LIGHTS]; |
90 | int LIGHT_TYPE_UNROLL_NAMES[MAX_LIGHTS]; |
91 | int LIGHT_COLOR_UNROLL_NAMES[MAX_LIGHTS]; |
92 | int LIGHT_INTENSITY_UNROLL_NAMES[MAX_LIGHTS]; |
93 | QString LIGHT_STRUCT_UNROLL_NAMES[MAX_LIGHTS]; |
94 | |
95 | std::atomic_bool wasInitialized {}; |
96 | |
97 | } // anonymous namespace |
98 | |
99 | // TODO: Move this somewhere global where GraphicsContext::setViewport() can use it too |
100 | static QRectF resolveViewport(const QRectF &fractionalViewport, const QSize &surfaceSize) |
101 | { |
102 | return QRectF(fractionalViewport.x() * surfaceSize.width(), |
103 | (1.0 - fractionalViewport.y() - fractionalViewport.height()) |
104 | * surfaceSize.height(), |
105 | fractionalViewport.width() * surfaceSize.width(), |
106 | fractionalViewport.height() * surfaceSize.height()); |
107 | } |
108 | |
109 | static Matrix4x4 getProjectionMatrix(const CameraLens *lens) |
110 | { |
111 | Matrix4x4 m; |
112 | if (lens) |
113 | m = lens->projection(); |
114 | return m; |
115 | } |
116 | |
117 | /*! |
118 | \internal |
119 | Walks up the framegraph tree from \a fgLeaf and builds up as much state |
120 | as possible and populates \a rv. For cases where we can't get the specific state |
121 | (e.g. because it depends upon more than just the framegraph) we store the data from |
122 | the framegraph that will be needed to later when the rest of the data becomes available |
123 | */ |
124 | void RenderView::setRenderViewConfigFromFrameGraphLeafNode(RenderView *rv, const FrameGraphNode *fgLeaf) |
125 | { |
126 | // The specific RenderPass to be used is also dependent upon the Effect and TechniqueFilter |
127 | // which is referenced by the Material which is referenced by the RenderMesh. So we can |
128 | // only store the filter info in the RenderView structure and use it to do the resolving |
129 | // when we build the RenderCommand list. |
130 | const NodeManagers *manager = rv->nodeManagers(); |
131 | const FrameGraphNode *node = fgLeaf; |
132 | |
133 | while (node) { |
134 | FrameGraphNode::FrameGraphNodeType type = node->nodeType(); |
135 | if (node->isEnabled()) |
136 | switch (type) { |
137 | case FrameGraphNode::InvalidNodeType: |
138 | // A base FrameGraphNode, can be used for grouping purposes |
139 | break; |
140 | case FrameGraphNode::CameraSelector: |
141 | // Can be set only once and we take camera nearest to the leaf node |
142 | if (!rv->renderCameraLens()) { |
143 | const CameraSelector *cameraSelector = |
144 | static_cast<const CameraSelector *>(node); |
145 | Entity *camNode = manager->renderNodesManager()->lookupResource( |
146 | id: cameraSelector->cameraUuid()); |
147 | if (camNode) { |
148 | CameraLens *lens = camNode->renderComponent<CameraLens>(); |
149 | rv->setRenderCameraEntity(camNode); |
150 | if (lens && lens->isEnabled()) { |
151 | rv->setRenderCameraLens(lens); |
152 | // ViewMatrix and ProjectionMatrix are computed |
153 | // later in updateMatrices() |
154 | // since at this point the transformation matrices |
155 | // may not yet have been updated |
156 | } |
157 | } |
158 | } |
159 | break; |
160 | |
161 | case FrameGraphNode::LayerFilter: // Can be set multiple times in the tree |
162 | rv->appendLayerFilter(layerFilterId: static_cast<const LayerFilterNode *>(node)->peerId()); |
163 | break; |
164 | |
165 | case FrameGraphNode::ProximityFilter: // Can be set multiple times in the tree |
166 | rv->appendProximityFilterId(proximityFilterId: node->peerId()); |
167 | break; |
168 | |
169 | case FrameGraphNode::RenderPassFilter: |
170 | // Can be set once |
171 | // TODO: Amalgamate all render pass filters from leaf to root |
172 | if (!rv->renderPassFilter()) |
173 | rv->setRenderPassFilter(static_cast<const RenderPassFilter *>(node)); |
174 | break; |
175 | |
176 | case FrameGraphNode::RenderTarget: { |
177 | // Can be set once and we take render target nearest to the leaf node |
178 | const RenderTargetSelector *targetSelector = |
179 | static_cast<const RenderTargetSelector *>(node); |
180 | Qt3DCore::QNodeId renderTargetUid = targetSelector->renderTargetUuid(); |
181 | |
182 | // Note: we ignore the render target outputs the RenderTargetSelector |
183 | // might specify as we can't handle that with RHI |
184 | |
185 | // Add renderTarget Handle |
186 | if (!rv->renderTargetId()) { |
187 | rv->setRenderTargetId(renderTargetUid); |
188 | } |
189 | break; |
190 | } |
191 | |
192 | case FrameGraphNode::ClearBuffers: { |
193 | const ClearBuffers *cbNode = static_cast<const ClearBuffers *>(node); |
194 | rv->addClearBuffers(cb: cbNode); |
195 | break; |
196 | } |
197 | |
198 | case FrameGraphNode::TechniqueFilter: |
199 | // Can be set once |
200 | // TODO Amalgamate all technique filters from leaf to root |
201 | if (!rv->techniqueFilter()) |
202 | rv->setTechniqueFilter(static_cast<const TechniqueFilter *>(node)); |
203 | break; |
204 | |
205 | case FrameGraphNode::Viewport: { |
206 | // If the Viewport has already been set in a lower node |
207 | // Make it so that the new viewport is actually |
208 | // a subregion relative to that of the parent viewport |
209 | const ViewportNode *vpNode = static_cast<const ViewportNode *>(node); |
210 | rv->setViewport(ViewportNode::computeViewport(childViewport: rv->viewport(), parentViewport: vpNode)); |
211 | rv->setGamma(vpNode->gamma()); |
212 | break; |
213 | } |
214 | |
215 | case FrameGraphNode::SortMethod: { |
216 | const Render::SortPolicy *sortPolicy = |
217 | static_cast<const Render::SortPolicy *>(node); |
218 | rv->addSortType(sortTypes: sortPolicy->sortTypes()); |
219 | break; |
220 | } |
221 | |
222 | case FrameGraphNode::SubtreeEnabler: |
223 | // Has no meaning here. SubtreeEnabler was used |
224 | // in a prior step to filter the list of RenderViewJobs |
225 | break; |
226 | |
227 | case FrameGraphNode::StateSet: { |
228 | const Render::StateSetNode *rStateSet = static_cast<const Render::StateSetNode *>(node); |
229 | // Add states from new stateSet we might be missing |
230 | // but don' t override existing states (lower StateSetNode always has priority) |
231 | if (rStateSet->hasRenderStates()) { |
232 | // Create global RenderStateSet for renderView if no stateSet was set before |
233 | RenderStateSet *stateSet = rv->getOrCreateStateSet(); |
234 | addStatesToRenderStateSet(stateSet, stateIds: rStateSet->renderStates(), manager: manager->renderStateManager()); |
235 | } |
236 | break; |
237 | } |
238 | |
239 | case FrameGraphNode::NoDraw: { |
240 | rv->setNoDraw(true); |
241 | break; |
242 | } |
243 | |
244 | case FrameGraphNode::FrustumCulling: { |
245 | rv->setFrustumCulling(true); |
246 | break; |
247 | } |
248 | |
249 | case FrameGraphNode::ComputeDispatch: { |
250 | const Render::DispatchCompute *dispatchCompute = |
251 | static_cast<const Render::DispatchCompute *>(node); |
252 | rv->setCompute(true); |
253 | rv->setComputeWorkgroups(x: dispatchCompute->x(), y: dispatchCompute->y(), |
254 | z: dispatchCompute->z()); |
255 | break; |
256 | } |
257 | |
258 | case FrameGraphNode::Lighting: { |
259 | // TODO |
260 | break; |
261 | } |
262 | |
263 | case FrameGraphNode::Surface: { |
264 | // Use the surface closest to leaf node |
265 | if (rv->surface() == nullptr) { |
266 | const Render::RenderSurfaceSelector *surfaceSelector = |
267 | static_cast<const Render::RenderSurfaceSelector *>(node); |
268 | rv->setSurface(surfaceSelector->surface()); |
269 | rv->setSurfaceSize(surfaceSelector->renderTargetSize() |
270 | * surfaceSelector->devicePixelRatio()); |
271 | rv->setDevicePixelRatio(surfaceSelector->devicePixelRatio()); |
272 | } |
273 | break; |
274 | } |
275 | case FrameGraphNode::RenderCapture: { |
276 | auto *renderCapture = const_cast<Render::RenderCapture *>( |
277 | static_cast<const Render::RenderCapture *>(node)); |
278 | if (rv->renderCaptureNodeId().isNull() && renderCapture->wasCaptureRequested()) { |
279 | rv->setRenderCaptureNodeId(renderCapture->peerId()); |
280 | rv->setRenderCaptureRequest(renderCapture->takeCaptureRequest()); |
281 | } |
282 | break; |
283 | } |
284 | |
285 | case FrameGraphNode::MemoryBarrier: { |
286 | // Not available in rhi |
287 | break; |
288 | } |
289 | |
290 | case FrameGraphNode::BufferCapture: { |
291 | auto *bufferCapture = const_cast<Render::BufferCapture *>( |
292 | static_cast<const Render::BufferCapture *>(node)); |
293 | if (bufferCapture != nullptr) |
294 | rv->setIsDownloadBuffersEnable(bufferCapture->isEnabled()); |
295 | break; |
296 | } |
297 | |
298 | case FrameGraphNode::BlitFramebuffer: { |
299 | const Render::BlitFramebuffer *blitFramebufferNode = |
300 | static_cast<const Render::BlitFramebuffer *>(node); |
301 | rv->setHasBlitFramebufferInfo(true); |
302 | BlitFramebufferInfo bfbInfo; |
303 | bfbInfo.sourceRenderTargetId = blitFramebufferNode->sourceRenderTargetId(); |
304 | bfbInfo.destinationRenderTargetId = |
305 | blitFramebufferNode->destinationRenderTargetId(); |
306 | bfbInfo.sourceRect = blitFramebufferNode->sourceRect(); |
307 | bfbInfo.destinationRect = blitFramebufferNode->destinationRect(); |
308 | bfbInfo.sourceAttachmentPoint = blitFramebufferNode->sourceAttachmentPoint(); |
309 | bfbInfo.destinationAttachmentPoint = |
310 | blitFramebufferNode->destinationAttachmentPoint(); |
311 | bfbInfo.interpolationMethod = blitFramebufferNode->interpolationMethod(); |
312 | rv->setBlitFrameBufferInfo(bfbInfo); |
313 | break; |
314 | } |
315 | |
316 | case FrameGraphNode::WaitFence: { |
317 | // Not available in rhi |
318 | break; |
319 | } |
320 | |
321 | case FrameGraphNode::SetFence: { |
322 | // Not available in rhi |
323 | break; |
324 | } |
325 | |
326 | case FrameGraphNode::NoPicking: |
327 | // Nothing to do RenderView wise for NoPicking |
328 | break; |
329 | |
330 | case FrameGraphNode::DebugOverlay: |
331 | // Not supported yet with RHI |
332 | break; |
333 | |
334 | default: |
335 | // Should never get here |
336 | qCWarning(Backend) << "Unhandled FrameGraphNode type" ; |
337 | } |
338 | |
339 | node = node->parent(); |
340 | } |
341 | } |
342 | |
343 | RenderView::RenderView() |
344 | { |
345 | if (Q_UNLIKELY(!wasInitialized.exchange(true))) { |
346 | // Needed as we can control the init order of static/global variables across compile units |
347 | // and this hash relies on the static StringToInt class |
348 | |
349 | LIGHT_COUNT_NAME_ID = StringToInt::lookupId(str: QLatin1String("lightCount" )); |
350 | for (int i = 0; i < MAX_LIGHTS; ++i) { |
351 | Q_STATIC_ASSERT_X(MAX_LIGHTS < 10, "can't use the QChar trick anymore" ); |
352 | LIGHT_STRUCT_NAMES[i] = |
353 | QLatin1String("lights[" ) + QLatin1Char(char('0' + i)) + QLatin1Char(']'); |
354 | LIGHT_POSITION_NAMES[i] = |
355 | StringToInt::lookupId(str: LIGHT_STRUCT_NAMES[i] + LIGHT_POSITION_NAME); |
356 | LIGHT_TYPE_NAMES[i] = StringToInt::lookupId(str: LIGHT_STRUCT_NAMES[i] + LIGHT_TYPE_NAME); |
357 | LIGHT_COLOR_NAMES[i] = StringToInt::lookupId(str: LIGHT_STRUCT_NAMES[i] + LIGHT_COLOR_NAME); |
358 | LIGHT_INTENSITY_NAMES[i] = |
359 | StringToInt::lookupId(str: LIGHT_STRUCT_NAMES[i] + LIGHT_INTENSITY_NAME); |
360 | |
361 | LIGHT_STRUCT_UNROLL_NAMES[i] = |
362 | QLatin1String("light_" ) + QLatin1Char(char('0' + i)); |
363 | LIGHT_POSITION_UNROLL_NAMES[i] = |
364 | StringToInt::lookupId(str: LIGHT_STRUCT_UNROLL_NAMES[i] + LIGHT_POSITION_NAME); |
365 | LIGHT_TYPE_UNROLL_NAMES[i] = StringToInt::lookupId(str: LIGHT_STRUCT_UNROLL_NAMES[i] + LIGHT_TYPE_NAME); |
366 | LIGHT_COLOR_UNROLL_NAMES[i] = StringToInt::lookupId(str: LIGHT_STRUCT_UNROLL_NAMES[i] + LIGHT_COLOR_NAME); |
367 | LIGHT_INTENSITY_UNROLL_NAMES[i] = |
368 | StringToInt::lookupId(str: LIGHT_STRUCT_UNROLL_NAMES[i] + LIGHT_INTENSITY_NAME); |
369 | } |
370 | } |
371 | } |
372 | |
373 | RenderView::~RenderView() |
374 | { |
375 | } |
376 | |
377 | namespace { |
378 | |
379 | template<int SortType> |
380 | struct AdjacentSubRangeFinder |
381 | { |
382 | static bool adjacentSubRange(const RenderCommand &, const RenderCommand &) |
383 | { |
384 | Q_UNREACHABLE_RETURN(false); |
385 | } |
386 | }; |
387 | |
388 | template<> |
389 | struct AdjacentSubRangeFinder<QSortPolicy::StateChangeCost> |
390 | { |
391 | static bool adjacentSubRange(const RenderCommand &a, const RenderCommand &b) |
392 | { |
393 | return a.m_changeCost == b.m_changeCost; |
394 | } |
395 | }; |
396 | |
397 | template<> |
398 | struct AdjacentSubRangeFinder<QSortPolicy::BackToFront> |
399 | { |
400 | static bool adjacentSubRange(const RenderCommand &a, const RenderCommand &b) |
401 | { |
402 | return qFuzzyCompare(p1: a.m_depth, p2: b.m_depth); |
403 | } |
404 | }; |
405 | |
406 | template<> |
407 | struct AdjacentSubRangeFinder<QSortPolicy::Material> |
408 | { |
409 | static bool adjacentSubRange(const RenderCommand &a, const RenderCommand &b) |
410 | { |
411 | return a.m_rhiShader == b.m_rhiShader; |
412 | } |
413 | }; |
414 | |
415 | template<> |
416 | struct AdjacentSubRangeFinder<QSortPolicy::FrontToBack> |
417 | { |
418 | static bool adjacentSubRange(const RenderCommand &a, const RenderCommand &b) |
419 | { |
420 | return qFuzzyCompare(p1: a.m_depth, p2: b.m_depth); |
421 | } |
422 | }; |
423 | |
424 | template<> |
425 | struct AdjacentSubRangeFinder<QSortPolicy::Texture> |
426 | { |
427 | static bool adjacentSubRange(const RenderCommand &a, const RenderCommand &b) |
428 | { |
429 | // Two renderCommands are adjacent if one contains all the other command's textures |
430 | const std::vector<ShaderParameterPack::NamedResource> &texturesA = a.m_parameterPack.textures(); |
431 | const std::vector<ShaderParameterPack::NamedResource> &texturesB = b.m_parameterPack.textures(); |
432 | |
433 | const bool bBigger = texturesB.size() > texturesA.size(); |
434 | const std::vector<ShaderParameterPack::NamedResource> &smallestVector = bBigger ? texturesA : texturesB; |
435 | const std::vector<ShaderParameterPack::NamedResource> &biggestVector = bBigger ? texturesB : texturesA; |
436 | |
437 | const auto e = biggestVector.cend(); |
438 | for (const ShaderParameterPack::NamedResource &tex : smallestVector) { |
439 | if (std::find(first: biggestVector.begin(), last: e, val: tex) == e) |
440 | return false; |
441 | } |
442 | |
443 | return true; |
444 | } |
445 | }; |
446 | |
447 | template<typename Predicate> |
448 | int advanceUntilNonAdjacent(const EntityRenderCommandDataView *view, |
449 | const size_t beg, const size_t end, Predicate pred) |
450 | { |
451 | const std::vector<size_t> &commandIndices = view->indices; |
452 | const std::vector<RenderCommand> &commands = view->data.commands; |
453 | size_t i = beg + 1; |
454 | if (i < end) { |
455 | const size_t startIdx = commandIndices[beg]; |
456 | while (i < end) { |
457 | const size_t targetIdx = commandIndices[i]; |
458 | if (!pred(commands[startIdx], commands[targetIdx])) |
459 | break; |
460 | ++i; |
461 | } |
462 | } |
463 | return int(i); |
464 | } |
465 | |
466 | |
467 | template<int SortType> |
468 | struct SubRangeSorter |
469 | { |
470 | static void sortSubRange(EntityRenderCommandDataView *view, size_t begin, const size_t end) |
471 | { |
472 | Q_UNUSED(view); |
473 | Q_UNUSED(begin); |
474 | Q_UNUSED(end); |
475 | Q_UNREACHABLE(); |
476 | } |
477 | }; |
478 | |
479 | template<> |
480 | struct SubRangeSorter<QSortPolicy::StateChangeCost> |
481 | { |
482 | static void sortSubRange(EntityRenderCommandDataView *view, size_t begin, const size_t end) |
483 | { |
484 | std::vector<size_t> &commandIndices = view->indices; |
485 | const std::vector<RenderCommand> &commands = view->data.commands; |
486 | std::stable_sort(first: commandIndices.begin() + begin, last: commandIndices.begin() + end, |
487 | comp: [&commands] (const size_t &iA, const size_t &iB) { |
488 | const RenderCommand &a = commands[iA]; |
489 | const RenderCommand &b = commands[iB]; |
490 | return a.m_changeCost > b.m_changeCost; |
491 | }); |
492 | } |
493 | }; |
494 | |
495 | template<> |
496 | struct SubRangeSorter<QSortPolicy::BackToFront> |
497 | { |
498 | static void sortSubRange(EntityRenderCommandDataView *view, size_t begin, const size_t end) |
499 | { |
500 | std::vector<size_t> &commandIndices = view->indices; |
501 | const std::vector<RenderCommand> &commands = view->data.commands; |
502 | std::stable_sort(first: commandIndices.begin() + begin, last: commandIndices.begin() + end, |
503 | comp: [&commands] (const size_t &iA, const size_t &iB) { |
504 | const RenderCommand &a = commands[iA]; |
505 | const RenderCommand &b = commands[iB]; |
506 | return a.m_depth > b.m_depth; |
507 | }); |
508 | } |
509 | }; |
510 | |
511 | template<> |
512 | struct SubRangeSorter<QSortPolicy::Material> |
513 | { |
514 | static void sortSubRange(EntityRenderCommandDataView *view, size_t begin, const size_t end) |
515 | { |
516 | std::vector<size_t> &commandIndices = view->indices; |
517 | const std::vector<RenderCommand> &commands = view->data.commands; |
518 | std::stable_sort(first: commandIndices.begin() + begin, last: commandIndices.begin() + end, |
519 | comp: [&commands] (const size_t &iA, const size_t &iB) { |
520 | const RenderCommand &a = commands[iA]; |
521 | const RenderCommand &b = commands[iB]; |
522 | return a.m_rhiShader > b.m_rhiShader; |
523 | }); |
524 | } |
525 | }; |
526 | |
527 | template<> |
528 | struct SubRangeSorter<QSortPolicy::FrontToBack> |
529 | { |
530 | static void sortSubRange(EntityRenderCommandDataView *view, size_t begin, const size_t end) |
531 | { |
532 | std::vector<size_t> &commandIndices = view->indices; |
533 | const std::vector<RenderCommand> &commands = view->data.commands; |
534 | std::stable_sort(first: commandIndices.begin() + begin, last: commandIndices.begin() + end, |
535 | comp: [&commands] (const size_t &iA, const size_t &iB) { |
536 | const RenderCommand &a = commands[iA]; |
537 | const RenderCommand &b = commands[iB]; |
538 | return a.m_depth < b.m_depth; |
539 | }); |
540 | } |
541 | }; |
542 | |
543 | template<> |
544 | struct SubRangeSorter<QSortPolicy::Texture> |
545 | { |
546 | static void sortSubRange(EntityRenderCommandDataView *view, size_t begin, const size_t end) |
547 | { |
548 | #ifndef Q_OS_WIN |
549 | std::vector<size_t> &commandIndices = view->indices; |
550 | const std::vector<RenderCommand> &commands = view->data.commands; |
551 | std::stable_sort(first: commandIndices.begin() + begin, last: commandIndices.begin() + end, |
552 | comp: [&commands] (const int &iA, const int &iB) { |
553 | const RenderCommand &a = commands[iA]; |
554 | const RenderCommand &b = commands[iB]; |
555 | const std::vector<ShaderParameterPack::NamedResource> &texturesA = a.m_parameterPack.textures(); |
556 | const std::vector<ShaderParameterPack::NamedResource> &texturesB = b.m_parameterPack.textures(); |
557 | |
558 | const bool bBigger = texturesB.size() > texturesA.size(); |
559 | const std::vector<ShaderParameterPack::NamedResource> &smallestVector = bBigger ? texturesA : texturesB; |
560 | const std::vector<ShaderParameterPack::NamedResource> &biggestVector = bBigger ? texturesB : texturesA; |
561 | |
562 | size_t identicalTextureCount = 0; |
563 | const auto e = biggestVector.cend(); |
564 | for (const ShaderParameterPack::NamedResource &tex : smallestVector) { |
565 | if (std::find(first: biggestVector.begin(), last: e, val: tex) != e) |
566 | ++identicalTextureCount; |
567 | } |
568 | |
569 | return identicalTextureCount < smallestVector.size(); |
570 | }); |
571 | #else |
572 | Q_UNUSED(view); |
573 | Q_UNUSED(begin); |
574 | Q_UNUSED(end); |
575 | #endif |
576 | } |
577 | }; |
578 | |
579 | int findSubRange(const EntityRenderCommandDataView *view, |
580 | const int begin, const int end, |
581 | const QSortPolicy::SortType sortType) |
582 | { |
583 | switch (sortType) { |
584 | case QSortPolicy::StateChangeCost: |
585 | return advanceUntilNonAdjacent(view, beg: begin, end, pred: AdjacentSubRangeFinder<QSortPolicy::StateChangeCost>::adjacentSubRange); |
586 | case QSortPolicy::BackToFront: |
587 | return advanceUntilNonAdjacent(view, beg: begin, end, pred: AdjacentSubRangeFinder<QSortPolicy::BackToFront>::adjacentSubRange); |
588 | case QSortPolicy::Material: |
589 | return advanceUntilNonAdjacent(view, beg: begin, end, pred: AdjacentSubRangeFinder<QSortPolicy::Material>::adjacentSubRange); |
590 | case QSortPolicy::FrontToBack: |
591 | return advanceUntilNonAdjacent(view, beg: begin, end, pred: AdjacentSubRangeFinder<QSortPolicy::FrontToBack>::adjacentSubRange); |
592 | case QSortPolicy::Texture: |
593 | return advanceUntilNonAdjacent(view, beg: begin, end, pred: AdjacentSubRangeFinder<QSortPolicy::Texture>::adjacentSubRange); |
594 | case QSortPolicy::Uniform: |
595 | return end; |
596 | default: |
597 | Q_UNREACHABLE_RETURN(end); |
598 | } |
599 | } |
600 | |
601 | void sortByMaterial(EntityRenderCommandDataView *view, int begin, const int end) |
602 | { |
603 | // We try to arrange elements so that their rendering cost is minimized for a given shader |
604 | std::vector<size_t> &commandIndices = view->indices; |
605 | const std::vector<RenderCommand> &commands = view->data.commands; |
606 | int rangeEnd = advanceUntilNonAdjacent(view, beg: begin, end, pred: AdjacentSubRangeFinder<QSortPolicy::Material>::adjacentSubRange); |
607 | while (begin != end) { |
608 | if (begin + 1 < rangeEnd) { |
609 | std::stable_sort(first: commandIndices.begin() + begin + 1, last: commandIndices.begin() + rangeEnd, |
610 | comp: [&commands] (const size_t &iA, const size_t &iB) { |
611 | const RenderCommand &a = commands[iA]; |
612 | const RenderCommand &b = commands[iB]; |
613 | return a.m_material.handle() < b.m_material.handle(); |
614 | }); |
615 | } |
616 | begin = rangeEnd; |
617 | rangeEnd = advanceUntilNonAdjacent(view, beg: begin, end, pred: AdjacentSubRangeFinder<QSortPolicy::Material>::adjacentSubRange); |
618 | } |
619 | } |
620 | |
621 | void sortCommandRange(EntityRenderCommandDataView *view, int begin, int end, const size_t level, |
622 | const std::vector<Qt3DRender::QSortPolicy::SortType> &sortingTypes) |
623 | { |
624 | if (level >= sortingTypes.size()) |
625 | return; |
626 | |
627 | switch (sortingTypes.at(n: level)) { |
628 | case QSortPolicy::StateChangeCost: |
629 | SubRangeSorter<QSortPolicy::StateChangeCost>::sortSubRange(view, begin, end); |
630 | break; |
631 | case QSortPolicy::BackToFront: |
632 | SubRangeSorter<QSortPolicy::BackToFront>::sortSubRange(view, begin, end); |
633 | break; |
634 | case QSortPolicy::Material: |
635 | // Groups all same shader DNA together |
636 | SubRangeSorter<QSortPolicy::Material>::sortSubRange(view, begin, end); |
637 | // Group all same material together (same parameters most likely) |
638 | sortByMaterial(view, begin, end); |
639 | break; |
640 | case QSortPolicy::FrontToBack: |
641 | SubRangeSorter<QSortPolicy::FrontToBack>::sortSubRange(view, begin, end); |
642 | break; |
643 | case QSortPolicy::Texture: |
644 | SubRangeSorter<QSortPolicy::Texture>::sortSubRange(view, begin, end); |
645 | break; |
646 | case QSortPolicy::Uniform: |
647 | break; |
648 | default: |
649 | Q_UNREACHABLE(); |
650 | } |
651 | |
652 | // For all sub ranges of adjacent item for sortType[i] |
653 | // Perform filtering with sortType[i + 1] |
654 | int rangeEnd = findSubRange(view, begin, end, sortType: sortingTypes.at(n: level)); |
655 | while (begin != end) { |
656 | sortCommandRange(view, begin, end: rangeEnd, level: level + 1, sortingTypes); |
657 | begin = rangeEnd; |
658 | rangeEnd = findSubRange(view, begin, end, sortType: sortingTypes.at(n: level)); |
659 | } |
660 | } |
661 | |
662 | } // anonymous |
663 | |
664 | void RenderView::sort() |
665 | { |
666 | assert(m_renderCommandDataView); |
667 | // Compares the bitsetKey of the RenderCommands |
668 | // Key[Depth | StateCost | Shader] |
669 | sortCommandRange(view: m_renderCommandDataView.data(), begin: 0, end: int(m_renderCommandDataView->size()), level: 0, sortingTypes: m_sortingTypes); |
670 | |
671 | // For RenderCommand with the same shader |
672 | // We compute the adjacent change cost |
673 | |
674 | // Only perform uniform minimization if we explicitly asked for it |
675 | if (!Qt3DCore::contains(destination: m_sortingTypes, element: QSortPolicy::Uniform)) |
676 | return; |
677 | |
678 | // Minimize uniform changes |
679 | size_t i = 0; |
680 | std::vector<RenderCommand> &commands = m_renderCommandDataView->data.commands; |
681 | const std::vector<size_t> &indices = m_renderCommandDataView->indices; |
682 | const size_t commandSize = indices.size(); |
683 | |
684 | while (i < commandSize) { |
685 | size_t j = i; |
686 | |
687 | // Advance while commands share the same shader |
688 | while (i < commandSize && |
689 | commands[indices[j]].m_rhiShader == commands[indices[i]].m_rhiShader) |
690 | ++i; |
691 | |
692 | if (i - j > 0) { // Several commands have the same shader, so we minimize uniform changes |
693 | PackUniformHash cachedUniforms = commands[indices[j++]].m_parameterPack.uniforms(); |
694 | |
695 | while (j < i) { |
696 | // We need the reference here as we are modifying the original container |
697 | // not the copy |
698 | PackUniformHash &uniforms = commands[indices[j]].m_parameterPack.m_uniforms; |
699 | |
700 | for (size_t u = 0; u < uniforms.keys.size();) { |
701 | // We are comparing the values: |
702 | // - raw uniform values |
703 | // - the texture Node id if the uniform represents a texture |
704 | // since all textures are assigned texture units before the RenderCommands |
705 | // sharing the same material (shader) are rendered, we can't have the case |
706 | // where two uniforms, referencing the same texture eventually have 2 different |
707 | // texture unit values |
708 | const int uniformNameId = uniforms.keys.at(n: u); |
709 | const UniformValue &refValue = cachedUniforms.value(key: uniformNameId); |
710 | const UniformValue &newValue = uniforms.values.at(n: u); |
711 | if (newValue == refValue) { |
712 | uniforms.erase(idx: int(u)); |
713 | } else { |
714 | // Record updated value so that subsequent comparison |
715 | // for the next command will be made againts latest |
716 | // uniform value |
717 | cachedUniforms.insert(key: uniformNameId, value: newValue); |
718 | ++u; |
719 | } |
720 | } |
721 | ++j; |
722 | } |
723 | } |
724 | } |
725 | } |
726 | |
727 | void RenderView::setRenderer(Renderer *renderer) |
728 | { |
729 | m_renderer = renderer; |
730 | m_manager = renderer->nodeManagers(); |
731 | } |
732 | |
733 | RenderStateSet *RenderView::getOrCreateStateSet() |
734 | { |
735 | if (!m_stateSet) |
736 | m_stateSet.reset(other: new RenderStateSet()); |
737 | return m_stateSet.data(); |
738 | } |
739 | |
740 | void RenderView::addClearBuffers(const ClearBuffers *cb) |
741 | { |
742 | QClearBuffers::BufferTypeFlags type = cb->type(); |
743 | |
744 | if (type & QClearBuffers::StencilBuffer) { |
745 | m_clearStencilValue = cb->clearStencilValue(); |
746 | m_clearBuffer |= QClearBuffers::StencilBuffer; |
747 | } |
748 | if (type & QClearBuffers::DepthBuffer) { |
749 | m_clearDepthValue = cb->clearDepthValue(); |
750 | m_clearBuffer |= QClearBuffers::DepthBuffer; |
751 | } |
752 | // keep track of global ClearColor (if set) and collect all DrawBuffer-specific |
753 | // ClearColors |
754 | if (type & QClearBuffers::ColorBuffer) { |
755 | ClearBufferInfo clearBufferInfo; |
756 | clearBufferInfo.clearColor = cb->clearColor(); |
757 | |
758 | if (cb->clearsAllColorBuffers()) { |
759 | m_globalClearColorBuffer = clearBufferInfo; |
760 | m_clearBuffer |= QClearBuffers::ColorBuffer; |
761 | } else { |
762 | if (cb->bufferId()) { |
763 | const RenderTargetOutput *targetOutput = |
764 | m_manager->attachmentManager()->lookupResource(id: cb->bufferId()); |
765 | if (targetOutput) { |
766 | clearBufferInfo.attchmentPoint = targetOutput->point(); |
767 | // Note: a job is later performed to find the drawIndex from the buffer |
768 | // attachment point using the AttachmentPack |
769 | m_specificClearColorBuffers.push_back(x: clearBufferInfo); |
770 | } |
771 | } |
772 | } |
773 | } |
774 | } |
775 | |
776 | // If we are there, we know that entity had a GeometryRenderer + Material |
777 | EntityRenderCommandData RenderView::buildDrawRenderCommands(const Entity **entities, |
778 | int offset, int count) const |
779 | { |
780 | EntityRenderCommandData commands; |
781 | |
782 | commands.reserve(size: count); |
783 | |
784 | for (int i = 0; i < count; ++i) { |
785 | const int idx = offset + i; |
786 | const Entity *entity = entities[idx]; |
787 | GeometryRenderer *geometryRenderer = nullptr; |
788 | HGeometryRenderer geometryRendererHandle = entity->componentHandle<GeometryRenderer>(); |
789 | |
790 | // There is a geometry renderer with geometry |
791 | if ((geometryRenderer = m_manager->geometryRendererManager()->data(handle: geometryRendererHandle)) |
792 | != nullptr |
793 | && geometryRenderer->isEnabled() && !geometryRenderer->geometryId().isNull()) { |
794 | |
795 | const Qt3DCore::QNodeId materialComponentId = entity->componentUuid<Material>(); |
796 | const HMaterial materialHandle = entity->componentHandle<Material>(); |
797 | const std::vector<RenderPassParameterData> renderPassData = |
798 | m_parameters.value(key: materialComponentId); |
799 | |
800 | HGeometry geometryHandle = |
801 | m_manager->geometryManager()->lookupHandle(id: geometryRenderer->geometryId()); |
802 | Geometry *geometry = m_manager->geometryManager()->data(handle: geometryHandle); |
803 | |
804 | if (geometry == nullptr) |
805 | continue; |
806 | |
807 | // 1 RenderCommand per RenderPass pass on an Entity with a Mesh |
808 | for (const RenderPassParameterData &passData : renderPassData) { |
809 | // Add the RenderPass Parameters |
810 | RenderCommand command = {}; |
811 | command.m_geometryRenderer = geometryRendererHandle; |
812 | command.m_geometry = geometryHandle; |
813 | |
814 | command.m_material = materialHandle; |
815 | // For RenderPass based states we use the globally set RenderState |
816 | // if no renderstates are defined as part of the pass. That means: |
817 | // RenderPass { renderStates: [] } will use the states defined by |
818 | // StateSet in the FrameGraph |
819 | RenderPass *pass = passData.pass; |
820 | if (pass->hasRenderStates()) { |
821 | command.m_stateSet = RenderStateSetPtr::create(); |
822 | addStatesToRenderStateSet(stateSet: command.m_stateSet.data(), stateIds: pass->renderStates(), |
823 | manager: m_manager->renderStateManager()); |
824 | if (m_stateSet != nullptr) |
825 | command.m_stateSet->merge(other: m_stateSet.get()); |
826 | command.m_changeCost = |
827 | m_renderer->defaultRenderState()->changeCost(previousState: command.m_stateSet.data()); |
828 | } |
829 | command.m_shaderId = pass->shaderProgram(); |
830 | |
831 | // At submission time, shaderId is used to retrieve a RHIShader |
832 | // No point in continuing if shaderId is null |
833 | if (!command.m_shaderId) |
834 | continue; |
835 | |
836 | // We try to resolve the m_rhiShader here. If the shader exist, |
837 | // it won't be null and will allow us to full process the |
838 | // command over a single frame. Otherwise, the shader will be |
839 | // loaded at the next submission time and the command will only |
840 | // be fully valid on the next frame. Additionally, that way, if |
841 | // a commands keeps being rebuilt, frame after frame, it will |
842 | // still be visible on screen as long as the shader exists |
843 | RHIShaderManager *rhiShaderManager = m_renderer->rhiResourceManagers()->rhiShaderManager(); |
844 | command.m_rhiShader = rhiShaderManager->lookupResource(shaderId: command.m_shaderId); |
845 | |
846 | { // Scoped to show extent |
847 | |
848 | // Build of list of Attribute Layout information which |
849 | // allows use to compare the layout of geometries against |
850 | // one another. |
851 | // { name, classification, stride, offset, divisor } |
852 | |
853 | // Update the draw command with what's going to be needed for the drawing |
854 | int primitiveCount = geometryRenderer->vertexCount(); |
855 | int estimatedCount = 0; |
856 | Attribute *indexAttribute = nullptr; |
857 | Attribute *indirectAttribute = nullptr; |
858 | |
859 | const QList<Qt3DCore::QNodeId> attributeIds = geometry->attributes(); |
860 | command.m_attributeInfo.clear(); |
861 | command.m_attributeInfo.reserve(n: attributeIds.size()); |
862 | for (Qt3DCore::QNodeId attributeId : attributeIds) { |
863 | using namespace Qt3DCore; |
864 | |
865 | Attribute *attribute = |
866 | m_manager->attributeManager()->lookupResource(id: attributeId); |
867 | switch (attribute->attributeType()) { |
868 | case QAttribute::IndexAttribute: |
869 | indexAttribute = attribute; |
870 | break; |
871 | case QAttribute::DrawIndirectAttribute: |
872 | indirectAttribute = attribute; |
873 | break; |
874 | case QAttribute::VertexAttribute: |
875 | estimatedCount = std::max(a: int(attribute->count()), b: estimatedCount); |
876 | break; |
877 | default: |
878 | Q_UNREACHABLE(); |
879 | break; |
880 | } |
881 | |
882 | if (attribute->attributeType() == QAttribute::VertexAttribute) { |
883 | command.m_attributeInfo.push_back(x: { .nameId: attribute->nameId(), |
884 | .classification: attribute->divisor() == 0 ? QRhiVertexInputBinding::PerVertex : QRhiVertexInputBinding::PerInstance, |
885 | .stride: size_t(attribute->byteStride()), |
886 | .offset: size_t(attribute->byteOffset()), |
887 | .divisor: size_t(attribute->divisor()) }); |
888 | } |
889 | } |
890 | |
891 | // Sort attributes by name so that same attributes added |
892 | // in different order would still result in the same geometeyLayout key |
893 | std::sort(first: command.m_attributeInfo.begin(), |
894 | last: command.m_attributeInfo.end(), |
895 | comp: [] (const AttributeInfo &a, const AttributeInfo &b) { |
896 | return a.nameId < b.nameId; |
897 | }); |
898 | |
899 | command.m_drawIndexed = (indexAttribute != nullptr); |
900 | command.m_drawIndirect = (indirectAttribute != nullptr); |
901 | command.indexAttribute = nullptr; |
902 | command.indexBuffer = nullptr; |
903 | command.pipeline = std::monostate{}; |
904 | |
905 | // Update the draw command with all the information required for the drawing |
906 | if (command.m_drawIndexed) { |
907 | command.m_indexAttributeDataType = indexAttribute->vertexBaseType(); |
908 | command.m_indexAttributeByteOffset = indexAttribute->byteOffset() |
909 | + geometryRenderer->indexBufferByteOffset(); |
910 | } |
911 | |
912 | // Note: we only care about the primitiveCount when using direct draw calls |
913 | // For indirect draw calls it is assumed the buffer was properly set already |
914 | if (command.m_drawIndirect) { |
915 | command.m_indirectAttributeByteOffset = indirectAttribute->byteOffset(); |
916 | command.m_indirectDrawBuffer = m_manager->bufferManager()->lookupHandle( |
917 | id: indirectAttribute->bufferId()); |
918 | } else { |
919 | // Use the count specified by the GeometryRender |
920 | // If not specify use the indexAttribute count if present |
921 | // Otherwise tries to use the count from the attribute with the highest |
922 | // count |
923 | if (primitiveCount == 0) { |
924 | if (indexAttribute) |
925 | primitiveCount = indexAttribute->count(); |
926 | else |
927 | primitiveCount = estimatedCount; |
928 | } |
929 | } |
930 | |
931 | command.m_primitiveCount = primitiveCount; |
932 | command.m_primitiveType = geometryRenderer->primitiveType(); |
933 | command.m_primitiveRestartEnabled = geometryRenderer->primitiveRestartEnabled(); |
934 | command.m_restartIndexValue = geometryRenderer->restartIndexValue(); |
935 | command.m_firstInstance = geometryRenderer->firstInstance(); |
936 | command.m_instanceCount = geometryRenderer->instanceCount(); |
937 | command.m_firstVertex = geometryRenderer->firstVertex(); |
938 | command.m_indexOffset = geometryRenderer->indexOffset(); |
939 | command.m_verticesPerPatch = geometryRenderer->verticesPerPatch(); |
940 | } // scope |
941 | |
942 | commands.push_back(e: entity, c: std::move(command), p: passData); |
943 | } |
944 | } |
945 | } |
946 | |
947 | return commands; |
948 | } |
949 | |
950 | EntityRenderCommandData RenderView::buildComputeRenderCommands(const Entity **entities, |
951 | int offset, int count) const |
952 | { |
953 | // If the RenderView contains only a ComputeDispatch then it cares about |
954 | // A ComputeDispatch is also implicitely a NoDraw operation |
955 | // enabled flag |
956 | // layer component |
957 | // material/effect/technique/parameters/filters/ |
958 | EntityRenderCommandData commands; |
959 | |
960 | commands.reserve(size: count); |
961 | |
962 | for (int i = 0; i < count; ++i) { |
963 | const int idx = offset + i; |
964 | const Entity *entity = entities[idx]; |
965 | ComputeCommand *computeJob = nullptr; |
966 | HComputeCommand computeCommandHandle = entity->componentHandle<ComputeCommand>(); |
967 | if ((computeJob = nodeManagers()->computeJobManager()->data(handle: computeCommandHandle)) |
968 | != nullptr |
969 | && computeJob->isEnabled()) { |
970 | |
971 | const Qt3DCore::QNodeId materialComponentId = entity->componentUuid<Material>(); |
972 | const std::vector<RenderPassParameterData> &renderPassData = |
973 | m_parameters.value(key: materialComponentId); |
974 | |
975 | // 1 RenderCommand per RenderPass pass on an Entity with a Mesh |
976 | for (const RenderPassParameterData &passData : renderPassData) { |
977 | // Add the RenderPass Parameters |
978 | RenderCommand command = {}; |
979 | RenderPass *pass = passData.pass; |
980 | |
981 | if (pass->hasRenderStates()) { |
982 | command.m_stateSet = RenderStateSetPtr::create(); |
983 | addStatesToRenderStateSet(stateSet: command.m_stateSet.data(), stateIds: pass->renderStates(), |
984 | manager: m_manager->renderStateManager()); |
985 | |
986 | // Merge per pass stateset with global stateset |
987 | // so that the local stateset only overrides |
988 | if (m_stateSet != nullptr) |
989 | command.m_stateSet->merge(other: m_stateSet.get()); |
990 | command.m_changeCost = |
991 | m_renderer->defaultRenderState()->changeCost(previousState: command.m_stateSet.data()); |
992 | } |
993 | command.m_shaderId = pass->shaderProgram(); |
994 | |
995 | // At submission time, shaderId is used to retrieve a RHIShader |
996 | // No point in continuing if shaderId is null |
997 | if (!command.m_shaderId) |
998 | continue; |
999 | |
1000 | // We try to resolve the m_rhiShader here. If the shader exist, |
1001 | // it won't be null and will allow us to full process the |
1002 | // command over a single frame. Otherwise, the shader will be |
1003 | // loaded at the next submission time and the command will only |
1004 | // be fully valid on the next frame. Additionally, that way, if |
1005 | // a commands keeps being rebuilt, frame after frame, it will |
1006 | // still be visible on screen as long as the shader exists |
1007 | RHIShaderManager *rhiShaderManager = m_renderer->rhiResourceManagers()->rhiShaderManager(); |
1008 | command.m_rhiShader = rhiShaderManager->lookupResource(shaderId: command.m_shaderId); |
1009 | |
1010 | command.m_computeCommand = computeCommandHandle; |
1011 | command.m_type = RenderCommand::Compute; |
1012 | command.m_workGroups[0] = std::max(a: m_workGroups[0], b: computeJob->x()); |
1013 | command.m_workGroups[1] = std::max(a: m_workGroups[1], b: computeJob->y()); |
1014 | command.m_workGroups[2] = std::max(a: m_workGroups[2], b: computeJob->z()); |
1015 | |
1016 | commands.push_back(e: entity, c: std::move(command), p: passData); |
1017 | } |
1018 | } |
1019 | } |
1020 | |
1021 | return commands; |
1022 | } |
1023 | |
1024 | namespace |
1025 | { |
1026 | void copyNormalMatrix(float(&destination)[12], const float* src) noexcept { |
1027 | destination[0] = src[0 * 3 + 0]; |
1028 | destination[1] = src[0 * 3 + 1]; |
1029 | destination[2] = src[0 * 3 + 2]; |
1030 | |
1031 | destination[4] = src[1 * 3 + 0]; |
1032 | destination[5] = src[1 * 3 + 1]; |
1033 | destination[6] = src[1 * 3 + 2]; |
1034 | |
1035 | destination[8] = src[2 * 3 + 0]; |
1036 | destination[9] = src[2 * 3 + 1]; |
1037 | destination[10] = src[2 * 3 + 2]; |
1038 | } |
1039 | } |
1040 | |
1041 | void RenderView::updateRenderCommand(const EntityRenderCommandDataSubView &subView) |
1042 | { |
1043 | // Update RenderViewUBO (Qt3D standard uniforms) |
1044 | const bool yIsUp = m_renderer->submissionContext()->rhi()->isYUpInNDC(); |
1045 | |
1046 | const Matrix4x4 clipCorrectionMatrix = Matrix4x4(m_renderer->submissionContext()->rhi()->clipSpaceCorrMatrix()); |
1047 | const Matrix4x4 unCorrectedProjectionMatrix = getProjectionMatrix(lens: m_renderCameraLens); |
1048 | const Matrix4x4 projectionMatrix = clipCorrectionMatrix * unCorrectedProjectionMatrix; |
1049 | const Matrix4x4 inverseViewMatrix = m_viewMatrix.inverted(); |
1050 | const Matrix4x4 inversedProjectionMatrix = projectionMatrix.inverted(); |
1051 | const Matrix4x4 viewProjectionMatrix = (projectionMatrix * m_viewMatrix); |
1052 | const Matrix4x4 inversedViewProjectionMatrix = viewProjectionMatrix.inverted(); |
1053 | { |
1054 | memcpy(dest: &m_renderViewUBO.viewMatrix, src: &m_viewMatrix, n: sizeof(Matrix4x4)); |
1055 | memcpy(dest: &m_renderViewUBO.projectionMatrix, src: &projectionMatrix, n: sizeof(Matrix4x4)); |
1056 | memcpy(dest: &m_renderViewUBO.clipCorrectionMatrix, src: &clipCorrectionMatrix, n: sizeof(Matrix4x4)); |
1057 | memcpy(dest: &m_renderViewUBO.uncorrectedProjectionMatrix, src: &unCorrectedProjectionMatrix, n: sizeof(Matrix4x4)); |
1058 | memcpy(dest: &m_renderViewUBO.viewProjectionMatrix, src: &viewProjectionMatrix, n: sizeof(Matrix4x4)); |
1059 | memcpy(dest: &m_renderViewUBO.inverseViewMatrix, src: &inverseViewMatrix, n: sizeof(Matrix4x4)); |
1060 | memcpy(dest: &m_renderViewUBO.inverseProjectionMatrix, src: &inversedProjectionMatrix, |
1061 | n: sizeof(Matrix4x4)); |
1062 | memcpy(dest: &m_renderViewUBO.inverseViewProjectionMatrix, src: &inversedViewProjectionMatrix, |
1063 | n: sizeof(Matrix4x4)); |
1064 | { |
1065 | QMatrix4x4 viewportMatrix; |
1066 | // TO DO: Implement on Matrix4x4 |
1067 | viewportMatrix.viewport(rect: resolveViewport(fractionalViewport: m_viewport, surfaceSize: m_surfaceSize)); |
1068 | Matrix4x4 vpMatrix(viewportMatrix); |
1069 | Matrix4x4 invVpMatrix = vpMatrix.inverted(); |
1070 | memcpy(dest: &m_renderViewUBO.viewportMatrix, src: &vpMatrix, n: sizeof(Matrix4x4)); |
1071 | memcpy(dest: &m_renderViewUBO.inverseViewportMatrix, src: &invVpMatrix, n: sizeof(Matrix4x4)); |
1072 | } |
1073 | memcpy(dest: &m_renderViewUBO.textureTransformMatrix, src: m_renderer->textureTransform(), |
1074 | n: sizeof(float) * 4); |
1075 | |
1076 | memcpy(dest: &m_renderViewUBO.eyePosition, src: &m_eyePos, n: sizeof(float) * 3); |
1077 | const float ratio = |
1078 | float(m_surfaceSize.width()) / std::max(a: 1.f, b: float(m_surfaceSize.height())); |
1079 | memcpy(dest: &m_renderViewUBO.aspectRatio, src: &ratio, n: sizeof(float)); |
1080 | memcpy(dest: &m_renderViewUBO.gamma, src: &m_gamma, n: sizeof(float)); |
1081 | const float exposure = |
1082 | m_renderCameraLens ? m_renderCameraLens->exposure() : 0.0f; |
1083 | memcpy(dest: &m_renderViewUBO.exposure, src: &exposure, n: sizeof(float)); |
1084 | const float timeValue = float(m_renderer->time() / 1000000000.0f); |
1085 | memcpy(dest: &m_renderViewUBO.time, src: &timeValue, n: sizeof(float)); |
1086 | |
1087 | const float yUpNDC = yIsUp ? 1.0f : 0.0f; |
1088 | const float yUpFBO = m_renderer->submissionContext()->rhi()->isYUpInFramebuffer() ? 1.0f : 0.0f; |
1089 | memcpy(dest: &m_renderViewUBO.yUpInNDC, src: &yUpNDC, n: sizeof(float)); |
1090 | memcpy(dest: &m_renderViewUBO.yUpInFBO, src: &yUpFBO, n: sizeof(float)); |
1091 | } |
1092 | |
1093 | subView.forEach(func: [&] (const Entity *entity, |
1094 | const RenderPassParameterData &passData, |
1095 | RenderCommand &command) { |
1096 | |
1097 | // Pick which lights to take in to account. |
1098 | // For now decide based on the distance by taking the MAX_LIGHTS closest lights. |
1099 | // Replace with more sophisticated mechanisms later. |
1100 | // Copy vector so that we can sort it concurrently and we only want to sort the one for the |
1101 | // current command |
1102 | std::vector<LightSource> lightSources; |
1103 | EnvironmentLight *environmentLight = nullptr; |
1104 | |
1105 | if (command.m_type == RenderCommand::Draw) { |
1106 | // Project the camera-to-object-center vector onto the camera |
1107 | // view vector. This gives a depth value suitable as the key |
1108 | // for BackToFront sorting. |
1109 | command.m_depth = Vector3D::dotProduct( |
1110 | a: entity->worldBoundingVolume()->center() - m_eyePos, b: m_eyeViewDir); |
1111 | |
1112 | auto geometryRenderer = m_manager->geometryRendererManager()->data(handle: command.m_geometryRenderer); |
1113 | if (geometryRenderer && !qFuzzyCompare(p1: geometryRenderer->sortIndex(), p2: -1.f)) |
1114 | command.m_depth = geometryRenderer->sortIndex(); |
1115 | |
1116 | environmentLight = m_environmentLight; |
1117 | lightSources = m_lightSources; |
1118 | |
1119 | if (lightSources.size() > 1) { |
1120 | const Vector3D entityCenter = entity->worldBoundingVolume()->center(); |
1121 | std::sort(first: lightSources.begin(), last: lightSources.end(), |
1122 | comp: [&](const LightSource &a, const LightSource &b) { |
1123 | const float distA = entityCenter.distanceToPoint( |
1124 | point: a.entity->worldBoundingVolume()->center()); |
1125 | const float distB = entityCenter.distanceToPoint( |
1126 | point: b.entity->worldBoundingVolume()->center()); |
1127 | return distA < distB; |
1128 | }); |
1129 | m_lightSources = {lightSources.begin(), lightSources.begin() + std::min(a: lightSources.size(), b: size_t(MAX_LIGHTS)) }; |
1130 | } |
1131 | } else { // Compute |
1132 | // Note: if frameCount has reached 0 in the previous frame, isEnabled |
1133 | // would be false |
1134 | ComputeCommand *computeJob = |
1135 | m_manager->computeJobManager()->data(handle: command.m_computeCommand); |
1136 | if (computeJob->runType() == QComputeCommand::Manual) |
1137 | computeJob->updateFrameCount(); |
1138 | } |
1139 | |
1140 | ParameterInfoList globalParameters = passData.parameterInfo; |
1141 | // setShaderAndUniforms can initialize a localData |
1142 | // make sure this is cleared before we leave this function |
1143 | |
1144 | setShaderAndUniforms(command: &command, parameters&: globalParameters, entity, activeLightSources: lightSources, environmentLight); |
1145 | |
1146 | // Update CommandUBO (Qt3D standard uniforms) |
1147 | const Matrix4x4 worldTransform = *(entity->worldTransform()); |
1148 | const Matrix4x4 inverseWorldTransform = worldTransform.inverted(); |
1149 | const QMatrix3x3 modelNormalMatrix = convertToQMatrix4x4(v: worldTransform).normalMatrix(); |
1150 | const Matrix4x4 modelViewMatrix = m_viewMatrix * worldTransform; |
1151 | const QMatrix3x3 modelViewNormalMatrix = convertToQMatrix4x4(v: modelViewMatrix).normalMatrix(); |
1152 | const Matrix4x4 inverseModelViewMatrix = modelViewMatrix.inverted(); |
1153 | const Matrix4x4 mvp = projectionMatrix * modelViewMatrix; |
1154 | const Matrix4x4 inverseModelViewProjection = mvp.inverted(); |
1155 | { |
1156 | memcpy(dest: &command.m_commandUBO.modelMatrix, src: &worldTransform, n: sizeof(Matrix4x4)); |
1157 | memcpy(dest: &command.m_commandUBO.inverseModelMatrix, src: &inverseWorldTransform, |
1158 | n: sizeof(Matrix4x4)); |
1159 | memcpy(dest: &command.m_commandUBO.modelViewMatrix, src: &modelViewMatrix, n: sizeof(Matrix4x4)); |
1160 | copyNormalMatrix(destination&: command.m_commandUBO.modelNormalMatrix, src: modelNormalMatrix.constData()); |
1161 | memcpy(dest: &command.m_commandUBO.inverseModelViewMatrix, src: &inverseModelViewMatrix, |
1162 | n: sizeof(Matrix4x4)); |
1163 | memcpy(dest: &command.m_commandUBO.mvp, src: &mvp, n: sizeof(Matrix4x4)); |
1164 | memcpy(dest: &command.m_commandUBO.inverseModelViewProjectionMatrix, |
1165 | src: &inverseModelViewProjection, n: sizeof(Matrix4x4)); |
1166 | copyNormalMatrix(destination&: command.m_commandUBO.modelViewNormalMatrix, src: modelViewNormalMatrix.constData()); |
1167 | } |
1168 | }); |
1169 | } |
1170 | |
1171 | void RenderView::updateMatrices() |
1172 | { |
1173 | if (m_renderCameraNode && m_renderCameraLens |
1174 | && m_renderCameraLens->isEnabled()) { |
1175 | const Matrix4x4 cameraWorld = *(m_renderCameraNode->worldTransform()); |
1176 | setViewMatrix(m_renderCameraLens->viewMatrix(worldTransform: cameraWorld)); |
1177 | |
1178 | setViewProjectionMatrix(m_renderCameraLens->projection() * viewMatrix()); |
1179 | // To get the eyePosition of the camera, we need to use the inverse of the |
1180 | // camera's worldTransform matrix. |
1181 | const Matrix4x4 inverseWorldTransform = viewMatrix().inverted(); |
1182 | const Vector3D eyePosition(inverseWorldTransform.column(index: 3)); |
1183 | setEyePosition(eyePosition); |
1184 | |
1185 | // Get the viewing direction of the camera. Use the normal matrix to |
1186 | // ensure non-uniform scale works too. |
1187 | const QMatrix3x3 normalMat = convertToQMatrix4x4(v: m_viewMatrix).normalMatrix(); |
1188 | // dir = normalize(QVector3D(0, 0, -1) * normalMat) |
1189 | setEyeViewDirection( |
1190 | Vector3D(-normalMat(2, 0), -normalMat(2, 1), -normalMat(2, 2)).normalized()); |
1191 | } |
1192 | } |
1193 | |
1194 | void RenderView::setUniformValue(ShaderParameterPack &uniformPack, int nameId, |
1195 | const UniformValue &value) const |
1196 | { |
1197 | // At this point a uniform value can only be a scalar type |
1198 | // or a Qt3DCore::QNodeId corresponding to a Texture or Image |
1199 | // ShaderData/Buffers would be handled as UBO/SSBO and would therefore |
1200 | // not be in the default uniform block |
1201 | if (value.valueType() == UniformValue::NodeId) { |
1202 | const Qt3DCore::QNodeId *nodeIds = value.constData<Qt3DCore::QNodeId>(); |
1203 | |
1204 | const int uniformArraySize = value.byteSize() / sizeof(Qt3DCore::QNodeId); |
1205 | UniformValue::ValueType resourceType = UniformValue::TextureValue; |
1206 | |
1207 | for (int i = 0; i < uniformArraySize; ++i) { |
1208 | const Qt3DCore::QNodeId resourceId = nodeIds[i]; |
1209 | |
1210 | const Texture *tex = m_manager->textureManager()->lookupResource(id: resourceId); |
1211 | if (tex != nullptr) { |
1212 | uniformPack.setTexture(glslNameId: nameId, uniformArrayIndex: i, id: resourceId); |
1213 | } else { |
1214 | const ShaderImage *img = |
1215 | m_manager->shaderImageManager()->lookupResource(id: resourceId); |
1216 | if (img != nullptr) { |
1217 | resourceType = UniformValue::ShaderImageValue; |
1218 | uniformPack.setImage(glslNameId: nameId, uniformArrayIndex: i, id: resourceId); |
1219 | } |
1220 | } |
1221 | } |
1222 | |
1223 | // This uniform will be overridden in SubmissionContext::setParameters |
1224 | // and -1 values will be replaced by valid Texture or Image units |
1225 | UniformValue uniformValue(uniformArraySize * sizeof(int), resourceType); |
1226 | std::fill(first: uniformValue.data<int>(), last: uniformValue.data<int>() + uniformArraySize, value: -1); |
1227 | uniformPack.setUniform(glslNameId: nameId, val: uniformValue); |
1228 | } else { |
1229 | uniformPack.setUniform(glslNameId: nameId, val: value); |
1230 | } |
1231 | } |
1232 | |
1233 | void RenderView::setUniformBlockValue(ShaderParameterPack &uniformPack, const RHIShader *shader, |
1234 | const ShaderUniformBlock &block, |
1235 | const UniformValue &value) const |
1236 | { |
1237 | Q_UNUSED(shader); |
1238 | |
1239 | if (value.valueType() == UniformValue::NodeId) { |
1240 | |
1241 | Buffer *buffer = nullptr; |
1242 | if ((buffer = m_manager->bufferManager()->lookupResource( |
1243 | id: *value.constData<Qt3DCore::QNodeId>())) |
1244 | != nullptr) { |
1245 | BlockToUBO uniformBlockUBO; |
1246 | uniformBlockUBO.m_blockIndex = block.m_index; |
1247 | uniformBlockUBO.m_bindingIndex = block.m_binding; |
1248 | uniformBlockUBO.m_bufferID = buffer->peerId(); |
1249 | uniformBlockUBO.m_needsUpdate = false; |
1250 | uniformPack.setUniformBuffer(std::move(uniformBlockUBO)); |
1251 | // Buffer update to GL buffer will be done at render time |
1252 | } |
1253 | } |
1254 | } |
1255 | |
1256 | void RenderView::setShaderStorageValue(ShaderParameterPack &uniformPack, const RHIShader *shader, |
1257 | const ShaderStorageBlock &block, |
1258 | const UniformValue &value) const |
1259 | { |
1260 | Q_UNUSED(shader); |
1261 | if (value.valueType() == UniformValue::NodeId) { |
1262 | Buffer *buffer = nullptr; |
1263 | if ((buffer = m_manager->bufferManager()->lookupResource( |
1264 | id: *value.constData<Qt3DCore::QNodeId>())) |
1265 | != nullptr) { |
1266 | BlockToSSBO shaderStorageBlock; |
1267 | shaderStorageBlock.m_blockIndex = block.m_index; |
1268 | shaderStorageBlock.m_bufferID = buffer->peerId(); |
1269 | shaderStorageBlock.m_bindingIndex = block.m_binding; |
1270 | uniformPack.setShaderStorageBuffer(shaderStorageBlock); |
1271 | // Buffer update to RHI buffer will be done at render time |
1272 | } |
1273 | } |
1274 | } |
1275 | |
1276 | void RenderView::setShaderDataValue(ShaderParameterPack &uniformPack, |
1277 | const ShaderUniformBlock &block, |
1278 | const Qt3DCore::QNodeId &shaderDataId) const |
1279 | { |
1280 | if (block.m_binding >= 0) { |
1281 | ShaderDataForUBO shaderDataForUBO; |
1282 | shaderDataForUBO.m_shaderDataID = shaderDataId; |
1283 | shaderDataForUBO.m_bindingIndex = block.m_binding; |
1284 | uniformPack.setShaderDataForUBO(shaderDataForUBO); |
1285 | } |
1286 | } |
1287 | |
1288 | void RenderView::setDefaultUniformBlockShaderDataValue(ShaderParameterPack &uniformPack, |
1289 | const RHIShader *shader, |
1290 | ShaderData *shaderData, |
1291 | const QString &structName) const |
1292 | { |
1293 | UniformBlockValueBuilder builder(shader->uniformsNamesIds(), |
1294 | m_manager->shaderDataManager(), |
1295 | m_manager->textureManager(), |
1296 | m_viewMatrix); |
1297 | |
1298 | // Build name-value map for the block |
1299 | builder.buildActiveUniformNameValueMapStructHelper(rShaderData: shaderData, blockName: structName); |
1300 | // Set uniform values for each entrie of the block name-value map |
1301 | QHash<int, QVariant>::const_iterator activeValuesIt = |
1302 | builder.activeUniformNamesToValue.constBegin(); |
1303 | const QHash<int, QVariant>::const_iterator activeValuesEnd = |
1304 | builder.activeUniformNamesToValue.constEnd(); |
1305 | |
1306 | // TO DO: Make the ShaderData store UniformValue |
1307 | while (activeValuesIt != activeValuesEnd) { |
1308 | setUniformValue(uniformPack, nameId: activeValuesIt.key(), |
1309 | value: UniformValue::fromVariant(variant: activeValuesIt.value())); |
1310 | ++activeValuesIt; |
1311 | } |
1312 | } |
1313 | |
1314 | void RenderView::applyParameter(const Parameter *param, RenderCommand *command, |
1315 | const RHIShader *shader) const noexcept |
1316 | { |
1317 | const int nameId = param->nameId(); |
1318 | const UniformValue &uniformValue = param->uniformValue(); |
1319 | const RHIShader::ParameterKind parameterKind = shader->categorizeVariable(nameId); |
1320 | |
1321 | switch (parameterKind) { |
1322 | case RHIShader::Uniform: { |
1323 | setUniformValue(uniformPack&: command->m_parameterPack, nameId, value: uniformValue); |
1324 | break; |
1325 | } |
1326 | case RHIShader::UBO: { |
1327 | setUniformBlockValue(uniformPack&: command->m_parameterPack, shader, |
1328 | block: shader->uniformBlockForBlockNameId(blockIndex: nameId), value: uniformValue); |
1329 | break; |
1330 | } |
1331 | case RHIShader::SSBO: { |
1332 | setShaderStorageValue(uniformPack&: command->m_parameterPack, shader, |
1333 | block: shader->storageBlockForBlockNameId(blockNameId: nameId), value: uniformValue); |
1334 | break; |
1335 | } |
1336 | case RHIShader::Struct: { |
1337 | // Structs will have to be converted to UBOs later on |
1338 | ShaderData *shaderData = nullptr; |
1339 | |
1340 | if (uniformValue.valueType() == UniformValue::NodeId |
1341 | && (shaderData = m_manager->shaderDataManager()->lookupResource( |
1342 | id: *uniformValue.constData<Qt3DCore::QNodeId>())) |
1343 | != nullptr) { |
1344 | // We need to find the Block associated with the uniform name |
1345 | setShaderDataValue(uniformPack&: command->m_parameterPack, |
1346 | block: shader->uniformBlockForInstanceNameId(instanceNameId: nameId), shaderDataId: shaderData->peerId()); |
1347 | } |
1348 | break; |
1349 | } |
1350 | } |
1351 | } |
1352 | |
1353 | void RenderView::setShaderAndUniforms(RenderCommand *command, ParameterInfoList ¶meters, |
1354 | const Entity *entity, |
1355 | const std::vector<LightSource> &activeLightSources, |
1356 | EnvironmentLight *environmentLight) const |
1357 | { |
1358 | Q_UNUSED(entity); |
1359 | |
1360 | // The VAO Handle is set directly in the renderer thread so as to avoid having to use a mutex |
1361 | // here Set shader, technique, and effect by basically doing : |
1362 | // ShaderProgramManager[MaterialManager[frontentEntity->id()]->Effect->Techniques[TechniqueFilter->name]->RenderPasses[RenderPassFilter->name]]; |
1363 | // The Renderer knows that if one of those is null, a default material / technique / effect as |
1364 | // to be used |
1365 | |
1366 | // Find all RenderPasses (in order) matching values set in the RenderPassFilter |
1367 | // Get list of parameters for the Material, Effect, and Technique |
1368 | // For each ParameterBinder in the RenderPass -> create a QUniformPack |
1369 | // Once that works, improve that to try and minimize QUniformPack updates |
1370 | |
1371 | RHIShader *shader = command->m_rhiShader; |
1372 | if (shader == nullptr || !shader->isLoaded()) |
1373 | return; |
1374 | |
1375 | // If we have already build the uniforms previously, we should |
1376 | // only update values of uniforms that have changed |
1377 | // If parameters add been added/removed, the command would have been rebuild |
1378 | // and the parameter pack would be empty |
1379 | const bool updateUniformsOnly = command->m_parameterPack.submissionUniformIndices().size() > 0; |
1380 | |
1381 | if (!updateUniformsOnly) { |
1382 | // Builds the QUniformPack, sets shader standard uniforms and store attributes name / glname |
1383 | // bindings If a parameter is defined and not found in the bindings it is assumed to be a |
1384 | // binding of Uniform type with the glsl name equals to the parameter name |
1385 | |
1386 | // Set fragData Name and index |
1387 | // Later on we might want to relink the shader if attachments have changed |
1388 | // But for now we set them once and for all |
1389 | if (!m_renderTarget.isNull() && !shader->isLoaded()) { |
1390 | QHash<QString, int> fragOutputs; |
1391 | const auto atts = m_attachmentPack.attachments(); |
1392 | for (const Attachment &att : atts) { |
1393 | if (att.m_point <= QRenderTargetOutput::Color15) |
1394 | fragOutputs.insert(key: att.m_name, value: att.m_point); |
1395 | } |
1396 | // Set frag outputs in the shaders if hash not empty |
1397 | if (!fragOutputs.isEmpty()) |
1398 | shader->setFragOutputs(fragOutputs); |
1399 | } |
1400 | // Set default attributes |
1401 | command->m_activeAttributes = shader->attributeNamesIds(); |
1402 | |
1403 | // At this point we know whether the command is a valid draw command or not |
1404 | // We still need to process the uniforms as the command could be a compute command |
1405 | command->m_isValid = (!command->m_activeAttributes.empty()) || (command->m_type == RenderCommand::CommandType::Compute); |
1406 | } |
1407 | |
1408 | if (shader->hasActiveVariables()) { |
1409 | // Unlike the GL engine, the standard uniforms are set a bit before this function, |
1410 | // in RenderView::updateRenderCommand |
1411 | |
1412 | // Parameters remaining could be |
1413 | // -> uniform scalar / vector |
1414 | // -> uniform struct / arrays |
1415 | // -> uniform block / array (4.3) |
1416 | // -> ssbo block / array (4.3) |
1417 | |
1418 | ParameterInfoList::const_iterator it = parameters.cbegin(); |
1419 | const ParameterInfoList::const_iterator parametersEnd = parameters.cend(); |
1420 | |
1421 | while (it != parametersEnd) { |
1422 | const Parameter *param = m_manager->data<Parameter, ParameterManager>(handle: it->handle); |
1423 | applyParameter(param, command, shader); |
1424 | ++it; |
1425 | } |
1426 | |
1427 | // Lights |
1428 | int lightIdx = 0; |
1429 | for (const LightSource &lightSource : activeLightSources) { |
1430 | if (lightIdx == MAX_LIGHTS) |
1431 | break; |
1432 | const Entity *lightEntity = lightSource.entity; |
1433 | const Matrix4x4 lightWorldTransform = *(lightEntity->worldTransform()); |
1434 | const Vector3D worldPos = lightWorldTransform.map(point: Vector3D(0.0f, 0.0f, 0.0f)); |
1435 | for (Light *light : lightSource.lights) { |
1436 | if (!light->isEnabled()) |
1437 | continue; |
1438 | |
1439 | ShaderData *shaderData = |
1440 | m_manager->shaderDataManager()->lookupResource(id: light->shaderData()); |
1441 | if (!shaderData) |
1442 | continue; |
1443 | |
1444 | if (lightIdx == MAX_LIGHTS) |
1445 | break; |
1446 | |
1447 | // Note: implicit conversion of values to UniformValue |
1448 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: LIGHT_POSITION_NAMES[lightIdx], |
1449 | value: worldPos); |
1450 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: LIGHT_TYPE_NAMES[lightIdx], |
1451 | value: int(QAbstractLight::PointLight)); |
1452 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: LIGHT_COLOR_NAMES[lightIdx], |
1453 | value: Vector3D(1.0f, 1.0f, 1.0f)); |
1454 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: LIGHT_INTENSITY_NAMES[lightIdx], |
1455 | value: 0.5f); |
1456 | // Unrolled |
1457 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: LIGHT_POSITION_UNROLL_NAMES[lightIdx], |
1458 | value: worldPos); |
1459 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: LIGHT_TYPE_UNROLL_NAMES[lightIdx], |
1460 | value: int(QAbstractLight::PointLight)); |
1461 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: LIGHT_COLOR_UNROLL_NAMES[lightIdx], |
1462 | value: Vector3D(1.0f, 1.0f, 1.0f)); |
1463 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: LIGHT_INTENSITY_UNROLL_NAMES[lightIdx], value: 0.5f); |
1464 | |
1465 | // There is no risk in doing that even if multithreaded |
1466 | // since we are sure that a shaderData is unique for a given light |
1467 | // and won't ever be referenced as a Component either |
1468 | const Matrix4x4 *worldTransform = lightEntity->worldTransform(); |
1469 | if (worldTransform) |
1470 | shaderData->updateWorldTransform(worldMatrix: *worldTransform); |
1471 | |
1472 | setDefaultUniformBlockShaderDataValue(uniformPack&: command->m_parameterPack, shader, |
1473 | shaderData, structName: LIGHT_STRUCT_NAMES[lightIdx]); |
1474 | ++lightIdx; |
1475 | } |
1476 | } |
1477 | |
1478 | if (shader->hasUniform(nameId: LIGHT_COUNT_NAME_ID)) |
1479 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: LIGHT_COUNT_NAME_ID, |
1480 | value: UniformValue(qMax(a: (environmentLight ? 0 : 1), b: lightIdx))); |
1481 | |
1482 | // If no active light sources and no environment light, add a default light |
1483 | if (activeLightSources.empty() && !environmentLight) { |
1484 | // Note: implicit conversion of values to UniformValue |
1485 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: LIGHT_POSITION_NAMES[0], |
1486 | value: Vector3D(10.0f, 10.0f, 0.0f)); |
1487 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: LIGHT_TYPE_NAMES[0], |
1488 | value: int(QAbstractLight::PointLight)); |
1489 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: LIGHT_COLOR_NAMES[0], |
1490 | value: Vector3D(1.0f, 1.0f, 1.0f)); |
1491 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: LIGHT_INTENSITY_NAMES[0], value: 0.5f); |
1492 | // Unrolled |
1493 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: LIGHT_POSITION_UNROLL_NAMES[0], |
1494 | value: Vector3D(10.0f, 10.0f, 0.0f)); |
1495 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: LIGHT_TYPE_UNROLL_NAMES[0], |
1496 | value: int(QAbstractLight::PointLight)); |
1497 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: LIGHT_COLOR_UNROLL_NAMES[0], |
1498 | value: Vector3D(1.0f, 1.0f, 1.0f)); |
1499 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: LIGHT_INTENSITY_UNROLL_NAMES[0], value: 0.5f); |
1500 | } |
1501 | |
1502 | // Environment Light |
1503 | int envLightCount = 0; |
1504 | if (environmentLight && environmentLight->isEnabled()) { |
1505 | static const int irradianceStructId = |
1506 | StringToInt::lookupId(str: QLatin1String("envLight_irradiance" )); |
1507 | static const int specularStructId = |
1508 | StringToInt::lookupId(str: QLatin1String("envLight_specular" )); |
1509 | static const int irradianceId = |
1510 | StringToInt::lookupId(str: QLatin1String("envLightIrradiance" )); |
1511 | static const int specularId = |
1512 | StringToInt::lookupId(str: QLatin1String("envLightSpecular" )); |
1513 | ShaderData *shaderData = m_manager->shaderDataManager()->lookupResource( |
1514 | id: environmentLight->shaderData()); |
1515 | if (shaderData) { |
1516 | envLightCount = 1; |
1517 | |
1518 | // ("specularSize", "irradiance", "irradianceSize", "specular") |
1519 | auto irr = |
1520 | shaderData->properties()["irradiance" ].value.value<Qt3DCore::QNodeId>(); |
1521 | auto spec = |
1522 | shaderData->properties()["specular" ].value.value<Qt3DCore::QNodeId>(); |
1523 | |
1524 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: irradianceId, value: irr); |
1525 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: irradianceStructId, value: irr); |
1526 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: specularId, value: spec); |
1527 | setUniformValue(uniformPack&: command->m_parameterPack, nameId: specularStructId, value: spec); |
1528 | } |
1529 | } |
1530 | setUniformValue(uniformPack&: command->m_parameterPack, |
1531 | nameId: StringToInt::lookupId(QStringLiteral("envLightCount" )), value: envLightCount); |
1532 | } |
1533 | } |
1534 | |
1535 | bool RenderView::hasBlitFramebufferInfo() const |
1536 | { |
1537 | return m_hasBlitFramebufferInfo; |
1538 | } |
1539 | |
1540 | void RenderView::setHasBlitFramebufferInfo(bool hasBlitFramebufferInfo) |
1541 | { |
1542 | m_hasBlitFramebufferInfo = hasBlitFramebufferInfo; |
1543 | } |
1544 | |
1545 | BlitFramebufferInfo RenderView::blitFrameBufferInfo() const |
1546 | { |
1547 | return m_blitFrameBufferInfo; |
1548 | } |
1549 | |
1550 | void RenderView::setBlitFrameBufferInfo(const BlitFramebufferInfo &blitFrameBufferInfo) |
1551 | { |
1552 | m_blitFrameBufferInfo = blitFrameBufferInfo; |
1553 | } |
1554 | |
1555 | bool RenderView::isDownloadBuffersEnable() const |
1556 | { |
1557 | return m_isDownloadBuffersEnable; |
1558 | } |
1559 | |
1560 | void RenderView::setIsDownloadBuffersEnable(bool isDownloadBuffersEnable) |
1561 | { |
1562 | m_isDownloadBuffersEnable = isDownloadBuffersEnable; |
1563 | } |
1564 | |
1565 | } // namespace Rhi |
1566 | } // namespace Render |
1567 | } // namespace Qt3DRender |
1568 | |
1569 | QT_END_NAMESPACE |
1570 | |