• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022 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/graphics_state_component.h>
21 #include <3d/ecs/components/joint_matrices_component.h>
22 #include <3d/ecs/components/layer_component.h>
23 #include <3d/ecs/components/material_component.h>
24 #include <3d/ecs/components/mesh_component.h>
25 #include <3d/ecs/components/node_component.h>
26 #include <3d/ecs/components/render_handle_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/default_material_constants.h>
32 #include <3d/render/intf_render_data_store_default_camera.h>
33 #include <3d/render/intf_render_data_store_default_light.h>
34 #include <3d/render/intf_render_data_store_default_material.h>
35 #include <3d/render/intf_render_data_store_default_scene.h>
36 #include <3d/render/intf_render_data_store_morph.h>
37 #include <3d/util/intf_picking.h>
38 #include <core/ecs/intf_ecs.h>
39 #include <core/ecs/intf_entity_manager.h>
40 #include <core/implementation_uids.h>
41 #include <core/intf_engine.h>
42 #include <core/log.h>
43 #include <core/namespace.h>
44 #include <core/perf/cpu_perf_scope.h>
45 #include <core/perf/intf_performance_data_manager.h>
46 #include <core/plugin/intf_plugin_register.h>
47 #include <core/property/scoped_handle.h>
48 #include <core/property_tools/property_api_impl.inl>
49 #include <core/property_tools/property_macros.h>
50 #include <render/implementation_uids.h>
51 
52 #include "util/component_util_functions.h"
53 #include "util/log.h"
54 
55 CORE_BEGIN_NAMESPACE()
56 DECLARE_PROPERTY_TYPE(RENDER_NS::IRenderDataStoreManager*);
57 CORE_END_NAMESPACE()
58 
59 CORE3D_BEGIN_NAMESPACE()
60 using namespace RENDER_NS;
61 using namespace BASE_NS;
62 using namespace CORE_NS;
63 
64 PROPERTY_LIST(IRenderPreprocessorSystem::Properties, ComponentMetadata,
65     MEMBER_PROPERTY(dataStoreScene, "dataStoreScene", 0), MEMBER_PROPERTY(dataStoreCamera, "dataStoreCamera", 0),
66     MEMBER_PROPERTY(dataStoreLight, "dataStoreLight", 0), MEMBER_PROPERTY(dataStoreMaterial, "dataStoreMaterial", 0),
67     MEMBER_PROPERTY(dataStoreMorph, "dataStoreMorph", 0), MEMBER_PROPERTY(dataStorePrefix, "", 0));
68 
operator <(const RenderPreprocessorSystem::MaterialProperties & lhs,const RenderPreprocessorSystem::MaterialProperties & rhs)69 constexpr bool operator<(
70     const RenderPreprocessorSystem::MaterialProperties& lhs, const RenderPreprocessorSystem::MaterialProperties& rhs)
71 {
72     return lhs.material.id < rhs.material.id;
73 }
74 
operator <(const RenderPreprocessorSystem::MaterialProperties & element,const Entity & value)75 constexpr bool operator<(const RenderPreprocessorSystem::MaterialProperties& element, const Entity& value)
76 {
77     return element.material.id < value.id;
78 }
79 
operator <(const Entity & element,const RenderPreprocessorSystem::MaterialProperties & value)80 constexpr bool operator<(const Entity& element, const RenderPreprocessorSystem::MaterialProperties& value)
81 {
82     return element.id < value.material.id;
83 }
84 
85 namespace {
86 static constexpr string_view DEFAULT_DS_SCENE_NAME { "RenderDataStoreDefaultScene" };
87 static constexpr string_view DEFAULT_DS_CAMERA_NAME { "RenderDataStoreDefaultCamera" };
88 static constexpr string_view DEFAULT_DS_LIGHT_NAME { "RenderDataStoreDefaultLight" };
89 static constexpr string_view DEFAULT_DS_MATERIAL_NAME { "RenderDataStoreDefaultMaterial" };
90 static constexpr string_view DEFAULT_DS_MORPH_NAME { "RenderDataStoreMorph" };
91 
92 constexpr const auto RMC = 0U;
93 constexpr const auto NC = 1U;
94 constexpr const auto WMC = 2U;
95 constexpr const auto LC = 3U;
96 constexpr const auto JMC = 4U;
97 constexpr const auto SC = 5U;
98 
99 static const MaterialComponent DEF_MATERIAL_COMPONENT {};
100 static constexpr RenderDataDefaultMaterial::InputMaterialUniforms DEF_INPUT_MATERIAL_UNIFORMS {};
101 
102 struct SceneBoundingVolumeHelper {
103     BASE_NS::Math::Vec3 sumOfSubmeshPoints { 0.0f, 0.0f, 0.0f };
104     uint32_t submeshCount { 0 };
105 
106     BASE_NS::Math::Vec3 minAABB { std::numeric_limits<float>::max(), std::numeric_limits<float>::max(),
107         std::numeric_limits<float>::max() };
108     BASE_NS::Math::Vec3 maxAABB { -std::numeric_limits<float>::max(), -std::numeric_limits<float>::max(),
109         -std::numeric_limits<float>::max() };
110 };
111 
112 template<typename DataStoreType>
CreateIfNeeded(IRenderDataStoreManager & manager,refcnt_ptr<DataStoreType> & dataStore,string_view dataStoreName)113 inline auto CreateIfNeeded(
114     IRenderDataStoreManager& manager, refcnt_ptr<DataStoreType>& dataStore, string_view dataStoreName)
115 {
116     if (!dataStore || dataStore->GetName() != dataStoreName) {
117         dataStore = refcnt_ptr<DataStoreType>(manager.Create(DataStoreType::UID, dataStoreName.data()));
118     }
119     return dataStore;
120 }
121 
122 #if (CORE3D_VALIDATION_ENABLED == 1)
ValidateInputColor(const Entity material,const MaterialComponent & matComp)123 void ValidateInputColor(const Entity material, const MaterialComponent& matComp)
124 {
125     if (matComp.type < MaterialComponent::Type::CUSTOM) {
126         const auto& base = matComp.textures[MaterialComponent::TextureIndex::BASE_COLOR];
127         if ((base.factor.x > 1.0f) || (base.factor.y > 1.0f) || (base.factor.z > 1.0f) || (base.factor.w > 1.0f)) {
128             CORE_LOG_ONCE_I("ValidateInputColor_expect_base_colorfactor",
129                 "CORE3D_VALIDATION: Non custom material type expects base color factor to be <= 1.0f.");
130         }
131         const auto& mat = matComp.textures[MaterialComponent::TextureIndex::MATERIAL];
132         if ((mat.factor.y > 1.0f) || (mat.factor.z > 1.0f)) {
133             CORE_LOG_ONCE_I("ValidateInputColor_expect_roughness_metallic_factor",
134                 "CORE3D_VALIDATION: Non custom material type expects roughness and metallic to be <= 1.0f.");
135         }
136     }
137 }
138 #endif
139 
InputMaterialUniformsFromMaterialComponent(const Entity material,const MaterialComponent & matDesc)140 RenderDataDefaultMaterial::InputMaterialUniforms InputMaterialUniformsFromMaterialComponent(
141     const Entity material, const MaterialComponent& matDesc)
142 {
143     RenderDataDefaultMaterial::InputMaterialUniforms mu = DEF_INPUT_MATERIAL_UNIFORMS;
144 
145 #if (CORE3D_VALIDATION_ENABLED == 1)
146     ValidateInputColor(material, matDesc);
147 #endif
148 
149     uint32_t transformBits = 0u;
150     constexpr const uint32_t texCount = Math::min(static_cast<uint32_t>(MaterialComponent::TextureIndex::TEXTURE_COUNT),
151         RenderDataDefaultMaterial::MATERIAL_TEXTURE_COUNT);
152     for (uint32_t idx = 0u; idx < texCount; ++idx) {
153         const auto& tex = matDesc.textures[idx];
154         auto& texRef = mu.textureData[idx];
155         texRef.factor = tex.factor;
156         texRef.translation = tex.transform.translation;
157         texRef.rotation = tex.transform.rotation;
158         texRef.scale = tex.transform.scale;
159         const bool hasTransform = (texRef.translation.x != 0.0f) || (texRef.translation.y != 0.0f) ||
160                                   (texRef.rotation != 0.0f) || (texRef.scale.x != 1.0f) || (texRef.scale.y != 1.0f);
161         transformBits |= static_cast<uint32_t>(hasTransform) << idx;
162     }
163     {
164         // NOTE: premultiplied alpha, applied here and therefore the baseColor factor is special
165         const auto& tex = matDesc.textures[MaterialComponent::TextureIndex::BASE_COLOR];
166         const float alpha = tex.factor.w;
167         const Math::Vec4 baseColor = {
168             tex.factor.x * alpha,
169             tex.factor.y * alpha,
170             tex.factor.z * alpha,
171             alpha,
172         };
173 
174         constexpr uint32_t index = 0u;
175         mu.textureData[index].factor = baseColor;
176     }
177     mu.alphaCutoff = matDesc.alphaCutoff;
178     mu.texCoordSetBits = matDesc.useTexcoordSetBit;
179     mu.texTransformSetBits = transformBits;
180     mu.id = material.id;
181     return mu;
182 }
183 
GetRenderHandleReferences(const IRenderHandleComponentManager & renderHandleMgr,const array_view<const EntityReference> inputs,array_view<RenderHandleReference> & outputs)184 inline void GetRenderHandleReferences(const IRenderHandleComponentManager& renderHandleMgr,
185     const array_view<const EntityReference> inputs, array_view<RenderHandleReference>& outputs)
186 {
187     for (size_t idx = 0; idx < outputs.size(); ++idx) {
188         outputs[idx] = renderHandleMgr.GetRenderHandleReference(inputs[idx]);
189     }
190 }
191 
RenderMaterialLightingFlagsFromMaterialFlags(const MaterialComponent::LightingFlags materialFlags)192 constexpr uint32_t RenderMaterialLightingFlagsFromMaterialFlags(const MaterialComponent::LightingFlags materialFlags)
193 {
194     uint32_t rmf = 0;
195     if (materialFlags & MaterialComponent::LightingFlagBits::SHADOW_RECEIVER_BIT) {
196         rmf |= RenderMaterialFlagBits::RENDER_MATERIAL_SHADOW_RECEIVER_BIT;
197     }
198     if (materialFlags & MaterialComponent::LightingFlagBits::SHADOW_CASTER_BIT) {
199         rmf |= RenderMaterialFlagBits::RENDER_MATERIAL_SHADOW_CASTER_BIT;
200     }
201     if (materialFlags & MaterialComponent::LightingFlagBits::PUNCTUAL_LIGHT_RECEIVER_BIT) {
202         rmf |= RenderMaterialFlagBits::RENDER_MATERIAL_PUNCTUAL_LIGHT_RECEIVER_BIT;
203     }
204     if (materialFlags & MaterialComponent::LightingFlagBits::INDIRECT_LIGHT_RECEIVER_BIT) {
205         rmf |= RenderMaterialFlagBits::RENDER_MATERIAL_INDIRECT_LIGHT_RECEIVER_BIT;
206     }
207     return rmf;
208 }
209 
GetMaterialHandles(const MaterialComponent & materialDesc,const IRenderHandleComponentManager & gpuManager)210 inline RenderDataDefaultMaterial::MaterialHandlesWithHandleReference GetMaterialHandles(
211     const MaterialComponent& materialDesc, const IRenderHandleComponentManager& gpuManager)
212 {
213     RenderDataDefaultMaterial::MaterialHandlesWithHandleReference materialHandles;
214     auto imageIt = std::begin(materialHandles.images);
215     auto samplerIt = std::begin(materialHandles.samplers);
216     for (const MaterialComponent::TextureInfo& info : materialDesc.textures) {
217         *imageIt++ = gpuManager.GetRenderHandleReference(info.image);
218         *samplerIt++ = gpuManager.GetRenderHandleReference(info.sampler);
219     }
220     return materialHandles;
221 }
222 
RenderMaterialFlagsFromMaterialValues(const MaterialComponent & matComp,const RenderDataDefaultMaterial::MaterialHandlesWithHandleReference & handles,const uint32_t hasTransformBit)223 uint32_t RenderMaterialFlagsFromMaterialValues(const MaterialComponent& matComp,
224     const RenderDataDefaultMaterial::MaterialHandlesWithHandleReference& handles, const uint32_t hasTransformBit)
225 {
226     uint32_t rmf = 0;
227     // enable built-in specialization for default materials
228     CORE_ASSERT(matComp.type <= MaterialComponent::Type::CUSTOM_COMPLEX);
229     if (matComp.type < MaterialComponent::Type::CUSTOM) {
230         if (handles.images[MaterialComponent::TextureIndex::NORMAL] ||
231             handles.images[MaterialComponent::TextureIndex::CLEARCOAT_NORMAL]) {
232             // need to check for tangents as well with submesh
233             rmf |= RenderMaterialFlagBits::RENDER_MATERIAL_NORMAL_MAP_BIT;
234         }
235         if (matComp.textures[MaterialComponent::TextureIndex::CLEARCOAT].factor.x > 0.0f) {
236             rmf |= RenderMaterialFlagBits::RENDER_MATERIAL_CLEAR_COAT_BIT;
237         }
238         if ((matComp.textures[MaterialComponent::TextureIndex::SHEEN].factor.x > 0.0f) ||
239             (matComp.textures[MaterialComponent::TextureIndex::SHEEN].factor.y > 0.0f) ||
240             (matComp.textures[MaterialComponent::TextureIndex::SHEEN].factor.z > 0.0f)) {
241             rmf |= RenderMaterialFlagBits::RENDER_MATERIAL_SHEEN_BIT;
242         }
243         if (matComp.textures[MaterialComponent::TextureIndex::SPECULAR].factor != Math::Vec4(1.f, 1.f, 1.f, 1.f) ||
244             handles.images[MaterialComponent::TextureIndex::SPECULAR]) {
245             rmf |= RenderMaterialFlagBits::RENDER_MATERIAL_SPECULAR_BIT;
246         }
247         if (matComp.textures[MaterialComponent::TextureIndex::TRANSMISSION].factor.x > 0.0f) {
248             rmf |= RenderMaterialFlagBits::RENDER_MATERIAL_TRANSMISSION_BIT;
249         }
250     }
251     rmf |= (hasTransformBit > 0U) ? RenderMaterialFlagBits::RENDER_MATERIAL_TEXTURE_TRANSFORM_BIT : 0U;
252     // NOTE: built-in shaders write 1.0 to alpha always when discard is enabled
253     rmf |= (matComp.alphaCutoff < 1.0f) ? RenderMaterialFlagBits::RENDER_MATERIAL_SHADER_DISCARD_BIT : 0U;
254     // NOTE: GPU instancing specialization needs to be enabled during rendering
255     return rmf;
256 }
257 
RemoveMaterialProperties(IRenderDataStoreDefaultMaterial & dsMaterial,const IMaterialComponentManager & materialManager,vector<RenderPreprocessorSystem::MaterialProperties> & materialProperties,array_view<const Entity> removedMaterials)258 void RemoveMaterialProperties(IRenderDataStoreDefaultMaterial& dsMaterial,
259     const IMaterialComponentManager& materialManager,
260     vector<RenderPreprocessorSystem::MaterialProperties>& materialProperties, array_view<const Entity> removedMaterials)
261 {
262     materialProperties.erase(std::set_difference(materialProperties.begin(), materialProperties.end(),
263                                  removedMaterials.cbegin(), removedMaterials.cend(), materialProperties.begin()),
264         materialProperties.cend());
265 
266     // destroy rendering side decoupled material data
267     for (const auto& entRef : removedMaterials) {
268         dsMaterial.DestroyMaterialData(entRef.id);
269     }
270 }
271 
RenderSubmeshFlagsFromMeshFlags(const MeshComponent::Submesh::Flags flags)272 constexpr uint32_t RenderSubmeshFlagsFromMeshFlags(const MeshComponent::Submesh::Flags flags)
273 {
274     uint32_t rmf = 0;
275     if (flags & MeshComponent::Submesh::FlagBits::TANGENTS_BIT) {
276         rmf |= RenderSubmeshFlagBits::RENDER_SUBMESH_TANGENTS_BIT;
277     }
278     if (flags & MeshComponent::Submesh::FlagBits::VERTEX_COLORS_BIT) {
279         rmf |= RenderSubmeshFlagBits::RENDER_SUBMESH_VERTEX_COLORS_BIT;
280     }
281     if (flags & MeshComponent::Submesh::FlagBits::SKIN_BIT) {
282         rmf |= RenderSubmeshFlagBits::RENDER_SUBMESH_SKIN_BIT;
283     }
284     if (flags & MeshComponent::Submesh::FlagBits::SECOND_TEXCOORD_BIT) {
285         rmf |= RenderSubmeshFlagBits::RENDER_SUBMESH_SECOND_TEXCOORD_BIT;
286     }
287     return rmf;
288 }
289 
SetupSubmeshBuffers(const IRenderHandleComponentManager & renderHandleManager,const MeshComponent::Submesh & submesh,RenderSubmeshDataWithHandleReference & renderSubmesh)290 void SetupSubmeshBuffers(const IRenderHandleComponentManager& renderHandleManager,
291     const MeshComponent::Submesh& submesh, RenderSubmeshDataWithHandleReference& renderSubmesh)
292 {
293     CORE_STATIC_ASSERT(
294         MeshComponent::Submesh::BUFFER_COUNT <= RENDER_NS::PipelineStateConstants::MAX_VERTEX_BUFFER_COUNT);
295     // calculate real vertex buffer count and fill "safety" handles for default material
296     // no default shader variants without joints etc.
297     // NOTE: optimize for minimal GetRenderHandleReference calls
298     // often the same vertex buffer is used.
299     Entity prevEntity = {};
300 
301     for (size_t idx = 0; idx < countof(submesh.bufferAccess); ++idx) {
302         const auto& acc = submesh.bufferAccess[idx];
303         auto& vb = renderSubmesh.buffers.vertexBuffers[idx];
304         if (EntityUtil::IsValid(prevEntity) && (prevEntity == acc.buffer)) {
305             vb.bufferHandle = renderSubmesh.buffers.vertexBuffers[idx - 1].bufferHandle;
306             vb.bufferOffset = acc.offset;
307             vb.byteSize = acc.byteSize;
308         } else if (acc.buffer) {
309             vb.bufferHandle = renderHandleManager.GetRenderHandleReference(acc.buffer);
310             vb.bufferOffset = acc.offset;
311             vb.byteSize = acc.byteSize;
312 
313             // store the previous entity
314             prevEntity = acc.buffer;
315         } else {
316             vb.bufferHandle = renderSubmesh.buffers.vertexBuffers[0].bufferHandle; // expecting safety binding
317             vb.bufferOffset = 0;
318             vb.byteSize = 0;
319         }
320     }
321 
322     // NOTE: we will get max amount of vertex buffers if there is at least one
323     renderSubmesh.buffers.vertexBufferCount =
324         submesh.bufferAccess[0U].buffer ? static_cast<uint32_t>(countof(submesh.bufferAccess)) : 0U;
325 
326     if (submesh.indexBuffer.buffer) {
327         renderSubmesh.buffers.indexBuffer.bufferHandle =
328             renderHandleManager.GetRenderHandleReference(submesh.indexBuffer.buffer);
329         renderSubmesh.buffers.indexBuffer.bufferOffset = submesh.indexBuffer.offset;
330         renderSubmesh.buffers.indexBuffer.byteSize = submesh.indexBuffer.byteSize;
331         renderSubmesh.buffers.indexBuffer.indexType = submesh.indexBuffer.indexType;
332     }
333     if (submesh.indirectArgsBuffer.buffer) {
334         renderSubmesh.buffers.indirectArgsBuffer.bufferHandle =
335             renderHandleManager.GetRenderHandleReference(submesh.indirectArgsBuffer.buffer);
336         renderSubmesh.buffers.indirectArgsBuffer.bufferOffset = submesh.indirectArgsBuffer.offset;
337         renderSubmesh.buffers.indirectArgsBuffer.byteSize = submesh.indirectArgsBuffer.byteSize;
338     }
339     renderSubmesh.buffers.inputAssembly = submesh.inputAssembly;
340 }
341 } // namespace
342 
RenderPreprocessorSystem(IEcs & ecs)343 RenderPreprocessorSystem::RenderPreprocessorSystem(IEcs& ecs)
344     : ecs_(ecs), graphicsStateManager_(GetManager<IGraphicsStateComponentManager>(ecs)),
345       jointMatricesManager_(GetManager<IJointMatricesComponentManager>(ecs)),
346       layerManager_(GetManager<ILayerComponentManager>(ecs)),
347       materialManager_(GetManager<IMaterialComponentManager>(ecs)),
348       renderHandleManager_(GetManager<IRenderHandleComponentManager>(ecs)),
349       meshManager_(GetManager<IMeshComponentManager>(ecs)), nodeManager_(GetManager<INodeComponentManager>(ecs)),
350       renderMeshManager_(GetManager<IRenderMeshComponentManager>(ecs)),
351       skinManager_(GetManager<ISkinComponentManager>(ecs)),
352       worldMatrixManager_(GetManager<IWorldMatrixComponentManager>(ecs)),
353       RENDER_PREPROCESSOR_SYSTEM_PROPERTIES(&properties_, array_view(ComponentMetadata))
354 {
355     if (IEngine* engine = ecs_.GetClassFactory().GetInterface<IEngine>(); engine) {
356         renderContext_ = GetInstance<IRenderContext>(*engine->GetInterface<IClassRegister>(), UID_RENDER_CONTEXT);
357         if (renderContext_) {
358             picking_ = GetInstance<IPicking>(*renderContext_->GetInterface<IClassRegister>(), UID_PICKING);
359             graphicsContext_ =
360                 GetInstance<IGraphicsContext>(*renderContext_->GetInterface<IClassRegister>(), UID_GRAPHICS_CONTEXT);
361         }
362     }
363 }
364 
365 RenderPreprocessorSystem::~RenderPreprocessorSystem() = default;
366 
SetActive(bool state)367 void RenderPreprocessorSystem::SetActive(bool state)
368 {
369     active_ = state;
370 }
371 
IsActive() const372 bool RenderPreprocessorSystem::IsActive() const
373 {
374     return active_;
375 }
376 
GetName() const377 string_view RenderPreprocessorSystem::GetName() const
378 {
379     return CORE3D_NS::GetName(this);
380 }
381 
GetUid() const382 Uid RenderPreprocessorSystem::GetUid() const
383 {
384     return UID;
385 }
386 
GetProperties()387 IPropertyHandle* RenderPreprocessorSystem::GetProperties()
388 {
389     return RENDER_PREPROCESSOR_SYSTEM_PROPERTIES.GetData();
390 }
391 
GetProperties() const392 const IPropertyHandle* RenderPreprocessorSystem::GetProperties() const
393 {
394     return RENDER_PREPROCESSOR_SYSTEM_PROPERTIES.GetData();
395 }
396 
SetProperties(const IPropertyHandle & data)397 void RenderPreprocessorSystem::SetProperties(const IPropertyHandle& data)
398 {
399     if (data.Owner() != &RENDER_PREPROCESSOR_SYSTEM_PROPERTIES) {
400         return;
401     }
402     if (const auto in = ScopedHandle<const IRenderPreprocessorSystem::Properties>(&data); in) {
403         properties_.dataStorePrefix = in->dataStorePrefix;
404         properties_.dataStoreScene = properties_.dataStorePrefix + in->dataStoreScene;
405         properties_.dataStoreCamera = properties_.dataStorePrefix + in->dataStoreCamera;
406         properties_.dataStoreLight = properties_.dataStorePrefix + in->dataStoreLight;
407         properties_.dataStoreMaterial = properties_.dataStorePrefix + in->dataStoreMaterial;
408         properties_.dataStoreMorph = properties_.dataStorePrefix + in->dataStoreMorph;
409         if (renderContext_) {
410             SetDataStorePointers(renderContext_->GetRenderDataStoreManager());
411         }
412     }
413 }
414 
OnComponentEvent(EventType type,const IComponentManager & componentManager,BASE_NS::array_view<const Entity> entities)415 void RenderPreprocessorSystem::OnComponentEvent(
416     EventType type, const IComponentManager& componentManager, BASE_NS::array_view<const Entity> entities)
417 {
418     if (componentManager.GetUid() == IMaterialComponentManager::UID) {
419         if ((type == EventType::CREATED) || (type == EventType::MODIFIED)) {
420             materialModifiedEvents_.append(entities.cbegin(), entities.cend());
421         } else if (type == EventType::DESTROYED) {
422             materialDestroyedEvents_.append(entities.cbegin(), entities.cend());
423         }
424     } else if (componentManager.GetUid() == IRenderMeshComponentManager::UID) {
425         if (type == EventType::CREATED) {
426             renderMeshAabbs_.reserve(entities.size());
427             for (const auto& newRenderMesh : entities) {
428                 auto pos = std::lower_bound(renderMeshAabbs_.cbegin(), renderMeshAabbs_.cend(), newRenderMesh,
429                     [](const RenderMeshAaabb& elem, const Entity& value) { return elem.entity < value; });
430                 if ((pos == renderMeshAabbs_.cend()) || (pos->entity != newRenderMesh)) {
431                     renderMeshAabbs_.insert(pos, RenderMeshAaabb { newRenderMesh, {}, {} });
432                 }
433             }
434         } else if (type == EventType::DESTROYED) {
435             for (const auto& removedRenderMesh : entities) {
436                 auto pos = std::lower_bound(renderMeshAabbs_.cbegin(), renderMeshAabbs_.cend(), removedRenderMesh,
437                     [](const RenderMeshAaabb& elem, const Entity& value) { return elem.entity < value; });
438                 if ((pos != renderMeshAabbs_.cend()) && (pos->entity == removedRenderMesh)) {
439                     renderMeshAabbs_.erase(pos);
440                 }
441             }
442         }
443     } else if (componentManager.GetUid() == IMeshComponentManager::UID) {
444         if ((type == EventType::CREATED) || (type == EventType::MODIFIED)) {
445             meshModifiedEvents_.append(entities.cbegin(), entities.cend());
446         } else if (type == EventType::DESTROYED) {
447             meshDestroyedEvents_.append(entities.cbegin(), entities.cend());
448         }
449     } else if (componentManager.GetUid() == IGraphicsStateComponentManager::UID) {
450         if ((type == EventType::CREATED) || (type == EventType::MODIFIED)) {
451             graphicsStateModifiedEvents_.append(entities.cbegin(), entities.cend());
452             graphicsStateGeneration_ = componentManager.GetGenerationCounter();
453         }
454     }
455 }
456 
HandleMaterialEvents()457 void RenderPreprocessorSystem::HandleMaterialEvents()
458 {
459 #if (CORE3D_DEV_ENABLED == 1)
460     CORE_CPU_PERF_SCOPE("CORE3D", "RenderPreprocessorSystem", "HandleMaterialEvents", CORE3D_PROFILER_DEFAULT_COLOR);
461 #endif
462     if (!materialDestroyedEvents_.empty()) {
463         std::sort(materialDestroyedEvents_.begin(), materialDestroyedEvents_.end());
464     }
465 
466     if (!materialModifiedEvents_.empty()) {
467         std::sort(materialModifiedEvents_.begin(), materialModifiedEvents_.end());
468         // creating a component generates created and modified events. filter out materials which were created and
469         // modified.
470         materialModifiedEvents_.erase(std::unique(materialModifiedEvents_.begin(), materialModifiedEvents_.end()),
471             materialModifiedEvents_.cend());
472         if (!materialDestroyedEvents_.empty()) {
473             // filter out materials which were created/modified, but also destroyed.
474             materialModifiedEvents_.erase(std::set_difference(materialModifiedEvents_.cbegin(),
475                                               materialModifiedEvents_.cend(), materialDestroyedEvents_.cbegin(),
476                                               materialDestroyedEvents_.cend(), materialModifiedEvents_.begin()),
477                 materialModifiedEvents_.cend());
478         }
479         UpdateMaterialProperties();
480         materialModifiedEvents_.clear();
481     }
482     if (!materialDestroyedEvents_.empty()) {
483         RemoveMaterialProperties(*dsMaterial_, *materialManager_, materialProperties_, materialDestroyedEvents_);
484         materialDestroyedEvents_.clear();
485     }
486 }
487 
HandleMeshEvents()488 void RenderPreprocessorSystem::HandleMeshEvents()
489 {
490 #if (CORE3D_DEV_ENABLED == 1)
491     CORE_CPU_PERF_SCOPE("CORE3D", "RenderPreprocessorSystem", "HandleMeshEvents", CORE3D_PROFILER_DEFAULT_COLOR);
492 #endif
493     if (!meshModifiedEvents_.empty()) {
494         // creating a component generates created and modified events. filter out materials which were created and
495         // modified.
496         std::sort(meshModifiedEvents_.begin(), meshModifiedEvents_.end());
497         meshModifiedEvents_.erase(
498             std::unique(meshModifiedEvents_.begin(), meshModifiedEvents_.end()), meshModifiedEvents_.cend());
499         for (const auto& destroyRef : meshDestroyedEvents_) {
500             for (auto iter = meshModifiedEvents_.begin(); iter != meshModifiedEvents_.end();) {
501                 if (destroyRef.id == iter->id) {
502                     meshModifiedEvents_.erase(iter);
503                 } else {
504                     ++iter;
505                 }
506             }
507         }
508         vector<uint64_t> additionalMaterials;
509         for (const auto& entRef : meshModifiedEvents_) {
510             if (auto meshHandle = meshManager_->Read(entRef); meshHandle) {
511                 MeshDataWithHandleReference md;
512                 md.aabbMin = meshHandle->aabbMin;
513                 md.aabbMax = meshHandle->aabbMax;
514                 md.meshId = entRef.id;
515                 const bool hasSkin = (!meshHandle->jointBounds.empty());
516                 // md.jointBounds
517                 md.submeshes.resize(meshHandle->submeshes.size());
518                 for (size_t smIdx = 0; smIdx < md.submeshes.size(); smIdx++) {
519                     auto& writeRef = md.submeshes[smIdx];
520                     const auto& readRef = meshHandle->submeshes[smIdx];
521                     writeRef.materialId = readRef.material.id;
522                     if (!readRef.additionalMaterials.empty()) {
523                         additionalMaterials.clear();
524                         additionalMaterials.resize(readRef.additionalMaterials.size());
525                         for (size_t matIdx = 0; matIdx < additionalMaterials.size(); ++matIdx) {
526                             additionalMaterials[matIdx] = readRef.additionalMaterials[matIdx].id;
527                         }
528                         // array view to additional materials
529                         writeRef.additionalMaterials = additionalMaterials;
530                     }
531 
532                     writeRef.meshRenderSortLayer = readRef.renderSortLayer;
533                     writeRef.meshRenderSortLayerOrder = readRef.renderSortLayerOrder;
534                     writeRef.aabbMin = readRef.aabbMin;
535                     writeRef.aabbMax = readRef.aabbMax;
536 
537                     SetupSubmeshBuffers(*renderHandleManager_, readRef, writeRef);
538                     writeRef.submeshFlags = RenderSubmeshFlagsFromMeshFlags(readRef.flags);
539 
540                     // Clear skinning bit if joint matrices were not given.
541                     if (!hasSkin) {
542                         writeRef.submeshFlags &= ~RenderSubmeshFlagBits::RENDER_SUBMESH_SKIN_BIT;
543                     }
544 
545                     writeRef.drawCommand.vertexCount = readRef.vertexCount;
546                     writeRef.drawCommand.indexCount = readRef.indexCount;
547                     writeRef.drawCommand.instanceCount = readRef.instanceCount;
548                     writeRef.drawCommand.drawCountIndirect = readRef.drawCountIndirect;
549                     writeRef.drawCommand.strideIndirect = readRef.strideIndirect;
550                     writeRef.drawCommand.firstIndex = readRef.firstIndex;
551                     writeRef.drawCommand.vertexOffset = readRef.vertexOffset;
552                     writeRef.drawCommand.firstInstance = readRef.firstInstance;
553                 }
554                 dsMaterial_->UpdateMeshData(md.meshId, md);
555             }
556         }
557         meshModifiedEvents_.clear();
558     }
559     if (!meshDestroyedEvents_.empty()) {
560         // destroy rendering side decoupled material data
561         for (const auto& entRef : meshDestroyedEvents_) {
562             dsMaterial_->DestroyMeshData(entRef.id);
563         }
564     }
565 }
566 
HandleGraphicsStateEvents()567 void RenderPreprocessorSystem::HandleGraphicsStateEvents() noexcept
568 {
569     std::sort(graphicsStateModifiedEvents_.begin(), graphicsStateModifiedEvents_.end());
570     graphicsStateModifiedEvents_.erase(
571         std::unique(graphicsStateModifiedEvents_.begin(), graphicsStateModifiedEvents_.end()),
572         graphicsStateModifiedEvents_.cend());
573     auto& shaderManager = renderContext_->GetDevice().GetShaderManager();
574     const auto materialCount = materialManager_->GetComponentCount();
575     for (const auto& modifiedEntity : graphicsStateModifiedEvents_) {
576         auto handle = graphicsStateManager_->Read(modifiedEntity);
577         if (!handle) {
578             continue;
579         }
580         const auto stateHash = shaderManager.HashGraphicsState(handle->graphicsState);
581         auto gsRenderHandleRef = shaderManager.GetGraphicsStateHandleByHash(stateHash);
582         // if the state doesn't match any existing states based on the hash create a new one
583         if (!gsRenderHandleRef) {
584             const auto path = "3dshaderstates://" + to_hex(stateHash);
585             string_view renderSlot = handle->renderSlot;
586             if (renderSlot.empty()) {
587                 // if no render slot is given select translucent or opaque based on blend state.
588                 renderSlot = std::any_of(handle->graphicsState.colorBlendState.colorAttachments,
589                                  handle->graphicsState.colorBlendState.colorAttachments +
590                                      handle->graphicsState.colorBlendState.colorAttachmentCount,
591                                  [](const GraphicsState::ColorBlendState::Attachment& attachment) {
592                                      return attachment.enableBlend;
593                                  })
594                                  ? DefaultMaterialShaderConstants::RENDER_SLOT_FORWARD_TRANSLUCENT
595                                  : DefaultMaterialShaderConstants::RENDER_SLOT_FORWARD_OPAQUE;
596             }
597             IShaderManager::GraphicsStateCreateInfo createInfo { path, handle->graphicsState };
598             IShaderManager::GraphicsStateVariantCreateInfo variantCreateInfo;
599             variantCreateInfo.renderSlot = renderSlot;
600             gsRenderHandleRef = shaderManager.CreateGraphicsState(createInfo, variantCreateInfo);
601         }
602         if (gsRenderHandleRef) {
603             // when there's render handle for the state check that there's also a RenderHandleComponent which points to
604             // the render handle.
605             auto rhHandle = renderHandleManager_->Write(modifiedEntity);
606             if (!rhHandle) {
607                 renderHandleManager_->Create(modifiedEntity);
608                 rhHandle = renderHandleManager_->Write(modifiedEntity);
609             }
610             if (rhHandle) {
611                 rhHandle->reference = gsRenderHandleRef;
612             }
613         }
614         // add any material using the state to the list of modified materials, so that we update the material to render
615         // data store.
616         for (IComponentManager::ComponentId id = 0U; id < materialCount; ++id) {
617             if (auto materialHandle = materialManager_->Read(id)) {
618                 if (materialHandle->materialShader.graphicsState == modifiedEntity ||
619                     materialHandle->depthShader.graphicsState == modifiedEntity) {
620                     materialModifiedEvents_.push_back(materialManager_->GetEntity(id));
621                 }
622             }
623         }
624     }
625     graphicsStateModifiedEvents_.clear();
626 }
627 
SetDataStorePointers(IRenderDataStoreManager & manager)628 void RenderPreprocessorSystem::SetDataStorePointers(IRenderDataStoreManager& manager)
629 {
630     // creates own data stores based on names (the data store name will have the prefix)
631     dsScene_ = CreateIfNeeded(manager, dsScene_, properties_.dataStoreScene);
632     dsCamera_ = CreateIfNeeded(manager, dsCamera_, properties_.dataStoreCamera);
633     dsLight_ = CreateIfNeeded(manager, dsLight_, properties_.dataStoreLight);
634     dsMaterial_ = CreateIfNeeded(manager, dsMaterial_, properties_.dataStoreMaterial);
635     dsMorph_ = CreateIfNeeded(manager, dsMorph_, properties_.dataStoreMorph);
636 }
637 
CalculateSceneBounds()638 void RenderPreprocessorSystem::CalculateSceneBounds()
639 {
640     SceneBoundingVolumeHelper helper;
641 
642     for (const auto& i : renderMeshAabbs_) {
643         // the mesh aabb will have default value if all the submeshes were skipped. in the default value min > max.
644         // meshes which don't cast shadows are also skipped from the scene bounds.
645         if ((i.meshAabb.min.x < i.meshAabb.max.x) && i.shadowCaster) {
646             helper.sumOfSubmeshPoints += (i.meshAabb.min + i.meshAabb.max) / 2.f;
647             helper.minAABB = Math::min(helper.minAABB, i.meshAabb.min);
648             helper.maxAABB = Math::max(helper.maxAABB, i.meshAabb.max);
649             ++helper.submeshCount;
650         }
651     }
652 
653     if (helper.submeshCount == 0) {
654         boundingSphere_.radius = 0.0f;
655         boundingSphere_.center = Math::Vec3();
656     } else {
657         const auto boundingSpherePosition = helper.sumOfSubmeshPoints / static_cast<float>(helper.submeshCount);
658 
659         const float radMin = Math::Magnitude(boundingSpherePosition - helper.minAABB);
660         const float radMax = Math::Magnitude(helper.maxAABB - boundingSpherePosition);
661         const float boundingSphereRadius = Math::max(radMin, radMax);
662 
663         // Compensate jitter and adjust scene bounding sphere only if change in bounds is meaningful.
664         if (boundingSphere_.radius > 0.0f) {
665             // Calculate distance to new bounding sphere origin from current sphere.
666             const float pointDistance = Math::Magnitude(boundingSpherePosition - boundingSphere_.center);
667             // Calculate distance to edge of new bounding sphere from current sphere origin.
668             const float sphereEdgeDistance = pointDistance + boundingSphereRadius;
669 
670             // Calculate step size for adjustment, use 10% granularity from current bounds.
671             constexpr float granularityPct = 0.10f;
672             const float granularity = boundingSphere_.radius * granularityPct;
673 
674             // Calculate required change of size, in order to fit new sphere inside current bounds.
675             const float radDifference = sphereEdgeDistance - boundingSphere_.radius;
676             const float posDifference = Math::Magnitude(boundingSpherePosition - boundingSphere_.center);
677             // We need to adjust only if the change is bigger than the step size.
678             if ((Math::abs(radDifference) > granularity) || (posDifference > granularity)) {
679                 // Calculate how many steps we need to change and in to which direction.
680                 const float radAmount = ceil((boundingSphereRadius - boundingSphere_.radius) / granularity);
681                 const int32_t posAmount = (int32_t)ceil(posDifference / granularity);
682                 if ((radAmount != 0.f) || (posAmount != 0)) {
683                     // Update size and position of the bounds.
684                     boundingSphere_.center = boundingSpherePosition;
685                     boundingSphere_.radius = boundingSphere_.radius + (radAmount * granularity);
686                 }
687             }
688         } else {
689             // No existing bounds, start with new values.
690             boundingSphere_.radius = boundingSphereRadius;
691             boundingSphere_.center = boundingSpherePosition;
692         }
693     }
694 }
695 struct NodeData {
696     bool effectivelyEnabled;
697     uint32_t sceneId;
698 };
GatherSortData()699 void RenderPreprocessorSystem::GatherSortData()
700 {
701 #if (CORE3D_DEV_ENABLED == 1)
702     CORE_CPU_PERF_SCOPE("CORE3D", "RenderPreprocessorSystem", "GatherSortData", CORE3D_PROFILER_DEFAULT_COLOR);
703 #endif
704     const auto& results = renderableQuery_.GetResults();
705     const auto renderMeshes = static_cast<IComponentManager::ComponentId>(results.size());
706     meshComponents_.clear();
707     meshComponents_.reserve(renderMeshes);
708     renderMeshAabbs_.reserve(renderMeshes);
709 
710     vector<bool> disabled;
711     vector<bool> shadowCaster;
712     for (const auto& row : results) {
713         // this list needs to update only when render mesh, node, or layer component have changed
714         const auto nodeData = [](INodeComponentManager* nodeManager, IComponentManager::ComponentId id) {
715             auto handle = nodeManager->Read(id);
716             return NodeData { handle->effectivelyEnabled, handle->sceneId };
717         }(nodeManager_, row.components[NC]);
718         const uint64_t layerMask = !row.IsValidComponentId(LC) ? LayerConstants::DEFAULT_LAYER_MASK
719                                                                : layerManager_->Get(row.components[LC]).layerMask;
720         if (nodeData.effectivelyEnabled && (layerMask != LayerConstants::NONE_LAYER_MASK)) {
721             auto renderMeshHandle = renderMeshManager_->Read(row.components[RMC]);
722             // gather the submesh world aabbs
723             if (const auto meshData = meshManager_->Read(renderMeshHandle->mesh); meshData) {
724                 // render system doesn't necessarily have to read the render mesh components. preprocessor
725                 // could offer two lists of mesh+world, one containing the render mesh batch style meshes and second
726                 // containing regular meshes
727                 const auto renderMeshEntity = renderMeshManager_->GetEntity(row.components[RMC]);
728 
729                 auto pos = std::lower_bound(renderMeshAabbs_.begin(), renderMeshAabbs_.end(), renderMeshEntity,
730                     [](const RenderMeshAaabb& elem, const Entity& value) { return elem.entity < value; });
731                 if ((pos == renderMeshAabbs_.end()) || (pos->entity != renderMeshEntity)) {
732                     pos = renderMeshAabbs_.insert(pos, RenderMeshAaabb { renderMeshEntity, {}, {} });
733                 }
734                 auto& data = *pos;
735                 auto& meshAabb = data.meshAabb;
736                 meshAabb = {};
737                 auto& aabbs = data.submeshAabbs;
738                 aabbs.clear();
739 
740                 // check MaterialComponent::DISABLE_BIT and discard those, check
741                 // MaterialComponent::LightingFlagBits::SHADOW_CASTER_BIT and don't include them in scene bounding.
742                 disabled.clear();
743                 disabled.resize(meshData->submeshes.size());
744                 shadowCaster.clear();
745                 shadowCaster.resize(meshData->submeshes.size());
746                 auto submeshIdx = 0U;
747                 bool allowInstancing = true;
748                 for (const auto& submesh : meshData->submeshes) {
749                     if (EntityUtil::IsValid(submesh.material)) {
750                         if (auto matPos = GetMaterialProperties(submesh.material)) {
751                             disabled[submeshIdx] = matPos->disabled;
752                             allowInstancing = allowInstancing && matPos->allowInstancing;
753                             shadowCaster[submeshIdx] = matPos->shadowCaster;
754                         }
755                     } else {
756                         // assuming MaterialComponent::materialLightingFlags default value includes
757                         // SHADOW_CASTER_BIT
758                         shadowCaster[submeshIdx] = true;
759                     }
760                     for (const auto additionalMaterial : submesh.additionalMaterials) {
761                         if (EntityUtil::IsValid(additionalMaterial)) {
762                             if (auto matPos = GetMaterialProperties(additionalMaterial)) {
763                                 disabled[submeshIdx] = matPos->disabled;
764                                 allowInstancing = allowInstancing && matPos->allowInstancing;
765                                 shadowCaster[submeshIdx] = matPos->shadowCaster;
766                             }
767                         } else {
768                             // assuming MaterialComponent::materialLightingFlags default value includes
769                             // SHADOW_CASTER_BIT
770                             shadowCaster[submeshIdx] = true;
771                         }
772                     }
773                     ++submeshIdx;
774                 }
775                 data.shadowCaster = std::any_of(
776                     shadowCaster.cbegin(), shadowCaster.cend(), [](const bool shadowCaster) { return shadowCaster; });
777 
778                 if (std::any_of(disabled.cbegin(), disabled.cend(), [](const bool disabled) { return !disabled; })) {
779                     bool hasJoints = row.IsValidComponentId(JMC);
780                     if (hasJoints) {
781                         // this needs to happen only when joint matrices have changed
782                         auto jointMatricesHandle = jointMatricesManager_->Read(row.components[JMC]);
783                         hasJoints = (jointMatricesHandle->count > 0U);
784                         if (hasJoints) {
785                             aabbs.push_back(
786                                 Aabb { jointMatricesHandle->jointsAabbMin, jointMatricesHandle->jointsAabbMax });
787                             meshAabb.min = Math::min(meshAabb.min, jointMatricesHandle->jointsAabbMin);
788                             meshAabb.max = Math::max(meshAabb.max, jointMatricesHandle->jointsAabbMax);
789                         }
790                     }
791                     if (!hasJoints) {
792                         submeshIdx = 0U;
793                         const auto& world = worldMatrixManager_->Read(row.components[WMC])->matrix;
794                         if (allowInstancing) {
795                             // negative scale requires a different graphics state and assuming most of the content
796                             // doesn't have negative scaling we'll just use separate draws for inverted meshes instead
797                             // of instanced draws. negative scaling factor can be determined by checking is the
798                             // determinant of the 3x3 sub-matrix negative.
799                             const float determinant = world.x.x * (world.y.y * world.z.z - world.z.y * world.y.z) -
800                                                       world.x.y * (world.y.x * world.z.z - world.y.z * world.z.x) +
801                                                       world.x.z * (world.y.x * world.z.y - world.y.y * world.z.x);
802                             if (determinant < 0.f) {
803                                 allowInstancing = false;
804                             }
805                         }
806                         for (const auto& submesh : meshData->submeshes) {
807                             // this needs to happen only when world matrix, or mesh component have changed
808                             if (disabled[submeshIdx]) {
809                                 aabbs.push_back({});
810                             } else {
811                                 const MinAndMax mam = picking_->GetWorldAABB(world, submesh.aabbMin, submesh.aabbMax);
812                                 aabbs.push_back({ mam.minAABB, mam.maxAABB });
813                                 meshAabb.min = Math::min(meshAabb.min, mam.minAABB);
814                                 meshAabb.max = Math::max(meshAabb.max, mam.maxAABB);
815                             }
816                             ++submeshIdx;
817                         }
818                     }
819 
820                     auto skin = (row.IsValidComponentId(SC)) ? skinManager_->Read(row.components[SC])->skin : Entity {};
821                     meshComponents_.push_back({ nodeData.sceneId, row.components[RMC], renderMeshHandle->mesh,
822                         renderMeshHandle->renderMeshBatch, skin, allowInstancing });
823                 }
824             }
825         }
826     }
827 }
828 
UpdateMaterialProperties()829 void RenderPreprocessorSystem::UpdateMaterialProperties()
830 {
831     // assuming modifiedMaterials was sorted we can assume the next entity is between pos and end.
832     auto pos = materialProperties_.begin();
833     for (const auto& entity : materialModifiedEvents_) {
834         auto materialHandle = materialManager_->Read(entity);
835         if (!materialHandle) {
836             continue;
837         }
838         pos = std::lower_bound(pos, materialProperties_.end(), entity);
839         if ((pos == materialProperties_.end()) || (pos->material != entity)) {
840             pos = materialProperties_.insert(pos, {});
841             pos->material = entity;
842         }
843         pos->disabled =
844             (materialHandle->extraRenderingFlags & MaterialComponent::ExtraRenderingFlagBits::DISABLE_BIT) ==
845             MaterialComponent::ExtraRenderingFlagBits::DISABLE_BIT;
846         pos->allowInstancing = ((materialHandle->extraRenderingFlags &
847                                     MaterialComponent::ExtraRenderingFlagBits::ALLOW_GPU_INSTANCING_BIT) ==
848                                    MaterialComponent::ExtraRenderingFlagBits::ALLOW_GPU_INSTANCING_BIT) ||
849                                !EntityUtil::IsValid(materialHandle->materialShader.shader);
850         pos->shadowCaster =
851             (materialHandle->materialLightingFlags & MaterialComponent::LightingFlagBits::SHADOW_CASTER_BIT) ==
852             MaterialComponent::LightingFlagBits::SHADOW_CASTER_BIT;
853 
854         // create/update rendering side decoupled material data
855         UpdateSingleMaterial(entity, &(*materialHandle));
856     }
857 }
858 
UpdateSingleMaterial(const Entity matEntity,const MaterialComponent * materialHandle)859 void RenderPreprocessorSystem::UpdateSingleMaterial(const Entity matEntity, const MaterialComponent* materialHandle)
860 {
861     const MaterialComponent& materialComp = (materialHandle) ? *materialHandle : DEF_MATERIAL_COMPONENT;
862     RenderDataDefaultMaterial::InputMaterialUniforms materialUniforms =
863         InputMaterialUniformsFromMaterialComponent(matEntity, materialComp);
864 
865     // NOTE: we force material updates, no early outs
866 
867     array_view<const uint8_t> customData;
868     if (materialComp.customProperties) {
869         const auto buffer = static_cast<const uint8_t*>(materialComp.customProperties->RLock());
870         // NOTE: set and binding are currently not supported, we only support built-in mapping
871         // the data goes to a predefined set and binding
872         customData = array_view(buffer, materialComp.customProperties->Size());
873         materialComp.customProperties->RUnlock();
874     }
875     // material extensions
876     RenderHandleReference handleReferences[RenderDataDefaultMaterial::MAX_MATERIAL_CUSTOM_RESOURCE_COUNT];
877     array_view<RenderHandleReference> extHandles;
878     // extension valid only with non-default material
879     if (EntityUtil::IsValid(matEntity)) {
880         // first check the preferred vector version
881         if (!materialComp.customResources.empty()) {
882             const size_t maxCount = Math::min(static_cast<size_t>(materialComp.customResources.size()),
883                 static_cast<size_t>(RenderDataDefaultMaterial::MAX_MATERIAL_CUSTOM_RESOURCE_COUNT));
884             extHandles = { handleReferences, maxCount };
885             GetRenderHandleReferences(*renderHandleManager_, materialComp.customResources, extHandles);
886         }
887     }
888     const uint32_t transformBits = materialUniforms.texTransformSetBits;
889     const RenderMaterialFlags rmfFromBits =
890         RenderMaterialLightingFlagsFromMaterialFlags(materialComp.materialLightingFlags);
891     {
892         const RenderDataDefaultMaterial::MaterialHandlesWithHandleReference materialHandles =
893             GetMaterialHandles(materialComp, *renderHandleManager_);
894         const RenderMaterialFlags rmfFromValues =
895             RenderMaterialFlagsFromMaterialValues(materialComp, materialHandles, transformBits);
896         const RenderMaterialFlags rmf = rmfFromBits | rmfFromValues;
897         const uint32_t customCameraId = materialComp.cameraEntity.id & 0xFFFFffffU;
898         const RenderDataDefaultMaterial::MaterialData data {
899             { renderHandleManager_->GetRenderHandleReference(materialComp.materialShader.shader),
900                 renderHandleManager_->GetRenderHandleReference(materialComp.materialShader.graphicsState) },
901             { renderHandleManager_->GetRenderHandleReference(materialComp.depthShader.shader),
902                 renderHandleManager_->GetRenderHandleReference(materialComp.depthShader.graphicsState) },
903             materialComp.extraRenderingFlags, rmf, materialComp.customRenderSlotId, customCameraId,
904             RenderMaterialType(materialComp.type), materialComp.renderSort.renderSortLayer,
905             materialComp.renderSort.renderSortLayerOrder
906         };
907 
908         dsMaterial_->UpdateMaterialData(matEntity.id, materialUniforms, materialHandles, data, customData, extHandles);
909     }
910 }
911 
GetMaterialProperties(CORE_NS::Entity matEntity)912 RenderPreprocessorSystem::MaterialProperties* RenderPreprocessorSystem::GetMaterialProperties(CORE_NS::Entity matEntity)
913 {
914     auto matPos = std::lower_bound(materialProperties_.begin(), materialProperties_.end(), matEntity,
915         [](const MaterialProperties& element, const Entity& value) { return element.material.id < value.id; });
916     if ((matPos != materialProperties_.end()) && (matPos->material == matEntity)) {
917         // material's properties were cached
918         return &(*matPos);
919     }
920 
921     // material is unknown (created by a system after ProcessEvents), try to cache the properties
922     auto materialHandle = materialManager_->Read(matEntity);
923     if (!materialHandle) {
924         // entity didn't have a material component
925         return nullptr;
926     }
927 
928     matPos = materialProperties_.insert(matPos, {});
929     matPos->material = matEntity;
930     matPos->disabled = (materialHandle->extraRenderingFlags & MaterialComponent::ExtraRenderingFlagBits::DISABLE_BIT) ==
931                        MaterialComponent::ExtraRenderingFlagBits::DISABLE_BIT;
932     matPos->allowInstancing =
933         ((materialHandle->extraRenderingFlags & MaterialComponent::ExtraRenderingFlagBits::ALLOW_GPU_INSTANCING_BIT) ==
934             MaterialComponent::ExtraRenderingFlagBits::ALLOW_GPU_INSTANCING_BIT) ||
935         !EntityUtil::IsValid(materialHandle->materialShader.shader);
936     matPos->shadowCaster =
937         (materialHandle->materialLightingFlags & MaterialComponent::LightingFlagBits::SHADOW_CASTER_BIT) ==
938         MaterialComponent::LightingFlagBits::SHADOW_CASTER_BIT;
939 
940     // create/update rendering side decoupled material data
941     UpdateSingleMaterial(matEntity, &(*materialHandle));
942 
943     return &(*matPos);
944 }
945 
GetECS() const946 const IEcs& RenderPreprocessorSystem::GetECS() const
947 {
948     return ecs_;
949 }
950 
GetSceneData() const951 array_view<const RenderPreprocessorSystem::SceneData> RenderPreprocessorSystem::GetSceneData() const
952 {
953     return renderMeshComponentsPerScene_;
954 }
955 
GetRenderBatchMeshEntities(const uint32_t sceneId) const956 array_view<const Entity> RenderPreprocessorSystem::GetRenderBatchMeshEntities(const uint32_t sceneId) const
957 {
958     if (auto pos = std::find_if(renderMeshComponentsPerScene_.cbegin(), renderMeshComponentsPerScene_.cend(),
959             [sceneId](const SceneData& data) { return data.sceneId == sceneId; });
960         pos != renderMeshComponentsPerScene_.cend()) {
961         return pos->renderBatchComponents;
962     }
963     return {};
964 }
965 
GetInstancingAllowedEntities(const uint32_t sceneId) const966 array_view<const Entity> RenderPreprocessorSystem::GetInstancingAllowedEntities(const uint32_t sceneId) const
967 {
968     if (auto pos = std::find_if(renderMeshComponentsPerScene_.cbegin(), renderMeshComponentsPerScene_.cend(),
969             [sceneId](const SceneData& data) { return data.sceneId == sceneId; });
970         pos != renderMeshComponentsPerScene_.cend()) {
971         return pos->instancingAllowed;
972     }
973     return {};
974 }
975 
GetInstancingDisabledEntities(const uint32_t sceneId) const976 array_view<const Entity> RenderPreprocessorSystem::GetInstancingDisabledEntities(const uint32_t sceneId) const
977 {
978     if (auto pos = std::find_if(renderMeshComponentsPerScene_.cbegin(), renderMeshComponentsPerScene_.cend(),
979             [sceneId](const SceneData& data) { return data.sceneId == sceneId; });
980         pos != renderMeshComponentsPerScene_.cend()) {
981         return pos->rest;
982     }
983     return {};
984 }
985 
GetRenderMeshAabb(Entity renderMesh) const986 RenderPreprocessorSystem::Aabb RenderPreprocessorSystem::GetRenderMeshAabb(Entity renderMesh) const
987 {
988     const auto pos = std::lower_bound(renderMeshAabbs_.cbegin(), renderMeshAabbs_.cend(), renderMesh,
989         [](const RenderMeshAaabb& elem, const Entity& value) { return elem.entity < value; });
990     if ((pos != renderMeshAabbs_.cend()) && (pos->entity == renderMesh)) {
991         return pos->meshAabb;
992     }
993     return {};
994 }
995 
GetRenderMeshAabbs(Entity renderMesh) const996 array_view<const RenderPreprocessorSystem::Aabb> RenderPreprocessorSystem::GetRenderMeshAabbs(Entity renderMesh) const
997 {
998     const auto pos = std::lower_bound(renderMeshAabbs_.cbegin(), renderMeshAabbs_.cend(), renderMesh,
999         [](const RenderMeshAaabb& elem, const Entity& value) { return elem.entity < value; });
1000     if ((pos != renderMeshAabbs_.cend()) && (pos->entity == renderMesh)) {
1001         return pos->submeshAabbs;
1002     }
1003     return {};
1004 }
1005 
GetBoundingSphere() const1006 RenderPreprocessorSystem::Sphere RenderPreprocessorSystem::GetBoundingSphere() const
1007 {
1008     return boundingSphere_;
1009 }
1010 
CheckIfDefaultDataStoreNames() const1011 bool RenderPreprocessorSystem::CheckIfDefaultDataStoreNames() const
1012 {
1013     if ((properties_.dataStoreScene == DEFAULT_DS_SCENE_NAME) &&
1014         (properties_.dataStoreCamera == DEFAULT_DS_CAMERA_NAME) &&
1015         (properties_.dataStoreLight == DEFAULT_DS_LIGHT_NAME) &&
1016         (properties_.dataStoreMaterial == DEFAULT_DS_MATERIAL_NAME) &&
1017         (properties_.dataStoreMorph == DEFAULT_DS_MORPH_NAME)) {
1018         return true;
1019     } else {
1020         return false;
1021     }
1022 }
1023 
Initialize()1024 void RenderPreprocessorSystem::Initialize()
1025 {
1026     if (graphicsContext_ && renderContext_) {
1027         const bool hasDefaultNames = CheckIfDefaultDataStoreNames();
1028         if ((properties_.dataStorePrefix.empty()) && (ecs_.GetId() != 0) && hasDefaultNames) {
1029             properties_.dataStorePrefix = to_string(ecs_.GetId());
1030         }
1031         if (hasDefaultNames) {
1032             properties_.dataStoreScene = properties_.dataStorePrefix + properties_.dataStoreScene;
1033             properties_.dataStoreCamera = properties_.dataStorePrefix + properties_.dataStoreCamera;
1034             properties_.dataStoreLight = properties_.dataStorePrefix + properties_.dataStoreLight;
1035             properties_.dataStoreMaterial = properties_.dataStorePrefix + properties_.dataStoreMaterial;
1036             properties_.dataStoreMorph = properties_.dataStorePrefix + properties_.dataStoreMorph;
1037         }
1038         SetDataStorePointers(renderContext_->GetRenderDataStoreManager());
1039     }
1040     if (renderMeshManager_ && nodeManager_ && layerManager_) {
1041         const ComponentQuery::Operation operations[] = {
1042             { *nodeManager_, ComponentQuery::Operation::REQUIRE },
1043             { *worldMatrixManager_, ComponentQuery::Operation::REQUIRE },
1044             { *layerManager_, ComponentQuery::Operation::OPTIONAL },
1045             { *jointMatricesManager_, ComponentQuery::Operation::OPTIONAL },
1046             { *skinManager_, ComponentQuery::Operation::OPTIONAL },
1047         };
1048         renderableQuery_.SetEcsListenersEnabled(true);
1049         renderableQuery_.SetupQuery(*renderMeshManager_, operations, false);
1050     }
1051     ecs_.AddListener(*renderMeshManager_, *this);
1052     ecs_.AddListener(*materialManager_, *this);
1053     ecs_.AddListener(*meshManager_, *this);
1054     ecs_.AddListener(*graphicsStateManager_, *this);
1055 }
1056 
Update(bool frameRenderingQueued,uint64_t totalTime,uint64_t deltaTime)1057 bool RenderPreprocessorSystem::Update(bool frameRenderingQueued, uint64_t totalTime, uint64_t deltaTime)
1058 {
1059     if (!active_) {
1060         return false;
1061     }
1062 
1063     const auto gsGen = graphicsStateManager_->GetGenerationCounter();
1064     if (gsGen != graphicsStateGeneration_) {
1065         graphicsStateGeneration_ = gsGen;
1066     }
1067     if (!graphicsStateModifiedEvents_.empty()) {
1068         HandleGraphicsStateEvents();
1069     }
1070 
1071     const bool queryChanged = renderableQuery_.Execute();
1072     const auto materialChanged = (!materialModifiedEvents_.empty()) || (!materialDestroyedEvents_.empty());
1073     const auto meshChanged = (!meshModifiedEvents_.empty()) || (!meshDestroyedEvents_.empty());
1074     const auto jointGen = jointMatricesManager_->GetGenerationCounter();
1075     const auto layerGen = layerManager_->GetGenerationCounter();
1076     const auto nodeGen = nodeManager_->GetGenerationCounter();
1077     const auto renderMeshGen = renderMeshManager_->GetGenerationCounter();
1078     const auto worldMatrixGen = worldMatrixManager_->GetGenerationCounter();
1079     if (!queryChanged && !materialChanged && !meshChanged && (jointGeneration_ == jointGen) &&
1080         (layerGeneration_ == layerGen) && (nodeGeneration_ == nodeGen) && (renderMeshGeneration_ == renderMeshGen) &&
1081         (worldMatrixGeneration_ == worldMatrixGen)) {
1082         return false;
1083     }
1084     if (materialChanged) {
1085         HandleMaterialEvents();
1086     }
1087     if (meshChanged) {
1088         HandleMeshEvents();
1089     }
1090 
1091     // gather which mesh is used by each render mesh component.
1092     GatherSortData();
1093 
1094     jointGeneration_ = jointGen;
1095     layerGeneration_ = layerGen;
1096     nodeGeneration_ = nodeGen;
1097     renderMeshGeneration_ = renderMeshGen;
1098     worldMatrixGeneration_ = worldMatrixGen;
1099 #if (CORE3D_DEV_ENABLED == 1)
1100     CORE_CPU_PERF_BEGIN(
1101         sortMeshComponents, "CORE3D", "RenderPreprocessorSystem", "SortMeshComponents", CORE3D_PROFILER_DEFAULT_COLOR);
1102 #endif
1103     std::sort(meshComponents_.begin(), meshComponents_.end(), [](const SortData& lhs, const SortData& rhs) {
1104         // first sort by scene ID
1105         if (lhs.sceneId < rhs.sceneId) {
1106             return true;
1107         }
1108         if (lhs.sceneId > rhs.sceneId) {
1109             return false;
1110         }
1111         // entities with render mesh batch are grouped at the begining of each scene sorted by entity id
1112         if (EntityUtil::IsValid(lhs.batch)) {
1113             if (!EntityUtil::IsValid(rhs.batch)) {
1114                 return true;
1115             }
1116             return lhs.renderMeshId < rhs.renderMeshId;
1117         }
1118         if (EntityUtil::IsValid(rhs.batch)) {
1119             return false;
1120         }
1121         // render mesh batch entities are followed by in
1122         if (lhs.allowInstancing && !rhs.allowInstancing) {
1123             return true;
1124         }
1125         if (!lhs.allowInstancing && rhs.allowInstancing) {
1126             return false;
1127         }
1128         // next are meshes that can be instanced sorted by mesh id, skin id and entity id
1129         if (lhs.mesh < rhs.mesh) {
1130             return true;
1131         }
1132         if (lhs.mesh > rhs.mesh) {
1133             return false;
1134         }
1135         if (lhs.skin < rhs.skin) {
1136             return true;
1137         }
1138         if (lhs.skin > rhs.skin) {
1139             return false;
1140         }
1141         return lhs.renderMeshId < rhs.renderMeshId;
1142     });
1143 #if (CORE3D_DEV_ENABLED == 1)
1144     CORE_CPU_PERF_END(sortMeshComponents);
1145 #endif
1146 #if (CORE3D_DEV_ENABLED == 1)
1147     CORE_CPU_PERF_BEGIN(renderMeshComponentsPerScene, "CORE3D", "RenderPreprocessorSystem", "BuildLists",
1148         CORE3D_PROFILER_DEFAULT_COLOR);
1149 #endif
1150     // list the entities with render mesh components in sorted order.
1151     const auto meshCount = meshComponents_.size();
1152     renderMeshComponents_.clear();
1153     renderMeshComponents_.reserve(meshCount);
1154     renderMeshComponentsPerScene_.clear();
1155     uint32_t currentScene = meshCount ? meshComponents_.front().sceneId : 0U;
1156     size_t startIndex = 0U;
1157     size_t lastBatchIndex = 0U;
1158     size_t lastAllowInstancingIndex = 0U;
1159     for (const auto& data : meshComponents_) {
1160         if (currentScene != data.sceneId) {
1161             const auto endIndex = renderMeshComponents_.size();
1162             renderMeshComponentsPerScene_.push_back({ currentScene,
1163                 array_view(renderMeshComponents_.data() + startIndex, lastBatchIndex - startIndex),
1164                 array_view(renderMeshComponents_.data() + lastBatchIndex, lastAllowInstancingIndex - lastBatchIndex),
1165                 array_view(
1166                     renderMeshComponents_.data() + lastAllowInstancingIndex, endIndex - lastAllowInstancingIndex) });
1167             startIndex = endIndex;
1168             lastBatchIndex = endIndex;
1169             lastAllowInstancingIndex = endIndex;
1170             currentScene = data.sceneId;
1171         }
1172         renderMeshComponents_.push_back(renderMeshManager_->GetEntity(data.renderMeshId));
1173         const auto endIndex = renderMeshComponents_.size();
1174         if (EntityUtil::IsValid(data.batch)) {
1175             lastBatchIndex = endIndex;
1176             lastAllowInstancingIndex = endIndex;
1177         } else if (data.allowInstancing) {
1178             lastAllowInstancingIndex = endIndex;
1179         }
1180     }
1181     {
1182         const auto endIndex = renderMeshComponents_.size();
1183         renderMeshComponentsPerScene_.push_back({ currentScene,
1184             array_view(renderMeshComponents_.data() + startIndex, lastBatchIndex - startIndex),
1185             array_view(renderMeshComponents_.data() + lastBatchIndex, lastAllowInstancingIndex - lastBatchIndex),
1186             array_view(renderMeshComponents_.data() + lastAllowInstancingIndex, endIndex - lastAllowInstancingIndex) });
1187     }
1188 #if (CORE3D_DEV_ENABLED == 1)
1189     CORE_CPU_PERF_END(renderMeshComponentsPerScene);
1190 #endif
1191 
1192     CalculateSceneBounds();
1193 
1194     return true;
1195 }
1196 
Uninitialize()1197 void RenderPreprocessorSystem::Uninitialize()
1198 {
1199     ecs_.RemoveListener(*graphicsStateManager_, *this);
1200     ecs_.RemoveListener(*meshManager_, *this);
1201     ecs_.RemoveListener(*materialManager_, *this);
1202     ecs_.RemoveListener(*renderMeshManager_, *this);
1203     renderableQuery_.SetEcsListenersEnabled(false);
1204 }
1205 
IRenderPreprocessorSystemInstance(IEcs & ecs)1206 ISystem* IRenderPreprocessorSystemInstance(IEcs& ecs)
1207 {
1208     return new RenderPreprocessorSystem(ecs);
1209 }
1210 
IRenderPreprocessorSystemDestroy(ISystem * instance)1211 void IRenderPreprocessorSystemDestroy(ISystem* instance)
1212 {
1213     delete static_cast<RenderPreprocessorSystem*>(instance);
1214 }
1215 CORE3D_END_NAMESPACE()
1216