PrefabBuilderComponent.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  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 "PrefabBuilderComponent.h"
  9. #include <AzToolsFramework/Debug/TraceContext.h>
  10. namespace AZ::Prefab
  11. {
  12. // This allows the component's serialization version, and the builder version, to remain in sync.
  13. // Not strictly required, but it's useful to modify one when the other is modified, to keep data synchronized.
  14. const int PrefabBuilderComponentVersion = 1;
  15. void PrefabBuilderComponent::Reflect(AZ::ReflectContext* context)
  16. {
  17. if (AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context))
  18. {
  19. serialize->Class<PrefabBuilderComponent, AZ::Component>()
  20. ->Version(PrefabBuilderComponentVersion)
  21. ->Attribute(
  22. AZ::Edit::Attributes::SystemComponentTags, AZStd::vector<AZ::Crc32>({AssetBuilderSDK::ComponentTags::AssetBuilder}));
  23. }
  24. }
  25. AzToolsFramework::Fingerprinting::TypeFingerprint PrefabBuilderComponent::CalculateBuilderFingerprint() const
  26. {
  27. AzToolsFramework::Fingerprinting::TypeCollection typeCollection = m_typeFingerprinter->GatherAllTypesForComponents();
  28. AzToolsFramework::Fingerprinting::TypeFingerprint fingerprint = m_typeFingerprinter->GenerateFingerprintForAllTypes(typeCollection);
  29. AZStd::hash_combine(fingerprint, m_pipeline.GetFingerprint());
  30. return fingerprint;
  31. }
  32. void PrefabBuilderComponent::Activate()
  33. {
  34. AssetBuilderSDK::AssetBuilderCommandBus::Handler::BusConnect(m_builderId);
  35. m_pipeline.LoadStackProfile(ConfigKey);
  36. AZ::SerializeContext* serializeContext = nullptr;
  37. AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
  38. AZ_Assert(serializeContext, "SerializeContext not found");
  39. m_typeFingerprinter = AZStd::make_unique<AzToolsFramework::Fingerprinting::TypeFingerprinter>(*serializeContext);
  40. AzToolsFramework::Fingerprinting::TypeFingerprint fingerprint = CalculateBuilderFingerprint();
  41. AssetBuilderSDK::AssetBuilderDesc builderDesc;
  42. builderDesc.m_name = "Prefab Builder";
  43. builderDesc.m_version = PrefabBuilderComponentVersion;
  44. builderDesc.m_patterns.emplace_back("*.prefab", AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard);
  45. builderDesc.m_builderType = AssetBuilderSDK::AssetBuilderDesc::AssetBuilderType::External;
  46. builderDesc.m_busId = m_builderId;
  47. builderDesc.m_analysisFingerprint = AZStd::to_string(fingerprint);
  48. builderDesc.m_createJobFunction =
  49. [this](const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response) {
  50. CreateJobs(request, response);
  51. };
  52. builderDesc.m_processJobFunction =
  53. [this](const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response) {
  54. ProcessJob(request, response);
  55. };
  56. AssetBuilderSDK::AssetBuilderBus::Broadcast(&AssetBuilderSDK::AssetBuilderBusTraits::RegisterBuilderInformation, builderDesc);
  57. }
  58. void PrefabBuilderComponent::Deactivate()
  59. {
  60. AssetBuilderSDK::AssetBuilderCommandBus::Handler::BusDisconnect(m_builderId);
  61. }
  62. void PrefabBuilderComponent::ShutDown()
  63. {
  64. m_isShuttingDown = true;
  65. }
  66. AzToolsFramework::Fingerprinting::TypeFingerprint PrefabBuilderComponent::CalculatePrefabFingerprint(
  67. const AzToolsFramework::Prefab::PrefabDom& genericDocument) const
  68. {
  69. AzToolsFramework::Fingerprinting::TypeFingerprint fingerprint = m_pipeline.GetFingerprint();
  70. // Deserialize all of the entities and their components (for this prefab only)
  71. auto newInstance = AZStd::make_unique<AzToolsFramework::Prefab::Instance>();
  72. AzToolsFramework::EntityList entities;
  73. if (AzToolsFramework::Prefab::PrefabDomUtils::LoadInstanceFromPrefabDom(*newInstance, entities, genericDocument))
  74. {
  75. // Add the fingerprint of all the components and their types
  76. AZStd::hash_combine(fingerprint, m_typeFingerprinter->GenerateFingerprintForAllTypesInObject(&entities));
  77. }
  78. return fingerprint;
  79. }
  80. AZStd::vector<AssetBuilderSDK::SourceFileDependency> PrefabBuilderComponent::GetSourceDependencies(const AzToolsFramework::Prefab::PrefabDom& genericDocument)
  81. {
  82. AZStd::vector<AssetBuilderSDK::SourceFileDependency> sourceFileDependencies;
  83. auto instancesIterator = genericDocument.FindMember(AzToolsFramework::Prefab::PrefabDomUtils::InstancesName);
  84. if (instancesIterator != genericDocument.MemberEnd())
  85. {
  86. auto&& instances = instancesIterator->value;
  87. if (instances.IsObject())
  88. {
  89. for (auto&& entry : instances.GetObject())
  90. {
  91. auto sourceIterator = entry.value.FindMember(AzToolsFramework::Prefab::PrefabDomUtils::SourceName);
  92. if (sourceIterator != entry.value.MemberEnd())
  93. {
  94. auto&& source = sourceIterator->value;
  95. if (source.IsString())
  96. {
  97. sourceFileDependencies.emplace_back(source.GetString(), Uuid::CreateNull());
  98. }
  99. }
  100. }
  101. }
  102. }
  103. return sourceFileDependencies;
  104. }
  105. void PrefabBuilderComponent::CreateJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response) const
  106. {
  107. using namespace AzToolsFramework::Prefab;
  108. using namespace AzToolsFramework::Prefab::PrefabConversionUtils;
  109. if (m_isShuttingDown)
  110. {
  111. response.m_result = AssetBuilderSDK::CreateJobsResultCode::ShuttingDown;
  112. return;
  113. }
  114. AZStd::string fullPath;
  115. AZ::StringFunc::Path::ConstructFull(request.m_watchFolder.c_str(), request.m_sourceFile.c_str(), fullPath);
  116. // Load the JSON Dom
  117. AZ::Outcome<PrefabDom, AZStd::string> readPrefabFileResult = AZ::JsonSerializationUtils::ReadJsonFile(fullPath);
  118. if (!readPrefabFileResult.IsSuccess())
  119. {
  120. AZ_Error(
  121. "Prefab", false,
  122. "PrefabLoader::LoadPrefabFile - Failed to load Prefab file from '%s'."
  123. "Error message: '%s'",
  124. fullPath.c_str(), readPrefabFileResult.GetError().c_str());
  125. return;
  126. }
  127. auto genericDocument = readPrefabFileResult.TakeValue();
  128. size_t fingerprint = CalculatePrefabFingerprint(genericDocument);
  129. AZStd::vector<AssetBuilderSDK::SourceFileDependency> sourceFileDependencies = GetSourceDependencies(genericDocument);
  130. for (const AssetBuilderSDK::PlatformInfo& info : request.m_enabledPlatforms)
  131. {
  132. AssetBuilderSDK::JobDescriptor job;
  133. job.m_jobKey = PrefabJobKey;
  134. job.SetPlatformIdentifier(info.m_identifier.c_str());
  135. job.m_additionalFingerprintInfo = AZStd::to_string(fingerprint);
  136. // Add a fingerprint job dependency on any referenced prefab so this prefab will rebuild if the dependent fingerprint changes
  137. for (const AssetBuilderSDK::SourceFileDependency& sourceFileDependency : sourceFileDependencies)
  138. {
  139. job.m_jobDependencyList.emplace_back(
  140. PrefabJobKey, info.m_identifier, AssetBuilderSDK::JobDependencyType::Fingerprint, sourceFileDependency);
  141. }
  142. response.m_createJobOutputs.push_back(AZStd::move(job));
  143. }
  144. response.m_result = AssetBuilderSDK::CreateJobsResultCode::Success;
  145. }
  146. bool PrefabBuilderComponent::StoreProducts(
  147. AZ::IO::PathView tempDirPath,
  148. const AzToolsFramework::Prefab::PrefabConversionUtils::PrefabProcessorContext::ProcessedObjectStoreContainer& store,
  149. const AzToolsFramework::Prefab::PrefabConversionUtils::PrefabProcessorContext::ProductAssetDependencyContainer& registeredDependencies,
  150. AZStd::vector<AssetBuilderSDK::JobProduct>& outputProducts) const
  151. {
  152. using namespace AzToolsFramework::Prefab::PrefabConversionUtils;
  153. outputProducts.reserve(store.size());
  154. AZStd::vector<uint8_t> data;
  155. for (auto& object : store)
  156. {
  157. AZ_TracePrintf("Prefab Builder", " Serializing Prefab product '%s'.\n", object.GetId().c_str());
  158. if (!object.Serialize(data))
  159. {
  160. AZ_Error("Prefab Builder", false, "Failed to serialize object '%s'.", object.GetId().c_str());
  161. return false;
  162. }
  163. AZ::IO::Path productPath = tempDirPath;
  164. productPath /= object.GetId();
  165. AZ_TracePrintf("Prefab Builder", " Storing Prefab product '%s'.\n", object.GetId().c_str());
  166. AZStd::unique_ptr<AZ::IO::GenericStream> productFile = GetOutputStream(productPath);
  167. if (!productFile->IsOpen())
  168. {
  169. AZ_Error("Prefab Builder", false, "Unable to open product file at '%s'.", productPath.c_str());
  170. return false;
  171. }
  172. if (productFile->Write(data.size(), data.data()) != data.size())
  173. {
  174. AZ_Error("Prefab Builder", false, "Unable to write product file at '%s'.", productPath.c_str());
  175. return false;
  176. }
  177. AssetBuilderSDK::JobProduct product;
  178. if (AssetBuilderSDK::OutputObject(&object.GetAsset(), object.GetAssetType(), productPath.String(), object.GetAssetType(),
  179. object.GetAsset().GetId().m_subId, product))
  180. {
  181. auto range = registeredDependencies.equal_range(object.GetAsset().GetId());
  182. AZStd::transform(range.first, range.second,
  183. AZStd::back_inserter(product.m_dependencies),
  184. [](const auto& dependency) -> AssetBuilderSDK::ProductDependency
  185. {
  186. return AssetBuilderSDK::ProductDependency(
  187. dependency.second.m_assetId, AZ::Data::ProductDependencyInfo::CreateFlags(dependency.second.m_loadBehavior));
  188. });
  189. outputProducts.push_back(AZStd::move(product));
  190. }
  191. data.clear();
  192. }
  193. return true;
  194. }
  195. AZStd::unique_ptr<AZ::IO::GenericStream> PrefabBuilderComponent::GetOutputStream(const AZ::IO::Path& path) const
  196. {
  197. return AZStd::make_unique<AZ::IO::FileIOStream>(path.c_str(), AZ::IO::OpenMode::ModeWrite | AZ::IO::OpenMode::ModeCreatePath);
  198. }
  199. bool PrefabBuilderComponent::ProcessPrefab(
  200. const AZ::PlatformTagSet& platformTags, const char* filePath, AZ::IO::PathView tempDirPath, const AZ::Uuid& sourceFileUuid,
  201. AzToolsFramework::Prefab::PrefabDom&& rootDom, AZStd::vector<AssetBuilderSDK::JobProduct>& jobProducts)
  202. {
  203. AZ_TraceContext("Stack config", ConfigKey);
  204. AzToolsFramework::Prefab::PrefabConversionUtils::PrefabProcessorContext context(sourceFileUuid);
  205. AZStd::string rootPrefabName;
  206. if (!StringFunc::Path::GetFileName(filePath, rootPrefabName))
  207. {
  208. AZ_Error("Prefab Builder", false, "Unable to extract filename from '%s'.",
  209. filePath);
  210. return false;
  211. }
  212. AzToolsFramework::Prefab::PrefabConversionUtils::PrefabDocument rootDocument(AZStd::move(rootPrefabName));
  213. rootDocument.SetPrefabDom(AZStd::move(rootDom));
  214. context.AddPrefab(AZStd::move(rootDocument));
  215. context.SetPlatformTags(AZStd::move(platformTags));
  216. AZ_TracePrintf("Prefab Builder", "Sending Prefab to the processor stack.\n");
  217. m_pipeline.ProcessPrefab(context);
  218. if (context.HasCompletedSuccessfully())
  219. {
  220. AZ_TracePrintf("Prefab Builder", "Finalizing products.\n");
  221. if (StoreProducts(tempDirPath, context.GetProcessedObjects(),
  222. context.GetRegisteredProductAssetDependencies(), jobProducts))
  223. {
  224. return true;
  225. }
  226. else
  227. {
  228. AZ_Error("Prefab Builder", false, "One or more objects couldn't be committed to disk.");
  229. }
  230. }
  231. else
  232. {
  233. AZ_Error("Prefab Builder", false, "Failed to fully process the target prefab.");
  234. }
  235. return false;
  236. }
  237. void PrefabBuilderComponent::ProcessJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response)
  238. {
  239. using namespace AzToolsFramework::Prefab;
  240. using namespace AzToolsFramework::Prefab::PrefabConversionUtils;
  241. if (m_isShuttingDown)
  242. {
  243. response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Cancelled;
  244. return;
  245. }
  246. response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
  247. auto* system = AZ::Interface<PrefabSystemComponentInterface>::Get();
  248. if (!system)
  249. {
  250. AZ_Error("Prefab Builder", false, "Prefab system is not available.");
  251. return;
  252. }
  253. auto* loader = AZ::Interface<PrefabLoaderInterface>::Get();
  254. if (!loader)
  255. {
  256. AZ_Error("Prefab Builder", false, "Prefab loader is not available.");
  257. return;
  258. }
  259. AZ_TracePrintf("Prefab Builder", "Loading Prefab in '%s'.\n", request.m_fullPath.c_str());
  260. AzToolsFramework::Prefab::TemplateId templateId = loader->LoadTemplateFromFile(AZStd::string_view(request.m_fullPath));
  261. if (templateId == InvalidTemplateId)
  262. {
  263. AZ_Error("Prefab Builder", false, "Failed to load Prefab template.");
  264. return;
  265. }
  266. PrefabDom mutableRootDom;
  267. mutableRootDom.CopyFrom(system->FindTemplateDom(templateId), mutableRootDom.GetAllocator());
  268. AZ::PlatformTagSet platformTags;
  269. const auto& tags = request.m_platformInfo.m_tags;
  270. AZStd::for_each(tags.begin(), tags.end(), [&platformTags](const auto& tag) {
  271. platformTags.emplace(AZ::Crc32(tag.c_str(), tag.size(), true));
  272. });
  273. if (ProcessPrefab(
  274. platformTags, request.m_fullPath.c_str(), request.m_tempDirPath.c_str(), request.m_sourceFileUUID,
  275. AZStd::move(mutableRootDom), response.m_outputProducts))
  276. {
  277. response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success;
  278. }
  279. AZ_TracePrintf("Prefab Builder", "Cleaning up.\n");
  280. system->RemoveAllTemplates();
  281. AZ_TracePrintf("Prefab Builder", "Prefab processing completed.\n");
  282. }
  283. } // namespace AZ::Prefab