3
0

StreamingImageAssetHandler.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  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 <Atom/RPI.Reflect/Image/StreamingImageAssetHandler.h>
  9. #include <Atom/RPI.Public/Image/ImageSystemInterface.h>
  10. #include <Atom/RPI.Public/AssetTagBus.h>
  11. #include <AzCore/Settings/SettingsRegistry.h>
  12. #include <AzFramework/Asset/AssetSystemBus.h>
  13. namespace AZ
  14. {
  15. namespace RPI
  16. {
  17. StreamingImageAssetHandler::~StreamingImageAssetHandler()
  18. {
  19. for (const auto& pendingAsset: m_pendingReloadImageAsset)
  20. {
  21. HandleMipChainAssetBuses(pendingAsset.second.m_imageAsset, false);
  22. }
  23. }
  24. Data::AssetHandler::LoadResult StreamingImageAssetHandler::LoadAssetData(
  25. const Data::Asset<Data::AssetData>& asset,
  26. AZStd::shared_ptr<Data::AssetDataStream> stream,
  27. const Data::AssetFilterCB& assetLoadFilterCB)
  28. {
  29. AZ_UNUSED(assetLoadFilterCB);
  30. StreamingImageAsset* assetData = asset.GetAs<StreamingImageAsset>();
  31. AZ_Assert(assetData, "Asset is of the wrong type.");
  32. AZ_Assert(m_serializeContext, "Unable to retrieve serialize context.");
  33. Data::AssetHandler::LoadResult loadResult = Data::AssetHandler::LoadResult::Error;
  34. if (assetData)
  35. {
  36. if (stream->GetLength() > 0)
  37. {
  38. loadResult = Utils::LoadObjectFromStreamInPlace<StreamingImageAsset>(*stream, *assetData, m_serializeContext)
  39. ? Data::AssetHandler::LoadResult::LoadComplete
  40. : Data::AssetHandler::LoadResult::Error;
  41. }
  42. if (loadResult == Data::AssetHandler::LoadResult::LoadComplete)
  43. {
  44. // ImageMipChainAsset has some internal variables need to initialized after it was loaded.
  45. assetData->m_tailMipChain.Init();
  46. if (const auto& imageTags = assetData->GetTags(); !imageTags.empty())
  47. {
  48. AssetQuality highestMiplevel = AssetQualityLowest;
  49. for (const AZ::Name& tag : imageTags)
  50. {
  51. AssetQuality tagQuality = AssetQualityHighest;
  52. ImageTagBus::BroadcastResult(tagQuality, &ImageTagBus::Events::GetQuality, tag);
  53. highestMiplevel = AZStd::min(highestMiplevel, tagQuality);
  54. }
  55. assetData->RemoveFrontMipchains(highestMiplevel);
  56. for (const AZ::Name& tag : imageTags)
  57. {
  58. ImageTagBus::Broadcast(&ImageTagBus::Events::RegisterAsset, tag, assetData->GetId());
  59. }
  60. }
  61. // Handle StreamingImageAsset reload (which the asset can be found from asset manager)
  62. auto& assetManager = AZ::Data::AssetManager::Instance();
  63. Data::Asset<StreamingImageAsset> foundImageAsset = assetManager.FindAsset(asset.GetId(), AZ::Data::AssetLoadBehavior::Default);
  64. // If the asset has full mipchain assets, then we want to reload all of them when reload StreamingImageAsset
  65. if (foundImageAsset && foundImageAsset.GetData() != asset.GetData() && foundImageAsset->HasFullMipChainAssets())
  66. {
  67. // Save the reloaded StreamingImageAsset data
  68. Uuid imageGuid = asset.GetId().m_guid;
  69. PendingImageAssetInfo pendingAssetInfo;
  70. pendingAssetInfo.m_imageAsset = asset;
  71. for (auto& mipChainAsset : assetData->m_mipChains)
  72. {
  73. Data::AssetId mipChainAssetId = mipChainAsset.m_asset.GetId();
  74. if (mipChainAssetId.IsValid())
  75. {
  76. // Release the mipchain asset since it could reference the old asset.
  77. if (auto foundAsset = assetManager.FindAsset(mipChainAssetId, AZ::Data::AssetLoadBehavior::PreLoad))
  78. {
  79. // if the asset was loaded, trigger reload
  80. foundAsset.Reload();
  81. mipChainAsset.m_asset = foundAsset;
  82. }
  83. else
  84. {
  85. // The asset wasn't loaded, queue load
  86. // This may happen if the image's resolution increased which generate new mipchain assets
  87. mipChainAsset.m_asset.QueueLoad();
  88. }
  89. pendingAssetInfo.m_mipChainAssetSubIds.push_back(mipChainAssetId.m_subId);
  90. Data::AssetBus::MultiHandler::BusConnect(mipChainAssetId);
  91. }
  92. }
  93. if (!pendingAssetInfo.m_mipChainAssetSubIds.empty())
  94. {
  95. AZStd::scoped_lock<AZStd::mutex> lock(m_accessPendingAssetsMutex);
  96. m_pendingReloadImageAsset[imageGuid] = AZStd::move(pendingAssetInfo);
  97. }
  98. }
  99. }
  100. }
  101. return loadResult;
  102. }
  103. void StreamingImageAssetHandler::OnAssetReady(Data::Asset<Data::AssetData> asset)
  104. {
  105. HandleMipChainAssetLoad(asset, true);
  106. }
  107. void StreamingImageAssetHandler::OnAssetError(AZ::Data::Asset<AZ::Data::AssetData> asset)
  108. {
  109. HandleMipChainAssetLoad(asset, false);
  110. }
  111. void StreamingImageAssetHandler::OnAssetReloaded(Data::Asset<Data::AssetData> asset)
  112. {
  113. HandleMipChainAssetLoad(asset, true);
  114. }
  115. void StreamingImageAssetHandler::OnAssetReloadError(Data::Asset<Data::AssetData> asset)
  116. {
  117. HandleMipChainAssetLoad(asset, false);
  118. }
  119. void StreamingImageAssetHandler::HandleMipChainAssetBuses(Data::Asset<Data::AssetData> streamingImageAsset, bool connect)
  120. {
  121. StreamingImageAsset* assetData = streamingImageAsset.GetAs<StreamingImageAsset>();
  122. for (auto& mipChainAsset : assetData->m_mipChains)
  123. {
  124. // Need to skip tail mipchain which is in the list and doesn't have an asset id.
  125. if (mipChainAsset.m_asset.GetId().IsValid())
  126. {
  127. if (connect)
  128. {
  129. Data::AssetBus::MultiHandler::BusConnect(mipChainAsset.m_asset.GetId());
  130. }
  131. else
  132. {
  133. Data::AssetBus::MultiHandler::BusDisconnect(mipChainAsset.m_asset.GetId());
  134. }
  135. }
  136. }
  137. }
  138. void StreamingImageAssetHandler::HandleMipChainAssetLoad(Data::Asset<Data::AssetData> asset, bool isLoadSuccess)
  139. {
  140. bool hasError = false;
  141. bool reloadEnded = false;
  142. Data::Asset<Data::AssetData> imageAsset;
  143. {
  144. // Note: lock the mutex at minimum scope and avoid calling broadcast or bus connect/disconnect when mutex is locked.
  145. AZStd::scoped_lock<AZStd::mutex> lock(m_accessPendingAssetsMutex);
  146. auto itr = m_pendingReloadImageAsset.find(asset.GetId().m_guid);
  147. if (itr == m_pendingReloadImageAsset.end())
  148. {
  149. return;
  150. }
  151. if (!isLoadSuccess)
  152. {
  153. reloadEnded = true;
  154. hasError = true;
  155. }
  156. uint32_t loadedMipChainAssets = 0;
  157. for (u32& subId : itr->second.m_mipChainAssetSubIds)
  158. {
  159. if (subId == asset.GetId().m_subId)
  160. {
  161. StreamingImageAsset* assetData = itr->second.m_imageAsset.GetAs<StreamingImageAsset>();
  162. // Assign the reloaded/loaded asset to StreamingImageAsset
  163. for (auto& mipChainAsset : assetData->m_mipChains)
  164. {
  165. if (mipChainAsset.m_asset.GetId() == asset.GetId())
  166. {
  167. mipChainAsset.m_asset = asset;
  168. // set the subId to 0 after it's loaded
  169. subId = 0;
  170. break;
  171. }
  172. }
  173. }
  174. if (subId == 0)
  175. {
  176. loadedMipChainAssets++;
  177. }
  178. }
  179. if (loadedMipChainAssets == itr->second.m_mipChainAssetSubIds.size())
  180. {
  181. reloadEnded = true;
  182. hasError = false;
  183. }
  184. if (reloadEnded)
  185. {
  186. imageAsset = itr->second.m_imageAsset;
  187. m_pendingReloadImageAsset.erase(itr);
  188. }
  189. }
  190. if (reloadEnded)
  191. {
  192. HandleMipChainAssetBuses(imageAsset, false);
  193. if (hasError)
  194. {
  195. Data::AssetManagerBus::Broadcast(&Data::AssetManagerBus::Events::OnAssetReloadError, imageAsset);
  196. }
  197. else
  198. {
  199. Data::AssetManagerBus::Broadcast(&Data::AssetManagerBus::Events::OnAssetReloaded, imageAsset);
  200. }
  201. }
  202. }
  203. void StreamingImageAssetHandler::InitAsset(const Data::Asset<Data::AssetData>& asset, bool loadStageSucceeded, bool isReload)
  204. {
  205. m_accessPendingAssetsMutex.lock();
  206. bool isPendingReload = m_pendingReloadImageAsset.find(asset.GetId().m_guid) != m_pendingReloadImageAsset.end();
  207. m_accessPendingAssetsMutex.unlock();
  208. // Broadcast the asset loading events via AssetHandler::InitAsset
  209. // If the asset was reloaded successfully and also have pending mipchain assets to load, skip here and delay the broadcast the reloaded event.
  210. if (!(loadStageSucceeded && isReload && isPendingReload))
  211. {
  212. AZ_Assert(!isPendingReload, "The asset shouldn't be added to pending reload asset list");
  213. AssetHandler::InitAsset(asset, loadStageSucceeded, isReload);
  214. }
  215. }
  216. Data::AssetId StreamingImageAssetHandler::AssetMissingInCatalog(const Data::Asset<Data::AssetData>& asset)
  217. {
  218. AZ_Info("Streaming Image",
  219. "Streaming Image id " AZ_STRING_FORMAT " not found in asset catalog, using fallback image.\n",
  220. AZ_STRING_ARG(asset.GetId().ToFixedString()));
  221. // Find out if the asset is missing completely, or just still processing
  222. // and escalate the asset to the top of the list
  223. AzFramework::AssetSystem::AssetStatus missingAssetStatus = AzFramework::AssetSystem::AssetStatus::AssetStatus_Unknown;
  224. AzFramework::AssetSystemRequestBus::BroadcastResult(
  225. missingAssetStatus, &AzFramework::AssetSystem::AssetSystemRequests::GetAssetStatusById, asset.GetId().m_guid);
  226. // Determine which fallback image to use
  227. const char* relativePath = DefaultImageAssetPaths::DefaultFallback;
  228. bool useDebugFallbackImages = true;
  229. if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
  230. {
  231. settingsRegistry->GetObject(useDebugFallbackImages, "/O3DE/Atom/RPI/UseDebugFallbackImages");
  232. }
  233. if (useDebugFallbackImages)
  234. {
  235. switch (missingAssetStatus)
  236. {
  237. case AzFramework::AssetSystem::AssetStatus::AssetStatus_Queued:
  238. case AzFramework::AssetSystem::AssetStatus::AssetStatus_Compiling:
  239. relativePath = DefaultImageAssetPaths::Processing;
  240. break;
  241. case AzFramework::AssetSystem::AssetStatus::AssetStatus_Failed:
  242. relativePath = DefaultImageAssetPaths::ProcessingFailed;
  243. break;
  244. case AzFramework::AssetSystem::AssetStatus::AssetStatus_Missing:
  245. case AzFramework::AssetSystem::AssetStatus::AssetStatus_Unknown:
  246. case AzFramework::AssetSystem::AssetStatus::AssetStatus_Compiled:
  247. relativePath = DefaultImageAssetPaths::Missing;
  248. break;
  249. }
  250. }
  251. // Make sure the fallback image has been processed
  252. AzFramework::AssetSystem::AssetStatus status = AzFramework::AssetSystem::AssetStatus_Unknown;
  253. AzFramework::AssetSystemRequestBus::BroadcastResult(
  254. status, &AzFramework::AssetSystemRequestBus::Events::CompileAssetSync, relativePath);
  255. // Return the asset id of the fallback image
  256. Data::AssetId assetId{};
  257. bool autoRegisterIfNotFound = false;
  258. Data::AssetCatalogRequestBus::BroadcastResult(
  259. assetId, &Data::AssetCatalogRequestBus::Events::GetAssetIdByPath, relativePath,
  260. azrtti_typeid<AZ::RPI::StreamingImageAsset>(), autoRegisterIfNotFound);
  261. return assetId;
  262. }
  263. } // namespace RPI
  264. } // namespace AZ