AtomSampleViewerApplication.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  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/AzFrameworkNativeUIModule.h>
  18. #include <AzFramework/Components/NativeUISystemComponent.h>
  19. #include <AzFramework/IO/LocalFileIO.h>
  20. #include <AzFramework/Network/AssetProcessorConnection.h>
  21. #include <AzFramework/StringFunc/StringFunc.h>
  22. #include <Atom/RPI.Public/RPISystemInterface.h>
  23. #include <AzCore/Math/Random.h>
  24. #include <AzCore/Script/ScriptAsset.h>
  25. #include <AzCore/Settings/SettingsRegistryMergeUtils.h>
  26. #include <AzCore/Utils/Utils.h>
  27. #include <AzGameFramework/Application/GameApplication.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, "CriticalAssetsCompiled", R"({})");
  83. }
  84. ReadTimeoutShutdown();
  85. WriteStartupLog();
  86. }
  87. void AtomSampleViewerApplication::OnSampleManagerActivated()
  88. {
  89. ReadAutomatedTestOptions();
  90. // enable native UI for some error messages if it's not test mode
  91. if (!m_isTestMode)
  92. {
  93. if (auto nativeUI = AZ::Interface<AZ::NativeUI::NativeUIRequests>::Get(); nativeUI != nullptr)
  94. {
  95. nativeUI->SetMode(AZ::NativeUI::Mode::ENABLED);
  96. }
  97. }
  98. }
  99. void AtomSampleViewerApplication::CreateStaticModules(AZStd::vector<AZ::Module*>& outModules)
  100. {
  101. AzFramework::Application::CreateStaticModules(outModules);
  102. outModules.push_back(aznew AzFramework::AzFrameworkNativeUIModule());
  103. }
  104. void AtomSampleViewerApplication::WriteStartupLog()
  105. {
  106. AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance();
  107. AZ_Assert(fileIO != nullptr, "FileIO should be running at this point");
  108. // There is no log system online so we have to create your own log file.
  109. char resolveBuffer[AZ_MAX_PATH_LEN] = { 0 };
  110. fileIO->ResolvePath("@user@", resolveBuffer, AZ_MAX_PATH_LEN);
  111. // Note: @log@ hasn't been set at this point in the standalone AtomSampleViewer
  112. AZStd::string logDirectory;
  113. AzFramework::StringFunc::Path::Join(resolveBuffer, "log", logDirectory);
  114. fileIO->SetAlias("@log@", logDirectory.c_str());
  115. fileIO->CreatePath("@products@");
  116. fileIO->CreatePath("@user@");
  117. fileIO->CreatePath("@log@");
  118. AZStd::string logPath;
  119. AzFramework::StringFunc::Path::Join(logDirectory.c_str(), s_logFileBaseName, logPath);
  120. using namespace AzFramework;
  121. m_logFile.reset(aznew LogFile(logPath.c_str()));
  122. if (m_logFile)
  123. {
  124. m_logFile->SetMachineReadable(false);
  125. for (const LogMessage& message : *m_startupLogSink)
  126. {
  127. m_logFile->AppendLog(AzFramework::LogFile::SEV_NORMAL, message.window.c_str(), message.message.c_str());
  128. }
  129. m_startupLogSink->clear();
  130. m_logFile->FlushLog();
  131. }
  132. }
  133. void AtomSampleViewerApplication::ReadAutomatedTestOptions()
  134. {
  135. if (GetArgC() != nullptr && GetArgV() != nullptr)
  136. {
  137. AzFramework::CommandLine commandLine;
  138. commandLine.Parse(*GetArgC(), *GetArgV());
  139. constexpr const char* testSuiteSwitch = "runtestsuite";
  140. constexpr const char* testExitSwitch = "exitontestend";
  141. constexpr const char* testRandomSeed = "randomtestseed";
  142. bool exitOnTestEnd = commandLine.HasSwitch(testExitSwitch);
  143. if (commandLine.HasSwitch(testSuiteSwitch))
  144. {
  145. const AZStd::string& testSuitePath = commandLine.GetSwitchValue(testSuiteSwitch, 0);
  146. int randomSeed = 0;
  147. if (commandLine.HasSwitch(testRandomSeed))
  148. {
  149. randomSeed = atoi(commandLine.GetSwitchValue(testRandomSeed, 0).c_str());
  150. }
  151. SampleComponentManagerRequestBus::Broadcast(&SampleComponentManagerRequestBus::Events::RunMainTestSuite, testSuitePath, exitOnTestEnd, randomSeed);
  152. m_isTestMode = true;
  153. }
  154. }
  155. }
  156. bool AtomSampleViewerApplication::OnOutput(const char* window, const char* message)
  157. {
  158. if (m_logFile)
  159. {
  160. m_logFile->AppendLog(AzFramework::LogFile::SEV_NORMAL, window, message);
  161. }
  162. else if (m_startupLogSink)
  163. {
  164. m_startupLogSink->push_back({ window, message });
  165. }
  166. return false;
  167. }
  168. void AtomSampleViewerApplication::Destroy()
  169. {
  170. m_logFile.reset();
  171. m_startupLogSink.reset();
  172. if (s_connectToAssetProcessor)
  173. {
  174. AzFramework::AssetSystemRequestBus::Broadcast(&AzFramework::AssetSystem::AssetSystemRequests::StartDisconnectingAssetProcessor);
  175. }
  176. Application::Destroy();
  177. }
  178. AZ::ComponentTypeList AtomSampleViewerApplication::GetRequiredSystemComponents() const
  179. {
  180. AZ::ComponentTypeList components = AzFramework::Application::GetRequiredSystemComponents();
  181. components.insert(
  182. components.end(),
  183. {
  184. azrtti_typeid<AzFramework::NativeUISystemComponent>(),
  185. });
  186. return components;
  187. }
  188. void AtomSampleViewerApplication::Tick()
  189. {
  190. TickSystem();
  191. Application::Tick();
  192. TickTimeoutShutdown();
  193. }
  194. void AtomSampleViewerApplication::ReadTimeoutShutdown()
  195. {
  196. if (m_commandLine.HasSwitch("timeout"))
  197. {
  198. const AZStd::string& timeoutValue = m_commandLine.GetSwitchValue("timeout", 0);
  199. const float timeoutInSeconds = static_cast<float>(atoi(timeoutValue.c_str()));
  200. AZ_Printf("AtomSampleViewer", "starting up with timeout shutdown of %f seconds", timeoutInSeconds);
  201. m_secondsBeforeShutdown = timeoutInSeconds;
  202. }
  203. }
  204. void AtomSampleViewerApplication::TickTimeoutShutdown()
  205. {
  206. if (m_secondsBeforeShutdown > 0.f)
  207. {
  208. const AZ::TimeUs deltaTimeUs = AZ::GetRealTickDeltaTimeUs();
  209. m_secondsBeforeShutdown -= AZ::TimeUsToSeconds(deltaTimeUs);
  210. if (m_secondsBeforeShutdown <= 0.f)
  211. {
  212. AZ_Printf("AtomSampleViewer", "Timeout reached, shutting down");
  213. AzFramework::ApplicationRequests::Bus::Broadcast(&AzFramework::ApplicationRequests::ExitMainLoop); // or ::TerminateOnError for a more forceful option
  214. }
  215. }
  216. }
  217. void AtomSampleViewerApplication::AssetSystemAvailable()
  218. {
  219. if (s_connectToAssetProcessor)
  220. {
  221. bool connectedToAssetProcessor = false;
  222. // When the AssetProcessor is already launched it should take less than a second to perform a connection
  223. // but when the AssetProcessor needs to be launch it could take up to 15 seconds to have the AssetProcessor initialize
  224. // and able to negotiate a connection when running a debug build
  225. // and to negotiate a connection
  226. AzFramework::AssetSystem::ConnectionSettings connectionSettings;
  227. AzFramework::AssetSystem::ReadConnectionSettingsFromSettingsRegistry(connectionSettings);
  228. connectionSettings.m_connectionDirection = AzFramework::AssetSystem::ConnectionSettings::ConnectionDirection::ConnectToAssetProcessor;
  229. connectionSettings.m_connectionIdentifier = "AtomSampleViewer";
  230. connectionSettings.m_loggingCallback = []([[maybe_unused]] AZStd::string_view logData)
  231. {
  232. AZ_TracePrintf("AtomSampleViewer", "%.*s", aznumeric_cast<int>(logData.size()), logData.data());
  233. };
  234. AzFramework::AssetSystemRequestBus::BroadcastResult(connectedToAssetProcessor,
  235. &AzFramework::AssetSystemRequestBus::Events::EstablishAssetProcessorConnection, connectionSettings);
  236. if (connectedToAssetProcessor)
  237. {
  238. CompileCriticalAssets();
  239. }
  240. }
  241. AzFramework::AssetSystemStatusBus::Handler::BusDisconnect();
  242. }
  243. void AtomSampleViewerApplication::CompileCriticalAssets()
  244. {
  245. // Critical assets for AtomSampleViewer application. They only matter if the application run with AP connection.
  246. const char AssetPaths[][128] =
  247. {
  248. "Shaders/imgui/imgui.azshader",
  249. "Shaders/auxgeom/auxgeomworld.azshader",
  250. "Shaders/auxgeom/auxgeomobject.azshader",
  251. "Shaders/auxgeom/auxgeomobjectlit.azshader"
  252. };
  253. const uint32_t AssetCount = sizeof(AssetPaths) / sizeof(AssetPaths[0]);
  254. // Wait for AP initial scan before we can request compile and sync asset
  255. // The following implementation is similar to CSystem::WaitForAssetProcessorToBeReady()
  256. bool isAssetProcessorReady = false;
  257. AzFramework::AssetSystem::RequestAssetProcessorStatus request;
  258. request.m_platform = "pc";
  259. while (!isAssetProcessorReady)
  260. {
  261. AzFramework::ApplicationRequests::Bus::Broadcast(&AzFramework::ApplicationRequests::PumpSystemEventLoopUntilEmpty);
  262. AzFramework::AssetSystem::ResponseAssetProcessorStatus response;
  263. if (!AzFramework::AssetSystem::SendRequest(request, response))
  264. {
  265. AZ_Warning("AtomSampleViewerApplication", false, "Failed to send Asset Processor Status request for platform %s.", request.m_platform.c_str());
  266. return;
  267. }
  268. else
  269. {
  270. if (response.m_isAssetProcessorReady)
  271. {
  272. isAssetProcessorReady = true;
  273. }
  274. }
  275. AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(100));
  276. }
  277. // Force AP to compile assets and wait for them
  278. // Note: with AssetManager's current implementation, a compiled asset won't be added in asset registry until next system tick.
  279. // So the asset id won't be found right after CompileAssetSync call.
  280. for (uint32_t assetIdx = 0; assetIdx < AssetCount; assetIdx++)
  281. {
  282. // Wait for the shader asset be compiled
  283. AzFramework::AssetSystem::AssetStatus status = AzFramework::AssetSystem::AssetStatus_Unknown;
  284. AzFramework::AssetSystemRequestBus::BroadcastResult(
  285. status, &AzFramework::AssetSystemRequestBus::Events::CompileAssetSync, AssetPaths[assetIdx]);
  286. if (status != AzFramework::AssetSystem::AssetStatus_Compiled)
  287. {
  288. AZ_Error("AtomSampleViewerApplication", false, "Shader asset [%s] error %d", AssetPaths[assetIdx], status);
  289. }
  290. }
  291. }
  292. void AtomSampleViewerApplication::QueryApplicationType(AZ::ApplicationTypeQuery& appType) const
  293. {
  294. appType.m_maskValue = AZ::ApplicationTypeQuery::Masks::Game;
  295. };
  296. int RunGameCommon(int argc, char** argv, AZStd::function<void()> customRunCode)
  297. {
  298. const AZ::Debug::Trace tracer;
  299. AtomSampleViewer::AtomSampleViewerApplication app(&argc, &argv);
  300. const AzGameFramework::GameApplication::StartupParameters gameAppParams;
  301. app.Start({}, gameAppParams);
  302. if (customRunCode)
  303. {
  304. customRunCode();
  305. }
  306. app.RunMainLoop();
  307. app.Stop();
  308. return app.GetExitCode();
  309. }
  310. } // namespace AtomSampleViewer