ScriptCanvasGraphUtilities.inl 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452
  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 <Asset/EditorAssetSystemComponent.h>
  9. #include <AzCore/Asset/AssetManagerBus.h>
  10. #include <AzCore/Component/TickBus.h>
  11. #include <AzCore/IO/FileIO.h>
  12. #include <AzCore/Script/ScriptAsset.h>
  13. #include <AzCore/Script/ScriptContext.h>
  14. #include <AzFramework/API/ApplicationAPI.h>
  15. #include <Editor/Framework/ScriptCanvasReporter.h>
  16. #include <ScriptCanvas/Asset/RuntimeAsset.h>
  17. #include <ScriptCanvas/Asset/RuntimeAssetHandler.h>
  18. #include <ScriptCanvas/Execution/ExecutionState.h>
  19. #include <ScriptCanvas/Execution/Interpreted/ExecutionInterpretedAPI.h>
  20. #include <ScriptCanvas/Execution/RuntimeComponent.h>
  21. #include <ScriptCanvas/Libraries/UnitTesting/UnitTestBusSender.h>
  22. namespace ScriptCanvasEditor
  23. {
  24. using namespace ScriptCanvas;
  25. // The runtime context (appropriately) always assumes that EntityIds are overridden, this step copies the values from the runtime data
  26. // over to the override data to simulate build step that does this when building prefabs
  27. AZ_INLINE void CopyAssetEntityIdsToOverrides(RuntimeDataOverrides& runtimeDataOverrides)
  28. {
  29. runtimeDataOverrides.m_entityIds.reserve(runtimeDataOverrides.m_runtimeAsset->m_runtimeData.m_input.m_entityIds.size());
  30. for (auto& varEntityPar : runtimeDataOverrides.m_runtimeAsset->m_runtimeData.m_input.m_entityIds)
  31. {
  32. runtimeDataOverrides.m_entityIds.push_back(varEntityPar.second);
  33. }
  34. for (auto& dependency : runtimeDataOverrides.m_dependencies)
  35. {
  36. CopyAssetEntityIdsToOverrides(dependency);
  37. }
  38. }
  39. AZ_INLINE AZStd::vector<LoadedInterpretedDependency> LoadInterpretedDepencies(const ScriptCanvas::DependencySet& dependencySet)
  40. {
  41. AZStd::vector<LoadedInterpretedDependency> loadedAssets;
  42. if (!dependencySet.empty())
  43. {
  44. for (auto& namespacePath : dependencySet)
  45. {
  46. if (namespacePath.empty())
  47. {
  48. continue;
  49. }
  50. AZ_Assert(namespacePath.size() >= 3, "This functions assumes unit test dependencies are in the ScriptCanvas gem unit test folder");
  51. AZStd::string originalPath = namespacePath[2].data();
  52. for (size_t index = 3; index < namespacePath.size(); ++index)
  53. {
  54. originalPath += "/";
  55. originalPath += namespacePath[index];
  56. }
  57. if (originalPath.ends_with(Grammar::k_internalRuntimeSuffix) || originalPath.ends_with(Grammar::k_internalRuntimeSuffixLC))
  58. {
  59. originalPath.resize(originalPath.size() - AZStd::string_view(Grammar::k_internalRuntimeSuffix).size());
  60. }
  61. AZStd::string path = AZStd::string::format("%s/%s.scriptcanvas", k_unitTestDirPathRelative, originalPath.data());
  62. LoadTestGraphResult loadResult = LoadTestGraph(path);
  63. AZ_Assert(loadResult.m_runtimeAsset, "failed to load dependent asset");
  64. AZ::Outcome<ScriptCanvas::Translation::LuaAssetResult, AZStd::string> luaAssetOutcome = AZ::Failure(AZStd::string("lua asset creation for function failed"));
  65. ScriptCanvasEditor::EditorAssetConversionBus::BroadcastResult(luaAssetOutcome, &ScriptCanvasEditor::EditorAssetConversionBusTraits::CreateLuaAsset, loadResult.m_editorAsset, loadResult.m_editorAsset.RelativePath().c_str());
  66. AZ_Assert(luaAssetOutcome.IsSuccess(), "failed to create Lua asset");
  67. AZStd::string modulePath = namespacePath[0].data();
  68. for (size_t index = 1; index < namespacePath.size(); ++index)
  69. {
  70. modulePath += "/";
  71. modulePath += namespacePath[index];
  72. }
  73. const ScriptCanvas::Translation::LuaAssetResult& luaAssetResult = luaAssetOutcome.GetValue();
  74. // #functions2_recursive_unit_tests
  75. loadedAssets.push_back({ modulePath, loadResult.m_runtimeAsset, luaAssetResult, {} });
  76. }
  77. }
  78. return loadedAssets;
  79. }
  80. AZ_INLINE DurationSpec DurationSpec::Seconds(float seconds)
  81. {
  82. DurationSpec spec;
  83. spec.m_spec = eDuration::Seconds;
  84. spec.m_seconds = seconds;
  85. return spec;
  86. }
  87. AZ_INLINE DurationSpec DurationSpec::Ticks(size_t ticks)
  88. {
  89. DurationSpec spec;
  90. spec.m_spec = eDuration::Ticks;
  91. spec.m_ticks = ticks;
  92. return spec;
  93. }
  94. AZ_INLINE LoadTestGraphResult LoadTestGraph(AZStd::string_view graphPath)
  95. {
  96. if (auto fileLoadResult = LoadFromFile(graphPath))
  97. {
  98. auto& source = fileLoadResult.m_handle;
  99. auto testableSource = SourceHandle::FromRelativePath(source, AZ::Uuid::CreateRandom(), source.RelativePath().c_str());
  100. AZ::Outcome<AZ::Data::Asset<ScriptCanvas::RuntimeAsset>, AZStd::string> assetOutcome(AZ::Failure(AZStd::string("asset create failed")));
  101. ScriptCanvasEditor::EditorAssetConversionBus::BroadcastResult(assetOutcome
  102. , &ScriptCanvasEditor::EditorAssetConversionBusTraits::CreateRuntimeAsset, testableSource);
  103. if (assetOutcome.IsSuccess())
  104. {
  105. LoadTestGraphResult loadTestGraphResult;
  106. loadTestGraphResult.m_editorAsset = AZStd::move(testableSource);
  107. loadTestGraphResult.m_runtimeAsset = assetOutcome.GetValue();
  108. loadTestGraphResult.m_entity = AZStd::make_unique<AZ::Entity>("Loaded Graph");
  109. return loadTestGraphResult;
  110. }
  111. }
  112. return {};
  113. }
  114. AZ_INLINE void RunGraphImplementation(const RunGraphSpec& runGraphSpec, Reporters& reporters)
  115. {
  116. AZ_Assert(!reporters.empty(), "there must be at least one report");
  117. for (auto& reporter : reporters)
  118. {
  119. if (runGraphSpec.runSpec.expectRuntimeFailure)
  120. {
  121. reporter.MarkExpectRuntimeFailure();
  122. }
  123. reporter.SetExecutionMode(runGraphSpec.runSpec.execution);
  124. RunGraphImplementation(runGraphSpec, reporter);
  125. }
  126. }
  127. AZ_INLINE void RunEditorAsset(SourceHandle asset, Reporter& reporter, ScriptCanvas::ExecutionMode mode)
  128. {
  129. AZ::Data::AssetId assetId = asset.Id();
  130. AZ::Data::AssetId runtimeAssetId(assetId.m_guid, RuntimeDataSubId);
  131. AZ::Data::Asset<ScriptCanvas::RuntimeAsset> runtimeAsset;
  132. if (!runtimeAsset.Create(runtimeAssetId, true))
  133. {
  134. return;
  135. }
  136. reporter.SetExecutionMode(mode);
  137. LoadTestGraphResult loadResult;
  138. loadResult.m_editorAsset = SourceHandle::FromRelativePath(nullptr, assetId.m_guid, asset.RelativePath());
  139. AZ::EntityId scriptCanvasId;
  140. loadResult.m_entity = AZStd::make_unique<AZ::Entity>("Loaded test graph");
  141. loadResult.m_runtimeAsset = runtimeAsset;
  142. RunGraphSpec runGraphSpec;
  143. runGraphSpec.dirPath = "";
  144. runGraphSpec.graphPath = asset.RelativePath().c_str();
  145. runGraphSpec.runSpec.duration.m_spec = eDuration::Ticks;
  146. runGraphSpec.runSpec.duration.m_ticks = 10;
  147. runGraphSpec.runSpec.execution = mode;
  148. runGraphSpec.runSpec.release = true;
  149. runGraphSpec.runSpec.debug = runGraphSpec.runSpec.traced = false;
  150. RunGraphImplementation(runGraphSpec, loadResult, reporter);
  151. }
  152. AZ_INLINE void RunGraphImplementation(const RunGraphSpec& runGraphSpec, Reporter& reporter)
  153. {
  154. TraceSuppressionBus::Broadcast(&TraceSuppressionRequests::SuppressPrintf, true);
  155. LoadTestGraphResult loadResult = LoadTestGraph(runGraphSpec.graphPath);
  156. TraceSuppressionBus::Broadcast(&TraceSuppressionRequests::SuppressPrintf, false);
  157. RunGraphImplementation(runGraphSpec, loadResult, reporter);
  158. }
  159. AZ_INLINE void RunGraphImplementation(const RunGraphSpec& runGraphSpec, LoadTestGraphResult& loadResult, Reporter& reporter)
  160. {
  161. ScriptCanvas::SystemRequestBus::Broadcast(&ScriptCanvas::SystemRequests::MarkScriptUnitTestBegin);
  162. if (loadResult.m_entity)
  163. {
  164. reporter.MarkGraphLoaded();
  165. RuntimeData runtimeDataBuffer;
  166. AZStd::vector<RuntimeData> dependencyDataBuffer;
  167. AZStd::vector<LoadedInterpretedDependency> dependencies;
  168. if (runGraphSpec.runSpec.execution == ExecutionMode::Interpreted)
  169. {
  170. ScopedOutputSuppression outputSuppressor;
  171. AZ::Outcome<ScriptCanvas::Translation::LuaAssetResult, AZStd::string> luaAssetOutcome = AZ::Failure(AZStd::string("lua asset creation failed"));
  172. ScriptCanvasEditor::EditorAssetConversionBus::BroadcastResult(luaAssetOutcome
  173. , &ScriptCanvasEditor::EditorAssetConversionBusTraits::CreateLuaAsset, loadResult.m_editorAsset, loadResult.m_editorAsset.RelativePath().c_str());
  174. reporter.MarkParseAttemptMade();
  175. if (luaAssetOutcome.IsSuccess())
  176. {
  177. const ScriptCanvas::Translation::LuaAssetResult& luaAssetResult = luaAssetOutcome.GetValue();
  178. reporter.SetEntity(loadResult.m_entity->GetId());
  179. reporter.SetDurations(luaAssetResult.m_parseDuration, luaAssetResult.m_translationDuration);
  180. reporter.MarkCompiled();
  181. if (!reporter.IsProcessOnly())
  182. {
  183. RuntimeDataOverrides runtimeDataOverrides;
  184. runtimeDataOverrides.m_runtimeAsset = loadResult.m_runtimeAsset;
  185. runtimeDataOverrides.m_runtimeAsset.SetHint("original");
  186. runtimeDataOverrides.m_runtimeAsset.Get()->m_runtimeData.m_script.SetHint("original");
  187. #if defined(LINUX) //////////////////////////////////////////////////////////////////////////
  188. if (!luaAssetResult.m_dependencies.source.userSubgraphs.empty())
  189. {
  190. reporter.MarkLinuxDependencyTestBypass();
  191. ScriptCanvas::SystemRequestBus::Broadcast(&ScriptCanvas::SystemRequests::MarkScriptUnitTestEnd);
  192. return;
  193. }
  194. #else ///////////////////////////////////////////////////////////////////////////////////////
  195. dependencies = LoadInterpretedDepencies(luaAssetResult.m_dependencies.source.userSubgraphs);
  196. if (!dependencies.empty())
  197. {
  198. // #functions2_recursive_unit_tests eventually, this will need to be recursive, or the full asset handling system will need to be integrated into the testing framework
  199. // in order to test functionality with a dependency stack greater than 2
  200. // load all script assets, and their dependencies, initialize statics on all those dependencies if it is the first time loaded
  201. {
  202. AZ::InMemoryScriptModules inMemoryModules;
  203. inMemoryModules.reserve(dependencies.size());
  204. dependencyDataBuffer.resize(dependencies.size());
  205. for (auto& dependency : dependencies)
  206. {
  207. inMemoryModules.emplace_back(dependency.path, dependency.luaAssetResult.m_scriptAsset);
  208. }
  209. AZ::ScriptSystemRequestBus::Broadcast(&AZ::ScriptSystemRequests::UseInMemoryRequireHook, inMemoryModules, AZ::ScriptContextIds::DefaultScriptContextId);
  210. }
  211. for (size_t index = 0; index < dependencies.size(); ++index)
  212. {
  213. auto& dependency = dependencies[index];
  214. const ScriptCanvas::Translation::LuaAssetResult& depencyAssetResult = dependency.luaAssetResult;
  215. RuntimeDataOverrides dependencyRuntimeDataOverrides;
  216. dependencyRuntimeDataOverrides.m_runtimeAsset = dependency.runtimeAsset;
  217. AZStd::string dependencyHint = AZStd::string::format("dependency_%zu", index);
  218. dependencyRuntimeDataOverrides.m_runtimeAsset.SetHint(dependencyHint);
  219. dependencyRuntimeDataOverrides.m_runtimeAsset.Get()->m_runtimeData.m_script.SetHint(dependencyHint);
  220. runtimeDataOverrides.m_dependencies.push_back(dependencyRuntimeDataOverrides);
  221. RuntimeData& dependencyData = dependencyDataBuffer[index];
  222. dependencyData.m_input = depencyAssetResult.m_runtimeInputs;
  223. dependencyData.m_debugMap = depencyAssetResult.m_debugMap;
  224. dependencyData.m_script = depencyAssetResult.m_scriptAsset;
  225. Execution::Context::InitializeStaticActivationData(dependencyData);
  226. Execution::InitializeInterpretedStatics(dependencyData);
  227. }
  228. }
  229. #endif //////////////////////////////////////////////////////////////////////////////////////
  230. loadResult.m_scriptAsset = luaAssetResult.m_scriptAsset;
  231. loadResult.m_runtimeAsset.Get()->m_runtimeData.m_script = loadResult.m_scriptAsset;
  232. loadResult.m_runtimeAsset.Get()->m_runtimeData.m_input = luaAssetResult.m_runtimeInputs;
  233. loadResult.m_runtimeAsset.Get()->m_runtimeData.m_debugMap = luaAssetResult.m_debugMap;
  234. loadResult.m_runtimeComponent = loadResult.m_entity->CreateComponent<ScriptCanvas::RuntimeComponent>();
  235. CopyAssetEntityIdsToOverrides(runtimeDataOverrides);
  236. loadResult.m_runtimeComponent->TakeRuntimeDataOverrides(AZStd::move(runtimeDataOverrides));
  237. Execution::Context::InitializeStaticActivationData(loadResult.m_runtimeAsset->m_runtimeData);
  238. Execution::InitializeInterpretedStatics(loadResult.m_runtimeAsset->m_runtimeData);
  239. }
  240. }
  241. }
  242. if (reporter.IsCompiled())
  243. {
  244. if (reporter.IsProcessOnly())
  245. {
  246. reporter.FinishReport();
  247. }
  248. else
  249. {
  250. loadResult.m_entity->Init();
  251. reporter.SetGraph(loadResult.m_runtimeAsset.GetId());
  252. {
  253. ScopedOutputSuppression outputSuppressor;
  254. if (runGraphSpec.runSpec.execution == ExecutionMode::Interpreted)
  255. {
  256. // make sure the functions have debug info, too
  257. if (reporter.GetExecutionConfiguration() == ExecutionConfiguration::Release)
  258. {
  259. ScriptCanvas::SystemRequestBus::Broadcast(&ScriptCanvas::SystemRequests::SetInterpretedBuildConfiguration, ScriptCanvas::BuildConfiguration::Release);
  260. }
  261. else if (reporter.GetExecutionConfiguration() == ExecutionConfiguration::Performance)
  262. {
  263. ScriptCanvas::SystemRequestBus::Broadcast(&ScriptCanvas::SystemRequests::SetInterpretedBuildConfiguration, ScriptCanvas::BuildConfiguration::Performance);
  264. }
  265. else
  266. {
  267. ScriptCanvas::SystemRequestBus::Broadcast(&ScriptCanvas::SystemRequests::SetInterpretedBuildConfiguration, ScriptCanvas::BuildConfiguration::Debug);
  268. }
  269. }
  270. loadResult.m_entity->Activate();
  271. SimulateDuration(runGraphSpec.runSpec.duration);
  272. }
  273. if (runGraphSpec.runSpec.m_onPostSimulate)
  274. {
  275. AZStd::invoke(runGraphSpec.runSpec.m_onPostSimulate);
  276. }
  277. loadResult.m_entity->Deactivate();
  278. reporter.CollectPerformanceTiming();
  279. reporter.FinishReport();
  280. loadResult.m_entity.reset();
  281. }
  282. }
  283. if (runGraphSpec.runSpec.execution == ExecutionMode::Interpreted)
  284. {
  285. AZ::ScriptSystemRequestBus::Broadcast(&AZ::ScriptSystemRequests::ClearAssetReferences, loadResult.m_scriptAsset.GetId());
  286. if (!dependencies.empty())
  287. {
  288. AZ::ScriptSystemRequestBus::Broadcast(&AZ::ScriptSystemRequests::RestoreDefaultRequireHook, AZ::ScriptContextIds::DefaultScriptContextId);
  289. }
  290. AZ::ScriptSystemRequestBus::Broadcast(&AZ::ScriptSystemRequests::GarbageCollect);
  291. }
  292. }
  293. if (!reporter.IsReportFinished())
  294. {
  295. reporter.FinishReport();
  296. }
  297. ScriptCanvas::SystemRequestBus::Broadcast(&ScriptCanvas::SystemRequests::MarkScriptUnitTestEnd);
  298. }
  299. AZ_INLINE Reporters RunGraph(const RunGraphSpec& runGraphSpec)
  300. {
  301. Reporters reporters;
  302. if (runGraphSpec.runSpec.processOnly)
  303. {
  304. Reporter reporter;
  305. reporter.SetFilePath(runGraphSpec.graphPath);
  306. reporter.SetProcessOnly(runGraphSpec.runSpec.processOnly);
  307. reporters.push_back(reporter);
  308. }
  309. else
  310. {
  311. if (runGraphSpec.runSpec.release)
  312. {
  313. Reporter reporterRelease;
  314. reporterRelease.SetFilePath(runGraphSpec.graphPath);
  315. reporterRelease.SetExecutionConfiguration(ExecutionConfiguration::Release);
  316. reporters.push_back(reporterRelease);
  317. Reporter reporterPeformance;
  318. reporterPeformance.SetFilePath(runGraphSpec.graphPath);
  319. reporterPeformance.SetExecutionConfiguration(ExecutionConfiguration::Performance);
  320. reporters.push_back(reporterPeformance);
  321. }
  322. if (runGraphSpec.runSpec.debug)
  323. {
  324. Reporter reporterDebug;
  325. reporterDebug.SetFilePath(runGraphSpec.graphPath);
  326. reporterDebug.SetExecutionConfiguration(ExecutionConfiguration::Debug);
  327. reporters.push_back(reporterDebug);
  328. }
  329. if (runGraphSpec.runSpec.traced)
  330. {
  331. Reporter reporterTraced;
  332. reporterTraced.SetFilePath(runGraphSpec.graphPath);
  333. reporterTraced.SetExecutionConfiguration(ExecutionConfiguration::Traced);
  334. reporters.push_back(reporterTraced);
  335. }
  336. }
  337. RunGraphImplementation(runGraphSpec, reporters);
  338. return reporters;
  339. }
  340. AZ_INLINE void Simulate(const DurationSpec& duration)
  341. {
  342. AZ::SystemTickBus::Broadcast(&AZ::SystemTickBus::Events::OnSystemTick);
  343. AZ::SystemTickBus::ExecuteQueuedEvents();
  344. AZ::TickBus::Broadcast(&AZ::TickEvents::OnTick, duration.m_timeStep, AZ::ScriptTimePoint(AZStd::chrono::steady_clock::now()));
  345. AZ::TickBus::ExecuteQueuedEvents();
  346. }
  347. AZ_INLINE void SimulateDuration(const DurationSpec& duration)
  348. {
  349. switch (duration.m_spec)
  350. {
  351. case eDuration::InitialActivation:
  352. break;
  353. case eDuration::Seconds:
  354. SimulateSeconds(duration);
  355. break;
  356. case eDuration::Ticks:
  357. SimulateTicks(duration);
  358. break;
  359. default:
  360. break;
  361. }
  362. }
  363. AZ_INLINE void SimulateSeconds(const DurationSpec& duration)
  364. {
  365. float simulationDuration = duration.m_seconds;
  366. while (simulationDuration > 0.0f)
  367. {
  368. Simulate(duration);
  369. simulationDuration -= duration.m_timeStep;
  370. }
  371. }
  372. AZ_INLINE void SimulateTicks(const DurationSpec& duration)
  373. {
  374. size_t remainingTicks = duration.m_ticks;
  375. while (remainingTicks)
  376. {
  377. Simulate(duration);
  378. --remainingTicks;
  379. }
  380. }
  381. }