Slice.cpp 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include "FileIOBaseTestTypes.h"
  9. #include <AzCore/IO/FileIO.h>
  10. #include <AzCore/IO/Streamer/Streamer.h>
  11. #include <AzCore/IO/Streamer/StreamerComponent.h>
  12. #include <AzCore/Jobs/JobManager.h>
  13. #include <AzCore/Jobs/JobContext.h>
  14. #include <AzCore/Slice/SliceComponent.h>
  15. #include <AzCore/Serialization/SerializeContext.h>
  16. #include <AzCore/Component/Entity.h>
  17. #include <AzCore/Component/Component.h>
  18. #include <AzCore/Component/ComponentApplication.h>
  19. #include <AzCore/Serialization/ObjectStream.h>
  20. #include <AzCore/Serialization/Utils.h>
  21. #include <AzCore/Slice/SliceAssetHandler.h>
  22. #include <AzCore/Asset/AssetManager.h>
  23. #include <AzCore/Math/Sfmt.h>
  24. #include <AzCore/Memory/PoolAllocator.h>
  25. #include <AzCore/Slice/SliceMetadataInfoComponent.h>
  26. #include <AzCore/Task/TaskExecutor.h>
  27. #include <AzCore/UnitTest/TestTypes.h>
  28. #include <AZTestShared/Utils/Utils.h>
  29. #if defined(HAVE_BENCHMARK)
  30. #include <benchmark/benchmark.h>
  31. #endif
  32. namespace UnitTest
  33. {
  34. class MyTestComponent1
  35. : public AZ::Component
  36. {
  37. public:
  38. AZ_COMPONENT(MyTestComponent1, "{5D3B5B45-64DF-4F88-8003-271646B6CA27}");
  39. void Activate() override
  40. {
  41. }
  42. void Deactivate() override
  43. {
  44. }
  45. static void Reflect(AZ::ReflectContext* reflection)
  46. {
  47. auto serializeContext = azrtti_cast<AZ::SerializeContext*>(reflection);
  48. if (serializeContext)
  49. {
  50. serializeContext->Class<MyTestComponent1, AZ::Component>()->
  51. Field("float", &MyTestComponent1::m_float)->
  52. Field("int", &MyTestComponent1::m_int);
  53. }
  54. }
  55. float m_float = 0.f;
  56. int m_int = 0;
  57. };
  58. class MyTestComponent2
  59. : public AZ::Component
  60. {
  61. public:
  62. AZ_COMPONENT(MyTestComponent2, "{ACD7B78D-0C30-46E8-A52E-61D4B33D5528}");
  63. void Activate() override
  64. {
  65. }
  66. void Deactivate() override
  67. {
  68. }
  69. static void Reflect(AZ::ReflectContext* reflection)
  70. {
  71. auto serializeContext = azrtti_cast<AZ::SerializeContext*>(reflection);
  72. if (serializeContext)
  73. {
  74. serializeContext->Class<MyTestComponent2, AZ::Component>()->
  75. Field("entityId", &MyTestComponent2::m_entityId);
  76. }
  77. }
  78. AZ::EntityId m_entityId;
  79. };
  80. class SliceTest_MockCatalog
  81. : public AZ::Data::AssetCatalog
  82. , public AZ::Data::AssetCatalogRequestBus::Handler
  83. {
  84. private:
  85. AZ::Uuid randomUuid = AZ::Uuid::CreateRandom();
  86. AZStd::vector<AZ::Data::AssetId> m_mockAssetIds;
  87. public:
  88. AZ_CLASS_ALLOCATOR(SliceTest_MockCatalog, AZ::SystemAllocator);
  89. SliceTest_MockCatalog()
  90. {
  91. AZ::Data::AssetCatalogRequestBus::Handler::BusConnect();
  92. }
  93. ~SliceTest_MockCatalog() override
  94. {
  95. AZ::Data::AssetCatalogRequestBus::Handler::BusDisconnect();
  96. }
  97. AZ::Data::AssetId GenerateMockAssetId()
  98. {
  99. AZ::Data::AssetId assetId = AZ::Data::AssetId(AZ::Uuid::CreateRandom(), 0);
  100. m_mockAssetIds.push_back(assetId);
  101. return assetId;
  102. }
  103. //////////////////////////////////////////////////////////////////////////
  104. // AZ::Data::AssetCatalogRequestBus
  105. AZ::Data::AssetInfo GetAssetInfoById(const AZ::Data::AssetId& id) override
  106. {
  107. AZ::Data::AssetInfo result;
  108. result.m_assetType = AZ::AzTypeInfo<AZ::SliceAsset>::Uuid();
  109. for (const auto& assetId : m_mockAssetIds)
  110. {
  111. if (assetId == id)
  112. {
  113. result.m_assetId = id;
  114. break;
  115. }
  116. }
  117. return result;
  118. }
  119. //////////////////////////////////////////////////////////////////////////
  120. AZ::Data::AssetStreamInfo GetStreamInfoForLoad(const AZ::Data::AssetId& id, const AZ::Data::AssetType& type) override
  121. {
  122. EXPECT_TRUE(type == AZ::AzTypeInfo<AZ::SliceAsset>::Uuid());
  123. AZ::Data::AssetStreamInfo info;
  124. info.m_dataOffset = 0;
  125. info.m_streamFlags = AZ::IO::OpenMode::ModeRead;
  126. for (int i = 0; i < m_mockAssetIds.size(); ++i)
  127. {
  128. if (m_mockAssetIds[i] == id)
  129. {
  130. info.m_streamName = AZStd::string::format("MockSliceAssetName%d", i);
  131. }
  132. }
  133. if (!info.m_streamName.empty())
  134. {
  135. // this ensures tha parallel running unit tests do not overlap their files that they use.
  136. AZ::IO::Path fullName = GetTestFolderPath() / AZStd::string::format("%s-%s", randomUuid.ToString<AZStd::string>().c_str(), info.m_streamName.c_str());
  137. info.m_streamName = AZStd::move(fullName.Native());
  138. info.m_dataLen = static_cast<size_t>(AZ::IO::SystemFile::Length(info.m_streamName.c_str()));
  139. }
  140. else
  141. {
  142. info.m_dataLen = 0;
  143. }
  144. return info;
  145. }
  146. AZ::Data::AssetStreamInfo GetStreamInfoForSave(const AZ::Data::AssetId& id, const AZ::Data::AssetType& type) override
  147. {
  148. AZ::Data::AssetStreamInfo info;
  149. info = GetStreamInfoForLoad(id, type);
  150. info.m_streamFlags = AZ::IO::OpenMode::ModeWrite;
  151. return info;
  152. }
  153. bool SaveAsset(AZ::Data::Asset<AZ::SliceAsset>& asset)
  154. {
  155. volatile bool isDone = false;
  156. volatile bool succeeded = false;
  157. AZ::Data::AssetBusCallbacks callbacks;
  158. callbacks.SetCallbacks(nullptr, nullptr, nullptr,
  159. [&isDone, &succeeded](const AZ::Data::Asset<AZ::Data::AssetData>& /*asset*/, bool isSuccessful, AZ::Data::AssetBusCallbacks& /*callbacks*/)
  160. {
  161. isDone = true;
  162. succeeded = isSuccessful;
  163. }, nullptr, nullptr, nullptr);
  164. callbacks.BusConnect(asset.GetId());
  165. asset.Save();
  166. while (!isDone)
  167. {
  168. AZ::Data::AssetManager::Instance().DispatchEvents();
  169. }
  170. return succeeded;
  171. }
  172. };
  173. class SliceTest
  174. : public LeakDetectionFixture
  175. {
  176. public:
  177. void SetUp() override
  178. {
  179. LeakDetectionFixture::SetUp();
  180. m_taskExecutor = aznew AZ::TaskExecutor();
  181. AZ::TaskExecutor::SetInstance(m_taskExecutor);
  182. m_serializeContext = aznew AZ::SerializeContext(true, true);
  183. m_sliceDescriptor = AZ::SliceComponent::CreateDescriptor();
  184. m_prevFileIO = AZ::IO::FileIOBase::GetInstance();
  185. AZ::IO::FileIOBase::SetInstance(&m_fileIO);
  186. m_streamer = aznew AZ::IO::Streamer(AZStd::thread_desc{}, AZ::StreamerComponent::CreateStreamerStack());
  187. AZ::Interface<AZ::IO::IStreamer>::Register(m_streamer);
  188. m_sliceDescriptor->Reflect(m_serializeContext);
  189. MyTestComponent1::Reflect(m_serializeContext);
  190. MyTestComponent2::Reflect(m_serializeContext);
  191. AZ::SliceMetadataInfoComponent::Reflect(m_serializeContext);
  192. AZ::Entity::Reflect(m_serializeContext);
  193. AZ::DataPatch::Reflect(m_serializeContext);
  194. // Create database
  195. AZ::Data::AssetManager::Descriptor desc;
  196. AZ::Data::AssetManager::Create(desc);
  197. AZ::Data::AssetManager::Instance().RegisterHandler(aznew AZ::SliceAssetHandler(m_serializeContext), AZ::AzTypeInfo<AZ::SliceAsset>::Uuid());
  198. m_catalog.reset(aznew SliceTest_MockCatalog());
  199. AZ::Data::AssetManager::Instance().RegisterCatalog(m_catalog.get(), AZ::AzTypeInfo<AZ::SliceAsset>::Uuid());
  200. }
  201. void TearDown() override
  202. {
  203. m_catalog->DisableCatalog();
  204. m_catalog.reset();
  205. AZ::Data::AssetManager::Destroy();
  206. delete m_sliceDescriptor;
  207. delete m_serializeContext;
  208. AZ::Interface<AZ::IO::IStreamer>::Unregister(m_streamer);
  209. delete m_streamer;
  210. AZ::IO::FileIOBase::SetInstance(m_prevFileIO);
  211. AZ::TaskExecutor::SetInstance(nullptr);
  212. delete m_taskExecutor;
  213. LeakDetectionFixture::TearDown();
  214. }
  215. AZ::IO::FileIOBase* m_prevFileIO{ nullptr };
  216. AZ::TaskExecutor* m_taskExecutor{ nullptr };
  217. AZ::IO::Streamer* m_streamer{ nullptr };
  218. TestFileIOBase m_fileIO;
  219. AZStd::unique_ptr<SliceTest_MockCatalog> m_catalog;
  220. AZ::SerializeContext* m_serializeContext;
  221. AZ::ComponentDescriptor* m_sliceDescriptor;
  222. };
  223. TEST_F(SliceTest, UberTest)
  224. {
  225. AZ::SerializeContext& serializeContext = *m_serializeContext;
  226. AZStd::vector<char> binaryBuffer;
  227. AZ::Entity* sliceEntity = nullptr;
  228. AZ::SliceComponent* sliceComponent = nullptr;
  229. AZ::Entity* entity1 = nullptr;
  230. MyTestComponent1* component1 = nullptr;
  231. /////////////////////////////////////////////////////////////////////////////
  232. // Test prefab without base prefab - aka root prefab
  233. /////////////////////////////////////////////////////////////////////////////
  234. sliceEntity = aznew AZ::Entity();
  235. sliceComponent = aznew AZ::SliceComponent();
  236. sliceComponent->SetSerializeContext(&serializeContext);
  237. sliceEntity->AddComponent(sliceComponent);
  238. sliceEntity->Init();
  239. sliceEntity->Activate();
  240. // Create an entity with a component to be part of the prefab
  241. entity1 = aznew AZ::Entity();
  242. component1 = aznew MyTestComponent1();
  243. component1->m_float = 2.0f;
  244. component1->m_int = 11;
  245. entity1->AddComponent(component1);
  246. sliceComponent->AddEntity(entity1);
  247. // store to stream
  248. AZ::IO::ByteContainerStream<AZStd::vector<char> > binaryStream(&binaryBuffer);
  249. AZ::ObjectStream* binaryObjStream = AZ::ObjectStream::Create(&binaryStream, serializeContext, AZ::ObjectStream::ST_BINARY);
  250. binaryObjStream->WriteClass(sliceEntity);
  251. AZ_TEST_ASSERT(binaryObjStream->Finalize());
  252. // load from stream and check values
  253. binaryStream.Seek(0, AZ::IO::GenericStream::ST_SEEK_BEGIN);
  254. AZ::Entity* sliceEntity2 = AZ::Utils::LoadObjectFromStream<AZ::Entity>(binaryStream, &serializeContext);
  255. AZ_TEST_ASSERT(sliceEntity2);
  256. AZ_TEST_ASSERT(sliceEntity2->FindComponent<AZ::SliceComponent>() != nullptr);
  257. AZ_TEST_ASSERT(sliceEntity2->FindComponent<AZ::SliceComponent>()->GetSlices().empty());
  258. const AZ::SliceComponent::EntityList& entityContainer = sliceEntity2->FindComponent<AZ::SliceComponent>()->GetNewEntities();
  259. AZ_TEST_ASSERT(entityContainer.size() == 1);
  260. AZ_TEST_ASSERT(entityContainer.front()->FindComponent<MyTestComponent1>());
  261. AZ_TEST_ASSERT(entityContainer.front()->FindComponent<MyTestComponent1>()->m_float == 2.0f);
  262. AZ_TEST_ASSERT(entityContainer.front()->FindComponent<MyTestComponent1>()->m_int == 11);
  263. delete sliceEntity2;
  264. sliceEntity2 = nullptr;
  265. /////////////////////////////////////////////////////////////////////////////
  266. /////////////////////////////////////////////////////////////////////////////
  267. // Test slice component with a single base prefab
  268. // Create "fake" asset - so we don't have to deal with the asset database, etc.
  269. AZ::Data::Asset<AZ::SliceAsset> sliceAssetHolder = AZ::Data::AssetManager::Instance().CreateAsset<AZ::SliceAsset>(m_catalog->GenerateMockAssetId(), AZ::Data::AssetLoadBehavior::Default);
  270. AZ::SliceAsset* sliceAsset1 = sliceAssetHolder.Get();
  271. sliceAsset1->SetData(sliceEntity, sliceComponent);
  272. // ASSERT_TRUE(m_catalog->SaveAsset(sliceAssetHolder));
  273. AZ::Data::AssetInfo assetInfo;
  274. AZ::Data::AssetCatalogRequestBus::BroadcastResult(assetInfo, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetInfoById, sliceAssetHolder.GetId());
  275. ASSERT_TRUE(assetInfo.m_assetId.IsValid());
  276. sliceEntity2 = aznew AZ::Entity;
  277. AZ::SliceComponent* sliceComponent2 = sliceEntity2->CreateComponent<AZ::SliceComponent>();
  278. sliceComponent2->SetSerializeContext(&serializeContext);
  279. sliceComponent2->AddSlice(sliceAssetHolder);
  280. sliceEntity2->Init();
  281. sliceEntity2->Activate();
  282. AZ::SliceComponent::EntityList entities;
  283. sliceComponent2->GetEntities(entities); // get all entities (Instantiated if needed)
  284. AZ_TEST_ASSERT(entities.size() == 1);
  285. AZ_TEST_ASSERT(entities.front()->FindComponent<MyTestComponent1>());
  286. AZ_TEST_ASSERT(entities.front()->FindComponent<MyTestComponent1>()->m_float == 2.0f);
  287. AZ_TEST_ASSERT(entities.front()->FindComponent<MyTestComponent1>()->m_int == 11);
  288. // Modify Component
  289. entities.front()->FindComponent<MyTestComponent1>()->m_float = 5.0f;
  290. // Add a new entity
  291. AZ::Entity* entity2 = aznew AZ::Entity();
  292. entity2->CreateComponent<MyTestComponent2>();
  293. sliceComponent2->AddEntity(entity2);
  294. // FindSlice test
  295. auto prefabFindResult = sliceComponent2->FindSlice(entity2);
  296. AZ_TEST_ASSERT(prefabFindResult.GetReference() == nullptr); // prefab reference
  297. AZ_TEST_ASSERT(prefabFindResult.GetInstance() == nullptr); // prefab instance
  298. prefabFindResult = sliceComponent2->FindSlice(entities.front());
  299. AZ_TEST_ASSERT(prefabFindResult.GetReference() != nullptr); // prefab reference
  300. AZ_TEST_ASSERT(prefabFindResult.GetInstance() != nullptr); // prefab instance
  301. // store to stream
  302. binaryBuffer.clear();
  303. binaryStream.Seek(0, AZ::IO::GenericStream::ST_SEEK_BEGIN);
  304. binaryObjStream = AZ::ObjectStream::Create(&binaryStream, serializeContext, AZ::ObjectStream::ST_XML);
  305. binaryObjStream->WriteClass(sliceEntity2);
  306. AZ_TEST_ASSERT(binaryObjStream->Finalize());
  307. // load from stream and validate
  308. binaryStream.Seek(0, AZ::IO::GenericStream::ST_SEEK_BEGIN);
  309. AZ::Entity* sliceEntity2Clone = AZ::Utils::LoadObjectFromStream<AZ::Entity>(binaryStream, &serializeContext);
  310. AZ_TEST_ASSERT(sliceEntity2Clone);
  311. AZ_TEST_ASSERT(sliceEntity2Clone->FindComponent<AZ::SliceComponent>() != nullptr);
  312. AZ_TEST_ASSERT(sliceEntity2Clone->FindComponent<AZ::SliceComponent>()->GetSlices().size() == 1);
  313. AZ_TEST_ASSERT(sliceEntity2Clone->FindComponent<AZ::SliceComponent>()->GetNewEntities().size() == 1);
  314. // instantiate the loaded component
  315. sliceEntity2Clone->FindComponent<AZ::SliceComponent>()->SetSerializeContext(&serializeContext);
  316. entities.clear();
  317. sliceEntity2Clone->FindComponent<AZ::SliceComponent>()->GetEntities(entities);
  318. AZ_TEST_ASSERT(entities.size() == 2); // original entity (prefab) plus the newly added one
  319. AZ_TEST_ASSERT(entities.front()->FindComponent<MyTestComponent1>());
  320. AZ_TEST_ASSERT(entities.front()->FindComponent<MyTestComponent1>()->m_float == 5.0f); // test modified field
  321. AZ_TEST_ASSERT(entities.front()->FindComponent<MyTestComponent1>()->m_int == 11);
  322. AZ_TEST_ASSERT(entities.back()->FindComponent<MyTestComponent2>());
  323. ////////////////////////////////////////////////////////////////////////////
  324. /////////////////////////////////////////////////////////////////////////////
  325. // Test slice component 3 levels of customization
  326. AZ::Data::Asset<AZ::SliceAsset> sliceAssetHolder1 = AZ::Data::AssetManager::Instance().CreateAsset<AZ::SliceAsset>(m_catalog->GenerateMockAssetId(), AZ::Data::AssetLoadBehavior::Default);
  327. AZ::SliceAsset* sliceAsset2 = sliceAssetHolder1.Get();
  328. sliceAsset2->SetData(sliceEntity2, sliceComponent2);
  329. AZ::Entity* sliceEntity3 = aznew AZ::Entity();
  330. AZ::SliceComponent* sliceComponent3 = sliceEntity3->CreateComponent<AZ::SliceComponent>();
  331. sliceComponent3->SetSerializeContext(&serializeContext);
  332. sliceEntity3->Init();
  333. sliceEntity3->Activate();
  334. sliceComponent3->AddSlice(sliceAssetHolder1);
  335. entities.clear();
  336. sliceComponent3->GetEntities(entities);
  337. AZ_TEST_ASSERT(entities.size() == 2);
  338. AZ_TEST_ASSERT(entities.front()->FindComponent<MyTestComponent1>());
  339. AZ_TEST_ASSERT(entities.front()->FindComponent<MyTestComponent1>()->m_float == 5.0f);
  340. AZ_TEST_ASSERT(entities.front()->FindComponent<MyTestComponent1>()->m_int == 11);
  341. AZ_TEST_ASSERT(entities.back()->FindComponent<MyTestComponent2>());
  342. // Modify Component
  343. entities.front()->FindComponent<MyTestComponent1>()->m_int = 22;
  344. // Remove a entity
  345. sliceComponent3->RemoveEntity(entities.back());
  346. // store to stream
  347. binaryBuffer.clear();
  348. binaryStream.Seek(0, AZ::IO::GenericStream::ST_SEEK_BEGIN);
  349. binaryObjStream = AZ::ObjectStream::Create(&binaryStream, serializeContext, AZ::ObjectStream::ST_XML);
  350. binaryObjStream->WriteClass(sliceEntity3);
  351. AZ_TEST_ASSERT(binaryObjStream->Finalize());
  352. // modify root asset (to make sure that changes propgate properly), we have not overridden MyTestComponent1::m_float
  353. entities.clear();
  354. sliceComponent2->GetEntities(entities);
  355. entities.front()->FindComponent<MyTestComponent1>()->m_float = 15.0f;
  356. // load from stream and validate
  357. binaryStream.Seek(0, AZ::IO::GenericStream::ST_SEEK_BEGIN);
  358. AZ::Entity* sliceEntity3Clone = AZ::Utils::LoadObjectFromStream<AZ::Entity>(binaryStream, &serializeContext);
  359. // instantiate the loaded component
  360. sliceEntity3Clone->FindComponent<AZ::SliceComponent>()->SetSerializeContext(&serializeContext);
  361. sliceEntity3Clone->Init();
  362. sliceEntity3Clone->Activate();
  363. entities.clear();
  364. sliceEntity3Clone->FindComponent<AZ::SliceComponent>()->GetEntities(entities);
  365. AZ_TEST_ASSERT(entities.size() == 1);
  366. AZ_TEST_ASSERT(entities.front()->FindComponent<MyTestComponent1>());
  367. AZ_TEST_ASSERT(entities.front()->FindComponent<MyTestComponent1>()->m_float == 15.0f); // this value was modified in the base prefab
  368. AZ_TEST_ASSERT(entities.front()->FindComponent<MyTestComponent1>()->m_int == 22);
  369. /////////////////////////////////////////////////////////////////////////////
  370. /////////////////////////////////////////////////////////////////////////////
  371. // Testing patching instance-owned entities in an existing prefab.
  372. {
  373. AZ::Entity rootEntity;
  374. AZ::SliceComponent* rootSlice = rootEntity.CreateComponent<AZ::SliceComponent>();
  375. rootSlice->SetSerializeContext(&serializeContext);
  376. rootEntity.Init();
  377. rootEntity.Activate();
  378. AZ::Data::Asset<AZ::SliceAsset> testAsset = AZ::Data::AssetManager::Instance().CreateAsset<AZ::SliceAsset>(m_catalog->GenerateMockAssetId(), AZ::Data::AssetLoadBehavior::Default);
  379. AZ::Entity* testAssetEntity = aznew AZ::Entity;
  380. AZ::SliceComponent* testAssetSlice = testAssetEntity->CreateComponent<AZ::SliceComponent>();
  381. testAssetSlice->SetSerializeContext(&serializeContext);
  382. // Add a couple test entities to the prefab asset.
  383. AZ::Entity* testEntity1 = aznew AZ::Entity();
  384. testEntity1->CreateComponent<MyTestComponent1>()->m_float = 15.f;
  385. testAssetSlice->AddEntity(testEntity1);
  386. AZ::Entity* testEntity2 = aznew AZ::Entity();
  387. testEntity2->CreateComponent<MyTestComponent1>()->m_float = 5.f;
  388. testAssetSlice->AddEntity(testEntity2);
  389. testAsset.Get()->SetData(testAssetEntity, testAssetSlice);
  390. // Test removing an entity from an existing prefab instance, cloning it, and re-adding it.
  391. // This emulates what's required for fast undo/redo entity restore for slice-owned entities.
  392. {
  393. rootSlice->AddSlice(testAsset);
  394. entities.clear();
  395. rootSlice->GetEntities(entities);
  396. // Grab one of the entities to remove, clone, and re-add.
  397. AZ::Entity* entity = entities.back();
  398. AZ_TEST_ASSERT(entity);
  399. AZ::SliceComponent::SliceInstanceAddress addr = rootSlice->FindSlice(entity);
  400. AZ_TEST_ASSERT(addr.IsValid());
  401. const AZ::SliceComponent::SliceInstanceId instanceId = addr.GetInstance()->GetId();
  402. AZ::SliceComponent::EntityAncestorList ancestors;
  403. addr.GetReference()->GetInstanceEntityAncestry(entity->GetId(), ancestors, 1);
  404. AZ_TEST_ASSERT(ancestors.size() == 1);
  405. AZ::SliceComponent::EntityRestoreInfo restoreInfo;
  406. AZ_TEST_ASSERT(rootSlice->GetEntityRestoreInfo(entity->GetId(), restoreInfo));
  407. // Duplicate the entity and make a data change we can validate later.
  408. AZ::Entity* clone = serializeContext.CloneObject(entity);
  409. clone->FindComponent<MyTestComponent1>()->m_float = 10.f;
  410. // Remove the original. We have two entities in the instance, so this will not wipe the instance.
  411. rootSlice->RemoveEntity(entity);
  412. // Patch it back into the prefab.
  413. rootSlice->RestoreEntity(clone, restoreInfo);
  414. // Re-retrieve the entity. We should find it, and it should match the data of the clone.
  415. addr = rootSlice->FindSlice(clone);
  416. ASSERT_TRUE(addr.IsValid());
  417. EXPECT_EQ(instanceId, addr.GetInstance()->GetId());
  418. entities.clear();
  419. rootSlice->GetEntities(entities);
  420. ASSERT_EQ(clone, entities.back());
  421. EXPECT_EQ(10.0f, entities.back()->FindComponent<MyTestComponent1>()->m_float);
  422. ancestors.clear();
  423. addr.GetReference()->GetInstanceEntityAncestry(clone->GetId(), ancestors, 1);
  424. EXPECT_EQ(1, ancestors.size());
  425. }
  426. // We also support re-adding when the reference no longer exists, so run the above
  427. // test, but such that the entity we remove is the last one, in turn destroying
  428. // the whole instance & reference.
  429. {
  430. rootSlice->RemoveSlice(testAsset);
  431. rootSlice->AddSlice(testAsset);
  432. entities.clear();
  433. rootSlice->GetEntities(entities);
  434. // Remove one of the entities.
  435. rootSlice->RemoveEntity(entities.front());
  436. AZ::Entity* entity = entities.back();
  437. AZ_TEST_ASSERT(entity);
  438. AZ::SliceComponent::SliceInstanceAddress addr = rootSlice->FindSlice(entity);
  439. AZ_TEST_ASSERT(addr.IsValid());
  440. const AZ::SliceComponent::SliceInstanceId instanceId = addr.GetInstance()->GetId();
  441. AZ::SliceComponent::EntityAncestorList ancestors;
  442. addr.GetReference()->GetInstanceEntityAncestry(entity->GetId(), ancestors, 1);
  443. AZ_TEST_ASSERT(ancestors.size() == 1);
  444. AZ::SliceComponent::EntityRestoreInfo restoreInfo;
  445. AZ_TEST_ASSERT(rootSlice->GetEntityRestoreInfo(entity->GetId(), restoreInfo));
  446. // Duplicate the entity and make a data change we can validate later.
  447. AZ::Entity* clone = serializeContext.CloneObject(entity);
  448. clone->FindComponent<MyTestComponent1>()->m_float = 10.f;
  449. // Remove the original. We have two entities in the instance, so this will not wipe the instance.
  450. rootSlice->RemoveEntity(entity);
  451. // Patch it back into the prefab.
  452. rootSlice->RestoreEntity(clone, restoreInfo);
  453. // Re-retrieve the entity. We should find it, and it should match the data of the clone.
  454. addr = rootSlice->FindSlice(clone);
  455. AZ_TEST_ASSERT(addr.IsValid());
  456. AZ_TEST_ASSERT(addr.GetInstance()->GetId() == instanceId);
  457. entities.clear();
  458. rootSlice->GetEntities(entities);
  459. AZ_TEST_ASSERT(entities.back() == clone);
  460. AZ_TEST_ASSERT(entities.back()->FindComponent<MyTestComponent1>()->m_float == 10.0f);
  461. ancestors.clear();
  462. addr.GetReference()->GetInstanceEntityAncestry(clone->GetId(), ancestors, 1);
  463. AZ_TEST_ASSERT(ancestors.size() == 1);
  464. }
  465. }
  466. /////////////////////////////////////////////////////////////////////////////
  467. // just reset the asset, don't delete the owned entity (we delete it later)
  468. sliceAsset1->SetData(nullptr, nullptr, false);
  469. sliceAsset2->SetData(nullptr, nullptr, false);
  470. delete sliceEntity;
  471. delete sliceEntity2;
  472. delete sliceEntity2Clone;
  473. delete sliceEntity3;
  474. delete sliceEntity3Clone;
  475. sliceAssetHolder.Reset(); // release asset
  476. sliceAssetHolder1.Reset();
  477. AZ::Data::AssetManager::Destroy();
  478. }
  479. /// a mock asset catalog which only contains two fixed assets.
  480. class SliceTest_RecursionDetection_Catalog
  481. : public AZ::Data::AssetCatalog
  482. , public AZ::Data::AssetCatalogRequestBus::Handler
  483. {
  484. public:
  485. AZ_CLASS_ALLOCATOR(SliceTest_RecursionDetection_Catalog, AZ::SystemAllocator);
  486. AZ::Uuid randomUuid = AZ::Uuid::CreateRandom();
  487. AZ::Data::AssetId m_assetIdSlice1 = AZ::Data::AssetId("{9DE6E611-CE12-4D78-90A5-53D106A50042}", 0);
  488. AZ::Data::AssetId m_assetIdSlice2 = AZ::Data::AssetId("{884C3DEE-3C86-4941-85E9-89103BAE314B}", 0);
  489. SliceTest_RecursionDetection_Catalog()
  490. {
  491. AZ::Data::AssetCatalogRequestBus::Handler::BusConnect();
  492. }
  493. ~SliceTest_RecursionDetection_Catalog() override
  494. {
  495. AZ::Data::AssetCatalogRequestBus::Handler::BusDisconnect();
  496. }
  497. //////////////////////////////////////////////////////////////////////////
  498. // AZ::Data::AssetCatalogRequestBus
  499. AZ::Data::AssetInfo GetAssetInfoById(const AZ::Data::AssetId& id) override
  500. {
  501. AZ::Data::AssetInfo result;
  502. result.m_assetType = azrtti_typeid<AZ::SliceAsset>();
  503. if (id == m_assetIdSlice1)
  504. {
  505. result.m_assetId = id;
  506. }
  507. else if (id == m_assetIdSlice2)
  508. {
  509. result.m_assetId = id;
  510. }
  511. return result;
  512. }
  513. //////////////////////////////////////////////////////////////////////////
  514. AZ::Data::AssetStreamInfo GetStreamInfoForLoad(const AZ::Data::AssetId& id, const AZ::Data::AssetType& type) override
  515. {
  516. EXPECT_TRUE(type == AZ::AzTypeInfo<AZ::SliceAsset>::Uuid());
  517. AZ::Data::AssetStreamInfo info;
  518. info.m_dataOffset = 0;
  519. info.m_streamFlags = AZ::IO::OpenMode::ModeRead;
  520. if (m_assetIdSlice1 == id)
  521. {
  522. info.m_streamName = "MySliceAsset1.txt";
  523. }
  524. else if (m_assetIdSlice2 == id)
  525. {
  526. info.m_streamName = "MySliceAsset2.txt";
  527. }
  528. if (!info.m_streamName.empty())
  529. {
  530. // this ensures the parallel running unit tests do not overlap their files that they use.
  531. AZ::IO::Path fullName = GetTestFolderPath() / AZStd::string::format("%s-%s", randomUuid.ToString<AZStd::string>().c_str(), info.m_streamName.c_str());
  532. info.m_streamName = AZStd::move(fullName.Native());
  533. info.m_dataLen = static_cast<size_t>(AZ::IO::SystemFile::Length(info.m_streamName.c_str()));
  534. }
  535. else
  536. {
  537. info.m_dataLen = 0;
  538. }
  539. return info;
  540. }
  541. AZ::Data::AssetStreamInfo GetStreamInfoForSave(const AZ::Data::AssetId& id, const AZ::Data::AssetType& type) override
  542. {
  543. AZ::Data::AssetStreamInfo info;
  544. info = GetStreamInfoForLoad(id, type);
  545. info.m_streamFlags = AZ::IO::OpenMode::ModeWrite;
  546. return info;
  547. }
  548. };
  549. class DataFlags_CleanupTest
  550. : public LeakDetectionFixture
  551. {
  552. protected:
  553. void SetUp() override
  554. {
  555. auto allEntitiesValidFunction = [](AZ::EntityId) { return true; };
  556. m_dataFlags = AZStd::make_unique<AZ::SliceComponent::DataFlagsPerEntity>(allEntitiesValidFunction);
  557. m_addressOfSetFlag.push_back(AZ_CRC_CE("Components"));
  558. m_valueOfSetFlag = AZ::DataPatch::Flag::ForceOverrideSet;
  559. m_entityIdToGoMissing = AZ::Entity::MakeId();
  560. m_entityIdToRemain = AZ::Entity::MakeId();
  561. m_remainingEntity = AZStd::make_unique<AZ::Entity>(m_entityIdToRemain);
  562. m_remainingEntityList.push_back(m_remainingEntity.get());
  563. m_dataFlags->SetEntityDataFlagsAtAddress(m_entityIdToGoMissing, m_addressOfSetFlag, m_valueOfSetFlag);
  564. m_dataFlags->SetEntityDataFlagsAtAddress(m_entityIdToRemain, m_addressOfSetFlag, m_valueOfSetFlag);
  565. m_dataFlags->Cleanup(m_remainingEntityList);
  566. }
  567. void TearDown() override
  568. {
  569. m_remainingEntity.reset();
  570. m_dataFlags.reset();
  571. }
  572. AZStd::unique_ptr<AZ::SliceComponent::DataFlagsPerEntity> m_dataFlags;
  573. AZ::DataPatch::AddressType m_addressOfSetFlag;
  574. AZ::DataPatch::Flags m_valueOfSetFlag;
  575. AZ::EntityId m_entityIdToGoMissing;
  576. AZ::EntityId m_entityIdToRemain;
  577. AZStd::unique_ptr<AZ::Entity> m_remainingEntity;
  578. AZ::SliceComponent::EntityList m_remainingEntityList;
  579. };
  580. TEST_F(DataFlags_CleanupTest, MissingEntitiesPruned)
  581. {
  582. EXPECT_TRUE(m_dataFlags->GetEntityDataFlags(m_entityIdToGoMissing).empty());
  583. }
  584. TEST_F(DataFlags_CleanupTest, ValidEntitiesRemain)
  585. {
  586. EXPECT_EQ(m_dataFlags->GetEntityDataFlagsAtAddress(m_entityIdToRemain, m_addressOfSetFlag), m_valueOfSetFlag);
  587. }
  588. TEST_F(SliceTest, SliceMetadataInfoComponentV1ToV2Converter)
  589. {
  590. AZStd::string_view sliceAssociatedEntityIds = R"(<ObjectStream version="3">
  591. <Class name="SliceMetadataInfoComponent" field="element" version="1" type="{25EE4D75-8A17-4449-81F4-E561005BAABD}">
  592. <Class name="AZStd::unordered_set" field="AssociatedIds" type="{6C8F8E52-AB4A-5C1F-8E56-9AC390290B94}">
  593. <Class name="EntityId" field="element" version="1" type="{6383F1D3-BB27-4E6B-A49A-6409B2059EAA}">
  594. <Class name="AZ::u64" field="id" value="421626392978" type="{D6597933-47CD-4FC8-B911-63F3E2B0993A}"/>
  595. </Class>
  596. </Class>
  597. </Class>
  598. </ObjectStream>)";
  599. AZ::Entity* entity = aznew AZ::Entity("Slice");
  600. AZ::SliceMetadataInfoComponent* sliceMetadataInfoComponent = entity->CreateComponent<AZ::SliceMetadataInfoComponent>();
  601. AZ::IO::MemoryStream xmlStream(sliceAssociatedEntityIds.data(), sliceAssociatedEntityIds.size());
  602. AZ::Utils::LoadObjectFromStreamInPlace(xmlStream, *sliceMetadataInfoComponent, m_serializeContext);
  603. sliceMetadataInfoComponent->Activate();
  604. AZStd::set<AZ::EntityId> associatedEntities;
  605. AZ::SliceMetadataInfoRequestBus::Event(sliceMetadataInfoComponent->GetEntityId(), &AZ::SliceMetadataInfoRequestBus::Events::GetAssociatedEntities, associatedEntities);
  606. ASSERT_TRUE(associatedEntities.size() == 1);
  607. delete sliceMetadataInfoComponent;
  608. delete entity;
  609. }
  610. TEST_F(SliceTest, PreventOverrideOfPropertyinEntityFromSlice_InstancedSlicesCantOverrideProperty)
  611. {
  612. ////////////////////////////////////////////////////////////////////////
  613. // Create a root slice and make it an asset
  614. AZ::Entity* rootSliceEntity = aznew AZ::Entity();
  615. AZ::SliceComponent* rootSliceComponent = rootSliceEntity->CreateComponent<AZ::SliceComponent>();
  616. AZ::EntityId entityId1InRootSlice;
  617. AZ::ComponentId componentId1InRootSlice;
  618. AZ::Data::Asset<AZ::SliceAsset> rootSliceAssetRef;
  619. {
  620. rootSliceComponent->SetSerializeContext(m_serializeContext);
  621. rootSliceEntity->Init();
  622. rootSliceEntity->Activate();
  623. // put 1 entity in the root slice
  624. AZ::Entity* entity1InRootSlice = aznew AZ::Entity();
  625. entityId1InRootSlice = entity1InRootSlice->GetId();
  626. MyTestComponent1* component1InRootSlice = entity1InRootSlice->CreateComponent<MyTestComponent1>();
  627. componentId1InRootSlice = component1InRootSlice->GetId();
  628. rootSliceComponent->AddEntity(entity1InRootSlice);
  629. // create a "fake" slice asset
  630. rootSliceAssetRef = AZ::Data::AssetManager::Instance().CreateAsset<AZ::SliceAsset>(m_catalog->GenerateMockAssetId(), AZ::Data::AssetLoadBehavior::Default);
  631. rootSliceAssetRef.Get()->SetData(rootSliceEntity, rootSliceComponent);
  632. }
  633. ////////////////////////////////////////////////////////////////////////
  634. // Create slice2, which contains an instance of the first slice
  635. // store slice2 in stream
  636. AZStd::vector<AZ::u8> slice2Buffer;
  637. AZ::IO::ByteContainerStream<AZStd::vector<AZ::u8>> slice2Stream(&slice2Buffer);
  638. {
  639. AZ::Entity* slice2Entity = aznew AZ::Entity();
  640. AZ::SliceComponent* slice2Component = slice2Entity->CreateComponent<AZ::SliceComponent>();
  641. slice2Component->SetSerializeContext(m_serializeContext);
  642. slice2Component->AddSlice(rootSliceAssetRef);
  643. AZ::SliceComponent::EntityList entitiesInSlice2;
  644. slice2Component->GetEntities(entitiesInSlice2); // this instantiates slice2 and the rootSlice
  645. // change a property
  646. entitiesInSlice2[0]->FindComponent<MyTestComponent1>()->m_int = 43;
  647. auto slice2ObjectStream = AZ::ObjectStream::Create(&slice2Stream, *m_serializeContext, AZ::ObjectStream::ST_BINARY);
  648. EXPECT_TRUE(slice2ObjectStream->WriteClass(slice2Entity));
  649. EXPECT_TRUE(slice2ObjectStream->Finalize());
  650. delete slice2Entity;
  651. }
  652. ////////////////////////////////////////////////////////////////////////
  653. // Re-spawn slice2, the property should still be overridden
  654. {
  655. slice2Stream.Seek(0, AZ::IO::GenericStream::ST_SEEK_BEGIN);
  656. auto slice2Entity = AZ::Utils::LoadObjectFromStream<AZ::Entity>(slice2Stream, m_serializeContext);
  657. AZ::SliceComponent::EntityList entitiesInSlice2;
  658. slice2Entity->FindComponent<AZ::SliceComponent>()->GetEntities(entitiesInSlice2);
  659. EXPECT_EQ(43, entitiesInSlice2[0]->FindComponent<MyTestComponent1>()->m_int);
  660. delete slice2Entity;
  661. }
  662. ////////////////////////////////////////////////////////////////////////
  663. // Now modify the root slice to prevent override of the component's m_int value.
  664. {
  665. AZ::DataPatch::AddressType address;
  666. address.push_back(AZ_CRC_CE("Components"));
  667. address.push_back(componentId1InRootSlice);
  668. address.push_back(AZ_CRC_CE("int"));
  669. rootSliceComponent->SetEntityDataFlagsAtAddress(entityId1InRootSlice, address, AZ::DataPatch::Flag::PreventOverrideSet);
  670. // There are expectations that slice assets don't change after they're loaded,
  671. // so fake an "asset reload" by writing out the slice and reading it back in.
  672. AZStd::vector<char> rootSliceBuffer;
  673. AZ::IO::ByteContainerStream<AZStd::vector<char>> rootSliceStream(&rootSliceBuffer);
  674. auto rootSliceObjectStream = AZ::ObjectStream::Create(&rootSliceStream, *m_serializeContext, AZ::ObjectStream::ST_XML);
  675. EXPECT_TRUE(rootSliceObjectStream->WriteClass(rootSliceEntity));
  676. EXPECT_TRUE(rootSliceObjectStream->Finalize());
  677. rootSliceStream.Seek(0, AZ::IO::GenericStream::ST_SEEK_BEGIN);
  678. rootSliceEntity = AZ::Utils::LoadObjectFromStream<AZ::Entity>(rootSliceStream, m_serializeContext);
  679. rootSliceComponent = rootSliceEntity->FindComponent<AZ::SliceComponent>();
  680. rootSliceComponent->SetSerializeContext(m_serializeContext);
  681. rootSliceEntity->Init();
  682. rootSliceEntity->Activate();
  683. rootSliceAssetRef.Get()->SetData(rootSliceEntity, rootSliceComponent, true);
  684. }
  685. ////////////////////////////////////////////////////////////////////////
  686. // Re-spawn slice2, the property should NOT be overridden
  687. {
  688. slice2Stream.Seek(0, AZ::IO::GenericStream::ST_SEEK_BEGIN);
  689. auto slice2Entity = AZ::Utils::LoadObjectFromStream<AZ::Entity>(slice2Stream, m_serializeContext);
  690. AZ::SliceComponent::EntityList entitiesInSlice2;
  691. slice2Entity->FindComponent<AZ::SliceComponent>()->GetEntities(entitiesInSlice2);
  692. EXPECT_NE(43, entitiesInSlice2[0]->FindComponent<MyTestComponent1>()->m_int);
  693. delete slice2Entity;
  694. }
  695. }
  696. }
  697. #ifdef HAVE_BENCHMARK
  698. namespace Benchmark
  699. {
  700. static void BM_Slice_GenerateNewIdsAndFixRefs(benchmark::State& state)
  701. {
  702. AZ::ComponentApplication componentApp;
  703. AZ::ComponentApplication::Descriptor desc;
  704. desc.m_useExistingAllocator = true;
  705. AZ::ComponentApplication::StartupParameters startupParameters;
  706. startupParameters.m_loadSettingsRegistry = false;
  707. componentApp.Create(desc, startupParameters);
  708. UnitTest::MyTestComponent1::Reflect(componentApp.GetSerializeContext());
  709. UnitTest::MyTestComponent2::Reflect(componentApp.GetSerializeContext());
  710. // we use some randomness to set up this scenario,
  711. // seed the generator so we get the same results each time.
  712. AZ::u32 randSeed[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
  713. AZ::Sfmt::GetInstance().Seed(randSeed, AZ_ARRAY_SIZE(randSeed));
  714. // setup a container with N entities
  715. AZ::SliceComponent::InstantiatedContainer container;
  716. for (int64_t entityI = 0; entityI < state.range(0); ++entityI)
  717. {
  718. auto entity = aznew AZ::Entity();
  719. // add components with nothing to remap, just to bulk up the volume of processed data
  720. entity->CreateComponent<UnitTest::MyTestComponent1>();
  721. entity->CreateComponent<UnitTest::MyTestComponent1>();
  722. entity->CreateComponent<UnitTest::MyTestComponent1>();
  723. // add component which references another EntityId
  724. auto component2 = entity->CreateComponent<UnitTest::MyTestComponent2>();
  725. if (entityI != 0)
  726. {
  727. component2->m_entityId = container.m_entities[AZ::Sfmt::GetInstance().Rand64() % container.m_entities.size()]->GetId();
  728. }
  729. container.m_entities.push_back(entity);
  730. }
  731. // shuffle entities so that some ID references are earlier in the data and some are later
  732. for (size_t i = container.m_entities.size() - 1; i > 0; --i)
  733. {
  734. AZStd::swap(container.m_entities[i], container.m_entities[AZ::Sfmt::GetInstance().Rand64() % (i+1)]);
  735. }
  736. AZStd::unordered_map<AZ::EntityId, AZ::EntityId> remappedIds;
  737. while (state.KeepRunning())
  738. {
  739. // Setup
  740. state.PauseTiming();
  741. AZ::SliceComponent::InstantiatedContainer* clonedContainer = componentApp.GetSerializeContext()->CloneObject(&container);
  742. state.ResumeTiming();
  743. // Timed Test
  744. AZ::IdUtils::Remapper<AZ::EntityId>::GenerateNewIdsAndFixRefs(clonedContainer, remappedIds, componentApp.GetSerializeContext());
  745. state.PauseTiming();
  746. delete clonedContainer;
  747. remappedIds.clear();
  748. state.ResumeTiming();
  749. }
  750. }
  751. BENCHMARK(BM_Slice_GenerateNewIdsAndFixRefs)->Arg(10)->Arg(1000);
  752. } // namespace Benchmark
  753. #endif // HAVE_BENCHMARK