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