Engine.cpp 33 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042
  1. //
  2. // Copyright (c) 2008-2016 the Urho3D project.
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to deal
  6. // in the Software without restriction, including without limitation the rights
  7. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. // copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20. // THE SOFTWARE.
  21. //
  22. #include "../Precompiled.h"
  23. #include "../Audio/Audio.h"
  24. #include "../Core/Context.h"
  25. #include "../Core/CoreEvents.h"
  26. #include "../Core/EventProfiler.h"
  27. #include "../Core/ProcessUtils.h"
  28. #include "../Core/WorkQueue.h"
  29. #include "../Engine/Engine.h"
  30. #include "../Graphics/Graphics.h"
  31. #include "../Graphics/Renderer.h"
  32. #include "../Input/Input.h"
  33. #include "../IO/FileSystem.h"
  34. #include "../IO/Log.h"
  35. #include "../IO/PackageFile.h"
  36. // ATOMIC BEGIN
  37. #include "../Resource/XMLFile.h"
  38. // ATOMIC END
  39. #ifdef ATOMIC_NAVIGATION
  40. #include "../Navigation/NavigationMesh.h"
  41. #endif
  42. #ifdef ATOMIC_NETWORK
  43. #include "../Network/Network.h"
  44. #endif
  45. #ifdef ATOMIC_DATABASE
  46. #include "../Database/Database.h"
  47. #endif
  48. #ifdef ATOMIC_PHYSICS
  49. #include "../Physics/PhysicsWorld.h"
  50. #endif
  51. #include "../Resource/ResourceCache.h"
  52. #include "../Resource/Localization.h"
  53. #include "../Scene/Scene.h"
  54. #include "../Scene/SceneEvents.h"
  55. #include "../UI/UI.h"
  56. #ifdef ATOMIC_ATOMIC2D
  57. #include "../Atomic2D/Atomic2D.h"
  58. #endif
  59. #if defined(__EMSCRIPTEN__) && defined(ATOMIC_TESTING)
  60. #include <emscripten/emscripten.h>
  61. #endif
  62. #include "../DebugNew.h"
  63. #if defined(_MSC_VER) && defined(_DEBUG)
  64. // From dbgint.h
  65. #define nNoMansLandSize 4
  66. typedef struct _CrtMemBlockHeader
  67. {
  68. struct _CrtMemBlockHeader* pBlockHeaderNext;
  69. struct _CrtMemBlockHeader* pBlockHeaderPrev;
  70. char* szFileName;
  71. int nLine;
  72. size_t nDataSize;
  73. int nBlockUse;
  74. long lRequest;
  75. unsigned char gap[nNoMansLandSize];
  76. } _CrtMemBlockHeader;
  77. #endif
  78. namespace Atomic
  79. {
  80. extern const char* logLevelPrefixes[];
  81. Engine::Engine(Context* context) :
  82. Object(context),
  83. timeStep_(0.0f),
  84. timeStepSmoothing_(2),
  85. minFps_(10),
  86. #if defined(IOS) || defined(__ANDROID__) || defined(__arm__) || defined(__aarch64__)
  87. maxFps_(60),
  88. maxInactiveFps_(10),
  89. pauseMinimized_(true),
  90. #else
  91. maxFps_(200),
  92. maxInactiveFps_(60),
  93. pauseMinimized_(false),
  94. #endif
  95. #ifdef ATOMIC_TESTING
  96. timeOut_(0),
  97. #endif
  98. autoExit_(true),
  99. initialized_(false),
  100. exiting_(false),
  101. headless_(false),
  102. audioPaused_(false),
  103. // ATOMIC BEGIN
  104. paused_(false),
  105. runNextPausedFrame_(false)
  106. // ATOMIC END
  107. {
  108. // Register self as a subsystem
  109. context_->RegisterSubsystem(this);
  110. // Create subsystems which do not depend on engine initialization or startup parameters
  111. context_->RegisterSubsystem(new Time(context_));
  112. context_->RegisterSubsystem(new WorkQueue(context_));
  113. #ifdef ATOMIC_PROFILING
  114. context_->RegisterSubsystem(new Profiler(context_));
  115. #endif
  116. context_->RegisterSubsystem(new FileSystem(context_));
  117. #ifdef ATOMIC_LOGGING
  118. context_->RegisterSubsystem(new Log(context_));
  119. #endif
  120. context_->RegisterSubsystem(new ResourceCache(context_));
  121. context_->RegisterSubsystem(new Localization(context_));
  122. #ifdef ATOMIC_NETWORK
  123. context_->RegisterSubsystem(new Network(context_));
  124. #endif
  125. #ifdef ATOMIC_DATABASE
  126. context_->RegisterSubsystem(new Database(context_));
  127. #endif
  128. context_->RegisterSubsystem(new Input(context_));
  129. context_->RegisterSubsystem(new Audio(context_));
  130. context_->RegisterSubsystem(new UI(context_));
  131. // Register object factories for libraries which are not automatically registered along with subsystem creation
  132. RegisterSceneLibrary(context_);
  133. #ifdef ATOMIC_PHYSICS
  134. RegisterPhysicsLibrary(context_);
  135. #endif
  136. #ifdef ATOMIC_NAVIGATION
  137. RegisterNavigationLibrary(context_);
  138. #endif
  139. SubscribeToEvent(E_EXITREQUESTED, ATOMIC_HANDLER(Engine, HandleExitRequested));
  140. // ATOMIC BEGIN
  141. SubscribeToEvent(E_PAUSERESUMEREQUESTED, ATOMIC_HANDLER(Engine, HandlePauseResumeRequested));
  142. SubscribeToEvent(E_PAUSESTEPREQUESTED, ATOMIC_HANDLER(Engine, HandlePauseStepRequested));
  143. // ATOMIC END
  144. }
  145. Engine::~Engine()
  146. {
  147. }
  148. bool Engine::Initialize(const VariantMap& parameters)
  149. {
  150. if (initialized_)
  151. return true;
  152. ATOMIC_PROFILE(InitEngine);
  153. // Set headless mode
  154. headless_ = GetParameter(parameters, "Headless", false).GetBool();
  155. // Register the rest of the subsystems
  156. if (!headless_)
  157. {
  158. context_->RegisterSubsystem(new Graphics(context_));
  159. context_->RegisterSubsystem(new Renderer(context_));
  160. }
  161. else
  162. {
  163. // Register graphics library objects explicitly in headless mode to allow them to work without using actual GPU resources
  164. RegisterGraphicsLibrary(context_);
  165. }
  166. #ifdef ATOMIC_ATOMIC2D
  167. // 2D graphics library is dependent on 3D graphics library
  168. RegisterAtomic2DLibrary(context_);
  169. #endif
  170. // Start logging
  171. Log* log = GetSubsystem<Log>();
  172. if (log)
  173. {
  174. if (HasParameter(parameters, "LogLevel"))
  175. log->SetLevel(GetParameter(parameters, "LogLevel").GetInt());
  176. log->SetQuiet(GetParameter(parameters, "LogQuiet", false).GetBool());
  177. log->Open(GetParameter(parameters, "LogName", "Atomic.log").GetString());
  178. }
  179. // Set maximally accurate low res timer
  180. GetSubsystem<Time>()->SetTimerPeriod(1);
  181. // Configure max FPS
  182. if (GetParameter(parameters, "FrameLimiter", true) == false)
  183. SetMaxFps(0);
  184. // Set amount of worker threads according to the available physical CPU cores. Using also hyperthreaded cores results in
  185. // unpredictable extra synchronization overhead. Also reserve one core for the main thread
  186. #ifdef ATOMIC_THREADING
  187. unsigned numThreads = GetParameter(parameters, "WorkerThreads", true).GetBool() ? GetNumPhysicalCPUs() - 1 : 0;
  188. if (numThreads)
  189. {
  190. GetSubsystem<WorkQueue>()->CreateThreads(numThreads);
  191. ATOMIC_LOGINFOF("Created %u worker thread%s", numThreads, numThreads > 1 ? "s" : "");
  192. }
  193. #endif
  194. // Add resource paths
  195. ResourceCache* cache = GetSubsystem<ResourceCache>();
  196. FileSystem* fileSystem = GetSubsystem<FileSystem>();
  197. Vector<String> resourcePrefixPaths = GetParameter(parameters, "ResourcePrefixPaths", String::EMPTY).GetString().Split(';', true);
  198. for (unsigned i = 0; i < resourcePrefixPaths.Size(); ++i)
  199. resourcePrefixPaths[i] = AddTrailingSlash(
  200. IsAbsolutePath(resourcePrefixPaths[i]) ? resourcePrefixPaths[i] : fileSystem->GetProgramDir() + resourcePrefixPaths[i]);
  201. Vector<String> resourcePaths = GetParameter(parameters, "ResourcePaths", "Data;CoreData").GetString().Split(';');
  202. Vector<String> resourcePackages = GetParameter(parameters, "ResourcePackages").GetString().Split(';');
  203. Vector<String> autoLoadPaths = GetParameter(parameters, "AutoloadPaths", "Autoload").GetString().Split(';');
  204. for (unsigned i = 0; i < resourcePaths.Size(); ++i)
  205. {
  206. // If path is not absolute, prefer to add it as a package if possible
  207. if (!IsAbsolutePath(resourcePaths[i]))
  208. {
  209. unsigned j = 0;
  210. for (; j < resourcePrefixPaths.Size(); ++j)
  211. {
  212. // ATOMIC BEGIN
  213. String packageName = resourcePrefixPaths[j] + resourcePaths[i] + PAK_EXTENSION;
  214. // ATOMIC END
  215. if (fileSystem->FileExists(packageName))
  216. {
  217. if (cache->AddPackageFile(packageName))
  218. break;
  219. else
  220. return false; // The root cause of the error should have already been logged
  221. }
  222. String pathName = resourcePrefixPaths[j] + resourcePaths[i];
  223. if (fileSystem->DirExists(pathName))
  224. {
  225. if (cache->AddResourceDir(pathName))
  226. break;
  227. else
  228. return false;
  229. }
  230. }
  231. // ATOMIC: Only fail when CoreData can't be opened and not headless
  232. if (j == resourcePrefixPaths.Size() && !headless_)
  233. {
  234. ATOMIC_LOGERRORF(
  235. "Failed to add resource path '%s', check the documentation on how to set the 'resource prefix path'",
  236. resourcePaths[i].CString());
  237. return false;
  238. }
  239. }
  240. else
  241. {
  242. String pathName = resourcePaths[i];
  243. if (fileSystem->DirExists(pathName))
  244. if (!cache->AddResourceDir(pathName))
  245. return false;
  246. }
  247. }
  248. // Then add specified packages
  249. for (unsigned i = 0; i < resourcePackages.Size(); ++i)
  250. {
  251. unsigned j = 0;
  252. for (; j < resourcePrefixPaths.Size(); ++j)
  253. {
  254. String packageName = resourcePrefixPaths[j] + resourcePackages[i];
  255. if (fileSystem->FileExists(packageName))
  256. {
  257. if (cache->AddPackageFile(packageName))
  258. break;
  259. else
  260. return false;
  261. }
  262. }
  263. // ATOMIC: Only fail when CoreData can't be opened and not headless
  264. if (j == resourcePrefixPaths.Size() && !headless_)
  265. {
  266. ATOMIC_LOGERRORF(
  267. "Failed to add resource package '%s', check the documentation on how to set the 'resource prefix path'",
  268. resourcePackages[i].CString());
  269. return false;
  270. }
  271. }
  272. // Add auto load folders. Prioritize these (if exist) before the default folders
  273. for (unsigned i = 0; i < autoLoadPaths.Size(); ++i)
  274. {
  275. bool autoLoadPathExist = false;
  276. for (unsigned j = 0; j < resourcePrefixPaths.Size(); ++j)
  277. {
  278. String autoLoadPath(autoLoadPaths[i]);
  279. if (!IsAbsolutePath(autoLoadPath))
  280. autoLoadPath = resourcePrefixPaths[j] + autoLoadPath;
  281. if (fileSystem->DirExists(autoLoadPath))
  282. {
  283. autoLoadPathExist = true;
  284. // Add all the subdirs (non-recursive) as resource directory
  285. Vector<String> subdirs;
  286. fileSystem->ScanDir(subdirs, autoLoadPath, "*", SCAN_DIRS, false);
  287. for (unsigned y = 0; y < subdirs.Size(); ++y)
  288. {
  289. String dir = subdirs[y];
  290. if (dir.StartsWith("."))
  291. continue;
  292. String autoResourceDir = autoLoadPath + "/" + dir;
  293. if (!cache->AddResourceDir(autoResourceDir, 0))
  294. return false;
  295. }
  296. // Add all the found package files (non-recursive)
  297. Vector<String> paks;
  298. // ATOMIC BEGIN
  299. fileSystem->ScanDir(paks, autoLoadPath, ToString("*.%s", PAK_EXTENSION), SCAN_FILES, false);
  300. // ATOMIC END
  301. for (unsigned y = 0; y < paks.Size(); ++y)
  302. {
  303. String pak = paks[y];
  304. if (pak.StartsWith("."))
  305. continue;
  306. String autoPackageName = autoLoadPath + "/" + pak;
  307. if (!cache->AddPackageFile(autoPackageName, 0))
  308. return false;
  309. }
  310. }
  311. }
  312. // The following debug message is confusing when user is not aware of the autoload feature
  313. // Especially because the autoload feature is enabled by default without user intervention
  314. // The following extra conditional check below is to suppress unnecessary debug log entry under such default situation
  315. // The cleaner approach is to not enable the autoload by default, i.e. do not use 'Autoload' as default value for 'AutoloadPaths' engine parameter
  316. // However, doing so will break the existing applications that rely on this
  317. if (!autoLoadPathExist && (autoLoadPaths.Size() > 1 || autoLoadPaths[0] != "Autoload"))
  318. ATOMIC_LOGDEBUGF(
  319. "Skipped autoload path '%s' as it does not exist, check the documentation on how to set the 'resource prefix path'",
  320. autoLoadPaths[i].CString());
  321. }
  322. // Initialize graphics & audio output
  323. if (!headless_)
  324. {
  325. Graphics* graphics = GetSubsystem<Graphics>();
  326. Renderer* renderer = GetSubsystem<Renderer>();
  327. if (HasParameter(parameters, "ExternalWindow"))
  328. graphics->SetExternalWindow(GetParameter(parameters, "ExternalWindow").GetVoidPtr());
  329. graphics->SetWindowTitle(GetParameter(parameters, "WindowTitle", "Atomic").GetString());
  330. graphics->SetWindowIcon(cache->GetResource<Image>(GetParameter(parameters, "WindowIcon", String::EMPTY).GetString()));
  331. graphics->SetFlushGPU(GetParameter(parameters, "FlushGPU", false).GetBool());
  332. graphics->SetOrientations(GetParameter(parameters, "Orientations", "LandscapeLeft LandscapeRight").GetString());
  333. if (HasParameter(parameters, "WindowPositionX") && HasParameter(parameters, "WindowPositionY"))
  334. graphics->SetWindowPosition(GetParameter(parameters, "WindowPositionX").GetInt(),
  335. GetParameter(parameters, "WindowPositionY").GetInt());
  336. #ifdef ATOMIC_OPENGL
  337. if (HasParameter(parameters, "ForceGL2"))
  338. graphics->SetForceGL2(GetParameter(parameters, "ForceGL2").GetBool());
  339. #endif
  340. if (!graphics->SetMode(
  341. // ATOMIC BEGIN
  342. GetParameter(parameters, "WindowMaximized", false).GetBool() ? 0 : GetParameter(parameters, "WindowWidth", 0).GetInt(),
  343. GetParameter(parameters, "WindowMaximized", false).GetBool() ? 0 : GetParameter(parameters, "WindowHeight", 0).GetInt(),
  344. // ATOMIC END
  345. GetParameter(parameters, "FullScreen", true).GetBool(),
  346. GetParameter(parameters, "Borderless", false).GetBool(),
  347. GetParameter(parameters, "WindowResizable", false).GetBool(),
  348. GetParameter(parameters, "HighDPI", false).GetBool(),
  349. GetParameter(parameters, "VSync", false).GetBool(),
  350. GetParameter(parameters, "TripleBuffer", false).GetBool(),
  351. GetParameter(parameters, "MultiSample", 1).GetInt()
  352. ))
  353. return false;
  354. if (HasParameter(parameters, "DumpShaders"))
  355. graphics->BeginDumpShaders(GetParameter(parameters, "DumpShaders", String::EMPTY).GetString());
  356. if (HasParameter(parameters, "RenderPath"))
  357. renderer->SetDefaultRenderPath(cache->GetResource<XMLFile>(GetParameter(parameters, "RenderPath").GetString()));
  358. renderer->SetDrawShadows(GetParameter(parameters, "Shadows", true).GetBool());
  359. if (renderer->GetDrawShadows() && GetParameter(parameters, "LowQualityShadows", false).GetBool())
  360. renderer->SetShadowQuality(SHADOWQUALITY_SIMPLE_16BIT);
  361. renderer->SetMaterialQuality(GetParameter(parameters, "MaterialQuality", QUALITY_HIGH).GetInt());
  362. renderer->SetTextureQuality(GetParameter(parameters, "TextureQuality", QUALITY_HIGH).GetInt());
  363. renderer->SetTextureFilterMode((TextureFilterMode)GetParameter(parameters, "TextureFilterMode", FILTER_TRILINEAR).GetInt());
  364. renderer->SetTextureAnisotropy(GetParameter(parameters, "TextureAnisotropy", 4).GetInt());
  365. if (GetParameter(parameters, "Sound", true).GetBool())
  366. {
  367. GetSubsystem<Audio>()->SetMode(
  368. GetParameter(parameters, "SoundBuffer", 100).GetInt(),
  369. GetParameter(parameters, "SoundMixRate", 44100).GetInt(),
  370. GetParameter(parameters, "SoundStereo", true).GetBool(),
  371. GetParameter(parameters, "SoundInterpolation", true).GetBool()
  372. );
  373. }
  374. }
  375. // Init FPU state of main thread
  376. InitFPU();
  377. // Initialize input
  378. if (HasParameter(parameters, "TouchEmulation"))
  379. GetSubsystem<Input>()->SetTouchEmulation(GetParameter(parameters, "TouchEmulation").GetBool());
  380. #ifdef ATOMIC_TESTING
  381. if (HasParameter(parameters, "TimeOut"))
  382. timeOut_ = GetParameter(parameters, "TimeOut", 0).GetInt() * 1000000LL;
  383. #endif
  384. #ifdef ATOMIC_PROFILING
  385. if (GetParameter(parameters, "EventProfiler", true).GetBool())
  386. {
  387. context_->RegisterSubsystem(new EventProfiler(context_));
  388. EventProfiler::SetActive(true);
  389. }
  390. #endif
  391. frameTimer_.Reset();
  392. ATOMIC_LOGINFO("Initialized engine");
  393. initialized_ = true;
  394. return true;
  395. }
  396. void Engine::RunFrame()
  397. {
  398. assert(initialized_);
  399. // If not headless, and the graphics subsystem no longer has a window open, assume we should exit
  400. if (!headless_ && !GetSubsystem<Graphics>()->IsInitialized())
  401. exiting_ = true;
  402. if (exiting_)
  403. return;
  404. // Note: there is a minimal performance cost to looking up subsystems (uses a hashmap); if they would be looked up several
  405. // times per frame it would be better to cache the pointers
  406. Time* time = GetSubsystem<Time>();
  407. Input* input = GetSubsystem<Input>();
  408. Audio* audio = GetSubsystem<Audio>();
  409. #ifdef ATOMIC_PROFILING
  410. if (EventProfiler::IsActive())
  411. {
  412. EventProfiler* eventProfiler = GetSubsystem<EventProfiler>();
  413. if (eventProfiler)
  414. eventProfiler->BeginFrame();
  415. }
  416. #endif
  417. time->BeginFrame(timeStep_);
  418. // ATOMIC BEGIN
  419. // If paused, or pause when minimized -mode is in use, stop updates and audio as necessary
  420. if ((paused_ && !runNextPausedFrame_) ||
  421. (pauseMinimized_ && input->IsMinimized()))
  422. {
  423. if (audio->IsPlaying())
  424. {
  425. audio->Stop();
  426. audioPaused_ = true;
  427. }
  428. }
  429. else
  430. {
  431. // Only unpause when it was paused by the engine
  432. if (audioPaused_)
  433. {
  434. audio->Play();
  435. audioPaused_ = false;
  436. }
  437. // Only run one frame when stepping
  438. runNextPausedFrame_ = false;
  439. Update();
  440. }
  441. // ATOMIC END
  442. Render();
  443. ApplyFrameLimit();
  444. time->EndFrame();
  445. }
  446. Console* Engine::CreateConsole()
  447. {
  448. // ATOMIC BEGIN
  449. /*
  450. if (headless_ || !initialized_)
  451. return 0;
  452. // Return existing console if possible
  453. Console* console = GetSubsystem<Console>();
  454. if (!console)
  455. {
  456. console = new Console(context_);
  457. context_->RegisterSubsystem(console);
  458. }
  459. return console;
  460. */
  461. // ATOMIC END
  462. return 0;
  463. }
  464. DebugHud* Engine::CreateDebugHud()
  465. {
  466. // ATOMIC BEGIN
  467. /*
  468. if (headless_ || !initialized_)
  469. return 0;
  470. // Return existing debug HUD if possible
  471. DebugHud* debugHud = GetSubsystem<DebugHud>();
  472. if (!debugHud)
  473. {
  474. debugHud = new DebugHud(context_);
  475. context_->RegisterSubsystem(debugHud);
  476. }
  477. return debugHud;
  478. */
  479. // ATOMIC END
  480. return 0;
  481. }
  482. void Engine::SetTimeStepSmoothing(int frames)
  483. {
  484. timeStepSmoothing_ = (unsigned)Clamp(frames, 1, 20);
  485. }
  486. void Engine::SetMinFps(int fps)
  487. {
  488. minFps_ = (unsigned)Max(fps, 0);
  489. }
  490. void Engine::SetMaxFps(int fps)
  491. {
  492. maxFps_ = (unsigned)Max(fps, 0);
  493. }
  494. void Engine::SetMaxInactiveFps(int fps)
  495. {
  496. maxInactiveFps_ = (unsigned)Max(fps, 0);
  497. }
  498. void Engine::SetPauseMinimized(bool enable)
  499. {
  500. pauseMinimized_ = enable;
  501. }
  502. void Engine::SetAutoExit(bool enable)
  503. {
  504. // On mobile platforms exit is mandatory if requested by the platform itself and should not be attempted to be disabled
  505. #if defined(__ANDROID__) || defined(IOS)
  506. enable = true;
  507. #endif
  508. autoExit_ = enable;
  509. }
  510. void Engine::SetNextTimeStep(float seconds)
  511. {
  512. timeStep_ = Max(seconds, 0.0f);
  513. }
  514. void Engine::Exit()
  515. {
  516. #if defined(IOS)
  517. // On iOS it's not legal for the application to exit on its own, instead it will be minimized with the home key
  518. #else
  519. DoExit();
  520. #endif
  521. }
  522. void Engine::DumpProfiler()
  523. {
  524. #ifdef ATOMIC_LOGGING
  525. if (!Thread::IsMainThread())
  526. return;
  527. Profiler* profiler = GetSubsystem<Profiler>();
  528. if (profiler)
  529. ATOMIC_LOGRAW(profiler->PrintData(true, true) + "\n");
  530. #endif
  531. }
  532. void Engine::DumpResources(bool dumpFileName)
  533. {
  534. #ifdef ATOMIC_LOGGING
  535. if (!Thread::IsMainThread())
  536. return;
  537. ResourceCache* cache = GetSubsystem<ResourceCache>();
  538. const HashMap<StringHash, ResourceGroup>& resourceGroups = cache->GetAllResources();
  539. if (dumpFileName)
  540. {
  541. ATOMIC_LOGRAW("Used resources:\n");
  542. for (HashMap<StringHash, ResourceGroup>::ConstIterator i = resourceGroups.Begin(); i != resourceGroups.End(); ++i)
  543. {
  544. const HashMap<StringHash, SharedPtr<Resource> >& resources = i->second_.resources_;
  545. if (dumpFileName)
  546. {
  547. for (HashMap<StringHash, SharedPtr<Resource> >::ConstIterator j = resources.Begin(); j != resources.End(); ++j)
  548. ATOMIC_LOGRAW(j->second_->GetName() + "\n");
  549. }
  550. }
  551. }
  552. else
  553. ATOMIC_LOGRAW(cache->PrintMemoryUsage() + "\n");
  554. #endif
  555. }
  556. void Engine::DumpMemory()
  557. {
  558. #ifdef ATOMIC_LOGGING
  559. #if defined(_MSC_VER) && defined(_DEBUG)
  560. _CrtMemState state;
  561. _CrtMemCheckpoint(&state);
  562. _CrtMemBlockHeader* block = state.pBlockHeader;
  563. unsigned total = 0;
  564. unsigned blocks = 0;
  565. for (;;)
  566. {
  567. if (block && block->pBlockHeaderNext)
  568. block = block->pBlockHeaderNext;
  569. else
  570. break;
  571. }
  572. while (block)
  573. {
  574. if (block->nBlockUse > 0)
  575. {
  576. if (block->szFileName)
  577. ATOMIC_LOGRAW("Block " + String((int)block->lRequest) + ": " + String(block->nDataSize) + " bytes, file " + String(block->szFileName) + " line " + String(block->nLine) + "\n");
  578. else
  579. ATOMIC_LOGRAW("Block " + String((int)block->lRequest) + ": " + String(block->nDataSize) + " bytes\n");
  580. total += block->nDataSize;
  581. ++blocks;
  582. }
  583. block = block->pBlockHeaderPrev;
  584. }
  585. ATOMIC_LOGRAW("Total allocated memory " + String(total) + " bytes in " + String(blocks) + " blocks\n\n");
  586. #else
  587. ATOMIC_LOGRAW("DumpMemory() supported on MSVC debug mode only\n\n");
  588. #endif
  589. #endif
  590. }
  591. void Engine::Update()
  592. {
  593. ATOMIC_PROFILE(Update);
  594. // Logic update event
  595. using namespace Update;
  596. VariantMap& eventData = GetEventDataMap();
  597. eventData[P_TIMESTEP] = timeStep_;
  598. SendEvent(E_UPDATE, eventData);
  599. // Logic post-update event
  600. SendEvent(E_POSTUPDATE, eventData);
  601. // Rendering update event
  602. SendEvent(E_RENDERUPDATE, eventData);
  603. // Post-render update event
  604. SendEvent(E_POSTRENDERUPDATE, eventData);
  605. }
  606. void Engine::Render()
  607. {
  608. if (headless_)
  609. return;
  610. ATOMIC_PROFILE(Render);
  611. // If device is lost, BeginFrame will fail and we skip rendering
  612. Graphics* graphics = GetSubsystem<Graphics>();
  613. if (!graphics->BeginFrame())
  614. return;
  615. GetSubsystem<Renderer>()->Render();
  616. GetSubsystem<UI>()->Render();
  617. graphics->EndFrame();
  618. }
  619. void Engine::ApplyFrameLimit()
  620. {
  621. if (!initialized_)
  622. return;
  623. unsigned maxFps = maxFps_;
  624. Input* input = GetSubsystem<Input>();
  625. if (input && !input->HasFocus())
  626. maxFps = Min(maxInactiveFps_, maxFps);
  627. long long elapsed = 0;
  628. #ifndef __EMSCRIPTEN__
  629. // Perform waiting loop if maximum FPS set
  630. #ifndef IOS
  631. if (maxFps)
  632. #else
  633. // If on iOS and target framerate is 60 or above, just let the animation callback handle frame timing
  634. // instead of waiting ourselves
  635. if (maxFps < 60)
  636. #endif
  637. {
  638. ATOMIC_PROFILE(ApplyFrameLimit);
  639. long long targetMax = 1000000LL / maxFps;
  640. for (;;)
  641. {
  642. elapsed = frameTimer_.GetUSec(false);
  643. if (elapsed >= targetMax)
  644. break;
  645. // Sleep if 1 ms or more off the frame limiting goal
  646. if (targetMax - elapsed >= 1000LL)
  647. {
  648. unsigned sleepTime = (unsigned)((targetMax - elapsed) / 1000LL);
  649. Time::Sleep(sleepTime);
  650. }
  651. }
  652. }
  653. #endif
  654. elapsed = frameTimer_.GetUSec(true);
  655. #ifdef ATOMIC_TESTING
  656. if (timeOut_ > 0)
  657. {
  658. timeOut_ -= elapsed;
  659. if (timeOut_ <= 0)
  660. Exit();
  661. }
  662. #endif
  663. // If FPS lower than minimum, clamp elapsed time
  664. if (minFps_)
  665. {
  666. long long targetMin = 1000000LL / minFps_;
  667. if (elapsed > targetMin)
  668. elapsed = targetMin;
  669. }
  670. // Perform timestep smoothing
  671. timeStep_ = 0.0f;
  672. lastTimeSteps_.Push(elapsed / 1000000.0f);
  673. if (lastTimeSteps_.Size() > timeStepSmoothing_)
  674. {
  675. // If the smoothing configuration was changed, ensure correct amount of samples
  676. lastTimeSteps_.Erase(0, lastTimeSteps_.Size() - timeStepSmoothing_);
  677. for (unsigned i = 0; i < lastTimeSteps_.Size(); ++i)
  678. timeStep_ += lastTimeSteps_[i];
  679. timeStep_ /= lastTimeSteps_.Size();
  680. }
  681. else
  682. timeStep_ = lastTimeSteps_.Back();
  683. }
  684. VariantMap Engine::ParseParameters(const Vector<String>& arguments)
  685. {
  686. VariantMap ret;
  687. // Pre-initialize the parameters with environment variable values when they are set
  688. if (const char* paths = getenv("ATOMIC_PREFIX_PATH"))
  689. ret["ResourcePrefixPaths"] = paths;
  690. for (unsigned i = 0; i < arguments.Size(); ++i)
  691. {
  692. if (arguments[i].Length() > 1 && arguments[i][0] == '-')
  693. {
  694. String argument = arguments[i].Substring(1).ToLower();
  695. String value = i + 1 < arguments.Size() ? arguments[i + 1] : String::EMPTY;
  696. if (argument == "headless")
  697. ret["Headless"] = true;
  698. else if (argument == "nolimit")
  699. ret["FrameLimiter"] = false;
  700. else if (argument == "flushgpu")
  701. ret["FlushGPU"] = true;
  702. else if (argument == "gl2")
  703. ret["ForceGL2"] = true;
  704. else if (argument == "landscape")
  705. ret["Orientations"] = "LandscapeLeft LandscapeRight " + ret["Orientations"].GetString();
  706. else if (argument == "portrait")
  707. ret["Orientations"] = "Portrait PortraitUpsideDown " + ret["Orientations"].GetString();
  708. else if (argument == "nosound")
  709. ret["Sound"] = false;
  710. else if (argument == "noip")
  711. ret["SoundInterpolation"] = false;
  712. else if (argument == "mono")
  713. ret["SoundStereo"] = false;
  714. else if (argument == "prepass")
  715. ret["RenderPath"] = "RenderPaths/Prepass.xml";
  716. else if (argument == "deferred")
  717. ret["RenderPath"] = "RenderPaths/Deferred.xml";
  718. else if (argument == "renderpath" && !value.Empty())
  719. {
  720. ret["RenderPath"] = value;
  721. ++i;
  722. }
  723. else if (argument == "noshadows")
  724. ret["Shadows"] = false;
  725. else if (argument == "lqshadows")
  726. ret["LowQualityShadows"] = true;
  727. else if (argument == "nothreads")
  728. ret["WorkerThreads"] = false;
  729. else if (argument == "v")
  730. ret["VSync"] = true;
  731. else if (argument == "t")
  732. ret["TripleBuffer"] = true;
  733. else if (argument == "w")
  734. ret["FullScreen"] = false;
  735. else if (argument == "borderless")
  736. ret["Borderless"] = true;
  737. else if (argument == "s")
  738. ret["WindowResizable"] = true;
  739. else if (argument == "hd")
  740. ret["HighDPI"] = true;
  741. else if (argument == "q")
  742. ret["LogQuiet"] = true;
  743. else if (argument == "log" && !value.Empty())
  744. {
  745. unsigned logLevel = GetStringListIndex(value.CString(), logLevelPrefixes, M_MAX_UNSIGNED);
  746. if (logLevel != M_MAX_UNSIGNED)
  747. {
  748. ret["LogLevel"] = logLevel;
  749. ++i;
  750. }
  751. }
  752. else if (argument == "x" && !value.Empty())
  753. {
  754. ret["WindowWidth"] = ToInt(value);
  755. ++i;
  756. }
  757. else if (argument == "y" && !value.Empty())
  758. {
  759. ret["WindowHeight"] = ToInt(value);
  760. ++i;
  761. }
  762. else if (argument == "m" && !value.Empty())
  763. {
  764. ret["MultiSample"] = ToInt(value);
  765. ++i;
  766. }
  767. else if (argument == "b" && !value.Empty())
  768. {
  769. ret["SoundBuffer"] = ToInt(value);
  770. ++i;
  771. }
  772. else if (argument == "r" && !value.Empty())
  773. {
  774. ret["SoundMixRate"] = ToInt(value);
  775. ++i;
  776. }
  777. else if (argument == "pp" && !value.Empty())
  778. {
  779. ret["ResourcePrefixPaths"] = value;
  780. ++i;
  781. }
  782. else if (argument == "p" && !value.Empty())
  783. {
  784. ret["ResourcePaths"] = value;
  785. ++i;
  786. }
  787. else if (argument == "pf" && !value.Empty())
  788. {
  789. ret["ResourcePackages"] = value;
  790. ++i;
  791. }
  792. else if (argument == "ap" && !value.Empty())
  793. {
  794. ret["AutoloadPaths"] = value;
  795. ++i;
  796. }
  797. else if (argument == "ds" && !value.Empty())
  798. {
  799. ret["DumpShaders"] = value;
  800. ++i;
  801. }
  802. else if (argument == "mq" && !value.Empty())
  803. {
  804. ret["MaterialQuality"] = ToInt(value);
  805. ++i;
  806. }
  807. else if (argument == "tq" && !value.Empty())
  808. {
  809. ret["TextureQuality"] = ToInt(value);
  810. ++i;
  811. }
  812. else if (argument == "tf" && !value.Empty())
  813. {
  814. ret["TextureFilterMode"] = ToInt(value);
  815. ++i;
  816. }
  817. else if (argument == "af" && !value.Empty())
  818. {
  819. ret["TextureFilterMode"] = FILTER_ANISOTROPIC;
  820. ret["TextureAnisotropy"] = ToInt(value);
  821. ++i;
  822. }
  823. else if (argument == "touch")
  824. ret["TouchEmulation"] = true;
  825. // ATOMIC BEGIN
  826. else if (argument == "logname" && !value.Empty())
  827. {
  828. ret["LogName"] = value;
  829. ++i;
  830. }
  831. // ATOMIC END
  832. #ifdef ATOMIC_TESTING
  833. else if (argument == "timeout" && !value.Empty())
  834. {
  835. ret["TimeOut"] = ToInt(value);
  836. ++i;
  837. }
  838. #endif
  839. }
  840. }
  841. return ret;
  842. }
  843. bool Engine::HasParameter(const VariantMap& parameters, const String& parameter)
  844. {
  845. StringHash nameHash(parameter);
  846. return parameters.Find(nameHash) != parameters.End();
  847. }
  848. const Variant& Engine::GetParameter(const VariantMap& parameters, const String& parameter, const Variant& defaultValue)
  849. {
  850. StringHash nameHash(parameter);
  851. VariantMap::ConstIterator i = parameters.Find(nameHash);
  852. return i != parameters.End() ? i->second_ : defaultValue;
  853. }
  854. void Engine::HandleExitRequested(StringHash eventType, VariantMap& eventData)
  855. {
  856. if (autoExit_)
  857. {
  858. // Do not call Exit() here, as it contains mobile platform -specific tests to not exit.
  859. // If we do receive an exit request from the system on those platforms, we must comply
  860. DoExit();
  861. }
  862. }
  863. void Engine::DoExit()
  864. {
  865. Graphics* graphics = GetSubsystem<Graphics>();
  866. if (graphics)
  867. graphics->Close();
  868. exiting_ = true;
  869. #if defined(__EMSCRIPTEN__) && defined(ATOMIC_TESTING)
  870. emscripten_force_exit(EXIT_SUCCESS); // Some how this is required to signal emrun to stop
  871. #endif
  872. }
  873. // ATOMIC BEGIN
  874. void Engine::SetPaused(bool paused)
  875. {
  876. paused_ = paused;
  877. using namespace UpdatesPaused;
  878. // Updates paused event
  879. VariantMap& eventData = GetEventDataMap();
  880. eventData[P_PAUSED] = paused_;
  881. SendEvent(E_UPDATESPAUSEDRESUMED, eventData);
  882. }
  883. void Engine::SetRunNextPausedFrame(bool run)
  884. {
  885. runNextPausedFrame_ = run;
  886. }
  887. void Engine::HandlePauseResumeRequested(StringHash eventType, VariantMap& eventData)
  888. {
  889. SetPaused(!IsPaused());
  890. }
  891. void Engine::HandlePauseStepRequested(StringHash eventType, VariantMap& eventData)
  892. {
  893. if (IsPaused())
  894. {
  895. SetRunNextPausedFrame(true);
  896. }
  897. }
  898. bool Engine::GetDebugBuild() const
  899. {
  900. #ifdef ATOMIC_DEBUG
  901. return true;
  902. #else
  903. return false;
  904. #endif
  905. }
  906. // ATOMIC END
  907. }