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