AtomSampleViewerApplication.cpp 13 KB

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