| 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 |  |