AtomSampleViewerApplication.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  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 <AtomSampleViewerApplication.h>
  9. #include <SampleComponentManagerBus.h>
  10. #include <AzCore/Component/ComponentApplicationLifecycle.h>
  11. #include <AzCore/PlatformIncl.h>
  12. #include <AzCore/Component/ComponentApplication.h>
  13. #include <AzCore/Debug/Trace.h>
  14. #include <AzCore/Time/ITime.h>
  15. #include <AzFramework/Asset/AssetSystemBus.h>
  16. #include <AzFramework/Asset/AssetProcessorMessages.h>
  17. #include <AzFramework/IO/LocalFileIO.h>
  18. #include <AzFramework/Network/AssetProcessorConnection.h>
  19. #include <AzFramework/StringFunc/StringFunc.h>
  20. #include <Atom/RPI.Public/RPISystemInterface.h>
  21. #include <AzCore/Math/Random.h>
  22. #include <AzCore/Script/ScriptAsset.h>
  23. #include <AzCore/Settings/SettingsRegistryMergeUtils.h>
  24. #include <AzCore/Utils/Utils.h>
  25. #include <AzGameFramework/Application/GameApplication.h>
  26. #include <GridMate/GridMate.h>
  27. namespace AtomSampleViewer
  28. {
  29. //! This function returns the build system target name of "AtomSampleViewerStandalone"
  30. AZStd::string_view GetBuildTargetName()
  31. {
  32. #if !defined (LY_CMAKE_TARGET)
  33. #error "LY_CMAKE_TARGET must be defined in order to add this source file to a CMake executable target"
  34. #endif
  35. return AZStd::string_view{ LY_CMAKE_TARGET };
  36. }
  37. AtomSampleViewerApplication::AtomSampleViewerApplication()
  38. {
  39. AZ::Debug::Trace::Instance().Init();
  40. #if defined(AZ_DEBUG_BUILD) || defined(AZ_PROFILE_BUILD)
  41. AZ::Debug::Trace::SetAssertVerbosityLevel(2); //enable break on AZ_Assert()
  42. #endif
  43. m_startupLogSink = AZStd::make_unique<AZStd::vector<LogMessage>>();
  44. AZ::Debug::TraceMessageBus::Handler::BusConnect();
  45. AtomSampleViewerRequestsBus::Handler::BusConnect();
  46. SampleComponentManagerNotificationBus::Handler::BusConnect();
  47. AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddBuildSystemTargetSpecialization(
  48. *AZ::SettingsRegistry::Get(), GetBuildTargetName());
  49. }
  50. AtomSampleViewerApplication::AtomSampleViewerApplication(int* argc, char*** argv)
  51. : Application(argc, argv)
  52. {
  53. AZ::Debug::Trace::Instance().Init();
  54. #if defined(AZ_DEBUG_BUILD) || defined(AZ_PROFILE_BUILD)
  55. AZ::Debug::Trace::SetAssertVerbosityLevel(2); //enable break on AZ_Assert()
  56. #endif
  57. m_startupLogSink = AZStd::make_unique<AZStd::vector<LogMessage>>();
  58. AZ::Debug::TraceMessageBus::Handler::BusConnect();
  59. AtomSampleViewerRequestsBus::Handler::BusConnect();
  60. SampleComponentManagerNotificationBus::Handler::BusConnect();
  61. SetupConsoleHandlerRoutine();
  62. AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddBuildSystemTargetSpecialization(
  63. *AZ::SettingsRegistry::Get(), GetBuildTargetName());
  64. }
  65. AtomSampleViewerApplication::~AtomSampleViewerApplication()
  66. {
  67. AZ::Debug::TraceMessageBus::Handler::BusDisconnect();
  68. AtomSampleViewerRequestsBus::Handler::BusDisconnect();
  69. SampleComponentManagerNotificationBus::Handler::BusDisconnect();
  70. if (s_connectToAssetProcessor)
  71. {
  72. AzFramework::AssetSystemRequestBus::Broadcast(&AzFramework::AssetSystem::AssetSystemRequests::StartDisconnectingAssetProcessor);
  73. }
  74. }
  75. void AtomSampleViewerApplication::StartCommon(AZ::Entity* systemEntity)
  76. {
  77. AzFramework::AssetSystemStatusBus::Handler::BusConnect();
  78. AzFramework::Application::StartCommon(systemEntity);
  79. if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
  80. {
  81. AZ::ComponentApplicationLifecycle::SignalEvent(*settingsRegistry, "LegacySystemInterfaceCreated", R"({})");
  82. }
  83. if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
  84. {
  85. AZ::ComponentApplicationLifecycle::SignalEvent(*settingsRegistry, "LegacySystemInterfaceCreated", R"({})");
  86. }
  87. ReadTimeoutShutdown();
  88. WriteStartupLog();
  89. }
  90. void AtomSampleViewerApplication::OnSampleManagerActivated()
  91. {
  92. ReadAutomatedTestOptions();
  93. }
  94. void AtomSampleViewerApplication::WriteStartupLog()
  95. {
  96. AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance();
  97. AZ_Assert(fileIO != nullptr, "FileIO should be running at this point");
  98. // There is no log system online so we have to create your own log file.
  99. char resolveBuffer[AZ_MAX_PATH_LEN] = { 0 };
  100. fileIO->ResolvePath("@user@", resolveBuffer, AZ_MAX_PATH_LEN);
  101. // Note: @log@ hasn't been set at this point in the standalone AtomSampleViewer
  102. AZStd::string logDirectory;
  103. AzFramework::StringFunc::Path::Join(resolveBuffer, "log", logDirectory);
  104. fileIO->SetAlias("@log@", logDirectory.c_str());
  105. fileIO->CreatePath("@products@");
  106. fileIO->CreatePath("@user@");
  107. fileIO->CreatePath("@log@");
  108. AZStd::string logPath;
  109. AzFramework::StringFunc::Path::Join(logDirectory.c_str(), s_logFileBaseName, logPath);
  110. using namespace AzFramework;
  111. m_logFile.reset(aznew LogFile(logPath.c_str()));
  112. if (m_logFile)
  113. {
  114. m_logFile->SetMachineReadable(false);
  115. for (const LogMessage& message : *m_startupLogSink)
  116. {
  117. m_logFile->AppendLog(AzFramework::LogFile::SEV_NORMAL, message.window.c_str(), message.message.c_str());
  118. }
  119. m_startupLogSink->clear();
  120. m_logFile->FlushLog();
  121. }
  122. }
  123. void AtomSampleViewerApplication::ReadAutomatedTestOptions()
  124. {
  125. if (GetArgC() != nullptr && GetArgV() != nullptr)
  126. {
  127. AzFramework::CommandLine commandLine;
  128. commandLine.Parse(*GetArgC(), *GetArgV());
  129. constexpr const char* testSuiteSwitch = "runtestsuite";
  130. constexpr const char* testExitSwitch = "exitontestend";
  131. constexpr const char* testRandomSeed = "randomtestseed";
  132. bool exitOnTestEnd = commandLine.HasSwitch(testExitSwitch);
  133. if (commandLine.HasSwitch(testSuiteSwitch))
  134. {
  135. const AZStd::string& testSuitePath = commandLine.GetSwitchValue(testSuiteSwitch, 0);
  136. int randomSeed = 0;
  137. if (commandLine.HasSwitch(testRandomSeed))
  138. {
  139. randomSeed = atoi(commandLine.GetSwitchValue(testRandomSeed, 0).c_str());
  140. }
  141. SampleComponentManagerRequestBus::Broadcast(&SampleComponentManagerRequestBus::Events::RunMainTestSuite, testSuitePath, exitOnTestEnd, randomSeed);
  142. }
  143. }
  144. }
  145. bool AtomSampleViewerApplication::OnOutput(const char* window, const char* message)
  146. {
  147. if (m_logFile)
  148. {
  149. m_logFile->AppendLog(AzFramework::LogFile::SEV_NORMAL, window, message);
  150. }
  151. else if (m_startupLogSink)
  152. {
  153. m_startupLogSink->push_back({ window, message });
  154. }
  155. return false;
  156. }
  157. void AtomSampleViewerApplication::Destroy()
  158. {
  159. m_logFile.reset();
  160. m_startupLogSink.reset();
  161. if (s_connectToAssetProcessor)
  162. {
  163. AzFramework::AssetSystemRequestBus::Broadcast(&AzFramework::AssetSystem::AssetSystemRequests::StartDisconnectingAssetProcessor);
  164. }
  165. Application::Destroy();
  166. }
  167. void AtomSampleViewerApplication::Tick()
  168. {
  169. TickSystem();
  170. Application::Tick();
  171. TickTimeoutShutdown();
  172. }
  173. void AtomSampleViewerApplication::ReadTimeoutShutdown()
  174. {
  175. if (m_commandLine.HasSwitch("timeout"))
  176. {
  177. const AZStd::string& timeoutValue = m_commandLine.GetSwitchValue("timeout", 0);
  178. const float timeoutInSeconds = static_cast<float>(atoi(timeoutValue.c_str()));
  179. AZ_Printf("AtomSampleViewer", "starting up with timeout shutdown of %f seconds", timeoutInSeconds);
  180. m_secondsBeforeShutdown = timeoutInSeconds;
  181. }
  182. }
  183. void AtomSampleViewerApplication::TickTimeoutShutdown()
  184. {
  185. if (m_secondsBeforeShutdown > 0.f)
  186. {
  187. const AZ::TimeUs deltaTimeUs = AZ::GetRealTickDeltaTimeUs();
  188. m_secondsBeforeShutdown -= AZ::TimeUsToSeconds(deltaTimeUs);
  189. if (m_secondsBeforeShutdown <= 0.f)
  190. {
  191. AZ_Printf("AtomSampleViewer", "Timeout reached, shutting down");
  192. AzFramework::ApplicationRequests::Bus::Broadcast(&AzFramework::ApplicationRequests::ExitMainLoop); // or ::TerminateOnError for a more forceful option
  193. }
  194. }
  195. }
  196. void AtomSampleViewerApplication::AssetSystemAvailable()
  197. {
  198. if (s_connectToAssetProcessor)
  199. {
  200. bool connectedToAssetProcessor = false;
  201. // When the AssetProcessor is already launched it should take less than a second to perform a connection
  202. // but when the AssetProcessor needs to be launch it could take up to 15 seconds to have the AssetProcessor initialize
  203. // and able to negotiate a connection when running a debug build
  204. // and to negotiate a connection
  205. AzFramework::AssetSystem::ConnectionSettings connectionSettings;
  206. AzFramework::AssetSystem::ReadConnectionSettingsFromSettingsRegistry(connectionSettings);
  207. connectionSettings.m_connectionDirection = AzFramework::AssetSystem::ConnectionSettings::ConnectionDirection::ConnectToAssetProcessor;
  208. connectionSettings.m_connectionIdentifier = "AtomSampleViewer";
  209. connectionSettings.m_loggingCallback = []([[maybe_unused]] AZStd::string_view logData)
  210. {
  211. AZ_TracePrintf("AtomSampleViewer", "%.*s", aznumeric_cast<int>(logData.size()), logData.data());
  212. };
  213. AzFramework::AssetSystemRequestBus::BroadcastResult(connectedToAssetProcessor,
  214. &AzFramework::AssetSystemRequestBus::Events::EstablishAssetProcessorConnection, connectionSettings);
  215. if (connectedToAssetProcessor)
  216. {
  217. CompileCriticalAssets();
  218. }
  219. }
  220. AzFramework::AssetSystemStatusBus::Handler::BusDisconnect();
  221. }
  222. void AtomSampleViewerApplication::CompileCriticalAssets()
  223. {
  224. // Critical assets for AtomSampleViewer application. They only matter if the application run with AP connection.
  225. const char AssetPaths[][128] =
  226. {
  227. "Shaders/imgui/imgui.azshader",
  228. "Shaders/auxgeom/auxgeomworld.azshader",
  229. "Shaders/auxgeom/auxgeomobject.azshader",
  230. "Shaders/auxgeom/auxgeomobjectlit.azshader"
  231. };
  232. const uint32_t AssetCount = sizeof(AssetPaths) / sizeof(AssetPaths[0]);
  233. // Wait for AP initial scan before we can request compile and sync asset
  234. // The following implementation is similar to CSystem::WaitForAssetProcessorToBeReady()
  235. bool isAssetProcessorReady = false;
  236. AzFramework::AssetSystem::RequestAssetProcessorStatus request;
  237. request.m_platform = "pc";
  238. while (!isAssetProcessorReady)
  239. {
  240. AzFramework::ApplicationRequests::Bus::Broadcast(&AzFramework::ApplicationRequests::PumpSystemEventLoopUntilEmpty);
  241. AzFramework::AssetSystem::ResponseAssetProcessorStatus response;
  242. if (!AzFramework::AssetSystem::SendRequest(request, response))
  243. {
  244. AZ_Warning("AtomSampleViewerApplication", false, "Failed to send Asset Processor Status request for platform %s.", request.m_platform.c_str());
  245. return;
  246. }
  247. else
  248. {
  249. if (response.m_isAssetProcessorReady)
  250. {
  251. isAssetProcessorReady = true;
  252. }
  253. }
  254. AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(100));
  255. }
  256. // Force AP to compile assets and wait for them
  257. // Note: with AssetManager's current implementation, a compiled asset won't be added in asset registry until next system tick.
  258. // So the asset id won't be found right after CompileAssetSync call.
  259. for (uint32_t assetIdx = 0; assetIdx < AssetCount; assetIdx++)
  260. {
  261. // Wait for the shader asset be compiled
  262. AzFramework::AssetSystem::AssetStatus status = AzFramework::AssetSystem::AssetStatus_Unknown;
  263. AzFramework::AssetSystemRequestBus::BroadcastResult(
  264. status, &AzFramework::AssetSystemRequestBus::Events::CompileAssetSync, AssetPaths[assetIdx]);
  265. if (status != AzFramework::AssetSystem::AssetStatus_Compiled)
  266. {
  267. AZ_Error("AtomSampleViewerApplication", false, "Shader asset [%s] error %d", AssetPaths[assetIdx], status);
  268. }
  269. }
  270. }
  271. void AtomSampleViewerApplication::QueryApplicationType(AZ::ApplicationTypeQuery& appType) const
  272. {
  273. appType.m_maskValue = AZ::ApplicationTypeQuery::Masks::Game;
  274. };
  275. int RunGameCommon(int argc, char** argv, AZStd::function<void()> customRunCode)
  276. {
  277. AtomSampleViewer::AtomSampleViewerApplication app(&argc, &argv);
  278. const AzGameFramework::GameApplication::StartupParameters gameAppParams;
  279. app.Start({}, gameAppParams);
  280. if (customRunCode)
  281. {
  282. customRunCode();
  283. }
  284. //GridMate allocator is created in StartCommon
  285. const GridMate::GridMateDesc desc;
  286. GridMate::IGridMate* gridMate = GridMate::GridMateCreate(desc);
  287. AZ_Assert(gridMate, "Failed to create gridmate!");
  288. app.RunMainLoop();
  289. GridMate::GridMateDestroy(gridMate);
  290. app.Stop();
  291. return app.GetExitCode();
  292. }
  293. } // namespace AtomSampleViewer