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