• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <algorithm>
19 
20 #include <3d/ecs/components/joint_matrices_component.h>
21 #include <3d/ecs/components/layer_component.h>
22 #include <3d/ecs/components/material_component.h>
23 #include <3d/ecs/components/mesh_component.h>
24 #include <3d/ecs/components/node_component.h>
25 #include <3d/ecs/components/render_handle_component.h>
26 #include <3d/ecs/components/render_mesh_component.h>
27 #include <3d/ecs/components/skin_component.h>
28 #include <3d/ecs/components/world_matrix_component.h>
29 #include <3d/implementation_uids.h>
30 #include <3d/render/intf_render_data_store_default_camera.h>
31 #include <3d/render/intf_render_data_store_default_light.h>
32 #include <3d/render/intf_render_data_store_default_material.h>
33 #include <3d/render/intf_render_data_store_default_scene.h>
34 #include <3d/render/intf_render_data_store_morph.h>
35 #include <3d/util/intf_picking.h>
36 #include <core/ecs/intf_ecs.h>
37 #include <core/ecs/intf_entity_manager.h>
38 #include <core/implementation_uids.h>
39 #include <core/intf_engine.h>
40 #include <core/log.h>
41 #include <core/namespace.h>
42 #include <core/perf/cpu_perf_scope.h>
43 #include <core/perf/intf_performance_data_manager.h>
44 #include <core/plugin/intf_plugin_register.h>
45 #include <core/property/scoped_handle.h>
46 #include <core/property_tools/property_api_impl.inl>
47 #include <core/property_tools/property_macros.h>
48 #include <render/implementation_uids.h>
49 
50 #include "util/component_util_functions.h"
51 #include "util/log.h"
52 
53 CORE_BEGIN_NAMESPACE()
54 DECLARE_PROPERTY_TYPE(RENDER_NS::IRenderDataStoreManager*);
55 CORE_END_NAMESPACE()
56 
57 CORE3D_BEGIN_NAMESPACE()
58 using namespace RENDER_NS;
59 using namespace BASE_NS;
60 using namespace CORE_NS;
61 
62 PROPERTY_LIST(IRenderPreprocessorSystem::Properties, ComponentMetadata,
63     MEMBER_PROPERTY(dataStoreScene, "dataStoreScene", 0), MEMBER_PROPERTY(dataStoreCamera, "dataStoreCamera", 0),
64     MEMBER_PROPERTY(dataStoreLight, "dataStoreLight", 0), MEMBER_PROPERTY(dataStoreMaterial, "dataStoreMaterial", 0),
65     MEMBER_PROPERTY(dataStoreMorph, "dataStoreMorph", 0), MEMBER_PROPERTY(dataStorePrefix, "", 0));
66 
operator <(const RenderPreprocessorSystem::MaterialProperties & lhs,const RenderPreprocessorSystem::MaterialProperties & rhs)67 constexpr bool operator<(
68     const RenderPreprocessorSystem::MaterialProperties& lhs, const RenderPreprocessorSystem::MaterialProperties& rhs)
69 {
70     return lhs.material.id < rhs.material.id;
71 }
72 
operator <(const RenderPreprocessorSystem::MaterialProperties & element,const Entity & value)73 constexpr bool operator<(const RenderPreprocessorSystem::MaterialProperties& element, const Entity& value)
74 {
75     return element.material.id < value.id;
76 }
77 
operator <(const Entity & element,const RenderPreprocessorSystem::MaterialProperties & value)78 constexpr bool operator<(const Entity& element, const RenderPreprocessorSystem::MaterialProperties& value)
79 {
80     return element.id < value.material.id;
81 }
82 
83 namespace {
84 constexpr const auto RMC = 0U;
85 constexpr const auto NC = 1U;
86 constexpr const auto WMC = 2U;
87 constexpr const auto LC = 3U;
88 constexpr const auto JMC = 4U;
89 constexpr const auto SC = 5U;
90 
91 static const MaterialComponent DEF_MATERIAL_COMPONENT {};
92 static constexpr RenderDataDefaultMaterial::InputMaterialUniforms DEF_INPUT_MATERIAL_UNIFORMS {};
93 
94 struct SceneBoundingVolumeHelper {
95     BASE_NS::Math::Vec3 sumOfSubmeshPoints { 0.0f, 0.0f, 0.0f };
96     uint32_t submeshCount { 0 };
97 
98     BASE_NS::Math::Vec3 minAABB { std::numeric_limits<float>::max(), std::numeric_limits<float>::max(),
99         std::numeric_limits<float>::max() };
100     BASE_NS::Math::Vec3 maxAABB { -std::numeric_limits<float>::max(), -std::numeric_limits<float>::max(),
101         -std::numeric_limits<float>::max() };
102 };
103 
104 template<typename DataStoreType>
CreateIfNeeded(IRenderDataStoreManager & manager,refcnt_ptr<DataStoreType> & dataStore,string_view dataStoreName)105 inline auto CreateIfNeeded(
106     IRenderDataStoreManager& manager, refcnt_ptr<DataStoreType>& dataStore, string_view dataStoreName)
107 {
108     if (!dataStore || dataStore->GetName() != dataStoreName) {
109         dataStore = refcnt_ptr<DataStoreType>(manager.Create(DataStoreType::UID, dataStoreName.data()));
110     }
111     return dataStore;
112 }
113 
114 #if (CORE3D_VALIDATION_ENABLED == 1)
ValidateInputColor(const Entity material,const MaterialComponent & matComp)115 void ValidateInputColor(const Entity material, const MaterialComponent& matComp)
116 {
117     if (matComp.type < MaterialComponent::Type::CUSTOM) {
118         const auto& base = matComp.textures[MaterialComponent::TextureIndex::BASE_COLOR];
119         if ((base.factor.x > 1.0f) || (base.factor.y > 1.0f) || (base.factor.z > 1.0f) || (base.factor.w > 1.0f)) {
120             CORE_LOG_ONCE_W(to_string(material.id) + "_base_fac",
121                 "CORE3D_VALIDATION: Non custom material type expects base color factor to be <= 1.0f.");
122         }
123         const auto& mat = matComp.textures[MaterialComponent::TextureIndex::MATERIAL];
124         if ((mat.factor.y > 1.0f) || (mat.factor.z > 1.0f)) {
125             CORE_LOG_ONCE_W(to_string(material.id) + "_mat_fac",
126                 "CORE3D_VALIDATION: Non custom material type expects roughness and metallic to be <= 1.0f.");
127         }
128     }
129 }
130 #endif
131 
InputMaterialUniformsFromMaterialComponent(const Entity material,const MaterialComponent & matDesc)132 RenderDataDefaultMaterial::InputMaterialUniforms InputMaterialUniformsFromMaterialComponent(
133     const Entity material, const MaterialComponent& matDesc)
134 {
135     RenderDataDefaultMaterial::InputMaterialUniforms mu = DEF_INPUT_MATERIAL_UNIFORMS;
136 
137 #if (CORE3D_VALIDATION_ENABLED == 1)
138     ValidateInputColor(material, matDesc);
139 #endif
140 
141     uint32_t transformBits = 0u;
142     constexpr const uint32_t texCount = Math::min(static_cast<uint32_t>(MaterialComponent::TextureIndex::TEXTURE_COUNT),
143         RenderDataDefaultMaterial::MATERIAL_TEXTURE_COUNT);
144     for (uint32_t idx = 0u; idx < texCount; ++idx) {
145         const auto& tex = matDesc.textures[idx];
146         auto& texRef = mu.textureData[idx];
147         texRef.factor = tex.factor;
148         texRef.translation = tex.transform.translation;
149         texRef.rotation = tex.transform.rotation;
150         texRef.scale = tex.transform.scale;
151         const bool hasTransform = (texRef.translation.x != 0.0f) || (texRef.translation.y != 0.0f) ||
152                                   (texRef.rotation != 0.0f) || (texRef.scale.x != 1.0f) || (texRef.scale.y != 1.0f);
153         transformBits |= static_cast<uint32_t>(hasTransform) << idx;
154     }
155     {
156         // NOTE: premultiplied alpha, applied here and therefore the baseColor factor is special
157         const auto& tex = matDesc.textures[MaterialComponent::TextureIndex::BASE_COLOR];
158         const float alpha = tex.factor.w;
159         const Math::Vec4 baseColor = {
160             tex.factor.x * alpha,
161             tex.factor.y * alpha,
162             tex.factor.z * alpha,
163             alpha,
164         };
165 
166         constexpr uint32_t index = 0u;
167         mu.textureData[index].factor = baseColor;
168     }
169     mu.alphaCutoff = matDesc.alphaCutoff;
170     mu.texCoordSetBits = matDesc.useTexcoordSetBit;
171     mu.texTransformSetBits = transformBits;
172     mu.id = material.id;
173     return mu;
174 }
175 
GetRenderHandleReferences(const IRenderHandleComponentManager & renderHandleMgr,const array_view<const EntityReference> inputs,array_view<RenderHandleReference> & outputs)176 inline void GetRenderHandleReferences(const IRenderHandleComponentManager& renderHandleMgr,
177     const array_view<const EntityReference> inputs, array_view<RenderHandleReference>& outputs)
178 {
179     for (size_t idx = 0; idx < outputs.size(); ++idx) {
180         outputs[idx] = renderHandleMgr.GetRenderHandleReference(inputs[idx]);
181     }
182 }
183 
RenderMaterialLightingFlagsFromMaterialFlags(const MaterialComponent::LightingFlags materialFlags)184 constexpr uint32_t RenderMaterialLightingFlagsFromMaterialFlags(const MaterialComponent::LightingFlags materialFlags)
185 {
186     uint32_t rmf = 0;
187     if (materialFlags & MaterialComponent::LightingFlagBits::SHADOW_RECEIVER_BIT) {
188         rmf |= RenderMaterialFlagBits::RENDER_MATERIAL_SHADOW_RECEIVER_BIT;
189     }
190     if (materialFlags & MaterialComponent::LightingFlagBits::SHADOW_CASTER_BIT) {
191         rmf |= RenderMaterialFlagBits::RENDER_MATERIAL_SHADOW_CASTER_BIT;
192     }
193     if (materialFlags & MaterialComponent::LightingFlagBits::PUNCTUAL_LIGHT_RECEIVER_BIT) {
194         rmf |= RenderMaterialFlagBits::RENDER_MATERIAL_PUNCTUAL_LIGHT_RECEIVER_BIT;
195     }
196     if (materialFlags & MaterialComponent::LightingFlagBits::INDIRECT_LIGHT_RECEIVER_BIT) {
197         rmf |= RenderMaterialFlagBits::RENDER_MATERIAL_INDIRECT_LIGHT_RECEIVER_BIT;
198     }
199     return rmf;
200 }
201 
GetMaterialHandles(const MaterialComponent & materialDesc,const IRenderHandleComponentManager & gpuManager)202 inline RenderDataDefaultMaterial::MaterialHandlesWithHandleReference GetMaterialHandles(
203     const MaterialComponent& materialDesc, const IRenderHandleComponentManager& gpuManager)
204 {
205     RenderDataDefaultMaterial::MaterialHandlesWithHandleReference materialHandles;
206     auto imageIt = std::begin(materialHandles.images);
207     auto samplerIt = std::begin(materialHandles.samplers);
208     for (const MaterialComponent::TextureInfo& info : materialDesc.textures) {
209         *imageIt++ = gpuManager.GetRenderHandleReference(info.image);
210         *samplerIt++ = gpuManager.GetRenderHandleReference(info.sampler);
211     }
212     return materialHandles;
213 }
214 
RenderMaterialFlagsFromMaterialValues(const MaterialComponent & matComp,const RenderDataDefaultMaterial::MaterialHandlesWithHandleReference & handles,const uint32_t hasTransformBit)215 uint32_t RenderMaterialFlagsFromMaterialValues(const MaterialComponent& matComp,
216     const RenderDataDefaultMaterial::MaterialHandlesWithHandleReference& handles, const uint32_t hasTransformBit)
217 {
218     uint32_t rmf = 0;
219     // enable built-in specialization for default materials
220     CORE_ASSERT(matComp.type <= MaterialComponent::Type::CUSTOM_COMPLEX);
221     if (matComp.type < MaterialComponent::Type::CUSTOM) {
222         if (handles.images[MaterialComponent::TextureIndex::NORMAL] ||
223             handles.images[MaterialComponent::TextureIndex::CLEARCOAT_NORMAL]) {
224             // need to check for tangents as well with submesh
225             rmf |= RenderMaterialFlagBits::RENDER_MATERIAL_NORMAL_MAP_BIT;
226         }
227         if (matComp.textures[MaterialComponent::TextureIndex::CLEARCOAT].factor.x > 0.0f) {
228             rmf |= RenderMaterialFlagBits::RENDER_MATERIAL_CLEAR_COAT_BIT;
229         }
230         if ((matComp.textures[MaterialComponent::TextureIndex::SHEEN].factor.x > 0.0f) ||
231             (matComp.textures[MaterialComponent::TextureIndex::SHEEN].factor.y > 0.0f) ||
232             (matComp.textures[MaterialComponent::TextureIndex::SHEEN].factor.z > 0.0f)) {
233             rmf |= RenderMaterialFlagBits::RENDER_MATERIAL_SHEEN_BIT;
234         }
235         if (matComp.textures[MaterialComponent::TextureIndex::SPECULAR].factor != Math::Vec4(1.f, 1.f, 1.f, 1.f) ||
236             handles.images[MaterialComponent::TextureIndex::SPECULAR]) {
237             rmf |= RenderMaterialFlagBits::RENDER_MATERIAL_SPECULAR_BIT;
238         }
239         if (matComp.textures[MaterialComponent::TextureIndex::TRANSMISSION].factor.x > 0.0f) {
240             rmf |= RenderMaterialFlagBits::RENDER_MATERIAL_TRANSMISSION_BIT;
241         }
242     }
243     rmf |= (hasTransformBit > 0U) ? RenderMaterialFlagBits::RENDER_MATERIAL_TEXTURE_TRANSFORM_BIT : 0U;
244     // NOTE: built-in shaders write 1.0 to alpha always when discard is enabled
245     rmf |= (matComp.alphaCutoff < 1.0f) ? RenderMaterialFlagBits::RENDER_MATERIAL_SHADER_DISCARD_BIT : 0U;
246     // NOTE: GPU instancing specialization needs to be enabled during rendering
247     return rmf;
248 }
249 
RemoveMaterialProperties(IRenderDataStoreDefaultMaterial & dsMaterial,const IMaterialComponentManager & materialManager,vector<RenderPreprocessorSystem::MaterialProperties> & materialProperties,array_view<const Entity> removedMaterials)250 void RemoveMaterialProperties(IRenderDataStoreDefaultMaterial& dsMaterial,
251     const IMaterialComponentManager& materialManager,
252     vector<RenderPreprocessorSystem::MaterialProperties>& materialProperties, array_view<const Entity> removedMaterials)
253 {
254     materialProperties.erase(std::set_difference(materialProperties.begin(), materialProperties.end(),
255         removedMaterials.cbegin(), removedMaterials.cend(), materialProperties.begin()),
256         materialProperties.cend());
257 
258     // destroy rendering side decoupled material data
259     for (const auto& entRef : removedMaterials) {
260         dsMaterial.DestroyMaterialData(entRef.id);
261     }
262 }
263 } // namespace
264 
RenderPreprocessorSystem(IEcs & ecs)265 RenderPreprocessorSystem::RenderPreprocessorSystem(IEcs& ecs)
266     : ecs_(ecs), jointMatricesManager_(GetManager<IJointMatricesComponentManager>(ecs)),
267       layerManager_(GetManager<ILayerComponentManager>(ecs)),
268       materialManager_(GetManager<IMaterialComponentManager>(ecs)),
269       renderHandleManager_(GetManager<IRenderHandleComponentManager>(ecs)),
270       meshManager_(GetManager<IMeshComponentManager>(ecs)), nodeManager_(GetManager<INodeComponentManager>(ecs)),
271       renderMeshManager_(GetManager<IRenderMeshComponentManager>(ecs)),
272       skinManager_(GetManager<ISkinComponentManager>(ecs)),
273       worldMatrixManager_(GetManager<IWorldMatrixComponentManager>(ecs)),
274       RENDER_PREPROCESSOR_SYSTEM_PROPERTIES(&properties_, array_view(ComponentMetadata))
275 {
276     if (IEngine* engine = ecs_.GetClassFactory().GetInterface<IEngine>(); engine) {
277         renderContext_ = GetInstance<IRenderContext>(*engine->GetInterface<IClassRegister>(), UID_RENDER_CONTEXT);
278         if (renderContext_) {
279             picking_ = GetInstance<IPicking>(*renderContext_->GetInterface<IClassRegister>(), UID_PICKING);
280             graphicsContext_ =
281                 GetInstance<IGraphicsContext>(*renderContext_->GetInterface<IClassRegister>(), UID_GRAPHICS_CONTEXT);
282         }
283     }
284 }
285 
286 RenderPreprocessorSystem::~RenderPreprocessorSystem() = default;
287 
SetActive(bool state)288 void RenderPreprocessorSystem::SetActive(bool state)
289 {
290     active_ = state;
291 }
292 
IsActive() const293 bool RenderPreprocessorSystem::IsActive() const
294 {
295     return active_;
296 }
297 
GetName() const298 string_view RenderPreprocessorSystem::GetName() const
299 {
300     return CORE3D_NS::GetName(this);
301 }
302 
GetUid() const303 Uid RenderPreprocessorSystem::GetUid() const
304 {
305     return UID;
306 }
307 
GetProperties()308 IPropertyHandle* RenderPreprocessorSystem::GetProperties()
309 {
310     return RENDER_PREPROCESSOR_SYSTEM_PROPERTIES.GetData();
311 }
312 
GetProperties() const313 const IPropertyHandle* RenderPreprocessorSystem::GetProperties() const
314 {
315     return RENDER_PREPROCESSOR_SYSTEM_PROPERTIES.GetData();
316 }
317 
SetProperties(const IPropertyHandle & data)318 void RenderPreprocessorSystem::SetProperties(const IPropertyHandle& data)
319 {
320     if (data.Owner() != &RENDER_PREPROCESSOR_SYSTEM_PROPERTIES) {
321         return;
322     }
323     if (const auto in = ScopedHandle<const IRenderPreprocessorSystem::Properties>(&data); in) {
324         properties_.dataStoreScene = in->dataStoreScene;
325         properties_.dataStoreCamera = in->dataStoreCamera;
326         properties_.dataStoreLight = in->dataStoreLight;
327         properties_.dataStoreMaterial = in->dataStoreMaterial;
328         properties_.dataStoreMorph = in->dataStoreMorph;
329         if (renderContext_) {
330             SetDataStorePointers(renderContext_->GetRenderDataStoreManager());
331         }
332     }
333 }
334 
OnComponentEvent(EventType type,const IComponentManager & componentManager,BASE_NS::array_view<const Entity> entities)335 void RenderPreprocessorSystem::OnComponentEvent(
336     EventType type, const IComponentManager& componentManager, BASE_NS::array_view<const Entity> entities)
337 {
338     if (componentManager.GetUid() == IMaterialComponentManager::UID) {
339         if ((type == EventType::CREATED) || (type == EventType::MODIFIED)) {
340             materialModifiedEvents_.append(entities.cbegin(), entities.cend());
341         } else if (type == EventType::DESTROYED) {
342             materialDestroyedEvents_.append(entities.cbegin(), entities.cend());
343         }
344     } else if (componentManager.GetUid() == IRenderMeshComponentManager::UID) {
345         if (type == EventType::CREATED) {
346             renderMeshAabbs_.reserve(entities.size());
347             for (const auto& newRenderMesh : entities) {
348                 auto pos = std::lower_bound(renderMeshAabbs_.cbegin(), renderMeshAabbs_.cend(), newRenderMesh,
349                     [](const RenderMeshAaabb& elem, const Entity& value) { return elem.entity < value; });
350                 if ((pos == renderMeshAabbs_.cend()) || (pos->entity != newRenderMesh)) {
351                     renderMeshAabbs_.insert(pos, RenderMeshAaabb { newRenderMesh, {}, {} });
352                 }
353             }
354         } else if (type == EventType::DESTROYED) {
355             for (const auto& removedRenderMesh : entities) {
356                 auto pos = std::lower_bound(renderMeshAabbs_.cbegin(), renderMeshAabbs_.cend(), removedRenderMesh,
357                     [](const RenderMeshAaabb& elem, const Entity& value) { return elem.entity < value; });
358                 if ((pos != renderMeshAabbs_.cend()) && (pos->entity == removedRenderMesh)) {
359                     renderMeshAabbs_.erase(pos);
360                 }
361             }
362         }
363     }
364 }
365 
HandleMaterialEvents()366 void RenderPreprocessorSystem::HandleMaterialEvents()
367 {
368 #if (CORE3D_DEV_ENABLED == 1)
369     CORE_CPU_PERF_SCOPE("CORE3D", "RenderPreprocessorSystem", "HandleMaterialEvents", CORE3D_PROFILER_DEFAULT_COLOR);
370 #endif
371     if (!materialDestroyedEvents_.empty()) {
372         std::sort(materialDestroyedEvents_.begin(), materialDestroyedEvents_.end());
373     }
374 
375     if (!materialModifiedEvents_.empty()) {
376         std::sort(materialModifiedEvents_.begin(), materialModifiedEvents_.end());
377         // creating a component generates created and modified events. filter out materials which were created and
378         // modified.
379         materialModifiedEvents_.erase(std::unique(materialModifiedEvents_.begin(), materialModifiedEvents_.end()),
380             materialModifiedEvents_.cend());
381         if (!materialDestroyedEvents_.empty()) {
382             // filter out materials which were created/modified, but also destroyed.
383             materialModifiedEvents_.erase(std::set_difference(materialModifiedEvents_.cbegin(),
384                 materialModifiedEvents_.cend(), materialDestroyedEvents_.cbegin(),
385                 materialDestroyedEvents_.cend(), materialModifiedEvents_.begin()),
386                 materialModifiedEvents_.cend());
387         }
388         UpdateMaterialProperties();
389         materialModifiedEvents_.clear();
390     }
391     if (!materialDestroyedEvents_.empty()) {
392         RemoveMaterialProperties(*dsMaterial_, *materialManager_, materialProperties_, materialDestroyedEvents_);
393         materialDestroyedEvents_.clear();
394     }
395 }
396 
SetDataStorePointers(IRenderDataStoreManager & manager)397 void RenderPreprocessorSystem::SetDataStorePointers(IRenderDataStoreManager& manager)
398 {
399     // creates own data stores based on names
400     dsScene_ = CreateIfNeeded(manager, dsScene_, properties_.dataStoreScene);
401     dsCamera_ = CreateIfNeeded(manager, dsCamera_, properties_.dataStoreCamera);
402     dsLight_ = CreateIfNeeded(manager, dsLight_, properties_.dataStoreLight);
403     dsMaterial_ = CreateIfNeeded(manager, dsMaterial_, properties_.dataStoreMaterial);
404     dsMorph_ = CreateIfNeeded(manager, dsMorph_, properties_.dataStoreMorph);
405 }
406 
CalculateSceneBounds()407 void RenderPreprocessorSystem::CalculateSceneBounds()
408 {
409     SceneBoundingVolumeHelper helper;
410 
411     for (const auto& i : renderMeshAabbs_) {
412         // the mesh aabb will have default value if all the submeshes were skipped. in the default value min > max.
413         // meshes which don't cast shadows are also skipped from the scene bounds.
414         if ((i.meshAabb.min.x < i.meshAabb.max.x) && i.shadowCaster) {
415             helper.sumOfSubmeshPoints += (i.meshAabb.min + i.meshAabb.max) / 2.f;
416             helper.minAABB = Math::min(helper.minAABB, i.meshAabb.min);
417             helper.maxAABB = Math::max(helper.maxAABB, i.meshAabb.max);
418             ++helper.submeshCount;
419         }
420     }
421 
422     if (helper.submeshCount == 0) {
423         boundingSphere_.radius = 0.0f;
424         boundingSphere_.center = Math::Vec3();
425     } else {
426         const auto boundingSpherePosition = helper.sumOfSubmeshPoints / static_cast<float>(helper.submeshCount);
427 
428         const float radMin = Math::Magnitude(boundingSpherePosition - helper.minAABB);
429         const float radMax = Math::Magnitude(helper.maxAABB - boundingSpherePosition);
430         const float boundingSphereRadius = Math::max(radMin, radMax);
431 
432         // Compensate jitter and adjust scene bounding sphere only if change in bounds is meaningful.
433         if (boundingSphere_.radius > 0.0f) {
434             // Calculate distance to new bounding sphere origin from current sphere.
435             const float pointDistance = Math::Magnitude(boundingSpherePosition - boundingSphere_.center);
436             // Calculate distance to edge of new bounding sphere from current sphere origin.
437             const float sphereEdgeDistance = pointDistance + boundingSphereRadius;
438 
439             // Calculate step size for adjustment, use 10% granularity from current bounds.
440             constexpr float granularityPct = 0.10f;
441             const float granularity = boundingSphere_.radius * granularityPct;
442 
443             // Calculate required change of size, in order to fit new sphere inside current bounds.
444             const float radDifference = sphereEdgeDistance - boundingSphere_.radius;
445             const float posDifference = Math::Magnitude(boundingSpherePosition - boundingSphere_.center);
446             // We need to adjust only if the change is bigger than the step size.
447             if ((Math::abs(radDifference) > granularity) || (posDifference > granularity)) {
448                 // Calculate how many steps we need to change and in to which direction.
449                 const float radAmount = ceil((boundingSphereRadius - boundingSphere_.radius) / granularity);
450                 const int32_t posAmount = static_cast<int32_t>(ceil(posDifference / granularity));
451                 if ((radAmount != 0.f) || (posAmount != 0)) {
452                     // Update size and position of the bounds.
453                     boundingSphere_.center = boundingSpherePosition;
454                     boundingSphere_.radius = boundingSphere_.radius + (radAmount * granularity);
455                 }
456             }
457         } else {
458             // No existing bounds, start with new values.
459             boundingSphere_.radius = boundingSphereRadius;
460             boundingSphere_.center = boundingSpherePosition;
461         }
462     }
463 }
464 
GatherSortData()465 void RenderPreprocessorSystem::GatherSortData()
466 {
467 #if (CORE3D_DEV_ENABLED == 1)
468     CORE_CPU_PERF_SCOPE("CORE3D", "RenderPreprocessorSystem", "GatherSortData", CORE3D_PROFILER_DEFAULT_COLOR);
469 #endif
470     const auto& results = renderableQuery_.GetResults();
471     const auto renderMeshes = static_cast<IComponentManager::ComponentId>(results.size());
472     meshComponents_.clear();
473     meshComponents_.reserve(renderMeshes);
474     renderMeshAabbs_.reserve(renderMeshes);
475 
476     vector<bool> disabled;
477     vector<bool> shadowCaster;
478     for (const auto& row : results) {
479         const bool effectivelyEnabled = nodeManager_->Read(row.components[NC])->effectivelyEnabled;
480         const uint64_t layerMask = !row.IsValidComponentId(LC) ? LayerConstants::DEFAULT_LAYER_MASK
481                                                                : layerManager_->Get(row.components[LC]).layerMask;
482         if (effectivelyEnabled && (layerMask != LayerConstants::NONE_LAYER_MASK)) {
483             auto renderMeshHandle = renderMeshManager_->Read(row.components[RMC]);
484             // gather the submesh world aabbs
485             if (const auto meshData = meshManager_->Read(renderMeshHandle->mesh); meshData) {
486                 // could offer two lists of mesh+world, one containing the render mesh batch style meshes and second
487                 // containing regular meshes
488                 const auto renderMeshEntity = renderMeshManager_->GetEntity(row.components[RMC]);
489 
490                 auto pos = std::lower_bound(renderMeshAabbs_.begin(), renderMeshAabbs_.end(), renderMeshEntity,
491                     [](const RenderMeshAaabb& elem, const Entity& value) { return elem.entity < value; });
492                 if ((pos == renderMeshAabbs_.end()) || (pos->entity != renderMeshEntity)) {
493                     pos = renderMeshAabbs_.insert(pos, RenderMeshAaabb { renderMeshEntity, {}, {} });
494                 }
495                 auto& data = *pos;
496                 auto& meshAabb = data.meshAabb;
497                 meshAabb = {};
498                 auto& aabbs = data.submeshAabbs;
499                 aabbs.clear();
500 
501                 // check MaterialComponent::DISABLE_BIT and discard those, check
502                 // MaterialComponent::LightingFlagBits::SHADOW_CASTER_BIT and don't include them in scene bounding.
503                 disabled.clear();
504                 disabled.resize(meshData->submeshes.size());
505                 shadowCaster.clear();
506                 shadowCaster.resize(meshData->submeshes.size());
507                 auto submeshIdx = 0U;
508                 bool allowInstancing = true;
509                 for (const auto& submesh : meshData->submeshes) {
510                     if (EntityUtil::IsValid(submesh.material)) {
511                         if (auto matPos = GetMaterialProperties(submesh.material)) {
512                             disabled[submeshIdx] = matPos->disabled;
513                             allowInstancing = allowInstancing && matPos->allowInstancing;
514                             shadowCaster[submeshIdx] = matPos->shadowCaster;
515                         }
516                     } else {
517                         // assuming MaterialComponent::materialLightingFlags default value includes
518                         // SHADOW_CASTER_BIT
519                         shadowCaster[submeshIdx] = true;
520                     }
521                     for (const auto additionalMaterial : submesh.additionalMaterials) {
522                         if (EntityUtil::IsValid(additionalMaterial)) {
523                             if (auto matPos = GetMaterialProperties(additionalMaterial)) {
524                                 disabled[submeshIdx] = matPos->disabled;
525                                 allowInstancing = allowInstancing && matPos->allowInstancing;
526                                 shadowCaster[submeshIdx] = matPos->shadowCaster;
527                             }
528                         } else {
529                             // assuming MaterialComponent::materialLightingFlags default value includes
530                             // SHADOW_CASTER_BIT
531                             shadowCaster[submeshIdx] = true;
532                         }
533                     }
534                     ++submeshIdx;
535                 }
536                 data.shadowCaster = std::any_of(
537                     shadowCaster.cbegin(), shadowCaster.cend(), [](const bool shadowCaster) { return shadowCaster; });
538 
539                 if (std::any_of(disabled.cbegin(), disabled.cend(), [](const bool disabled) { return !disabled; })) {
540                     bool hasJoints = row.IsValidComponentId(JMC);
541                     if (hasJoints) {
542                         auto jointMatricesHandle = jointMatricesManager_->Read(row.components[JMC]);
543                         hasJoints = (jointMatricesHandle->count > 0U);
544                         if (hasJoints) {
545                             aabbs.push_back(
546                                 Aabb { jointMatricesHandle->jointsAabbMin, jointMatricesHandle->jointsAabbMax });
547                             meshAabb.min = Math::min(meshAabb.min, jointMatricesHandle->jointsAabbMin);
548                             meshAabb.max = Math::max(meshAabb.max, jointMatricesHandle->jointsAabbMax);
549                         }
550                     }
551                     if (!hasJoints) {
552                         submeshIdx = 0U;
553                         const auto& world = worldMatrixManager_->Read(row.components[WMC])->matrix;
554                         for (const auto& submesh : meshData->submeshes) {
555                             if (disabled[submeshIdx]) {
556                                 aabbs.push_back({});
557                             } else {
558                                 const MinAndMax mam = picking_->GetWorldAABB(world, submesh.aabbMin, submesh.aabbMax);
559                                 aabbs.push_back({ mam.minAABB, mam.maxAABB });
560                                 meshAabb.min = Math::min(meshAabb.min, mam.minAABB);
561                                 meshAabb.max = Math::max(meshAabb.max, mam.maxAABB);
562                             }
563                             ++submeshIdx;
564                         }
565                     }
566 
567                     auto skin = (row.IsValidComponentId(SC)) ? skinManager_->Read(row.components[SC])->skin : Entity {};
568                     meshComponents_.push_back({ row.components[RMC], renderMeshHandle->mesh,
569                         renderMeshHandle->renderMeshBatch, skin, allowInstancing });
570                 }
571             }
572         }
573     }
574 }
575 
UpdateMaterialProperties()576 void RenderPreprocessorSystem::UpdateMaterialProperties()
577 {
578     // assuming modifiedMaterials was sorted we can assume the next entity is between pos and end.
579     auto pos = materialProperties_.begin();
580     for (const auto& entity : materialModifiedEvents_) {
581         auto materialHandle = materialManager_->Read(entity);
582         if (!materialHandle) {
583             continue;
584         }
585         pos = std::lower_bound(pos, materialProperties_.end(), entity);
586         if ((pos == materialProperties_.end()) || (pos->material != entity)) {
587             pos = materialProperties_.insert(pos, {});
588             pos->material = entity;
589         }
590         pos->disabled =
591             (materialHandle->extraRenderingFlags & MaterialComponent::ExtraRenderingFlagBits::DISABLE_BIT) ==
592             MaterialComponent::ExtraRenderingFlagBits::DISABLE_BIT;
593         pos->allowInstancing = ((materialHandle->extraRenderingFlags &
594                                     MaterialComponent::ExtraRenderingFlagBits::ALLOW_GPU_INSTANCING_BIT) ==
595                                    MaterialComponent::ExtraRenderingFlagBits::ALLOW_GPU_INSTANCING_BIT) ||
596                                !EntityUtil::IsValid(materialHandle->materialShader.shader);
597         pos->shadowCaster =
598             (materialHandle->materialLightingFlags & MaterialComponent::LightingFlagBits::SHADOW_CASTER_BIT) ==
599             MaterialComponent::LightingFlagBits::SHADOW_CASTER_BIT;
600 
601         // create/update rendering side decoupled material data
602         UpdateSingleMaterial(entity, &(*materialHandle));
603     }
604 }
605 
UpdateSingleMaterial(const Entity matEntity,const MaterialComponent * materialHandle)606 void RenderPreprocessorSystem::UpdateSingleMaterial(const Entity matEntity, const MaterialComponent* materialHandle)
607 {
608     const MaterialComponent& materialComp = (materialHandle) ? *materialHandle : DEF_MATERIAL_COMPONENT;
609     RenderDataDefaultMaterial::InputMaterialUniforms materialUniforms =
610         InputMaterialUniformsFromMaterialComponent(matEntity, materialComp);
611 
612     // NOTE: we force material updates, no early outs
613 
614     array_view<const uint8_t> customData;
615     if (materialComp.customProperties) {
616         const auto buffer = static_cast<const uint8_t*>(materialComp.customProperties->RLock());
617         // NOTE: set and binding are currently not supported, we only support built-in mapping
618         // the data goes to a predefined set and binding
619         customData = array_view(buffer, materialComp.customProperties->Size());
620         materialComp.customProperties->RUnlock();
621     }
622     // material extensions
623     RenderHandleReference handleReferences[RenderDataDefaultMaterial::MAX_MATERIAL_CUSTOM_RESOURCE_COUNT];
624     array_view<RenderHandleReference> extHandles;
625     // extension valid only with non-default material
626     if (EntityUtil::IsValid(matEntity)) {
627         // first check the preferred vector version
628         if (!materialComp.customResources.empty()) {
629             const size_t maxCount = Math::min(static_cast<size_t>(materialComp.customResources.size()),
630                 static_cast<size_t>(RenderDataDefaultMaterial::MAX_MATERIAL_CUSTOM_RESOURCE_COUNT));
631             extHandles = { handleReferences, maxCount };
632             GetRenderHandleReferences(*renderHandleManager_, materialComp.customResources, extHandles);
633         }
634     }
635     const uint32_t transformBits = materialUniforms.texTransformSetBits;
636     const RenderMaterialFlags rmfFromBits =
637         RenderMaterialLightingFlagsFromMaterialFlags(materialComp.materialLightingFlags);
638     {
639         const RenderDataDefaultMaterial::MaterialHandlesWithHandleReference materialHandles =
640             GetMaterialHandles(materialComp, *renderHandleManager_);
641         const RenderMaterialFlags rmfFromValues =
642             RenderMaterialFlagsFromMaterialValues(materialComp, materialHandles, transformBits);
643         const RenderMaterialFlags rmf = rmfFromBits | rmfFromValues;
644         const RenderDataDefaultMaterial::MaterialData data { RenderMaterialType(materialComp.type),
645             materialComp.extraRenderingFlags, rmf, materialComp.customRenderSlotId,
646             { renderHandleManager_->GetRenderHandleReference(materialComp.materialShader.shader),
647                 renderHandleManager_->GetRenderHandleReference(materialComp.materialShader.graphicsState) },
648             { renderHandleManager_->GetRenderHandleReference(materialComp.depthShader.shader),
649                 renderHandleManager_->GetRenderHandleReference(materialComp.depthShader.graphicsState) },
650             materialComp.renderSort.renderSortLayer, materialComp.renderSort.renderSortLayerOrder };
651 
652         dsMaterial_->UpdateMaterialData(matEntity.id, materialUniforms, materialHandles, data, customData, extHandles);
653     }
654 }
655 
GetMaterialProperties(CORE_NS::Entity matEntity)656 RenderPreprocessorSystem::MaterialProperties* RenderPreprocessorSystem::GetMaterialProperties(CORE_NS::Entity matEntity)
657 {
658     auto matPos = std::lower_bound(materialProperties_.begin(), materialProperties_.end(), matEntity,
659         [](const MaterialProperties& element, const Entity& value) { return element.material.id < value.id; });
660     if ((matPos != materialProperties_.end()) && (matPos->material == matEntity)) {
661         // material's properties were cached
662         return &(*matPos);
663     }
664 
665     // material is unknown (created by a system after ProcessEvents), try to cache the properties
666     auto materialHandle = materialManager_->Read(matEntity);
667     if (!materialHandle) {
668         // entity didn't have a material component
669         return nullptr;
670     }
671 
672     matPos = materialProperties_.insert(matPos, {});
673     matPos->material = matEntity;
674     matPos->disabled = (materialHandle->extraRenderingFlags & MaterialComponent::ExtraRenderingFlagBits::DISABLE_BIT) ==
675                        MaterialComponent::ExtraRenderingFlagBits::DISABLE_BIT;
676     matPos->allowInstancing =
677         ((materialHandle->extraRenderingFlags & MaterialComponent::ExtraRenderingFlagBits::ALLOW_GPU_INSTANCING_BIT) ==
678             MaterialComponent::ExtraRenderingFlagBits::ALLOW_GPU_INSTANCING_BIT) ||
679         !EntityUtil::IsValid(materialHandle->materialShader.shader);
680     matPos->shadowCaster =
681         (materialHandle->materialLightingFlags & MaterialComponent::LightingFlagBits::SHADOW_CASTER_BIT) ==
682         MaterialComponent::LightingFlagBits::SHADOW_CASTER_BIT;
683 
684     // create/update rendering side decoupled material data
685     UpdateSingleMaterial(matEntity, &(*materialHandle));
686 
687     return &(*matPos);
688 }
689 
GetECS() const690 const IEcs& RenderPreprocessorSystem::GetECS() const
691 {
692     return ecs_;
693 }
694 
GetRenderBatchMeshEntities() const695 array_view<const Entity> RenderPreprocessorSystem::GetRenderBatchMeshEntities() const
696 {
697     return renderBatchComponents_;
698 }
699 
GetInstancingAllowedEntities() const700 array_view<const Entity> RenderPreprocessorSystem::GetInstancingAllowedEntities() const
701 {
702     return instancingAllowed_;
703 }
704 
GetInstancingDisabledEntities() const705 array_view<const Entity> RenderPreprocessorSystem::GetInstancingDisabledEntities() const
706 {
707     return rest_;
708 }
709 
GetRenderMeshAabb(Entity renderMesh) const710 RenderPreprocessorSystem::Aabb RenderPreprocessorSystem::GetRenderMeshAabb(Entity renderMesh) const
711 {
712     const auto pos = std::lower_bound(renderMeshAabbs_.cbegin(), renderMeshAabbs_.cend(), renderMesh,
713         [](const RenderMeshAaabb& elem, const Entity& value) { return elem.entity < value; });
714     if ((pos != renderMeshAabbs_.cend()) && (pos->entity == renderMesh)) {
715         return pos->meshAabb;
716     }
717     return {};
718 }
719 
GetRenderMeshAabbs(Entity renderMesh) const720 array_view<const RenderPreprocessorSystem::Aabb> RenderPreprocessorSystem::GetRenderMeshAabbs(Entity renderMesh) const
721 {
722     const auto pos = std::lower_bound(renderMeshAabbs_.cbegin(), renderMeshAabbs_.cend(), renderMesh,
723         [](const RenderMeshAaabb& elem, const Entity& value) { return elem.entity < value; });
724     if ((pos != renderMeshAabbs_.cend()) && (pos->entity == renderMesh)) {
725         return pos->submeshAabbs;
726     }
727     return {};
728 }
729 
GetBoundingSphere() const730 RenderPreprocessorSystem::Sphere RenderPreprocessorSystem::GetBoundingSphere() const
731 {
732     return boundingSphere_;
733 }
734 
Initialize()735 void RenderPreprocessorSystem::Initialize()
736 {
737     if (graphicsContext_ && renderContext_) {
738         SetDataStorePointers(renderContext_->GetRenderDataStoreManager());
739     }
740     if (renderMeshManager_ && nodeManager_ && layerManager_) {
741         const ComponentQuery::Operation operations[] = {
742             { *nodeManager_, ComponentQuery::Operation::REQUIRE },
743             { *worldMatrixManager_, ComponentQuery::Operation::REQUIRE },
744             { *layerManager_, ComponentQuery::Operation::OPTIONAL },
745             { *jointMatricesManager_, ComponentQuery::Operation::OPTIONAL },
746             { *skinManager_, ComponentQuery::Operation::OPTIONAL },
747         };
748         renderableQuery_.SetEcsListenersEnabled(true);
749         renderableQuery_.SetupQuery(*renderMeshManager_, operations, false);
750     }
751     ecs_.AddListener(*renderMeshManager_, *this);
752     ecs_.AddListener(*materialManager_, *this);
753 }
754 
Update(bool frameRenderingQueued,uint64_t totalTime,uint64_t deltaTime)755 bool RenderPreprocessorSystem::Update(bool frameRenderingQueued, uint64_t totalTime, uint64_t deltaTime)
756 {
757     if (!active_) {
758         return false;
759     }
760 
761     const bool queryChanged = renderableQuery_.Execute();
762 
763     const auto jointGen = jointMatricesManager_->GetGenerationCounter();
764     const auto layerGen = layerManager_->GetGenerationCounter();
765     const auto materialGen = materialManager_->GetGenerationCounter();
766     const auto meshGen = meshManager_->GetGenerationCounter();
767     const auto nodeGen = nodeManager_->GetGenerationCounter();
768     const auto renderMeshGen = renderMeshManager_->GetGenerationCounter();
769     const auto worldMatrixGen = worldMatrixManager_->GetGenerationCounter();
770     if (!queryChanged && (jointGeneration_ == jointGen) && (layerGeneration_ == layerGen) &&
771         (materialGeneration_ == materialGen) && (meshGeneration_ == meshGen) && (nodeGeneration_ == nodeGen) &&
772         (renderMeshGeneration_ == renderMeshGen) && (worldMatrixGeneration_ == worldMatrixGen)) {
773         return false;
774     }
775     if (!materialModifiedEvents_.empty() || !materialDestroyedEvents_.empty()) {
776         HandleMaterialEvents();
777     }
778 
779     // gather which mesh is used by each render mesh component.
780     GatherSortData();
781 
782     jointGeneration_ = jointGen;
783     layerGeneration_ = layerGen;
784     materialGeneration_ = materialGen;
785     meshGeneration_ = meshGen;
786     nodeGeneration_ = nodeGen;
787     renderMeshGeneration_ = renderMeshGen;
788     worldMatrixGeneration_ = worldMatrixGen;
789 #if (CORE3D_DEV_ENABLED == 1)
790     CORE_CPU_PERF_BEGIN(
791         renderMeshBatch, "CORE3D", "RenderPreprocessorSystem", "SortRenderMeshBatches", CORE3D_PROFILER_DEFAULT_COLOR);
792 #endif
793     // reorder the list so that entities with render mesh batch are first sorted by entity id
794     const auto noBatchComponent = std::partition(meshComponents_.begin(), meshComponents_.end(),
795         [](const SortData& value) { return EntityUtil::IsValid(value.batch); });
796     std::sort(meshComponents_.begin(), noBatchComponent,
797         [](const SortData& lhs, const SortData& rhs) { return lhs.renderMeshId < rhs.renderMeshId; });
798 #if (CORE3D_DEV_ENABLED == 1)
799     CORE_CPU_PERF_END(renderMeshBatch);
800     CORE_CPU_PERF_BEGIN(
801         meshBatch, "CORE3D", "RenderPreprocessorSystem", "SortMeshBatches", CORE3D_PROFILER_DEFAULT_COLOR);
802 #endif
803     // next are meshes that can be instanced sorted by mesh id, skin id and entity id
804     auto noInstancing = std::partition(
805         noBatchComponent, meshComponents_.end(), [](const SortData& value) { return value.allowInstancing; });
806     std::sort(noBatchComponent, noInstancing, [](const SortData& lhs, const SortData& rhs) {
807         if (lhs.mesh < rhs.mesh) {
808             return true;
809         }
810         if (lhs.mesh > rhs.mesh) {
811             return false;
812         }
813         if (lhs.skin < rhs.skin) {
814             return true;
815         }
816         if (lhs.skin > rhs.skin) {
817             return false;
818         }
819         return lhs.renderMeshId < rhs.renderMeshId;
820     });
821 #if (CORE3D_DEV_ENABLED == 1)
822     CORE_CPU_PERF_END(meshBatch);
823 #endif
824 
825     // move entities, which could be instanced but have only one instance, to the end
826     {
827         auto begin = reverse_iterator(noInstancing);
828         auto end = reverse_iterator(noBatchComponent);
829         for (auto it = begin; it != end;) {
830             auto pos = std::adjacent_find(it, end, [](const SortData& lhs, const SortData& rhs) {
831                 return (lhs.mesh.id != rhs.mesh.id) || (lhs.skin.id != rhs.skin.id);
832             });
833             if (pos != end) {
834                 // pos points to the first element where comparison failed i.e. pos is the last of previous batch
835                 // and pos+1 is the first of the next batch
836                 ++pos;
837             }
838             // if the batch size is 1, move the single entry to the end
839             if (const auto batchSize = pos - it; batchSize == 1U) {
840                 auto i = std::rotate(pos.base(), pos.base() + 1, begin.base());
841                 begin = reverse_iterator(i);
842             }
843 
844             it = pos;
845         }
846         noInstancing = begin.base();
847     }
848 
849 #if (CORE3D_DEV_ENABLED == 1)
850     CORE_CPU_PERF_BEGIN(
851         nonBatched, "CORE3D", "RenderPreprocessorSystem", "SortNonBatched", CORE3D_PROFILER_DEFAULT_COLOR);
852 #endif
853     // finally sort the non instanced by entity id
854     std::sort(noInstancing, meshComponents_.end(),
855         [](const SortData& lhs, const SortData& rhs) { return lhs.renderMeshId < rhs.renderMeshId; });
856 #if (CORE3D_DEV_ENABLED == 1)
857     CORE_CPU_PERF_END(nonBatched);
858 #endif
859 
860     // list the entities with render mesh components in sorted order.
861     renderMeshComponents_.clear();
862     const auto meshCount = meshComponents_.size();
863     renderMeshComponents_.reserve(meshCount);
864     std::transform(meshComponents_.cbegin(), meshComponents_.cend(), std::back_inserter(renderMeshComponents_),
865         [renderMeshManager = renderMeshManager_](
866             const SortData& data) { return renderMeshManager->GetEntity(data.renderMeshId); });
867     renderBatchComponents_ =
868         array_view(renderMeshComponents_.data(), static_cast<size_t>(noBatchComponent - meshComponents_.begin()));
869     instancingAllowed_ = array_view(renderMeshComponents_.data() + renderBatchComponents_.size(),
870         static_cast<size_t>(noInstancing - noBatchComponent));
871     rest_ = array_view(renderMeshComponents_.data() + renderBatchComponents_.size() + instancingAllowed_.size(),
872         static_cast<size_t>(meshComponents_.end() - noInstancing));
873 
874     CalculateSceneBounds();
875 
876     return true;
877 }
878 
Uninitialize()879 void RenderPreprocessorSystem::Uninitialize()
880 {
881     ecs_.RemoveListener(*renderMeshManager_, *this);
882     ecs_.RemoveListener(*materialManager_, *this);
883     renderableQuery_.SetEcsListenersEnabled(false);
884 }
885 
IRenderPreprocessorSystemInstance(IEcs & ecs)886 ISystem* IRenderPreprocessorSystemInstance(IEcs& ecs)
887 {
888     return new RenderPreprocessorSystem(ecs);
889 }
890 
IRenderPreprocessorSystemDestroy(ISystem * instance)891 void IRenderPreprocessorSystemDestroy(ISystem* instance)
892 {
893     delete static_cast<RenderPreprocessorSystem*>(instance);
894 }
895 CORE3D_END_NAMESPACE()
896