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