ImageBuilderComponent.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  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 <ImageBuilderComponent.h>
  9. #include <AzCore/std/string/conversions.h>
  10. #include <AzCore/Serialization/SerializeContext.h>
  11. #include <AzFramework/StringFunc/StringFunc.h>
  12. #include <AzFramework/IO/LocalFileIO.h>
  13. #include <AzCore/Debug/Trace.h>
  14. #include <BuilderSettings/BuilderSettingManager.h>
  15. #include <BuilderSettings/CubemapSettings.h>
  16. #include <ImageLoader/ImageLoaders.h>
  17. #include <Processing/ImageAssetProducer.h>
  18. #include <Processing/ImageConvert.h>
  19. #include <Processing/ImageToProcess.h>
  20. #include <Processing/PixelFormatInfo.h>
  21. #include <AzFramework/API/ApplicationAPI.h>
  22. #include <AzCore/Serialization/EditContextConstants.inl>
  23. #include <QFile>
  24. #include <AzQtComponents/Utilities/QtPluginPaths.h>
  25. #include <Atom/RPI.Reflect/Asset/AssetHandler.h>
  26. #include <Atom/RPI.Reflect/Image/StreamingImageAssetHandler.h>
  27. #include <Atom/RPI.Reflect/Image/ImageMipChainAsset.h>
  28. namespace ImageProcessingAtom
  29. {
  30. BuilderPluginComponent::BuilderPluginComponent()
  31. {
  32. // AZ Components should only initialize their members to null and empty in constructor
  33. // after construction, they may be deserialized from file.
  34. }
  35. BuilderPluginComponent::~BuilderPluginComponent()
  36. {
  37. }
  38. void BuilderPluginComponent::Init()
  39. {
  40. }
  41. void BuilderPluginComponent::Activate()
  42. {
  43. //load qt plugins for some image file formats support
  44. AzQtComponents::PrepareQtPaths();
  45. // create and initialize BuilderSettingManager once since it's will be used for image conversion
  46. BuilderSettingManager::CreateInstance();
  47. auto outcome = BuilderSettingManager::Instance()->LoadConfig();
  48. AZ_Error("Image Processing", outcome.IsSuccess(), "Failed to load Atom image builder settings.");
  49. if (!outcome.IsSuccess())
  50. {
  51. return;
  52. }
  53. // Activate is where you'd perform registration with other objects and systems.
  54. // Since we want to register our builder, we do that here:
  55. AssetBuilderSDK::AssetBuilderDesc builderDescriptor;
  56. builderDescriptor.m_name = "Atom Image Builder";
  57. for (int i = 0; i < s_TotalSupportedImageExtensions; i++)
  58. {
  59. builderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern(s_SupportedImageExtensions[i], AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard));
  60. }
  61. builderDescriptor.m_busId = azrtti_typeid<ImageBuilderWorker>();
  62. builderDescriptor.m_createJobFunction = AZStd::bind(&ImageBuilderWorker::CreateJobs, &m_imageBuilder, AZStd::placeholders::_1, AZStd::placeholders::_2);
  63. builderDescriptor.m_processJobFunction = AZStd::bind(&ImageBuilderWorker::ProcessJob, &m_imageBuilder, AZStd::placeholders::_1, AZStd::placeholders::_2);
  64. builderDescriptor.m_version = 25; // [ATOM-16575]
  65. builderDescriptor.m_analysisFingerprint = ImageProcessingAtom::BuilderSettingManager::Instance()->GetAnalysisFingerprint();
  66. m_imageBuilder.BusConnect(builderDescriptor.m_busId);
  67. AssetBuilderSDK::AssetBuilderBus::Broadcast(&AssetBuilderSDK::AssetBuilderBusTraits::RegisterBuilderInformation, builderDescriptor);
  68. m_assetHandlers.emplace_back(AZ::RPI::MakeAssetHandler<AZ::RPI::ImageMipChainAssetHandler>());
  69. m_assetHandlers.emplace_back(AZ::RPI::MakeAssetHandler<AZ::RPI::StreamingImageAssetHandler>());
  70. ImageProcessingRequestBus::Handler::BusConnect();
  71. ImageBuilderRequestBus::Handler::BusConnect();
  72. }
  73. void BuilderPluginComponent::Deactivate()
  74. {
  75. ImageProcessingRequestBus::Handler::BusDisconnect();
  76. ImageBuilderRequestBus::Handler::BusDisconnect();
  77. m_imageBuilder.BusDisconnect();
  78. BuilderSettingManager::DestroyInstance();
  79. CPixelFormats::DestroyInstance();
  80. }
  81. void BuilderPluginComponent::Reflect(AZ::ReflectContext* context)
  82. {
  83. // components also get Reflect called automatically
  84. // this is your opportunity to perform static reflection or type registration of any types you want the serializer to know about
  85. if (AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context))
  86. {
  87. serialize->Class<BuilderPluginComponent, AZ::Component>()
  88. ->Version(0)
  89. ->Attribute(AZ::Edit::Attributes::SystemComponentTags, AZStd::vector<AZ::Crc32>({ AssetBuilderSDK::ComponentTags::AssetBuilder }))
  90. ;
  91. }
  92. BuilderSettingManager::Reflect(context);
  93. BuilderSettings::Reflect(context);
  94. MultiplatformPresetSettings::Reflect(context);
  95. PresetSettings::Reflect(context);
  96. CubemapSettings::Reflect(context);
  97. MipmapSettings::Reflect(context);
  98. TextureSettings::Reflect(context);
  99. }
  100. void BuilderPluginComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
  101. {
  102. provided.push_back(AZ_CRC("ImagerBuilderPluginService", 0x6dc0db6e));
  103. }
  104. void BuilderPluginComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
  105. {
  106. incompatible.push_back(AZ_CRC("ImagerBuilderPluginService", 0x6dc0db6e));
  107. }
  108. IImageObjectPtr BuilderPluginComponent::LoadImage(const AZStd::string& filePath)
  109. {
  110. return IImageObjectPtr(LoadImageFromFile(filePath));
  111. }
  112. IImageObjectPtr BuilderPluginComponent::LoadImagePreview(const AZStd::string& filePath)
  113. {
  114. IImageObjectPtr image(LoadImageFromFile(filePath));
  115. if (image)
  116. {
  117. ImageToProcess imageToProcess(image);
  118. imageToProcess.ConvertFormat(ePixelFormat_R8G8B8A8);
  119. return imageToProcess.Get();
  120. }
  121. return image;
  122. }
  123. IImageObjectPtr BuilderPluginComponent::CreateImage(
  124. AZ::u32 width,
  125. AZ::u32 height,
  126. AZ::u32 maxMipCount,
  127. EPixelFormat pixelFormat)
  128. {
  129. IImageObjectPtr image(IImageObject::CreateImage(width, height, maxMipCount, pixelFormat));
  130. return image;
  131. }
  132. AZStd::vector<AssetBuilderSDK::JobProduct> BuilderPluginComponent::ConvertImageObject(
  133. IImageObjectPtr imageObject,
  134. const AZStd::string& presetName,
  135. const AZStd::string& platformName,
  136. const AZStd::string& outputDir,
  137. const AZ::Data::AssetId& sourceAssetId,
  138. const AZStd::string& sourceAssetName)
  139. {
  140. AZStd::vector<AssetBuilderSDK::JobProduct> outProducts;
  141. AZStd::string_view presetFilePath;
  142. const PresetSettings* preset = BuilderSettingManager::Instance()->GetPreset(PresetName(presetName), platformName, &presetFilePath);
  143. if (preset == nullptr)
  144. {
  145. AZ_Assert(false, "Cannot find preset with name %s.", presetName.c_str());
  146. return outProducts;
  147. }
  148. AZStd::unique_ptr<ImageConvertProcessDescriptor> desc = AZStd::make_unique<ImageConvertProcessDescriptor>();
  149. TextureSettings& textureSettings = desc->m_textureSetting;
  150. textureSettings.m_preset = preset->m_name;
  151. desc->m_inputImage = imageObject;
  152. desc->m_presetSetting = *preset;
  153. desc->m_isPreview = false;
  154. desc->m_platform = platformName;
  155. desc->m_filePath = presetFilePath;
  156. desc->m_isStreaming = BuilderSettingManager::Instance()->GetBuilderSetting(platformName)->m_enableStreaming;
  157. desc->m_imageName = sourceAssetName;
  158. desc->m_outputFolder = outputDir;
  159. desc->m_sourceAssetId = sourceAssetId;
  160. // Create an image convert process
  161. ImageConvertProcess process(AZStd::move(desc));
  162. process.ProcessAll();
  163. bool result = process.IsSucceed();
  164. if (result)
  165. {
  166. process.GetAppendOutputProducts(outProducts);
  167. }
  168. return outProducts;
  169. }
  170. bool BuilderPluginComponent::DoesSupportPlatform(const AZStd::string& platformId)
  171. {
  172. return ImageProcessingAtom::BuilderSettingManager::Instance()->DoesSupportPlatform(platformId);
  173. }
  174. bool BuilderPluginComponent::IsPresetFormatSquarePow2(const AZStd::string& presetName, const AZStd::string& platformName)
  175. {
  176. AZStd::string_view filePath;
  177. const PresetSettings* preset = BuilderSettingManager::Instance()->GetPreset(PresetName(presetName), platformName, &filePath);
  178. if (preset == nullptr)
  179. {
  180. AZ_Assert(false, "Cannot find preset with name %s.", presetName.c_str());
  181. return false;
  182. }
  183. const PixelFormatInfo* info = CPixelFormats::GetInstance().GetPixelFormatInfo(preset->m_pixelFormat);
  184. return info->bSquarePow2;
  185. }
  186. void ImageBuilderWorker::ShutDown()
  187. {
  188. // it is important to note that this will be called on a different thread than your process job thread
  189. m_isShuttingDown = true;
  190. }
  191. // this happens early on in the file scanning pass
  192. // this function should consistently always create the same jobs, and should do no checking whether the job is up to date or not - just be consistent.
  193. void ImageBuilderWorker::CreateJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response)
  194. {
  195. if (m_isShuttingDown)
  196. {
  197. response.m_result = AssetBuilderSDK::CreateJobsResultCode::ShuttingDown;
  198. return;
  199. }
  200. // Get the extension of the file
  201. AZStd::string ext;
  202. AzFramework::StringFunc::Path::GetExtension(request.m_sourceFile.c_str(), ext, false);
  203. AZStd::to_upper(ext.begin(), ext.end());
  204. // We process the same file for all platforms
  205. for (const AssetBuilderSDK::PlatformInfo& platformInfo : request.m_enabledPlatforms)
  206. {
  207. if (ImageProcessingAtom::BuilderSettingManager::Instance()->DoesSupportPlatform(platformInfo.m_identifier))
  208. {
  209. AssetBuilderSDK::JobDescriptor descriptor;
  210. descriptor.m_jobKey = ext + " Atom Compile";
  211. descriptor.SetPlatformIdentifier(platformInfo.m_identifier.c_str());
  212. descriptor.m_critical = false;
  213. response.m_createJobOutputs.push_back(descriptor);
  214. }
  215. }
  216. response.m_result = AssetBuilderSDK::CreateJobsResultCode::Success;
  217. return;
  218. }
  219. // later on, this function will be called for jobs that actually need doing.
  220. // the request will contain the CreateJobResponse you constructed earlier, including any keys and values you placed into the hash table
  221. void ImageBuilderWorker::ProcessJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response)
  222. {
  223. // Before we begin, let's make sure we are not meant to abort.
  224. AssetBuilderSDK::JobCancelListener jobCancelListener(request.m_jobId);
  225. AZStd::vector<AZStd::string> productFilepaths;
  226. bool imageProcessingSuccessful = false;
  227. bool needConversion = true;
  228. // Do conversion and get exported file's path
  229. if (needConversion)
  230. {
  231. AZ_TracePrintf(AssetBuilderSDK::InfoWindow, "Performing image conversion: %s\n", request.m_fullPath.c_str());
  232. ImageConvertProcess* process = CreateImageConvertProcess(request.m_fullPath, request.m_tempDirPath,
  233. request.m_jobDescription.GetPlatformIdentifier(), response.m_outputProducts);
  234. if (process != nullptr)
  235. {
  236. //the process can be stopped if the job is cancelled or the worker is shutting down
  237. while (!process->IsFinished() && !m_isShuttingDown && !jobCancelListener.IsCancelled())
  238. {
  239. process->UpdateProcess();
  240. }
  241. //get process result
  242. imageProcessingSuccessful = process->IsSucceed();
  243. if (imageProcessingSuccessful)
  244. {
  245. process->GetAppendOutputProducts(response.m_outputProducts);
  246. }
  247. delete process;
  248. }
  249. else
  250. {
  251. imageProcessingSuccessful = false;
  252. }
  253. }
  254. if (imageProcessingSuccessful)
  255. {
  256. response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success;
  257. }
  258. else
  259. {
  260. if (m_isShuttingDown)
  261. {
  262. AZ_TracePrintf(AssetBuilderSDK::ErrorWindow, "Cancelled job %s because shutdown was requested.\n", request.m_fullPath.c_str());
  263. response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Cancelled;
  264. }
  265. else if (jobCancelListener.IsCancelled())
  266. {
  267. AZ_TracePrintf(AssetBuilderSDK::ErrorWindow, "Cancelled was requested for job %s.\n", request.m_fullPath.c_str());
  268. response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Cancelled;
  269. }
  270. else
  271. {
  272. AZ_TracePrintf(AssetBuilderSDK::ErrorWindow, "Unexpected error during processing job %s.\n", request.m_fullPath.c_str());
  273. response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
  274. }
  275. }
  276. }
  277. } // namespace ImageProcessingAtom