ModelReloader.cpp 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  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 <Source/Mesh/ModelReloader.h>
  9. #include <Atom/RPI.Reflect/Model/ModelAsset.h>
  10. #include <Atom/RPI.Public/Model/Model.h>
  11. namespace AZ
  12. {
  13. namespace Render
  14. {
  15. ModelReloader::ModelReloader(
  16. Data::Asset<RPI::ModelAsset> modelAsset)
  17. {
  18. m_modelAsset.push_back(modelAsset);
  19. m_pendingDependencyListStatus.reset();
  20. // Iterate over the model and track the assets that need to be reloaded
  21. for (auto& modelLodAsset : modelAsset->GetLodAssets())
  22. {
  23. for (auto& mesh : modelLodAsset->GetMeshes())
  24. {
  25. for (auto& streamBufferInfo : mesh.GetStreamBufferInfoList())
  26. {
  27. InsertMeshDependencyIfUnique(streamBufferInfo.m_bufferAssetView.GetBufferAsset());
  28. }
  29. InsertMeshDependencyIfUnique(mesh.GetIndexBufferAssetView().GetBufferAsset());
  30. }
  31. m_modelDependencies.push_back(modelLodAsset);
  32. }
  33. AZ_Assert(
  34. m_meshDependencies.size() <= m_pendingDependencyListStatus.size(),
  35. "There are more buffers used by the model %s than are supported by the ModelReloader.", modelAsset.GetHint().c_str());
  36. m_state = State::WaitingForMeshDependencies;
  37. ReloadDependenciesAndWait();
  38. }
  39. void ModelReloader::ConnectOnReloadedEventHandler(ModelReloadedEvent::Handler& onReloadedEventHandler)
  40. {
  41. onReloadedEventHandler.Connect(m_onModelReloaded);
  42. }
  43. void ModelReloader::OnAssetReloaded(AZ::Data::Asset<AZ::Data::AssetData> asset)
  44. {
  45. DependencyList& pendingDependencies = GetPendingDependencyList();
  46. const Data::AssetId& reloadedAssetId = asset.GetId();
  47. // Find the index of the asset that was reloaded
  48. const auto matchesId = [reloadedAssetId](const Data::Asset<Data::AssetData>& asset){ return asset.GetId() == reloadedAssetId;};
  49. const auto& iter = AZStd::find_if(AZStd::begin(pendingDependencies), AZStd::end(pendingDependencies), matchesId);
  50. AZ_Assert(
  51. iter != AZStd::end(pendingDependencies),
  52. "ModelReloader - handling an AssetReloaded event for an asset that is not part of the dependency list.");
  53. size_t currentIndex = AZStd::distance(AZStd::begin(pendingDependencies), iter);
  54. // Keep a reference to the newly reloaded asset to prevent it from being immediately released
  55. pendingDependencies[currentIndex] = asset;
  56. Data::AssetBus::MultiHandler::BusDisconnect(reloadedAssetId);
  57. // Clear the bit, now that it has been reloaded
  58. m_pendingDependencyListStatus.reset(currentIndex);
  59. if (m_pendingDependencyListStatus.none())
  60. {
  61. AdvanceToNextLevelOfHierarchy();
  62. }
  63. }
  64. void ModelReloader::OnAssetReloadError(Data::Asset<Data::AssetData> asset)
  65. {
  66. // An error is actually okay/expected in some situations.
  67. // For example, if the 2nd UV set was removed, and we tried to reload the second uv set, the reload would fail.
  68. // We want to treat it as a success, and mark that dependency as 'up to date'
  69. OnAssetReloaded(asset);
  70. }
  71. void ModelReloader::InsertMeshDependencyIfUnique(Data::Asset<Data::AssetData> asset)
  72. {
  73. if (AZStd::find(AZStd::begin(m_meshDependencies), AZStd::end(m_meshDependencies), asset) == AZStd::end(m_meshDependencies))
  74. {
  75. // Multiple meshes may reference the same buffer, so only add the dependency if it is unique
  76. m_meshDependencies.push_back(asset);
  77. }
  78. }
  79. void ModelReloader::ReloadDependenciesAndWait()
  80. {
  81. // Get the current list of dependencies depending on the current state
  82. DependencyList& dependencies = GetPendingDependencyList();
  83. if (!m_pendingDependencyListStatus.none())
  84. {
  85. AZ_Assert(
  86. m_pendingDependencyListStatus.none(),
  87. "ModelReloader attempting to add new dependencies while still waiting for other dependencies in the hierarchy to "
  88. "load.");
  89. }
  90. if (dependencies.empty())
  91. {
  92. // If the original model asset failed to load, it won't have any dependencies to reload
  93. AdvanceToNextLevelOfHierarchy();
  94. }
  95. AZ_Assert(
  96. dependencies.size() <= m_pendingDependencyListStatus.size(),
  97. "ModelReloader has more dependencies than can fit in the bitset. The size of m_pendingDependencyListStatus needs to be increased.");
  98. // Set all bits to 1
  99. m_pendingDependencyListStatus.set();
  100. // Clear the least significant n-bits
  101. m_pendingDependencyListStatus <<= dependencies.size();
  102. // Set the least significant n-bits to 1, and the rest to 0
  103. m_pendingDependencyListStatus.flip();
  104. // Reload all the assets
  105. for (Data::Asset<Data::AssetData>& dependencyAsset : dependencies)
  106. {
  107. Data::AssetBus::MultiHandler::BusConnect(dependencyAsset.GetId());
  108. dependencyAsset.Reload();
  109. }
  110. }
  111. void ModelReloader::AdvanceToNextLevelOfHierarchy()
  112. {
  113. switch (m_state)
  114. {
  115. case State::WaitingForMeshDependencies:
  116. m_state = State::WaitingForModelDependencies;
  117. ReloadDependenciesAndWait();
  118. break;
  119. case State::WaitingForModelDependencies:
  120. m_state = State::WaitingForModel;
  121. ReloadDependenciesAndWait();
  122. break;
  123. case State::WaitingForModel:
  124. Data::AssetBus::MultiHandler::BusDisconnect();
  125. // Since the model asset is finished reloading, orphan model from the instance database
  126. // so that all of the buffer instances are re-created with the latest data
  127. RPI::Model::TEMPOrphanFromDatabase(m_modelAsset.front());
  128. // Signal that the model is ready
  129. m_onModelReloaded.Signal(m_modelAsset.front());
  130. // Remove this reloader from the ModelReloaderSystem
  131. ModelReloaderSystemInterface::Get()->RemoveReloader(m_modelAsset.front().GetId());
  132. delete this;
  133. break;
  134. }
  135. }
  136. ModelReloader::DependencyList& ModelReloader::GetPendingDependencyList()
  137. {
  138. switch (m_state)
  139. {
  140. case State::WaitingForMeshDependencies:
  141. return m_meshDependencies;
  142. break;
  143. case State::WaitingForModelDependencies:
  144. return m_modelDependencies;
  145. break;
  146. case State::WaitingForModel:
  147. default:
  148. return m_modelAsset;
  149. break;
  150. }
  151. }
  152. } // namespace Render
  153. } // namespace AZ