3
0

SystemComponent.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511
  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 <iostream>
  9. #include <AzCore/Component/EntityUtils.h>
  10. #include <AzCore/Console/IConsole.h>
  11. #include <AzCore/Serialization/EditContext.h>
  12. #include <AzCore/Serialization/Json/RegistrationContext.h>
  13. #include <AzCore/Serialization/SerializeContext.h>
  14. #include <AzCore/Serialization/Utils.h>
  15. #include <Libraries/Libraries.h>
  16. #include <ScriptCanvas/Asset/RuntimeAsset.h>
  17. #include <ScriptCanvas/Core/Contract.h>
  18. #include <ScriptCanvas/Core/Graph.h>
  19. #include <ScriptCanvas/Core/Node.h>
  20. #include <ScriptCanvas/Core/Nodeable.h>
  21. #include <ScriptCanvas/Core/Slot.h>
  22. #include <ScriptCanvas/Execution/ExecutionPerformanceTimer.h>
  23. #include <ScriptCanvas/Execution/Interpreted/ExecutionInterpretedAPI.h>
  24. #include <ScriptCanvas/Execution/RuntimeComponent.h>
  25. #include <ScriptCanvas/Serialization/DatumSerializer.h>
  26. #include <ScriptCanvas/Serialization/BehaviorContextObjectSerializer.h>
  27. #include <ScriptCanvas/Serialization/RuntimeVariableSerializer.h>
  28. #include <ScriptCanvas/SystemComponent.h>
  29. #include <ScriptCanvas/Variable/GraphVariableManagerComponent.h>
  30. #include <ScriptCanvas/Core/Contracts/MathOperatorContract.h>
  31. #if defined(SC_EXECUTION_TRACE_ENABLED)
  32. #include <ScriptCanvas/Asset/ExecutionLogAsset.h>
  33. #endif
  34. #include <AutoGenDataRegistry.generated.h>
  35. #include <AutoGenFunctionRegistry.generated.h>
  36. #include <AutoGenNodeableRegistry.generated.h>
  37. #include <AutoGenGrammarRegistry.generated.h>
  38. REGISTER_SCRIPTCANVAS_AUTOGEN_DATA(ScriptCanvasStatic);
  39. REGISTER_SCRIPTCANVAS_AUTOGEN_FUNCTION(ScriptCanvasStatic);
  40. REGISTER_SCRIPTCANVAS_AUTOGEN_NODEABLE(ScriptCanvasStatic);
  41. REGISTER_SCRIPTCANVAS_AUTOGEN_GRAMMAR(ScriptCanvasStatic);
  42. namespace ScriptCanvasSystemComponentCpp
  43. {
  44. #if !defined(_RELEASE)
  45. const int k_infiniteLoopDetectionMaxIterations = 1000000;
  46. const int k_maxHandlerStackDepth = 25;
  47. #else
  48. const int k_infiniteLoopDetectionMaxIterations = 10000000;
  49. const int k_maxHandlerStackDepth = 100;
  50. #endif
  51. bool IsDeprecated(const AZ::AttributeArray& attributes)
  52. {
  53. bool isDeprecated{};
  54. if (auto isDeprecatedAttributePtr = AZ::FindAttribute(AZ::Script::Attributes::Deprecated, attributes))
  55. {
  56. AZ::AttributeReader(nullptr, isDeprecatedAttributePtr).Read<bool>(isDeprecated);
  57. }
  58. return isDeprecated;
  59. }
  60. }
  61. namespace ScriptCanvas
  62. {
  63. AZ_ENUM_CLASS(PerformanceReportFileStream,
  64. None,
  65. Stdout,
  66. Stderr
  67. );
  68. }
  69. namespace ScriptCanvas
  70. {
  71. // Console Variable to determine where the scriptcanvas output performance report is sent
  72. AZ_CVAR(PerformanceReportFileStream, sc_outputperformancereport, PerformanceReportFileStream::None, {}, AZ::ConsoleFunctorFlags::Null,
  73. "Determines where the Script Canvas performance report should be output.");
  74. void SystemComponent::Reflect(AZ::ReflectContext* context)
  75. {
  76. ScriptCanvas::AutoGenRegistryManager::Reflect(context);
  77. VersionData::Reflect(context);
  78. Nodeable::Reflect(context);
  79. SourceHandle::Reflect(context);
  80. ReflectLibraries(context);
  81. if (AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context))
  82. {
  83. serialize->Class<SystemComponent, AZ::Component>()
  84. ->Version(1)
  85. // ScriptCanvas avoids a use dependency on the AssetBuilderSDK. Therefore the Crc is used directly to register this component with the Gem builder
  86. ->Attribute(AZ::Edit::Attributes::SystemComponentTags, AZStd::vector<AZ::Crc32>({ AZ_CRC("AssetBuilder", 0xc739c7d7) }))
  87. ->Field("m_infiniteLoopDetectionMaxIterations", &SystemComponent::m_infiniteLoopDetectionMaxIterations)
  88. ->Field("maxHandlerStackDepth", &SystemComponent::m_maxHandlerStackDepth)
  89. ;
  90. if (AZ::EditContext* ec = serialize->GetEditContext())
  91. {
  92. ec->Class<SystemComponent>("Script Canvas", "Script Canvas System Component")
  93. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  94. ->Attribute(AZ::Edit::Attributes::Category, "Scripting")
  95. ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
  96. ->DataElement(AZ::Edit::UIHandlers::Default, &SystemComponent::m_infiniteLoopDetectionMaxIterations, "Infinite Loop Protection Max Iterations", "Script Canvas will avoid infinite loops by detecting potentially re-entrant conditions that execute up to this number of iterations.")
  97. ->DataElement(AZ::Edit::UIHandlers::Default, &SystemComponent::m_maxHandlerStackDepth, "Max Handler Stack Depth", "Script Canvas will avoid infinite loops at run-time by detecting sending Ebus Events while handling said Events. This limits the stack depth of the broadcast.")
  98. ->Attribute(AZ::Edit::Attributes::Min, 1000) // Safeguard user given value is valid
  99. ;
  100. }
  101. }
  102. if (AZ::JsonRegistrationContext* jsonContext = azrtti_cast<AZ::JsonRegistrationContext*>(context))
  103. {
  104. jsonContext->Serializer<AZ::DatumSerializer>()->HandlesType<Datum>();
  105. jsonContext->Serializer<AZ::BehaviorContextObjectSerializer>()->HandlesType<BehaviorContextObject>();
  106. jsonContext->Serializer<AZ::RuntimeVariableSerializer>()->HandlesType<RuntimeVariable>();
  107. }
  108. #if defined(SC_EXECUTION_TRACE_ENABLED)
  109. ExecutionLogData::Reflect(context);
  110. ExecutionLogAsset::Reflect(context);
  111. #endif//defined(SC_EXECUTION_TRACE_ENABLED)
  112. }
  113. void SystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
  114. {
  115. provided.push_back(AZ_CRC("ScriptCanvasService", 0x41fd58f3));
  116. }
  117. void SystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
  118. {
  119. (void)incompatible;
  120. }
  121. void SystemComponent::GetRequiredServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& required)
  122. {
  123. // \todo configure the application to require these services
  124. // required.push_back(AZ_CRC("AssetDatabaseService", 0x3abf5601));
  125. // required.push_back(AZ_CRC("ScriptService", 0x787235ab));
  126. }
  127. void SystemComponent::GetDependentServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& dependent)
  128. {
  129. }
  130. void SystemComponent::Init()
  131. {
  132. RegisterCreatableTypes();
  133. m_infiniteLoopDetectionMaxIterations = ScriptCanvasSystemComponentCpp::k_infiniteLoopDetectionMaxIterations;
  134. m_maxHandlerStackDepth = ScriptCanvasSystemComponentCpp::k_maxHandlerStackDepth;
  135. }
  136. void SystemComponent::Activate()
  137. {
  138. SystemRequestBus::Handler::BusConnect();
  139. AZ::BehaviorContext* behaviorContext{};
  140. AZ::ComponentApplicationBus::BroadcastResult(behaviorContext, &AZ::ComponentApplicationRequests::GetBehaviorContext);
  141. if (behaviorContext)
  142. {
  143. AZ::BehaviorContextBus::Handler::BusConnect(behaviorContext);
  144. }
  145. if (IsAnyScriptInterpreted()) // or if is the editor...
  146. {
  147. Execution::ActivateInterpreted();
  148. }
  149. SafeRegisterPerformanceTracker();
  150. }
  151. void SystemComponent::Deactivate()
  152. {
  153. AZ::BehaviorContextBus::Handler::BusDisconnect();
  154. SystemRequestBus::Handler::BusDisconnect();
  155. ModPerformanceTracker()->CalculateReports();
  156. Execution::PerformanceTrackingReport report = ModPerformanceTracker()->GetGlobalReport();
  157. const double ready = aznumeric_caster(report.timing.initializationTime);
  158. const double instant = aznumeric_caster(report.timing.executionTime);
  159. const double latent = aznumeric_caster(report.timing.latentTime);
  160. const double total = aznumeric_caster(report.timing.totalTime);
  161. FILE* performanceReportStream = nullptr;
  162. if (auto console = AZ::Interface<AZ::IConsole>::Get(); console != nullptr)
  163. {
  164. if (PerformanceReportFileStream performanceOutputOption;
  165. console->GetCvarValue("sc_outputperformancereport", performanceOutputOption) == AZ::GetValueResult::Success)
  166. {
  167. switch (performanceOutputOption)
  168. {
  169. case PerformanceReportFileStream::None:
  170. performanceReportStream = nullptr;
  171. break;
  172. case PerformanceReportFileStream::Stdout:
  173. performanceReportStream = stdout;
  174. break;
  175. case PerformanceReportFileStream::Stderr:
  176. performanceReportStream = stderr;
  177. break;
  178. }
  179. }
  180. }
  181. if (performanceReportStream != nullptr)
  182. {
  183. fprintf(performanceReportStream, "Global ScriptCanvas Performance Report:\n");
  184. fprintf(performanceReportStream, "[ INITIALIZE] %s\n", AZStd::fixed_string<32>::format("%7.3f ms", ready / 1000.0).c_str());
  185. fprintf(performanceReportStream, "[ EXECUTION] %s\n", AZStd::fixed_string<32>::format("%7.3f ms", instant / 1000.0).c_str());
  186. fprintf(performanceReportStream, "[ LATENT] %s\n", AZStd::fixed_string<32>::format("%7.3f ms", latent / 1000.0).c_str());
  187. fprintf(performanceReportStream, "[ TOTAL] %s\n", AZStd::fixed_string<32>::format("%7.3f ms", total / 1000.0).c_str());
  188. }
  189. SafeUnregisterPerformanceTracker();
  190. }
  191. bool SystemComponent::IsScriptUnitTestingInProgress()
  192. {
  193. return m_scriptBasedUnitTestingInProgress;
  194. }
  195. void SystemComponent::MarkScriptUnitTestBegin()
  196. {
  197. m_scriptBasedUnitTestingInProgress = true;
  198. }
  199. void SystemComponent::MarkScriptUnitTestEnd()
  200. {
  201. m_scriptBasedUnitTestingInProgress = false;
  202. }
  203. void SystemComponent::CreateEngineComponentsOnEntity(AZ::Entity* entity)
  204. {
  205. if (entity)
  206. {
  207. auto graph = entity->CreateComponent<Graph>();
  208. entity->CreateComponent<GraphVariableManagerComponent>(graph->GetScriptCanvasId());
  209. }
  210. }
  211. Graph* SystemComponent::CreateGraphOnEntity(AZ::Entity* graphEntity)
  212. {
  213. return graphEntity ? graphEntity->CreateComponent<Graph>() : nullptr;
  214. }
  215. ScriptCanvas::Graph* SystemComponent::MakeGraph()
  216. {
  217. AZ::Entity* graphEntity = aznew AZ::Entity("Script Canvas");
  218. ScriptCanvas::Graph* graph = graphEntity->CreateComponent<ScriptCanvas::Graph>();
  219. return graph;
  220. }
  221. ScriptCanvasId SystemComponent::FindScriptCanvasId(AZ::Entity* graphEntity)
  222. {
  223. auto* graph = graphEntity ? AZ::EntityUtils::FindFirstDerivedComponent<ScriptCanvas::Graph>(graphEntity) : nullptr;
  224. return graph ? graph->GetScriptCanvasId() : ScriptCanvasId();
  225. }
  226. ScriptCanvas::Node* SystemComponent::GetNode(const AZ::EntityId& nodeId, const AZ::Uuid& typeId)
  227. {
  228. AZ::Entity* entity = nullptr;
  229. AZ::ComponentApplicationBus::BroadcastResult(entity, &AZ::ComponentApplicationRequests::FindEntity, nodeId);
  230. if (entity)
  231. {
  232. ScriptCanvas::Node* node = azrtti_cast<ScriptCanvas::Node*>(entity->FindComponent(typeId));
  233. return node;
  234. }
  235. return nullptr;
  236. }
  237. ScriptCanvas::Node* SystemComponent::CreateNodeOnEntity(const AZ::EntityId& entityId, ScriptCanvasId scriptCanvasId, const AZ::Uuid& nodeType)
  238. {
  239. AZ::SerializeContext* serializeContext = nullptr;
  240. AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
  241. AZ_Assert(serializeContext, "Failed to retrieve application serialize context");
  242. const AZ::SerializeContext::ClassData* classData = serializeContext->FindClassData(nodeType);
  243. AZ_Assert(classData, "Type %s is not registered in the serialization context", nodeType.ToString<AZStd::string>().data());
  244. if (classData)
  245. {
  246. AZ::Entity* nodeEntity = nullptr;
  247. AZ::ComponentApplicationBus::BroadcastResult(nodeEntity, &AZ::ComponentApplicationRequests::FindEntity, entityId);
  248. ScriptCanvas::Node* node = reinterpret_cast<ScriptCanvas::Node*>(classData->m_factory->Create(classData->m_name));
  249. AZ_Assert(node, "ClassData (%s) does not correspond to a supported ScriptCanvas Node", classData->m_name);
  250. if (node && nodeEntity)
  251. {
  252. nodeEntity->SetName(classData->m_name);
  253. nodeEntity->AddComponent(node);
  254. }
  255. GraphRequestBus::Event(scriptCanvasId, &GraphRequests::AddNode, nodeEntity->GetId());
  256. return node;
  257. }
  258. return nullptr;
  259. }
  260. void SystemComponent::AddOwnedObjectReference(const void* object, BehaviorContextObject* behaviorContextObject)
  261. {
  262. if (object == nullptr)
  263. {
  264. return;
  265. }
  266. LockType lock(m_ownedObjectsByAddressMutex);
  267. [[maybe_unused]] auto emplaceResult = m_ownedObjectsByAddress.emplace(object, behaviorContextObject);
  268. AZ_Assert(emplaceResult.second, "Adding second owned reference to memory");
  269. }
  270. BehaviorContextObject* SystemComponent::FindOwnedObjectReference(const void* object)
  271. {
  272. if (object == nullptr)
  273. {
  274. return nullptr;
  275. }
  276. LockType lock(m_ownedObjectsByAddressMutex);
  277. auto iter = m_ownedObjectsByAddress.find(object);
  278. return iter == m_ownedObjectsByAddress.end() ? nullptr : iter->second;
  279. }
  280. void SystemComponent::RemoveOwnedObjectReference(const void* object)
  281. {
  282. if (object == nullptr)
  283. {
  284. return;
  285. }
  286. LockType lock(m_ownedObjectsByAddressMutex);
  287. m_ownedObjectsByAddress.erase(object);
  288. }
  289. AZStd::pair<DataRegistry::Createability, TypeProperties> SystemComponent::GetCreatibility(AZ::SerializeContext* serializeContext, AZ::BehaviorClass* behaviorClass)
  290. {
  291. TypeProperties typeProperties;
  292. bool canCreate{};
  293. // BehaviorContext classes with the ExcludeFrom attribute with a value of the ExcludeFlags::List is not creatable
  294. const AZ::u64 exclusionFlags = AZ::Script::Attributes::ExcludeFlags::List;
  295. auto excludeClassAttributeData = azrtti_cast<const AZ::Edit::AttributeData<AZ::Script::Attributes::ExcludeFlags>*>(AZ::FindAttribute(AZ::Script::Attributes::ExcludeFrom, behaviorClass->m_attributes));
  296. const AZ::u64 flags = excludeClassAttributeData ? excludeClassAttributeData->Get(nullptr) : 0;
  297. bool listOnly = ((flags & AZ::Script::Attributes::ExcludeFlags::ListOnly) == AZ::Script::Attributes::ExcludeFlags::ListOnly); // ListOnly exclusions may create variables
  298. canCreate = listOnly || (!excludeClassAttributeData || (!(flags & exclusionFlags)));
  299. canCreate = canCreate && (serializeContext->FindClassData(behaviorClass->m_typeId));
  300. canCreate = canCreate && !ScriptCanvasSystemComponentCpp::IsDeprecated(behaviorClass->m_attributes);
  301. if (canCreate)
  302. {
  303. for (auto base : behaviorClass->m_baseClasses)
  304. {
  305. if (AZ::Component::TYPEINFO_Uuid() == base)
  306. {
  307. canCreate = false;
  308. break; // only out of the for : base classes loop. DO NOT break out of the parent loop.
  309. }
  310. }
  311. }
  312. // Assets are not safe enough for variable creation, yet. They can be created with one Az type (Data::Asset<T>), but set to nothing.
  313. // When read back in, they will (if lucky) just be Data::Asset<Data>, which breaks type safety at best, and requires a lot of sanity checking.
  314. // This is NOT blacked at the createable types or BehaviorContext level, since they could be used to at least pass information through,
  315. // and may be used other scripting contexts.
  316. AZ::IRttiHelper* rttiHelper = behaviorClass->m_azRtti;
  317. if (rttiHelper && rttiHelper->GetGenericTypeId() == azrtti_typeid<AZ::Data::Asset>())
  318. {
  319. canCreate = false;
  320. }
  321. if (AZ::FindAttribute(AZ::ScriptCanvasAttributes::AllowInternalCreation, behaviorClass->m_attributes))
  322. {
  323. canCreate = true;
  324. typeProperties.m_isTransient = true;
  325. }
  326. // create able variables must have full memory support
  327. canCreate = canCreate &&
  328. (behaviorClass->m_allocate
  329. && behaviorClass->m_cloner
  330. && behaviorClass->m_mover
  331. && behaviorClass->m_destructor
  332. && behaviorClass->m_deallocate) &&
  333. AZStd::none_of(behaviorClass->m_baseClasses.begin(), behaviorClass->m_baseClasses.end(), [](const AZ::TypeId& base) { return azrtti_typeid<AZ::Component>() == base; });
  334. if (!canCreate)
  335. {
  336. return { DataRegistry::Createability::None , TypeProperties{} };
  337. }
  338. else if (!AZ::FindAttribute(AZ::ScriptCanvasAttributes::VariableCreationForbidden, behaviorClass->m_attributes))
  339. {
  340. return { DataRegistry::Createability::SlotAndVariable, typeProperties };
  341. }
  342. else
  343. {
  344. return { DataRegistry::Createability::SlotOnly, typeProperties };
  345. }
  346. }
  347. void SystemComponent::RegisterCreatableTypes()
  348. {
  349. AZ::SerializeContext* serializeContext{};
  350. AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
  351. AZ_Assert(serializeContext, "Serialize Context should not be missing at this point");
  352. AZ::BehaviorContext* behaviorContext{};
  353. AZ::ComponentApplicationBus::BroadcastResult(behaviorContext, &AZ::ComponentApplicationRequests::GetBehaviorContext);
  354. AZ_Assert(behaviorContext, "Behavior Context should not be missing at this point");
  355. auto dataRegistry = ScriptCanvas::GetDataRegistry();
  356. for (const auto& classIter : behaviorContext->m_classes)
  357. {
  358. auto createability = GetCreatibility(serializeContext, classIter.second);
  359. if (createability.first != DataRegistry::Createability::None)
  360. {
  361. dataRegistry->RegisterType(classIter.second->m_typeId, createability.second, createability.first);
  362. }
  363. }
  364. }
  365. void SystemComponent::OnAddClass(const char*, AZ::BehaviorClass* behaviorClass)
  366. {
  367. auto dataRegistry = ScriptCanvas::GetDataRegistry();
  368. if (!dataRegistry)
  369. {
  370. AZ_Warning("ScriptCanvas", false, "Data registry not available. Can't register new class.");
  371. return;
  372. }
  373. AZ::SerializeContext* serializeContext{};
  374. AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
  375. AZ_Assert(serializeContext, "Serialize Context missing. Can't register new class.");
  376. auto createability = GetCreatibility(serializeContext, behaviorClass);
  377. if (createability.first != DataRegistry::Createability::None)
  378. {
  379. dataRegistry->RegisterType(behaviorClass->m_typeId, createability.second, createability.first);
  380. }
  381. }
  382. void SystemComponent::OnRemoveClass(const char*, AZ::BehaviorClass* behaviorClass)
  383. {
  384. // The data registry might not be available when unloading the ScriptCanvas module
  385. auto dataRegistry = ScriptCanvas::GetDataRegistry();
  386. if (dataRegistry)
  387. {
  388. dataRegistry->UnregisterType(behaviorClass->m_typeId);
  389. }
  390. }
  391. void SystemComponent::SetInterpretedBuildConfiguration(BuildConfiguration config)
  392. {
  393. Execution::SetInterpretedExecutionMode(config);
  394. }
  395. AZ::EnvironmentVariable<Execution::PerformanceTracker*> SystemComponent::s_perfTracker;
  396. AZStd::shared_mutex SystemComponent::s_perfTrackerMutex;
  397. Execution::PerformanceTracker* SystemComponent::ModPerformanceTracker()
  398. {
  399. // First attempt to use the module-static reference; take a read lock to check it.
  400. // This is the fast path which won't block.
  401. {
  402. AZStd::shared_lock<AZStd::shared_mutex> lock(s_perfTrackerMutex);
  403. if (s_perfTracker)
  404. {
  405. return s_perfTracker.Get();
  406. }
  407. }
  408. // If the instance doesn't exist (which means we could be in a different module),
  409. // take the full lock and request it.
  410. AZStd::unique_lock<AZStd::shared_mutex> lock(s_perfTrackerMutex);
  411. s_perfTracker = AZ::Environment::FindVariable<Execution::PerformanceTracker*>(s_trackerName);
  412. return s_perfTracker ? s_perfTracker.Get() : nullptr;
  413. }
  414. void SystemComponent::SafeRegisterPerformanceTracker()
  415. {
  416. if (ModPerformanceTracker())
  417. {
  418. return;
  419. }
  420. AZStd::unique_lock<AZStd::shared_mutex> lock(s_perfTrackerMutex);
  421. auto tracker = aznew Execution::PerformanceTracker();
  422. s_perfTracker = AZ::Environment::CreateVariable<Execution::PerformanceTracker*>(s_trackerName);
  423. s_perfTracker.Get() = tracker;
  424. }
  425. void SystemComponent::SafeUnregisterPerformanceTracker()
  426. {
  427. auto performanceTracker = ModPerformanceTracker();
  428. if (!performanceTracker)
  429. {
  430. return;
  431. }
  432. AZStd::unique_lock<AZStd::shared_mutex> lock(s_perfTrackerMutex);
  433. *s_perfTracker = nullptr;
  434. s_perfTracker.Reset();
  435. delete performanceTracker;
  436. }
  437. }