1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include <PropertyTools/property_data.h>
17 #include <algorithm>
18 #include <charconv>
19 #include <cinttypes>
20 #include <functional>
21 
22 #include <3d/ecs/components/render_handle_component.h>
23 #include <base/util/base64_decode.h>
24 #include <base/util/base64_encode.h>
25 #include <base/util/uid_util.h>
26 #include <core/ecs/intf_component_manager.h>
27 #include <core/ecs/intf_entity_manager.h>
28 #include <core/image/intf_image_loader_manager.h>
29 #include <core/intf_engine.h>
30 #include <core/property/intf_property_handle.h>
31 #include <core/property/property_types.h>
32 #include <ecs_serializer/ecs_property_util.h>
33 #include <render/device/intf_gpu_resource_manager.h>
34 #include <render/device/intf_shader_manager.h>
35 #include <render/intf_render_context.h>
36 #include <render/util/intf_render_util.h>
37 #include <util/json_util.h>
38 #include <util/path_util.h>
39 
40 #include "ecs_serializer/intf_ecs_serializer.h"
41 
42 using namespace ;
43 using namespace CORE_NS;
44 using namespace RENDER_NS;
45 using namespace CORE3D_NS;
46 using namespace UTIL_NS;
47 
48 ECS_SERIALIZER_BEGIN_NAMESPACE()
49 
50 class EcsSerializer : public IEcsSerializer {
51 public:
52     explicit EcsSerializer(RENDER_NS::IRenderContext& renderContext);
53 
54     // From IEcsSerializer
55     void SetListener(IListener* listener) override;
56 
57     void SetDefaultSerializers() override;
58     void SetSerializer(const CORE_NS::PropertyTypeDecl& type, IPropertySerializer& serializer) override;
59 
60     bool WriteEntityCollection(const IEntityCollection& ec, CORE_NS::json::standalone_value& jsonOut) const override;
61     bool WriteComponents(
62         const IEntityCollection& ec, CORE_NS::Entity entity, CORE_NS::json::standalone_value& jsonOut) const override;
63     bool WriteComponent(const IEntityCollection& ec, CORE_NS::Entity entity, const CORE_NS::IComponentManager& cm,
64         CORE_NS::IComponentManager::ComponentId id, CORE_NS::json::standalone_value& jsonOut) const override;
65     bool WriteProperty(const IEntityCollection& ec, const CORE_NS::Property& property, uintptr_t offset,
66         CORE_NS::json::standalone_value& jsonOut) const override;
67 
68     bool GatherExternalCollections(const CORE_NS::json::value& jsonIn, ::string_view contextUri,
69         ::vector<ExternalCollection>& externalCollectionsOut) const override;
70 
71     IoUtil::SerializationResult ReadEntityCollection(
72         IEntityCollection& ec, const CORE_NS::json::value& jsonIn, ::string_view contextUri) const override;
73     bool ReadComponents(IEntityCollection& ec, const CORE_NS::json::value& jsonIn) const override;
74     bool ReadComponent(IEntityCollection& ec, const CORE_NS::json::value& jsonIn, CORE_NS::Entity entity,
75         CORE_NS::IComponentManager& component) const override;
76     bool ReadProperty(IEntityCollection& ec, const CORE_NS::json::value& jsonIn, const CORE_NS::Property& property,
77         uintptr_t offset) const override;
78 
79     RENDER_NS::RenderHandleReference LoadImageResource(::string_view uri) const override;
80 
81 protected:
82     void Destroy() override;
83 
84 private:
85     using PropertyToJsonFunc = std::function<bool(
86         const IEntityCollection& ec, const CORE_NS::Property&, uintptr_t, CORE_NS::json::standalone_value&)>;
87     using PropertyFromJsonFunc = std::function<bool(
88         const IEntityCollection& ec, const CORE_NS::json::value&, const CORE_NS::Property&, uintptr_t)>;
89     IPropertySerializer& Add(PropertyToJsonFunc toJson, PropertyFromJsonFunc fromJson);
90 
91     // A little wrapper class to create a serializer with functions for writing and readig a value.
92     class SimpleJsonSerializer : public IPropertySerializer {
93     public:
94         bool ToJson(const IEntityCollection& ec, const CORE_NS::Property& property, uintptr_t offset,
95             CORE_NS::json::standalone_value& jsonOut) const override;
96         bool FromJson(const IEntityCollection& ec, const CORE_NS::json::value& jsonIn,
97             const CORE_NS::Property& property, uintptr_t offset) const override;
98 
99         SimpleJsonSerializer(PropertyToJsonFunc toJson, PropertyFromJsonFunc fromJson);
100         virtual ~SimpleJsonSerializer() = default;
101 
102     private:
103         const PropertyToJsonFunc PropertyToJson;
104         const PropertyFromJsonFunc PropertyFromJson;
105     };
106     ::vector<::unique_ptr<SimpleJsonSerializer>> ownedSerializers_;
107 
108     RENDER_NS::IRenderContext& renderContext_;
109 
110     // Mapping from each property type to a apecific serializer.
111     using SerializerMap = ::unordered_map<CORE_NS::PropertyTypeDecl, IPropertySerializer*>;
112     SerializerMap typetoSerializerMap_;
113 
114     IListener* listener_ {};
115 };
116 
117 namespace {
118 template<typename Type>
GetPropertyValue(uintptr_t ptr)119 Type& GetPropertyValue(uintptr_t ptr)
120 {
121     return *reinterpret_cast<Type*>(ptr);
122 }
123 } // namespace
124 
125 // EcsSerializer::SimpleJsonSerializer
ToJson( const IEntityCollection& ec, const Property& property, uintptr_t offset, json::standalone_value& jsonOut) const126 bool EcsSerializer::SimpleJsonSerializer::ToJson(
127     const IEntityCollection& ec, const Property& property, uintptr_t offset, json::standalone_value& jsonOut) const
128 {
129     return PropertyToJson(ec, property, offset, jsonOut);
130 }
131 
FromJson( const IEntityCollection& ec, const json::value& jsonIn, const Property& property, uintptr_t offset) const132 bool EcsSerializer::SimpleJsonSerializer::FromJson(
133     const IEntityCollection& ec, const json::value& jsonIn, const Property& property, uintptr_t offset) const
134 {
135     return PropertyFromJson(ec, jsonIn, property, offset);
136 }
137 
SimpleJsonSerializer(PropertyToJsonFunc toJson, PropertyFromJsonFunc fromJson)138 EcsSerializer::SimpleJsonSerializer::SimpleJsonSerializer(PropertyToJsonFunc toJson, PropertyFromJsonFunc fromJson)
139     : PropertyToJson(toJson), PropertyFromJson(fromJson)
140 {}
141 
142 // EcsSerializer
EcsSerializer(RENDER_NS::IRenderContext& renderContext)143 EcsSerializer::EcsSerializer(RENDER_NS::IRenderContext& renderContext) : renderContext_(renderContext) {}
144 
Add(PropertyToJsonFunc toJson, PropertyFromJsonFunc fromJson)145 EcsSerializer::IPropertySerializer& EcsSerializer::Add(PropertyToJsonFunc toJson, PropertyFromJsonFunc fromJson)
146 {
147     auto serializer = new SimpleJsonSerializer(toJson, fromJson);
148     auto ptr = unique_ptr<SimpleJsonSerializer> { serializer };
149     ownedSerializers_.emplace_back(move(ptr));
150     return *ownedSerializers_.back();
151 }
152 
153 namespace {
154 template<class Type>
PropertyToJson( const IEntityCollection& , const Property& , uintptr_t offset, json::standalone_value& jsonOut)155 bool PropertyToJson(
156     const IEntityCollection& /*ec*/, const Property& /*property*/, uintptr_t offset, json::standalone_value& jsonOut)
157 {
158     jsonOut = ToJson(GetPropertyValue<Type>(offset));
159     return true;
160 }
161 
162 template<class Type>
PropertyFromJson( const IEntityCollection& , const json::value& jsonIn, const Property& , uintptr_t offset)163 bool PropertyFromJson(
164     const IEntityCollection& /*ec*/, const json::value& jsonIn, const Property& /*property*/, uintptr_t offset)
165 {
166     auto& value = GetPropertyValue<Type>(offset);
167     return FromJson(jsonIn, value);
168 }
169 
RenderHandleReferenceFromJson(const IEcsSerializer& ecsSerializer, IRenderContext& renderContext, const IEntityCollection& ec, const json::value& jsonIn, RenderHandleReference& handleRefOut)170 bool RenderHandleReferenceFromJson(const IEcsSerializer& ecsSerializer, IRenderContext& renderContext,
171     const IEntityCollection& ec, const json::value& jsonIn, RenderHandleReference& handleRefOut)
172 {
173     CORE_UNUSED(ec);
174 
175     if (!jsonIn.is_object()) {
176         return false;
177     }
178 
179     string error;
180     RenderHandleDesc desc;
181 
182     // Casting to enum directly from numeric value is a bit fishy.
183     uint32_t type = 0;
184     if (!SafeGetJsonValue(jsonIn, "type", error, type)) {
185         return false;
186     }
187     desc.type = static_cast<RenderHandleType>(type);
188 
189     if (!SafeGetJsonValue(jsonIn, "name", error, desc.name)) {
190         return false;
191     }
192 
193     SafeGetJsonValue(jsonIn, "additionalName", error, desc.additionalName);
194 
195     auto& renderUtil = renderContext.GetRenderUtil();
196     handleRefOut = renderUtil.GetRenderHandle(desc);
197 
198     if (!handleRefOut && desc.type == RenderHandleType::GPU_IMAGE && !desc.name.empty()) {
199         // Special handling for images: Load the image if it was not already loaded.
200         // Note: assuming that the name is the image uri.
201         handleRefOut = ecsSerializer.LoadImageResource(desc.name);
202     }
203 
204     return true;
205 }
206 
RenderHandleReferenceToJson(IRenderContext& renderContext, const IEntityCollection& ec, const RenderHandleReference& handleRefIn, json::standalone_value& jsonOut)207 bool RenderHandleReferenceToJson(IRenderContext& renderContext, const IEntityCollection& ec,
208     const RenderHandleReference& handleRefIn, json::standalone_value& jsonOut)
209 {
210     CORE_UNUSED(ec);
211 
212     auto& renderUtil = renderContext.GetRenderUtil();
213     const auto desc = renderUtil.GetRenderHandleDesc(handleRefIn);
214 
215     jsonOut = json::standalone_value::object {};
216     jsonOut["type"] = static_cast<unsigned int>(desc.type);
217     jsonOut["name"] = string(desc.name);
218     if (!desc.additionalName.empty()) {
219         jsonOut["additionalName"] = string(desc.additionalName);
220     }
221 
222     return true;
223 }
224 
EntityFromJson(const IEntityCollection& ec, const json::value& jsonIn, Entity& entityOut)225 bool EntityFromJson(const IEntityCollection& ec, const json::value& jsonIn, Entity& entityOut)
226 {
227     // Figure out to what entity id a piece of json refers to and handle error cases.
228     Entity entity {};
229     if (jsonIn.is_unsigned_int()) {
230         const auto entityReference = static_cast<uint32_t>(jsonIn.unsigned_);
231         entity = ec.GetEntity(entityReference);
232         if (entity == Entity {}) {
233             CORE_LOG_W("Component entity not found for index: %u", entityReference);
234             return false;
235         }
236     } else if (jsonIn.is_string()) {
237         string entityReferenceString;
238         if (FromJson(jsonIn, entityReferenceString)) {
239             entity = ec.GetEntity(entityReferenceString);
240         }
241         if (entity == Entity {}) {
242             CORE_LOG_W("Component entity not found for id: '%s'", entityReferenceString.c_str());
243             return false;
244         }
245     } else if (jsonIn.is_object()) {
246         // Empty object means "null ref".
247         if (jsonIn.empty()) {
248             entityOut = Entity {};
249             return true;
250         }
251 
252         // Figure out the correct collection (Recursive).
253         const IEntityCollection* collection { nullptr };
254         const auto* collectionJson = jsonIn.find("collection");
255         if (collectionJson) {
256             if (collectionJson->is_string()) {
257                 string collectionName;
258                 if (FromJson(*collectionJson, collectionName)) {
259                     if (auto index = ec.GetSubCollectionIndex(collectionName); index >= 0) {
260                         collection = ec.GetSubCollection(static_cast<size_t>(index));
261                     }
262                 }
263                 if (!collection) {
264                     CORE_LOG_W("Collection not found: '%s'", collectionName.c_str());
265                     return false;
266                 }
267 
268             } else if (collectionJson->is_unsigned_int()) {
269                 const auto collectionIndex = collectionJson->unsigned_;
270                 if (collectionIndex < ec.GetSubCollectionCount()) {
271                     collection = ec.GetSubCollection(collectionIndex);
272                 } else {
273                     CORE_LOG_W("Collection not found: %" PRIu64, collectionIndex);
274                     return false;
275                 }
276 
277             } else {
278                 CORE_LOG_W("Invalid collection for a component.");
279                 return false;
280             }
281 
282             if (collection) {
283                 const auto* entityJson = jsonIn.find("entity");
284                 if (entityJson) {
285                     return EntityFromJson(*collection, *entityJson, entityOut);
286                 }
287             }
288             return false;
289         }
290 
291     } else {
292         CORE_LOG_W("Component entity property must be an index to the entities array, an string id, or an object");
293         return false;
294     }
295 
296     entityOut = entity;
297     return true;
298 }
299 
EntityToJson(const IEntityCollection& ec, const Entity& entityIn, json::standalone_value& jsonOut)300 bool EntityToJson(const IEntityCollection& ec, const Entity& entityIn, json::standalone_value& jsonOut)
301 {
302     // Write entity index/name if part of this collection.
303     const auto entityCount = ec.GetEntityCount();
304     for (size_t i = 0; i < entityCount; ++i) {
305         if (entityIn == ec.GetEntity(i)) {
306             auto id = ec.GetId(entityIn);
307             if (!id.empty()) {
308                 jsonOut = string(id);
309             } else {
310                 jsonOut = i;
311             }
312             return true;
313         }
314     }
315 
316     // Otherwise check sub-collections recursively.
317     const auto collectionCount = ec.GetSubCollectionCount();
318     size_t collectionId = 0;
319     for (size_t i = 0; i < collectionCount; ++i) {
320         auto* collection = ec.GetSubCollection(i);
321         BASE_ASSERT(collection);
322         // NOTE: Skipping over destroyed collections (same needs to be done when writing the actual collections).
323         if (collection->IsMarkedDestroyed()) {
324             continue;
325         }
326         json::standalone_value entityJson;
327         if (EntityToJson(*collection, entityIn, entityJson)) {
328             jsonOut = json::standalone_value::object {};
329             jsonOut[string_view { "collection" }] = collectionId;
330             jsonOut[string_view { "entity" }] = move(entityJson);
331             return true;
332         }
333         collectionId++;
334     }
335 
336     return false;
337 }
338 
EntityReferenceToJson(IRenderContext& renderContext, const IEntityCollection& ec, const EntityReference& entityIn, json::standalone_value& jsonOut)339 bool EntityReferenceToJson(IRenderContext& renderContext, const IEntityCollection& ec, const EntityReference& entityIn,
340     json::standalone_value& jsonOut)
341 {
342     if (EntityToJson(ec, entityIn, jsonOut)) {
343         return true;
344     }
345 
346     // Write render handle reference as render handle desc.
347     auto* rhm = GetManager<IRenderHandleComponentManager>(ec.GetEcs());
348     if (rhm) {
349         if (auto handle = rhm->Read(entityIn); handle) {
350             json::standalone_value renderHandleJson = json::standalone_value::object {};
351             if (RenderHandleReferenceToJson(renderContext, ec, handle->reference, renderHandleJson["renderHandle"])) {
352                 jsonOut = move(renderHandleJson);
353                 return true;
354             }
355         }
356     }
357 
358     return false;
359 }
360 
EntityReferenceFromJson(const IEcsSerializer& ecsSerializer, IRenderContext& renderContext, const IEntityCollection& ec, const json::value& jsonIn, EntityReference& entityOut)361 bool EntityReferenceFromJson(const IEcsSerializer& ecsSerializer, IRenderContext& renderContext,
362     const IEntityCollection& ec, const json::value& jsonIn, EntityReference& entityOut)
363 {
364     // A generic handler for any uri to a render handle.
365     const auto* renderHandleJson = jsonIn.find("renderHandle");
366     if (renderHandleJson) {
367         // Try to find a named render handle by desc.
368         RenderHandleReference renderHandle;
369         if (RenderHandleReferenceFromJson(ecsSerializer, renderContext, ec, *renderHandleJson, renderHandle)) {
370             auto* rhm = GetManager<IRenderHandleComponentManager>(ec.GetEcs());
371             if (rhm && renderHandle) {
372                 entityOut = GetOrCreateEntityReference(ec.GetEcs().GetEntityManager(), *rhm, renderHandle);
373                 return true;
374             }
375         }
376     }
377 
378     Entity temp;
379     if (!EntityFromJson(ec, jsonIn, temp)) {
380         return false;
381     }
382     entityOut = ec.GetEcs().GetEntityManager().GetReferenceCounted(temp);
383     return true;
384 }
385 } // namespace
386 
SetDefaultSerializers()387 void EcsSerializer::SetDefaultSerializers()
388 {
389     // Basic types (for types that nlohman knows how to serialize).
390 
391     SetSerializer(PropertyType::FLOAT_T, Add(PropertyToJson<float>, PropertyFromJson<float>));
392     SetSerializer(PropertyType::DOUBLE_T, Add(PropertyToJson<double>, PropertyFromJson<double>));
393 
394     SetSerializer(PropertyType::UINT8_T, Add(PropertyToJson<uint8_t>, PropertyFromJson<uint8_t>));
395     SetSerializer(PropertyType::UINT16_T, Add(PropertyToJson<uint16_t>, PropertyFromJson<uint16_t>));
396     SetSerializer(PropertyType::UINT32_T, Add(PropertyToJson<uint32_t>, PropertyFromJson<uint32_t>));
397     SetSerializer(PropertyType::UINT64_T, Add(PropertyToJson<uint64_t>, PropertyFromJson<uint64_t>));
398 
399     SetSerializer(PropertyType::INT8_T, Add(PropertyToJson<int8_t>, PropertyFromJson<int8_t>));
400     SetSerializer(PropertyType::INT16_T, Add(PropertyToJson<int16_t>, PropertyFromJson<int16_t>));
401     SetSerializer(PropertyType::INT32_T, Add(PropertyToJson<int32_t>, PropertyFromJson<int32_t>));
402     SetSerializer(PropertyType::INT64_T, Add(PropertyToJson<int64_t>, PropertyFromJson<int64_t>));
403 
404     SetSerializer(PropertyType::VEC2_T, Add(PropertyToJson<Math::Vec2>, PropertyFromJson<Math::Vec2>));
405     SetSerializer(PropertyType::VEC3_T, Add(PropertyToJson<Math::Vec3>, PropertyFromJson<Math::Vec3>));
406     SetSerializer(PropertyType::VEC4_T, Add(PropertyToJson<Math::Vec4>, PropertyFromJson<Math::Vec4>));
407 
408     SetSerializer(PropertyType::UVEC2_T, Add(PropertyToJson<Math::UVec2>, PropertyFromJson<Math::UVec2>));
409     SetSerializer(PropertyType::UVEC3_T, Add(PropertyToJson<Math::UVec3>, PropertyFromJson<Math::UVec3>));
410     SetSerializer(PropertyType::UVEC4_T, Add(PropertyToJson<Math::UVec4>, PropertyFromJson<Math::UVec4>));
411 
412     SetSerializer(PropertyType::BOOL_T, Add(PropertyToJson<bool>, PropertyFromJson<bool>));
413     SetSerializer(PropertyType::QUAT_T, Add(PropertyToJson<Math::Quat>, PropertyFromJson<Math::Quat>));
414     SetSerializer(PropertyType::UID_T, Add(PropertyToJson<Uid>, PropertyFromJson<Uid>));
415 
416     SetSerializer(PropertyType::STRING_T, Add(PropertyToJson<string>, PropertyFromJson<string>));
417 
418     SetSerializer(PropertyType::CHAR_ARRAY_T,
419         Add(
420             [](const IEntityCollection& /*ec*/, const Property& property, uintptr_t offset,
421                 json::standalone_value& jsonOut) {
422                 const auto* value = &GetPropertyValue<char>(offset);
423                 // NOTE: a hacky way to calculate cstring size.
424                 string_view view(value);
425                 const size_t size = view.size() < property.size ? view.size() : property.size;
426                 jsonOut = ToJson(string(value, size));
427                 return true;
428             },
429             [](const IEntityCollection& /*ec*/, const json::value& jsonIn, const Property& property, uintptr_t offset) {
430                 string value;
431                 if (FromJson(jsonIn, value)) {
432                     char* charArray = &GetPropertyValue<char>(offset);
433                     charArray[value.copy(charArray, property.size - 1)] = '\0';
434                     return true;
435                 }
436                 return false;
437             }));
438 
439     SetSerializer(PropertyType::ENTITY_T,
440         Add(
441             [](const IEntityCollection& ec, const Property& /*property*/, uintptr_t offset,
442                 json::standalone_value& jsonOut) {
443                 return EntityToJson(ec, GetPropertyValue<const Entity>(offset), jsonOut);
444             },
445             [](const IEntityCollection& ec, const json::value& jsonIn, const Property& /*property*/, uintptr_t offset) {
446                 return EntityFromJson(ec, jsonIn, GetPropertyValue<Entity>(offset));
447             }));
448 
449     SetSerializer(PropertyType::ENTITY_REFERENCE_T,
450         Add(
451             [this](const IEntityCollection& ec, const Property& /*property*/, uintptr_t offset,
452                 json::standalone_value& jsonOut) {
453                 return EntityReferenceToJson(
454                     renderContext_, ec, GetPropertyValue<const EntityReference>(offset), jsonOut);
455             },
456             [this](const IEntityCollection& ec, const json::value& jsonIn, const Property& /*property*/,
457                 uintptr_t offset) {
458                 return EntityReferenceFromJson(
459                     *this, renderContext_, ec, jsonIn, GetPropertyValue<EntityReference>(offset));
460             }));
461 
462     SetSerializer(PROPERTYTYPE(RenderHandleReference),
463         Add(
464             [this](const IEntityCollection& ec, const Property& /*property*/, uintptr_t offset,
465                 json::standalone_value& jsonOut) {
466                 return RenderHandleReferenceToJson(
467                     renderContext_, ec, GetPropertyValue<const RenderHandleReference>(offset), jsonOut);
468             },
469             [this](const IEntityCollection& ec, const json::value& jsonIn, const Property& /*property*/,
470                 uintptr_t offset) {
471                 return RenderHandleReferenceFromJson(
472                     *this, renderContext_, ec, jsonIn, GetPropertyValue<RenderHandleReference>(offset));
473             }));
474 }
475 
SetListener(IListener* listener)476 void EcsSerializer::SetListener(IListener* listener)
477 {
478     listener_ = listener;
479 }
480 
SetSerializer(const PropertyTypeDecl& type, IPropertySerializer& serializer)481 void EcsSerializer::SetSerializer(const PropertyTypeDecl& type, IPropertySerializer& serializer)
482 {
483     typetoSerializerMap_[type] = &serializer;
484 }
485 
WriteEntityCollection(const IEntityCollection& ec, json::standalone_value& jsonOut) const486 bool EcsSerializer::WriteEntityCollection(const IEntityCollection& ec, json::standalone_value& jsonOut) const
487 {
488     // NOTE: We make sure collections and entities are written in the output before entity-components to make it
489     // possible to parse the file in one go.
490 
491     jsonOut = json::standalone_value::object();
492 
493     string type = ec.GetType();
494     if (type.empty()) {
495         type = "entitycollection";
496     }
497     const IoUtil::CompatibilityInfo info { VERSION_MAJOR, VERSION_MINOR, type };
498     if (!IoUtil::WriteCompatibilityInfo(jsonOut, info)) {
499         return false;
500     }
501 
502     if (string rootSrc = ec.GetSrc(); !rootSrc.empty()) {
503         jsonOut["src"] = move(rootSrc);
504     }
505 
506     // Write external collections instanced in this collection.
507     const auto collectionCount = ec.GetSubCollectionCount();
508     if (collectionCount > 0) {
509         json::standalone_value collectionsJson = json::standalone_value::array();
510         collectionsJson.array_.reserve(collectionCount);
511 
512         for (size_t i = 0; i < collectionCount; ++i) {
513             json::standalone_value collectionJson = json::standalone_value::object();
514             auto* collection = ec.GetSubCollection(i);
515 
516             if (!collection || collection->IsMarkedDestroyed()) {
517                 // NOTE: Skipping over destroyed collections (same needs to be done when writing entity references).
518                 continue;
519             }
520 
521             if (string src = collection->GetSrc(); !src.empty()) {
522                 collectionJson["src"] = move(src);
523             }
524             if (string id = collection->GetUri(); !id.empty()) {
525                 collectionJson["id"] = move(id);
526             }
527 
528             collectionsJson.array_.emplace_back(move(collectionJson));
529         }
530 
531         if (!collectionsJson.array_.empty()) {
532             jsonOut["collections"] = move(collectionsJson);
533         }
534     }
535 
536     // Write bare entities without components.
537     const auto entityCount = ec.GetEntityCount();
538     if (entityCount > 0) {
539         auto& entitiesJson = (jsonOut["entities"] = json::standalone_value::array());
540         entitiesJson.array_.reserve(entityCount);
541         for (size_t i = 0; i < entityCount; ++i) {
542             json::standalone_value entityJson = json::standalone_value::object();
543 
544             // Write id if one is defined.
545             auto entity = ec.GetEntity(i);
546             const auto& id = ec.GetId(entity);
547             if (!id.empty()) {
548                 entityJson["id"] = string(id);
549             }
550 
551             entitiesJson.array_.emplace_back(move(entityJson));
552         }
553     }
554 
555     vector<EntityReference> allEntities;
556     ec.GetEntitiesRecursive(false, allEntities);
557     if (allEntities.size() > 0) {
558         auto& entityComponentsJson = (jsonOut["entity-components"] = json::standalone_value::array());
559         for (Entity entity : allEntities) {
560             json::standalone_value componentJson = json::standalone_value::object();
561             if (WriteComponents(ec, entity, componentJson["components"])) {
562                 json::standalone_value entityRefJson;
563                 if (EntityToJson(ec, entity, entityRefJson)) {
564                     componentJson["entity"] = move(entityRefJson);
565                 }
566                 entityComponentsJson.array_.emplace_back(move(componentJson));
567             }
568         }
569     }
570 
571     // NOTE: Always returns true even if parts of the writing failed.
572     return true;
573 }
574 
WriteComponents(const IEntityCollection& ec, Entity entity, json::standalone_value& jsonOut) const575 bool EcsSerializer::WriteComponents(const IEntityCollection& ec, Entity entity, json::standalone_value& jsonOut) const
576 {
577     // Write all entity components to json (Sorting by uid to keep the order more stable).
578     vector<string> managerUids;
579     jsonOut = json::standalone_value::object();
580     auto cms = ec.GetEcs().GetComponentManagers();
581     for (auto cm : cms) {
582         const auto componentId = cm->GetComponentId(entity);
583         if (componentId != IComponentManager::INVALID_COMPONENT_ID) {
584             auto uidString = to_string(cm->GetUid());
585             managerUids.emplace_back(string(uidString));
586         }
587     }
588     std::sort(managerUids.begin(), managerUids.end());
589 
590     for (const auto& uidString : managerUids) {
591         const auto* cm = ec.GetEcs().GetComponentManager(StringToUid(uidString));
592         const auto componentId = cm->GetComponentId(entity);
593 
594         json::standalone_value componentJson = json::standalone_value::object();
595         if (WriteComponent(ec, entity, *cm, componentId, componentJson)) {
596             jsonOut[uidString] = move(componentJson);
597         }
598     }
599 
600     return (!jsonOut.empty());
601 }
602 
WriteComponent(const IEntityCollection& ec, Entity entity, const IComponentManager& cm, IComponentManager::ComponentId id, json::standalone_value& jsonOut) const603 bool EcsSerializer::WriteComponent(const IEntityCollection& ec, Entity entity, const IComponentManager& cm,
604     IComponentManager::ComponentId id, json::standalone_value& jsonOut) const
605 {
606     // Write all properties to json.
607     json::standalone_value propertiesJson = json::standalone_value::object();
608     const auto* props = ec.GetSerializedProperties(entity, cm.GetUid());
609     if (props) {
610         const auto* propertyHandle = cm.GetData(id);
611         if (!propertyHandle) {
612             return false;
613         }
614         for (const auto& propertyPath : *props) {
615             const IPropertyHandle* handle = propertyHandle;
616 
617             PropertyData propertyData;
618             PropertyData::PropertyOffset propertyOffset;
619 
620             // Check if this is property container.
621             string path, name;
622             auto containerHandle = ResolveContainerProperty(*handle, propertyPath, path, name);
623             if (containerHandle) {
624                 propertyOffset = propertyData.RLock(*containerHandle, name);
625             } else {
626                 propertyOffset = propertyData.RLock(*propertyHandle, propertyPath);
627             }
628 
629             if (propertyOffset) {
630                 if ((propertyOffset.property->flags & static_cast<uint32_t>(PropertyFlags::NO_SERIALIZE)) != 0) {
631                     continue;
632                 }
633 
634                 json::standalone_value propertyJson;
635                 if (WriteProperty(ec, *propertyOffset.property, propertyOffset.offset, propertyJson)) {
636                     if (!propertyJson.is_null()) {
637                         propertiesJson[propertyPath] = move(propertyJson);
638                     }
639                 }
640             }
641         }
642 
643         // NOTE: optional name to make reading files easier.
644         jsonOut["name"] = string(cm.GetName());
645 
646         if (!propertiesJson.empty()) {
647             jsonOut["properties"] = move(propertiesJson);
648         }
649 
650         // NOTE: Maybe return false if any property write fails?
651         return true;
652     }
653     return false;
654 }
655 
WriteProperty( const IEntityCollection& ec, const Property& property, uintptr_t offset, json::standalone_value& jsonOut) const656 bool EcsSerializer::WriteProperty(
657     const IEntityCollection& ec, const Property& property, uintptr_t offset, json::standalone_value& jsonOut) const
658 {
659     auto serializer = typetoSerializerMap_.find(property.type);
660     if (serializer != typetoSerializerMap_.end()) {
661         return serializer->second->ToJson(ec, property, offset, jsonOut);
662 
663     } else if (!property.metaData.enumMetaData.empty()) {
664         // Enum type property.
665         switch (property.size) {
666             case sizeof(uint8_t):
667                 jsonOut = GetPropertyValue<uint8_t>(offset);
668                 return true;
669             case sizeof(uint16_t):
670                 jsonOut = GetPropertyValue<uint16_t>(offset);
671                 return true;
672             case sizeof(uint32_t):
673                 jsonOut = GetPropertyValue<uint32_t>(offset);
674                 return true;
675             case sizeof(uint64_t):
676                 jsonOut = GetPropertyValue<uint64_t>(offset);
677                 return true;
678         }
679 
680     } else if (property.metaData.containerMethods) {
681         const auto& container = *property.metaData.containerMethods;
682 
683         // Container type property.
684         if (property.type.isArray) {
685             // Special handling for byte arrays. Save as a base64 encoded string.
686             if (property.type == PropertyType::UINT8_ARRAY_T) {
687                 array_view<const uint8_t> bytes { (const uint8_t*)offset, property.size };
688                 jsonOut = ::Base64Encode(bytes);
689                 return true;
690             }
691 
692             // C style array.
693             json::standalone_value array = json::standalone_value::array();
694             array.array_.reserve(property.count);
695             for (size_t i = 0; i < property.count; i++) {
696                 uintptr_t ptr = offset + i * container.property.size;
697                 // TODO: return false if any recurseive call fails?
698                 json::standalone_value elementJson;
699                 WriteProperty(ec, container.property, ptr, elementJson);
700                 array.array_.push_back(move(elementJson));
701             }
702             jsonOut = move(array);
703             return true;
704 
705         } else {
706             // This is a "non trivial container".
707             const auto count = container.size(offset);
708 
709             // Special handling for byte arrays. Save as a base64 encoded string.
710             // NOTE: Only specifically vector so we can assume that the memory usage is linear.
711             if (property.type == PROPERTYTYPE(vector<uint8_t>)) {
712                 if (count == 0) {
713                     jsonOut = "";
714                 } else {
715                     auto data = container.get(offset, 0);
716                     array_view<const uint8_t> bytes { reinterpret_cast<const uint8_t*>(data), count };
717                     jsonOut = ::Base64Encode(bytes);
718                 }
719                 return true;
720             }
721 
722             json::standalone_value array = json::standalone_value::array();
723             array.array_.reserve(count);
724 
725             for (size_t i = 0; i < count; i++) {
726                 uintptr_t ptr = container.get(offset, i);
727                 // TODO: return false if any recurseive call fails?
728                 json::standalone_value elementJson;
729                 WriteProperty(ec, container.property, ptr, elementJson);
730                 array.array_.push_back(move(elementJson));
731             }
732             jsonOut = move(array);
733             return true;
734         }
735 
736     } else if (!property.metaData.memberProperties.empty()) {
737         // Struct type property (ie. has sub properties).
738         json::standalone_value object = json::standalone_value::object();
739         for (const auto& subProperty : property.metaData.memberProperties) {
740             json::standalone_value subPropertyJson;
741             if (WriteProperty(ec, subProperty, offset + subProperty.offset, subPropertyJson)) {
742                 object[subProperty.name] = move(subPropertyJson);
743             }
744         }
745         // TODO: return false if any recurseive call fails?
746         jsonOut = move(object);
747         return true;
748 
749     } else {
750         CORE_LOG_V("Ecs serializer not found for '%s'", string(property.type.name).c_str());
751     }
752     return false;
753 }
754 
GatherExternalCollections( const json::value& jsonIn, string_view contextUri, vector<ExternalCollection>& externalCollectionsOut) const755 bool EcsSerializer::GatherExternalCollections(
756     const json::value& jsonIn, string_view contextUri, vector<ExternalCollection>& externalCollectionsOut) const
757 {
758     const auto* srcJson = jsonIn.find("src");
759     string srcUri;
760     if (srcJson && FromJson(*srcJson, srcUri)) {
761 #ifdef VERBOSE_LOGGING
762         CORE_LOG_D("External Collection: uri='%s' context='%s'", srcUri.c_str(), string(contextUri).c_str());
763 #endif
764         externalCollectionsOut.emplace_back(ExternalCollection { srcUri, string { contextUri } });
765     }
766 
767     const auto* collectionsJson = jsonIn.find("collections");
768     if (collectionsJson && collectionsJson->is_array()) {
769         for (const auto& collectionJson : collectionsJson->array_) {
770             if (collectionJson.is_object()) {
771                 const auto* collectionSrcJson = collectionJson.find("src");
772                 if (collectionSrcJson && collectionSrcJson->is_string()) {
773                     string collectionSrcUri;
774                     FromJson(*collectionSrcJson, collectionSrcUri);
775                     externalCollectionsOut.emplace_back(ExternalCollection { collectionSrcUri, string { contextUri } });
776                 }
777             }
778         }
779     }
780     return true;
781 }
782 
ReadEntityCollection( IEntityCollection& ec, const json::value& jsonIn, string_view contextUri) const783 IoUtil::SerializationResult EcsSerializer::ReadEntityCollection(
784     IEntityCollection& ec, const json::value& jsonIn, string_view contextUri) const
785 {
786     // TODO: Move version check to be separately so it can be done before gathering the dependencies.
787     // NOTE: Only comparing the major version.
788     const auto minor = IoUtil::CompatibilityRange::IGNORE_VERSION;
789     // TODO: Type name was changed to be in line with engine naming. Allow the old type name for a while.
790     const IoUtil::CompatibilityRange validVersions[] {
791         { VERSION_MAJOR, VERSION_MAJOR, minor, minor, ec.GetType() },
792         { VERSION_MAJOR, VERSION_MAJOR, minor, minor, "entity_collection" },
793     };
794 
795     auto result = IoUtil::CheckCompatibility(jsonIn, validVersions);
796 
797     const auto* srcJson = jsonIn.find("src");
798     string srcUri;
799 
800     const auto* collectionsJson = jsonIn.find("collections");
801     if (collectionsJson && collectionsJson->is_array()) {
802         for (const auto& collectionJson : collectionsJson->array_) {
803             string id;
804             const auto* idJson = collectionJson.find("id");
805             FromJson(*idJson, id);
806             const auto* collectionSrcJson = collectionJson.find("src");
807             if (collectionSrcJson && collectionSrcJson->is_string()) {
808                 string collectionSrcUri;
809                 FromJson(*collectionSrcJson, collectionSrcUri);
810 
811                 // Instantiate the collection pointed by src.
812 #ifdef VERBOSE_LOGGING
813                 CORE_LOG_D("External Collection: uri='%s' context='%s'", collectionSrcUri.c_str(),
814                     string(contextUri).c_str());
815 #endif
816                 auto* externalCollection =
817                     listener_->GetExternalCollection(ec.GetEcs(), collectionSrcUri, contextUri);
818             } else {
819                 ec.AddSubCollection(id, {});
820             }
821         }
822     }
823 
824     // NOTE: Always returns success, even if parts of the load failed.
825     return result;
826 }
827 
ReadComponents(IEntityCollection& ec, const json::value& jsonIn) const828 bool EcsSerializer::ReadComponents(IEntityCollection& ec, const json::value& jsonIn) const
829 {
830     // Figure out to which entity these components belong to.
831     const auto* entityJson = jsonIn.find("entity");
832     if (!entityJson) {
833         CORE_LOG_W("No entity defined for a component.");
834         return false;
835     }
836     Entity entity {};
837     if (!EntityFromJson(ec, *entityJson, entity)) {
838         return false;
839     }
840 
841     auto& ecs = ec.GetEcs();
842     const auto* componentsJson = jsonIn.find("components");
843     if (componentsJson) {
844         // Read all entity components from json.
845         for (auto& component : componentsJson->object_) {
846             auto& key = component.key;
847             const auto componentUid = StringToUid(key);
848 
849             auto& componentJson = component.value;
850             auto* cm = ecs.GetComponentManager(componentUid);
851             if (cm) {
852                 ReadComponent(ec, componentJson, entity, *cm);
853             } else {
854                 // TODO: Maybe we should try to find a matching component by name as a fallback
855                 CORE_LOG_W("Unrecognized component found: '%s'", string(key).c_str());
856             }
857         }
858     }
859 
860     return true;
861 }
862 
863 namespace {
864 // Helper function that makes sure that any dynamic arrays referenced in a property path are large enough to contain the
865 // referenced indices.
EnsureDynamicArraySize(IPropertyHandle* propertyHandle, string_view propertyPath)866 void EnsureDynamicArraySize(IPropertyHandle* propertyHandle, string_view propertyPath)
867 {
868     const auto separatorPosition = propertyPath.find('[');
869     if (separatorPosition == ::string::npos) {
870         return;
871     }
872     const auto separatorEndPosition = propertyPath.find(']', separatorPosition);
873     if (separatorEndPosition == ::string::npos) {
874         return;
875     }
876 
877     string arrayPath, arrayIndex;
878     arrayPath = propertyPath.substr(0, separatorPosition);
879     arrayIndex = propertyPath.substr(separatorPosition + 1, separatorEndPosition - separatorPosition - 1);
880 
881     char* end = nullptr;
882     const unsigned long index = std::strtoul(arrayIndex.c_str(), &end, 10); // 10: base
883     // Check that conversion stopped at the end of the string.
884     if (!end || *end != '\0') {
885         return;
886     }
887 
888     PropertyData propertyData;
889     PropertyData::PropertyOffset propertyOffset = propertyData.WLock(*propertyHandle, arrayPath);
890     if (propertyOffset) {
891         auto* containerMethods = propertyOffset.property->metaData.containerMethods;
892         if (containerMethods && containerMethods->resize) {
893             if (containerMethods->size(propertyOffset.offset) <= index) {
894                 containerMethods->resize(propertyOffset.offset, index + 1);
895             }
896         }
897     }
898 
899     const auto restOfThePath = propertyPath.substr(separatorEndPosition);
900     EnsureDynamicArraySize(&propertyData, restOfThePath);
901 }
902 } // namespace
903 
ReadComponent( IEntityCollection& ec, const json::value& jsonIn, Entity entity, IComponentManager& component) const904 bool EcsSerializer::ReadComponent(
905     IEntityCollection& ec, const json::value& jsonIn, Entity entity, IComponentManager& component) const
906 {
907     // Create the component if it does not exist yet.
908     auto componentId = component.GetComponentId(entity);
909     if (componentId == IComponentManager::INVALID_COMPONENT_ID) {
910         component.Create(entity);
911         componentId = component.GetComponentId(entity);
912     }
913 
914     ec.MarkComponentSerialized(entity, component.GetUid(), true);
915 
916     const auto* propertiesJson = jsonIn.find("properties");
917     if (!propertiesJson || propertiesJson->type != json::type::object) {
918         // No properties.
919         return true;
920     }
921 
922     auto* propertyHandle = component.GetData(componentId);
923     if (!propertyHandle) {
924         return false;
925     }
926 
927     for (auto& propertyJson : propertiesJson->object_) {
928         const auto& propertyPath = propertyJson.key;
929         const auto& propertyValueJson = propertyJson.value;
930         auto pathView = string_view(propertyPath.data(), propertyPath.size());
931 
932         // Find the property using the propertyName
933         {
934             const IPropertyHandle* handle = propertyHandle;
935             PropertyData propertyData;
936             PropertyData::PropertyOffset propertyOffset;
937 
938             // Check if this is property container.
939             string path, name;
940             auto containerHandle = ResolveContainerProperty(*handle, string(pathView), path, name);
941             if (containerHandle) {
942                 propertyOffset = propertyData.WLock(*containerHandle, name);
943             } else {
944                 // We can only ask for the property if we first make sure it exists (we may be referencing a dynamic
945                 // array).
946                 EnsureDynamicArraySize(propertyHandle, pathView);
947 
948                 propertyOffset = propertyData.WLock(*propertyHandle, pathView);
949             }
950 
951             if (propertyOffset) {
952                 if (ReadProperty(ec, propertyValueJson, *propertyOffset.property, propertyOffset.offset)) {
953                     // Mark this property value as serialized (instead of being a cloned from a prototype entity).
954                     ec.MarkPropertySerialized(entity, component.GetUid(), pathView, true);
955                 } else {
956                     CORE_LOG_W("Unrecognized property: Component: '%s' Property: '%s'", component.GetName().data(),
957                         string(pathView).c_str());
958                 }
959             }
960         }
961     }
962     return true;
963 }
964 
ReadProperty( IEntityCollection& ec, const json::value& jsonIn, const Property& property, uintptr_t offset) const965 bool EcsSerializer::ReadProperty(
966     IEntityCollection& ec, const json::value& jsonIn, const Property& property, uintptr_t offset) const
967 {
968     // See if there is a serializer for this type. Otherwise recurse further.
969     auto serializer = typetoSerializerMap_.find(property.type);
970     if (serializer != typetoSerializerMap_.end()) {
971         return serializer->second->FromJson(ec, jsonIn, property, offset);
972 
973     } else if (!property.metaData.enumMetaData.empty()) {
974         // Enum type property.
975         if (jsonIn.is_unsigned_int()) {
976             switch (property.size) {
977                 case sizeof(uint8_t):
978                     GetPropertyValue<uint8_t>(offset) = static_cast<uint8_t>(jsonIn.unsigned_);
979                     return true;
980                 case sizeof(uint16_t):
981                     GetPropertyValue<uint16_t>(offset) = static_cast<uint16_t>(jsonIn.unsigned_);
982                     return true;
983                 case sizeof(uint32_t):
984                     GetPropertyValue<uint32_t>(offset) = static_cast<uint32_t>(jsonIn.unsigned_);
985                     return true;
986                 case sizeof(uint64_t):
987                     GetPropertyValue<uint64_t>(offset) = static_cast<uint64_t>(jsonIn.unsigned_);
988                     return true;
989             }
990         }
991 
992     } else if (property.metaData.containerMethods) {
993         // Special handling for byte data encoded as base64.
994         if (jsonIn.is_string()) {
995             // A base64 encoded string containing raw array data
996             auto bytes = ::Base64Decode(jsonIn.string_);
997 
998             // Only valid for byte data.
999             if (property.type == PropertyType::UINT8_ARRAY_T) {
1000                 if (property.size != bytes.size()) {
1001                     CORE_LOG_W("Invalid base64 data size in: %s", string(property.name).c_str());
1002                     return false;
1003                 }
1004                 CloneData(GetPropertyValue<uint8_t*>(offset), property.size, &bytes[0], bytes.size());
1005                 return true;
1006 
1007             } else if (property.type == PROPERTYTYPE(vector<uint8_t>)) {
1008                 GetPropertyValue<vector<uint8_t>>(offset).swap(bytes);
1009                 return true;
1010             }
1011             return false;
1012         }
1013 
1014         // Container type property.
1015         if (property.type.isArray) {
1016             // C style array.
1017             if (jsonIn.is_array()) {
1018                 if (jsonIn.array_.size() != property.count) {
1019                     CORE_LOG_W("Expecting a json array of size %zu", property.count);
1020                     return false;
1021                 }
1022                 for (size_t i = 0; i < property.count; i++) {
1023                     uintptr_t ptr = offset + i * property.metaData.containerMethods->property.size;
1024                     // TODO: return false if any recurseive call fails?
1025                     ReadProperty(ec, jsonIn.array_.at(i), property.metaData.containerMethods->property, ptr);
1026                 }
1027                 return true;
1028 
1029             } else if (jsonIn.is_object()) {
1030                 // Allow "sparse arrays" by using objects with the array index as the key.
1031                 for (auto& element : jsonIn.object_) {
1032                     const auto& key = element.key;
1033                     const auto index = static_cast<size_t>(strtol(string(key).c_str(), nullptr, 10));
1034                     if ((index == 0 && key != "0") || index >= property.count) {
1035                         CORE_LOG_W("Invalid array Index: %s", string(key).c_str());
1036                         continue;
1037                     }
1038                     uintptr_t ptr = offset + index * property.metaData.containerMethods->property.size;
1039                     ReadProperty(ec, element.value, property.metaData.containerMethods->property, ptr);
1040                 }
1041                 return true;
1042             }
1043             return false;
1044 
1045         } else {
1046             // This is a "non trivial container".
1047             if (jsonIn.is_array()) {
1048                 const auto count = jsonIn.array_.size();
1049                 property.metaData.containerMethods->resize(offset, count);
1050                 for (size_t i = 0; i < count; i++) {
1051                     uintptr_t ptr = property.metaData.containerMethods->get(offset, i);
1052                     ReadProperty(ec, jsonIn.array_.at(i), property.metaData.containerMethods->property, ptr);
1053                 }
1054                 return true;
1055 
1056             } else if (jsonIn.is_object()) {
1057                 // Allow "sparse arrays" by using objects with the array index as the key.
1058                 for (auto& element : jsonIn.object_) {
1059                     const auto& key = element.key;
1060                     const auto index = static_cast<size_t>(strtol(string(key).c_str(), nullptr, 10));
1061                     if ((index == 0 && key != "0")) {
1062                         CORE_LOG_W("Invalid array Index: %s", string(key).c_str());
1063                         continue;
1064                     }
1065 
1066                     const auto count = property.metaData.containerMethods->size(offset);
1067                     if (count <= index) {
1068                         property.metaData.containerMethods->resize(offset, index + 1);
1069                     }
1070                     uintptr_t ptr = property.metaData.containerMethods->get(offset, index);
1071                     ReadProperty(ec, element.value, property.metaData.containerMethods->property, ptr);
1072                 }
1073                 return true;
1074             }
1075             return false;
1076         }
1077 
1078     } else if (!property.metaData.memberProperties.empty()) {
1079         // Struct type property (ie. has sub properties).
1080         if (jsonIn.is_object()) {
1081             for (const auto& subProperty : property.metaData.memberProperties) {
1082                 // TODO: is there a way to not create the string
1083                 const string name(subProperty.name);
1084                 const auto* subJson = jsonIn.find(name);
1085                 if (subJson) {
1086                     ReadProperty(ec, *subJson, subProperty, offset + subProperty.offset);
1087                 }
1088             }
1089             // TODO: return false if any recurseive call fails?
1090             return true;
1091         }
1092     }
1093 
1094     return false;
1095 }
1096 
LoadImageResource(::string_view uri) const1097 RENDER_NS::RenderHandleReference EcsSerializer::LoadImageResource(::string_view uri) const
1098 {
1099     // Get rid of a possible query string in the uri.
1100     const auto fileUri = PathUtil::ResolveUri({}, uri, false);
1101 
1102     auto params = PathUtil::GetUriParameters(uri);
1103 
1104     // Image loading flags can be passed in the uri query string.
1105     uint64_t imageLoaderFlags {};
1106     auto loaderFlags = params.find("loaderFlags");
1107     if (loaderFlags != params.end()) {
1108         const char* start = loaderFlags->second.data();
1109         const char* end = start + loaderFlags->second.size();
1110         std::from_chars(start, end, imageLoaderFlags);
1111     }
1112 
1113     auto imageLoadResult = renderContext_.GetEngine().GetImageLoaderManager().LoadImage(fileUri, imageLoaderFlags);
1114 
1115     RenderHandleReference imageHandle {};
1116     if (!imageLoadResult.success) {
1117         CORE_LOG_E("Could not load image asset: %s", imageLoadResult.error);
1118 
1119     } else {
1120         auto& gpuResourceMgr = renderContext_.GetDevice().GetGpuResourceManager();
1121         GpuImageDesc gpuDesc = gpuResourceMgr.CreateGpuImageDesc(imageLoadResult.image->GetImageDesc());
1122         gpuDesc.usageFlags = CORE_IMAGE_USAGE_SAMPLED_BIT | CORE_IMAGE_USAGE_TRANSFER_DST_BIT;
1123         if (gpuDesc.engineCreationFlags & EngineImageCreationFlagBits::CORE_ENGINE_IMAGE_CREATION_GENERATE_MIPS) {
1124             gpuDesc.usageFlags |= CORE_IMAGE_USAGE_TRANSFER_SRC_BIT;
1125         }
1126         gpuDesc.memoryPropertyFlags = CORE_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
1127 
1128         imageHandle = gpuResourceMgr.Create(uri, gpuDesc, std::move(imageLoadResult.image));
1129     }
1130 
1131     return imageHandle;
1132 }
1133 
Destroy()1134 void EcsSerializer::Destroy()
1135 {
1136     delete this;
1137 }
1138 
CreateEcsSerializer(RENDER_NS::IRenderContext& renderContext)1139 IEcsSerializer::Ptr CreateEcsSerializer(RENDER_NS::IRenderContext& renderContext)
1140 {
1141     return IEcsSerializer::Ptr { new EcsSerializer(renderContext) };
1142 }
1143 
1144 ECS_SERIALIZER_END_NAMESPACE()
1145