AssetCollectionAsyncLoaderTestComponent.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  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 "AssetCollectionAsyncLoaderTestComponent.h"
  9. #include <AzCore/RTTI/ReflectContext.h>
  10. #include <AzCore/Serialization/EditContext.h>
  11. #include <AzCore/Serialization/SerializeContext.h>
  12. #include <AzFramework/StringFunc/StringFunc.h>
  13. #include <AzCore/Serialization/Json/JsonUtils.h>
  14. // Included so we can deduce the asset type from asset paths.
  15. #include <Atom/RPI.Edit/Common/JsonUtils.h>
  16. #include <Atom/RPI.Reflect/Shader/ShaderAsset.h>
  17. #include <Atom/RPI.Reflect/Model/ModelAsset.h>
  18. #include <Atom/RPI.Reflect/Image/StreamingImageAsset.h>
  19. namespace AZ
  20. {
  21. namespace AtomBridge
  22. {
  23. [[maybe_unused]] static constexpr char AssetCollectionAsyncLoaderTestComponentName[] = " AssetCollectionAsyncLoaderTestComponent";
  24. void AssetCollectionAsyncLoaderTestComponent::Reflect(AZ::ReflectContext* context)
  25. {
  26. auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
  27. if (serializeContext)
  28. {
  29. serializeContext->Class<AssetCollectionAsyncLoaderTestComponent, EditorComponentBase>()
  30. ->Version(1)
  31. ->Field("AssetListPathJson", &AssetCollectionAsyncLoaderTestComponent::m_pathToAssetListJson)
  32. ;
  33. AZ::EditContext* editContext = serializeContext->GetEditContext();
  34. if (editContext)
  35. {
  36. editContext->Class<AssetCollectionAsyncLoaderTestComponent>(
  37. "AssetCollectionAsyncLoaderTest", "The AssetCollectionAsyncLoaderTest component allows you to test the API provided by AssetCollectionAsyncLoader")
  38. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  39. ->Attribute(AZ::Edit::Attributes::Category, "Test")
  40. ->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/Comment.svg")
  41. ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Comment.svg")
  42. ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZStd::vector<AZ::Crc32>({ AZ_CRC("Level", 0x9aeacc13), AZ_CRC("Game", 0x232b318c), AZ_CRC("Layer", 0xe4db211a) }))
  43. ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
  44. ->DataElement(AZ::Edit::UIHandlers::LineEdit, &AssetCollectionAsyncLoaderTestComponent::m_pathToAssetListJson, "", "Path To Asset List")
  45. ->Attribute(AZ::Edit::Attributes::PlaceholderText, "Path to a JSON file")
  46. ->UIElement(AZ::Edit::UIHandlers::Button, "", "Starts/Stop the test")
  47. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &AssetCollectionAsyncLoaderTestComponent::OnStartCancelButtonClicked)
  48. ->Attribute(AZ::Edit::Attributes::ButtonText, &AssetCollectionAsyncLoaderTestComponent::GetStartCancelButtonText);
  49. }
  50. }
  51. if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
  52. {
  53. behaviorContext->EBus<AssetCollectionAsyncLoaderTestBus>("AssetCollectionAsyncLoaderTestBus")
  54. ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
  55. ->Attribute(AZ::Script::Attributes::Category, "Test")
  56. ->Attribute(AZ::Script::Attributes::Module, "test")
  57. ->Event("StartLoadingAssetsFromJsonFile", &AssetCollectionAsyncLoaderTestBus::Events::StartLoadingAssetsFromJsonFile)
  58. ->Event("StartLoadingAssetsFromAssetList", &AssetCollectionAsyncLoaderTestBus::Events::StartLoadingAssetsFromAssetList)
  59. ->Event("CancelLoadingAssets", &AssetCollectionAsyncLoaderTestBus::Events::CancelLoadingAssets)
  60. ->Event("GetPendingAssetsList", &AssetCollectionAsyncLoaderTestBus::Events::GetPendingAssetsList)
  61. ->Event("GetCountOfPendingAssets", &AssetCollectionAsyncLoaderTestBus::Events::GetCountOfPendingAssets)
  62. ->Event("ValidateAssetWasLoaded", &AssetCollectionAsyncLoaderTestBus::Events::ValidateAssetWasLoaded)
  63. ;
  64. }
  65. }
  66. void AssetCollectionAsyncLoaderTestComponent::Activate()
  67. {
  68. m_assetCollectionAsyncLoader = AZStd::make_unique<AZ::AssetCollectionAsyncLoader>();
  69. AssetCollectionAsyncLoaderTestBus::Handler::BusConnect(GetEntityId());
  70. }
  71. void AssetCollectionAsyncLoaderTestComponent::Deactivate()
  72. {
  73. m_assetCollectionAsyncLoader = nullptr;
  74. AssetCollectionAsyncLoaderTestBus::Handler::BusDisconnect();
  75. }
  76. AZ::Crc32 AssetCollectionAsyncLoaderTestComponent::OnStartCancelButtonClicked()
  77. {
  78. switch (m_state)
  79. {
  80. case State::LoadingAssets:
  81. CancelLoadingAssets();
  82. break;
  83. default:
  84. StartLoadingAssetsFromJsonFile(m_pathToAssetListJson);
  85. break;
  86. }
  87. return AZ::Edit::PropertyRefreshLevels::AttributesAndValues;
  88. }
  89. AZStd::string AssetCollectionAsyncLoaderTestComponent::GetStartCancelButtonText() const
  90. {
  91. switch (m_state)
  92. {
  93. case State::LoadingAssets:
  94. return "Cancel Loading Assets";
  95. }
  96. return "Start Loading Assets";
  97. }
  98. //////////////////////////////////////////////////////////////////////////
  99. // AssetCollectionAsyncLoaderTestBus overrides
  100. bool AssetCollectionAsyncLoaderTestComponent::StartLoadingAssetsFromJsonFile(const AZStd::string& pathToAssetListJson)
  101. {
  102. rapidjson::Document jsonDoc;
  103. auto readJsonResult = JsonSerializationUtils::ReadJsonFile(pathToAssetListJson, AZ::RPI::JsonUtils::DefaultMaxFileSize);
  104. if (!readJsonResult.IsSuccess())
  105. {
  106. AZ_Error(AssetCollectionAsyncLoaderTestComponentName, false, "Failed to parse asset list json file %s", pathToAssetListJson.c_str());
  107. return false;
  108. }
  109. jsonDoc = readJsonResult.TakeValue();
  110. AZStd::vector<AZStd::string> assetList;
  111. for (rapidjson::Value::ConstValueIterator itr = jsonDoc.Begin(); itr != jsonDoc.End(); ++itr)
  112. {
  113. AZStd::string_view assetPath = itr->GetString();
  114. AZ_TracePrintf(AssetCollectionAsyncLoaderTestComponentName, "Asset path: %s\n", assetPath.data());
  115. assetList.push_back(assetPath);
  116. }
  117. return StartLoadingAssetsFromAssetList(assetList);
  118. }
  119. //! Helper method
  120. static Data::AssetType GetAssetTypeFromAssetPath(const AZStd::string& assetPath)
  121. {
  122. AZStd::string extension;
  123. if (!AzFramework::StringFunc::Path::GetExtension(assetPath.c_str(), extension, false /*include dot*/))
  124. {
  125. AZ_Error(AssetCollectionAsyncLoaderTestComponentName, false, "Failed to get extension from path: %s", assetPath.c_str());
  126. return {};
  127. }
  128. if (extension == "azshader")
  129. {
  130. return azrtti_typeid<RPI::ShaderAsset>();
  131. }
  132. else if (extension == "azmodel")
  133. {
  134. return azrtti_typeid<RPI::ModelAsset>();
  135. }
  136. else if (extension == "streamingimage")
  137. {
  138. return azrtti_typeid<RPI::StreamingImageAsset>();
  139. }
  140. AZ_Error(AssetCollectionAsyncLoaderTestComponentName, false, "Do not know the asset type for file: %s", assetPath.c_str());
  141. return {};
  142. }
  143. bool AssetCollectionAsyncLoaderTestComponent::StartLoadingAssetsFromAssetList(const AZStd::vector<AZStd::string>& assetList)
  144. {
  145. if (assetList.empty())
  146. {
  147. AZ_Error(AssetCollectionAsyncLoaderTestComponentName, false, "Input asset list is empty");
  148. return false;
  149. }
  150. // Build the list with asset types deduced from the file extensions.
  151. AZStd::vector<AssetCollectionAsyncLoader::AssetToLoadInfo> assetListWithType;
  152. assetListWithType.reserve(assetList.size());
  153. for (const auto& assetPath : assetList)
  154. {
  155. const auto assetType = GetAssetTypeFromAssetPath(assetPath);
  156. assetListWithType.push_back({ assetPath, assetType });
  157. m_pendingAssets.insert(assetPath);
  158. }
  159. m_assetCollectionAsyncLoader->LoadAssetsAsync(assetListWithType,
  160. [&](AZStd::string_view assetPath, [[maybe_unused]] bool success, [[maybe_unused]] size_t pendingAssetCount)
  161. {
  162. AZ_TracePrintf(AssetCollectionAsyncLoaderTestComponentName, "Got asset load [%s] for asset [%s]. Pending asset count [%zu]\n"
  163. , success ? "SUCCESS" : "ERROR", assetPath.data(), pendingAssetCount);
  164. switch (m_state)
  165. {
  166. case State::LoadingAssets:
  167. {
  168. if (m_pendingAssets.count(assetPath))
  169. {
  170. m_pendingAssets.erase(assetPath);
  171. if (!m_pendingAssets.size())
  172. {
  173. AZ_TracePrintf(AssetCollectionAsyncLoaderTestComponentName, "Asset Loading Is Successfully Complete\n");
  174. m_state = State::Idle;
  175. }
  176. }
  177. else
  178. {
  179. AZ_Error(AssetCollectionAsyncLoaderTestComponentName, false, "While loading assets, got asset update from an unexpected asset with path: %s", assetPath.data());
  180. m_assetCollectionAsyncLoader->Cancel();
  181. m_state = State::FatalError;
  182. }
  183. }
  184. break;
  185. default:
  186. AZ_Error(AssetCollectionAsyncLoaderTestComponentName, false, "Got asset update from an unexpected asset with path: %s", assetPath.data());
  187. m_state = State::FatalError;
  188. break;
  189. }
  190. });
  191. m_state = State::LoadingAssets;
  192. return true;
  193. }
  194. void AssetCollectionAsyncLoaderTestComponent::CancelLoadingAssets()
  195. {
  196. m_assetCollectionAsyncLoader->Cancel();
  197. m_pendingAssets.clear();
  198. m_state = State::Idle;
  199. }
  200. AZStd::vector<AZStd::string> AssetCollectionAsyncLoaderTestComponent::GetPendingAssetsList() const
  201. {
  202. AZStd::vector<AZStd::string> retList;
  203. retList.reserve(m_pendingAssets.size());
  204. AZStd::for_each(m_pendingAssets.begin(), m_pendingAssets.end(),
  205. [&](const AZStd::string& assetPath)
  206. {
  207. retList.push_back(assetPath);
  208. });
  209. return retList;
  210. }
  211. uint32_t AssetCollectionAsyncLoaderTestComponent::GetCountOfPendingAssets() const
  212. {
  213. return static_cast<uint32_t>(m_pendingAssets.size());
  214. }
  215. bool AssetCollectionAsyncLoaderTestComponent::ValidateAssetWasLoaded(const AZStd::string& assetPath) const
  216. {
  217. Data::AssetType assetType = GetAssetTypeFromAssetPath(assetPath);
  218. if (assetType == azrtti_typeid<RPI::ShaderAsset>())
  219. {
  220. auto asset = m_assetCollectionAsyncLoader->GetAsset<RPI::ShaderAsset>(assetPath);
  221. return (bool)asset && asset.GetId().IsValid() && asset.IsReady() && !asset->GetName().IsEmpty();
  222. }
  223. else if (assetType == azrtti_typeid<RPI::ModelAsset>())
  224. {
  225. auto asset = m_assetCollectionAsyncLoader->GetAsset<RPI::ModelAsset>(assetPath);
  226. return (bool)asset && asset.GetId().IsValid() && asset.IsReady() && asset->GetLodCount();
  227. }
  228. else if (assetType == azrtti_typeid<RPI::StreamingImageAsset>())
  229. {
  230. auto asset = m_assetCollectionAsyncLoader->GetAsset<RPI::StreamingImageAsset>(assetPath);
  231. return (bool)asset && asset.GetId().IsValid() && asset.IsReady() && asset->GetTotalImageDataSize();
  232. }
  233. AZ_Error(AssetCollectionAsyncLoaderTestComponentName, false, "Can not handle asset type for assetPath: %s", assetPath.c_str());
  234. return false;
  235. }
  236. //////////////////////////////////////////////////////////////////////////
  237. } //namespace AtomBridge
  238. } // namespace AZ