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