ImageBasedLightComponentController.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  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 <ImageBasedLights/ImageBasedLightComponentController.h>
  9. #include <AtomLyIntegration/CommonFeatures/ImageBasedLights/ImageBasedLightComponentConstants.h>
  10. #include <AzCore/RTTI/BehaviorContext.h>
  11. #include <AzCore/Serialization/SerializeContext.h>
  12. #include <Atom/RPI.Public/Scene.h>
  13. #include <Atom/Utils/Utils.h>
  14. namespace AZ
  15. {
  16. namespace Render
  17. {
  18. void ImageBasedLightComponentController::Reflect(ReflectContext* context)
  19. {
  20. ImageBasedLightComponentConfig::Reflect(context);
  21. if (SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context))
  22. {
  23. serializeContext->Class<ImageBasedLightComponentController>()
  24. ->Version(0)
  25. ->Field("Configuration", &ImageBasedLightComponentController::m_configuration)
  26. ;
  27. }
  28. if (BehaviorContext* behaviorContext = azrtti_cast<BehaviorContext*>(context))
  29. {
  30. behaviorContext->EBus<ImageBasedLightComponentRequestBus>("ImageBasedLightComponentRequestBus")
  31. ->Event("SetSpecularImageAssetId", &ImageBasedLightComponentRequestBus::Events::SetSpecularImageAssetId)
  32. ->Event("GetSpecularImageAssetId", &ImageBasedLightComponentRequestBus::Events::GetSpecularImageAssetId)
  33. ->Event("SetDiffuseImageAssetId", &ImageBasedLightComponentRequestBus::Events::SetDiffuseImageAssetId)
  34. ->Event("GetDiffuseImageAssetId", &ImageBasedLightComponentRequestBus::Events::GetDiffuseImageAssetId)
  35. ->Event("SetSpecularImageAssetPath", &ImageBasedLightComponentRequestBus::Events::SetSpecularImageAssetPath)
  36. ->Event("GetSpecularImageAssetPath", &ImageBasedLightComponentRequestBus::Events::GetSpecularImageAssetPath)
  37. ->Event("SetDiffuseImageAssetPath", &ImageBasedLightComponentRequestBus::Events::SetDiffuseImageAssetPath)
  38. ->Event("GetDiffuseImageAssetPath", &ImageBasedLightComponentRequestBus::Events::GetDiffuseImageAssetPath)
  39. ->VirtualProperty("SpecularImageAssetId", "GetSpecularImageAssetId", "SetSpecularImageAssetId")
  40. ->VirtualProperty("DiffuseImageAssetId", "GetDiffuseImageAssetId", "SetDiffuseImageAssetId")
  41. ->VirtualProperty("SpecularImageAssetPath", "GetSpecularImageAssetPath", "SetSpecularImageAssetPath")
  42. ->VirtualProperty("DiffuseImageAssetPath", "GetDiffuseImageAssetPath", "SetDiffuseImageAssetPath")
  43. ;
  44. }
  45. }
  46. void ImageBasedLightComponentController::GetProvidedServices(ComponentDescriptor::DependencyArrayType& provided)
  47. {
  48. provided.push_back(AZ_CRC_CE("ImageBasedLightService"));
  49. }
  50. void ImageBasedLightComponentController::GetIncompatibleServices(ComponentDescriptor::DependencyArrayType& incompatible)
  51. {
  52. incompatible.push_back(AZ_CRC_CE("ImageBasedLightService"));
  53. }
  54. ImageBasedLightComponentController::ImageBasedLightComponentController(const ImageBasedLightComponentConfig& config)
  55. : m_configuration(config)
  56. {
  57. }
  58. void ImageBasedLightComponentController::Activate(EntityId entityId)
  59. {
  60. m_entityId = entityId;
  61. m_featureProcessor = RPI::Scene::GetFeatureProcessorForEntity<ImageBasedLightFeatureProcessorInterface>(m_entityId);
  62. AZ_Error("ImageBasedLightComponentController", m_featureProcessor, "Unable to find a ImageBasedLightFeatureProcessorInterface on this entity's scene.");
  63. if (m_featureProcessor)
  64. {
  65. LoadImage(m_configuration.m_specularImageAsset);
  66. LoadImage(m_configuration.m_diffuseImageAsset);
  67. m_featureProcessor->SetExposure(m_configuration.m_exposure);
  68. TransformInterface* transformInterface = TransformBus::FindFirstHandler(m_entityId);
  69. AZ_Assert(transformInterface, "Unable to attach to a TransformBus handler. Entity transform will not affect IBL.");
  70. const AZ::Transform& transform = transformInterface ? transformInterface->GetWorldTM() : Transform::Identity();
  71. m_featureProcessor->SetOrientation(transform.GetRotation());
  72. TransformNotificationBus::Handler::BusConnect(m_entityId);
  73. ImageBasedLightComponentRequestBus::Handler::BusConnect(m_entityId);
  74. transformInterface = nullptr;
  75. }
  76. }
  77. void ImageBasedLightComponentController::Deactivate()
  78. {
  79. ImageBasedLightComponentRequestBus::Handler::BusDisconnect();
  80. TransformNotificationBus::Handler::BusDisconnect();
  81. ReleaseImages();
  82. if (m_featureProcessor)
  83. {
  84. m_featureProcessor->Reset();
  85. m_featureProcessor = nullptr;
  86. }
  87. m_entityId = EntityId(EntityId::InvalidEntityId);
  88. }
  89. void ImageBasedLightComponentController::SetConfiguration(const ImageBasedLightComponentConfig& config)
  90. {
  91. m_configuration = config;
  92. }
  93. const ImageBasedLightComponentConfig& ImageBasedLightComponentController::GetConfiguration() const
  94. {
  95. return m_configuration;
  96. }
  97. void ImageBasedLightComponentController::OnAssetReady(Data::Asset<Data::AssetData> asset)
  98. {
  99. UpdateWithAsset(asset);
  100. }
  101. void ImageBasedLightComponentController::OnAssetReloaded(Data::Asset<Data::AssetData> asset)
  102. {
  103. UpdateWithAsset(asset);
  104. }
  105. void ImageBasedLightComponentController::OnAssetError(Data::Asset<Data::AssetData> asset)
  106. {
  107. UpdateWithAsset(asset);
  108. }
  109. void ImageBasedLightComponentController::UpdateWithAsset(Data::Asset<Data::AssetData> updatedAsset)
  110. {
  111. // REMARK: This function is typically invoked within the context of one of the AssetBus::OnAssetXXX functions,
  112. // and a deadlock may occur according to the following sequence:
  113. // 1. Starting from Main thread, AssetBus locks a mutex.
  114. // 2. AssetBus calls OnAssetReady and it enters in this function.
  115. // 3. Start the instantiation of a new StreamingImage.
  116. // 4. StreamingImage asynchronously queues work in the "Seconday Copy Queue".
  117. // 5. StreamingImage waits until the work completes.
  118. // 6. The thread of "Seconday Copy Queue" gets a new work item, which may hold a reference
  119. // to an old StreamingImage.
  120. // 7. The old StreamingImage gets destroyed and it calls AssetBus::MultiHandler::BusDisconnect(GetAssetId());
  121. // 8. When calling AssetBus::MultiHandler::BusDisconnect(GetAssetId()); it tries to lock the same mutex
  122. // from step 1. But the mutex is already locked on Main Thread in step 1.
  123. // 9. The "Seconday Copy Queue" thread deadlocks and never completes the work.
  124. // 10. Main thread is also deadlocked waiting for "Seconday Copy Queue" to complete.
  125. // The solution is to enqueue texture update on the next tick.
  126. auto postTickLambda = [=]()
  127. {
  128. if (m_configuration.m_specularImageAsset.GetId() == updatedAsset.GetId())
  129. {
  130. if (m_featureProcessor && HandleAssetUpdate(updatedAsset, m_configuration.m_specularImageAsset))
  131. {
  132. m_featureProcessor->SetSpecularImage(m_configuration.m_specularImageAsset);
  133. ImageBasedLightComponentNotificationBus::Event(
  134. m_entityId, &ImageBasedLightComponentNotifications::OnSpecularImageUpdated);
  135. }
  136. }
  137. else if (m_configuration.m_diffuseImageAsset.GetId() == updatedAsset.GetId())
  138. {
  139. if (m_featureProcessor && HandleAssetUpdate(updatedAsset, m_configuration.m_diffuseImageAsset))
  140. {
  141. m_featureProcessor->SetDiffuseImage(m_configuration.m_diffuseImageAsset);
  142. ImageBasedLightComponentNotificationBus::Event(
  143. m_entityId, &ImageBasedLightComponentNotifications::OnDiffuseImageUpdated);
  144. }
  145. }
  146. };
  147. AZ::TickBus::QueueFunction(AZStd::move(postTickLambda));
  148. }
  149. bool ImageBasedLightComponentController::HandleAssetUpdate(Data::Asset<Data::AssetData> updatedAsset, Data::Asset<RPI::StreamingImageAsset>& configAsset)
  150. {
  151. configAsset = updatedAsset;
  152. if (updatedAsset.IsReady())
  153. {
  154. auto& descriptor = configAsset->GetImageDescriptor();
  155. bool isCubemap = descriptor.m_isCubemap || descriptor.m_arraySize == 6;
  156. if (isCubemap)
  157. {
  158. return true;
  159. }
  160. }
  161. return false;
  162. }
  163. void ImageBasedLightComponentController::SetSpecularImageAsset(const Data::Asset<RPI::StreamingImageAsset>& imageAsset)
  164. {
  165. Data::AssetBus::MultiHandler::BusDisconnect(m_configuration.m_specularImageAsset.GetId());
  166. m_configuration.m_specularImageAsset = imageAsset;
  167. if (imageAsset.GetId().IsValid())
  168. {
  169. LoadImage(m_configuration.m_specularImageAsset);
  170. }
  171. else if (m_featureProcessor)
  172. {
  173. // Clear out current image asset
  174. m_featureProcessor->SetSpecularImage(m_configuration.m_specularImageAsset);
  175. }
  176. }
  177. void ImageBasedLightComponentController::SetDiffuseImageAsset(const Data::Asset<RPI::StreamingImageAsset>& imageAsset)
  178. {
  179. Data::AssetBus::MultiHandler::BusDisconnect(m_configuration.m_diffuseImageAsset.GetId());
  180. m_configuration.m_diffuseImageAsset = imageAsset;
  181. if (imageAsset.GetId().IsValid())
  182. {
  183. LoadImage(m_configuration.m_diffuseImageAsset);
  184. }
  185. else if (m_featureProcessor)
  186. {
  187. // Clear out current image asset
  188. m_featureProcessor->SetDiffuseImage(m_configuration.m_diffuseImageAsset);
  189. }
  190. }
  191. Data::Asset<RPI::StreamingImageAsset> ImageBasedLightComponentController::GetSpecularImageAsset() const
  192. {
  193. return m_configuration.m_specularImageAsset;
  194. }
  195. Data::Asset<RPI::StreamingImageAsset> ImageBasedLightComponentController::GetDiffuseImageAsset() const
  196. {
  197. return m_configuration.m_diffuseImageAsset;
  198. }
  199. void ImageBasedLightComponentController::SetSpecularImageAssetId(const Data::AssetId imageAssetId)
  200. {
  201. SetSpecularImageAsset(GetAssetFromId<RPI::StreamingImageAsset>(imageAssetId, m_configuration.m_specularImageAsset.GetAutoLoadBehavior()));
  202. }
  203. void ImageBasedLightComponentController::SetDiffuseImageAssetId(const Data::AssetId imageAssetId)
  204. {
  205. SetDiffuseImageAsset(GetAssetFromId<RPI::StreamingImageAsset>(imageAssetId, m_configuration.m_diffuseImageAsset.GetAutoLoadBehavior()));
  206. }
  207. void ImageBasedLightComponentController::SetSpecularImageAssetPath(const AZStd::string path)
  208. {
  209. SetSpecularImageAsset(GetAssetFromPath<RPI::StreamingImageAsset>(path, m_configuration.m_specularImageAsset.GetAutoLoadBehavior()));
  210. }
  211. void ImageBasedLightComponentController::SetDiffuseImageAssetPath(const AZStd::string path)
  212. {
  213. SetDiffuseImageAsset(GetAssetFromPath<RPI::StreamingImageAsset>(path, m_configuration.m_diffuseImageAsset.GetAutoLoadBehavior()));
  214. }
  215. AZStd::string ImageBasedLightComponentController::GetSpecularImageAssetPath() const
  216. {
  217. AZStd::string assetPathString;
  218. Data::AssetCatalogRequestBus::BroadcastResult(assetPathString, &Data::AssetCatalogRequests::GetAssetPathById, m_configuration.m_specularImageAsset.GetId());
  219. return assetPathString;
  220. }
  221. AZStd::string ImageBasedLightComponentController::GetDiffuseImageAssetPath() const
  222. {
  223. AZStd::string assetPathString;
  224. Data::AssetCatalogRequestBus::BroadcastResult(assetPathString, &Data::AssetCatalogRequests::GetAssetPathById, m_configuration.m_diffuseImageAsset.GetId());
  225. return assetPathString;
  226. }
  227. Data::AssetId ImageBasedLightComponentController::GetSpecularImageAssetId() const
  228. {
  229. return m_configuration.m_specularImageAsset.GetId();
  230. }
  231. Data::AssetId ImageBasedLightComponentController::GetDiffuseImageAssetId() const
  232. {
  233. return m_configuration.m_diffuseImageAsset.GetId();
  234. }
  235. void ImageBasedLightComponentController::SetExposure(float exposure)
  236. {
  237. m_configuration.m_exposure = exposure;
  238. if (m_featureProcessor)
  239. {
  240. m_featureProcessor->SetExposure(exposure);
  241. }
  242. }
  243. float ImageBasedLightComponentController::GetExposure() const
  244. {
  245. return m_configuration.m_exposure;
  246. }
  247. void ImageBasedLightComponentController::OnTransformChanged([[maybe_unused]] const AZ::Transform& local, const AZ::Transform& world)
  248. {
  249. const Quaternion& rotation = world.GetRotation();
  250. if (m_featureProcessor)
  251. {
  252. m_featureProcessor->SetOrientation(rotation);
  253. }
  254. }
  255. void ImageBasedLightComponentController::LoadImage(Data::Asset<RPI::StreamingImageAsset>& imageAsset)
  256. {
  257. Data::AssetBus::MultiHandler::BusDisconnect(imageAsset.GetId());
  258. if (imageAsset.GetId().IsValid())
  259. {
  260. // If the asset is already loaded it'll call OnAssetReady() immediately on BusConnect().
  261. Data::AssetBus::MultiHandler::BusConnect(imageAsset.GetId());
  262. imageAsset.QueueLoad();
  263. }
  264. else
  265. {
  266. // Call update for invalid assets so current assets can be cleared if necessary.
  267. UpdateWithAsset(imageAsset);
  268. }
  269. }
  270. void ImageBasedLightComponentController::ReleaseImages()
  271. {
  272. Data::AssetBus::MultiHandler::BusDisconnect();
  273. m_configuration.m_specularImageAsset.Release();
  274. m_configuration.m_diffuseImageAsset.Release();
  275. }
  276. } // namespace Render
  277. } // namespace AZ