AssetManagerLoadingTests.cpp 169 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674
  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 <AzCore/Asset/AssetManager.h>
  9. #include <AzCore/Asset/AssetSerializer.h>
  10. #include <AzCore/Console/IConsole.h>
  11. #include <AzCore/Console/Console.h>
  12. #include <AzCore/Interface/Interface.h>
  13. #include <AzCore/IO/SystemFile.h>
  14. #include <AzCore/IO/Streamer/Streamer.h>
  15. #include <AzCore/IO/FileIO.h>
  16. #include <AzCore/IO/GenericStreams.h>
  17. #include <AzCore/Math/Crc.h>
  18. #include <AzCore/Math/Uuid.h>
  19. #include <AzCore/Jobs/JobManager.h>
  20. #include <AzCore/Jobs/JobContext.h>
  21. #include <AzCore/Outcome/Outcome.h>
  22. #include <AzCore/Serialization/SerializeContext.h>
  23. #include <AzCore/Serialization/ObjectStream.h>
  24. #include <AzCore/Serialization/Utils.h>
  25. #include <AzCore/std/parallel/thread.h>
  26. #include <AzCore/std/functional.h>
  27. #include <AzCore/std/parallel/condition_variable.h>
  28. #include <AzCore/UnitTest/TestTypes.h>
  29. #include <AzCore/UnitTest/Mocks/MockFileIOBase.h>
  30. #include <AZTestShared/Utils/Utils.h>
  31. #include <Streamer/IStreamerMock.h>
  32. #include <Tests/Asset/BaseAssetManagerTest.h>
  33. #include <Tests/Asset/TestAssetTypes.h>
  34. #include <Tests/SerializeContextFixture.h>
  35. #include <Tests/TestCatalog.h>
  36. namespace UnitTest
  37. {
  38. using namespace AZ;
  39. using namespace AZ::Data;
  40. struct OnAssetReadyListener :
  41. public AssetLoadBus::Handler
  42. {
  43. using OnAssetReadyCheck = AZStd::function<bool(const OnAssetReadyListener&)>;
  44. OnAssetReadyListener(const AssetId& assetId, const AssetType& assetType, bool autoConnect = true)
  45. : m_assetId(assetId)
  46. , m_assetType(assetType)
  47. {
  48. if (autoConnect && m_assetId.IsValid())
  49. {
  50. AssetLoadBus::Handler::BusConnect(m_assetId);
  51. }
  52. }
  53. ~OnAssetReadyListener() override
  54. {
  55. m_assetId.SetInvalid();
  56. m_latest = {};
  57. AssetLoadBus::Handler::BusDisconnect();
  58. }
  59. void OnAssetReady(Asset<AssetData> asset) override
  60. {
  61. EXPECT_EQ(asset->GetId(), m_assetId);
  62. EXPECT_EQ(asset->GetType(), m_assetType);
  63. if (m_readyCheck)
  64. {
  65. EXPECT_EQ(m_readyCheck(*this), true);
  66. }
  67. m_ready++;
  68. m_latest = asset;
  69. }
  70. void OnAssetReloaded(Asset<AssetData> asset) override
  71. {
  72. EXPECT_EQ(asset->GetId(), m_assetId);
  73. EXPECT_EQ(asset->GetType(), m_assetType);
  74. m_reloaded++;
  75. m_latest = asset;
  76. }
  77. void OnAssetError(Asset<AssetData> asset) override
  78. {
  79. EXPECT_EQ(asset->GetId(), m_assetId);
  80. EXPECT_EQ(asset->GetType(), m_assetType);
  81. m_error++;
  82. m_latest = asset;
  83. }
  84. void OnAssetDataLoaded(Asset<AssetData> asset) override
  85. {
  86. EXPECT_EQ(asset->GetId(), m_assetId);
  87. EXPECT_EQ(asset->GetType(), m_assetType);
  88. m_dataLoaded++;
  89. m_latest = asset;
  90. }
  91. AssetId m_assetId;
  92. AssetType m_assetType;
  93. AZStd::atomic_int m_ready{};
  94. AZStd::atomic_int m_error{};
  95. AZStd::atomic_int m_reloaded{};
  96. AZStd::atomic_int m_dataLoaded{};
  97. Asset<AssetData> m_latest;
  98. OnAssetReadyCheck m_readyCheck;
  99. };
  100. struct ContainerReadyListener
  101. : public AssetBus::MultiHandler
  102. {
  103. ContainerReadyListener(const AssetId& assetId)
  104. : m_assetId(assetId)
  105. {
  106. BusConnect(assetId);
  107. }
  108. ~ContainerReadyListener() override
  109. {
  110. BusDisconnect();
  111. }
  112. void OnAssetContainerReady(Asset<AssetData> asset) override
  113. {
  114. EXPECT_EQ(asset->GetId(), m_assetId);
  115. m_ready++;
  116. }
  117. AssetId m_assetId;
  118. AZStd::atomic_int m_ready{};
  119. };
  120. /**
  121. * Generate a situation where we have more dependent job loads than we have threads
  122. * to process them.
  123. * This will test the aspect of the system where ObjectStreams and asset jobs loading dependent
  124. * assets will do the work in their own thread.
  125. */
  126. class AssetJobsFloodTest : public DisklessAssetManagerBase
  127. {
  128. public:
  129. TestAssetManager* m_testAssetManager{ nullptr };
  130. DataDrivenHandlerAndCatalog* m_assetHandlerAndCatalog{ nullptr };
  131. static inline const AZ::Uuid MyAsset1Id{ "{5B29FE2B-6B41-48C9-826A-C723951B0560}" };
  132. static inline const AZ::Uuid MyAsset2Id{ "{BD354AE5-B5D5-402A-A12E-BE3C96F6522B}" };
  133. static inline const AZ::Uuid MyAsset3Id{ "{622C3FC9-5AE2-4E52-AFA2-5F7095ADAB53}" };
  134. static inline const AZ::Uuid MyAsset4Id{ "{EE99215B-7AB4-4757-B8AF-F78BD4903AC4}" };
  135. static inline const AZ::Uuid MyAsset5Id{ "{D9CDAB04-D206-431E-BDC0-1DD615D56197}" };
  136. static inline const AZ::Uuid MyAsset6Id{ "{B2F139C3-5032-4B52-ADCA-D52A8F88E043}" };
  137. static inline const AZ::Uuid DelayLoadAssetId{ "{5553A2B0-2401-4600-AE2F-4702A3288AB2}" };
  138. static inline const AZ::Uuid NoLoadAssetId{ "{E14BD18D-A933-4CBD-B64E-25F14D5E69E4}" };
  139. // A -> A
  140. static inline const AZ::Uuid CircularAId{ "{8FDEC8B3-AEB3-43AF-9D99-9DFF1BB59EA8}" };
  141. // B -> C -> B
  142. static inline const AZ::Uuid CircularBId{ "{ECB6EDBC-2FDA-42FF-9564-341A0B5F5249}" };
  143. static inline const AZ::Uuid CircularCId{ "{B86CF17A-779E-4679-BC0B-3C47446CF89F}" };
  144. // D -> B -> C -> B
  145. static inline const AZ::Uuid CircularDId{ "{1FE8342E-9DCE-4AA9-969A-3F3A3526E6CF}" };
  146. // Designed to test cases where a preload chain exists and one of the underlying assets
  147. // Can't be loaded either due to no asset info being found or no handler (The more likely case)
  148. static inline const AZ::Uuid PreloadBrokenDepAId{ "{E5ABB446-DB05-4413-9FE4-EA368F293A8F}" };
  149. static inline const AZ::Uuid PreloadBrokenDepBId{ "{828FF33A-D716-4DF8-AD6F-6BBC66F4CC8B}" };
  150. static inline const AZ::Uuid PreloadAssetNoDataId{ "{9F670DC8-0D89-4FA8-A1D5-B05AF7B04DBB}" };
  151. static inline const AZ::Uuid PreloadAssetNoHandlerId{ "{5F32A180-E380-45A2-89F8-C5CF53B53BDD}" };
  152. // Preload Tree has a root, PreloadA, and QueueLoadA
  153. // PreloadA has PreloadB and QueueLoadB
  154. // QueueLoadB has PreloadC and QueueLoadC
  155. static inline const AZ::Uuid PreloadAssetRootId{ "{A1C3C4EA-726E-4DA1-B783-5CB5032EED4C}" };
  156. static inline const AZ::Uuid PreloadAssetAId{ "{84795373-9A1F-44AA-9808-AF0BF67C8BD6}" };
  157. static inline const AZ::Uuid QueueLoadAssetAId{ "{A16A34C9-8CDC-44FC-9962-BE0192568FA2}" };
  158. static inline const AZ::Uuid PreloadAssetBId{ "{3F3745C1-3B9D-47BD-9993-7B61855E8FA0}" };
  159. static inline const AZ::Uuid QueueLoadAssetBId{ "{82A9B00E-BD2B-4EBF-AFDE-C2C30D1822C0}" };
  160. static inline const AZ::Uuid PreloadAssetCId{ "{8364CF95-C443-4A00-9BB4-DCD81E516769}" };
  161. static inline const AZ::Uuid QueueLoadAssetCId{ "{635E9E70-EBE1-493D-92AA-2E45E350D4F5}" };
  162. // Initialize the Job Manager with 2 threads for the Asset Manager to use.
  163. size_t GetNumJobManagerThreads() const override { return 2; }
  164. void SetUp() override
  165. {
  166. DisklessAssetManagerBase::SetUp();
  167. SetupTest();
  168. }
  169. void TearDown() override
  170. {
  171. AssetManager::Destroy();
  172. DisklessAssetManagerBase::TearDown();
  173. }
  174. void SetupAssets()
  175. {
  176. auto* catalog = m_assetHandlerAndCatalog;
  177. catalog->AddAsset<AssetWithAssetReference>(MyAsset1Id, "TestAsset1.txt")->AddPreload(MyAsset4Id);
  178. catalog->AddAsset<AssetWithAssetReference>(MyAsset2Id, "TestAsset2.txt")->AddPreload(MyAsset5Id);
  179. catalog->AddAsset<AssetWithAssetReference>(MyAsset3Id, "TestAsset3.txt")->AddPreload(MyAsset6Id);
  180. catalog->AddAsset<AssetWithSerializedData>(MyAsset4Id, "TestAsset4.txt");
  181. catalog->AddAsset<AssetWithSerializedData>(MyAsset5Id, "TestAsset5.txt");
  182. catalog->AddAsset<AssetWithSerializedData>(MyAsset6Id, "TestAsset6.txt");
  183. catalog->AddAsset<AssetWithAssetReference>(DelayLoadAssetId, "DelayLoadAsset.txt", 10);
  184. catalog->AddAsset<AssetWithAssetReference>(NoLoadAssetId, "NoLoadAsset.txt")->AddNoLoad(MyAsset2Id);
  185. catalog->AddAsset<AssetWithQueueAndPreLoadReferences>(PreloadAssetRootId, "PreLoadRoot.txt")->AddPreload(PreloadAssetAId)->AddQueueLoad(QueueLoadAssetAId);
  186. catalog->AddAsset<AssetWithQueueAndPreLoadReferences>(PreloadAssetAId, "PreLoadA.txt")->AddPreload(PreloadAssetBId)->AddQueueLoad(QueueLoadAssetBId);
  187. catalog->AddAsset<AssetWithQueueAndPreLoadReferences>(PreloadAssetBId, "PreLoadB.txt");
  188. catalog->AddAsset<AssetWithQueueAndPreLoadReferences>(QueueLoadAssetBId, "QueueLoadB.txt");
  189. catalog->AddAsset<AssetWithQueueAndPreLoadReferences>(QueueLoadAssetAId, "QueueLoadA.txt")->AddPreload(PreloadAssetCId)->AddQueueLoad(QueueLoadAssetCId);
  190. catalog->AddAsset<AssetWithQueueAndPreLoadReferences>(PreloadAssetCId, "PreLoadC.txt");
  191. catalog->AddAsset<AssetWithQueueAndPreLoadReferences>(QueueLoadAssetCId, "QueueLoadC.txt");
  192. catalog->AddAsset<AssetWithQueueAndPreLoadReferences>(PreloadBrokenDepAId, "PreLoadBrokenA.txt")->AddPreload(PreloadBrokenDepBId);
  193. catalog->AddAsset<AssetWithQueueAndPreLoadReferences>(PreloadBrokenDepBId, "PreLoadBrokenB.txt")->AddPreload(PreloadAssetNoDataId)->AddPreload(PreloadAssetNoHandlerId);
  194. catalog->AddAsset<AssetWithQueueAndPreLoadReferences>(PreloadAssetNoDataId, "PreLoadNoData.txt", 0, true);
  195. catalog->AddAsset<EmptyAssetWithNoHandler>(PreloadAssetNoHandlerId, "PreLoadNoHandler.txt", 0, false, true);
  196. catalog->AddAsset<AssetWithQueueAndPreLoadReferences>(CircularAId, "CircularA.txt")->AddPreload(CircularAId);
  197. catalog->AddAsset<AssetWithQueueAndPreLoadReferences>(CircularBId, "CircularB.txt")->AddPreload(CircularCId);
  198. catalog->AddAsset<AssetWithQueueAndPreLoadReferences>(CircularCId, "CircularC.txt")->AddPreload(CircularBId);
  199. catalog->AddAsset<AssetWithQueueAndPreLoadReferences>(CircularDId, "CircularD.txt")->AddPreload(CircularBId);
  200. }
  201. void SetupTest()
  202. {
  203. AssetWithSerializedData::Reflect(*m_serializeContext);
  204. AssetWithAssetReference::Reflect(*m_serializeContext);
  205. AssetWithQueueAndPreLoadReferences::Reflect(*m_serializeContext);
  206. AssetManager::Descriptor desc;
  207. m_testAssetManager = aznew TestAssetManager(desc);
  208. AssetManager::SetInstance(m_testAssetManager);
  209. m_assetHandlerAndCatalog = aznew DataDrivenHandlerAndCatalog();
  210. m_assetHandlerAndCatalog->m_context = m_serializeContext;
  211. SetupAssets();
  212. AZStd::vector<AssetType> types;
  213. m_assetHandlerAndCatalog->GetHandledAssetTypes(types);
  214. for (const auto& type : types)
  215. {
  216. m_testAssetManager->RegisterHandler(m_assetHandlerAndCatalog, type);
  217. m_testAssetManager->RegisterCatalog(m_assetHandlerAndCatalog, type);
  218. }
  219. {
  220. AssetWithSerializedData ap1;
  221. AssetWithSerializedData ap2;
  222. AssetWithSerializedData ap3;
  223. EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset4.txt", &ap1, m_serializeContext));
  224. EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset5.txt", &ap2, m_serializeContext));
  225. EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset6.txt", &ap3, m_serializeContext));
  226. AssetWithAssetReference assetWithPreload1;
  227. AssetWithAssetReference assetWithPreload2;
  228. AssetWithAssetReference assetWithPreload3;
  229. AssetWithAssetReference delayedAsset;
  230. AssetWithAssetReference noLoadAsset;
  231. assetWithPreload1.m_asset = m_testAssetManager->CreateAsset<AssetWithSerializedData>(MyAsset4Id, AssetLoadBehavior::PreLoad);
  232. assetWithPreload2.m_asset = m_testAssetManager->CreateAsset<AssetWithSerializedData>(MyAsset5Id, AssetLoadBehavior::PreLoad);
  233. assetWithPreload3.m_asset = m_testAssetManager->CreateAsset<AssetWithSerializedData>(MyAsset6Id, AssetLoadBehavior::PreLoad);
  234. noLoadAsset.m_asset = m_testAssetManager->CreateAsset<AssetWithSerializedData>(MyAsset2Id, AssetLoadBehavior::NoLoad);
  235. EXPECT_EQ(m_assetHandlerAndCatalog->m_numCreations, 4);
  236. EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset1.txt", &assetWithPreload1, m_serializeContext));
  237. EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset2.txt", &assetWithPreload2, m_serializeContext));
  238. EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset3.txt", &assetWithPreload3, m_serializeContext));
  239. EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("DelayLoadAsset.txt", &delayedAsset, m_serializeContext));
  240. EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("NoLoadAsset.txt", &noLoadAsset, m_serializeContext));
  241. AssetWithQueueAndPreLoadReferences preLoadRoot;
  242. AssetWithQueueAndPreLoadReferences preLoadA;
  243. AssetWithQueueAndPreLoadReferences queueLoadA;
  244. AssetWithQueueAndPreLoadReferences preLoadBrokenA;
  245. AssetWithQueueAndPreLoadReferences preLoadBrokenB;
  246. // We don't need to set up the internal asset references for PreLoadB, QueueLoadB, PreLoadC, QueueLoadC, PreLoadNoData
  247. // so we'll just share the same empty asset to serialize out for those.
  248. AssetWithQueueAndPreLoadReferences noRefs;
  249. preLoadRoot.m_preLoad = m_testAssetManager->CreateAsset<AssetWithAssetReference>(PreloadAssetAId, AssetLoadBehavior::PreLoad);
  250. preLoadRoot.m_queueLoad = m_testAssetManager->CreateAsset<AssetWithAssetReference>(QueueLoadAssetAId, AssetLoadBehavior::QueueLoad);
  251. preLoadA.m_preLoad = m_testAssetManager->CreateAsset<AssetWithAssetReference>(PreloadAssetBId, AssetLoadBehavior::PreLoad);
  252. preLoadA.m_queueLoad = m_testAssetManager->CreateAsset<AssetWithAssetReference>(QueueLoadAssetBId, AssetLoadBehavior::QueueLoad);
  253. queueLoadA.m_preLoad = m_testAssetManager->CreateAsset<AssetWithAssetReference>(PreloadAssetCId, AssetLoadBehavior::PreLoad);
  254. queueLoadA.m_queueLoad = m_testAssetManager->CreateAsset<AssetWithAssetReference>(QueueLoadAssetCId, AssetLoadBehavior::QueueLoad);
  255. preLoadBrokenA.m_preLoad = m_testAssetManager->CreateAsset<AssetWithAssetReference>(PreloadBrokenDepBId, AssetLoadBehavior::PreLoad);
  256. preLoadBrokenB.m_preLoad = m_testAssetManager->CreateAsset<AssetWithAssetReference>(PreloadAssetNoDataId, AssetLoadBehavior::PreLoad);
  257. EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("PreLoadRoot.txt", &preLoadRoot, m_serializeContext));
  258. EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("PreLoadA.txt", &preLoadA, m_serializeContext));
  259. EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("PreLoadB.txt", &noRefs, m_serializeContext));
  260. EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("PreLoadC.txt", &noRefs, m_serializeContext));
  261. EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("QueueLoadA.txt", &queueLoadA, m_serializeContext));
  262. EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("QueueLoadB.txt", &noRefs, m_serializeContext));
  263. EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("QueueLoadC.txt", &noRefs, m_serializeContext));
  264. EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("PreLoadBrokenA.txt", &preLoadBrokenA, m_serializeContext));
  265. EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("PreLoadBrokenB.txt", &preLoadBrokenB, m_serializeContext));
  266. EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("PreLoadNoData.txt", &noRefs, m_serializeContext));
  267. AssetWithQueueAndPreLoadReferences circularA;
  268. AssetWithQueueAndPreLoadReferences circularB;
  269. AssetWithQueueAndPreLoadReferences circularC;
  270. AssetWithQueueAndPreLoadReferences circularD;
  271. circularA.m_preLoad = m_testAssetManager->CreateAsset<AssetWithAssetReference>(CircularAId, AssetLoadBehavior::PreLoad);
  272. circularB.m_preLoad = m_testAssetManager->CreateAsset<AssetWithAssetReference>(CircularCId, AssetLoadBehavior::PreLoad);
  273. circularC.m_preLoad = m_testAssetManager->CreateAsset<AssetWithAssetReference>(CircularBId, AssetLoadBehavior::PreLoad);
  274. circularD.m_preLoad = circularC.m_preLoad;
  275. EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("CircularA.txt", &circularA, m_serializeContext));
  276. EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("CircularB.txt", &circularB, m_serializeContext));
  277. EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("CircularC.txt", &circularC, m_serializeContext));
  278. EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("CircularD.txt", &circularD, m_serializeContext));
  279. m_assetHandlerAndCatalog->m_numCreations = 0;
  280. }
  281. }
  282. void CheckFinishedCreationsAndDestructions()
  283. {
  284. // Make sure asset jobs have finished before validating the number of destroyed assets, because it's possible that the asset job
  285. // still contains a reference on the job thread that won't trigger the asset destruction until the asset job is destroyed.
  286. BlockUntilAssetJobsAreComplete();
  287. m_testAssetManager->DispatchEvents();
  288. EXPECT_EQ(m_assetHandlerAndCatalog->m_numCreations, m_assetHandlerAndCatalog->m_numDestructions);
  289. }
  290. };
  291. static constexpr AZStd::chrono::seconds MaxDispatchTimeoutSeconds = BaseAssetManagerTest::DefaultTimeoutSeconds * 12;
  292. template <typename Pred>
  293. bool DispatchEventsUntilCondition(AZ::Data::AssetManager& assetManager, Pred&& conditionPredicate,
  294. AZStd::chrono::seconds logIntervalSeconds = BaseAssetManagerTest::DefaultTimeoutSeconds,
  295. AZStd::chrono::seconds maxTimeoutSeconds = MaxDispatchTimeoutSeconds)
  296. {
  297. // If the Max Timeout is hit the test will be marked as a failure
  298. auto dispatchEventTimeStart = AZStd::chrono::steady_clock::now();
  299. AZStd::chrono::seconds dispatchEventNextLogTime = logIntervalSeconds;
  300. while (!conditionPredicate())
  301. {
  302. auto currentTime = AZStd::chrono::steady_clock::now();
  303. if (auto elapsedTime{ currentTime - dispatchEventTimeStart };
  304. elapsedTime >= dispatchEventNextLogTime)
  305. {
  306. const testing::TestInfo* test_info = ::testing::UnitTest::GetInstance()->current_test_info();
  307. AZ_Printf("AssetManagerLoadingTest", "The DispatchEventsUntiTimeout function has been waiting for %llu seconds"
  308. " in test %s.%s", elapsedTime.count(), test_info->test_case_name(), test_info->name());
  309. // Update the next log time to be the next multiple of DefaultTimeout Seconds
  310. // after current elapsed time
  311. dispatchEventNextLogTime = AZStd::chrono::duration_cast<AZStd::chrono::seconds>(elapsedTime + logIntervalSeconds - ((elapsedTime + logIntervalSeconds) % logIntervalSeconds));
  312. if (elapsedTime >= maxTimeoutSeconds)
  313. {
  314. return false;
  315. }
  316. }
  317. assetManager.DispatchEvents();
  318. AZStd::this_thread::yield();
  319. }
  320. return true;
  321. }
  322. #if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS || AZ_TRAIT_DISABLE_ASSET_MANAGER_FLOOD_TEST
  323. TEST_F(AssetJobsFloodTest, DISABLED_FloodTest)
  324. #else
  325. TEST_F(AssetJobsFloodTest, FloodTest)
  326. #endif // !AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  327. {
  328. const auto timeoutSeconds = AZStd::chrono::seconds(10);
  329. OnAssetReadyListener assetStatus1(MyAsset1Id, azrtti_typeid<AssetWithAssetReference>());
  330. OnAssetReadyListener assetStatus2(MyAsset2Id, azrtti_typeid<AssetWithAssetReference>());
  331. OnAssetReadyListener assetStatus3(MyAsset3Id, azrtti_typeid<AssetWithAssetReference>());
  332. OnAssetReadyListener assetStatus4(MyAsset4Id, azrtti_typeid<AssetWithSerializedData>());
  333. OnAssetReadyListener assetStatus5(MyAsset5Id, azrtti_typeid<AssetWithSerializedData>());
  334. OnAssetReadyListener assetStatus6(MyAsset6Id, azrtti_typeid<AssetWithSerializedData>());
  335. // Suspend the streamer until we've got all the assets queued simultaneously to ensure that we've flooded the system.
  336. auto streamer = AZ::Interface<AZ::IO::IStreamer>::Get();
  337. streamer->SuspendProcessing();
  338. // Load all three root assets, each of which cascades another asset load as PreLoad.
  339. Asset<AssetWithAssetReference> asset1 = m_testAssetManager->GetAsset(MyAsset1Id, azrtti_typeid<AssetWithAssetReference>(), AZ::Data::AssetLoadBehavior::Default);
  340. Asset<AssetWithAssetReference> asset2 = m_testAssetManager->GetAsset(MyAsset2Id, azrtti_typeid<AssetWithAssetReference>(), AZ::Data::AssetLoadBehavior::Default);
  341. Asset<AssetWithAssetReference> asset3 = m_testAssetManager->GetAsset(MyAsset3Id, azrtti_typeid<AssetWithAssetReference>(), AZ::Data::AssetLoadBehavior::Default);
  342. // These two assets should be queued - this should not block the above assets which reference them from processing them
  343. // Due to the Queued status we check for below
  344. Data::Asset<AssetWithSerializedData> asset4Block = m_testAssetManager->GetAsset(MyAsset4Id, azrtti_typeid<AssetWithSerializedData>(), AZ::Data::AssetLoadBehavior::Default);
  345. Data::Asset<AssetWithSerializedData> asset5Block = m_testAssetManager->GetAsset(MyAsset5Id, azrtti_typeid<AssetWithSerializedData>(), AZ::Data::AssetLoadBehavior::Default);
  346. EXPECT_EQ(m_assetHandlerAndCatalog->m_numCreations, 6);
  347. // Add a reload request - this should sit on the reload queue, however it should not be processed because the original job for asset3 has begun the
  348. // Load and assets in the m_assets map in loading state block reload.
  349. m_testAssetManager->ReloadAsset(MyAsset3Id, AZ::Data::AssetLoadBehavior::Default);
  350. // Now that all the initial requests are queued, start up the streamer.
  351. streamer->ResumeProcessing();
  352. // Make sure all 6 assets have loaded.
  353. asset1.BlockUntilLoadComplete();
  354. asset2.BlockUntilLoadComplete();
  355. asset3.BlockUntilLoadComplete();
  356. asset4Block.BlockUntilLoadComplete();
  357. asset5Block.BlockUntilLoadComplete();
  358. EXPECT_TRUE(asset1.IsReady());
  359. EXPECT_TRUE(asset2.IsReady());
  360. EXPECT_TRUE(asset3.IsReady());
  361. EXPECT_TRUE(asset1->m_asset.IsReady());
  362. EXPECT_TRUE(asset2->m_asset.IsReady());
  363. EXPECT_TRUE(asset3->m_asset.IsReady());
  364. EXPECT_TRUE(asset4Block.IsReady());
  365. EXPECT_TRUE(asset5Block.IsReady());
  366. EXPECT_EQ(m_assetHandlerAndCatalog->m_numCreations, 6);
  367. // Validate that asset 6 has already completed as well.
  368. Data::Asset<AssetWithSerializedData> asset6Block = m_testAssetManager->GetAsset(MyAsset6Id, azrtti_typeid<AssetWithSerializedData>(), AZ::Data::AssetLoadBehavior::Default);
  369. EXPECT_TRUE(asset6Block.IsReady());
  370. // Assets above can be ready (PreNotify) before the signal has reached our listener - allow for a small window to hear
  371. auto maxTimeout = AZStd::chrono::steady_clock::now() + timeoutSeconds;
  372. bool timedOut = false;
  373. while (!assetStatus1.m_ready || !assetStatus2.m_ready || !assetStatus3.m_ready)
  374. {
  375. AssetManager::Instance().DispatchEvents();
  376. if (AZStd::chrono::steady_clock::now() > maxTimeout)
  377. {
  378. timedOut = true;
  379. break;
  380. }
  381. }
  382. // Make sure we didn't time out.
  383. ASSERT_FALSE(timedOut);
  384. EXPECT_EQ(assetStatus1.m_ready, 1);
  385. EXPECT_EQ(assetStatus2.m_ready, 1);
  386. EXPECT_EQ(assetStatus3.m_ready, 1);
  387. // MyAsset4 and 5 were loaded as a blocking dependency while a job was waiting to load them
  388. // we want to verify they did not go through a second load
  389. EXPECT_EQ(assetStatus4.m_ready, 1);
  390. EXPECT_EQ(assetStatus5.m_ready, 1);
  391. // MyAsset6 was loaded by MyAsset3. Make sure it's ready too.
  392. EXPECT_EQ(assetStatus6.m_ready, 1);
  393. EXPECT_EQ(assetStatus1.m_reloaded, 0);
  394. EXPECT_EQ(assetStatus2.m_reloaded, 0);
  395. EXPECT_EQ(assetStatus3.m_reloaded, 0);
  396. EXPECT_EQ(assetStatus4.m_reloaded, 0);
  397. EXPECT_EQ(assetStatus5.m_reloaded, 0);
  398. EXPECT_EQ(assetStatus6.m_reloaded, 0);
  399. // Since Asset Container cleanup is queued on the ebus, dispatch events one last time to be sure the containers are released
  400. AssetManager::Instance().DispatchEvents();
  401. EXPECT_EQ(m_testAssetManager->GetAssetContainers().size(), 0);
  402. // This should process
  403. m_testAssetManager->ReloadAsset(MyAsset1Id, AZ::Data::AssetLoadBehavior::Default);
  404. // This should process
  405. m_testAssetManager->ReloadAsset(MyAsset2Id, AZ::Data::AssetLoadBehavior::Default);
  406. streamer->SuspendProcessing();
  407. // This should process but be queued
  408. m_testAssetManager->ReloadAsset(MyAsset3Id, AZ::Data::AssetLoadBehavior::Default);
  409. // This should get tossed because there's a reload request waiting
  410. m_testAssetManager->ReloadAsset(MyAsset3Id, AZ::Data::AssetLoadBehavior::Default);
  411. streamer->ResumeProcessing();
  412. // Allow our reloads to process and signal our listeners
  413. maxTimeout = AZStd::chrono::steady_clock::now() + timeoutSeconds;
  414. while (!assetStatus1.m_reloaded || !assetStatus2.m_reloaded || !assetStatus3.m_reloaded)
  415. {
  416. m_testAssetManager->DispatchEvents();
  417. if (AZStd::chrono::steady_clock::now() > maxTimeout)
  418. {
  419. timedOut = true;
  420. break;
  421. }
  422. AZStd::this_thread::yield();
  423. }
  424. // Make sure we didn't time out.
  425. ASSERT_FALSE(timedOut);
  426. EXPECT_EQ(assetStatus1.m_ready, 1);
  427. EXPECT_EQ(assetStatus2.m_ready, 1);
  428. EXPECT_EQ(assetStatus3.m_ready, 1);
  429. EXPECT_EQ(assetStatus1.m_reloaded, 1);
  430. EXPECT_EQ(assetStatus2.m_reloaded, 1);
  431. EXPECT_EQ(assetStatus3.m_reloaded, 1);
  432. EXPECT_EQ(assetStatus4.m_reloaded, 0);
  433. EXPECT_EQ(assetStatus5.m_reloaded, 0);
  434. EXPECT_EQ(assetStatus6.m_reloaded, 0);
  435. // Since Asset Container cleanup is queued on the ebus, dispatch events one last time to be sure the containers are released
  436. AssetManager::Instance().DispatchEvents();
  437. EXPECT_EQ(m_testAssetManager->GetAssetContainers().size(), 0);
  438. OnAssetReadyListener delayLoadAssetStatus(DelayLoadAssetId, azrtti_typeid<AssetWithAssetReference>());
  439. Data::Asset<AssetWithAssetReference> delayedAsset = m_testAssetManager->GetAsset(
  440. DelayLoadAssetId,
  441. azrtti_typeid<AssetWithAssetReference>(),
  442. AZ::Data::AssetLoadBehavior::Default);
  443. // this verifies that a reloading asset in "loading" state queues another load when it is complete
  444. maxTimeout = AZStd::chrono::steady_clock::now() + timeoutSeconds;
  445. while (!delayLoadAssetStatus.m_ready)
  446. {
  447. m_testAssetManager->DispatchEvents();
  448. if (AZStd::chrono::steady_clock::now() > maxTimeout)
  449. {
  450. timedOut = true;
  451. break;
  452. }
  453. }
  454. // Make sure we didn't time out.
  455. ASSERT_FALSE(timedOut);
  456. EXPECT_EQ(delayLoadAssetStatus.m_ready, 1);
  457. // Since Asset Container cleanup is queued on the ebus, dispatch events one last time to be sure the containers are released
  458. AssetManager::Instance().DispatchEvents();
  459. EXPECT_EQ(m_testAssetManager->GetAssetContainers().size(), 0);
  460. // This should go through to loading
  461. m_testAssetManager->ReloadAsset(DelayLoadAssetId, AZ::Data::AssetLoadBehavior::Default);
  462. maxTimeout = AZStd::chrono::steady_clock::now() + timeoutSeconds;
  463. while (m_testAssetManager->GetReloadStatus(DelayLoadAssetId) != AZ::Data::AssetData::AssetStatus::Loading)
  464. {
  465. m_testAssetManager->DispatchEvents();
  466. if (AZStd::chrono::steady_clock::now() > maxTimeout)
  467. {
  468. timedOut = true;
  469. break;
  470. }
  471. }
  472. // Make sure we didn't time out.
  473. ASSERT_FALSE(timedOut);
  474. // This should mark the first for an additional reload
  475. m_testAssetManager->ReloadAsset(DelayLoadAssetId, AZ::Data::AssetLoadBehavior::Default);
  476. // This should do nothing
  477. m_testAssetManager->ReloadAsset(DelayLoadAssetId, AZ::Data::AssetLoadBehavior::Default);
  478. maxTimeout = AZStd::chrono::steady_clock::now() + timeoutSeconds;
  479. while (delayLoadAssetStatus.m_reloaded < 2)
  480. {
  481. m_testAssetManager->DispatchEvents();
  482. if (AZStd::chrono::steady_clock::now() > maxTimeout)
  483. {
  484. timedOut = true;
  485. break;
  486. }
  487. AZStd::this_thread::yield();
  488. }
  489. // Make sure we didn't time out.
  490. ASSERT_FALSE(timedOut);
  491. EXPECT_EQ(delayLoadAssetStatus.m_ready, 1);
  492. // the initial reload and the marked reload should both have gone through
  493. EXPECT_EQ(delayLoadAssetStatus.m_reloaded, 2);
  494. // There should be no other reloads now. This is the status of our requests to reload the
  495. // asset which isn't usually the same as the status of the base delayedAsset we're still holding
  496. AZ::Data::AssetData::AssetStatus curStatus = m_testAssetManager->GetReloadStatus(DelayLoadAssetId);
  497. // For our reload tests "NotLoaded" is equivalent to "None currently reloading in any status"
  498. AZ::Data::AssetData::AssetStatus expected_status = AZ::Data::AssetData::AssetStatus::NotLoaded;
  499. EXPECT_EQ(curStatus, expected_status);
  500. // Our base delayedAsset should still be Ready as we still hold the reference
  501. AZ::Data::AssetData::AssetStatus baseStatus = delayedAsset->GetStatus();
  502. AZ::Data::AssetData::AssetStatus expected_base_status = AZ::Data::AssetData::AssetStatus::Ready;
  503. EXPECT_EQ(baseStatus, expected_base_status);
  504. }
  505. TEST_F(AssetJobsFloodTest, RapidAcquireAndRelease)
  506. {
  507. auto assetUuids = {
  508. MyAsset1Id,
  509. MyAsset2Id,
  510. MyAsset3Id,
  511. };
  512. AZStd::vector<AZStd::thread> threads;
  513. AZStd::mutex mutex;
  514. AZStd::atomic<int> threadCount(static_cast<int>(assetUuids.size()));
  515. AZStd::condition_variable cv;
  516. AZStd::atomic_bool keepDispatching(true);
  517. auto dispatch = [&keepDispatching]() {
  518. while (keepDispatching)
  519. {
  520. AssetManager::Instance().DispatchEvents();
  521. }
  522. };
  523. AZStd::thread dispatchThread(dispatch);
  524. for (const auto& assetUuid : assetUuids)
  525. {
  526. threads.emplace_back([this, &threadCount, &cv, assetUuid]() {
  527. bool checkLoaded = true;
  528. for (int i = 0; i < 1000; i++)
  529. {
  530. Asset<AssetWithAssetReference> asset1 =
  531. m_testAssetManager->GetAsset(assetUuid, azrtti_typeid<AssetWithAssetReference>(), AZ::Data::AssetLoadBehavior::PreLoad);
  532. if (checkLoaded)
  533. {
  534. asset1.BlockUntilLoadComplete();
  535. EXPECT_TRUE(asset1.IsReady()) << "Iteration " << i << " failed. Asset status: " << static_cast<int>(asset1.GetStatus());
  536. }
  537. checkLoaded = !checkLoaded;
  538. }
  539. --threadCount;
  540. cv.notify_one();
  541. });
  542. }
  543. bool timedOut = false;
  544. // Used to detect a deadlock. If we wait for more than 5 seconds, it's likely a deadlock has occurred
  545. while (threadCount > 0 && !timedOut)
  546. {
  547. AZStd::unique_lock<AZStd::mutex> lock(mutex);
  548. timedOut = (AZStd::cv_status::timeout == cv.wait_until(lock, AZStd::chrono::steady_clock::now() + DefaultTimeoutSeconds));
  549. }
  550. ASSERT_EQ(threadCount, 0) << "Thread count is non-zero, a thread has likely deadlocked. Test will not shut down cleanly.";
  551. for (auto& thread : threads)
  552. {
  553. thread.join();
  554. }
  555. keepDispatching = false;
  556. dispatchThread.join();
  557. AssetManager::Destroy();
  558. }
  559. #if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  560. TEST_F(AssetJobsFloodTest, DISABLED_AssetLoadBehaviorIsPreserved)
  561. #else
  562. TEST_F(AssetJobsFloodTest, AssetLoadBehaviorIsPreserved)
  563. #endif // !AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  564. {
  565. auto asset = AZ::Data::AssetManager::Instance().GetAsset<AssetWithAssetReference>(MyAsset1Id, AZ::Data::AssetLoadBehavior::PreLoad);
  566. asset.BlockUntilLoadComplete();
  567. EXPECT_EQ(asset.GetAutoLoadBehavior(), AZ::Data::AssetLoadBehavior::PreLoad);
  568. }
  569. #if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  570. TEST_F(AssetJobsFloodTest, DISABLED_BlockOnTheSameAsset_DoesNotDeadlock)
  571. #else
  572. TEST_F(AssetJobsFloodTest, BlockOnTheSameAsset_DoesNotDeadlock)
  573. #endif // !AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  574. {
  575. // This test is meant to ensure we don't get into a deadlock in the following situation:
  576. // Asset A is loaded and ready notification is queued for dispatch
  577. // Asset B is requested and we block waiting for it
  578. // While waiting, we dispatch events
  579. // Dispatched event leads to another blocking load of asset B
  580. // Setup
  581. constexpr auto AssetNoRefA = AZ::Uuid("{EC5E3E4F-518C-4B03-A8BF-C9966CF763EB}");
  582. constexpr auto AssetNoRefB = AZ::Uuid("{C07E55B5-E60C-4575-AE07-32DD3DC68B1A}");
  583. {
  584. m_assetHandlerAndCatalog->AddAsset<AssetWithSerializedData>(AssetNoRefA, "a.txt");
  585. m_assetHandlerAndCatalog->AddAsset<AssetWithSerializedData>(AssetNoRefB, "b.txt", 10);
  586. AssetWithSerializedData ap;
  587. EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("a.txt", &ap, m_serializeContext));
  588. EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("b.txt", &ap, m_serializeContext));
  589. }
  590. auto& assetManager = AssetManager::Instance();
  591. AssetBusCallbacks callbacks{};
  592. AZ_PUSH_DISABLE_WARNING(5233, "-Wunknown-warning-option") // Older versions of MSVC toolchain require to pass constexpr in the
  593. // capture. Newer versions issue unused warning
  594. callbacks.SetOnAssetReadyCallback([&, AssetNoRefB](const Asset<AssetData>&, AssetBusCallbacks&)
  595. AZ_POP_DISABLE_WARNING
  596. {
  597. // This callback should run inside the "main thread" dispatch events loop
  598. auto loadAsset = assetManager.GetAsset<AssetWithSerializedData>(AZ::Uuid(AssetNoRefB), AssetLoadBehavior::Default);
  599. loadAsset.BlockUntilLoadComplete();
  600. EXPECT_TRUE(loadAsset.IsReady());
  601. });
  602. callbacks.BusConnect(AssetId(AZ::Uuid(AssetNoRefA)));
  603. auto assetNoRefA = assetManager.GetAsset<AssetWithSerializedData>(AZ::Uuid(AssetNoRefA), AssetLoadBehavior::Default);
  604. // Don't call BlockUntilReady because that can call dispatch events, we need this asset to be in the PreNotify state
  605. while (assetNoRefA.GetStatus() != AssetData::AssetStatus::ReadyPreNotify)
  606. {
  607. AZStd::this_thread::yield();
  608. }
  609. EXPECT_EQ(assetNoRefA.GetStatus(), AssetData::AssetStatus::ReadyPreNotify);
  610. // Suspend the streamer until after we start blocking so we have time to get into the dispatch events call
  611. auto streamer = AZ::Interface<AZ::IO::IStreamer>::Get();
  612. streamer->SuspendProcessing();
  613. auto assetRefA = assetManager.GetAsset<AssetWithSerializedData>(AZ::Uuid(AssetNoRefB), AssetLoadBehavior::Default);
  614. AZStd::binary_semaphore completeSignal;
  615. AZStd::thread thread([streamer, &completeSignal]()
  616. {
  617. AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(1));
  618. streamer->ResumeProcessing();
  619. if (!completeSignal.try_acquire_for(AZStd::chrono::seconds(5)))
  620. {
  621. FAIL() << "Deadlock detected";
  622. }
  623. });
  624. assetRefA.BlockUntilLoadComplete();
  625. completeSignal.release();
  626. EXPECT_TRUE(assetNoRefA.IsReady());
  627. EXPECT_EQ(assetRefA.GetStatus(), AssetData::AssetStatus::Ready);
  628. thread.join();
  629. callbacks.BusDisconnect();
  630. }
  631. /**
  632. * Verify that loads without using the Asset Container still work correctly
  633. */
  634. class AssetContainerDisableTest
  635. : public DisklessAssetManagerBase
  636. {
  637. public:
  638. static inline const AZ::Uuid MyAsset1Id{ "{5B29FE2B-6B41-48C9-826A-C723951B0560}" };
  639. static inline const AZ::Uuid MyAsset2Id{ "{BD354AE5-B5D5-402A-A12E-BE3C96F6522B}" };
  640. static inline const AZ::Uuid MyAsset3Id{ "{622C3FC9-5AE2-4E52-AFA2-5F7095ADAB53}" };
  641. static inline const AZ::Uuid MyAsset4Id{ "{EE99215B-7AB4-4757-B8AF-F78BD4903AC4}" };
  642. static inline const AZ::Uuid MyAsset5Id{ "{D9CDAB04-D206-431E-BDC0-1DD615D56197}" };
  643. static inline const AZ::Uuid MyAsset6Id{ "{B2F139C3-5032-4B52-ADCA-D52A8F88E043}" };
  644. TestAssetManager* m_testAssetManager{ nullptr };
  645. DataDrivenHandlerAndCatalog* m_assetHandlerAndCatalog{ nullptr };
  646. LoadAssetDataSynchronizer m_loadDataSynchronizer;
  647. // Initialize the Job Manager with 2 threads for the Asset Manager to use.
  648. size_t GetNumJobManagerThreads() const override { return 2; }
  649. void SetUp() override
  650. {
  651. DisklessAssetManagerBase::SetUp();
  652. SetupTest();
  653. }
  654. void TearDown() override
  655. {
  656. AssetManager::Instance().UnregisterHandler(m_assetHandlerAndCatalog);
  657. delete m_assetHandlerAndCatalog;
  658. AssetManager::Destroy();
  659. DisklessAssetManagerBase::TearDown();
  660. }
  661. void SetupAssets()
  662. {
  663. auto* catalog = m_assetHandlerAndCatalog;
  664. catalog->AddAsset<AssetWithAssetReference>(MyAsset1Id, "TestAsset1.txt", 1000, false, false, &m_loadDataSynchronizer)->AddPreload(MyAsset4Id);
  665. catalog->AddAsset<AssetWithAssetReference>(MyAsset2Id, "TestAsset2.txt", 1000, false, false, &m_loadDataSynchronizer)->AddPreload(MyAsset5Id);
  666. catalog->AddAsset<AssetWithAssetReference>(MyAsset3Id, "TestAsset3.txt", 1000, false, false, &m_loadDataSynchronizer)->AddPreload(MyAsset6Id);
  667. catalog->AddAsset<AssetWithSerializedData>(MyAsset4Id, "TestAsset4.txt", 1000);
  668. catalog->AddAsset<AssetWithSerializedData>(MyAsset5Id, "TestAsset5.txt", 1000);
  669. catalog->AddAsset<AssetWithSerializedData>(MyAsset6Id, "TestAsset6.txt", 1000);
  670. }
  671. void SetupTest()
  672. {
  673. AssetWithSerializedData::Reflect(*m_serializeContext);
  674. AssetWithAssetReference::Reflect(*m_serializeContext);
  675. AssetManager::Descriptor desc;
  676. m_testAssetManager = aznew TestAssetManager(desc);
  677. AssetManager::SetInstance(m_testAssetManager);
  678. m_assetHandlerAndCatalog = aznew DataDrivenHandlerAndCatalog();
  679. m_assetHandlerAndCatalog->m_context = m_serializeContext;
  680. SetupAssets();
  681. AZStd::vector<AssetType> types;
  682. m_assetHandlerAndCatalog->GetHandledAssetTypes(types);
  683. for (const auto& type : types)
  684. {
  685. m_testAssetManager->RegisterHandler(m_assetHandlerAndCatalog, type);
  686. m_testAssetManager->RegisterCatalog(m_assetHandlerAndCatalog, type);
  687. }
  688. {
  689. AssetWithSerializedData ap1;
  690. AssetWithSerializedData ap2;
  691. AssetWithSerializedData ap3;
  692. EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset4.txt", &ap1, m_serializeContext));
  693. EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset5.txt", &ap2, m_serializeContext));
  694. EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset6.txt", &ap3, m_serializeContext));
  695. AssetWithAssetReference assetWithPreload1;
  696. AssetWithAssetReference assetWithPreload2;
  697. AssetWithAssetReference assetWithPreload3;
  698. assetWithPreload1.m_asset = m_testAssetManager->CreateAsset<AssetWithSerializedData>(MyAsset4Id, AssetLoadBehavior::PreLoad);
  699. assetWithPreload2.m_asset = m_testAssetManager->CreateAsset<AssetWithSerializedData>(MyAsset5Id, AssetLoadBehavior::PreLoad);
  700. assetWithPreload3.m_asset = m_testAssetManager->CreateAsset<AssetWithSerializedData>(MyAsset6Id, AssetLoadBehavior::PreLoad);
  701. EXPECT_EQ(m_assetHandlerAndCatalog->m_numCreations, 3);
  702. EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset1.txt", &assetWithPreload1, m_serializeContext));
  703. EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset2.txt", &assetWithPreload2, m_serializeContext));
  704. EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset3.txt", &assetWithPreload3, m_serializeContext));
  705. m_assetHandlerAndCatalog->m_numCreations = 0;
  706. }
  707. }
  708. };
  709. // Test is currently disabled. Without loading using containers this uses blocking waits in asset load jobs which
  710. // have a race condition deadlock. Details in SPEC-5061
  711. #if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS || AZ_TRAIT_DISABLE_ASSETCONTAINERDISABLETEST
  712. TEST_F(AssetContainerDisableTest, DISABLED_AssetContainerDisableTest_LoadMoreAssetsThanJobThreads_LoadSucceeds)
  713. #else
  714. TEST_F(AssetContainerDisableTest, DISABLED_AssetContainerDisableTest_LoadMoreAssetsThanJobThreads_LoadSucceeds)
  715. #endif // AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  716. {
  717. m_testAssetManager->SetParallelDependentLoadingEnabled(false);
  718. OnAssetReadyListener assetStatus1(MyAsset1Id, azrtti_typeid<AssetWithAssetReference>());
  719. OnAssetReadyListener assetStatus2(MyAsset2Id, azrtti_typeid<AssetWithAssetReference>());
  720. OnAssetReadyListener assetStatus3(MyAsset3Id, azrtti_typeid<AssetWithAssetReference>());
  721. OnAssetReadyListener assetStatus4(MyAsset4Id, azrtti_typeid<AssetWithSerializedData>());
  722. OnAssetReadyListener assetStatus5(MyAsset5Id, azrtti_typeid<AssetWithSerializedData>());
  723. OnAssetReadyListener assetStatus6(MyAsset6Id, azrtti_typeid<AssetWithSerializedData>());
  724. // Load all three root assets, each of which cascades another asset load with a PreLoad dependency.
  725. Asset<AssetWithAssetReference> asset1 = m_testAssetManager->GetAsset(MyAsset1Id, azrtti_typeid<AssetWithAssetReference>(), AZ::Data::AssetLoadBehavior::Default);
  726. Asset<AssetWithAssetReference> asset2 = m_testAssetManager->GetAsset(MyAsset2Id, azrtti_typeid<AssetWithAssetReference>(), AZ::Data::AssetLoadBehavior::Default);
  727. Asset<AssetWithAssetReference> asset3 = m_testAssetManager->GetAsset(MyAsset3Id, azrtti_typeid<AssetWithAssetReference>(), AZ::Data::AssetLoadBehavior::Default);
  728. // There should be at least 3 asset creations, but potentially could have up to 6 depending on how quickly the
  729. // loads process.
  730. EXPECT_GE(m_assetHandlerAndCatalog->m_numCreations, 3);
  731. EXPECT_LE(m_assetHandlerAndCatalog->m_numCreations, 6);
  732. while (m_loadDataSynchronizer.m_numBlocking < 2)
  733. {
  734. AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(1));
  735. }
  736. m_loadDataSynchronizer.m_readyToLoad = true;
  737. m_loadDataSynchronizer.m_condition.notify_all();
  738. asset3.BlockUntilLoadComplete();
  739. asset2.BlockUntilLoadComplete();
  740. asset1.BlockUntilLoadComplete();
  741. EXPECT_TRUE(asset1.IsReady());
  742. EXPECT_TRUE(asset2.IsReady());
  743. EXPECT_TRUE(asset3.IsReady());
  744. EXPECT_TRUE(asset1->m_asset.IsReady());
  745. EXPECT_TRUE(asset2->m_asset.IsReady());
  746. EXPECT_TRUE(asset3->m_asset.IsReady());
  747. EXPECT_EQ(m_assetHandlerAndCatalog->m_numCreations, 6);
  748. // Assets above can be ready (PreNotify) before the signal has reached our listener - allow for a small window to hear
  749. auto maxTimeout = AZStd::chrono::steady_clock::now() + DefaultTimeoutSeconds;
  750. while (!assetStatus1.m_ready || !assetStatus2.m_ready || !assetStatus3.m_ready)
  751. {
  752. AssetManager::Instance().DispatchEvents();
  753. if (AZStd::chrono::steady_clock::now() > maxTimeout)
  754. {
  755. AZ_Assert(false, "Timeout reached.");
  756. break;
  757. }
  758. }
  759. EXPECT_EQ(assetStatus1.m_ready, 1);
  760. EXPECT_EQ(assetStatus2.m_ready, 1);
  761. EXPECT_EQ(assetStatus3.m_ready, 1);
  762. // MyAsset4, MyAsset5, and MyAsset6 should have been loaded as blocking PreLoad dependent assets.
  763. EXPECT_EQ(assetStatus4.m_ready, 1);
  764. EXPECT_EQ(assetStatus5.m_ready, 1);
  765. EXPECT_EQ(assetStatus6.m_ready, 1);
  766. m_testAssetManager->SetParallelDependentLoadingEnabled(true);
  767. }
  768. #if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  769. TEST_F(AssetJobsFloodTest, DISABLED_LoadTest_SameAsset_DifferentFilters)
  770. #else
  771. TEST_F(AssetJobsFloodTest, LoadTest_SameAsset_DifferentFilters)
  772. #endif // AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  773. {
  774. m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusConnect();
  775. // Queue a load of PreloadAssetA, which will trigger the loads of PreloadAssetAId and its dependents (PreLoadAssetB, QueueLoadAssetB)
  776. Data::Asset<AssetWithQueueAndPreLoadReferences> asset2 = m_testAssetManager->GetAsset(PreloadAssetAId, azrtti_typeid<AssetWithQueueAndPreLoadReferences>(), AZ::Data::AssetLoadBehavior::Default);
  777. // Next queue a load of PreloadAssetA again, but with a "no load" filter to specifically NOT load the dependents.
  778. Data::Asset<AssetWithQueueAndPreLoadReferences> asset1 = m_testAssetManager->GetAsset(PreloadAssetAId, azrtti_typeid<AssetWithQueueAndPreLoadReferences>(), AZ::Data::AssetLoadBehavior::Default, AssetLoadParameters(&AssetFilterNoAssetLoading));
  779. // Wait for asset2 to be ready, as this means that the dependents are ready as well.
  780. while (!asset2.IsReady())
  781. {
  782. m_testAssetManager->DispatchEvents();
  783. AZStd::this_thread::yield();
  784. }
  785. // Even though asset1 filtered out the loads, it will still find the in-process load of PreLoadAssetB and get a reference to it.
  786. // Since asset2 completed, this means that both asset1 and asset2 should have valid, Ready references to PreLoadAssetB.
  787. EXPECT_TRUE(asset1->m_preLoad.IsReady());
  788. EXPECT_TRUE(asset2->m_preLoad.IsReady());
  789. bool stillWaiting;
  790. do
  791. {
  792. stillWaiting = false;
  793. auto&& containers = m_testAssetManager->GetAssetContainers();
  794. for (const auto& [container, containerSp] : containers)
  795. {
  796. if (!container->IsReady())
  797. {
  798. m_testAssetManager->DispatchEvents();
  799. AZStd::this_thread::yield();
  800. stillWaiting = true;
  801. break;
  802. }
  803. }
  804. // If any assets were ReadyPreNotify
  805. m_testAssetManager->DispatchEvents();
  806. if (!stillWaiting)
  807. {
  808. EXPECT_EQ(containers.size(), 0);
  809. }
  810. } while (stillWaiting);
  811. m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusDisconnect();
  812. }
  813. #if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  814. TEST_F(AssetJobsFloodTest, DISABLED_LoadTest_NoLoadDependentFilter_PreventsDependentsFromLoading)
  815. #else
  816. TEST_F(AssetJobsFloodTest, LoadTest_NoLoadDependentFilter_PreventsDependentsFromLoading)
  817. #endif // !AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  818. {
  819. m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusConnect();
  820. // Queue a load of PreloadAssetA with a "no load" filter to specifically NOT load the dependents.
  821. Data::Asset<AssetWithQueueAndPreLoadReferences> asset1 = m_testAssetManager->GetAsset(
  822. PreloadAssetAId, azrtti_typeid<AssetWithQueueAndPreLoadReferences>(),
  823. AZ::Data::AssetLoadBehavior::Default, AssetLoadParameters(&AssetFilterNoAssetLoading));
  824. // Wait for asset1 to be ready.
  825. while (!asset1.IsReady())
  826. {
  827. m_testAssetManager->DispatchEvents();
  828. AZStd::this_thread::yield();
  829. }
  830. // The dependent assets shouldn't be loaded for asset1 due to the filter.
  831. EXPECT_EQ(asset1->m_preLoad.GetStatus(), AssetData::AssetStatus::NotLoaded);
  832. EXPECT_EQ(asset1->m_queueLoad.GetStatus(), AssetData::AssetStatus::NotLoaded);
  833. m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusDisconnect();
  834. }
  835. #if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  836. TEST_F(AssetJobsFloodTest, DISABLED_ContainerLoadTest_NoDependencies_CanLoadAsContainer)
  837. #else
  838. TEST_F(AssetJobsFloodTest, ContainerLoadTest_NoDependencies_CanLoadAsContainer)
  839. #endif // !AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  840. {
  841. m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusConnect();
  842. // Setup has already created/destroyed assets
  843. m_assetHandlerAndCatalog->m_numCreations = 0;
  844. m_assetHandlerAndCatalog->m_numDestructions = 0;
  845. {
  846. ContainerReadyListener readyListener(PreloadAssetBId);
  847. OnAssetReadyListener preloadBListener(PreloadAssetBId, azrtti_typeid<AssetWithQueueAndPreLoadReferences>());
  848. auto asset = AssetManager::Instance().FindOrCreateAsset(PreloadAssetBId, azrtti_typeid<AssetWithQueueAndPreLoadReferences>(),
  849. AZ::Data::AssetLoadBehavior::Default);
  850. auto containerReady = m_testAssetManager->GetAssetContainer(asset);
  851. auto maxTimeout = AZStd::chrono::steady_clock::now() + DefaultTimeoutSeconds;
  852. while (!readyListener.m_ready || !preloadBListener.m_ready)
  853. {
  854. m_testAssetManager->DispatchEvents();
  855. if (AZStd::chrono::steady_clock::now() > maxTimeout)
  856. {
  857. break;
  858. }
  859. AZStd::this_thread::yield();
  860. }
  861. EXPECT_EQ(containerReady->IsReady(), true);
  862. EXPECT_EQ(containerReady->GetDependencies().size(), 0);
  863. EXPECT_EQ(containerReady->GetInvalidDependencies(), 0);
  864. EXPECT_EQ(preloadBListener.m_ready, 1);
  865. EXPECT_EQ(preloadBListener.m_dataLoaded, 0);
  866. }
  867. CheckFinishedCreationsAndDestructions();
  868. m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusDisconnect();
  869. }
  870. TEST_F(AssetJobsFloodTest, ContainerCoreTest_BasicDependencyManagement_Success)
  871. {
  872. m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusConnect();
  873. // Setup has already created/destroyed assets
  874. m_assetHandlerAndCatalog->m_numCreations = 0;
  875. m_assetHandlerAndCatalog->m_numDestructions = 0;
  876. const AZ::u32 NumTestAssets = 3;
  877. const AZ::u32 AssetsPerContainer = 2;
  878. {
  879. // Load all three root assets, each of which cascades another asset load.
  880. auto asset1 = m_testAssetManager->FindOrCreateAsset(MyAsset1Id, azrtti_typeid<AssetWithAssetReference>(), AZ::Data::AssetLoadBehavior::Default);
  881. auto asset2 = m_testAssetManager->FindOrCreateAsset(MyAsset2Id, azrtti_typeid<AssetWithAssetReference>(), AZ::Data::AssetLoadBehavior::Default);
  882. auto asset3 = m_testAssetManager->FindOrCreateAsset(MyAsset3Id, azrtti_typeid<AssetWithAssetReference>(), AZ::Data::AssetLoadBehavior::Default);
  883. ContainerReadyListener readyListener1(MyAsset1Id);
  884. ContainerReadyListener readyListener2(MyAsset2Id);
  885. ContainerReadyListener readyListener3(MyAsset3Id);
  886. auto asset1Container = m_testAssetManager->GetAssetContainer(asset1);
  887. auto asset2Container = m_testAssetManager->GetAssetContainer(asset2);
  888. auto asset3Container = m_testAssetManager->GetAssetContainer(asset3);
  889. auto maxTimeout = AZStd::chrono::steady_clock::now() + DefaultTimeoutSeconds;
  890. while (!readyListener1.m_ready || !readyListener2.m_ready || !readyListener3.m_ready)
  891. {
  892. m_testAssetManager->DispatchEvents();
  893. if (AZStd::chrono::steady_clock::now() > maxTimeout)
  894. {
  895. break;
  896. }
  897. AZStd::this_thread::yield();
  898. }
  899. EXPECT_EQ(asset1Container->IsReady(), true);
  900. EXPECT_EQ(asset2Container->IsReady(), true);
  901. EXPECT_EQ(asset3Container->IsReady(), true);
  902. auto rootAsset = asset1Container->GetRootAsset();
  903. EXPECT_EQ(rootAsset->GetId(), MyAsset1Id);
  904. EXPECT_EQ(rootAsset->GetType(), azrtti_typeid<AssetWithAssetReference>());
  905. rootAsset = asset2Container->GetRootAsset();
  906. EXPECT_EQ(rootAsset->GetId(), MyAsset2Id);
  907. EXPECT_EQ(rootAsset->GetType(), azrtti_typeid<AssetWithAssetReference>());
  908. rootAsset = asset3Container->GetRootAsset();
  909. EXPECT_EQ(rootAsset->GetId(), MyAsset3Id);
  910. EXPECT_EQ(rootAsset->GetType(), azrtti_typeid<AssetWithAssetReference>());
  911. rootAsset = {};
  912. EXPECT_EQ(m_assetHandlerAndCatalog->m_numDestructions, 0);
  913. EXPECT_EQ(asset1Container->IsReady(), true);
  914. EXPECT_EQ(asset2Container->IsReady(), true);
  915. EXPECT_EQ(asset3Container->IsReady(), true);
  916. EXPECT_EQ(asset1Container->GetDependencies().size(), 1);
  917. EXPECT_EQ(asset2Container->GetDependencies().size(), 1);
  918. EXPECT_EQ(asset3Container->GetDependencies().size(), 1);
  919. auto asset1CopyContainer = m_testAssetManager->GetAssetContainer(asset1);
  920. EXPECT_EQ(asset1CopyContainer->IsReady(), true);
  921. // We've now created the dependencies for each asset in the container as well
  922. EXPECT_EQ(m_assetHandlerAndCatalog->m_numCreations, NumTestAssets * AssetsPerContainer);
  923. EXPECT_EQ(asset1CopyContainer->GetDependencies().size(), 1);
  924. asset1Container = {};
  925. EXPECT_EQ(asset1CopyContainer->IsReady(), true);
  926. EXPECT_EQ(asset1CopyContainer->GetDependencies().size(), 1);
  927. asset1CopyContainer = {};
  928. asset1 = {};
  929. // Make sure events are dispatched after releasing the asset handles, so they get destroyed.
  930. // This addresses a rare race condition, a test failure roughly once every 2,000 runs on Linux.
  931. m_testAssetManager->DispatchEvents();
  932. // We've released the references for one asset and its dependency
  933. EXPECT_EQ(m_assetHandlerAndCatalog->m_numDestructions, AssetsPerContainer);
  934. asset1 = m_testAssetManager->FindOrCreateAsset(MyAsset1Id, azrtti_typeid<AssetWithAssetReference>(), AZ::Data::AssetLoadBehavior::Default);
  935. asset1Container = m_testAssetManager->GetAssetContainer(asset1);
  936. maxTimeout = AZStd::chrono::steady_clock::now() + DefaultTimeoutSeconds;
  937. while (!asset1Container->IsReady())
  938. {
  939. m_testAssetManager->DispatchEvents();
  940. if (AZStd::chrono::steady_clock::now() > maxTimeout)
  941. {
  942. break;
  943. }
  944. AZStd::this_thread::yield();
  945. }
  946. EXPECT_EQ(asset1Container->IsReady(), true);
  947. }
  948. CheckFinishedCreationsAndDestructions();
  949. // We created each asset and its dependency and recreated one pair
  950. EXPECT_EQ(m_assetHandlerAndCatalog->m_numCreations, (NumTestAssets + 1) * AssetsPerContainer);
  951. m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusDisconnect();
  952. }
  953. #if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  954. TEST_F(AssetJobsFloodTest, DISABLED_ContainerFilterTest_ContainersWithAndWithoutFiltering_Success)
  955. #else
  956. TEST_F(AssetJobsFloodTest, ContainerFilterTest_ContainersWithAndWithoutFiltering_Success)
  957. #endif // !AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  958. {
  959. m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusConnect();
  960. // Setup has already created/destroyed assets
  961. m_assetHandlerAndCatalog->m_numCreations = 0;
  962. m_assetHandlerAndCatalog->m_numDestructions = 0;
  963. {
  964. AZ::Data::AssetFilterCB filterNone = [](const AZ::Data::AssetFilterInfo&) { return true; };
  965. auto asset1 = m_testAssetManager->FindOrCreateAsset(MyAsset1Id, azrtti_typeid<AssetWithAssetReference>(), AZ::Data::AssetLoadBehavior::Default);
  966. auto asset1Container = m_testAssetManager->GetAssetContainer(asset1, AssetLoadParameters{ filterNone });
  967. auto maxTimeout = AZStd::chrono::steady_clock::now() + DefaultTimeoutSeconds;
  968. while (!asset1Container->IsReady())
  969. {
  970. m_testAssetManager->DispatchEvents();
  971. if (AZStd::chrono::steady_clock::now() > maxTimeout)
  972. {
  973. break;
  974. }
  975. AZStd::this_thread::yield();
  976. }
  977. EXPECT_EQ(asset1Container->IsReady(), true);
  978. EXPECT_EQ(asset1Container->GetDependencies().size(), 1);
  979. asset1 = {};
  980. asset1Container = {};
  981. AZ::Data::AssetFilterCB noDependencyCB = [](const AZ::Data::AssetFilterInfo& filterInfo) { return filterInfo.m_assetType != azrtti_typeid<AssetWithSerializedData>(); };
  982. asset1 = m_testAssetManager->FindOrCreateAsset(MyAsset1Id, azrtti_typeid<AssetWithAssetReference>(), AZ::Data::AssetLoadBehavior::Default);
  983. asset1Container = m_testAssetManager->GetAssetContainer(asset1, AssetLoadParameters{ noDependencyCB });
  984. maxTimeout = AZStd::chrono::steady_clock::now() + DefaultTimeoutSeconds;
  985. while (!asset1Container->IsReady())
  986. {
  987. m_testAssetManager->DispatchEvents();
  988. if (AZStd::chrono::steady_clock::now() > maxTimeout)
  989. {
  990. break;
  991. }
  992. AZStd::this_thread::yield();
  993. }
  994. EXPECT_EQ(asset1Container->IsReady(), true);
  995. EXPECT_EQ(asset1Container->GetDependencies().size(), 0);
  996. }
  997. CheckFinishedCreationsAndDestructions();
  998. m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusDisconnect();
  999. }
  1000. #if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  1001. TEST_F(AssetJobsFloodTest, DISABLED_ContainerNotificationTest_ListenForAssetReady_OnlyHearCorrectAsset)
  1002. #else
  1003. TEST_F(AssetJobsFloodTest, ContainerNotificationTest_ListenForAssetReady_OnlyHearCorrectAsset)
  1004. #endif // !AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  1005. {
  1006. m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusConnect();
  1007. // Setup has already created/destroyed assets
  1008. m_assetHandlerAndCatalog->m_numCreations = 0;
  1009. m_assetHandlerAndCatalog->m_numDestructions = 0;
  1010. {
  1011. ContainerReadyListener readyListener1(MyAsset1Id);
  1012. ContainerReadyListener readyListener2(MyAsset2Id);
  1013. auto asset1 = m_testAssetManager->FindOrCreateAsset(MyAsset1Id, azrtti_typeid<AssetWithAssetReference>(), AZ::Data::AssetLoadBehavior::Default);
  1014. auto asset1Container = m_testAssetManager->GetAssetContainer(asset1);
  1015. auto maxTimeout = AZStd::chrono::steady_clock::now() + DefaultTimeoutSeconds;
  1016. while (!readyListener1.m_ready)
  1017. {
  1018. m_testAssetManager->DispatchEvents();
  1019. if (AZStd::chrono::steady_clock::now() > maxTimeout)
  1020. {
  1021. break;
  1022. }
  1023. AZStd::this_thread::yield();
  1024. }
  1025. EXPECT_EQ(asset1Container->IsReady(), true);
  1026. EXPECT_EQ(asset1Container->GetDependencies().size(), 1);
  1027. // MyAsset2 should not have signalled
  1028. EXPECT_EQ(readyListener2.m_ready, 0);
  1029. auto asset2 = m_testAssetManager->FindOrCreateAsset(MyAsset2Id, azrtti_typeid<AssetWithAssetReference>(), AZ::Data::AssetLoadBehavior::Default);
  1030. auto asset2Container = m_testAssetManager->GetAssetContainer(asset2);
  1031. maxTimeout = AZStd::chrono::steady_clock::now() + DefaultTimeoutSeconds;
  1032. while (!asset2Container->IsReady())
  1033. {
  1034. m_testAssetManager->DispatchEvents();
  1035. if (AZStd::chrono::steady_clock::now() > maxTimeout)
  1036. {
  1037. break;
  1038. }
  1039. AZStd::this_thread::yield();
  1040. }
  1041. m_testAssetManager->DispatchEvents();
  1042. EXPECT_EQ(asset2Container->IsReady(), true);
  1043. EXPECT_EQ(asset2Container->GetDependencies().size(), 1);
  1044. auto readyVal = readyListener2.m_ready.load();
  1045. auto asset2ContainerCopy = m_testAssetManager->GetAssetContainer(asset2);
  1046. // Should be ready immediately
  1047. EXPECT_EQ(asset2ContainerCopy->IsReady(), true);
  1048. EXPECT_EQ(asset2ContainerCopy->GetDependencies().size(), 1);
  1049. // Copy shouldn't have signaled because it was ready to begin with
  1050. EXPECT_EQ(readyListener2.m_ready, readyVal);
  1051. }
  1052. CheckFinishedCreationsAndDestructions();
  1053. m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusDisconnect();
  1054. }
  1055. #if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  1056. TEST_F(AssetJobsFloodTest, DISABLED_AssetWithNoLoadReference_LoadDependencies_NoLoadNotLoaded)
  1057. #else
  1058. TEST_F(AssetJobsFloodTest, AssetWithNoLoadReference_LoadDependencies_NoLoadNotLoaded)
  1059. #endif // AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  1060. {
  1061. m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusConnect();
  1062. // Setup has already created/destroyed assets
  1063. m_assetHandlerAndCatalog->m_numCreations = 0;
  1064. m_assetHandlerAndCatalog->m_numDestructions = 0;
  1065. {
  1066. ContainerReadyListener readyListener(NoLoadAssetId);
  1067. // noLoad should only load itself, its dependency shouldn't be loaded but it should know about it
  1068. auto noLoadRef = m_testAssetManager->FindOrCreateAsset(NoLoadAssetId, azrtti_typeid<AssetWithAssetReference>(),
  1069. AZ::Data::AssetLoadBehavior::Default);
  1070. auto noLoadRefContainer = m_testAssetManager->GetAssetContainer(noLoadRef);
  1071. auto maxTimeout = AZStd::chrono::steady_clock::now() + DefaultTimeoutSeconds;
  1072. while (!readyListener.m_ready)
  1073. {
  1074. m_testAssetManager->DispatchEvents();
  1075. if (AZStd::chrono::steady_clock::now() > maxTimeout)
  1076. {
  1077. break;
  1078. }
  1079. AZStd::this_thread::yield();
  1080. }
  1081. EXPECT_EQ(readyListener.m_ready, 1);
  1082. EXPECT_EQ(noLoadRefContainer->IsReady(), true);
  1083. EXPECT_EQ(noLoadRefContainer->GetDependencies().size(), 0);
  1084. // Asset2 should be registered as a noload dependency
  1085. EXPECT_EQ(noLoadRefContainer->GetUnloadedDependencies().size(), 1);
  1086. EXPECT_NE(noLoadRefContainer->GetUnloadedDependencies().find(MyAsset2Id), noLoadRefContainer->GetUnloadedDependencies().end());
  1087. }
  1088. CheckFinishedCreationsAndDestructions();
  1089. m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusDisconnect();
  1090. }
  1091. #if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  1092. TEST_F(AssetJobsFloodTest, DISABLED_AssetWithNoLoadReference_LoadContainerDependencies_LoadAllLoadsNoLoad)
  1093. #else
  1094. TEST_F(AssetJobsFloodTest, AssetWithNoLoadReference_LoadContainerDependencies_LoadAllLoadsNoLoad)
  1095. #endif // AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  1096. {
  1097. m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusConnect();
  1098. // Setup has already created/destroyed assets
  1099. m_assetHandlerAndCatalog->m_numCreations = 0;
  1100. m_assetHandlerAndCatalog->m_numDestructions = 0;
  1101. {
  1102. ContainerReadyListener readyListener(NoLoadAssetId);
  1103. // noLoad should only load itself, its dependency shouldn't be loaded but it should know about it
  1104. auto noLoadRef = m_testAssetManager->FindOrCreateAsset(NoLoadAssetId, azrtti_typeid<AssetWithAssetReference>(),
  1105. AZ::Data::AssetLoadBehavior::Default);
  1106. auto noLoadRefContainer = m_testAssetManager->GetAssetContainer(noLoadRef, AssetLoadParameters(nullptr, AZ::Data::AssetDependencyLoadRules::LoadAll));
  1107. auto maxTimeout = AZStd::chrono::steady_clock::now() + DefaultTimeoutSeconds;
  1108. while (!readyListener.m_ready)
  1109. {
  1110. m_testAssetManager->DispatchEvents();
  1111. if (AZStd::chrono::steady_clock::now() > maxTimeout)
  1112. {
  1113. break;
  1114. }
  1115. AZStd::this_thread::yield();
  1116. }
  1117. EXPECT_EQ(readyListener.m_ready, 1);
  1118. EXPECT_EQ(noLoadRefContainer->IsReady(), true);
  1119. EXPECT_EQ(noLoadRefContainer->GetDependencies().size(), 2);
  1120. EXPECT_EQ(noLoadRefContainer->GetUnloadedDependencies().size(), 0);
  1121. }
  1122. CheckFinishedCreationsAndDestructions();
  1123. m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusDisconnect();
  1124. }
  1125. #if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  1126. TEST_F(AssetJobsFloodTest, DISABLED_AssetWithNoLoadReference_LoadDependencies_BehaviorObeyed)
  1127. #else
  1128. TEST_F(AssetJobsFloodTest, AssetWithNoLoadReference_LoadDependencies_BehaviorObeyed)
  1129. #endif // AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  1130. {
  1131. m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusConnect();
  1132. // Setup has already created/destroyed assets
  1133. m_assetHandlerAndCatalog->m_numCreations = 0;
  1134. m_assetHandlerAndCatalog->m_numDestructions = 0;
  1135. {
  1136. ContainerReadyListener containerLoadingCompleteListener(NoLoadAssetId);
  1137. OnAssetReadyListener readyListener(NoLoadAssetId, azrtti_typeid<AssetWithAssetReference>());
  1138. OnAssetReadyListener dependencyListener(MyAsset2Id, azrtti_typeid<AssetWithAssetReference>());
  1139. SCOPED_TRACE("LoadDependencies_BehaviorObeyed");
  1140. auto AssetOnlyReady = [&readyListener]() -> bool
  1141. {
  1142. return readyListener.m_ready;
  1143. };
  1144. auto AssetAndDependencyReady = [&readyListener, &dependencyListener]() -> bool
  1145. {
  1146. return readyListener.m_ready && dependencyListener.m_ready;
  1147. };
  1148. auto AssetContainerReady = [&containerLoadingCompleteListener]() -> bool
  1149. {
  1150. return containerLoadingCompleteListener.m_ready;
  1151. };
  1152. auto noLoadRef = m_testAssetManager->GetAsset(NoLoadAssetId, azrtti_typeid<AssetWithAssetReference>(),
  1153. AZ::Data::AssetLoadBehavior::Default);
  1154. // Dispatch AssetBus events until the NoLoadAssetId has signaled an OnAssetReady
  1155. // event or the timeout has been reached
  1156. EXPECT_TRUE(DispatchEventsUntilCondition(*m_testAssetManager, AssetOnlyReady))
  1157. << "The DispatchEventsUntiTimeout function has not completed in "
  1158. << MaxDispatchTimeoutSeconds.count() << " seconds. The test will be marked as a failure\n";
  1159. // Dispatch AssetBus events until the asset container used to load
  1160. // NoLoadAssetId has signaled an OnAssetContainerReady event
  1161. // or the timeout has been reached
  1162. // Wait until the current asset container has finished loading the NoLoadAssetId
  1163. // before trigger another load
  1164. // If the wait does not occur here, most likely what would occur is
  1165. // the AssetManager::m_ownedAssetContainers object is still loading the NoLoadAssetId
  1166. // using the default AssetLoadParameters
  1167. // If a call to GetAsset occurs at this point while the Asset is still loading
  1168. // it will ignore the new loadParams below and instead just re-use the existing
  1169. // AssetContainerReader instance, resulting in the dependent MyAsset2Id not
  1170. // being loaded
  1171. // The function that can return an existing AssetContainer instance is the
  1172. // AssetManager::GetAssetContainer. Since it can be in the middle of a load,
  1173. // updating the AssetLoadParams would have an effect on the current in progress
  1174. // load
  1175. EXPECT_TRUE(DispatchEventsUntilCondition(*m_testAssetManager, AssetContainerReady))
  1176. << "The DispatchEventsUntiTimeout function has not completed in "
  1177. << MaxDispatchTimeoutSeconds.count() << " seconds. The test will be marked as a failure\n";
  1178. // Reset the ContainerLoadingComplete ready status back to 0
  1179. containerLoadingCompleteListener.m_ready = 0;
  1180. AZ::Data::AssetLoadParameters loadParams(nullptr, AZ::Data::AssetDependencyLoadRules::LoadAll);
  1181. loadParams.m_reloadMissingDependencies = true;
  1182. auto loadDependencyRef = m_testAssetManager->GetAsset(NoLoadAssetId, azrtti_typeid<AssetWithAssetReference>(),
  1183. AZ::Data::AssetLoadBehavior::Default, loadParams);
  1184. // Dispatch AssetBus events until the NoLoadAssetId and the MyAsset2Id has signaled
  1185. // an OnAssetReady event or the timeout has been reached
  1186. EXPECT_TRUE(DispatchEventsUntilCondition(*m_testAssetManager, AssetAndDependencyReady))
  1187. << "The DispatchEventsUntiTimeout function has not completed in "
  1188. << MaxDispatchTimeoutSeconds.count() << " seconds. The test will be marked as a failure\n";
  1189. EXPECT_EQ(readyListener.m_ready, 1);
  1190. EXPECT_EQ(dependencyListener.m_ready, 1);
  1191. EXPECT_TRUE(DispatchEventsUntilCondition(*m_testAssetManager, AssetContainerReady))
  1192. << "The DispatchEventsUntiTimeout function has not completed in "
  1193. << MaxDispatchTimeoutSeconds.count() << " seconds. The test will be marked as a failure\n";
  1194. }
  1195. CheckFinishedCreationsAndDestructions();
  1196. m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusDisconnect();
  1197. }
  1198. #if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  1199. TEST_F(AssetJobsFloodTest, DISABLED_ContainerLoadTest_RootLoadedAlready_SuccessAndSignal)
  1200. #else
  1201. TEST_F(AssetJobsFloodTest, ContainerLoadTest_RootLoadedAlready_SuccessAndSignal)
  1202. #endif // !AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  1203. {
  1204. m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusConnect();
  1205. // Setup has already created/destroyed assets
  1206. m_assetHandlerAndCatalog->m_numCreations = 0;
  1207. m_assetHandlerAndCatalog->m_numDestructions = 0;
  1208. m_assetHandlerAndCatalog->SetArtificialDelayMilliseconds(0, 20);
  1209. {
  1210. // Listen for MyAsset2Id
  1211. ContainerReadyListener readyListener(MyAsset2Id);
  1212. auto asset2Get = m_testAssetManager->GetAssetInternal(MyAsset2Id, azrtti_typeid<AssetWithAssetReference>(), AZ::Data::AssetLoadBehavior::Default, AssetLoadParameters{ &AZ::Data::AssetFilterNoAssetLoading });
  1213. auto maxTimeout = AZStd::chrono::steady_clock::now() + DefaultTimeoutSeconds;
  1214. while (!asset2Get->IsReady())
  1215. {
  1216. m_testAssetManager->DispatchEvents();
  1217. if (AZStd::chrono::steady_clock::now() > maxTimeout)
  1218. {
  1219. break;
  1220. }
  1221. AZStd::this_thread::yield();
  1222. }
  1223. auto asset2 = m_testAssetManager->FindOrCreateAsset(MyAsset2Id, azrtti_typeid<AssetWithAssetReference>(), AZ::Data::AssetLoadBehavior::Default);
  1224. auto asset2Container = m_testAssetManager->GetAssetContainer(asset2);
  1225. maxTimeout = AZStd::chrono::steady_clock::now() + DefaultTimeoutSeconds;
  1226. while (!readyListener.m_ready)
  1227. {
  1228. m_testAssetManager->DispatchEvents();
  1229. if (AZStd::chrono::steady_clock::now() > maxTimeout)
  1230. {
  1231. break;
  1232. }
  1233. AZStd::this_thread::yield();
  1234. }
  1235. EXPECT_EQ(asset2Container->IsReady(), true);
  1236. EXPECT_EQ(asset2Container->GetDependencies().size(), 1);
  1237. EXPECT_EQ(readyListener.m_ready, 1);
  1238. asset2Get = { };
  1239. }
  1240. CheckFinishedCreationsAndDestructions();
  1241. m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusDisconnect();
  1242. }
  1243. #if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  1244. TEST_F(AssetJobsFloodTest, DISABLED_ContainerLoadTest_DependencyLoadedAlready_SuccessAndSignal)
  1245. #else
  1246. TEST_F(AssetJobsFloodTest, ContainerLoadTest_DependencyLoadedAlready_SuccessAndSignal)
  1247. #endif // !AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  1248. {
  1249. m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusConnect();
  1250. // Setup has already created/destroyed assets
  1251. m_assetHandlerAndCatalog->m_numCreations = 0;
  1252. m_assetHandlerAndCatalog->m_numDestructions = 0;
  1253. m_assetHandlerAndCatalog->SetArtificialDelayMilliseconds(0, 20);
  1254. {
  1255. // Listen for MyAsset2Id
  1256. ContainerReadyListener readyListener(MyAsset2Id);
  1257. auto asset2PrimeGet = m_testAssetManager->GetAsset(MyAsset5Id, azrtti_typeid<AssetWithSerializedData>(), AZ::Data::AssetLoadBehavior::Default);
  1258. auto maxTimeout = AZStd::chrono::steady_clock::now() + DefaultTimeoutSeconds;
  1259. while (!asset2PrimeGet->IsReady())
  1260. {
  1261. m_testAssetManager->DispatchEvents();
  1262. if (AZStd::chrono::steady_clock::now() > maxTimeout)
  1263. {
  1264. break;
  1265. }
  1266. AZStd::this_thread::yield();
  1267. }
  1268. auto asset2 = m_testAssetManager->FindOrCreateAsset(MyAsset2Id, azrtti_typeid<AssetWithAssetReference>(), AZ::Data::AssetLoadBehavior::Default);
  1269. auto asset2Container = m_testAssetManager->GetAssetContainer(asset2);
  1270. maxTimeout = AZStd::chrono::steady_clock::now() + DefaultTimeoutSeconds;
  1271. while (!readyListener.m_ready)
  1272. {
  1273. m_testAssetManager->DispatchEvents();
  1274. if (AZStd::chrono::steady_clock::now() > maxTimeout)
  1275. {
  1276. break;
  1277. }
  1278. AZStd::this_thread::yield();
  1279. }
  1280. EXPECT_EQ(asset2Container->IsReady(), true);
  1281. EXPECT_EQ(asset2Container->GetDependencies().size(), 1);
  1282. EXPECT_TRUE(asset2Container->GetRootAsset().GetAs<AssetWithAssetReference>()->m_asset.IsReady());
  1283. EXPECT_EQ(readyListener.m_ready, 1);
  1284. asset2PrimeGet = { };
  1285. }
  1286. CheckFinishedCreationsAndDestructions();
  1287. m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusDisconnect();
  1288. }
  1289. #if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  1290. TEST_F(AssetJobsFloodTest, DISABLED_ContainerLoadTest_DependencyAndRootLoadedAlready_SuccessAndNoNewSignal)
  1291. #else
  1292. TEST_F(AssetJobsFloodTest, ContainerLoadTest_DependencyAndRootLoadedAlready_SuccessAndNoNewSignal)
  1293. #endif // !AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  1294. {
  1295. m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusConnect();
  1296. // Setup has already created/destroyed assets
  1297. m_assetHandlerAndCatalog->m_numCreations = 0;
  1298. m_assetHandlerAndCatalog->m_numDestructions = 0;
  1299. {
  1300. // Listen for MyAsset2Id
  1301. ContainerReadyListener readyListener(MyAsset2Id);
  1302. auto asset2Get = m_testAssetManager->GetAssetInternal(MyAsset2Id, azrtti_typeid<AssetWithAssetReference>(), AZ::Data::AssetLoadBehavior::Default, AssetLoadParameters{ &AssetFilterNoAssetLoading });
  1303. auto asset2PrimeGet = m_testAssetManager->GetAssetInternal(MyAsset5Id, azrtti_typeid<AssetWithSerializedData>(), AZ::Data::AssetLoadBehavior::Default, AssetLoadParameters{ &AssetFilterNoAssetLoading });
  1304. auto maxTimeout = AZStd::chrono::steady_clock::now() + DefaultTimeoutSeconds;
  1305. while (!asset2Get->IsReady() || !asset2PrimeGet->IsReady())
  1306. {
  1307. m_testAssetManager->DispatchEvents();
  1308. if (AZStd::chrono::steady_clock::now() > maxTimeout)
  1309. {
  1310. break;
  1311. }
  1312. AZStd::this_thread::yield();
  1313. }
  1314. auto asset2 = m_testAssetManager->FindOrCreateAsset(MyAsset2Id, azrtti_typeid<AssetWithAssetReference>(), AZ::Data::AssetLoadBehavior::Default);
  1315. auto asset2Container = m_testAssetManager->GetAssetContainer(asset2);
  1316. // Should not need to wait, everything should be ready
  1317. EXPECT_EQ(asset2Container->IsReady(), true);
  1318. EXPECT_EQ(asset2Container->GetDependencies().size(), 1);
  1319. // No signal should have been sent, it was already ready
  1320. EXPECT_EQ(readyListener.m_ready, 0);
  1321. // NotifyAssetReady events can still hold references
  1322. m_testAssetManager->DispatchEvents();
  1323. asset2Get = { };
  1324. asset2PrimeGet = { };
  1325. }
  1326. CheckFinishedCreationsAndDestructions();
  1327. m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusDisconnect();
  1328. }
  1329. #if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  1330. TEST_F(AssetJobsFloodTest, DISABLED_ContainerLoadTest_AssetWithQueueAndPreLoadReferencesTwoLevels_OnAssetReadyFollowsPreloads)
  1331. #else
  1332. TEST_F(AssetJobsFloodTest, ContainerLoadTest_AssetWithQueueAndPreLoadReferencesTwoLevels_OnAssetReadyFollowsPreloads)
  1333. #endif // !AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  1334. {
  1335. m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusConnect();
  1336. // Setup has already created/destroyed assets
  1337. m_assetHandlerAndCatalog->m_numCreations = 0;
  1338. m_assetHandlerAndCatalog->m_numDestructions = 0;
  1339. {
  1340. // Listen for PreloadAssetAId which is one half of the tree under PreloadAssetRootId
  1341. ContainerReadyListener readyListener(PreloadAssetAId);
  1342. OnAssetReadyListener preLoadAListener(PreloadAssetAId, azrtti_typeid<AssetWithQueueAndPreLoadReferences>());
  1343. OnAssetReadyListener preLoadBListener(PreloadAssetBId, azrtti_typeid<AssetWithQueueAndPreLoadReferences>());
  1344. OnAssetReadyListener queueLoadBListener(QueueLoadAssetBId, azrtti_typeid<AssetWithQueueAndPreLoadReferences>());
  1345. preLoadAListener.m_readyCheck = [&]([[maybe_unused]] const OnAssetReadyListener& thisListener)
  1346. {
  1347. return (preLoadBListener.m_ready > 0);
  1348. };
  1349. auto asset = m_testAssetManager->FindOrCreateAsset(PreloadAssetAId, azrtti_typeid<AssetWithQueueAndPreLoadReferences>(),
  1350. AZ::Data::AssetLoadBehavior::Default);
  1351. auto containerReady = m_testAssetManager->GetAssetContainer(asset);
  1352. auto maxTimeout = AZStd::chrono::steady_clock::now() + DefaultTimeoutSeconds;
  1353. while (!readyListener.m_ready)
  1354. {
  1355. m_testAssetManager->DispatchEvents();
  1356. if (AZStd::chrono::steady_clock::now() > maxTimeout)
  1357. {
  1358. break;
  1359. }
  1360. AZStd::this_thread::yield();
  1361. }
  1362. EXPECT_EQ(containerReady->IsReady(), true);
  1363. EXPECT_EQ(containerReady->GetDependencies().size(), 2);
  1364. EXPECT_EQ(preLoadAListener.m_ready, 1);
  1365. EXPECT_EQ(preLoadAListener.m_dataLoaded, 1);
  1366. EXPECT_EQ(preLoadBListener.m_ready, 1);
  1367. EXPECT_EQ(preLoadBListener.m_dataLoaded, 0);
  1368. EXPECT_EQ(queueLoadBListener.m_ready, 1);
  1369. EXPECT_EQ(queueLoadBListener.m_dataLoaded, 0);
  1370. }
  1371. CheckFinishedCreationsAndDestructions();
  1372. m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusDisconnect();
  1373. }
  1374. #if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  1375. TEST_F(AssetJobsFloodTest, DISABLED_ContainerLoadTest_AssetWithQueueAndPreLoadReferencesThreeLevels_OnAssetReadyFollowsPreloads)
  1376. #else
  1377. TEST_F(AssetJobsFloodTest, ContainerLoadTest_AssetWithQueueAndPreLoadReferencesThreeLevels_OnAssetReadyFollowsPreloads)
  1378. #endif // !AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  1379. {
  1380. m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusConnect();
  1381. // Setup has already created/destroyed assets
  1382. m_assetHandlerAndCatalog->m_numCreations = 0;
  1383. m_assetHandlerAndCatalog->m_numDestructions = 0;
  1384. {
  1385. // Listen for PreloadAssetRootId - Should listen for the entire tree. Preload dependencies should signal NotifyAssetReady in
  1386. // order where dependencies signal first and will signal before the entire container is ready. QueueLoads are independent
  1387. // And will be ready before the entire container is considered ready, but don't prevent individual assets from signalling ready
  1388. // Once all of their preloads (if Any) and themselves are ready
  1389. ContainerReadyListener readyListener(PreloadAssetRootId);
  1390. OnAssetReadyListener preLoadRootListener(PreloadAssetRootId, azrtti_typeid<AssetWithQueueAndPreLoadReferences>());
  1391. OnAssetReadyListener preLoadAListener(PreloadAssetAId, azrtti_typeid<AssetWithQueueAndPreLoadReferences>());
  1392. OnAssetReadyListener preLoadBListener(PreloadAssetBId, azrtti_typeid<AssetWithQueueAndPreLoadReferences>());
  1393. OnAssetReadyListener preLoadCListener(PreloadAssetCId, azrtti_typeid<AssetWithQueueAndPreLoadReferences>());
  1394. OnAssetReadyListener queueLoadAListener(QueueLoadAssetAId, azrtti_typeid<AssetWithQueueAndPreLoadReferences>());
  1395. OnAssetReadyListener queueLoadBListener(QueueLoadAssetBId, azrtti_typeid<AssetWithQueueAndPreLoadReferences>());
  1396. OnAssetReadyListener queueLoadCListener(QueueLoadAssetCId, azrtti_typeid<AssetWithQueueAndPreLoadReferences>());
  1397. preLoadRootListener.m_readyCheck = [&]([[maybe_unused]] const OnAssetReadyListener& thisListener)
  1398. {
  1399. return (preLoadAListener.m_ready && preLoadBListener.m_ready);
  1400. };
  1401. preLoadAListener.m_readyCheck = [&]([[maybe_unused]] const OnAssetReadyListener& thisListener)
  1402. {
  1403. return (preLoadBListener.m_ready > 0);
  1404. };
  1405. queueLoadAListener.m_readyCheck = [&]([[maybe_unused]] const OnAssetReadyListener& thisListener)
  1406. {
  1407. return (preLoadCListener.m_ready > 0);
  1408. };
  1409. auto asset = m_testAssetManager->FindOrCreateAsset(PreloadAssetRootId, azrtti_typeid<AssetWithQueueAndPreLoadReferences>(), AZ::Data::AssetLoadBehavior::Default);
  1410. auto containerReady = m_testAssetManager->GetAssetContainer(asset);
  1411. auto maxTimeout = AZStd::chrono::steady_clock::now() + DefaultTimeoutSeconds;
  1412. while (!readyListener.m_ready)
  1413. {
  1414. m_testAssetManager->DispatchEvents();
  1415. if (AZStd::chrono::steady_clock::now() > maxTimeout)
  1416. {
  1417. break;
  1418. }
  1419. AZStd::this_thread::yield();
  1420. }
  1421. EXPECT_EQ(containerReady->IsReady(), true);
  1422. EXPECT_EQ(containerReady->GetDependencies().size(), 6);
  1423. EXPECT_EQ(preLoadRootListener.m_ready, 1);
  1424. EXPECT_EQ(preLoadRootListener.m_dataLoaded, 1);
  1425. EXPECT_EQ(preLoadAListener.m_ready, 1);
  1426. EXPECT_EQ(preLoadAListener.m_dataLoaded, 1);
  1427. EXPECT_EQ(preLoadBListener.m_ready, 1);
  1428. EXPECT_EQ(preLoadBListener.m_dataLoaded, 0);
  1429. EXPECT_EQ(preLoadCListener.m_ready, 1);
  1430. EXPECT_EQ(preLoadCListener.m_dataLoaded, 0);
  1431. EXPECT_EQ(queueLoadAListener.m_ready, 1);
  1432. EXPECT_EQ(queueLoadAListener.m_dataLoaded, 1);
  1433. EXPECT_EQ(queueLoadBListener.m_ready, 1);
  1434. EXPECT_EQ(queueLoadBListener.m_dataLoaded, 0);
  1435. EXPECT_EQ(queueLoadCListener.m_ready, 1);
  1436. EXPECT_EQ(queueLoadCListener.m_dataLoaded, 0);
  1437. }
  1438. CheckFinishedCreationsAndDestructions();
  1439. m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusDisconnect();
  1440. }
  1441. // If our preload list contains assets we can't load we should catch the errors and load what we can
  1442. #if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  1443. TEST_F(AssetJobsFloodTest, DISABLED_ContainerLoadTest_RootHasBrokenPreloads_LoadsRoot)
  1444. #else
  1445. TEST_F(AssetJobsFloodTest, ContainerLoadTest_RootHasBrokenPreloads_LoadsRoot)
  1446. #endif // !AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  1447. {
  1448. m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusConnect();
  1449. // Setup has already created/destroyed assets
  1450. m_assetHandlerAndCatalog->m_numCreations = 0;
  1451. m_assetHandlerAndCatalog->m_numDestructions = 0;
  1452. {
  1453. ContainerReadyListener readyListener(PreloadBrokenDepBId);
  1454. OnAssetReadyListener preLoadBListener(PreloadBrokenDepBId, azrtti_typeid<AssetWithQueueAndPreLoadReferences>());
  1455. OnAssetReadyListener preLoadNoDataListener(PreloadAssetNoDataId, azrtti_typeid<AssetWithQueueAndPreLoadReferences>());
  1456. OnAssetReadyListener preLoadNoHandlerListener(PreloadAssetNoHandlerId, azrtti_typeid<EmptyAssetWithNoHandler>());
  1457. auto asset = m_testAssetManager->FindOrCreateAsset(PreloadBrokenDepBId, azrtti_typeid<AssetWithQueueAndPreLoadReferences>(), AZ::Data::AssetLoadBehavior::Default);
  1458. auto containerReady = m_testAssetManager->GetAssetContainer(asset);
  1459. auto maxTimeout = AZStd::chrono::steady_clock::now() + DefaultTimeoutSeconds;
  1460. while (!readyListener.m_ready)
  1461. {
  1462. m_testAssetManager->DispatchEvents();
  1463. if (AZStd::chrono::steady_clock::now() > maxTimeout)
  1464. {
  1465. break;
  1466. }
  1467. AZStd::this_thread::yield();
  1468. }
  1469. EXPECT_EQ(containerReady->IsReady(), true);
  1470. EXPECT_EQ(containerReady->GetDependencies().size(), 0);
  1471. EXPECT_EQ(containerReady->GetInvalidDependencies(), 2);
  1472. EXPECT_EQ(preLoadBListener.m_ready, 1);
  1473. // Had no valid dependencies so didn't need to do any preloading
  1474. EXPECT_EQ(preLoadBListener.m_dataLoaded, 0);
  1475. // None of this should have signalled
  1476. EXPECT_EQ(preLoadNoDataListener.m_ready, 0);
  1477. EXPECT_EQ(preLoadNoDataListener.m_dataLoaded, 0);
  1478. EXPECT_EQ(preLoadNoHandlerListener.m_ready, 0);
  1479. EXPECT_EQ(preLoadNoHandlerListener.m_dataLoaded, 0);
  1480. }
  1481. CheckFinishedCreationsAndDestructions();
  1482. m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusDisconnect();
  1483. }
  1484. // If our preload list contains assets we can't load we should catch the errors and load what we can
  1485. #if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  1486. TEST_F(AssetJobsFloodTest, DISABLED_ContainerLoadTest_ChildHasBrokenPreloads_LoadsRootAndChild)
  1487. #else
  1488. TEST_F(AssetJobsFloodTest, ContainerLoadTest_ChildHasBrokenPreloads_LoadsRootAndChild)
  1489. #endif // !AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  1490. {
  1491. m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusConnect();
  1492. // Setup has already created/destroyed assets
  1493. m_assetHandlerAndCatalog->m_numCreations = 0;
  1494. m_assetHandlerAndCatalog->m_numDestructions = 0;
  1495. {
  1496. ContainerReadyListener readyListener(PreloadBrokenDepAId);
  1497. OnAssetReadyListener preLoadAListener(PreloadBrokenDepAId, azrtti_typeid<AssetWithQueueAndPreLoadReferences>());
  1498. OnAssetReadyListener preLoadBListener(PreloadBrokenDepBId, azrtti_typeid<AssetWithQueueAndPreLoadReferences>());
  1499. OnAssetReadyListener preLoadNoDataListener(PreloadAssetNoDataId, azrtti_typeid<AssetWithQueueAndPreLoadReferences>());
  1500. OnAssetReadyListener preLoadNoHandlerListener(PreloadAssetNoHandlerId, azrtti_typeid<EmptyAssetWithNoHandler>());
  1501. auto asset = m_testAssetManager->FindOrCreateAsset(PreloadBrokenDepAId, azrtti_typeid<AssetWithQueueAndPreLoadReferences>(),
  1502. AZ::Data::AssetLoadBehavior::Default);
  1503. auto containerReady = m_testAssetManager->GetAssetContainer(asset);
  1504. auto maxTimeout = AZStd::chrono::steady_clock::now() + DefaultTimeoutSeconds;
  1505. while (!readyListener.m_ready)
  1506. {
  1507. m_testAssetManager->DispatchEvents();
  1508. if (AZStd::chrono::steady_clock::now() > maxTimeout)
  1509. {
  1510. break;
  1511. }
  1512. AZStd::this_thread::yield();
  1513. }
  1514. EXPECT_EQ(containerReady->IsReady(), true);
  1515. EXPECT_EQ(containerReady->GetDependencies().size(), 1);
  1516. EXPECT_EQ(containerReady->GetInvalidDependencies(), 2);
  1517. EXPECT_EQ(preLoadAListener.m_ready, 1);
  1518. EXPECT_EQ(preLoadAListener.m_dataLoaded, 1);
  1519. EXPECT_EQ(preLoadBListener.m_ready, 1);
  1520. // Had no valid dependencies so didn't need to do any preloading
  1521. EXPECT_EQ(preLoadBListener.m_dataLoaded, 0);
  1522. // None of this should have signalled
  1523. EXPECT_EQ(preLoadNoDataListener.m_ready, 0);
  1524. EXPECT_EQ(preLoadNoDataListener.m_dataLoaded, 0);
  1525. EXPECT_EQ(preLoadNoHandlerListener.m_ready, 0);
  1526. EXPECT_EQ(preLoadNoHandlerListener.m_dataLoaded, 0);
  1527. }
  1528. CheckFinishedCreationsAndDestructions();
  1529. m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusDisconnect();
  1530. }
  1531. // If our preload list contains assets we can't load we should catch the errors and load what we can
  1532. #if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  1533. TEST_F(AssetJobsFloodTest, DISABLED_ContainerLoadTest_SimpleCircularPreload_LoadsRoot)
  1534. #else
  1535. TEST_F(AssetJobsFloodTest, ContainerLoadTest_SimpleCircularPreload_LoadsRoot)
  1536. #endif // !AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  1537. {
  1538. m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusConnect();
  1539. // Setup has already created/destroyed assets
  1540. m_assetHandlerAndCatalog->m_numCreations = 0;
  1541. m_assetHandlerAndCatalog->m_numDestructions = 0;
  1542. {
  1543. ContainerReadyListener readyListener(CircularAId);
  1544. OnAssetReadyListener circularAListener(CircularAId, azrtti_typeid<AssetWithQueueAndPreLoadReferences>());
  1545. // This will attempt to load asset A that has a preload dependency on A.
  1546. AZ_TEST_START_TRACE_SUPPRESSION;
  1547. auto asset = m_testAssetManager->FindOrCreateAsset(CircularAId, azrtti_typeid<AssetWithQueueAndPreLoadReferences>(),
  1548. AZ::Data::AssetLoadBehavior::Default);
  1549. auto containerReady = m_testAssetManager->GetAssetContainer(asset);
  1550. // We should catch the basic ciruclar dependency error as well as that it's a preload
  1551. AZ_TEST_STOP_TRACE_SUPPRESSION(2);
  1552. auto maxTimeout = AZStd::chrono::steady_clock::now() + DefaultTimeoutSeconds;
  1553. while (!readyListener.m_ready)
  1554. {
  1555. m_testAssetManager->DispatchEvents();
  1556. if (AZStd::chrono::steady_clock::now() > maxTimeout)
  1557. {
  1558. break;
  1559. }
  1560. AZStd::this_thread::yield();
  1561. }
  1562. // Even though it's a circular reference, the container will be considered ready once A is loaded,
  1563. // and asset A will in fact be loaded.
  1564. EXPECT_EQ(containerReady->IsReady(), true);
  1565. EXPECT_EQ(containerReady->GetDependencies().size(), 0);
  1566. EXPECT_EQ(containerReady->GetInvalidDependencies(), 1);
  1567. EXPECT_EQ(circularAListener.m_ready, 1);
  1568. EXPECT_EQ(circularAListener.m_dataLoaded, 0);
  1569. // Break the circular reference so that the test can clean up correctly without leaking memory.
  1570. {
  1571. auto assetData = asset.GetAs<AssetWithQueueAndPreLoadReferences>();
  1572. assetData->m_preLoad.Reset();
  1573. }
  1574. }
  1575. CheckFinishedCreationsAndDestructions();
  1576. m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusDisconnect();
  1577. }
  1578. // If our preload list contains assets we can't load we should catch the errors and load what we can
  1579. #if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  1580. TEST_F(AssetJobsFloodTest, DISABLED_ContainerLoadTest_DoubleCircularPreload_LoadsOne)
  1581. #else
  1582. TEST_F(AssetJobsFloodTest, ContainerLoadTest_DoubleCircularPreload_LoadsOne)
  1583. #endif // !AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  1584. {
  1585. m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusConnect();
  1586. // Setup has already created/destroyed assets
  1587. m_assetHandlerAndCatalog->m_numCreations = 0;
  1588. m_assetHandlerAndCatalog->m_numDestructions = 0;
  1589. {
  1590. ContainerReadyListener readyListener(CircularBId);
  1591. OnAssetReadyListener circularBListener(CircularBId, azrtti_typeid<AssetWithQueueAndPreLoadReferences>());
  1592. OnAssetReadyListener circularCListener(CircularCId, azrtti_typeid<AssetWithQueueAndPreLoadReferences>());
  1593. // This will attempt to load asset B that has a preload dependency on C, and C has a preload dependency on B.
  1594. AZ_TEST_START_TRACE_SUPPRESSION;
  1595. auto asset = m_testAssetManager->FindOrCreateAsset(CircularBId, azrtti_typeid<AssetWithQueueAndPreLoadReferences>(),
  1596. AZ::Data::AssetLoadBehavior::Default);
  1597. auto containerReady = m_testAssetManager->GetAssetContainer(asset);
  1598. // We should catch the basic circular dependency error as well as that it's a preload
  1599. AZ_TEST_STOP_TRACE_SUPPRESSION(2);
  1600. auto maxTimeout = AZStd::chrono::steady_clock::now() + DefaultTimeoutSeconds;
  1601. while (!readyListener.m_ready)
  1602. {
  1603. m_testAssetManager->DispatchEvents();
  1604. if (AZStd::chrono::steady_clock::now() > maxTimeout)
  1605. {
  1606. break;
  1607. }
  1608. AZStd::this_thread::yield();
  1609. }
  1610. // Even though it's a circular reference, the container will be considered ready once B and C are loaded.
  1611. EXPECT_EQ(containerReady->IsReady(), true);
  1612. EXPECT_EQ(containerReady->GetDependencies().size(), 1);
  1613. // C's dependency back on B should have been ignored
  1614. EXPECT_EQ(containerReady->GetInvalidDependencies(), 1);
  1615. EXPECT_EQ(circularBListener.m_ready, 1);
  1616. EXPECT_EQ(circularBListener.m_dataLoaded, 1);
  1617. EXPECT_EQ(circularCListener.m_ready, 1);
  1618. // Circular C should be treated as a regular dependency, not a "signaling" one, so it shouldn't go through the dataLoaded path.
  1619. EXPECT_EQ(circularCListener.m_dataLoaded, 0);
  1620. // Break the circular reference so that the test can clean up correctly without leaking memory.
  1621. {
  1622. auto assetData = asset.GetAs<AssetWithQueueAndPreLoadReferences>();
  1623. assetData->m_preLoad.Reset();
  1624. }
  1625. }
  1626. CheckFinishedCreationsAndDestructions();
  1627. m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusDisconnect();
  1628. }
  1629. // There should be three errors for self referential and chained circular preload dependencies
  1630. // This container will detect these and still load, however if they were truly required to be
  1631. // PreLoaded there could still be issues at run time, as one will be ready before the other
  1632. #if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  1633. TEST_F(AssetJobsFloodTest, DISABLED_ContainerLoadTest_CircularPreLoadBelowRoot_LoadCompletes)
  1634. #else
  1635. TEST_F(AssetJobsFloodTest, ContainerLoadTest_CircularPreLoadBelowRoot_LoadCompletes)
  1636. #endif // !AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  1637. {
  1638. m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusConnect();
  1639. // Setup has already created/destroyed assets
  1640. m_assetHandlerAndCatalog->m_numCreations = 0;
  1641. m_assetHandlerAndCatalog->m_numDestructions = 0;
  1642. {
  1643. ContainerReadyListener readyListener(CircularDId);
  1644. OnAssetReadyListener circularDListener(CircularDId, azrtti_typeid<AssetWithQueueAndPreLoadReferences>());
  1645. OnAssetReadyListener circularBListener(CircularBId, azrtti_typeid<AssetWithQueueAndPreLoadReferences>());
  1646. OnAssetReadyListener circularCListener(CircularCId, azrtti_typeid<AssetWithQueueAndPreLoadReferences>());
  1647. // This will attempt to load asset D, which has a preload dependency on B.
  1648. // B has a preload dependency on C, and C has a preload dependency on B.
  1649. AZ_TEST_START_TRACE_SUPPRESSION;
  1650. auto asset = m_testAssetManager->FindOrCreateAsset(CircularDId, azrtti_typeid<AssetWithQueueAndPreLoadReferences>(), AZ::Data::AssetLoadBehavior::Default);
  1651. auto containerReady = m_testAssetManager->GetAssetContainer(asset);
  1652. // One error in SetupPreloads - Two of the assets create a dependency loop
  1653. AZ_TEST_STOP_TRACE_SUPPRESSION(1);
  1654. auto maxTimeout = AZStd::chrono::steady_clock::now() + DefaultTimeoutSeconds;
  1655. while (!readyListener.m_ready)
  1656. {
  1657. m_testAssetManager->DispatchEvents();
  1658. if (AZStd::chrono::steady_clock::now() > maxTimeout)
  1659. {
  1660. break;
  1661. }
  1662. AZStd::this_thread::yield();
  1663. }
  1664. EXPECT_EQ(containerReady->IsReady(), true);
  1665. EXPECT_EQ(containerReady->GetDependencies().size(), 2);
  1666. // C's dependency back on B should have been ignored
  1667. EXPECT_EQ(containerReady->GetInvalidDependencies(), 0);
  1668. EXPECT_EQ(circularDListener.m_ready, 1);
  1669. EXPECT_EQ(circularDListener.m_dataLoaded, 1);
  1670. EXPECT_EQ(circularBListener.m_ready, 1);
  1671. EXPECT_EQ(circularCListener.m_ready, 1);
  1672. // Break the circular reference so that the test can clean up correctly without leaking memory.
  1673. // The references are D -> B <-> C, so we clear B's reference to C to break the loop.
  1674. {
  1675. auto assetDataD = asset.GetAs<AssetWithQueueAndPreLoadReferences>();
  1676. auto assetDataB = assetDataD->m_preLoad.GetAs<AssetWithQueueAndPreLoadReferences>();
  1677. assetDataB->m_preLoad.Reset();
  1678. }
  1679. }
  1680. CheckFinishedCreationsAndDestructions();
  1681. m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusDisconnect();
  1682. }
  1683. /**
  1684. * Run multiple threads that get and release assets simultaneously to test AssetManager's thread safety
  1685. */
  1686. class AssetJobsMultithreadedTest
  1687. : public DisklessAssetManagerBase
  1688. {
  1689. public:
  1690. static inline constexpr AZ::Uuid MyAsset1Id{ "{5B29FE2B-6B41-48C9-826A-C723951B0560}" };
  1691. static inline constexpr AZ::Uuid MyAsset2Id{ "{BD354AE5-B5D5-402A-A12E-BE3C96F6522B}" };
  1692. static inline constexpr AZ::Uuid MyAsset3Id{ "{622C3FC9-5AE2-4E52-AFA2-5F7095ADAB53}" };
  1693. static inline constexpr AZ::Uuid MyAsset4Id{ "{EE99215B-7AB4-4757-B8AF-F78BD4903AC4}" };
  1694. static inline constexpr AZ::Uuid MyAsset5Id{ "{D9CDAB04-D206-431E-BDC0-1DD615D56197}" };
  1695. static inline constexpr AZ::Uuid MyAsset6Id{ "{B2F139C3-5032-4B52-ADCA-D52A8F88E043}" };
  1696. // Initialize the Job Manager with 2 threads for the Asset Manager to use.
  1697. size_t GetNumJobManagerThreads() const override { return 2; }
  1698. static constexpr inline AZ::Uuid MyAssetAId{ "{C5B08D5D-8589-4706-A53F-96248CFDCE73}" };
  1699. static constexpr inline AZ::Uuid MyAssetBId{ "{E1DECFB8-6FAC-4FE4-BD54-3A4A4E6616A5}" };
  1700. static constexpr inline AZ::Uuid MyAssetCId{ "{F7091500-A220-4407-BEF4-B658D8D24289}" };
  1701. static constexpr inline AZ::Uuid MyAssetDId{ "{1BB6CA5B-CE56-497B-B721-9460365E1125}" };
  1702. void SetupAssets(DataDrivenHandlerAndCatalog* catalog)
  1703. {
  1704. catalog->AddAsset<AssetWithAssetReference>(MyAsset1Id, "TestAsset1.txt")->AddPreload(MyAsset4Id);
  1705. catalog->AddAsset<AssetWithAssetReference>(MyAsset2Id, "TestAsset2.txt")->AddPreload(MyAsset5Id);
  1706. catalog->AddAsset<AssetWithAssetReference>(MyAsset3Id, "TestAsset3.txt")->AddPreload(MyAsset6Id);
  1707. catalog->AddAsset<AssetWithSerializedData>(MyAsset4Id, "TestAsset4.txt");
  1708. catalog->AddAsset<AssetWithSerializedData>(MyAsset5Id, "TestAsset5.txt");
  1709. catalog->AddAsset<AssetWithSerializedData>(MyAsset6Id, "TestAsset6.txt");
  1710. catalog->AddAsset<AssetWithAssetReference>(MyAssetAId, "TestAsset1.txt")->AddPreload(MyAssetBId);
  1711. catalog->AddAsset<AssetWithAssetReference>(MyAssetBId, "TestAsset2.txt")->AddPreload(MyAssetCId);
  1712. catalog->AddAsset<AssetWithAssetReference>(MyAssetCId, "TestAsset3.txt")->AddPreload(MyAssetDId);
  1713. catalog->AddAsset<AssetWithSerializedData>(MyAssetDId, "TestAsset4.txt");
  1714. }
  1715. void ParallelCreateAndDestroy()
  1716. {
  1717. SerializeContext context;
  1718. AssetWithSerializedData::Reflect(context);
  1719. AssetWithAssetReference::Reflect(context);
  1720. AssetManager::Descriptor desc;
  1721. AssetManager::Create(desc);
  1722. auto& db = AssetManager::Instance();
  1723. auto* assetHandlerAndCatalog = aznew DataDrivenHandlerAndCatalog;
  1724. assetHandlerAndCatalog->m_context = &context;
  1725. SetupAssets(assetHandlerAndCatalog);
  1726. AZStd::vector<AssetType> types;
  1727. assetHandlerAndCatalog->GetHandledAssetTypes(types);
  1728. for (const auto& type : types)
  1729. {
  1730. db.RegisterHandler(assetHandlerAndCatalog, type);
  1731. db.RegisterCatalog(assetHandlerAndCatalog, type);
  1732. }
  1733. {
  1734. AssetWithSerializedData ap1;
  1735. AssetWithSerializedData ap2;
  1736. AssetWithSerializedData ap3;
  1737. EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset4.txt", &ap1, &context));
  1738. EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset5.txt", &ap2, &context));
  1739. EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset6.txt", &ap3, &context));
  1740. AssetWithAssetReference assetWithPreload1;
  1741. AssetWithAssetReference assetWithPreload2;
  1742. AssetWithAssetReference assetWithPreload3;
  1743. assetWithPreload1.m_asset = AssetManager::Instance().CreateAsset<AssetWithSerializedData>(MyAsset4Id, AssetLoadBehavior::PreLoad);
  1744. assetWithPreload2.m_asset = AssetManager::Instance().CreateAsset<AssetWithSerializedData>(MyAsset5Id, AssetLoadBehavior::PreLoad);
  1745. assetWithPreload3.m_asset = AssetManager::Instance().CreateAsset<AssetWithSerializedData>(MyAsset6Id, AssetLoadBehavior::PreLoad);
  1746. EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset1.txt", &assetWithPreload1, &context));
  1747. EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset2.txt", &assetWithPreload2, &context));
  1748. EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset3.txt", &assetWithPreload3, &context));
  1749. EXPECT_TRUE(assetHandlerAndCatalog->m_numCreations == 3);
  1750. assetHandlerAndCatalog->m_numCreations = 0;
  1751. }
  1752. auto assetUuids = {
  1753. MyAsset1Id,
  1754. MyAsset2Id,
  1755. MyAsset3Id,
  1756. };
  1757. AZStd::vector<AZStd::thread> threads;
  1758. AZStd::mutex mutex;
  1759. AZStd::atomic<int> threadCount((int)assetUuids.size());
  1760. AZStd::condition_variable cv;
  1761. AZStd::atomic_bool keepDispatching(true);
  1762. auto dispatch = [&keepDispatching]()
  1763. {
  1764. while (keepDispatching)
  1765. {
  1766. AssetManager::Instance().DispatchEvents();
  1767. }
  1768. };
  1769. AZStd::thread dispatchThread(dispatch);
  1770. for (const auto& assetUuid : assetUuids)
  1771. {
  1772. threads.emplace_back([&db, &threadCount, &cv, assetUuid]()
  1773. {
  1774. for (int i = 0; i < 1000; i++)
  1775. {
  1776. Asset<AssetWithAssetReference> asset1 = db.GetAsset(assetUuid, azrtti_typeid<AssetWithAssetReference>(), AZ::Data::AssetLoadBehavior::PreLoad);
  1777. asset1.BlockUntilLoadComplete();
  1778. EXPECT_TRUE(asset1.IsReady());
  1779. Asset<AssetWithSerializedData> dependentAsset = asset1->m_asset;
  1780. EXPECT_TRUE(dependentAsset.IsReady());
  1781. // There should be at least 1 ref here in this scope
  1782. EXPECT_GE(asset1->GetUseCount(), 1);
  1783. asset1 = Asset<AssetData>();
  1784. }
  1785. threadCount--;
  1786. cv.notify_one();
  1787. });
  1788. }
  1789. bool timedOut = false;
  1790. // Used to detect a deadlock. If we wait for more than 5 seconds, it's likely a deadlock has occurred
  1791. while (threadCount > 0 && !timedOut)
  1792. {
  1793. AZStd::unique_lock<AZStd::mutex> lock(mutex);
  1794. timedOut = (AZStd::cv_status::timeout == cv.wait_until(lock, AZStd::chrono::steady_clock::now() + DefaultTimeoutSeconds *2));
  1795. }
  1796. EXPECT_EQ(threadCount, 0);
  1797. for (auto& thread : threads)
  1798. {
  1799. thread.join();
  1800. }
  1801. keepDispatching = false;
  1802. dispatchThread.join();
  1803. AssetManager::Destroy();
  1804. }
  1805. void ParallelCyclicAssetReferences()
  1806. {
  1807. SerializeContext context;
  1808. AssetWithAssetReference::Reflect(context);
  1809. AssetManager::Descriptor desc;
  1810. AssetManager::Create(desc);
  1811. auto& db = AssetManager::Instance();
  1812. auto* assetHandlerAndCatalog = aznew DataDrivenHandlerAndCatalog;
  1813. assetHandlerAndCatalog->m_context = &context;
  1814. SetupAssets(assetHandlerAndCatalog);
  1815. AZStd::vector<AssetType> types;
  1816. assetHandlerAndCatalog->GetHandledAssetTypes(types);
  1817. for (const auto& type : types)
  1818. {
  1819. db.RegisterHandler(assetHandlerAndCatalog, type);
  1820. db.RegisterCatalog(assetHandlerAndCatalog, type);
  1821. }
  1822. {
  1823. // A will be saved to disk with MyAsset1Id
  1824. AssetWithAssetReference a;
  1825. a.m_asset = AssetManager::Instance().CreateAsset<AssetWithSerializedData>(MyAsset2Id);
  1826. EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset1.txt", &a, &context));
  1827. AssetWithAssetReference b;
  1828. b.m_asset = AssetManager::Instance().CreateAsset<AssetWithSerializedData>(MyAsset3Id);
  1829. EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset2.txt", &b, &context));
  1830. AssetWithAssetReference c;
  1831. c.m_asset = AssetManager::Instance().CreateAsset<AssetWithSerializedData>(MyAsset4Id);
  1832. EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset3.txt", &c, &context));
  1833. AssetWithAssetReference d;
  1834. d.m_asset = AssetManager::Instance().CreateAsset<AssetWithSerializedData>(MyAsset5Id);
  1835. EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset4.txt", &d, &context));
  1836. AssetWithAssetReference e;
  1837. e.m_asset = AssetManager::Instance().CreateAsset<AssetWithSerializedData>(MyAsset6Id);
  1838. EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset5.txt", &e, &context));
  1839. AssetWithAssetReference f;
  1840. f.m_asset = AssetManager::Instance().CreateAsset<AssetWithSerializedData>(MyAsset1Id); // refer back to asset1
  1841. EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset6.txt", &f, &context));
  1842. EXPECT_TRUE(assetHandlerAndCatalog->m_numCreations == 6);
  1843. assetHandlerAndCatalog->m_numCreations = 0;
  1844. }
  1845. const size_t numThreads = 4;
  1846. AZStd::atomic_int threadCount(numThreads);
  1847. AZStd::condition_variable cv;
  1848. AZStd::vector<AZStd::thread> threads;
  1849. AZStd::atomic_bool keepDispatching(true);
  1850. auto dispatch = [&keepDispatching]()
  1851. {
  1852. while (keepDispatching)
  1853. {
  1854. AssetManager::Instance().DispatchEvents();
  1855. }
  1856. };
  1857. AZStd::thread dispatchThread(dispatch);
  1858. for (size_t threadIdx = 0; threadIdx < numThreads; ++threadIdx)
  1859. {
  1860. threads.emplace_back([&threadCount, &db, &cv]()
  1861. {
  1862. Data::Asset<AssetWithAssetReference> assetA = db.GetAsset<AssetWithAssetReference>(
  1863. MyAsset1Id, AZ::Data::AssetLoadBehavior::Default);
  1864. while (assetA.IsLoading())
  1865. {
  1866. AZStd::this_thread::yield();
  1867. }
  1868. EXPECT_TRUE(assetA.IsReady());
  1869. Data::Asset<AssetWithAssetReference> assetB = assetA->m_asset;
  1870. EXPECT_TRUE(assetB.IsReady());
  1871. Data::Asset<AssetWithAssetReference> assetC = assetB->m_asset;
  1872. EXPECT_TRUE(assetC.IsReady());
  1873. Data::Asset<AssetWithAssetReference> assetD = assetC->m_asset;
  1874. EXPECT_TRUE(assetD.IsReady());
  1875. Data::Asset<AssetWithAssetReference> assetE = assetD->m_asset;
  1876. EXPECT_TRUE(assetE.IsReady());
  1877. assetA = Data::Asset<AssetWithAssetReference>();
  1878. --threadCount;
  1879. cv.notify_one();
  1880. });
  1881. }
  1882. // Used to detect a deadlock. If we wait for more than 5 seconds, it's likely a deadlock has occurred
  1883. bool timedOut = false;
  1884. AZStd::mutex mutex;
  1885. while (threadCount > 0 && !timedOut)
  1886. {
  1887. AZStd::unique_lock<AZStd::mutex> lock(mutex);
  1888. timedOut = (AZStd::cv_status::timeout == cv.wait_until(lock, AZStd::chrono::steady_clock::now() + DefaultTimeoutSeconds));
  1889. }
  1890. EXPECT_TRUE(threadCount == 0);
  1891. for (auto& thread : threads)
  1892. {
  1893. thread.join();
  1894. }
  1895. keepDispatching = false;
  1896. dispatchThread.join();
  1897. AssetManager::Destroy();
  1898. }
  1899. struct MockAssetContainer : AssetContainer
  1900. {
  1901. MockAssetContainer(Asset<AssetData> assetData, const AssetLoadParameters& loadParams, bool isReload)
  1902. {
  1903. // Copying the code in the original constructor, we can't call that constructor because it will not invoke our virtual method
  1904. m_rootAsset = AssetInternal::WeakAsset<AssetData>(assetData);
  1905. m_containerAssetId = m_rootAsset.GetId();
  1906. m_isReload = isReload;
  1907. AddDependentAssets(assetData, loadParams);
  1908. }
  1909. protected:
  1910. AZStd::vector<AZStd::pair<AssetInfo, Asset<AssetData>>> CreateAndQueueDependentAssets(
  1911. const AZStd::vector<AssetInfo>& dependencyInfoList, const AssetLoadParameters& loadParamsCopyWithNoLoadingFilter) override
  1912. {
  1913. auto result = AssetContainer::CreateAndQueueDependentAssets(dependencyInfoList, loadParamsCopyWithNoLoadingFilter);
  1914. // Sleep for a long enough time to allow asset loads to complete and start triggering AssetReady events
  1915. // This forces the race condition to occur
  1916. AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(500));
  1917. return result;
  1918. }
  1919. };
  1920. struct MockAssetManager : AssetManager
  1921. {
  1922. explicit MockAssetManager(const Descriptor& desc)
  1923. : AssetManager(desc)
  1924. {
  1925. }
  1926. protected:
  1927. AZStd::shared_ptr<AssetContainer> CreateAssetContainer(Asset<AssetData> asset, const AssetLoadParameters& loadParams, bool isReload) const override
  1928. {
  1929. return AZStd::shared_ptr<AssetContainer>(aznew MockAssetContainer(asset, loadParams, isReload));
  1930. }
  1931. };
  1932. void ParallelDeepAssetReferences()
  1933. {
  1934. SerializeContext context;
  1935. AssetWithSerializedData::Reflect(context);
  1936. AssetWithAssetReference::Reflect(context);
  1937. AssetManager::Descriptor desc;
  1938. AssetManager::SetInstance(aznew MockAssetManager(desc));
  1939. auto& db = AssetManager::Instance();
  1940. auto* assetHandlerAndCatalog = aznew DataDrivenHandlerAndCatalog;
  1941. assetHandlerAndCatalog->m_context = &context;
  1942. SetupAssets(assetHandlerAndCatalog);
  1943. AZStd::vector<AssetType> types;
  1944. assetHandlerAndCatalog->GetHandledAssetTypes(types);
  1945. for (const auto& type : types)
  1946. {
  1947. db.RegisterHandler(assetHandlerAndCatalog, type);
  1948. db.RegisterCatalog(assetHandlerAndCatalog, type);
  1949. }
  1950. {
  1951. // AssetD is MYASSETD
  1952. AssetWithSerializedData d;
  1953. d.m_data = 42;
  1954. EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset4.txt", &d, &context));
  1955. // AssetC is MYASSETC
  1956. AssetWithAssetReference c;
  1957. c.m_asset = db.CreateAsset<AssetWithSerializedData>(AssetId(MyAssetDId)); // point at D
  1958. EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset3.txt", &c, &context));
  1959. // AssetB is MYASSETB
  1960. AssetWithAssetReference b;
  1961. b.m_asset = db.CreateAsset<AssetWithAssetReference>(AssetId(MyAssetCId)); // point at C
  1962. EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset2.txt", &b, &context));
  1963. // AssetA will be written to disk as MYASSETA
  1964. AssetWithAssetReference a;
  1965. a.m_asset = db.CreateAsset<AssetWithAssetReference>(AssetId(MyAssetBId)); // point at B
  1966. EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset1.txt", &a, &context));
  1967. }
  1968. constexpr size_t NumThreads = 4;
  1969. AZStd::atomic_int threadCount(NumThreads);
  1970. AZStd::condition_variable cv;
  1971. AZStd::vector<AZStd::thread> threads;
  1972. AZStd::atomic_bool keepDispatching(true);
  1973. auto dispatch = [&keepDispatching]()
  1974. {
  1975. while (keepDispatching)
  1976. {
  1977. AssetManager::Instance().DispatchEvents();
  1978. }
  1979. };
  1980. AZStd::thread dispatchThread(dispatch);
  1981. for (size_t threadIdx = 0; threadIdx < NumThreads; ++threadIdx)
  1982. {
  1983. threads.emplace_back([&threadCount, &db, &cv]()
  1984. {
  1985. Data::Asset<AssetWithAssetReference> assetA = db.GetAsset<AssetWithAssetReference>(AssetId(MyAssetAId), AZ::Data::AssetLoadBehavior::Default);
  1986. assetA.BlockUntilLoadComplete();
  1987. EXPECT_TRUE(assetA.IsReady());
  1988. Data::Asset<AssetWithAssetReference> assetB = assetA->m_asset;
  1989. EXPECT_TRUE(assetB.IsReady());
  1990. Data::Asset<AssetWithAssetReference> assetC = assetB->m_asset;
  1991. EXPECT_TRUE(assetC.IsReady());
  1992. Data::Asset<AssetWithSerializedData> assetD = assetC->m_asset;
  1993. EXPECT_TRUE(assetD.IsReady());
  1994. EXPECT_EQ(42, assetD->m_data);
  1995. assetA = Data::Asset<AssetWithAssetReference>();
  1996. --threadCount;
  1997. cv.notify_one();
  1998. });
  1999. }
  2000. // Used to detect a deadlock. If we wait for more than 5 seconds, it's likely a deadlock has occurred
  2001. bool timedOut = false;
  2002. AZStd::mutex mutex;
  2003. while (threadCount > 0 && !timedOut)
  2004. {
  2005. AZStd::unique_lock<AZStd::mutex> lock(mutex);
  2006. timedOut = (AZStd::cv_status::timeout == cv.wait_until(lock, AZStd::chrono::steady_clock::now() + DefaultTimeoutSeconds));
  2007. }
  2008. EXPECT_TRUE(threadCount == 0);
  2009. for (auto& thread : threads)
  2010. {
  2011. thread.join();
  2012. }
  2013. keepDispatching = false;
  2014. dispatchThread.join();
  2015. AssetManager::Destroy();
  2016. }
  2017. void ParallelGetAndReleaseAsset()
  2018. {
  2019. SerializeContext context;
  2020. AssetWithSerializedData::Reflect(context);
  2021. const size_t numThreads = 4;
  2022. AssetManager::Descriptor desc;
  2023. AssetManager::Create(desc);
  2024. auto& db = AssetManager::Instance();
  2025. auto* assetHandlerAndCatalog = aznew DataDrivenHandlerAndCatalog;
  2026. assetHandlerAndCatalog->m_context = &context;
  2027. SetupAssets(assetHandlerAndCatalog);
  2028. AZStd::vector<AssetType> types;
  2029. assetHandlerAndCatalog->GetHandledAssetTypes(types);
  2030. for (const auto& type : types)
  2031. {
  2032. db.RegisterHandler(assetHandlerAndCatalog, type);
  2033. db.RegisterCatalog(assetHandlerAndCatalog, type);
  2034. }
  2035. AZStd::vector<AZ::Uuid> assetUuids = { MyAsset1Id, MyAsset2Id };
  2036. AZStd::vector<AZStd::thread> threads;
  2037. AZStd::atomic<int> threadCount(numThreads);
  2038. AZStd::atomic_bool keepDispatching(true);
  2039. auto dispatch = [&keepDispatching]()
  2040. {
  2041. while (keepDispatching)
  2042. {
  2043. AssetManager::Instance().DispatchEvents();
  2044. }
  2045. };
  2046. AZStd::atomic_bool wait(true);
  2047. AZStd::atomic_bool keepRunning(true);
  2048. AZStd::atomic<int> threadsRunning(0);
  2049. AZStd::atomic<int> dummy(0);
  2050. AZStd::thread dispatchThread(dispatch);
  2051. auto getAssetFunc = [&db, &threadCount, assetUuids, &wait, &threadsRunning, &dummy, &keepRunning](int index)
  2052. {
  2053. threadsRunning++;
  2054. while (wait)
  2055. {
  2056. AZStd::this_thread::yield();
  2057. }
  2058. while (keepRunning)
  2059. {
  2060. for (int innerIdx = index * 7; innerIdx > 0; innerIdx--)
  2061. {
  2062. // this inner loop is just to burn some time which will be different
  2063. // per thread. Adding a dummy statement to ensure that the compiler does not optimize this loop.
  2064. dummy = innerIdx;
  2065. }
  2066. int assetIndex = (int)(index % assetUuids.size());
  2067. Data::Asset<AssetWithSerializedData> asset1 = db.FindOrCreateAsset(assetUuids[assetIndex], azrtti_typeid<AssetWithSerializedData>(), AZ::Data::AssetLoadBehavior::Default);
  2068. // There should be at least 1 ref here in this scope
  2069. EXPECT_GE(asset1.Get()->GetUseCount(), 1);
  2070. };
  2071. threadCount--;
  2072. };
  2073. for (int idx = 0; idx < numThreads; idx++)
  2074. {
  2075. threads.emplace_back(AZStd::bind(getAssetFunc, idx));
  2076. }
  2077. while (threadsRunning < numThreads)
  2078. {
  2079. AZStd::this_thread::yield();
  2080. }
  2081. // We have ensured that all the threads have started at this point and we can let them start hammering at the AssetManager
  2082. wait = false;
  2083. AZStd::chrono::steady_clock::time_point start = AZStd::chrono::steady_clock::now();
  2084. while (AZStd::chrono::duration_cast<AZStd::chrono::milliseconds>(AZStd::chrono::steady_clock::now() - start) < AZStd::chrono::seconds(2))
  2085. {
  2086. AZStd::this_thread::yield();
  2087. }
  2088. keepRunning = false;
  2089. for (auto& thread : threads)
  2090. {
  2091. thread.join();
  2092. }
  2093. EXPECT_EQ(threadCount, 0);
  2094. // Make sure asset jobs have finished before validating the number of destroyed assets, because it's possible that the asset job
  2095. // still contains a reference on the job thread that won't trigger the asset destruction until the asset job is destroyed.
  2096. BlockUntilAssetJobsAreComplete();
  2097. EXPECT_EQ(assetHandlerAndCatalog->m_numCreations, assetHandlerAndCatalog->m_numDestructions);
  2098. EXPECT_FALSE(db.FindAsset(assetUuids[0], AZ::Data::AssetLoadBehavior::Default));
  2099. EXPECT_FALSE(db.FindAsset(assetUuids[1], AZ::Data::AssetLoadBehavior::Default));
  2100. keepDispatching = false;
  2101. dispatchThread.join();
  2102. AssetManager::Destroy();
  2103. }
  2104. };
  2105. #if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS || AZ_TRAIT_DISABLE_ASSET_JOB_PARALLEL_TESTS
  2106. TEST_F(AssetJobsMultithreadedTest, DISABLED_ParallelCreateAndDestroy)
  2107. #else
  2108. TEST_F(AssetJobsMultithreadedTest, ParallelCreateAndDestroy)
  2109. #endif // AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  2110. {
  2111. ParallelCreateAndDestroy();
  2112. }
  2113. #if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS || AZ_TRAIT_DISABLE_ASSET_JOB_PARALLEL_TESTS
  2114. TEST_F(AssetJobsMultithreadedTest, DISABLED_ParallelGetAndReleaseAsset)
  2115. #else
  2116. TEST_F(AssetJobsMultithreadedTest, ParallelGetAndReleaseAsset)
  2117. #endif // AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  2118. {
  2119. ParallelGetAndReleaseAsset();
  2120. }
  2121. // This is disabled because cyclic references + pre load is not supported currently, but should be
  2122. TEST_F(AssetJobsMultithreadedTest, DISABLED_ParallelCyclicAssetReferences)
  2123. {
  2124. ParallelCyclicAssetReferences();
  2125. }
  2126. #if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  2127. TEST_F(AssetJobsMultithreadedTest, DISABLED_ParallelDeepAssetReferences)
  2128. #else
  2129. TEST_F(AssetJobsMultithreadedTest, ParallelDeepAssetReferences)
  2130. #endif // AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  2131. {
  2132. ParallelDeepAssetReferences();
  2133. }
  2134. class AssetManagerTests
  2135. : public DisklessAssetManagerBase
  2136. {
  2137. protected:
  2138. static inline const AZ::Uuid MyAsset1Id{ "{5B29FE2B-6B41-48C9-826A-C723951B0560}" };
  2139. static inline const AZ::Uuid MyAsset2Id{ "{BD354AE5-B5D5-402A-A12E-BE3C96F6522B}" };
  2140. static inline const AZ::Uuid MyAsset3Id{ "{622C3FC9-5AE2-4E52-AFA2-5F7095ADAB53}" };
  2141. DataDrivenHandlerAndCatalog* m_assetHandlerAndCatalog;
  2142. AZStd::unique_ptr<AZ::Console> m_console;
  2143. // Initialize the Job Manager with 2 threads for the Asset Manager to use.
  2144. size_t GetNumJobManagerThreads() const override { return 2; }
  2145. void SetUp() override
  2146. {
  2147. DisklessAssetManagerBase::SetUp();
  2148. m_console = AZStd::make_unique<AZ::Console>();
  2149. AZ::Interface<AZ::IConsole>::Register(m_console.get());
  2150. m_console->LinkDeferredFunctors(AZ::ConsoleFunctorBase::GetDeferredHead());
  2151. // create the database
  2152. AssetManager::Descriptor desc;
  2153. AssetManager::Create(desc);
  2154. // create and register an asset handler
  2155. m_assetHandlerAndCatalog = aznew DataDrivenHandlerAndCatalog;
  2156. m_assetHandlerAndCatalog->m_context = m_serializeContext;
  2157. m_assetHandlerAndCatalog->SetArtificialDelayMilliseconds(10, 10);
  2158. AssetWithCustomData::Reflect(*m_serializeContext);
  2159. m_assetHandlerAndCatalog->AddAsset<AssetWithCustomData>(MyAsset1Id, "MyAsset1.txt");
  2160. m_assetHandlerAndCatalog->AddAsset<AssetWithCustomData>(MyAsset2Id, "MyAsset2.txt");
  2161. m_assetHandlerAndCatalog->AddAsset<AssetWithCustomData>(MyAsset3Id, "MyAsset3.txt");
  2162. AZStd::vector<AssetType> types;
  2163. m_assetHandlerAndCatalog->GetHandledAssetTypes(types);
  2164. for (const auto& type : types)
  2165. {
  2166. AssetManager::Instance().RegisterHandler(m_assetHandlerAndCatalog, type);
  2167. AssetManager::Instance().RegisterCatalog(m_assetHandlerAndCatalog, type);
  2168. }
  2169. WriteAssetToDisk("MyAsset1.txt", MyAsset1Id.ToString<AZStd::string>().c_str());
  2170. WriteAssetToDisk("MyAsset2.txt", MyAsset2Id.ToString<AZStd::string>().c_str());
  2171. WriteAssetToDisk("MyAsset3.txt", MyAsset3Id.ToString<AZStd::string>().c_str());
  2172. }
  2173. void TearDown() override
  2174. {
  2175. AssetManager::Destroy();
  2176. AZ::Interface<AZ::IConsole>::Unregister(m_console.get());
  2177. m_console = nullptr;
  2178. DisklessAssetManagerBase::TearDown();
  2179. }
  2180. };
  2181. #if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  2182. TEST_F(AssetManagerTests, DISABLED_BlockUntilLoadComplete_Queued_BlocksUntilLoaded)
  2183. #else
  2184. TEST_F(AssetManagerTests, BlockUntilLoadComplete_Queued_BlocksUntilLoaded)
  2185. #endif // AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  2186. {
  2187. m_assetHandlerAndCatalog->SetArtificialDelayMilliseconds(0, 500);
  2188. {
  2189. auto asset = AssetManager::Instance().GetAsset<AssetWithCustomData>(MyAsset1Id, AZ::Data::AssetLoadBehavior::Default);
  2190. EXPECT_FALSE(asset.IsError());
  2191. EXPECT_FALSE(asset.IsReady());
  2192. asset.BlockUntilLoadComplete();
  2193. EXPECT_TRUE(asset.IsReady());
  2194. }
  2195. }
  2196. #if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  2197. TEST_F(AssetManagerTests, DISABLED_BlockUntilLoadComplete_AlreadyLoaded_ContinuesImmediately)
  2198. #else
  2199. TEST_F(AssetManagerTests, BlockUntilLoadComplete_AlreadyLoaded_ContinuesImmediately)
  2200. #endif // AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  2201. {
  2202. m_assetHandlerAndCatalog->SetArtificialDelayMilliseconds(0, 0);
  2203. {
  2204. auto asset = AssetManager::Instance().GetAsset<AssetWithCustomData>(MyAsset1Id, AZ::Data::AssetLoadBehavior::Default);
  2205. asset.BlockUntilLoadComplete();
  2206. EXPECT_FALSE(asset.IsError());
  2207. EXPECT_TRUE(asset.IsReady());
  2208. asset.BlockUntilLoadComplete();
  2209. EXPECT_TRUE(asset.IsReady());
  2210. }
  2211. }
  2212. TEST_F(AssetManagerTests, BlockUntilLoadComplete_LoadFailure_ThreadContinuesAfterFailure)
  2213. {
  2214. m_assetHandlerAndCatalog->SetArtificialDelayMilliseconds(0, 50);
  2215. m_assetHandlerAndCatalog->m_failLoad = true;
  2216. {
  2217. auto asset = AssetManager::Instance().GetAsset<AssetWithCustomData>(MyAsset1Id, AZ::Data::AssetLoadBehavior::Default);
  2218. EXPECT_FALSE(asset.IsError());
  2219. EXPECT_FALSE(asset.IsReady());
  2220. asset.BlockUntilLoadComplete();
  2221. EXPECT_FALSE(asset.IsReady());
  2222. EXPECT_TRUE(asset.IsError());
  2223. }
  2224. }
  2225. TEST_F(AssetManagerTests, BlockUntilLoadComplete_NotQueued_Fails)
  2226. {
  2227. m_assetHandlerAndCatalog->SetArtificialDelayMilliseconds(0, 500);
  2228. {
  2229. auto asset = AssetManager::Instance().CreateAsset<AssetWithCustomData>(MyAsset1Id, AZ::Data::AssetLoadBehavior::Default);
  2230. EXPECT_FALSE(asset.IsError());
  2231. EXPECT_FALSE(asset.IsReady());
  2232. AZ_TEST_START_TRACE_SUPPRESSION;
  2233. asset.BlockUntilLoadComplete();
  2234. AZ_TEST_STOP_TRACE_SUPPRESSION(1);
  2235. EXPECT_FALSE(asset.IsReady());
  2236. }
  2237. }
  2238. TEST_F(AssetManagerTests, FindOrCreateAsset)
  2239. {
  2240. m_assetHandlerAndCatalog->SetArtificialDelayMilliseconds(0, 0);
  2241. m_assetHandlerAndCatalog->m_numCreations = 0;
  2242. {
  2243. {
  2244. // First call should create
  2245. auto asset = AssetManager::Instance().FindOrCreateAsset<AssetWithCustomData>(MyAsset1Id, AZ::Data::AssetLoadBehavior::Default);
  2246. ASSERT_TRUE(asset);
  2247. EXPECT_FALSE(asset.IsError());
  2248. EXPECT_FALSE(asset.IsReady());
  2249. EXPECT_EQ(m_assetHandlerAndCatalog->m_numCreations, 1);
  2250. // Second call should result in a find
  2251. asset = AssetManager::Instance().FindOrCreateAsset<AssetWithCustomData>(MyAsset1Id, AZ::Data::AssetLoadBehavior::Default);
  2252. ASSERT_TRUE(asset);
  2253. EXPECT_EQ(m_assetHandlerAndCatalog->m_numCreations, 1); // Should be the same as before
  2254. }
  2255. {
  2256. // Test that we create another asset after all references are deleted
  2257. auto asset = AssetManager::Instance().FindOrCreateAsset<AssetWithCustomData>(MyAsset1Id, AZ::Data::AssetLoadBehavior::Default);
  2258. ASSERT_TRUE(asset);
  2259. EXPECT_FALSE(asset.IsError());
  2260. EXPECT_FALSE(asset.IsReady());
  2261. EXPECT_EQ(m_assetHandlerAndCatalog->m_numCreations, 2);
  2262. }
  2263. }
  2264. }
  2265. struct CancelListener
  2266. : AssetBus::Handler
  2267. {
  2268. CancelListener(const AZ::Data::AssetId& assetId)
  2269. {
  2270. BusConnect(assetId);
  2271. }
  2272. void OnAssetCanceled([[maybe_unused]] AssetId assetId) override
  2273. {
  2274. m_canceled = true;
  2275. }
  2276. ~CancelListener() override
  2277. {
  2278. BusDisconnect();
  2279. }
  2280. AZStd::atomic_bool m_canceled{ false };
  2281. };
  2282. using AssetManagerCancelTests = AssetManagerTests;
  2283. #if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  2284. TEST_F(AssetManagerCancelTests, DISABLED_CancelLoad_NoReferences_LoadCancels)
  2285. #else
  2286. // Asset cancellation is temporarily disabled, re-enable this test when cancellation is more stable. LYN-3263
  2287. TEST_F(AssetManagerCancelTests, DISABLED_CancelLoad_NoReferences_LoadCancels)
  2288. #endif // AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  2289. {
  2290. m_assetHandlerAndCatalog->SetArtificialDelayMilliseconds(0, 100);
  2291. m_assetHandlerAndCatalog->m_numLoads = 0;
  2292. m_assetHandlerAndCatalog->m_numDestructions = 0;
  2293. {
  2294. CancelListener listener(MyAsset3Id);
  2295. auto asset1 = AssetManager::Instance().GetAsset<AssetWithCustomData>(MyAsset1Id, AZ::Data::AssetLoadBehavior::Default); // These are not flagged as blocking loads because doing so moves the work off the job thread, which we need to keep busy
  2296. auto asset2 = AssetManager::Instance().GetAsset<AssetWithCustomData>(MyAsset2Id, AZ::Data::AssetLoadBehavior::Default);
  2297. ASSERT_TRUE(asset1);
  2298. ASSERT_TRUE(asset2);
  2299. {
  2300. auto asset3 = AssetManager::Instance().GetAsset<AssetWithCustomData>(MyAsset3Id, AZ::Data::AssetLoadBehavior::Default);
  2301. ASSERT_TRUE(asset3);
  2302. }
  2303. asset1.BlockUntilLoadComplete();
  2304. asset2.BlockUntilLoadComplete();
  2305. EXPECT_TRUE(asset1.IsReady());
  2306. EXPECT_TRUE(asset2.IsReady());
  2307. AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(1));
  2308. AssetManager::Instance().DispatchEvents();
  2309. EXPECT_EQ(m_assetHandlerAndCatalog->m_numLoads, 2);
  2310. EXPECT_EQ(m_assetHandlerAndCatalog->m_numDestructions, 1);
  2311. EXPECT_TRUE(listener.m_canceled);
  2312. }
  2313. }
  2314. #if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  2315. TEST_F(AssetManagerCancelTests, DISABLED_CanceledLoad_CanBeLoadedAgainLater)
  2316. #else
  2317. // Asset cancellation is temporarily disabled, re-enable this test when cancellation is more stable. LYN-3263
  2318. TEST_F(AssetManagerCancelTests, DISABLED_CanceledLoad_CanBeLoadedAgainLater)
  2319. #endif // AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  2320. {
  2321. m_assetHandlerAndCatalog->SetArtificialDelayMilliseconds(0, 50);
  2322. m_assetHandlerAndCatalog->m_numLoads = 0;
  2323. m_assetHandlerAndCatalog->m_numDestructions = 0;
  2324. {
  2325. auto asset1 = AssetManager::Instance().GetAsset<AssetWithCustomData>(MyAsset1Id, AZ::Data::AssetLoadBehavior::Default);
  2326. auto asset2 = AssetManager::Instance().GetAsset<AssetWithCustomData>(MyAsset2Id, AZ::Data::AssetLoadBehavior::Default);
  2327. ASSERT_TRUE(asset1);
  2328. ASSERT_TRUE(asset2);
  2329. {
  2330. auto asset3 = AssetManager::Instance().GetAsset<AssetWithCustomData>(MyAsset3Id, AZ::Data::AssetLoadBehavior::Default);
  2331. ASSERT_TRUE(asset3);
  2332. }
  2333. asset1.BlockUntilLoadComplete();
  2334. asset2.BlockUntilLoadComplete();
  2335. EXPECT_TRUE(asset1.IsReady());
  2336. EXPECT_TRUE(asset2.IsReady());
  2337. AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(1));
  2338. AssetManager::Instance().DispatchEvents();
  2339. EXPECT_EQ(m_assetHandlerAndCatalog->m_numLoads, 2);
  2340. EXPECT_EQ(m_assetHandlerAndCatalog->m_numDestructions, 1);
  2341. {
  2342. auto asset3 = AssetManager::Instance().GetAsset<AssetWithCustomData>(MyAsset3Id, AZ::Data::AssetLoadBehavior::Default);
  2343. ASSERT_TRUE(asset3);
  2344. EXPECT_FALSE(asset3.IsReady());
  2345. asset3.BlockUntilLoadComplete();
  2346. EXPECT_TRUE(asset3.IsReady());
  2347. }
  2348. }
  2349. }
  2350. #if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  2351. TEST_F(AssetManagerCancelTests, DISABLED_CancelLoad_InProgressLoad_Continues)
  2352. #else
  2353. // Asset cancellation is temporarily disabled, re-enable this test when cancellation is more stable. LYN-3263
  2354. TEST_F(AssetManagerCancelTests, DISABLED_CancelLoad_InProgressLoad_Continues)
  2355. #endif // AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  2356. {
  2357. m_assetHandlerAndCatalog->SetArtificialDelayMilliseconds(0, 100);
  2358. m_assetHandlerAndCatalog->m_numLoads = 0;
  2359. m_assetHandlerAndCatalog->m_numDestructions = 0;
  2360. {
  2361. {
  2362. auto asset1 = AssetManager::Instance().GetAsset<AssetWithCustomData>(MyAsset1Id, AZ::Data::AssetLoadBehavior::Default);
  2363. ASSERT_TRUE(asset1);
  2364. AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(1));
  2365. }
  2366. AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(1));
  2367. AssetManager::Instance().DispatchEvents();
  2368. EXPECT_EQ(m_assetHandlerAndCatalog->m_numLoads, 1);
  2369. EXPECT_EQ(m_assetHandlerAndCatalog->m_numDestructions, 1);
  2370. }
  2371. }
  2372. struct AssetManagerLoadWarningTests
  2373. : AssetManagerTests
  2374. , public AZ::Debug::TraceMessageBus::Handler
  2375. {
  2376. public:
  2377. void SetUp() override
  2378. {
  2379. AssetManagerTests::SetUp();
  2380. AZ::Debug::TraceMessageBus::Handler::BusConnect();
  2381. }
  2382. void TearDown() override
  2383. {
  2384. AZ::Debug::TraceMessageBus::Handler::BusDisconnect();
  2385. AssetManagerTests::TearDown();
  2386. }
  2387. // Helper method to set the AssetManager console variable that controls the warning threshold.
  2388. // Whenever the threshold is exceeded, a warning will be printed.
  2389. void SetLoadWarningMilliseconds(uint32_t milliseconds)
  2390. {
  2391. AZ::IConsole* console = AZ::Interface<AZ::IConsole>::Get();
  2392. ASSERT_TRUE(console);
  2393. bool warningEnable = false;
  2394. console->PerformCommand("cl_assetLoadWarningEnable true");
  2395. EXPECT_EQ(console->GetCvarValue("cl_assetLoadWarningEnable", warningEnable), GetValueResult::Success);
  2396. EXPECT_TRUE(warningEnable);
  2397. uint32_t thresholdMs = 0;
  2398. console->PerformCommand(AZStd::string::format("cl_assetLoadWarningMsThreshold %u", milliseconds).c_str());
  2399. EXPECT_EQ(console->GetCvarValue("cl_assetLoadWarningMsThreshold", thresholdMs), GetValueResult::Success);
  2400. EXPECT_EQ(thresholdMs, milliseconds);
  2401. }
  2402. // Track the number of warnings emitted during the test.
  2403. bool OnWarning([[maybe_unused]] const char* window, [[maybe_unused]] const char* message) override
  2404. {
  2405. m_numWarnings++;
  2406. return false;
  2407. }
  2408. int m_numWarnings = 0;
  2409. };
  2410. #if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  2411. TEST_F(AssetManagerLoadWarningTests, DISABLED_AssetManager_LoadAssetThresholdWarning_TriggersWhenThresholdExceeded)
  2412. #else
  2413. TEST_F(AssetManagerLoadWarningTests, AssetManager_LoadAssetThresholdWarning_TriggersWhenThresholdExceeded)
  2414. #endif // AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  2415. {
  2416. constexpr uint32_t loadWarningThresholdMs = 50;
  2417. constexpr uint32_t loadMs = 100;
  2418. SetLoadWarningMilliseconds(loadWarningThresholdMs);
  2419. m_assetHandlerAndCatalog->SetArtificialDelayMilliseconds(0, loadMs);
  2420. {
  2421. auto asset = AssetManager::Instance().GetAsset<AssetWithCustomData>(MyAsset1Id, AZ::Data::AssetLoadBehavior::Default);
  2422. asset.BlockUntilLoadComplete();
  2423. EXPECT_TRUE(asset.IsReady());
  2424. }
  2425. // If warnings are enabled, we should get a notification that we've gone beyond the load time threshold.
  2426. #ifdef AZ_ENABLE_TRACING
  2427. EXPECT_EQ(1, m_numWarnings);
  2428. #else
  2429. EXPECT_EQ(0, m_numWarnings);
  2430. #endif
  2431. AssetManager::Destroy();
  2432. }
  2433. #if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  2434. TEST_F(AssetManagerLoadWarningTests, DISABLED_AssetManager_LoadAssetThresholdWarning_DoesNotTriggersWhenAtOrBelowThreshold)
  2435. #else
  2436. TEST_F(AssetManagerLoadWarningTests, AssetManager_LoadAssetThresholdWarning_DoesNotTriggersWhenAtOrBelowThreshold)
  2437. #endif // AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  2438. {
  2439. constexpr uint32_t loadWarningThresholdMs = 100;
  2440. constexpr uint32_t loadMs = 10;
  2441. SetLoadWarningMilliseconds(loadWarningThresholdMs);
  2442. m_assetHandlerAndCatalog->SetArtificialDelayMilliseconds(0, loadMs);
  2443. {
  2444. auto asset = AssetManager::Instance().GetAsset<AssetWithCustomData>(MyAsset1Id, AZ::Data::AssetLoadBehavior::Default);
  2445. asset.BlockUntilLoadComplete();
  2446. EXPECT_TRUE(asset.IsReady());
  2447. }
  2448. // We should NOT get a notification that we've gone beyond the load time threshold.
  2449. EXPECT_EQ(0, m_numWarnings);
  2450. AssetManager::Destroy();
  2451. }
  2452. /**
  2453. * This class sets up the data and parameters needed for testing various scenarios in which asset references get cleared while in
  2454. * the middle of loading. The tests help ensure that assets can't get stuck in perpetual loading states.
  2455. **/
  2456. class AssetManagerClearAssetReferenceTests
  2457. : public DisklessAssetManagerBase
  2458. {
  2459. protected:
  2460. static inline const AZ::Uuid RootAssetId{ "{AB13F568-C676-41FE-A7E9-341F71A78104}" };
  2461. static inline const AZ::Uuid DependentPreloadAssetId{ "{9E0AE541-0080-4ADA-B4F4-A0F25D0A6D1A}" };
  2462. static inline const AZ::Uuid NestedDependentPreloadBlockingAssetId{ "{FED70BCD-2846-4CBA-84D1-ED24DA7FCD4B}" };
  2463. static inline const AZ::Uuid RootWithSynchronizerAssetId{ "{6470ED30-D530-4023-9DEB-AC1E40062A2B}" };
  2464. DataDrivenHandlerAndCatalog* m_assetHandlerAndCatalog;
  2465. LoadAssetDataSynchronizer m_loadDataSynchronizer;
  2466. // Initialize the Job Manager with 1 thread for the Asset Manager to use.
  2467. // This is necessary for these tests to help ensure that we can control the exact loading state more deterministically.
  2468. size_t GetNumJobManagerThreads() const override { return 1; }
  2469. AssetManagerClearAssetReferenceTests() = default;
  2470. ~AssetManagerClearAssetReferenceTests() override = default;
  2471. void CreateAsset(AZ::Uuid assetId, const char* filename, LoadAssetDataSynchronizer* synchronizer = nullptr)
  2472. {
  2473. m_assetHandlerAndCatalog->AddAsset<AssetWithSerializedData>(assetId, filename, 0, false, false, synchronizer);
  2474. AssetWithSerializedData asset;
  2475. EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile(filename, &asset, m_serializeContext));
  2476. }
  2477. template<typename T>
  2478. void CreateAssetRef(AZ::Uuid assetId, const char* filename, AZ::Uuid referencedAssetId, LoadAssetDataSynchronizer* synchronizer = nullptr)
  2479. {
  2480. m_assetHandlerAndCatalog->AddAsset<AssetWithAssetReference>(assetId, filename, 0, false, false, synchronizer)->AddPreload(referencedAssetId);
  2481. AssetWithAssetReference asset;
  2482. asset.m_asset = AssetManager::Instance().FindOrCreateAsset(referencedAssetId, azrtti_typeid<T>(), PreLoad);
  2483. EXPECT_TRUE(asset.m_asset.GetData());
  2484. EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile(filename, &asset, m_serializeContext));
  2485. }
  2486. virtual void SetUpAssetManager()
  2487. {
  2488. AssetManager::Descriptor desc;
  2489. AssetManager::Create(desc);
  2490. }
  2491. void SetUp() override
  2492. {
  2493. DisklessAssetManagerBase::SetUp();
  2494. // create the database
  2495. SetUpAssetManager();
  2496. // create and register an asset handler
  2497. m_assetHandlerAndCatalog = aznew DataDrivenHandlerAndCatalog;
  2498. m_assetHandlerAndCatalog->m_context = m_serializeContext;
  2499. AssetWithCustomData::Reflect(*m_serializeContext);
  2500. AssetWithSerializedData::Reflect(*m_serializeContext);
  2501. AssetWithAssetReference::Reflect(*m_serializeContext);
  2502. for (auto&& type : {
  2503. azrtti_typeid<AssetWithCustomData>(),
  2504. azrtti_typeid<AssetWithSerializedData>(),
  2505. azrtti_typeid<AssetWithAssetReference>(),
  2506. })
  2507. {
  2508. AssetManager::Instance().RegisterHandler(m_assetHandlerAndCatalog, type);
  2509. AssetManager::Instance().RegisterCatalog(m_assetHandlerAndCatalog, type);
  2510. }
  2511. // For our tests, we will set up a chain of assets that look like this:
  2512. // RootAssetId -(Preload)-> DependentPreloadAssetId -(Preload)-> NestedDependentPreloadBlockingAssetId
  2513. // The asset at the end of the chain uses a loadDataSynchronizer to ensure that we can synchronize logic perfectly
  2514. // with the exact moment that it is in the middle of the "Loading" phase.
  2515. // These tests validate behaviors when the root asset is released while a dependent asset is in the middle of loading,
  2516. // so getting the timing correct is mandatory for these tests.
  2517. CreateAsset(NestedDependentPreloadBlockingAssetId, "DependentPreloadBlockingAsset.txt", &m_loadDataSynchronizer);
  2518. CreateAssetRef<AssetWithSerializedData>(DependentPreloadAssetId, "DependentPreloadAsset.txt", NestedDependentPreloadBlockingAssetId);
  2519. CreateAssetRef<AssetWithAssetReference>(RootAssetId, "RootAsset.txt", DependentPreloadAssetId);
  2520. CreateAssetRef<AssetWithAssetReference>(
  2521. RootWithSynchronizerAssetId,
  2522. "RootWithSynchronizerAsset.txt",
  2523. NestedDependentPreloadBlockingAssetId,
  2524. &m_loadDataSynchronizer);
  2525. }
  2526. void TearDown() override
  2527. {
  2528. // Manually release the handler. By doing this, we're also implicitly validating that no assets have
  2529. // remained in a loading state at the point of test teardown.
  2530. AssetManager::Instance().UnregisterHandler(m_assetHandlerAndCatalog);
  2531. AssetManager::Instance().UnregisterCatalog(m_assetHandlerAndCatalog);
  2532. delete m_assetHandlerAndCatalog;
  2533. AssetManager::Destroy();
  2534. DisklessAssetManagerBase::TearDown();
  2535. }
  2536. };
  2537. // Verify that within a SuspendAssetRelease / ResumeAssetRelease block, if an asset is in the Loading state and all references to it
  2538. // are removed, and then a new reference is requested, that the asset will finish loading successfully. The specific error case
  2539. // being checked is as follows:
  2540. // - A 0-refcounted asset reference could cause the surrounding asset container to be destroyed
  2541. // - The asset itself is never freed because of the SuspendAssetRelease call
  2542. // - A new asset reference is requested
  2543. // - The asset could now be in a perpetual loading state because the asset exists in a Loading state, but there is no top-level
  2544. // asset container to ever transition the asset to a Ready state.
  2545. #if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  2546. TEST_F(AssetManagerClearAssetReferenceTests,
  2547. DISABLED_ContainerLoadTest_AssetLosesAndGainsReferencesDuringLoadAndSuspendedRelease_AssetSuccessfullyFinishesLoading)
  2548. #else
  2549. // Asset cancellation is temporarily disabled, re-enable this test when cancellation is more stable. LYN-3263
  2550. TEST_F(AssetManagerClearAssetReferenceTests,
  2551. DISABLED_ContainerLoadTest_AssetLosesAndGainsReferencesDuringLoadAndSuspendedRelease_AssetSuccessfullyFinishesLoading)
  2552. #endif // AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  2553. {
  2554. // Start the load and wait for the dependent asset to hit the loading state.
  2555. auto rootAsset = AssetManager::Instance().GetAsset<AssetWithAssetReference>(RootAssetId, AssetLoadBehavior::Default);
  2556. while (m_loadDataSynchronizer.m_numBlocking < 1)
  2557. {
  2558. AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(1));
  2559. }
  2560. // Verify that the loading state on the nested dependent asset has been reached.
  2561. // (We use FindAsset instead of GetAsset to ensure that we don't create any additional asset containers or trigger any loads)
  2562. auto nestedDependentAsset = AssetManager::Instance().FindAsset<AssetWithSerializedData>(
  2563. NestedDependentPreloadBlockingAssetId, AssetLoadBehavior::Default);
  2564. EXPECT_EQ(nestedDependentAsset.GetStatus(), AssetData::AssetStatus::Loading);
  2565. // Verify that the root asset isn't loading yet, and so we have the only strong reference to the asset.
  2566. EXPECT_TRUE((rootAsset.GetStatus() == AssetData::AssetStatus::Queued) || (rootAsset.GetStatus() == AssetData::AssetStatus::Loading) || rootAsset.GetStatus() == AssetData::AssetStatus::StreamReady);
  2567. EXPECT_EQ(rootAsset->GetUseCount(), 1);
  2568. // Suspend asset releases so that 0-refcounted assets aren't destroyed.
  2569. AssetManager::Instance().SuspendAssetRelease();
  2570. // Release the reference to the asset. Normally, this would destroy the asset, but because we called SuspendAssetRelease the
  2571. // reference will stay around internally within the Asset Manager. This is normally used for cases where we expect the asset
  2572. // will nearly immediately get a new reference.
  2573. rootAsset.Reset();
  2574. // Get a new reference to the asset, and verify that it is still in a queued or loading state.
  2575. rootAsset = AssetManager::Instance().GetAsset<AssetWithAssetReference>(RootAssetId, AssetLoadBehavior::Default);
  2576. EXPECT_TRUE((rootAsset.GetStatus() == AssetData::AssetStatus::Queued) || (rootAsset.GetStatus() == AssetData::AssetStatus::Loading) || rootAsset.GetStatus() == AssetData::AssetStatus::StreamReady);
  2577. // Allow 0-refcounted assets to be destroyed again.
  2578. AssetManager::Instance().ResumeAssetRelease();
  2579. // Now that we've removed and regained a reference to the asset while in the loading state, allow the asset to continue loading.
  2580. m_loadDataSynchronizer.m_readyToLoad = true;
  2581. m_loadDataSynchronizer.m_condition.notify_all();
  2582. AssetManager::Instance().DispatchEvents();
  2583. // If the test works, the load will complete and the asset will successfully load.
  2584. // If the test fails, the asset will be in a perpetual loading state and this will deadlock, with the loading threads sitting idle.
  2585. rootAsset.BlockUntilLoadComplete();
  2586. EXPECT_TRUE(rootAsset.IsReady());
  2587. // Run one final DispatchEvents to clear out the event queue.
  2588. AssetManager::Instance().DispatchEvents();
  2589. }
  2590. // Verify that if the root asset in an asset container no longer has any references, and the asset container is still
  2591. // in the middle of loading, then the asset container will finish loading before destroying itself. The specific bug
  2592. // case to watch for is that any dependent asset loads that are triggered won't transition to a ready state until the
  2593. // entire container is ready, so deleting the container mid-load would cause those dependent assets to get stuck in a
  2594. // perpetual loading state.
  2595. #if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  2596. TEST_F(AssetManagerClearAssetReferenceTests, DISABLED_ContainerLoadTest_RootAssetDestroyedWhileContainerLoading_ContainerFinishesLoad)
  2597. #else
  2598. // Asset cancellation is temporarily disabled, re-enable this test when cancellation is more stable. LYN-3263
  2599. TEST_F(AssetManagerClearAssetReferenceTests, DISABLED_ContainerLoadTest_RootAssetDestroyedWhileContainerLoading_ContainerFinishesLoad)
  2600. #endif // AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  2601. {
  2602. OnAssetReadyListener assetStatus1(DependentPreloadAssetId, azrtti_typeid<AssetWithAssetReference>());
  2603. OnAssetReadyListener assetStatus2(NestedDependentPreloadBlockingAssetId, azrtti_typeid<AssetWithSerializedData>());
  2604. // Start the load and wait for the dependent asset to hit the loading state.
  2605. auto rootAsset = AssetManager::Instance().GetAsset<AssetWithAssetReference>(RootAssetId, AssetLoadBehavior::Default);
  2606. while (m_loadDataSynchronizer.m_numBlocking < 1)
  2607. {
  2608. AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(1));
  2609. }
  2610. // Get references to the dependent and nested dependent assets
  2611. auto dependentAsset = AssetManager::Instance().FindAsset<AssetWithAssetReference>(
  2612. DependentPreloadAssetId, AssetLoadBehavior::Default);
  2613. auto nestedDependentAsset = AssetManager::Instance().FindAsset<AssetWithSerializedData>(
  2614. NestedDependentPreloadBlockingAssetId, AssetLoadBehavior::Default);
  2615. // Verify that the loading state on the nested dependent asset has been reached.
  2616. EXPECT_EQ(nestedDependentAsset.GetStatus(), AssetData::AssetStatus::Loading);
  2617. // Verify that the root asset isn't loading yet, and so we have only one strong reference to the asset, owned by this method.
  2618. EXPECT_TRUE((rootAsset.GetStatus() == AssetData::AssetStatus::Queued) || (rootAsset.GetStatus() == AssetData::AssetStatus::Loading) || rootAsset.GetStatus() == AssetData::AssetStatus::StreamReady);
  2619. EXPECT_EQ(rootAsset->GetUseCount(), 1);
  2620. // Release the reference to the asset. This should destroy the asset. However, because the container for the asset is still in
  2621. // a loading state, it needs to remain around until it finishes or else the dependent assets that are mid-load will get stuck in a
  2622. // perpetual loading state.
  2623. rootAsset.Reset();
  2624. // Now that we've destroyed the root asset, allow the nested dependent asset to continue loading.
  2625. m_loadDataSynchronizer.m_readyToLoad = true;
  2626. m_loadDataSynchronizer.m_condition.notify_all();
  2627. AssetManager::Instance().DispatchEvents();
  2628. // If the test works, the loads will complete and the dependent assets will successfully load.
  2629. // We specifically wait for OnAssetReady to be triggered instead of using BlockUntilLoadComplete() to ensure that
  2630. // all loading jobs have 100% completed and there aren't any other outstanding asset references held on other threads.
  2631. const auto timeoutSeconds = AZStd::chrono::seconds(20);
  2632. auto maxTimeout = AZStd::chrono::steady_clock::now() + timeoutSeconds;
  2633. bool timedOut = false;
  2634. while (!(assetStatus1.m_ready && assetStatus2.m_ready))
  2635. {
  2636. AssetManager::Instance().DispatchEvents();
  2637. if (AZStd::chrono::steady_clock::now() > maxTimeout)
  2638. {
  2639. timedOut = true;
  2640. break;
  2641. }
  2642. }
  2643. EXPECT_FALSE(timedOut);
  2644. EXPECT_TRUE(dependentAsset.IsReady());
  2645. EXPECT_TRUE(nestedDependentAsset.IsReady());
  2646. }
  2647. TEST_F(AssetManagerClearAssetReferenceTests, ReloadTest_SUITE_sandbox)
  2648. {
  2649. // Regression test - there was a bug where rapid reloads could get stuck due to the owning container being invalidated
  2650. // Note that for this bug to occur, the loaded asset needs to have dependencies
  2651. // Order of events to reproduce:
  2652. // 1) Asset is loaded
  2653. // 2) Reload occurs
  2654. // 3) Another reload begins
  2655. // 4) Old asset is reassigned
  2656. // 4a) Old asset ref count hits 0
  2657. // 4b) Old asset triggers OnAssetUnused
  2658. // 4c) Container is released <-- This is where the bug happens, which should not occur with the fix
  2659. // 5) Reload stalls because container is gone
  2660. AZStd::atomic_bool running = true;
  2661. using SignalType = AZStd::unique_ptr<AZStd::binary_semaphore>;
  2662. // Start up a thread to dispatch queued events in the background
  2663. AZStd::thread eventThread(
  2664. [&running]()
  2665. {
  2666. while (running)
  2667. {
  2668. AssetManager::Instance().DispatchEvents();
  2669. }
  2670. });
  2671. struct ReloadHandler : AZ::Data::AssetBus::Handler
  2672. {
  2673. ReloadHandler(SignalType& reloadSignal, AZ::Data::Asset<AZ::Data::AssetData> asset)
  2674. : m_asset(asset), m_reloadSignal(reloadSignal)
  2675. {
  2676. BusConnect(asset.GetId());
  2677. }
  2678. ~ReloadHandler()
  2679. {
  2680. BusDisconnect();
  2681. }
  2682. void OnAssetReloaded(Asset<AssetData> asset) override
  2683. {
  2684. m_reloadedAsset = asset;
  2685. m_reloadSignal->release(); // Signal the reload has finished
  2686. }
  2687. AZ::Data::Asset<AZ::Data::AssetData> m_reloadedAsset;
  2688. AZ::Data::Asset<AZ::Data::AssetData> m_asset;
  2689. SignalType& m_reloadSignal;
  2690. };
  2691. SignalType reloadSignal = AZStd::make_unique<AZStd::binary_semaphore>();
  2692. {
  2693. // Intentional scope to allow asset references to be released before shutdown
  2694. // 1) Load the asset
  2695. ReloadHandler reloadHandler(
  2696. reloadSignal,
  2697. AssetManager::Instance().GetAsset<AssetWithAssetReference>(RootWithSynchronizerAssetId, AssetLoadBehavior::Default));
  2698. m_loadDataSynchronizer.m_readyToLoad = true;
  2699. m_loadDataSynchronizer.m_condition.notify_all();
  2700. ColoredPrintf(COLOR_DEFAULT, "Waiting for initial asset load to complete\n");
  2701. reloadHandler.m_asset.BlockUntilLoadComplete();
  2702. ASSERT_TRUE(reloadHandler.m_asset.IsReady());
  2703. ColoredPrintf(COLOR_DEFAULT, "Starting reload of asset\n");
  2704. // 2) Start a reload which will complete
  2705. AssetManager::Instance().ReloadAsset(RootWithSynchronizerAssetId, AssetLoadBehavior::Default);
  2706. ColoredPrintf(COLOR_DEFAULT, "Waiting for reload to complete\n");
  2707. reloadSignal->acquire(); // Wait until reload is done
  2708. ColoredPrintf(COLOR_DEFAULT, "Starting another reload of asset\n");
  2709. // 3) Start another reload
  2710. m_loadDataSynchronizer.m_readyToLoad = false; // Prevent the reload from progressing too far
  2711. AssetManager::Instance().ReloadAsset(RootWithSynchronizerAssetId, AssetLoadBehavior::Default); // Start another reload
  2712. // 4) Reassign the asset, which should cause the old asset to be unloaded
  2713. reloadHandler.m_asset = reloadHandler.m_reloadedAsset;
  2714. // Resume loading
  2715. m_loadDataSynchronizer.m_readyToLoad = true;
  2716. m_loadDataSynchronizer.m_condition.notify_all();
  2717. ColoredPrintf(COLOR_DEFAULT, "Waiting for 2nd reload to complete\n");
  2718. // 5) If the bug is still active, this will fail because the asset can never finish loading
  2719. EXPECT_TRUE(reloadSignal->try_acquire_for(AZStd::chrono::seconds(5)));
  2720. ColoredPrintf(COLOR_DEFAULT, "Test conditions complete, beginning shutdown\n");
  2721. }
  2722. // Shut down the event thread
  2723. running = false;
  2724. if (eventThread.joinable())
  2725. {
  2726. eventThread.join();
  2727. }
  2728. // Make sure any pending events are flushed out (to clear any remaining references)
  2729. AssetManager::Instance().DispatchEvents();
  2730. }
  2731. TEST_F(AssetManagerClearAssetReferenceTests, ReleaseOldReferenceWhileLoadingNewReference_DoesNotDeleteContainer_SUITE_sandbox)
  2732. {
  2733. // Regression test very similar to the above test but this time it occurs when releasing an old asset reference while *loading* (not reloading) the same asset
  2734. // Order of events to reproduce:
  2735. // 1) Asset is loaded
  2736. // 2) Asset is reloaded
  2737. // Reference to old asset is kept, newly loaded reference is released
  2738. // 3) Asset load is started again
  2739. // 4) Reference to old asset is released
  2740. // 4a) Old asset ref count hits 0
  2741. // 4b) Old asset triggers OnAssetUnused
  2742. // 4c) Container is released <-- This is where the bug happens, which should not occur with the fix
  2743. // 5) Load stalls because container is gone
  2744. AZStd::atomic_bool running = true;
  2745. using SignalType = AZStd::unique_ptr<AZStd::binary_semaphore>;
  2746. // Start up a thread to dispatch queued events in the background
  2747. AZStd::thread eventThread(
  2748. [&running]()
  2749. {
  2750. while (running)
  2751. {
  2752. AssetManager::Instance().DispatchEvents();
  2753. }
  2754. });
  2755. struct AssetEventHandler : AZ::Data::AssetBus::Handler
  2756. {
  2757. AssetEventHandler(SignalType& loadSignal, SignalType& unloadSignal, AZ::Data::Asset<AZ::Data::AssetData> asset)
  2758. : m_asset(asset)
  2759. , m_loadSignal(loadSignal)
  2760. , m_unloadSignal(unloadSignal)
  2761. {
  2762. BusConnect(asset.GetId());
  2763. }
  2764. ~AssetEventHandler() override
  2765. {
  2766. BusDisconnect();
  2767. }
  2768. void OnAssetReady([[maybe_unused]] Asset<AssetData> asset) override
  2769. {
  2770. m_loadSignal->release();
  2771. }
  2772. void OnAssetUnloaded([[maybe_unused]] const AssetId assetId, [[maybe_unused]] const AssetType assetType) override
  2773. {
  2774. m_unloadSignal->release();
  2775. }
  2776. AZ::Data::Asset<AZ::Data::AssetData> m_reloadedAsset;
  2777. AZ::Data::Asset<AZ::Data::AssetData> m_asset;
  2778. SignalType& m_loadSignal;
  2779. SignalType& m_unloadSignal;
  2780. };
  2781. SignalType loadSignal = AZStd::make_unique<AZStd::binary_semaphore>();
  2782. SignalType unloadSignal = AZStd::make_unique<AZStd::binary_semaphore>();
  2783. {
  2784. // Intentional scope to allow asset references to be released before shutdown
  2785. // 1) Load the asset
  2786. AssetEventHandler assetEventHandler(
  2787. loadSignal, unloadSignal,
  2788. AssetManager::Instance().GetAsset<AssetWithAssetReference>(RootWithSynchronizerAssetId, AssetLoadBehavior::Default));
  2789. m_loadDataSynchronizer.m_readyToLoad = true;
  2790. m_loadDataSynchronizer.m_condition.notify_all();
  2791. ColoredPrintf(COLOR_DEFAULT, "Waiting for initial asset load to complete\n");
  2792. loadSignal->acquire();
  2793. ASSERT_TRUE(assetEventHandler.m_asset.IsReady());
  2794. ColoredPrintf(COLOR_DEFAULT, "Starting reload of asset\n");
  2795. // 2) Start a reload which will complete
  2796. AssetManager::Instance().ReloadAsset(RootWithSynchronizerAssetId, AssetLoadBehavior::Default);
  2797. ColoredPrintf(COLOR_DEFAULT, "Waiting for reload of asset to complete\n");
  2798. unloadSignal->acquire(); // Wait until unload is done
  2799. ColoredPrintf(COLOR_DEFAULT, "Starting another load of asset\n");
  2800. // 3) Start another load
  2801. m_loadDataSynchronizer.m_readyToLoad = false; // Prevent the load from progressing too far
  2802. auto loadingAsset = AssetManager::Instance().GetAsset<AssetWithAssetReference>(RootWithSynchronizerAssetId, AssetLoadBehavior::Default);
  2803. // 4) Unload the old asset reference
  2804. assetEventHandler.m_asset = {};
  2805. // Resume loading
  2806. m_loadDataSynchronizer.m_readyToLoad = true;
  2807. m_loadDataSynchronizer.m_condition.notify_all();
  2808. ColoredPrintf(COLOR_DEFAULT, "Waiting for 2nd reload to complete\n");
  2809. // 5) If the bug is still active, this will fail because the asset can never finish loading
  2810. EXPECT_TRUE(loadSignal->try_acquire_for(AZStd::chrono::seconds(5)));
  2811. ColoredPrintf(COLOR_DEFAULT, "Test conditions complete, beginning shutdown\n");
  2812. }
  2813. // Shut down the event thread
  2814. running = false;
  2815. if (eventThread.joinable())
  2816. {
  2817. eventThread.join();
  2818. }
  2819. // Make sure any pending events are flushed out (to clear any remaining references)
  2820. AssetManager::Instance().DispatchEvents();
  2821. }
  2822. struct IContainerEvents
  2823. {
  2824. AZ_RTTI(IContainerEvents, "{4B29804D-DBC9-49C3-8968-87105915B251}");
  2825. virtual ~IContainerEvents() = default;
  2826. virtual void CreatingContainer() = 0;
  2827. };
  2828. // Test class to inject a Interface event before create a container
  2829. struct SignallingAssetManager : AssetManager
  2830. {
  2831. explicit SignallingAssetManager(const Descriptor& desc)
  2832. : AssetManager(desc)
  2833. {
  2834. }
  2835. private:
  2836. AZStd::shared_ptr<AssetContainer> CreateAssetContainer(Asset<AssetData> asset, const AssetLoadParameters& loadParams, bool isReload) const override
  2837. {
  2838. if(auto events = AZ::Interface<IContainerEvents>::Get())
  2839. {
  2840. events->CreatingContainer();
  2841. }
  2842. return AssetManager::CreateAssetContainer(asset, loadParams, isReload);
  2843. }
  2844. };
  2845. // Test class to listen to interface event and signal a semaphore when one occurs
  2846. class ContainerListener : public AZ::Interface<IContainerEvents>::Registrar
  2847. {
  2848. public:
  2849. explicit ContainerListener(AZStd::binary_semaphore& signal)
  2850. : m_signal(signal)
  2851. {
  2852. }
  2853. private:
  2854. void CreatingContainer() override
  2855. {
  2856. m_signal.release();
  2857. }
  2858. AZStd::binary_semaphore& m_signal;
  2859. };
  2860. // Tests make sure GetAsset can be called within an OnAsset* callback without causing a deadlock
  2861. class AssetManagerEbusSafety : public AssetManagerClearAssetReferenceTests
  2862. {
  2863. public:
  2864. static inline const AZ::Uuid AssetA{ "{CD2EBA84-9637-44D5-B9A5-1BDA82F5F433}" };
  2865. static inline const AZ::Uuid AssetB{ "{96C368C3-C96D-4C36-8E48-EA5A1B1A51F9}" };
  2866. static inline const AZ::Uuid AssetC{ "{189212F5-E430-4EF7-834D-AD4EF2856554}" };
  2867. private:
  2868. void SetUpAssetManager() override
  2869. {
  2870. AssetManager::Descriptor desc;
  2871. m_assetManager = aznew SignallingAssetManager(desc);
  2872. AssetManager::SetInstance(m_assetManager);
  2873. }
  2874. void SetUp() override
  2875. {
  2876. AssetManagerClearAssetReferenceTests::SetUp();
  2877. CreateAsset(AssetA, "AssetA.txt");
  2878. CreateAsset(AssetB, "AssetB.txt");
  2879. CreateAsset(AssetC, "AssetC.txt");
  2880. }
  2881. SignallingAssetManager* m_assetManager{};
  2882. };
  2883. TEST_F(AssetManagerEbusSafety, OnAssetReady_GetAsset_DoesNotDeadlock_SUITE_sandbox)
  2884. {
  2885. // Regression test
  2886. // Steps to repro:
  2887. // 1) An asset load is started
  2888. // 2) OnAssetReady is called on ThreadA
  2889. // 3) ThreadB calls GetAsset and blocks waiting for ThreadA to release the ebux mutex
  2890. // 4) ThreadA calls GetAsset
  2891. // 5) At this point, the threads deadlock each other because of lock inversion. The first thread is holding the ebus mutex and trying to acquire the asset container mutex
  2892. // The second thread is holding the asset container mutex and trying to acquire the asset events ebus mutex
  2893. AZStd::atomic_bool running = true;
  2894. using SignalType = AZStd::binary_semaphore;
  2895. // Start up a thread to dispatch queued events in the background
  2896. AZStd::thread threadA(
  2897. [&running]()
  2898. {
  2899. while (running)
  2900. {
  2901. AssetManager::Instance().DispatchEvents();
  2902. }
  2903. });
  2904. struct AssetBusHandler : AZ::Data::AssetBus::Handler
  2905. {
  2906. AssetBusHandler(SignalType& onAssetReadySignal, SignalType& clearToStartLoadingSignal, AZ::Data::Asset<AZ::Data::AssetData> asset)
  2907. : m_asset(asset)
  2908. , m_onAssetReadySignal(onAssetReadySignal)
  2909. , m_clearToStartLoadingSignal(clearToStartLoadingSignal)
  2910. {
  2911. BusConnect(asset.GetId());
  2912. }
  2913. ~AssetBusHandler() override
  2914. {
  2915. BusDisconnect();
  2916. }
  2917. void OnAssetReady([[maybe_unused]] Asset<AssetData> asset) override
  2918. {
  2919. ColoredPrintf(COLOR_YELLOW, "ThreadA: OnAssetReady called \n");
  2920. m_onAssetReadySignal.release();
  2921. m_clearToStartLoadingSignal.acquire();
  2922. ColoredPrintf(COLOR_YELLOW, "ThreadA: Resumed\n");
  2923. m_otherAsset = AssetManager::Instance().GetAsset<AssetWithSerializedData>(AssetC, Default);
  2924. ColoredPrintf(COLOR_YELLOW, "ThreadA: Got asset\n");
  2925. m_otherAsset.BlockUntilLoadComplete();
  2926. ColoredPrintf(COLOR_YELLOW, "ThreadA: Asset loaded\n");
  2927. }
  2928. AZ::Data::Asset<AZ::Data::AssetData> m_otherAsset;
  2929. AZ::Data::Asset<AZ::Data::AssetData> m_asset;
  2930. SignalType& m_onAssetReadySignal;
  2931. SignalType& m_clearToStartLoadingSignal;
  2932. };
  2933. SignalType onAssetReadySignal;
  2934. SignalType clearToStartLoadingSignal;
  2935. SignalType threadBFinishedSignal;
  2936. // This thread will wait until the initial OnAssetReady event has fired on threadA, at which point it will call GetAsset
  2937. // and signal for threadA to continue after the AssetContainer lock has been acquired
  2938. AZStd::thread threadB([&onAssetReadySignal, &clearToStartLoadingSignal, &threadBFinishedSignal]()
  2939. {
  2940. // Wait for threadA to get into the OnAssetReady event
  2941. onAssetReadySignal.acquire();
  2942. ContainerListener listener(clearToStartLoadingSignal);
  2943. ColoredPrintf(COLOR_YELLOW, "ThreadB: Starting\n");
  2944. auto asset = AssetManager::Instance().GetAsset<AssetWithSerializedData>(AssetB, Default);
  2945. ColoredPrintf(COLOR_YELLOW, "ThreadB: Got asset\n");
  2946. asset.BlockUntilLoadComplete();
  2947. ColoredPrintf(COLOR_YELLOW, "ThreadB: Asset loaded\n");
  2948. threadBFinishedSignal.release();
  2949. });
  2950. {
  2951. // Intentional scope to allow asset references to be released before shutdown
  2952. AssetBusHandler assetBusHandler(onAssetReadySignal, clearToStartLoadingSignal, AssetManager::Instance().GetAsset<AssetWithSerializedData>(AssetA, Default));
  2953. ASSERT_TRUE(threadBFinishedSignal.try_acquire_for(AZStd::chrono::seconds(5)));
  2954. }
  2955. running = false;
  2956. if (threadA.joinable())
  2957. {
  2958. threadA.join();
  2959. }
  2960. if(threadB.joinable())
  2961. {
  2962. threadB.join();
  2963. }
  2964. // Make sure any pending events are flushed out (to clear any remaining references)
  2965. AssetManager::Instance().DispatchEvents();
  2966. }
  2967. using AssetManagerErrorTests = AssetManagerTests;
  2968. #if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  2969. TEST_F(AssetManagerErrorTests, DISABLED_QueueLoad_WithMissingAsset_ReturnsFalse)
  2970. #else
  2971. TEST_F(AssetManagerErrorTests, QueueLoad_WithMissingAsset_ReturnsFalse)
  2972. #endif // AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  2973. {
  2974. AZ::Data::AssetId invalidId("{D000421C-96EF-4FF6-9B7A-3F639E907675}");
  2975. OnAssetReadyListener assetStatus(invalidId, azrtti_typeid<AssetWithCustomData>());
  2976. // Create an asset with an ID that isn't currently registered.
  2977. AZ::Data::Asset<AssetWithCustomData> invalidAsset(invalidId, azrtti_typeid<AssetWithCustomData>());
  2978. // Try to queue the load. This will generate a warning due to the asset not being in the catalog.
  2979. bool queueResult = invalidAsset.QueueLoad();
  2980. // Because the asset is missing, the QueueLoad should return false.
  2981. EXPECT_FALSE(queueResult);
  2982. // OnAssetError should get called for the asset once DispatchEvents is called.
  2983. AssetManager::Instance().DispatchEvents();
  2984. EXPECT_EQ(assetStatus.m_error, 1);
  2985. }
  2986. #if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  2987. TEST_F(AssetManagerErrorTests, DISABLED_GetAsset_WithMissingAsset_ReturnsValidAssetWithErrorState)
  2988. #else
  2989. TEST_F(AssetManagerErrorTests, GetAsset_WithMissingAsset_ReturnsValidAssetWithErrorState)
  2990. #endif // AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  2991. {
  2992. AZ::Data::AssetId invalidId("{D000421C-96EF-4FF6-9B7A-3F639E907675}");
  2993. OnAssetReadyListener assetStatus(invalidId, azrtti_typeid<AssetWithCustomData>());
  2994. // Call GetAsset with an ID that isn't currently registered. This will generate a warning
  2995. auto invalidAsset = AssetManager::Instance().GetAsset<AssetWithCustomData>(invalidId, AssetLoadBehavior::Default);
  2996. // Because the asset is missing, we should end up with a valid asset in an error state.
  2997. EXPECT_TRUE(invalidAsset);
  2998. EXPECT_TRUE(invalidAsset.IsError());
  2999. // OnAssetError should get called for the asset once DispatchEvents is called.
  3000. AssetManager::Instance().DispatchEvents();
  3001. EXPECT_EQ(assetStatus.m_error, 1);
  3002. }
  3003. #if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  3004. TEST_F(AssetManagerErrorTests, DISABLED_AssetBus_BusConnectAfterLoadOfMissingAsset_OnAssetErrorStillGenerated)
  3005. #else
  3006. TEST_F(AssetManagerErrorTests, AssetBus_BusConnectAfterLoadOfMissingAsset_OnAssetErrorStillGenerated)
  3007. #endif // AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
  3008. {
  3009. AZ::Data::AssetId invalidId("{D000421C-96EF-4FF6-9B7A-3F639E907675}");
  3010. // Call GetAsset with an ID that isn't currently registered. This will generate a warning due to the asset not being in the catalog
  3011. auto invalidAsset = AssetManager::Instance().GetAsset<AssetWithCustomData>(invalidId, AssetLoadBehavior::Default);
  3012. // Call DispatchEvents to clear out the event queue.
  3013. AssetManager::Instance().DispatchEvents();
  3014. // Start listening for the asset *after* the events have been sent. This should still immediately generate an OnAssetError.
  3015. {
  3016. OnAssetReadyListener assetStatus(invalidId, azrtti_typeid<AssetWithCustomData>());
  3017. EXPECT_EQ(assetStatus.m_error, 1);
  3018. }
  3019. }
  3020. }