InstanceSystemComponent.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592
  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 <VegetationProfiler.h>
  9. #include "InstanceSystemComponent.h"
  10. #include <AzCore/Debug/Profiler.h>
  11. #include <AzCore/Jobs/JobFunction.h>
  12. #include <AzCore/RTTI/BehaviorContext.h>
  13. #include <AzCore/Serialization/EditContext.h>
  14. #include <AzCore/Serialization/SerializeContext.h>
  15. #include <AzCore/std/smart_ptr/make_shared.h>
  16. #include <Vegetation/Ebuses/AreaInfoBus.h>
  17. #include <Vegetation/Ebuses/AreaSystemRequestBus.h>
  18. #include <Vegetation/Ebuses/DebugNotificationBus.h>
  19. #include <Vegetation/Ebuses/DebugSystemDataBus.h>
  20. namespace Vegetation
  21. {
  22. namespace InstanceSystemUtil
  23. {
  24. namespace Constants
  25. {
  26. static const int s_minTaskTimePerTick = 0;
  27. static const int s_maxTaskTimePerTick = 33000; //capping at 33ms presumably to maintain 30fps
  28. static const int s_minTaskBatchSize = 1;
  29. static const int s_maxTaskBatchSize = 2000; //prevents user from reserving excessive space as batches are processed faster than they can be filled
  30. }
  31. };
  32. //////////////////////////////////////////////////////////////////////////
  33. // InstanceSystemConfig
  34. void InstanceSystemConfig::Reflect(AZ::ReflectContext* context)
  35. {
  36. if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
  37. {
  38. serializeContext->Class<InstanceSystemConfig, AZ::ComponentConfig>()
  39. ->Version(3)
  40. ->Field("MaxInstanceProcessTimeMicroseconds", &InstanceSystemConfig::m_maxInstanceProcessTimeMicroseconds)
  41. ->Field("MaxInstanceTaskBatchSize", &InstanceSystemConfig::m_maxInstanceTaskBatchSize)
  42. ;
  43. if (AZ::EditContext* editContext = serializeContext->GetEditContext())
  44. {
  45. editContext->Class<InstanceSystemConfig>(
  46. "Vegetation Instance System", "Manages vegetation instance and render groups")
  47. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  48. ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
  49. ->DataElement(0, &InstanceSystemConfig::m_maxInstanceProcessTimeMicroseconds, "Max Instance Process Time Microseconds", "Maximum number of microseconds allowed for processing instance management tasks each tick")
  50. ->Attribute(AZ::Edit::Attributes::Min, InstanceSystemUtil::Constants::s_minTaskTimePerTick)
  51. ->Attribute(AZ::Edit::Attributes::Max, InstanceSystemUtil::Constants::s_maxTaskTimePerTick)
  52. ->DataElement(0, &InstanceSystemConfig::m_maxInstanceTaskBatchSize, "Max Instance Task Batch Size", "Maximum number of instance management tasks that can be batch processed together")
  53. ->Attribute(AZ::Edit::Attributes::Min, InstanceSystemUtil::Constants::s_minTaskBatchSize)
  54. ->Attribute(AZ::Edit::Attributes::Max, InstanceSystemUtil::Constants::s_maxTaskBatchSize)
  55. ;
  56. }
  57. }
  58. if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
  59. {
  60. behaviorContext->Class<InstanceSystemConfig>()
  61. ->Attribute(AZ::Script::Attributes::Category, "Vegetation")
  62. ->Constructor()
  63. ->Property("maxInstanceProcessTimeMicroseconds", BehaviorValueProperty(&InstanceSystemConfig::m_maxInstanceProcessTimeMicroseconds))
  64. ->Property("maxInstanceTaskBatchSize", BehaviorValueProperty(&InstanceSystemConfig::m_maxInstanceTaskBatchSize))
  65. ;
  66. }
  67. }
  68. //////////////////////////////////////////////////////////////////////////
  69. // InstanceSystemComponent
  70. void InstanceSystemComponent::Reflect(AZ::ReflectContext* context)
  71. {
  72. AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
  73. if (serialize)
  74. {
  75. serialize->Class<InstanceSystemComponent, AZ::Component>()
  76. ->Version(0)
  77. ->Field("Configuration", &InstanceSystemComponent::m_configuration)
  78. ;
  79. if (AZ::EditContext* editContext = serialize->GetEditContext())
  80. {
  81. editContext->Class<InstanceSystemComponent>("Vegetation Instance System", "Manages and processes requests to create and destroy vegetation instance render nodes")
  82. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  83. ->Attribute(AZ::Edit::Attributes::Category, "Vegetation")
  84. ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
  85. ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/")
  86. ->DataElement(0, &InstanceSystemComponent::m_configuration, "Configuration", "")
  87. ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
  88. ;
  89. }
  90. }
  91. }
  92. InstanceSystemComponent::InstanceSystemComponent(const InstanceSystemConfig& configuration)
  93. : m_configuration(configuration)
  94. {
  95. }
  96. InstanceSystemComponent::~InstanceSystemComponent()
  97. {
  98. Cleanup();
  99. }
  100. void InstanceSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services)
  101. {
  102. services.push_back(AZ_CRC_CE("VegetationInstanceSystemService"));
  103. }
  104. void InstanceSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services)
  105. {
  106. services.push_back(AZ_CRC_CE("VegetationInstanceSystemService"));
  107. }
  108. void InstanceSystemComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& services)
  109. {
  110. services.push_back(AZ_CRC_CE("VegetationDebugSystemService"));
  111. }
  112. void InstanceSystemComponent::Activate()
  113. {
  114. Cleanup();
  115. AZ::TickBus::Handler::BusConnect();
  116. InstanceSystemRequestBus::Handler::BusConnect();
  117. InstanceSystemStatsRequestBus::Handler::BusConnect();
  118. SystemConfigurationRequestBus::Handler::BusConnect();
  119. }
  120. void InstanceSystemComponent::Deactivate()
  121. {
  122. AZ::TickBus::Handler::BusDisconnect();
  123. InstanceSystemRequestBus::Handler::BusDisconnect();
  124. InstanceSystemStatsRequestBus::Handler::BusDisconnect();
  125. SystemConfigurationRequestBus::Handler::BusDisconnect();
  126. Cleanup();
  127. }
  128. bool InstanceSystemComponent::ReadInConfig(const AZ::ComponentConfig* baseConfig)
  129. {
  130. if (const auto config = azrtti_cast<const InstanceSystemConfig*>(baseConfig))
  131. {
  132. m_configuration = *config;
  133. return true;
  134. }
  135. return false;
  136. }
  137. bool InstanceSystemComponent::WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const
  138. {
  139. if (auto config = azrtti_cast<InstanceSystemConfig*>(outBaseConfig))
  140. {
  141. *config = m_configuration;
  142. return true;
  143. }
  144. return false;
  145. }
  146. DescriptorPtr InstanceSystemComponent::RegisterUniqueDescriptor(const Descriptor& descriptor)
  147. {
  148. AZ_PROFILE_FUNCTION(Vegetation);
  149. AZStd::lock_guard<decltype(m_uniqueDescriptorsMutex)> lock(m_uniqueDescriptorsMutex);
  150. AZStd::shared_ptr<InstanceSpawner> equivalentInstanceSpawner = descriptor.GetInstanceSpawner();
  151. // Loop through all registered unique descriptors to look for the following:
  152. // 1) Is there an exact match to this descriptor that we can reuse?
  153. // 2) Is there an exact match to the descriptor's instance spawner that we can reuse?
  154. for (auto& descPair : m_uniqueDescriptors)
  155. {
  156. DescriptorPtr existingDescriptorPtr = descPair.first;
  157. if (existingDescriptorPtr)
  158. {
  159. // If the descriptors and their spawners both match, just reuse and return a
  160. // pointer to the existing unique descriptor.
  161. if (*existingDescriptorPtr == descriptor)
  162. {
  163. DescriptorDetails& details = descPair.second;
  164. details.m_refCount++;
  165. return existingDescriptorPtr;
  166. }
  167. // Keep track of any already-existing instance spawners that match the one in
  168. // our new descriptor. If we need to create a new unique descriptor pointer,
  169. // we will at least try to reuse a instance spawner if it exists.
  170. if (descriptor.HasEquivalentInstanceSpawners(*existingDescriptorPtr))
  171. {
  172. equivalentInstanceSpawner = existingDescriptorPtr->GetInstanceSpawner();
  173. }
  174. }
  175. }
  176. // No existing Descriptor was found, so create a new one, but potentially reuse
  177. // an existing InstanceSpawner if one was found.
  178. DescriptorPtr createdDescriptorPtr(new Descriptor(descriptor));
  179. createdDescriptorPtr->SetInstanceSpawner(equivalentInstanceSpawner);
  180. // Notify the descriptor that it's being registered as a new unique descriptor.
  181. createdDescriptorPtr->OnRegisterUniqueDescriptor();
  182. m_uniqueDescriptors[createdDescriptorPtr] = DescriptorDetails();
  183. return createdDescriptorPtr;
  184. }
  185. void InstanceSystemComponent::ReleaseUniqueDescriptor(DescriptorPtr descriptorPtr)
  186. {
  187. AZ_PROFILE_FUNCTION(Vegetation);
  188. AZStd::lock_guard<decltype(m_uniqueDescriptorsMutex)> lock(m_uniqueDescriptorsMutex);
  189. auto descItr = m_uniqueDescriptors.find(descriptorPtr);
  190. if (descItr != m_uniqueDescriptors.end())
  191. {
  192. DescriptorPtr existingDescriptorPtr = descItr->first;
  193. DescriptorDetails& details = descItr->second;
  194. AZ_Assert(details.m_refCount > 0, "Ref count is already 0!");
  195. details.m_refCount--;
  196. if (details.m_refCount <= 0)
  197. {
  198. // Notify the descriptor that it's being released as a unique descriptor.
  199. existingDescriptorPtr->OnReleaseUniqueDescriptor();
  200. //queue entry for garbage collection
  201. m_uniqueDescriptorsToDelete[existingDescriptorPtr] = details;
  202. m_uniqueDescriptors.erase(descItr);
  203. }
  204. }
  205. }
  206. bool InstanceSystemComponent::IsDescriptorValid(DescriptorPtr descriptorPtr) const
  207. {
  208. //only support valid, registered descriptors with loaded meshes
  209. AZStd::lock_guard<decltype(m_uniqueDescriptorsMutex)> lock(m_uniqueDescriptorsMutex);
  210. return descriptorPtr && descriptorPtr->IsSpawnable() && m_uniqueDescriptors.find(descriptorPtr) != m_uniqueDescriptors.end();
  211. }
  212. void InstanceSystemComponent::GarbageCollectUniqueDescriptors()
  213. {
  214. //garbage collect unreferenced descriptors after all other references from all other systems are released
  215. for (auto descItr = m_uniqueDescriptorsToDelete.begin(); descItr != m_uniqueDescriptorsToDelete.end(); )
  216. {
  217. DescriptorPtr descriptorPtr = descItr->first;
  218. const auto remaining = descriptorPtr.use_count();
  219. if (remaining == 2) //one for the container and one for the local
  220. {
  221. descItr = m_uniqueDescriptorsToDelete.erase(descItr);
  222. continue;
  223. }
  224. ++descItr;
  225. }
  226. }
  227. void InstanceSystemComponent::CreateInstance(InstanceData& instanceData)
  228. {
  229. VEGETATION_PROFILE_FUNCTION_VERBOSE
  230. if (!IsDescriptorValid(instanceData.m_descriptorPtr))
  231. {
  232. //Descriptor and mesh must be valid and registered with the system to proceed but it's not an error
  233. //an edit, asset change, or other event could have released descriptors or render groups on this or another thread
  234. //this should result in a composition change and refresh
  235. instanceData.m_instanceId = InvalidInstanceId;
  236. return;
  237. }
  238. //generate new instance id, from pool if entries exist
  239. instanceData.m_instanceId = CreateInstanceId();
  240. if (instanceData.m_instanceId == InvalidInstanceId)
  241. {
  242. return;
  243. }
  244. // Doing this here risks a slighly inaccurate count if the Create*Node functions fail, but I need this to happen on the vegetation thread so the events are recorded in order.
  245. VEG_PROFILE_METHOD(DebugNotificationBus::TryQueueBroadcast(&DebugNotificationBus::Events::CreateInstance, instanceData.m_instanceId, instanceData.m_position, instanceData.m_id));
  246. //queue render node related tasks to process on the main thread
  247. AddTask([this, instanceData]() {
  248. CreateInstanceNode(instanceData);
  249. m_createTaskCount--;
  250. });
  251. m_createTaskCount++;
  252. }
  253. void InstanceSystemComponent::DestroyInstance(InstanceId instanceId)
  254. {
  255. AZ_PROFILE_FUNCTION(Vegetation);
  256. if (instanceId == InvalidInstanceId)
  257. {
  258. return;
  259. }
  260. // do this here so we retain a correct ordering of events based on the vegetation thread.
  261. VEG_PROFILE_METHOD(DebugNotificationBus::TryQueueBroadcast(&DebugNotificationBus::Events::DeleteInstance, instanceId));
  262. //queue render node related tasks to process on the main thread
  263. AddTask([this, instanceId]() {
  264. ReleaseInstanceNode(instanceId);
  265. AZStd::lock_guard<decltype(m_instanceDeletionSetMutex)> instanceDeletionSet(m_instanceDeletionSetMutex);
  266. m_instanceDeletionSet.erase(instanceId);
  267. m_destroyTaskCount--;
  268. });
  269. AZStd::lock_guard<decltype(m_instanceDeletionSetMutex)> instanceDeletionSet(m_instanceDeletionSetMutex);
  270. m_instanceDeletionSet.insert(instanceId);
  271. m_destroyTaskCount++;
  272. }
  273. void InstanceSystemComponent::DestroyAllInstances()
  274. {
  275. VEG_PROFILE_METHOD(DebugNotificationBus::TryQueueBroadcast(&DebugNotificationBus::Events::DeleteAllInstances));
  276. // make sure to clear out the instance work queue
  277. ClearTasks();
  278. // clear all instances
  279. {
  280. AZStd::lock_guard<decltype(m_instanceMapMutex)> scopedLock(m_instanceMapMutex);
  281. for (auto instancePair : m_instanceMap)
  282. {
  283. InstanceId instanceId = instancePair.first;
  284. DescriptorPtr descriptor = instancePair.second.first;
  285. InstancePtr opaqueInstanceData = instancePair.second.second;
  286. if (opaqueInstanceData)
  287. {
  288. descriptor->DestroyInstance(instanceId, opaqueInstanceData);
  289. }
  290. ReleaseInstanceId(instanceId);
  291. }
  292. m_instanceMap.clear();
  293. m_instanceCount = 0;
  294. }
  295. {
  296. AZStd::lock_guard<decltype(m_instanceDeletionSetMutex)> instanceDeletionSet(m_instanceDeletionSetMutex);
  297. m_instanceDeletionSet.clear();
  298. m_destroyTaskCount = 0;
  299. }
  300. }
  301. void InstanceSystemComponent::Cleanup()
  302. {
  303. DestroyAllInstances();
  304. {
  305. AZStd::lock_guard<decltype(m_uniqueDescriptorsMutex)> lock(m_uniqueDescriptorsMutex);
  306. m_uniqueDescriptors.clear();
  307. m_uniqueDescriptorsToDelete.clear();
  308. }
  309. }
  310. AZ::u32 InstanceSystemComponent::GetInstanceCount() const
  311. {
  312. return m_instanceCount;
  313. }
  314. AZ::u32 InstanceSystemComponent::GetTotalTaskCount() const
  315. {
  316. return m_createTaskCount + m_destroyTaskCount;
  317. }
  318. AZ::u32 InstanceSystemComponent::GetCreateTaskCount() const
  319. {
  320. return m_createTaskCount;
  321. }
  322. AZ::u32 InstanceSystemComponent::GetDestroyTaskCount() const
  323. {
  324. return m_destroyTaskCount;
  325. }
  326. void InstanceSystemComponent::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
  327. {
  328. if (HasTasks())
  329. {
  330. ProcessMainThreadTasks();
  331. }
  332. GarbageCollectUniqueDescriptors();
  333. }
  334. void InstanceSystemComponent::UpdateSystemConfig(const AZ::ComponentConfig* baseConfig)
  335. {
  336. ReadInConfig(baseConfig);
  337. }
  338. void InstanceSystemComponent::GetSystemConfig(AZ::ComponentConfig* outBaseConfig) const
  339. {
  340. WriteOutConfig(outBaseConfig);
  341. }
  342. InstanceId InstanceSystemComponent::CreateInstanceId()
  343. {
  344. AZStd::lock_guard<decltype(m_instanceIdMutex)> scopedLock(m_instanceIdMutex);
  345. //recycle a previously used id from the pool/free-list before generating a new one
  346. if (!m_instanceIdPool.empty())
  347. {
  348. auto instanceIdItr = m_instanceIdPool.begin();
  349. InstanceId instanceId = *instanceIdItr;
  350. m_instanceIdPool.erase(instanceIdItr);
  351. return instanceId;
  352. }
  353. //if all ids have been used, no more can be created until the counter is reset
  354. if (m_instanceIdCounter >= MaxInstanceId)
  355. {
  356. AZ_Error("vegetation", false, "MaxInstanceId reached! No more instance ids can be created until some are released!");
  357. return InvalidInstanceId;
  358. }
  359. return m_instanceIdCounter++;
  360. }
  361. void InstanceSystemComponent::ReleaseInstanceId(InstanceId instanceId)
  362. {
  363. AZStd::lock_guard<decltype(m_instanceIdMutex)> scopedLock(m_instanceIdMutex);
  364. //add released ids to the free list for recycling
  365. m_instanceIdPool.insert(instanceId);
  366. }
  367. bool InstanceSystemComponent::IsInstanceSkippable(const InstanceData& instanceData) const
  368. {
  369. VEGETATION_PROFILE_FUNCTION_VERBOSE
  370. //if the instance was queued for deletion before its creation task executed then skip it
  371. AZStd::lock_guard<decltype(m_instanceDeletionSetMutex)> instanceDeletionSet(m_instanceDeletionSetMutex);
  372. return instanceData.m_instanceId == InvalidInstanceId || m_instanceDeletionSet.find(instanceData.m_instanceId) != m_instanceDeletionSet.end();
  373. }
  374. void InstanceSystemComponent::CreateInstanceNode(const InstanceData& instanceData)
  375. {
  376. VEGETATION_PROFILE_FUNCTION_VERBOSE
  377. if (IsInstanceSkippable(instanceData))
  378. {
  379. return;
  380. }
  381. // Only support valid, registered descriptors with loaded assets
  382. if (!instanceData.m_descriptorPtr || !instanceData.m_descriptorPtr->IsLoaded())
  383. {
  384. //descriptor and mesh must be valid but it's not an error
  385. //an edit, asset change, or other event could have released descriptors or render groups on this or another thread
  386. //this should result in a composition change and refresh
  387. return;
  388. }
  389. {
  390. AZStd::lock_guard<decltype(m_uniqueDescriptorsMutex)> lock(m_uniqueDescriptorsMutex);
  391. auto descItr = m_uniqueDescriptors.find(instanceData.m_descriptorPtr);
  392. if (descItr == m_uniqueDescriptors.end())
  393. {
  394. //descriptor must be registered with the system to create an instance.
  395. //it could have been removed or re-added while editing or deleting entities that control the registration
  396. return;
  397. }
  398. }
  399. InstancePtr opaqueInstanceData = instanceData.m_descriptorPtr->CreateInstance(instanceData);
  400. if (opaqueInstanceData)
  401. {
  402. AZStd::lock_guard<decltype(m_instanceMapMutex)> scopedLock(m_instanceMapMutex);
  403. AZ_Assert(m_instanceMap.find(instanceData.m_instanceId) == m_instanceMap.end(), "InstanceId %llu is already in use!", instanceData.m_instanceId);
  404. m_instanceMap[instanceData.m_instanceId] = AZStd::make_pair(instanceData.m_descriptorPtr, opaqueInstanceData);
  405. m_instanceCount = static_cast<int>(m_instanceMap.size());
  406. }
  407. }
  408. void InstanceSystemComponent::ReleaseInstanceNode(InstanceId instanceId)
  409. {
  410. AZ_PROFILE_FUNCTION(Vegetation);
  411. DescriptorPtr descriptor = nullptr;
  412. InstancePtr opaqueInstanceData = nullptr;
  413. {
  414. AZStd::lock_guard<decltype(m_instanceMapMutex)> scopedLock(m_instanceMapMutex);
  415. auto instanceItr = m_instanceMap.find(instanceId);
  416. if (instanceItr != m_instanceMap.end())
  417. {
  418. descriptor = instanceItr->second.first;
  419. opaqueInstanceData = instanceItr->second.second;
  420. m_instanceMap.erase(instanceItr);
  421. }
  422. m_instanceCount = static_cast<int>(m_instanceMap.size());
  423. }
  424. if (opaqueInstanceData)
  425. {
  426. descriptor->DestroyInstance(instanceId, opaqueInstanceData);
  427. }
  428. ReleaseInstanceId(instanceId);
  429. }
  430. bool InstanceSystemComponent::HasTasks() const
  431. {
  432. AZStd::lock_guard<decltype(m_mainThreadTaskMutex)> mainThreadTaskLock(m_mainThreadTaskMutex);
  433. return !m_mainThreadTaskQueue.empty();
  434. }
  435. void InstanceSystemComponent::AddTask(const Task& task)
  436. {
  437. VEGETATION_PROFILE_FUNCTION_VERBOSE
  438. AZStd::lock_guard<decltype(m_mainThreadTaskMutex)> mainThreadTaskLock(m_mainThreadTaskMutex);
  439. if (m_mainThreadTaskQueue.empty() || m_mainThreadTaskQueue.back().size() >= m_configuration.m_maxInstanceTaskBatchSize)
  440. {
  441. m_mainThreadTaskQueue.emplace_back().reserve(m_configuration.m_maxInstanceTaskBatchSize);
  442. }
  443. m_mainThreadTaskQueue.back().emplace_back(task);
  444. }
  445. void InstanceSystemComponent::ClearTasks()
  446. {
  447. AZ_PROFILE_FUNCTION(Vegetation);
  448. AZStd::lock_guard<decltype(m_mainThreadTaskInProgressMutex)> mainThreadTaskInProgressLock(m_mainThreadTaskInProgressMutex);
  449. AZStd::lock_guard<decltype(m_mainThreadTaskMutex)> mainThreadTaskLock(m_mainThreadTaskMutex);
  450. m_mainThreadTaskQueue.clear();
  451. m_createTaskCount = 0;
  452. m_destroyTaskCount = 0;
  453. }
  454. bool InstanceSystemComponent::GetTasks(TaskList& removedTasks)
  455. {
  456. AZ_PROFILE_FUNCTION(Vegetation);
  457. AZStd::lock_guard<decltype(m_mainThreadTaskMutex)> mainThreadTaskLock(m_mainThreadTaskMutex);
  458. if (!m_mainThreadTaskQueue.empty())
  459. {
  460. removedTasks.splice(removedTasks.end(), m_mainThreadTaskQueue, m_mainThreadTaskQueue.begin());
  461. return true;
  462. }
  463. return false;
  464. }
  465. void InstanceSystemComponent::ExecuteTasks()
  466. {
  467. AZ_PROFILE_FUNCTION(Vegetation);
  468. AZStd::lock_guard<decltype(m_mainThreadTaskInProgressMutex)> scopedLock(m_mainThreadTaskInProgressMutex);
  469. AZStd::chrono::steady_clock::time_point initialTime = AZStd::chrono::steady_clock::now();
  470. AZStd::chrono::steady_clock::time_point currentTime = initialTime;
  471. auto removedTasksPtr = AZStd::make_shared<TaskList>();
  472. while (GetTasks(*removedTasksPtr))
  473. {
  474. for (const auto& task : (*removedTasksPtr).back())
  475. {
  476. task();
  477. }
  478. currentTime = AZStd::chrono::steady_clock::now();
  479. if (AZStd::chrono::duration_cast<AZStd::chrono::microseconds>(currentTime - initialTime).count() > m_configuration.m_maxInstanceProcessTimeMicroseconds)
  480. {
  481. break;
  482. }
  483. }
  484. //offloading garbage collection to job to save time deallocating tasks on main thread
  485. auto garbageCollectionJob = AZ::CreateJobFunction([]() mutable {}, true);
  486. garbageCollectionJob->Start();
  487. }
  488. void InstanceSystemComponent::ProcessMainThreadTasks()
  489. {
  490. AZ_PROFILE_FUNCTION(Vegetation);
  491. ExecuteTasks();
  492. }
  493. }