1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "render_preprocessor_system.h"
17 
18 #include <PropertyTools/property_api_impl.inl>
19 #include <PropertyTools/property_macros.h>
20 #include <algorithm>
21 
22 #include <3d/ecs/components/joint_matrices_component.h>
23 #include <3d/ecs/components/layer_component.h>
24 #include <3d/ecs/components/material_component.h>
25 #include <3d/ecs/components/mesh_component.h>
26 #include <3d/ecs/components/node_component.h>
27 #include <3d/ecs/components/render_mesh_component.h>
28 #include <3d/ecs/components/skin_component.h>
29 #include <3d/ecs/components/world_matrix_component.h>
30 #include <3d/implementation_uids.h>
31 #include <3d/render/intf_render_data_store_default_camera.h>
32 #include <3d/render/intf_render_data_store_default_light.h>
33 #include <3d/render/intf_render_data_store_default_material.h>
34 #include <3d/render/intf_render_data_store_default_scene.h>
35 #include <3d/render/intf_render_data_store_morph.h>
36 #include <3d/util/intf_picking.h>
37 #include <core/ecs/intf_ecs.h>
38 #include <core/ecs/intf_entity_manager.h>
39 #include <core/implementation_uids.h>
40 #include <core/intf_engine.h>
41 #include <core/log.h>
42 #include <core/namespace.h>
43 #include <core/perf/cpu_perf_scope.h>
44 #include <core/perf/intf_performance_data_manager.h>
45 #include <core/plugin/intf_plugin_register.h>
46 #include <core/property/scoped_handle.h>
47 #include <render/implementation_uids.h>
48 
49 #include "util/component_util_functions.h"
50 
51 CORE_BEGIN_NAMESPACE()
52 DECLARE_PROPERTY_TYPE(RENDER_NS::IRenderDataStoreManager*);
53 CORE_END_NAMESPACE()
54 
55 CORE3D_BEGIN_NAMESPACE()
56 using namespace RENDER_NS;
57 using namespace BASE_NS;
58 using namespace CORE_NS;
59 
60 BEGIN_PROPERTY(IRenderPreprocessorSystem::Properties, ComponentMetadata)
61     DECL_PROPERTY2(IRenderPreprocessorSystem::Properties, dataStoreScene, "dataStoreScene", 0)
62     DECL_PROPERTY2(IRenderPreprocessorSystem::Properties, dataStoreCamera, "dataStoreCamera", 0)
63     DECL_PROPERTY2(IRenderPreprocessorSystem::Properties, dataStoreLight, "dataStoreLight", 0)
64     DECL_PROPERTY2(IRenderPreprocessorSystem::Properties, dataStoreMaterial, "dataStoreMaterial", 0)
65     DECL_PROPERTY2(IRenderPreprocessorSystem::Properties, dataStoreMorph, "dataStoreMorph", 0)
66     DECL_PROPERTY2(IRenderPreprocessorSystem::Properties, dataStorePrefix, "", 0)
67 END_PROPERTY();
68 
69 namespace {
70 constexpr const auto RMC = 0U;
71 constexpr const auto NC = 1U;
72 constexpr const auto WMC = 2U;
73 constexpr const auto LC = 3U;
74 constexpr const auto JMC = 4U;
75 constexpr const auto SC = 5U;
76 
77 struct SceneBoundingVolumeHelper {
78     BASE_NS::Math::Vec3 sumOfSubmeshPoints { 0.0f, 0.0f, 0.0f };
79     uint32_t submeshCount { 0 };
80 
81     BASE_NS::Math::Vec3 minAABB { std::numeric_limits<float>::max(), std::numeric_limits<float>::max(),
82         std::numeric_limits<float>::max() };
83     BASE_NS::Math::Vec3 maxAABB { -std::numeric_limits<float>::max(), -std::numeric_limits<float>::max(),
84         -std::numeric_limits<float>::max() };
85 };
86 
87 template<typename DataStoreType>
CreateIfNeeded(IRenderDataStoreManager & manager,DataStoreType * dataStore,string_view dataStoreName)88 inline auto CreateIfNeeded(IRenderDataStoreManager& manager, DataStoreType* dataStore, string_view dataStoreName)
89 {
90     if (!dataStore || dataStore->GetName() != dataStoreName) {
91         dataStore = static_cast<DataStoreType*>(manager.Create(DataStoreType::UID, dataStoreName.data()));
92     }
93     return dataStore;
94 }
95 
GatherMaterialProperties(IMaterialComponentManager & materialManager)96 vector<RenderPreprocessorSystem::MaterialProperties> GatherMaterialProperties(
97     IMaterialComponentManager& materialManager)
98 {
99     vector<RenderPreprocessorSystem::MaterialProperties> materialProperties;
100     const auto materials = static_cast<IComponentManager::ComponentId>(materialManager.GetComponentCount());
101     materialProperties.reserve(materials);
102     for (auto id = 0U; id < materials; ++id) {
103         if (auto materialHandle = materialManager.Read(id); materialHandle) {
104             materialProperties.push_back({
105                 materialManager.GetEntity(id),
106                 (materialHandle->extraRenderingFlags & MaterialComponent::ExtraRenderingFlagBits::DISABLE_BIT) ==
107                     MaterialComponent::ExtraRenderingFlagBits::DISABLE_BIT,
108                 ((materialHandle->extraRenderingFlags &
109                      MaterialComponent::ExtraRenderingFlagBits::ALLOW_GPU_INSTANCING_BIT) ==
110                     MaterialComponent::ExtraRenderingFlagBits::ALLOW_GPU_INSTANCING_BIT) ||
111                     !EntityUtil::IsValid(materialHandle->materialShader.shader),
112                 (materialHandle->materialLightingFlags & MaterialComponent::LightingFlagBits::SHADOW_CASTER_BIT) ==
113                     MaterialComponent::LightingFlagBits::SHADOW_CASTER_BIT,
114             });
115         }
116     }
117     std::sort(materialProperties.begin(), materialProperties.end(),
118         [](const RenderPreprocessorSystem::MaterialProperties& lhs,
119             const RenderPreprocessorSystem::MaterialProperties& rhs) { return (lhs.material.id < rhs.material.id); });
120     return materialProperties;
121 }
122 } // namespace
123 
RenderPreprocessorSystem(IEcs & ecs)124 RenderPreprocessorSystem::RenderPreprocessorSystem(IEcs& ecs)
125     : ecs_(ecs), jointMatricesManager_(GetManager<IJointMatricesComponentManager>(ecs)),
126       layerManager_(GetManager<ILayerComponentManager>(ecs)),
127       materialManager_(GetManager<IMaterialComponentManager>(ecs)),
128       meshManager_(GetManager<IMeshComponentManager>(ecs)), nodeManager_(GetManager<INodeComponentManager>(ecs)),
129       renderMeshManager_(GetManager<IRenderMeshComponentManager>(ecs)),
130       skinManager_(GetManager<ISkinComponentManager>(ecs)),
131       worldMatrixManager_(GetManager<IWorldMatrixComponentManager>(ecs)),
132       RENDER_PREPROCESSOR_SYSTEM_PROPERTIES(&properties_, array_view(ComponentMetadata))
133 {
134     if (IEngine* engine = ecs_.GetClassFactory().GetInterface<IEngine>(); engine) {
135         renderContext_ = GetInstance<IRenderContext>(*engine->GetInterface<IClassRegister>(), UID_RENDER_CONTEXT);
136         if (renderContext_) {
137             picking_ = GetInstance<IPicking>(*renderContext_->GetInterface<IClassRegister>(), UID_PICKING);
138             graphicsContext_ =
139                 GetInstance<IGraphicsContext>(*renderContext_->GetInterface<IClassRegister>(), UID_GRAPHICS_CONTEXT);
140         }
141     }
142 }
143 
~RenderPreprocessorSystem()144 RenderPreprocessorSystem::~RenderPreprocessorSystem()
145 {
146     if (IEngine* engine = ecs_.GetClassFactory().GetInterface<IEngine>(); engine) {
147         // check that render context is still alive
148         if (auto renderContext =
149                 GetInstance<IRenderContext>(*engine->GetInterface<IClassRegister>(), UID_RENDER_CONTEXT);
150             renderContext) {
151             IRenderDataStoreManager& rdsMgr = renderContext->GetRenderDataStoreManager();
152             if (dsScene_) {
153                 rdsMgr.Destroy(IRenderDataStoreDefaultScene::UID, dsScene_);
154             }
155             if (dsCamera_) {
156                 rdsMgr.Destroy(IRenderDataStoreDefaultCamera::UID, dsCamera_);
157             }
158             if (dsLight_) {
159                 rdsMgr.Destroy(IRenderDataStoreDefaultLight::UID, dsLight_);
160             }
161             if (dsMaterial_) {
162                 rdsMgr.Destroy(IRenderDataStoreDefaultMaterial::UID, dsMaterial_);
163             }
164             if (dsMorph_) {
165                 rdsMgr.Destroy(IRenderDataStoreMorph::UID, dsMorph_);
166             }
167         }
168     }
169 }
170 
SetActive(bool state)171 void RenderPreprocessorSystem::SetActive(bool state)
172 {
173     active_ = state;
174 }
175 
IsActive() const176 bool RenderPreprocessorSystem::IsActive() const
177 {
178     return active_;
179 }
180 
GetName() const181 string_view RenderPreprocessorSystem::GetName() const
182 {
183     return CORE3D_NS::GetName(this);
184 }
185 
GetUid() const186 Uid RenderPreprocessorSystem::GetUid() const
187 {
188     return UID;
189 }
190 
GetProperties()191 IPropertyHandle* RenderPreprocessorSystem::GetProperties()
192 {
193     return RENDER_PREPROCESSOR_SYSTEM_PROPERTIES.GetData();
194 }
195 
GetProperties() const196 const IPropertyHandle* RenderPreprocessorSystem::GetProperties() const
197 {
198     return RENDER_PREPROCESSOR_SYSTEM_PROPERTIES.GetData();
199 }
200 
SetProperties(const IPropertyHandle & data)201 void RenderPreprocessorSystem::SetProperties(const IPropertyHandle& data)
202 {
203     if (data.Owner() != &RENDER_PREPROCESSOR_SYSTEM_PROPERTIES) {
204         return;
205     }
206     if (const auto in = ScopedHandle<const IRenderPreprocessorSystem::Properties>(&data); in) {
207         properties_.dataStoreScene = in->dataStoreScene;
208         properties_.dataStoreCamera = in->dataStoreCamera;
209         properties_.dataStoreLight = in->dataStoreLight;
210         properties_.dataStoreMaterial = in->dataStoreMaterial;
211         properties_.dataStoreMorph = in->dataStoreMorph;
212         if (renderContext_) {
213             SetDataStorePointers(renderContext_->GetRenderDataStoreManager());
214         }
215     }
216 }
217 
SetDataStorePointers(IRenderDataStoreManager & manager)218 void RenderPreprocessorSystem::SetDataStorePointers(IRenderDataStoreManager& manager)
219 {
220     // creates own data stores based on names
221     dsScene_ = CreateIfNeeded(manager, dsScene_, properties_.dataStoreScene);
222     dsCamera_ = CreateIfNeeded(manager, dsCamera_, properties_.dataStoreCamera);
223     dsLight_ = CreateIfNeeded(manager, dsLight_, properties_.dataStoreLight);
224     dsMaterial_ = CreateIfNeeded(manager, dsMaterial_, properties_.dataStoreMaterial);
225     dsMorph_ = CreateIfNeeded(manager, dsMorph_, properties_.dataStoreMorph);
226 }
227 
CalculateSceneBounds()228 void RenderPreprocessorSystem::CalculateSceneBounds()
229 {
230     SceneBoundingVolumeHelper helper;
231 
232     for (const auto& i : renderMeshAabbs_) {
233         // the mesh aabb will have default value if all the submeshes were skipped. in the default value min > max.
234         if (i.second.meshAabb.min.x < i.second.meshAabb.max.x) {
235             helper.sumOfSubmeshPoints += (i.second.meshAabb.min + i.second.meshAabb.max) / 2.f;
236             helper.minAABB = Math::min(helper.minAABB, i.second.meshAabb.min);
237             helper.maxAABB = Math::max(helper.maxAABB, i.second.meshAabb.max);
238             ++helper.submeshCount;
239         }
240     }
241 
242     if (helper.submeshCount == 0) {
243         boundingSphere_.radius = 0.0f;
244         boundingSphere_.center = Math::Vec3();
245     } else {
246         const auto boundingSpherePosition = helper.sumOfSubmeshPoints / static_cast<float>(helper.submeshCount);
247 
248         const float radMin = Math::Magnitude(boundingSpherePosition - helper.minAABB);
249         const float radMax = Math::Magnitude(helper.maxAABB - boundingSpherePosition);
250         const float boundingSphereRadius = Math::max(radMin, radMax);
251 
252         // Compensate jitter and adjust scene bounding sphere only if change in bounds is meaningful.
253         if (boundingSphere_.radius > 0.0f) {
254             // Calculate distance to new bounding sphere origin from current sphere.
255             const float pointDistance = Math::Magnitude(boundingSpherePosition - boundingSphere_.center);
256             // Calculate distance to edge of new bounding sphere from current sphere origin.
257             const float sphereEdgeDistance = pointDistance + boundingSphereRadius;
258 
259             // Calculate step size for adjustment, use 10% granularity from current bounds.
260             constexpr float granularityPct = 0.10f;
261             const float granularity = boundingSphere_.radius * granularityPct;
262 
263             // Calculate required change of size, in order to fit new sphere inside current bounds.
264             const float radDifference = sphereEdgeDistance - boundingSphere_.radius;
265             const float posDifference = Math::Magnitude(boundingSpherePosition - boundingSphere_.center);
266             // We need to adjust only if the change is bigger than the step size.
267             if ((Math::abs(radDifference) > granularity) || (posDifference > granularity)) {
268                 // Calculate how many steps we need to change and in to which direction.
269                 const float radAmount = ceil((boundingSphereRadius - boundingSphere_.radius) / granularity);
270                 const int32_t posAmount = static_cast<int32_t>(ceil(posDifference / granularity));
271                 if ((radAmount != 0.f) || (posAmount != 0)) {
272                     // Update size and position of the bounds.
273                     boundingSphere_.center = boundingSpherePosition;
274                     boundingSphere_.radius = boundingSphere_.radius + (radAmount * granularity);
275                 }
276             }
277         } else {
278             // No existing bounds, start with new values.
279             boundingSphere_.radius = boundingSphereRadius;
280             boundingSphere_.center = boundingSpherePosition;
281         }
282     }
283 }
284 
GatherSortData()285 void RenderPreprocessorSystem::GatherSortData()
286 {
287     const auto& results = renderableQuery_.GetResults();
288     const auto renderMeshes = static_cast<IComponentManager::ComponentId>(results.size());
289     meshComponents_.clear();
290     meshComponents_.reserve(renderMeshes);
291     renderMeshAabbs_.clear();
292     renderMeshAabbs_.reserve(renderMeshes);
293 
294     vector<bool> disabled;
295     vector<bool> shadowCaster;
296     for (const auto& row : results) {
297         // TODO this list needs to update only when render mesh, node, or layer component have changed
298         const bool effectivelyEnabled = nodeManager_->Read(row.components[NC])->effectivelyEnabled;
299         const uint64_t layerMask = !row.IsValidComponentId(LC) ? LayerConstants::DEFAULT_LAYER_MASK
300                                                                : layerManager_->Get(row.components[LC]).layerMask;
301         if (effectivelyEnabled && (layerMask != LayerConstants::NONE_LAYER_MASK)) {
302             auto renderMeshHandle = renderMeshManager_->Read(row.components[RMC]);
303             // gather the submesh world aabbs
304             if (const auto meshData = meshManager_->Read(renderMeshHandle->mesh); meshData) {
305                 // TODO render system doesn't necessarily have to read the render mesh components. preprocessor
306                 // could offer two lists of mesh+world, one containing the render mesh batch style meshes and second
307                 // containing regular meshes
308                 const auto renderMeshEntity = renderMeshManager_->GetEntity(row.components[RMC]);
309                 auto& data = renderMeshAabbs_[renderMeshEntity];
310                 auto& meshAabb = data.meshAabb;
311                 meshAabb = {};
312                 auto& aabbs = data.submeshAabbs;
313                 aabbs.clear();
314 
315                 // check MaterialComponent::DISABLE_BIT and discard those, check
316                 // MaterialComponent::LightingFlagBits::SHADOW_CASTER_BIT and don't include them in scene bounding.
317                 disabled.clear();
318                 disabled.resize(meshData->submeshes.size());
319                 shadowCaster.clear();
320                 shadowCaster.resize(meshData->submeshes.size());
321                 auto submeshIdx = 0U;
322                 bool allowInstancing = true;
323                 for (const auto& submesh : meshData->submeshes) {
324                     if (EntityUtil::IsValid(submesh.material)) {
325                         if (auto pos = std::lower_bound(materialProperties_.cbegin(), materialProperties_.cend(),
326                                 submesh.material,
327                                 [](const MaterialProperties& element, const Entity& value) {
328                                     return element.material.id < value.id;
329                                 });
330                             (pos != materialProperties_.cend()) && (pos->material == submesh.material)) {
331                             disabled[submeshIdx] = pos->disabled;
332                             allowInstancing = allowInstancing && pos->allowInstancing;
333                             shadowCaster[submeshIdx] = pos->shadowCaster;
334                         }
335                     } else {
336                         // assuming MaterialComponent::materialLightingFlags default value includes
337                         // SHADOW_CASTER_BIT
338                         shadowCaster[submeshIdx] = true;
339                     }
340                     for (const auto additionalMaterial : submesh.additionalMaterials) {
341                         if (EntityUtil::IsValid(additionalMaterial)) {
342                             if (auto pos = std::lower_bound(materialProperties_.cbegin(), materialProperties_.cend(),
343                                     additionalMaterial,
344                                     [](const MaterialProperties& element, const Entity& value) {
345                                         return element.material.id < value.id;
346                                     });
347                                 (pos != materialProperties_.cend()) && (pos->material == additionalMaterial)) {
348                                 disabled[submeshIdx] = pos->disabled;
349                                 allowInstancing = allowInstancing && pos->allowInstancing;
350                                 shadowCaster[submeshIdx] = pos->shadowCaster;
351                             }
352                         } else {
353                             // assuming MaterialComponent::materialLightingFlags default value includes
354                             // SHADOW_CASTER_BIT
355                             shadowCaster[submeshIdx] = true;
356                         }
357                     }
358                     ++submeshIdx;
359                 }
360 
361                 if (std::any_of(disabled.cbegin(), disabled.cend(), [](const bool disabled) { return !disabled; })) {
362                     bool hasJoints = row.IsValidComponentId(JMC);
363                     if (hasJoints) {
364                         // TODO this needs to happen only when joint matrices have changed
365                         auto jointMatricesHandle = jointMatricesManager_->Read(row.components[JMC]);
366                         hasJoints = (jointMatricesHandle->count > 0U);
367                         if (hasJoints) {
368                             aabbs.push_back(
369                                 Aabb { jointMatricesHandle->jointsAabbMin, jointMatricesHandle->jointsAabbMax });
370                             meshAabb.min = Math::min(meshAabb.min, jointMatricesHandle->jointsAabbMin);
371                             meshAabb.max = Math::max(meshAabb.max, jointMatricesHandle->jointsAabbMax);
372                         }
373                     }
374                     if (!hasJoints) {
375                         submeshIdx = 0U;
376                         const auto& world = worldMatrixManager_->Read(row.components[WMC])->matrix;
377                         for (const auto& submesh : meshData->submeshes) {
378                             // TODO this needs to happen only when world matrix, or mesh component have changed
379                             if (disabled[submeshIdx]) {
380                                 aabbs.push_back({});
381                             } else {
382                                 const MinAndMax mam = picking_->GetWorldAABB(world, submesh.aabbMin, submesh.aabbMax);
383                                 aabbs.push_back({ mam.minAABB, mam.maxAABB });
384                                 if (shadowCaster[submeshIdx]) {
385                                     meshAabb.min = Math::min(meshAabb.min, mam.minAABB);
386                                     meshAabb.max = Math::max(meshAabb.max, mam.maxAABB);
387                                 }
388                             }
389                             ++submeshIdx;
390                         }
391                     }
392 
393                     auto skin = (row.IsValidComponentId(SC)) ? skinManager_->Read(row.components[SC])->skin : Entity {};
394                     meshComponents_.push_back({ row.components[RMC], renderMeshHandle->mesh,
395                         renderMeshHandle->renderMeshBatch, skin, allowInstancing });
396                 }
397             }
398         }
399     }
400 }
401 
GetECS() const402 const IEcs& RenderPreprocessorSystem::GetECS() const
403 {
404     return ecs_;
405 }
406 
GetRenderBatchMeshEntities() const407 array_view<const Entity> RenderPreprocessorSystem::GetRenderBatchMeshEntities() const
408 {
409     return renderBatchComponents_;
410 }
411 
GetInstancingAllowedEntities() const412 array_view<const Entity> RenderPreprocessorSystem::GetInstancingAllowedEntities() const
413 {
414     return instancingAllowed_;
415 }
416 
GetInstancingDisabledEntities() const417 array_view<const Entity> RenderPreprocessorSystem::GetInstancingDisabledEntities() const
418 {
419     return rest_;
420 }
421 
GetRenderMeshAabb(Entity renderMesh) const422 RenderPreprocessorSystem::Aabb RenderPreprocessorSystem::GetRenderMeshAabb(Entity renderMesh) const
423 {
424     if (auto pos = renderMeshAabbs_.find(renderMesh); pos != renderMeshAabbs_.cend()) {
425         return pos->second.meshAabb;
426     }
427     return {};
428 }
429 
GetRenderMeshAabbs(Entity renderMesh) const430 array_view<const RenderPreprocessorSystem::Aabb> RenderPreprocessorSystem::GetRenderMeshAabbs(Entity renderMesh) const
431 {
432     if (auto pos = renderMeshAabbs_.find(renderMesh); pos != renderMeshAabbs_.cend()) {
433         return pos->second.submeshAabbs;
434     }
435     return {};
436 }
437 
GetBoundingSphere() const438 RenderPreprocessorSystem::Sphere RenderPreprocessorSystem::GetBoundingSphere() const
439 {
440     return boundingSphere_;
441 }
442 
Initialize()443 void RenderPreprocessorSystem::Initialize()
444 {
445     if (graphicsContext_ && renderContext_) {
446         SetDataStorePointers(renderContext_->GetRenderDataStoreManager());
447     }
448     if (renderMeshManager_ && nodeManager_ && layerManager_) {
449         const ComponentQuery::Operation operations[] = {
450             { *nodeManager_, ComponentQuery::Operation::REQUIRE },
451             { *worldMatrixManager_, ComponentQuery::Operation::REQUIRE },
452             { *layerManager_, ComponentQuery::Operation::OPTIONAL },
453             { *jointMatricesManager_, ComponentQuery::Operation::OPTIONAL },
454             { *skinManager_, ComponentQuery::Operation::OPTIONAL },
455         };
456         renderableQuery_.SetEcsListenersEnabled(true);
457         renderableQuery_.SetupQuery(*renderMeshManager_, operations, false);
458     }
459 }
460 
Update(bool frameRenderingQueued,uint64_t totalTime,uint64_t deltaTime)461 bool RenderPreprocessorSystem::Update(bool frameRenderingQueued, uint64_t totalTime, uint64_t deltaTime)
462 {
463     if (!active_) {
464         return false;
465     }
466 
467     renderableQuery_.Execute();
468 
469     const auto layerGen = layerManager_->GetGenerationCounter();
470     const auto materialGen = materialManager_->GetGenerationCounter();
471     const auto meshGen = meshManager_->GetGenerationCounter();
472     const auto nodeGen = nodeManager_->GetGenerationCounter();
473     const auto renderMeshGen = renderMeshManager_->GetGenerationCounter();
474     const auto worldMatrixGen = worldMatrixManager_->GetGenerationCounter();
475     if ((layerGeneration_ == layerGen) && (materialGeneration_ == materialGen) && (meshGeneration_ == meshGen) &&
476         (nodeGeneration_ == nodeGen) && (renderMeshGeneration_ == renderMeshGen) &&
477         (worldMatrixGeneration_ == worldMatrixGen)) {
478         return false;
479     }
480 
481     layerGeneration_ = layerGen;
482     materialGeneration_ = materialGen;
483     meshGeneration_ = meshGen;
484     nodeGeneration_ = nodeGen;
485     renderMeshGeneration_ = renderMeshGen;
486     worldMatrixGeneration_ = worldMatrixGen;
487 
488     materialProperties_ = GatherMaterialProperties(*materialManager_);
489 
490     // gather which mesh is used by each render mesh component.
491     GatherSortData();
492 
493     // reorder the list so that entities with render mesh batch are first sorted by entity id
494     const auto noBatchComponent = std::partition(meshComponents_.begin(), meshComponents_.end(),
495         [](const SortData& value) { return EntityUtil::IsValid(value.batch); });
496     std::sort(meshComponents_.begin(), noBatchComponent,
497         [](const SortData& lhs, const SortData& rhs) { return lhs.renderMeshId < rhs.renderMeshId; });
498 
499     // next are meshes that can be instanced sorted by mesh id, skin id and entity id
500     auto noInstancing = std::partition(
501         noBatchComponent, meshComponents_.end(), [](const SortData& value) { return value.allowInstancing; });
502     std::sort(noBatchComponent, noInstancing, [](const SortData& lhs, const SortData& rhs) {
503         if (lhs.mesh.id < rhs.mesh.id) {
504             return true;
505         }
506         if (lhs.mesh.id > rhs.mesh.id) {
507             return false;
508         }
509         if (lhs.skin.id < rhs.skin.id) {
510             return true;
511         }
512         if (lhs.skin.id > rhs.skin.id) {
513             return false;
514         }
515         return lhs.renderMeshId < rhs.renderMeshId;
516     });
517 
518     // move entities, which could be instanced but have only one instance, to the end
519     {
520         auto begin = reverse_iterator(noInstancing);
521         auto end = reverse_iterator(noBatchComponent);
522         for (auto it = begin; it != end;) {
523             auto pos = std::adjacent_find(it, end, [](const SortData& lhs, const SortData& rhs) {
524                 return (lhs.mesh.id != rhs.mesh.id) || (lhs.skin.id != rhs.skin.id);
525             });
526             if (pos != end) {
527                 // pos points to the first element where comparison failed i.e. pos is the last of previous batch
528                 // and pos+1 is the first of the next batch
529                 ++pos;
530             }
531             // if the batch size is 1, move the single entry to the end
532             if (const auto batchSize = pos - it; batchSize == 1U) {
533                 auto i = std::rotate(pos.base(), pos.base() + 1, begin.base());
534                 begin = reverse_iterator(i);
535             }
536 
537             it = pos;
538         }
539         noInstancing = begin.base();
540     }
541 
542     // finally sort the non instanced by entity id
543     std::sort(noInstancing, meshComponents_.end(),
544         [](const SortData& lhs, const SortData& rhs) { return lhs.renderMeshId < rhs.renderMeshId; });
545 
546     // list the entities with render mesh components in sorted order.
547     renderMeshComponents_.clear();
548     const auto meshCount = meshComponents_.size();
549     renderMeshComponents_.reserve(meshCount);
550     std::transform(meshComponents_.cbegin(), meshComponents_.cend(), std::back_inserter(renderMeshComponents_),
551         [renderMeshManager = renderMeshManager_](
552             const SortData& data) { return renderMeshManager->GetEntity(data.renderMeshId); });
553     renderBatchComponents_ =
554         array_view(renderMeshComponents_.data(), static_cast<size_t>(noBatchComponent - meshComponents_.begin()));
555     instancingAllowed_ = array_view(renderMeshComponents_.data() + renderBatchComponents_.size(),
556         static_cast<size_t>(noInstancing - noBatchComponent));
557     rest_ = array_view(renderMeshComponents_.data() + renderBatchComponents_.size() + instancingAllowed_.size(),
558         static_cast<size_t>(meshComponents_.end() - noInstancing));
559 
560     CalculateSceneBounds();
561 
562     return true;
563 }
564 
Uninitialize()565 void RenderPreprocessorSystem::Uninitialize()
566 {
567     renderableQuery_.SetEcsListenersEnabled(false);
568 }
569 
IRenderPreprocessorSystemInstance(IEcs & ecs)570 ISystem* IRenderPreprocessorSystemInstance(IEcs& ecs)
571 {
572     return new RenderPreprocessorSystem(ecs);
573 }
574 
IRenderPreprocessorSystemDestroy(ISystem * instance)575 void IRenderPreprocessorSystemDestroy(ISystem* instance)
576 {
577     delete static_cast<RenderPreprocessorSystem*>(instance);
578 }
579 CORE3D_END_NAMESPACE()
580