• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include <3d/ecs/components/material_component.h>
17 #include <3d/ecs/components/render_handle_component.h>
18 #include <base/containers/refcnt_ptr.h>
19 #include <base/containers/unordered_map.h>
20 #include <base/util/compile_time_hashes.h>
21 #include <base/util/errors.h>
22 #include <base/util/hash.h>
23 #include <core/ecs/intf_ecs.h>
24 #include <core/intf_engine.h>
25 #include <core/property/intf_property_api.h>
26 #include <core/property/property_types.h>
27 #include <render/device/intf_shader_manager.h>
28 #include <render/implementation_uids.h>
29 #include <render/intf_render_context.h>
30 
31 #include "util/property_util.h"
32 
33 #define IMPLEMENT_MANAGER
34 #include <algorithm>
35 #include <iterator>
36 
37 #include <core/property_tools/property_macros.h>
38 
39 CORE_BEGIN_NAMESPACE()
40 using BASE_NS::string_view;
41 using BASE_NS::vector;
42 using CORE3D_NS::MaterialComponent;
43 
44 /** Extend propertysystem with the types */
45 DECLARE_PROPERTY_TYPE(MaterialComponent::Type);
46 DECLARE_PROPERTY_TYPE(MaterialComponent::TextureTransform);
47 DECLARE_PROPERTY_TYPE(MaterialComponent::TextureInfo);
48 DECLARE_PROPERTY_TYPE(MaterialComponent::Shader);
49 DECLARE_PROPERTY_TYPE(MaterialComponent::RenderSort);
50 DECLARE_PROPERTY_TYPE(vector<MaterialComponent::TextureTransform>);
51 DECLARE_PROPERTY_TYPE(CORE_NS::IPropertyHandle*);
52 
53 // Declare their metadata
54 ENUM_TYPE_METADATA(MaterialComponent::Type, ENUM_VALUE(METALLIC_ROUGHNESS, "Metallic Roughness"),
55     ENUM_VALUE(SPECULAR_GLOSSINESS, "Specular Glossiness"), ENUM_VALUE(UNLIT, "Unlit"),
56     ENUM_VALUE(UNLIT_SHADOW_ALPHA, "Unlit Shadow Alpha"), ENUM_VALUE(CUSTOM, "Custom material"),
57     ENUM_VALUE(CUSTOM_COMPLEX, "Custom complex material"))
58 
59 ENUM_TYPE_METADATA(MaterialComponent::LightingFlagBits, ENUM_VALUE(SHADOW_RECEIVER_BIT, "Shadow Receiver"),
60     ENUM_VALUE(SHADOW_CASTER_BIT, "Shadow Caster"), ENUM_VALUE(PUNCTUAL_LIGHT_RECEIVER_BIT, "Punctual Light Receiver"),
61     ENUM_VALUE(INDIRECT_LIGHT_RECEIVER_BIT, "Indirect Light Receiver"))
62 
63 ENUM_TYPE_METADATA(MaterialComponent::ExtraRenderingFlagBits, ENUM_VALUE(DISCARD_BIT, "Discard Special Materials"),
64     ENUM_VALUE(DISABLE_BIT, "Disable Material Rendering"), ENUM_VALUE(ALLOW_GPU_INSTANCING_BIT, "Allow GPU Instancing"))
65 
66 DATA_TYPE_METADATA(MaterialComponent::TextureTransform, MEMBER_PROPERTY(translation, "Translation", 0),
67     MEMBER_PROPERTY(rotation, "Rotation", 0), MEMBER_PROPERTY(scale, "Scale", 0))
68 
69 DATA_TYPE_METADATA(MaterialComponent::TextureInfo, MEMBER_PROPERTY(image, "Texture", 0),
70     MEMBER_PROPERTY(sampler, "Sampler", 0), MEMBER_PROPERTY(factor, "Factor", 0),
71     MEMBER_PROPERTY(transform, "Transform", 0))
72 
73 DATA_TYPE_METADATA(MaterialComponent::Shader, MEMBER_PROPERTY(shader, "Shader", 0),
74     MEMBER_PROPERTY(graphicsState, "Graphics State", 0))
75 
76 DATA_TYPE_METADATA(MaterialComponent::RenderSort, MEMBER_PROPERTY(renderSortLayer, "Render Sort Layer", 0),
77     MEMBER_PROPERTY(renderSortLayerOrder, "Render Sort Layer Order (fine ordering)", 0))
78 
79 constexpr PropertyTypeDecl TEXTURE_INFO_T = PROPERTYTYPE(MaterialComponent::TextureInfo);
80 
operator ==(const Property & lhs,const Property & rhs)81 static constexpr inline bool operator==(const Property& lhs, const Property& rhs) noexcept
82 {
83     return (lhs.type == rhs.type) && (lhs.hash == rhs.hash) && (lhs.name == rhs.name);
84 }
85 CORE_END_NAMESPACE()
86 
87 CORE3D_BEGIN_NAMESPACE()
88 using BASE_NS::array_view;
89 using BASE_NS::countof;
90 using BASE_NS::exchange;
91 using BASE_NS::forward;
92 using BASE_NS::move;
93 using BASE_NS::refcnt_ptr;
94 using BASE_NS::string;
95 using BASE_NS::string_view;
96 using BASE_NS::Uid;
97 using BASE_NS::unordered_map;
98 using BASE_NS::vector;
99 
100 using CORE_NS::Entity;
101 using CORE_NS::GetInstance;
102 using CORE_NS::GetManager;
103 using CORE_NS::IClassRegister;
104 using CORE_NS::IComponentManager;
105 using CORE_NS::IEcs;
106 using CORE_NS::IPropertyApi;
107 using CORE_NS::IPropertyHandle;
108 using CORE_NS::Property;
109 using CORE_NS::PropertyFlags;
110 using CORE_NS::ScopedHandle;
111 
112 using RENDER_NS::IRenderContext;
113 using RENDER_NS::IShaderManager;
114 using RENDER_NS::RenderHandle;
115 using RENDER_NS::RenderHandleReference;
116 using RENDER_NS::UID_RENDER_CONTEXT;
117 
118 namespace {
119 constexpr string_view PROPERTIES = "properties";
120 constexpr string_view NAME = "name";
121 constexpr string_view DISPLAY_NAME = "displayName";
122 constexpr string_view MATERIAL_COMPONENT_NAME = CORE_NS::GetName<MaterialComponent>();
123 
124 // RenderDataDefaultMaterial::MAX_MATERIAL_CUSTOM_PROPERTY_BYTE_SIZE
125 constexpr uint32_t CUSTOM_PROPERTY_POD_CONTAINER_BYTE_SIZE { 256u };
126 constexpr string_view CUSTOM_PROPERTIES = "customProperties";
127 constexpr string_view CUSTOM_BINDING_PROPERTIES = "customBindingProperties";
128 constexpr string_view CUSTOM_PROPERTY_DATA = "data";
129 
130 constexpr uint32_t MODIFIED = 0x80000000;
131 
132 template<typename Container, typename Predicate>
FindIf(const Container & container,Predicate && predicate)133 inline typename Container::const_iterator FindIf(const Container& container, Predicate&& predicate)
134 {
135     return std::find_if(container.cbegin(), container.cend(), forward<Predicate>(predicate));
136 }
137 
138 template<typename Container, typename Predicate>
FindIf(Container & container,Predicate && predicate)139 inline typename Container::iterator FindIf(Container& container, Predicate&& predicate)
140 {
141     return std::find_if(container.begin(), container.end(), forward<Predicate>(predicate));
142 }
143 
FindMaterialComponentJson(const CORE_NS::json::value & metaJson)144 const CORE_NS::json::value* FindMaterialComponentJson(const CORE_NS::json::value& metaJson)
145 {
146     if (auto material = FindIf(metaJson.array_,
147             [](const CORE_NS::json::value& value) {
148                 auto nameJson = value.find(NAME);
149                 return nameJson && nameJson->is_string() && nameJson->string_ == MATERIAL_COMPONENT_NAME;
150             });
151         material != metaJson.array_.end()) {
152         return &(*material);
153     }
154     return nullptr;
155 }
156 
HandlePropertyObject(vector<Property> & newProperties,vector<string> & newStringPropStorage,const Property & property,const CORE_NS::json::value & value,const uint32_t index)157 void HandlePropertyObject(vector<Property>& newProperties, vector<string>& newStringPropStorage,
158     const Property& property, const CORE_NS::json::value& value, const uint32_t index)
159 {
160     const auto& containerProperty = property.metaData.containerMethods->property;
161 
162     // calculate offset to current struct in the array
163     const auto offset = property.offset + containerProperty.size * index;
164     Property newProperty = containerProperty;
165 
166     // add the currect struct offset to the member offset
167     newProperty.offset += offset;
168 
169     // update 'name' and 'displayName' if needed
170     if (auto name = value.find(NAME); name && name->is_string()) {
171         newStringPropStorage.push_back(string(name->string_));
172         newProperty.name = newStringPropStorage.back();
173         newProperty.hash = BASE_NS::FNV1aHash(newProperty.name.data(), newProperty.name.size());
174     }
175     if (auto displayName = value.find(DISPLAY_NAME); displayName && displayName->is_string()) {
176         newStringPropStorage.push_back(string(displayName->string_));
177         newProperty.displayName = newStringPropStorage.back();
178     }
179     newProperties.push_back(newProperty);
180 }
181 
HandlePropertyArray(vector<Property> & newProperties,vector<string> & newStringPropStorage,const Property & property,const CORE_NS::json::value::array & propertyArray)182 void HandlePropertyArray(vector<Property>& newProperties, vector<string>& newStringPropStorage,
183     const Property& property, const CORE_NS::json::value::array& propertyArray)
184 {
185     uint32_t index = 0u;
186     // reserve strings, name and display name (reason for * 2)
187     newStringPropStorage.reserve(propertyArray.size() * 2);
188     for (const CORE_NS::json::value& value : propertyArray) {
189         // if the array has objects, the metadata should have containerMethods for
190         // the members
191         if (value.is_object() && !value.object_.empty() && property.metaData.containerMethods) {
192             HandlePropertyObject(newProperties, newStringPropStorage, property, value, index);
193         } else {
194             // NOTE: handle non-object properties?
195         }
196         ++index;
197     }
198 }
199 
AppendProperties(vector<Property> & newProperties,vector<string> & newStringPropStorage,const CORE_NS::json::value & material,const array_view<const Property> componentMetaData)200 void AppendProperties(vector<Property>& newProperties, vector<string>& newStringPropStorage,
201     const CORE_NS::json::value& material, const array_view<const Property> componentMetaData)
202 {
203     const CORE_NS::json::value* propertiesJson = material.find(PROPERTIES);
204     if (!propertiesJson || !propertiesJson->is_object()) {
205         return;
206     }
207     for (const auto& propertyObject : propertiesJson->object_) {
208         const auto propertyIt =
209             FindIf(componentMetaData, [key = propertyObject.key](const Property& p) { return p.name == key; });
210         if (propertyIt == componentMetaData.cend()) {
211             continue;
212         }
213         // if it's an array, make sure sizes match
214 #if (CORE3D_VALIDATION_ENABLED == 1)
215         if (propertyObject.value.is_array() && propertyIt->type.isArray &&
216             propertyObject.value.array_.size() > propertyIt->count) {
217             CORE_LOG_W("CORE3D_VALIDATION: material property metadata count mismatch (%u <= %u)",
218                 uint32_t(propertyObject.value.array_.size()), uint32_t(propertyIt->count));
219         }
220 #endif
221         if (propertyObject.value.is_array() && propertyIt->type.isArray &&
222             propertyObject.value.array_.size() <= propertyIt->count) {
223             HandlePropertyArray(newProperties, newStringPropStorage, *propertyIt, propertyObject.value.array_);
224         } else {
225             // NOTE: handle non-array properties?
226         }
227     }
228 }
229 
MapTextureSlots(array_view<MaterialComponent::TextureInfo> textures,size_t baseOffset,array_view<const Property> newProperties,array_view<const Property> oldProperties)230 void MapTextureSlots(array_view<MaterialComponent::TextureInfo> textures, size_t baseOffset,
231     array_view<const Property> newProperties, array_view<const Property> oldProperties)
232 {
233     MaterialComponent::TextureInfo tmp[MaterialComponent::TextureIndex::TEXTURE_COUNT];
234     for (const auto& newProp : newProperties) {
235         if (newProp.type == CORE_NS::TEXTURE_INFO_T) {
236             if (auto it = std::find(oldProperties.cbegin(), oldProperties.cend(), newProp);
237                 it != oldProperties.cend()) {
238                 auto& oldProp = *it;
239                 // theoretically the offset of TextureInfos can't be less than the offset of the first and this could be
240                 // an assertion
241                 if (oldProp.offset >= baseOffset && newProp.offset >= baseOffset) {
242                     auto oldIndex = (oldProp.offset - baseOffset) / sizeof(MaterialComponent::TextureInfo);
243                     auto newIndex = (newProp.offset - baseOffset) / sizeof(MaterialComponent::TextureInfo);
244                     if (newIndex < BASE_NS::countof(tmp) && oldIndex < textures.size()) {
245                         tmp[newIndex] = BASE_NS::move(textures[oldIndex]);
246                     }
247                 }
248             }
249         }
250     }
251     std::move(std::begin(tmp), std::end(tmp), std::begin(textures));
252 }
253 
UpdateCustomPropertyMetadata(const CORE_NS::json::value & customProperties,CustomPropertyPodContainer & properties)254 void UpdateCustomPropertyMetadata(const CORE_NS::json::value& customProperties, CustomPropertyPodContainer& properties)
255 {
256     for (const CORE_NS::json::value& propertyValue : customProperties.array_) {
257         if (propertyValue.is_object()) {
258             // NOTE: set and binding are currently ignored as the custom material property cannot be mapped
259             // to user defined sets and bindings
260             for (const auto& object : propertyValue.object_) {
261                 if (object.key == CUSTOM_PROPERTY_DATA && object.value.is_array()) {
262                     // reserve the property count
263                     properties.ReservePropertyCount(object.value.array_.size());
264                     for (const auto& dataValue : object.value.array_) {
265                         if (dataValue.is_object()) {
266                             string_view name;
267                             string_view displayName;
268                             string_view type;
269                             const CORE_NS::json::value* value = nullptr;
270                             for (const auto& dataObject : dataValue.object_) {
271                                 if (dataObject.key == NAME && dataObject.value.is_string()) {
272                                     name = dataObject.value.string_;
273                                 } else if (dataObject.key == DISPLAY_NAME && dataObject.value.is_string()) {
274                                     displayName = dataObject.value.string_;
275                                 } else if (dataObject.key == "type" && dataObject.value.is_string()) {
276                                     type = dataObject.value.string_;
277                                 } else if (dataObject.key == "value") {
278                                     value = &dataObject.value;
279                                 }
280                             }
281                             const CORE_NS::PropertyTypeDecl typeDecl =
282                                 CustomPropertyPodHelper::GetPropertyTypeDeclaration(type);
283                             const size_t align = CustomPropertyPodHelper::GetPropertyTypeAlignment(typeDecl);
284                             const size_t offset = [](size_t value, size_t align) -> size_t {
285                                 if (align == 0U) {
286                                     return value;
287                                 }
288                                 return ((value + align - 1U) / align) * align;
289                             }(properties.GetByteSize(), align);
290                             properties.AddOffsetProperty(name, displayName, offset, typeDecl);
291                             CustomPropertyPodHelper::SetCustomPropertyBlobValue(typeDecl, value, properties, offset);
292                         }
293                     }
294                 }
295             }
296         }
297     }
298 }
299 
UpdateCustomBindingPropertyMetadata(const CORE_NS::json::value & customProperties,CustomPropertyBindingContainer & properties)300 void UpdateCustomBindingPropertyMetadata(
301     const CORE_NS::json::value& customProperties, CustomPropertyBindingContainer& properties)
302 {
303     for (const CORE_NS::json::value& propertyValue : customProperties.array_) {
304         if (propertyValue.is_object()) {
305             // NOTE: set and binding are currently ignored as the custom material property cannot be mapped
306             // to user defined sets and bindings
307             for (const auto& object : propertyValue.object_) {
308                 if (object.key == CUSTOM_PROPERTY_DATA && object.value.is_array()) {
309                     // reserve the property count
310                     properties.ReservePropertyCount(object.value.array_.size());
311                     for (const auto& dataValue : object.value.array_) {
312                         if (dataValue.is_object()) {
313                             string_view name;
314                             string_view displayName;
315                             string_view type;
316                             for (const auto& dataObject : dataValue.object_) {
317                                 if (dataObject.key == NAME && dataObject.value.is_string()) {
318                                     name = dataObject.value.string_;
319                                 } else if (dataObject.key == DISPLAY_NAME && dataObject.value.is_string()) {
320                                     displayName = dataObject.value.string_;
321                                 } else if (dataObject.key == "type" && dataObject.value.is_string()) {
322                                     type = dataObject.value.string_;
323                                 }
324                             }
325                             const CORE_NS::PropertyTypeDecl typeDecl =
326                                 CustomPropertyBindingHelper::GetPropertyTypeDeclaration(type);
327                             const size_t align = CustomPropertyBindingHelper::GetPropertyTypeAlignment(typeDecl);
328                             const size_t offset = [](size_t value, size_t align) -> size_t {
329                                 if (align == 0U) {
330                                     return value;
331                                 }
332                                 return ((value + align - 1U) / align) * align;
333                             }(properties.GetByteSize(), align);
334                             properties.AddOffsetProperty(name, displayName, offset, typeDecl);
335                         }
336                     }
337                 }
338             }
339         }
340     }
341 }
342 
CreateCustomPropertyPodContainer(CustomPropertyWriteSignal & signal,const CORE_NS::json::value * const propertiesJson,const CustomPropertyPodContainer * oldContainer)343 BASE_NS::unique_ptr<CustomPropertyPodContainer> CreateCustomPropertyPodContainer(CustomPropertyWriteSignal& signal,
344     const CORE_NS::json::value* const propertiesJson, const CustomPropertyPodContainer* oldContainer)
345 {
346     // create and fill new POD container
347     auto newPod = BASE_NS::make_unique<CustomPropertyPodContainer>(signal, CUSTOM_PROPERTY_POD_CONTAINER_BYTE_SIZE);
348     if (propertiesJson) {
349         UpdateCustomPropertyMetadata(*propertiesJson, *newPod);
350     }
351 
352     if (oldContainer) {
353         newPod->CopyValues(*oldContainer);
354     }
355     return newPod;
356 }
357 } // namespace
358 
359 class MaterialComponentManager : public IMaterialComponentManager, public IPropertyApi {
360     using ComponentId = IComponentManager::ComponentId;
361 
362 public:
363     explicit MaterialComponentManager(IEcs& ecs) noexcept;
364     ~MaterialComponentManager() override;
365 
366     // IPropertyApi
367     size_t PropertyCount() const override;
368     const Property* MetaData(size_t index) const override;
369     array_view<const Property> MetaData() const override;
370     IPropertyHandle* Create() const override;
371     IPropertyHandle* Clone(const IPropertyHandle*) const override;
372     void Release(IPropertyHandle*) const override;
373     uint64_t Type() const override;
374 
375     // IComponentManager
376     virtual string_view GetName() const override;
377     virtual Uid GetUid() const override;
378     size_t GetComponentCount() const override;
379     const IPropertyApi& GetPropertyApi() const override;
380     Entity GetEntity(ComponentId index) const override;
381     uint32_t GetComponentGeneration(ComponentId index) const override;
382     bool HasComponent(Entity entity) const override;
383     IComponentManager::ComponentId GetComponentId(Entity entity) const override;
384     void Create(Entity entity) override;
385     bool Destroy(Entity entity) override;
386     void Gc() override;
387     void Destroy(array_view<const Entity> gcList) override;
388     vector<Entity> GetAddedComponents() override;
389     vector<Entity> GetRemovedComponents() override;
390     vector<Entity> GetUpdatedComponents() override;
391     vector<Entity> GetMovedComponents() override;
392     CORE_NS::ComponentManagerModifiedFlags GetModifiedFlags() const override;
393     void ClearModifiedFlags() override;
394     uint32_t GetGenerationCounter() const override;
395     void SetData(Entity entity, const IPropertyHandle& dataHandle) override;
396     const IPropertyHandle* GetData(Entity entity) const override;
397     IPropertyHandle* GetData(Entity entity) override;
398     void SetData(ComponentId index, const IPropertyHandle& dataHandle) override;
399     const IPropertyHandle* GetData(ComponentId index) const override;
400     IPropertyHandle* GetData(ComponentId index) override;
401     IEcs& GetEcs() const override;
402 
403     // IMaterialComponentManager
404     MaterialComponent Get(ComponentId index) const override;
405     MaterialComponent Get(Entity entity) const override;
406     void Set(ComponentId index, const MaterialComponent& aData) override;
407     void Set(Entity entity, const MaterialComponent& aData) override;
408     ScopedHandle<const MaterialComponent> Read(ComponentId index) const override;
409     ScopedHandle<const MaterialComponent> Read(Entity entity) const override;
410     ScopedHandle<MaterialComponent> Write(ComponentId index) override;
411     ScopedHandle<MaterialComponent> Write(Entity entity) override;
412 
413     // internal, non-public
414     void Updated(Entity entity);
415 
416 private:
417     bool IsMatchingHandle(const IPropertyHandle& handle);
418 
419     BEGIN_PROPERTY(MaterialComponent, componentProperties_)
420 #include <3d/ecs/components/material_component.h>
421     END_PROPERTY();
422 
423     static constexpr array_view<const Property> componentMetaData_ { componentProperties_ };
424 
425     static constexpr CORE_NS::Property texturesMetaData_[] = {
426         RENAMED_MEMBER_PROPERTY(textures[MaterialComponent::TextureIndex::BASE_COLOR], baseColor, "Base Color", 0),
427         RENAMED_MEMBER_PROPERTY(textures[MaterialComponent::TextureIndex::NORMAL], normal, "Normal Map", 0),
428         RENAMED_MEMBER_PROPERTY(textures[MaterialComponent::TextureIndex::MATERIAL], material, "Material", 0),
429         RENAMED_MEMBER_PROPERTY(textures[MaterialComponent::TextureIndex::EMISSIVE], emissive, "Emissive", 0),
430         RENAMED_MEMBER_PROPERTY(
431             textures[MaterialComponent::TextureIndex::AO], ambientOcclusion, "Ambient Occlusion", 0),
432         RENAMED_MEMBER_PROPERTY(textures[MaterialComponent::TextureIndex::CLEARCOAT], clearcoat, "Clearcoat", 0),
433         RENAMED_MEMBER_PROPERTY(textures[MaterialComponent::TextureIndex::CLEARCOAT_ROUGHNESS], clearcoatRoughness,
434             "Clearcoat Roughness", 0),
435         RENAMED_MEMBER_PROPERTY(
436             textures[MaterialComponent::TextureIndex::CLEARCOAT_NORMAL], clearcoatNormal, "Clearcoat Normal", 0),
437 
438         RENAMED_MEMBER_PROPERTY(textures[MaterialComponent::TextureIndex::SHEEN], sheen, "Sheen", 0),
439         RENAMED_MEMBER_PROPERTY(
440             textures[MaterialComponent::TextureIndex::TRANSMISSION], transmission, "Transmission", 0),
441         RENAMED_MEMBER_PROPERTY(textures[MaterialComponent::TextureIndex::SPECULAR], specular, "Specular", 0),
442     };
443 
444     static constexpr array_view<const Property> defaultMetaData_ { texturesMetaData_ };
445 
446     static constexpr uint64_t typeHash_ = BASE_NS::CompileTime::FNV1aHash(CORE_NS::GetName<MaterialComponent>().data());
447 
448     struct ReferencedProperties {
RefMaterialComponentManager::ReferencedProperties449         void Ref()
450         {
451             ++cnt;
452         }
453 
UnrefMaterialComponentManager::ReferencedProperties454         void Unref()
455         {
456             if (--cnt == 0) {
457                 delete this;
458             }
459         }
460 
461         vector<Property> properties;
462         // need to be stored to have a valid string_view in properties (for name and display name)
463         vector<string> stringStorage;
464         uint32_t cnt { 0u };
465     };
466 
467     class ComponentHandle;
468 
469     class MaterialPropertyBindingSignal final : public CustomPropertyWriteSignal {
470     public:
MaterialPropertyBindingSignal(MaterialComponentManager::ComponentHandle * componentHandle)471         explicit MaterialPropertyBindingSignal(MaterialComponentManager::ComponentHandle* componentHandle)
472             : componentHandle_(componentHandle)
473         {}
474         ~MaterialPropertyBindingSignal() override = default;
475 
Signal()476         void Signal() override
477         {
478             componentHandle_->BindBindingProperties();
479 
480             // update generation etc..
481             ++componentHandle_->generation_;
482             if (CORE_NS::EntityUtil::IsValid(componentHandle_->entity_)) {
483                 componentHandle_->dirty_ = true;
484                 componentHandle_->manager_->Updated(componentHandle_->entity_);
485             }
486         }
487 
488     private:
489         MaterialComponentManager::ComponentHandle* componentHandle_ {};
490     };
491 
492     class MaterialPropertySignal final : public CustomPropertyWriteSignal {
493     public:
MaterialPropertySignal(MaterialComponentManager::ComponentHandle * componentHandle)494         explicit MaterialPropertySignal(MaterialComponentManager::ComponentHandle* componentHandle)
495             : componentHandle_(componentHandle)
496         {}
497         ~MaterialPropertySignal() override = default;
498 
Signal()499         void Signal() override
500         {
501             // update generation etc..
502             ++componentHandle_->generation_;
503             if (CORE_NS::EntityUtil::IsValid(componentHandle_->entity_)) {
504                 componentHandle_->dirty_ = true;
505                 componentHandle_->manager_->Updated(componentHandle_->entity_);
506             }
507         }
508 
509     private:
510         MaterialComponentManager::ComponentHandle* componentHandle_ {};
511     };
512 
513     class ComponentHandle : public IPropertyHandle, IPropertyApi {
514     public:
515         ComponentHandle() = delete;
516         ComponentHandle(MaterialComponentManager* owner, Entity entity) noexcept;
517         ComponentHandle(MaterialComponentManager* owner, Entity entity, const MaterialComponent& data) noexcept;
518         ~ComponentHandle() override = default;
519         ComponentHandle(const ComponentHandle& other) = delete;
520         ComponentHandle(ComponentHandle&& other) noexcept;
521         ComponentHandle& operator=(const ComponentHandle& other);
522         ComponentHandle& operator=(ComponentHandle&& other) noexcept;
523 
524         // IPropertyHandle
525         const IPropertyApi* Owner() const override;
526         size_t Size() const override;
527         const void* RLock() const override;
528         void RUnlock() const override;
529         void* WLock() override;
530         void WUnlock() override;
531 
532         // IPropertyApi
533         size_t PropertyCount() const override;
534         const Property* MetaData(size_t index) const override;
535         array_view<const Property> MetaData() const override;
536         uint64_t Type() const override;
537         IPropertyHandle* Create() const override;
538         IPropertyHandle* Clone(const IPropertyHandle* src) const override;
539         void Release(IPropertyHandle* handle) const override;
540 
541         void UpdateMetadata();
542         void FillMetadata(const RENDER_NS::RenderHandleReference& currentShader,
543             const CORE_NS::json::value*& propertiesJson, const CORE_NS::json::value*& propertiesBindingsJson,
544             bool updatedShader, array_view<const Property> previousProperties);
545         void CreateCustomBindings(
546             const CORE_NS::json::value& propertiesBindingsJson, const CustomPropertyBindingContainer* oldBindings);
547         // additional method to bind the data from binding properties
548         // NOTE: should use IShaderPipelineBinder later
549         void BindBindingProperties();
550 
551         MaterialComponentManager* manager_ { nullptr };
552         Entity entity_;
553         struct CachedShader {
554             // we do not keep reference here, if it's not in use then it could be destroyed
555             RenderHandle shader {};
556             // frame index to see the shader "timestamp"
557             uint64_t frameIndex { ~0ULL };
558         };
559         CachedShader cachedShader_;
560         refcnt_ptr<ReferencedProperties> metaData_;
561         uint32_t generation_ { 0 };
562 #ifndef NDEBUG
563         mutable uint32_t rLocked_ { 0 };
564         mutable bool wLocked_ { false };
565 #endif
566         bool dirty_ { false };
567         MaterialComponent data_;
568         BASE_NS::unique_ptr<CustomPropertyPodContainer> custom_;
569         BASE_NS::unique_ptr<CustomPropertyBindingContainer> customBindings_;
570 
571         // used with the custom bindings
572         MaterialPropertySignal propertySignal_;
573         MaterialPropertyBindingSignal propertyBindingSignal_;
574     };
575 
576     IEcs& ecs_;
577     IRenderHandleComponentManager* renderHandleManager_ { nullptr };
578     IShaderManager* shaderManager_ { nullptr };
579 
580     uint32_t generationCounter_ { 0 };
581     uint32_t modifiedFlags_ { 0 };
582     unordered_map<Entity, ComponentId> entityComponent_;
583     vector<ComponentHandle> components_;
584     vector<Entity> added_;
585     vector<Entity> removed_;
586     vector<Entity> updated_;
587     vector<Entity> moved_;
588 
589     unordered_map<RenderHandle, refcnt_ptr<ReferencedProperties>> properties_;
590 };
591 
MaterialComponentManager(IEcs & ecs)592 MaterialComponentManager::MaterialComponentManager(IEcs& ecs) noexcept
593     : ecs_(ecs), renderHandleManager_(GetManager<IRenderHandleComponentManager>(ecs))
594 {
595     if (CORE_NS::IEngine* engine = ecs_.GetClassFactory().GetInterface<CORE_NS::IEngine>(); engine) {
596         if (IRenderContext* renderContext =
597                 GetInstance<IRenderContext>(*engine->GetInterface<IClassRegister>(), UID_RENDER_CONTEXT);
598             renderContext) {
599             shaderManager_ = &renderContext->GetDevice().GetShaderManager();
600         }
601     }
602 
603     // Initial reservation for 64 components/entities.
604     // Will resize as needed.
605     constexpr size_t initialComponentReserveSize = 64;
606     components_.reserve(initialComponentReserveSize);
607     entityComponent_.reserve(initialComponentReserveSize);
608 
609     auto result =
610         properties_.insert_or_assign(RenderHandle {}, refcnt_ptr<ReferencedProperties>(new ReferencedProperties));
611     auto& properties = result.first->second->properties;
612     properties.reserve(componentMetaData_.size() + defaultMetaData_.size());
613     properties.append(componentMetaData_.begin(), componentMetaData_.end());
614     properties.append(defaultMetaData_.begin(), defaultMetaData_.end());
615 }
616 
617 MaterialComponentManager::~MaterialComponentManager() = default;
618 
619 // IPropertyApi
PropertyCount() const620 size_t MaterialComponentManager::PropertyCount() const
621 {
622     return componentMetaData_.size();
623 }
624 
MetaData(size_t index) const625 const Property* MaterialComponentManager::MetaData(size_t index) const
626 {
627     if (index < componentMetaData_.size()) {
628         return &componentMetaData_[index];
629     }
630     return nullptr;
631 }
632 
MetaData() const633 array_view<const Property> MaterialComponentManager::MetaData() const
634 {
635     return componentMetaData_;
636 }
637 
Create() const638 IPropertyHandle* MaterialComponentManager::Create() const
639 {
640     return new ComponentHandle(const_cast<MaterialComponentManager*>(this), {}, {});
641 }
642 
Clone(const IPropertyHandle * src) const643 IPropertyHandle* MaterialComponentManager::Clone(const IPropertyHandle* src) const
644 {
645     if (src) {
646         auto owner = src->Owner();
647         if (owner == this) {
648             auto* h = static_cast<const ComponentHandle*>(src);
649             return new ComponentHandle(const_cast<MaterialComponentManager*>(this), {}, h->data_);
650         } else if (owner) {
651             return owner->Clone(src);
652         }
653     }
654     return nullptr;
655 }
656 
Release(IPropertyHandle * dst) const657 void MaterialComponentManager::Release(IPropertyHandle* dst) const
658 {
659     if (!dst) {
660         return;
661     }
662     auto owner = dst->Owner();
663     if (owner == this) {
664         // we can only destroy things we "own" (know)
665         const auto* handle = static_cast<ComponentHandle*>(dst);
666         if (const auto id = GetComponentId(handle->entity_);
667             (id != IComponentManager::INVALID_COMPONENT_ID) && (&components_[id] == handle)) {
668             // This is one of the components (bound to an entity) so do nothing
669             return;
670         }
671         delete handle;
672     } else if (owner) {
673         owner->Release(dst);
674     }
675 }
676 
Type() const677 uint64_t MaterialComponentManager::Type() const
678 {
679     return typeHash_;
680 }
681 
682 // IComponentManager
GetName() const683 string_view MaterialComponentManager::GetName() const
684 {
685     constexpr auto name = CORE_NS::GetName<MaterialComponent>();
686     return name;
687 }
688 
GetUid() const689 Uid MaterialComponentManager::GetUid() const
690 {
691     return IMaterialComponentManager::UID;
692 }
693 
GetComponentCount() const694 size_t MaterialComponentManager::GetComponentCount() const
695 {
696     return components_.size();
697 }
698 
GetPropertyApi() const699 const IPropertyApi& MaterialComponentManager::GetPropertyApi() const
700 {
701     return *this;
702 }
703 
GetEntity(ComponentId index) const704 Entity MaterialComponentManager::GetEntity(ComponentId index) const
705 {
706     if (index < components_.size()) {
707         return components_[index].entity_;
708     }
709     return Entity();
710 }
711 
GetComponentGeneration(ComponentId index) const712 uint32_t MaterialComponentManager::GetComponentGeneration(ComponentId index) const
713 {
714     if (index < components_.size()) {
715         return components_[index].generation_;
716     }
717     return 0;
718 }
719 
HasComponent(Entity entity) const720 bool MaterialComponentManager::HasComponent(Entity entity) const
721 {
722     return GetComponentId(entity) != IComponentManager::INVALID_COMPONENT_ID;
723 }
724 
GetComponentId(Entity entity) const725 IComponentManager::ComponentId MaterialComponentManager::GetComponentId(Entity entity) const
726 {
727     if (CORE_NS::EntityUtil::IsValid(entity)) {
728         if (auto it = entityComponent_.find(entity); it != entityComponent_.end()) {
729             return it->second;
730         }
731     }
732     return IComponentManager::INVALID_COMPONENT_ID;
733 }
734 
Create(Entity entity)735 void MaterialComponentManager::Create(Entity entity)
736 {
737     if (CORE_NS::EntityUtil::IsValid(entity)) {
738         if (auto it = entityComponent_.find(entity); it == entityComponent_.end()) {
739             entityComponent_.insert({ entity, static_cast<ComponentId>(components_.size()) });
740             const auto oldCapacity = components_.capacity();
741             auto& component = components_.emplace_back(this, entity);
742             if (components_.capacity() != oldCapacity) {
743                 moved_.reserve(moved_.size() + components_.size());
744                 for (const auto& handle : components_) {
745                     moved_.push_back(handle.entity_);
746                 }
747                 modifiedFlags_ |= CORE_NS::CORE_COMPONENT_MANAGER_COMPONENT_MOVED_BIT;
748             }
749             added_.push_back(entity);
750             modifiedFlags_ |= CORE_NS::CORE_COMPONENT_MANAGER_COMPONENT_ADDED_BIT;
751             generationCounter_++;
752 
753             // lock/unlock for toggling component updated behavior.
754             component.WLock();
755             component.WUnlock();
756         } else {
757             if (auto dst = CORE_NS::ScopedHandle<MaterialComponent>(&components_[it->second]); dst) {
758                 *dst = {};
759             }
760         }
761     }
762 }
763 
Destroy(Entity entity)764 bool MaterialComponentManager::Destroy(Entity entity)
765 {
766     if (CORE_NS::EntityUtil::IsValid(entity)) {
767         if (auto it = entityComponent_.find(entity); it != entityComponent_.end()) {
768             components_[it->second].entity_ = {}; // invalid entity. (marks it as ready for re-use)
769             entityComponent_.erase(it);
770             removed_.push_back(entity);
771             modifiedFlags_ |= CORE_NS::CORE_COMPONENT_MANAGER_COMPONENT_REMOVED_BIT;
772             generationCounter_++;
773             return true;
774         }
775     }
776     return false;
777 }
778 
Gc()779 void MaterialComponentManager::Gc()
780 {
781     const bool hasRemovedComponents = modifiedFlags_ & CORE_NS::CORE_COMPONENT_MANAGER_COMPONENT_REMOVED_BIT;
782     if (!hasRemovedComponents) {
783         return;
784     }
785     ComponentId componentCount = static_cast<ComponentId>(components_.size());
786     for (ComponentId id = 0; id < componentCount;) {
787         if (CORE_NS::EntityUtil::IsValid(components_[id].entity_)) {
788             ++id;
789             continue;
790         }
791         // invalid entity.. if so clean garbage
792         // find last valid and swap with it
793         ComponentId rid = componentCount - 1;
794         while ((rid > id) && !CORE_NS::EntityUtil::IsValid(components_[rid].entity_)) {
795             --rid;
796         }
797         if ((rid > id) && CORE_NS::EntityUtil::IsValid(components_[rid].entity_)) {
798             moved_.push_back(components_[rid].entity_);
799             // fix the entityComponent_ map (update the component id for the entity)
800             entityComponent_[components_[rid].entity_] = id;
801             components_[id] = BASE_NS::move(components_[rid]);
802         }
803         --componentCount;
804     }
805     if (!moved_.empty()) {
806         modifiedFlags_ |= CORE_NS::CORE_COMPONENT_MANAGER_COMPONENT_MOVED_BIT;
807     }
808     if (components_.size() > componentCount) {
809         auto diff = static_cast<typename decltype(components_)::difference_type>(componentCount);
810         components_.erase(components_.cbegin() + diff, components_.cend());
811     }
812 }
813 
Destroy(const array_view<const Entity> gcList)814 void MaterialComponentManager::Destroy(const array_view<const Entity> gcList)
815 {
816     for (const Entity e : gcList) {
817         Destroy(e);
818     }
819 }
820 
GetAddedComponents()821 vector<Entity> MaterialComponentManager::GetAddedComponents()
822 {
823     return BASE_NS::move(added_);
824 }
825 
GetRemovedComponents()826 vector<Entity> MaterialComponentManager::GetRemovedComponents()
827 {
828     return BASE_NS::move(removed_);
829 }
830 
GetUpdatedComponents()831 vector<Entity> MaterialComponentManager::GetUpdatedComponents()
832 {
833     vector<Entity> updated;
834     if (modifiedFlags_ & MODIFIED) {
835         modifiedFlags_ &= ~MODIFIED;
836         updated.reserve(components_.size() / 2); // 2: half
837         for (auto& handle : components_) {
838             if (handle.dirty_) {
839                 handle.dirty_ = false;
840                 updated.push_back(handle.entity_);
841             }
842         }
843     }
844     return updated;
845 }
846 
GetMovedComponents()847 vector<Entity> MaterialComponentManager::GetMovedComponents()
848 {
849     return BASE_NS::move(moved_);
850 }
851 
GetModifiedFlags() const852 CORE_NS::ComponentManagerModifiedFlags MaterialComponentManager::GetModifiedFlags() const
853 {
854     return modifiedFlags_ & ~MODIFIED;
855 }
856 
ClearModifiedFlags()857 void MaterialComponentManager::ClearModifiedFlags()
858 {
859     modifiedFlags_ &= MODIFIED;
860 }
861 
GetGenerationCounter() const862 uint32_t MaterialComponentManager::GetGenerationCounter() const
863 {
864     return generationCounter_;
865 }
866 
SetData(Entity entity,const IPropertyHandle & dataHandle)867 void MaterialComponentManager::SetData(Entity entity, const IPropertyHandle& dataHandle)
868 {
869     if (!IsMatchingHandle(dataHandle)) {
870         return;
871     }
872     if (const auto src = ScopedHandle<const MaterialComponent>(&dataHandle); src) {
873         if (const auto it = entityComponent_.find(entity); it != entityComponent_.end()) {
874             components_[it->second] = static_cast<const ComponentHandle&>(dataHandle);
875         }
876     }
877 }
878 
GetData(Entity entity) const879 const IPropertyHandle* MaterialComponentManager::GetData(Entity entity) const
880 {
881     if (CORE_NS::EntityUtil::IsValid(entity)) {
882         if (const auto it = entityComponent_.find(entity); it != entityComponent_.end()) {
883             if (it->second < components_.size()) {
884                 return &components_[it->second];
885             }
886         }
887     }
888     return nullptr;
889 }
890 
GetData(Entity entity)891 IPropertyHandle* MaterialComponentManager::GetData(Entity entity)
892 {
893     if (CORE_NS::EntityUtil::IsValid(entity)) {
894         if (const auto it = entityComponent_.find(entity); it != entityComponent_.end()) {
895             if (it->second < components_.size()) {
896                 return &components_[it->second];
897             }
898         }
899     }
900     return nullptr;
901 }
902 
SetData(ComponentId index,const IPropertyHandle & dataHandle)903 void MaterialComponentManager::SetData(ComponentId index, const IPropertyHandle& dataHandle)
904 {
905     if (!IsMatchingHandle(dataHandle)) {
906         // We could verify the metadata here.
907         // And in copy only the matching properties one-by-one also.
908         return;
909     }
910     if (index < components_.size()) {
911         if (const auto src = ScopedHandle<const MaterialComponent>(&dataHandle); src) {
912             components_[index] = static_cast<const ComponentHandle&>(dataHandle);
913         }
914     }
915 }
916 
GetData(ComponentId index) const917 const IPropertyHandle* MaterialComponentManager::GetData(ComponentId index) const
918 {
919     if (index < components_.size()) {
920         return &components_[index];
921     }
922     return nullptr;
923 }
924 
GetData(ComponentId index)925 IPropertyHandle* MaterialComponentManager::GetData(ComponentId index)
926 {
927     if (index < components_.size()) {
928         return &components_[index];
929     }
930     return nullptr;
931 }
932 
GetEcs() const933 IEcs& MaterialComponentManager::GetEcs() const
934 {
935     return ecs_;
936 }
937 
938 // IMaterialComponentManager
Get(ComponentId index) const939 MaterialComponent MaterialComponentManager::Get(ComponentId index) const
940 {
941     if (auto handle = Read(index); handle) {
942         return *handle;
943     }
944     return MaterialComponent {};
945 }
946 
Get(Entity entity) const947 MaterialComponent MaterialComponentManager::Get(Entity entity) const
948 {
949     if (auto handle = Read(entity); handle) {
950         return *handle;
951     }
952     return MaterialComponent {};
953 }
954 
Set(ComponentId index,const MaterialComponent & data)955 void MaterialComponentManager::Set(ComponentId index, const MaterialComponent& data)
956 {
957     if (auto handle = Write(index); handle) {
958         *handle = data;
959     }
960 }
961 
Set(Entity entity,const MaterialComponent & data)962 void MaterialComponentManager::Set(Entity entity, const MaterialComponent& data)
963 {
964     if (CORE_NS::EntityUtil::IsValid(entity)) {
965         if (auto handle = Write(entity); handle) {
966             *handle = data;
967             // NOTE: customProperties are valid when updating itself
968         } else {
969             entityComponent_.insert({ entity, static_cast<ComponentId>(components_.size()) });
970             const auto oldCapacity = components_.capacity();
971             auto& component = components_.emplace_back(this, entity, data);
972             if (components_.capacity() != oldCapacity) {
973                 moved_.reserve(moved_.size() + components_.size());
974                 for (const auto& componentHandle : components_) {
975                     moved_.push_back(componentHandle.entity_);
976                 }
977                 modifiedFlags_ |= CORE_NS::CORE_COMPONENT_MANAGER_COMPONENT_MOVED_BIT;
978             }
979             added_.push_back(entity);
980             modifiedFlags_ |= CORE_NS::CORE_COMPONENT_MANAGER_COMPONENT_ADDED_BIT;
981             generationCounter_++;
982 
983             // lock/unlock for toggling component updated behavior.
984             component.WLock();
985             component.WUnlock();
986         }
987     }
988 }
989 
Read(ComponentId index) const990 ScopedHandle<const MaterialComponent> MaterialComponentManager::Read(ComponentId index) const
991 {
992     return ScopedHandle<const MaterialComponent> { GetData(index) };
993 }
994 
Read(Entity entity) const995 ScopedHandle<const MaterialComponent> MaterialComponentManager::Read(Entity entity) const
996 {
997     return ScopedHandle<const MaterialComponent> { GetData(entity) };
998 }
999 
Write(ComponentId index)1000 ScopedHandle<MaterialComponent> MaterialComponentManager::Write(ComponentId index)
1001 {
1002     return ScopedHandle<MaterialComponent> { GetData(index) };
1003 }
1004 
Write(Entity entity)1005 ScopedHandle<MaterialComponent> MaterialComponentManager::Write(Entity entity)
1006 {
1007     return ScopedHandle<MaterialComponent> { GetData(entity) };
1008 }
1009 
1010 // Internal
Updated(Entity entity)1011 void MaterialComponentManager::Updated(Entity entity)
1012 {
1013     CORE_ASSERT_MSG(CORE_NS::EntityUtil::IsValid(entity), "Invalid ComponentId, bound to INVALID_ENTITY");
1014     modifiedFlags_ |= CORE_NS::CORE_COMPONENT_MANAGER_COMPONENT_UPDATED_BIT | MODIFIED;
1015     generationCounter_++;
1016 }
1017 
IsMatchingHandle(const IPropertyHandle & dataHandle)1018 bool MaterialComponentManager::IsMatchingHandle(const IPropertyHandle& dataHandle)
1019 {
1020     if (dataHandle.Owner() == this) {
1021         return true;
1022     }
1023     if (dataHandle.Owner() && (dataHandle.Owner()->Type() == typeHash_)) {
1024         return true;
1025     }
1026     return false;
1027 }
1028 
1029 // Handle implementation
ComponentHandle(MaterialComponentManager * owner,Entity entity)1030 MaterialComponentManager::ComponentHandle::ComponentHandle(MaterialComponentManager* owner, Entity entity) noexcept
1031     : ComponentHandle(owner, entity, {})
1032 {}
1033 
1034 WARNING_SCOPE_START(W_THIS_USED_BASE_INITIALIZER_LIST);
ComponentHandle(MaterialComponentManager * owner,Entity entity,const MaterialComponent & data)1035 MaterialComponentManager::ComponentHandle::ComponentHandle(
1036     MaterialComponentManager* owner, Entity entity, const MaterialComponent& data) noexcept
1037     : manager_(owner), entity_(entity), data_(data), propertySignal_(this), propertyBindingSignal_(this)
1038 {}
1039 
ComponentHandle(ComponentHandle && other)1040 MaterialComponentManager::ComponentHandle::ComponentHandle(ComponentHandle&& other) noexcept
1041     : manager_(other.manager_), entity_(exchange(other.entity_, {})), cachedShader_(exchange(other.cachedShader_, {})),
1042       metaData_(exchange(other.metaData_, nullptr)), generation_(exchange(other.generation_, 0U)),
1043 #ifndef NDEBUG
1044       rLocked_(exchange(other.rLocked_, 0U)), wLocked_(exchange(other.wLocked_, false)),
1045 #endif
1046       data_(exchange(other.data_, {})), custom_(exchange(other.custom_, {})),
1047       customBindings_(exchange(other.customBindings_, {})), propertySignal_(this), propertyBindingSignal_(this)
1048 {
1049     if (custom_) {
1050         custom_->UpdateSignal(propertySignal_);
1051     }
1052     if (customBindings_) {
1053         customBindings_->UpdateSignal(propertyBindingSignal_);
1054     }
1055 }
1056 WARNING_SCOPE_END();
1057 
operator =(ComponentHandle && other)1058 MaterialComponentManager::ComponentHandle& MaterialComponentManager::ComponentHandle::operator=(
1059     ComponentHandle&& other) noexcept
1060 {
1061     if (this != &other) {
1062         CORE_ASSERT(manager_ == other.manager_);
1063         entity_ = exchange(other.entity_, {});
1064         cachedShader_ = exchange(other.cachedShader_, {});
1065         metaData_ = exchange(other.metaData_, nullptr);
1066         generation_ = exchange(other.generation_, 0U);
1067 #ifndef NDEBUG
1068         rLocked_ = exchange(other.rLocked_, 0U);
1069         wLocked_ = exchange(other.wLocked_, false);
1070 #endif
1071         dirty_ = exchange(other.dirty_, false);
1072         data_ = exchange(other.data_, {});
1073         custom_ = exchange(other.custom_, {});
1074         customBindings_ = exchange(other.customBindings_, {});
1075         if (custom_) {
1076             custom_->UpdateSignal(propertySignal_);
1077         }
1078         if (customBindings_) {
1079             customBindings_->UpdateSignal(propertyBindingSignal_);
1080         }
1081     }
1082     return *this;
1083 }
1084 
1085 // ComponentHandle IPropertyHandle
Owner() const1086 const IPropertyApi* MaterialComponentManager::ComponentHandle::Owner() const
1087 {
1088     return this;
1089 }
1090 
Size() const1091 size_t MaterialComponentManager::ComponentHandle::Size() const
1092 {
1093     return sizeof(MaterialComponent);
1094 }
1095 
RLock() const1096 const void* MaterialComponentManager::ComponentHandle::RLock() const
1097 {
1098     CORE_ASSERT(manager_);
1099 #ifndef NDEBUG
1100     CORE_ASSERT(!wLocked_);
1101     rLocked_++;
1102 #endif
1103     return &data_;
1104 }
1105 
RUnlock() const1106 void MaterialComponentManager::ComponentHandle::RUnlock() const
1107 {
1108     CORE_ASSERT(manager_);
1109 #ifndef NDEBUG
1110     CORE_ASSERT(rLocked_ > 0U);
1111     rLocked_--;
1112 #endif
1113 }
1114 
WLock()1115 void* MaterialComponentManager::ComponentHandle::WLock()
1116 {
1117     CORE_ASSERT(manager_);
1118 #ifndef NDEBUG
1119     CORE_ASSERT(rLocked_ <= 1U && !wLocked_);
1120     wLocked_ = true;
1121 #endif
1122     return &data_;
1123 }
1124 
WUnlock()1125 void MaterialComponentManager::ComponentHandle::WUnlock()
1126 {
1127     CORE_ASSERT(manager_);
1128 #ifndef NDEBUG
1129     CORE_ASSERT(wLocked_);
1130     wLocked_ = false;
1131 #endif
1132     // update generation etc..
1133     generation_++;
1134     if (CORE_NS::EntityUtil::IsValid(entity_)) {
1135         dirty_ = true;
1136         UpdateMetadata();
1137         manager_->Updated(entity_);
1138     }
1139 }
1140 
1141 // ComponentHandle IPropertyApi
PropertyCount() const1142 size_t MaterialComponentManager::ComponentHandle::PropertyCount() const
1143 {
1144     if (!metaData_) {
1145         return manager_->PropertyCount();
1146     } else {
1147         return metaData_->properties.size();
1148     }
1149 }
1150 
MetaData(size_t index) const1151 const Property* MaterialComponentManager::ComponentHandle::MetaData(size_t index) const
1152 {
1153     if (!metaData_) {
1154         return manager_->MetaData(index);
1155     } else if (index < metaData_->properties.size()) {
1156         return &metaData_->properties[index];
1157     }
1158     return nullptr;
1159 }
1160 
MetaData() const1161 array_view<const Property> MaterialComponentManager::ComponentHandle::MetaData() const
1162 {
1163     if (!metaData_) {
1164         return manager_->MetaData();
1165     } else {
1166         return metaData_->properties;
1167     }
1168 }
1169 
Type() const1170 uint64_t MaterialComponentManager::ComponentHandle::Type() const
1171 {
1172     return manager_->Type();
1173 }
1174 
Create() const1175 IPropertyHandle* MaterialComponentManager::ComponentHandle::Create() const
1176 {
1177     return new ComponentHandle(manager_, {}, {});
1178 }
1179 
Clone(const IPropertyHandle * src) const1180 IPropertyHandle* MaterialComponentManager::ComponentHandle::Clone(const IPropertyHandle* src) const
1181 {
1182     if (src) {
1183         auto owner = src->Owner();
1184         if (owner == this) {
1185             auto* h = static_cast<const ComponentHandle*>(src);
1186             return new ComponentHandle(h->manager_, {}, h->data_);
1187         } else if (owner) {
1188             return owner->Clone(src);
1189         }
1190         return manager_->Clone(src);
1191     }
1192     return nullptr;
1193 }
1194 
Release(IPropertyHandle * handle) const1195 void MaterialComponentManager::ComponentHandle::Release(IPropertyHandle* handle) const
1196 {
1197     if (handle) {
1198         auto owner = handle->Owner();
1199         if (owner == this) {
1200             auto* componentHandle = static_cast<ComponentHandle*>(handle);
1201             if (auto id = manager_->GetComponentId(componentHandle->entity_);
1202                 id != IComponentManager::INVALID_COMPONENT_ID) {
1203                 if (manager_->GetData(id) == componentHandle) {
1204                     // This is one of the components (bound to an entity) so do nothing
1205                     return;
1206                 }
1207             }
1208             delete componentHandle;
1209         } else if (owner) {
1210             owner->Release(handle);
1211         }
1212     }
1213 }
1214 
1215 // ComponentHandle
operator =(const MaterialComponentManager::ComponentHandle & other)1216 MaterialComponentManager::ComponentHandle& MaterialComponentManager::ComponentHandle::operator=(
1217     const MaterialComponentManager::ComponentHandle& other)
1218 {
1219     if (this != &other) {
1220         if (!manager_->renderHandleManager_) {
1221             manager_->renderHandleManager_ = GetManager<IRenderHandleComponentManager>(manager_->ecs_);
1222         }
1223 
1224         // copy the material component, but..
1225         data_ = other.data_;
1226         // ..forget the custom property values and create new ones
1227         data_.customProperties = nullptr;
1228         data_.customBindingProperties = nullptr;
1229 
1230         const CORE_NS::json::value* propertiesJson = nullptr;
1231         const CORE_NS::json::value* propertiesBindingsJson = nullptr;
1232         if (manager_ == other.manager_) {
1233             // for the same manager instance we can use the same metadata
1234             cachedShader_ = other.cachedShader_;
1235             metaData_ = other.metaData_;
1236         } else {
1237             // for a different manager instance find metadata matching the shader or create new metadata
1238             if (manager_->renderHandleManager_ && manager_->shaderManager_) {
1239                 const auto currentShader =
1240                     manager_->renderHandleManager_->GetRenderHandleReference(data_.materialShader.shader);
1241                 const auto frameIndex = currentShader ? manager_->shaderManager_->GetFrameIndex(currentShader) : 0U;
1242                 const bool updatedShader = other.cachedShader_.frameIndex != frameIndex;
1243                 cachedShader_ = { currentShader.GetHandle(), frameIndex };
1244 
1245                 if (auto pos = manager_->properties_.find(cachedShader_.shader);
1246                     (pos != manager_->properties_.end()) && !updatedShader) {
1247                     metaData_ = pos->second;
1248                 } else {
1249                     FillMetadata(currentShader, propertiesJson, propertiesBindingsJson, updatedShader,
1250                         other.metaData_ ? other.metaData_->properties : array_view<const Property> {});
1251                 }
1252             }
1253         }
1254 
1255         // find the custom properties for the shader
1256         if (data_.materialShader.shader && !propertiesJson && !propertiesBindingsJson) {
1257             if (manager_->renderHandleManager_ && manager_->shaderManager_) {
1258                 auto currentShader =
1259                     manager_->renderHandleManager_->GetRenderHandleReference(data_.materialShader.shader);
1260 
1261                 if (auto* metaJson = manager_->shaderManager_->GetMaterialMetadata(currentShader);
1262                     metaJson && metaJson->is_array()) {
1263                     if (auto* material = FindMaterialComponentJson(*metaJson); material) {
1264                         if (auto* propJson = material->find(CUSTOM_PROPERTIES); propJson && propJson->is_array()) {
1265                             propertiesJson = propJson;
1266                         }
1267                         if (auto* propJson = material->find(CUSTOM_BINDING_PROPERTIES);
1268                             propJson && propJson->is_array()) {
1269                             propertiesBindingsJson = propJson;
1270                         }
1271                     }
1272                 }
1273             }
1274         }
1275 
1276         if (metaData_ && (propertiesJson || propertiesBindingsJson)) {
1277             // create and fill new POD container based on 'other's container
1278             custom_ = CreateCustomPropertyPodContainer(propertySignal_, propertiesJson, other.custom_.get());
1279             data_.customProperties = custom_.get();
1280 
1281             // create and fill new binding container
1282             if (propertiesBindingsJson) {
1283                 CreateCustomBindings(*propertiesBindingsJson, other.customBindings_.get());
1284             }
1285         } else if (!metaData_) {
1286             if (auto pos = manager_->properties_.find(RenderHandle {}); pos != manager_->properties_.cend()) {
1287                 metaData_ = pos->second;
1288             }
1289         }
1290 
1291         ++generation_;
1292         dirty_ = true;
1293         manager_->Updated(entity_);
1294     }
1295 
1296     return *this;
1297 }
1298 
UpdateMetadata()1299 void MaterialComponentManager::ComponentHandle::UpdateMetadata()
1300 {
1301     if (!manager_->renderHandleManager_) {
1302         manager_->renderHandleManager_ = GetManager<IRenderHandleComponentManager>(manager_->ecs_);
1303         if (!manager_->renderHandleManager_) {
1304             return;
1305         }
1306     }
1307     if (!manager_->shaderManager_) {
1308         return;
1309     }
1310     const auto currentShader = manager_->renderHandleManager_->GetRenderHandleReference(data_.materialShader.shader);
1311     const auto frameIndex = currentShader ? manager_->shaderManager_->GetFrameIndex(currentShader) : 0U;
1312     const bool newShader = (cachedShader_.shader != currentShader.GetHandle());
1313     const bool updatedShader = !newShader && (cachedShader_.frameIndex != frameIndex);
1314     if (!metaData_ || newShader || updatedShader) {
1315         cachedShader_ = { currentShader.GetHandle(), frameIndex };
1316 
1317         const CORE_NS::json::value* propertiesJson = nullptr;
1318         const CORE_NS::json::value* propertiesBindingsJson = nullptr;
1319         auto& propertyCache = manager_->properties_;
1320         if ((newShader || updatedShader) && currentShader) {
1321             FillMetadata(currentShader, propertiesJson, propertiesBindingsJson, updatedShader,
1322                 metaData_ ? metaData_->properties : array_view<const Property> {});
1323         } else if (auto cached = propertyCache.find(cachedShader_.shader); cached != propertyCache.end()) {
1324             metaData_ = cached->second;
1325         }
1326 
1327         data_.customProperties = nullptr;
1328         data_.customBindingProperties = nullptr;
1329         if (metaData_ && (propertiesJson || propertiesBindingsJson)) {
1330             // create and fill new POD container
1331             custom_ = CreateCustomPropertyPodContainer(
1332                 propertySignal_, propertiesJson, updatedShader ? custom_.get() : nullptr);
1333             data_.customProperties = custom_.get();
1334 
1335             // create and fill new binding container
1336             if (propertiesBindingsJson) {
1337                 CreateCustomBindings(*propertiesBindingsJson, updatedShader ? customBindings_.get() : nullptr);
1338             }
1339         } else if (!metaData_) {
1340             if (auto pos = manager_->properties_.find(RenderHandle {}); pos != manager_->properties_.cend()) {
1341                 metaData_ = pos->second;
1342             }
1343         }
1344     }
1345 }
1346 
FillMetadata(const RenderHandleReference & currentShader,const CORE_NS::json::value * & propertiesJson,const CORE_NS::json::value * & propertiesBindingsJson,bool updatedShader,array_view<const Property> oldProperties)1347 void MaterialComponentManager::ComponentHandle::FillMetadata(const RenderHandleReference& currentShader,
1348     const CORE_NS::json::value*& propertiesJson, const CORE_NS::json::value*& propertiesBindingsJson,
1349     bool updatedShader, array_view<const Property> oldProperties)
1350 {
1351     if (!manager_->shaderManager_) {
1352         return;
1353     }
1354 
1355     // start with a blank property collection
1356     auto newMetaData = refcnt_ptr<ReferencedProperties>(new ReferencedProperties);
1357     auto& newProperties = newMetaData->properties;
1358 
1359     // insert basic properties from the component
1360     newProperties.append(componentMetaData_.begin(), componentMetaData_.end());
1361     const auto basicProperties = newProperties.size();
1362 
1363     // go through the metadata which should be an array of metadata for different component types
1364     if (auto* metaJson = manager_->shaderManager_->GetMaterialMetadata(currentShader);
1365         metaJson && metaJson->is_array()) {
1366         if (auto* material = FindMaterialComponentJson(*metaJson); material) {
1367             // insert properties for textures
1368             AppendProperties(newProperties, newMetaData->stringStorage, *material, componentMetaData_);
1369 
1370             if (auto* propJson = material->find(CUSTOM_PROPERTIES); propJson && propJson->is_array()) {
1371                 propertiesJson = propJson;
1372             }
1373             if (auto* propJson = material->find(CUSTOM_BINDING_PROPERTIES); propJson && propJson->is_array()) {
1374                 propertiesBindingsJson = propJson;
1375             }
1376         }
1377     }
1378     // if the material had metadata with customized TextureInfos try to map old locations to new ones.
1379     if (updatedShader && (oldProperties.size() > basicProperties) && (newProperties.size() > basicProperties)) {
1380         MapTextureSlots(data_.textures, offsetof(MaterialComponent, textures),
1381             array_view(newProperties.cbegin().ptr() + basicProperties, newProperties.cend().ptr()),
1382             array_view(oldProperties.cbegin().ptr() + basicProperties, oldProperties.cend().ptr()));
1383     }
1384 
1385     // replace previous properties with the new updated properties in the cache and this component handle
1386     manager_->properties_.insert_or_assign(currentShader.GetHandle(), newMetaData);
1387     metaData_ = move(newMetaData);
1388 }
1389 
CreateCustomBindings(const CORE_NS::json::value & propertiesBindingsJson,const CustomPropertyBindingContainer * oldBindings)1390 void MaterialComponentManager::ComponentHandle::CreateCustomBindings(
1391     const CORE_NS::json::value& propertiesBindingsJson, const CustomPropertyBindingContainer* oldBindings)
1392 {
1393     auto newBindings = BASE_NS::make_unique<CustomPropertyBindingContainer>(propertyBindingSignal_);
1394     UpdateCustomBindingPropertyMetadata(propertiesBindingsJson, *newBindings);
1395     if (oldBindings) {
1396         newBindings->CopyValues(*oldBindings);
1397     }
1398     customBindings_ = BASE_NS::move(newBindings);
1399     data_.customBindingProperties = customBindings_.get();
1400 }
1401 
BindBindingProperties()1402 void MaterialComponentManager::ComponentHandle::BindBindingProperties()
1403 {
1404     // fetch the bindings from properties, and try to bind them
1405     if (customBindings_ && (customBindings_->PropertyCount() > 0)) {
1406         uint32_t bindingIdx = 0;
1407         const uint32_t customBindingCount = static_cast<uint32_t>(customBindings_->PropertyCount());
1408         if (data_.customResources.size() < customBindingCount) {
1409             data_.customResources.resize(customBindingCount);
1410         }
1411         const uint32_t customResourceCount = static_cast<uint32_t>(data_.customResources.size());
1412         for (auto& prop : customBindings_->Owner()->MetaData()) {
1413             switch (prop.type) {
1414                 case CORE_NS::PropertyType::ENTITY_REFERENCE_T: {
1415                     if (bindingIdx < customResourceCount) {
1416                         if (const auto ent = customBindings_->GetValue<CORE_NS::EntityReference>(bindingIdx); ent) {
1417                             data_.customResources[bindingIdx] = ent;
1418                         }
1419                     }
1420                 } break;
1421                 default: {
1422                 } break;
1423             }
1424             bindingIdx++;
1425         }
1426     }
1427 }
1428 
IMaterialComponentManagerInstance(IEcs & ecs)1429 IComponentManager* IMaterialComponentManagerInstance(IEcs& ecs)
1430 {
1431     return new MaterialComponentManager(ecs);
1432 }
1433 
IMaterialComponentManagerDestroy(IComponentManager * instance)1434 void IMaterialComponentManagerDestroy(IComponentManager* instance)
1435 {
1436     delete static_cast<MaterialComponentManager*>(instance);
1437 }
1438 CORE3D_END_NAMESPACE()
1439