App.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686
  1. // Copyright (C) 2009-2023, Panagiotis Christopoulos Charitos and contributors.
  2. // All rights reserved.
  3. // Code licensed under the BSD License.
  4. // http://www.anki3d.org/LICENSE
  5. #include <AnKi/Core/App.h>
  6. #include <AnKi/Core/ConfigSet.h>
  7. #include <AnKi/Core/GpuMemory/UnifiedGeometryBuffer.h>
  8. #include <AnKi/Util/Logger.h>
  9. #include <AnKi/Util/File.h>
  10. #include <AnKi/Util/Filesystem.h>
  11. #include <AnKi/Util/System.h>
  12. #include <AnKi/Util/ThreadHive.h>
  13. #include <AnKi/Util/Tracer.h>
  14. #include <AnKi/Util/HighRezTimer.h>
  15. #include <AnKi/Core/CoreTracer.h>
  16. #include <AnKi/Core/GpuMemory/RebarTransientMemoryPool.h>
  17. #include <AnKi/Core/GpuMemory/GpuVisibleTransientMemoryPool.h>
  18. #include <AnKi/Core/GpuMemory/GpuReadbackMemoryPool.h>
  19. #include <AnKi/Core/DeveloperConsole.h>
  20. #include <AnKi/Core/StatsUi.h>
  21. #include <AnKi/Core/StatsSet.h>
  22. #include <AnKi/Window/NativeWindow.h>
  23. #include <AnKi/Core/MaliHwCounters.h>
  24. #include <AnKi/Window/Input.h>
  25. #include <AnKi/Scene/SceneGraph.h>
  26. #include <AnKi/Renderer/RenderQueue.h>
  27. #include <AnKi/Resource/ResourceManager.h>
  28. #include <AnKi/Physics/PhysicsWorld.h>
  29. #include <AnKi/Renderer/MainRenderer.h>
  30. #include <AnKi/Script/ScriptManager.h>
  31. #include <AnKi/Resource/ResourceFilesystem.h>
  32. #include <AnKi/Resource/AsyncLoader.h>
  33. #include <AnKi/Ui/UiManager.h>
  34. #include <AnKi/Ui/Canvas.h>
  35. #include <csignal>
  36. #if ANKI_OS_ANDROID
  37. # include <android_native_app_glue.h>
  38. #endif
  39. namespace anki {
  40. #if ANKI_OS_ANDROID
  41. /// The one and only android hack
  42. android_app* g_androidApp = nullptr;
  43. #endif
  44. StatCounter g_cpuTotalTime(StatCategory::kTime, "CPU total", StatFlag::kMilisecond | StatFlag::kShowAverage);
  45. static StatCounter g_cpuAllocatedMem(StatCategory::kCpuMem, "Total", StatFlag::kBytes | StatFlag::kThreadSafe);
  46. static StatCounter g_cpuAllocationCount(StatCategory::kCpuMem, "Allocations/frame",
  47. StatFlag::kBytes | StatFlag::kZeroEveryFrame | StatFlag::kThreadSafe);
  48. static StatCounter g_cpuFreesCount(StatCategory::kCpuMem, "Frees/frame", StatFlag::kBytes | StatFlag::kZeroEveryFrame | StatFlag::kThreadSafe);
  49. static NumericCVar<U32> g_widthCVar(CVarSubsystem::kCore, "Width", 1920, 16, 16 * 1024, "Width");
  50. static NumericCVar<U32> g_heightCVar(CVarSubsystem::kCore, "Height", 1080, 16, 16 * 1024, "Height");
  51. NumericCVar<U32> g_windowFullscreenCVar(CVarSubsystem::kCore, "WindowFullscreen", 1, 0, 2,
  52. "0: windowed, 1: borderless fullscreen, 2: exclusive fullscreen");
  53. NumericCVar<U32> g_targetFpsCVar(CVarSubsystem::kCore, "TargetFps", 60u, 1u, kMaxU32, "Target FPS");
  54. static NumericCVar<U32> g_jobThreadCountCVar(CVarSubsystem::kCore, "JobThreadCount", max(2u, getCpuCoresCount() / 2u), 2u, 1024u,
  55. "Number of job thread");
  56. NumericCVar<U32> g_displayStatsCVar(CVarSubsystem::kCore, "DisplayStats", 0, 0, 2, "Display stats, 0: None, 1: Simple, 2: Detailed");
  57. BoolCVar g_clearCachesCVar(CVarSubsystem::kCore, "ClearCaches", false, "Clear all caches");
  58. BoolCVar g_verboseLogCVar(CVarSubsystem::kCore, "VerboseLog", false, "Verbose logging");
  59. BoolCVar g_benchmarkModeCVar(CVarSubsystem::kCore, "BenchmarkMode", false, "Run in a benchmark mode. Fixed timestep, unlimited target FPS");
  60. NumericCVar<U32> g_benchmarkModeFrameCountCVar(CVarSubsystem::kCore, "BenchmarkModeFrameCount", 60 * 60 * 2, 1, kMaxU32,
  61. "How many frames the benchmark will run before it quits");
  62. NumericCVar<F32> g_lod0MaxDistanceCVar(CVarSubsystem::kCore, "Lod0MaxDistance", 20.0f, 1.0f, kMaxF32,
  63. "Distance that will be used to calculate the LOD 0");
  64. NumericCVar<F32> g_lod1MaxDistanceCVar(CVarSubsystem::kCore, "Lod1MaxDistance", 40.0f, 2.0f, kMaxF32,
  65. "Distance that will be used to calculate the LOD 1");
  66. #if ANKI_PLATFORM_MOBILE
  67. static StatCounter g_maliGpuActive(StatCategory::kGpuMisc, "Mali active cycles");
  68. static StatCounter g_maliGpuReadBandwidth(StatCategory::kGpuMisc, "Mali read bandwidth");
  69. static StatCounter g_maliGpuWriteBandwidth(StatCategory::kGpuMisc, "Mali write bandwidth");
  70. static BoolCVar g_maliHwCountersCVar(CVarSubsystem::kCore, "MaliHwCounters", false, "Enable Mali counters");
  71. #endif
  72. void* App::statsAllocCallback(void* userData, void* ptr, PtrSize size, [[maybe_unused]] PtrSize alignment)
  73. {
  74. ANKI_ASSERT(userData);
  75. constexpr PtrSize kMaxAlignment = 64;
  76. struct alignas(kMaxAlignment) Header
  77. {
  78. PtrSize m_allocatedSize;
  79. Array<U8, kMaxAlignment - sizeof(PtrSize)> _m_padding;
  80. };
  81. static_assert(sizeof(Header) == kMaxAlignment, "See file");
  82. static_assert(alignof(Header) == kMaxAlignment, "See file");
  83. void* out = nullptr;
  84. if(ptr == nullptr)
  85. {
  86. // Need to allocate
  87. ANKI_ASSERT(size > 0);
  88. ANKI_ASSERT(alignment > 0 && alignment <= kMaxAlignment);
  89. const PtrSize newAlignment = kMaxAlignment;
  90. const PtrSize newSize = sizeof(Header) + size;
  91. // Allocate
  92. App* self = static_cast<App*>(userData);
  93. Header* allocation = static_cast<Header*>(self->m_originalAllocCallback(self->m_originalAllocUserData, nullptr, newSize, newAlignment));
  94. allocation->m_allocatedSize = size;
  95. ++allocation;
  96. out = static_cast<void*>(allocation);
  97. // Update stats
  98. g_cpuAllocatedMem.atomicIncrement(size);
  99. g_cpuAllocationCount.atomicIncrement(1);
  100. }
  101. else
  102. {
  103. // Need to free
  104. App* self = static_cast<App*>(userData);
  105. Header* allocation = static_cast<Header*>(ptr);
  106. --allocation;
  107. ANKI_ASSERT(allocation->m_allocatedSize > 0);
  108. // Update stats
  109. g_cpuAllocatedMem.atomicDecrement(allocation->m_allocatedSize);
  110. g_cpuFreesCount.atomicIncrement(1);
  111. // Free
  112. self->m_originalAllocCallback(self->m_originalAllocUserData, allocation, 0, 0);
  113. }
  114. return out;
  115. }
  116. App::App(AllocAlignedCallback allocCb, void* allocCbUserData)
  117. {
  118. m_originalAllocCallback = allocCb;
  119. m_originalAllocUserData = allocCbUserData;
  120. }
  121. App::~App()
  122. {
  123. ANKI_CORE_LOGI("Destroying application");
  124. cleanup();
  125. }
  126. void App::cleanup()
  127. {
  128. m_statsUi.reset(nullptr);
  129. m_console.reset(nullptr);
  130. SceneGraph::freeSingleton();
  131. ScriptManager::freeSingleton();
  132. MainRenderer::freeSingleton();
  133. UiManager::freeSingleton();
  134. GpuSceneMicroPatcher::freeSingleton();
  135. ResourceManager::freeSingleton();
  136. PhysicsWorld::freeSingleton();
  137. RebarTransientMemoryPool::freeSingleton();
  138. GpuVisibleTransientMemoryPool::freeSingleton();
  139. UnifiedGeometryBuffer::freeSingleton();
  140. GpuSceneBuffer::freeSingleton();
  141. GpuReadbackMemoryPool::freeSingleton();
  142. CoreThreadHive::freeSingleton();
  143. MaliHwCounters::freeSingleton();
  144. GrManager::freeSingleton();
  145. Input::freeSingleton();
  146. NativeWindow::freeSingleton();
  147. #if ANKI_TRACING_ENABLED
  148. CoreTracer::freeSingleton();
  149. #endif
  150. GlobalFrameIndex::freeSingleton();
  151. m_settingsDir.destroy();
  152. m_cacheDir.destroy();
  153. CoreMemoryPool::freeSingleton();
  154. DefaultMemoryPool::freeSingleton();
  155. }
  156. Error App::init()
  157. {
  158. const Error err = initInternal();
  159. if(err)
  160. {
  161. ANKI_CORE_LOGE("App initialization failed. Shutting down");
  162. cleanup();
  163. }
  164. return err;
  165. }
  166. Error App::initInternal()
  167. {
  168. Logger::getSingleton().enableVerbosity(g_verboseLogCVar.get());
  169. setSignalHandlers();
  170. AllocAlignedCallback allocCb = m_originalAllocCallback;
  171. void* allocCbUserData = m_originalAllocUserData;
  172. initMemoryCallbacks(allocCb, allocCbUserData);
  173. DefaultMemoryPool::allocateSingleton(allocCb, allocCbUserData);
  174. CoreMemoryPool::allocateSingleton(allocCb, allocCbUserData);
  175. ANKI_CHECK(initDirs());
  176. // Print a message
  177. const char* buildType =
  178. #if ANKI_OPTIMIZE
  179. "optimized, "
  180. #else
  181. "NOT optimized, "
  182. #endif
  183. #if ANKI_DEBUG_SYMBOLS
  184. "dbg symbols, "
  185. #else
  186. "NO dbg symbols, "
  187. #endif
  188. #if ANKI_EXTRA_CHECKS
  189. "extra checks, "
  190. #else
  191. "NO extra checks, "
  192. #endif
  193. #if ANKI_TRACING_ENABLED
  194. "built with tracing";
  195. #else
  196. "NOT built with tracing";
  197. #endif
  198. ANKI_CORE_LOGI("Initializing application ("
  199. "version %u.%u, "
  200. "%s, "
  201. "compiler %s, "
  202. "build date %s, "
  203. "commit %s)",
  204. ANKI_VERSION_MAJOR, ANKI_VERSION_MINOR, buildType, ANKI_COMPILER_STR, __DATE__, ANKI_REVISION);
  205. // Check SIMD support
  206. #if ANKI_SIMD_SSE && ANKI_COMPILER_GCC_COMPATIBLE
  207. if(!__builtin_cpu_supports("sse4.2"))
  208. {
  209. ANKI_CORE_LOGF("AnKi is built with sse4.2 support but your CPU doesn't support it. Try bulding without SSE support");
  210. }
  211. #endif
  212. ANKI_CORE_LOGI("Number of job threads: %u", g_jobThreadCountCVar.get());
  213. if(g_benchmarkModeCVar.get() && g_vsyncCVar.get())
  214. {
  215. ANKI_CORE_LOGW("Vsync is enabled and benchmark mode as well. Will turn vsync off");
  216. g_vsyncCVar.set(false);
  217. }
  218. GlobalFrameIndex::allocateSingleton();
  219. //
  220. // Core tracer
  221. //
  222. #if ANKI_TRACING_ENABLED
  223. ANKI_CHECK(CoreTracer::allocateSingleton().init(m_settingsDir));
  224. #endif
  225. //
  226. // Window
  227. //
  228. NativeWindowInitInfo nwinit;
  229. nwinit.m_width = g_widthCVar.get();
  230. nwinit.m_height = g_heightCVar.get();
  231. nwinit.m_depthBits = 0;
  232. nwinit.m_stencilBits = 0;
  233. nwinit.m_fullscreenDesktopRez = g_windowFullscreenCVar.get() > 0;
  234. nwinit.m_exclusiveFullscreen = g_windowFullscreenCVar.get() == 2;
  235. nwinit.m_targetFps = g_targetFpsCVar.get();
  236. NativeWindow::allocateSingleton();
  237. ANKI_CHECK(NativeWindow::getSingleton().init(nwinit));
  238. //
  239. // Input
  240. //
  241. Input::allocateSingleton();
  242. ANKI_CHECK(Input::getSingleton().init());
  243. //
  244. // ThreadPool
  245. //
  246. const Bool pinThreads = !ANKI_OS_ANDROID;
  247. CoreThreadHive::allocateSingleton(g_jobThreadCountCVar.get(), pinThreads);
  248. //
  249. // Graphics API
  250. //
  251. GrManagerInitInfo grInit;
  252. grInit.m_allocCallback = allocCb;
  253. grInit.m_allocCallbackUserData = allocCbUserData;
  254. grInit.m_cacheDirectory = m_cacheDir.toCString();
  255. ANKI_CHECK(GrManager::allocateSingleton().init(grInit));
  256. //
  257. // Mali HW counters
  258. //
  259. #if ANKI_PLATFORM_MOBILE
  260. if(ANKI_STATS_ENABLED && GrManager::getSingleton().getDeviceCapabilities().m_gpuVendor == GpuVendor::kArm && g_maliHwCountersCVar.get())
  261. {
  262. MaliHwCounters::allocateSingleton();
  263. }
  264. #endif
  265. //
  266. // GPU mem
  267. //
  268. UnifiedGeometryBuffer::allocateSingleton().init();
  269. GpuSceneBuffer::allocateSingleton().init();
  270. RebarTransientMemoryPool::allocateSingleton().init();
  271. GpuVisibleTransientMemoryPool::allocateSingleton();
  272. GpuReadbackMemoryPool::allocateSingleton();
  273. //
  274. // Physics
  275. //
  276. PhysicsWorld::allocateSingleton();
  277. ANKI_CHECK(PhysicsWorld::getSingleton().init(allocCb, allocCbUserData));
  278. //
  279. // Resources
  280. //
  281. #if !ANKI_OS_ANDROID
  282. // Add the location of the executable where the shaders are supposed to be
  283. String executableFname;
  284. ANKI_CHECK(getApplicationPath(executableFname));
  285. ANKI_CORE_LOGI("Executable path is: %s", executableFname.cstr());
  286. String shadersPath;
  287. getParentFilepath(executableFname, shadersPath);
  288. shadersPath += ":";
  289. shadersPath += g_dataPathsCVar.get();
  290. g_dataPathsCVar.set(shadersPath);
  291. #endif
  292. ANKI_CHECK(ResourceManager::allocateSingleton().init(allocCb, allocCbUserData));
  293. //
  294. // UI
  295. //
  296. ANKI_CHECK(UiManager::allocateSingleton().init(allocCb, allocCbUserData));
  297. //
  298. // GPU scene
  299. //
  300. ANKI_CHECK(GpuSceneMicroPatcher::allocateSingleton().init());
  301. //
  302. // Renderer
  303. //
  304. MainRendererInitInfo renderInit;
  305. renderInit.m_swapchainSize = UVec2(NativeWindow::getSingleton().getWidth(), NativeWindow::getSingleton().getHeight());
  306. renderInit.m_allocCallback = allocCb;
  307. renderInit.m_allocCallbackUserData = allocCbUserData;
  308. ANKI_CHECK(MainRenderer::allocateSingleton().init(renderInit));
  309. //
  310. // Script
  311. //
  312. ScriptManager::allocateSingleton(allocCb, allocCbUserData);
  313. //
  314. // Scene
  315. //
  316. ANKI_CHECK(SceneGraph::allocateSingleton().init(allocCb, allocCbUserData));
  317. //
  318. // Misc
  319. //
  320. ANKI_CHECK(UiManager::getSingleton().newInstance<StatsUi>(m_statsUi));
  321. ANKI_CHECK(UiManager::getSingleton().newInstance<DeveloperConsole>(m_console));
  322. ANKI_CORE_LOGI("Application initialized");
  323. return Error::kNone;
  324. }
  325. Error App::initDirs()
  326. {
  327. // Settings path
  328. #if !ANKI_OS_ANDROID
  329. String home;
  330. ANKI_CHECK(getHomeDirectory(home));
  331. m_settingsDir.sprintf("%s/.anki", &home[0]);
  332. #else
  333. m_settingsDir.sprintf("%s/.anki", g_androidApp->activity->internalDataPath);
  334. #endif
  335. if(!directoryExists(m_settingsDir.toCString()))
  336. {
  337. ANKI_CORE_LOGI("Creating settings dir \"%s\"", &m_settingsDir[0]);
  338. ANKI_CHECK(createDirectory(m_settingsDir.toCString()));
  339. }
  340. else
  341. {
  342. ANKI_CORE_LOGI("Using settings dir \"%s\"", &m_settingsDir[0]);
  343. }
  344. // Cache
  345. m_cacheDir.sprintf("%s/cache", &m_settingsDir[0]);
  346. const Bool cacheDirExists = directoryExists(m_cacheDir.toCString());
  347. if(g_clearCachesCVar.get() && cacheDirExists)
  348. {
  349. ANKI_CORE_LOGI("Will delete the cache dir and start fresh: %s", m_cacheDir.cstr());
  350. ANKI_CHECK(removeDirectory(m_cacheDir.toCString()));
  351. ANKI_CHECK(createDirectory(m_cacheDir.toCString()));
  352. }
  353. else if(!cacheDirExists)
  354. {
  355. ANKI_CORE_LOGI("Will create cache dir: %s", m_cacheDir.cstr());
  356. ANKI_CHECK(createDirectory(m_cacheDir.toCString()));
  357. }
  358. return Error::kNone;
  359. }
  360. Error App::mainLoop()
  361. {
  362. ANKI_CORE_LOGI("Entering main loop");
  363. Bool quit = false;
  364. Second prevUpdateTime = HighRezTimer::getCurrentTime();
  365. Second crntTime = prevUpdateTime;
  366. // Benchmark mode stuff:
  367. const Bool benchmarkMode = g_benchmarkModeCVar.get();
  368. Second aggregatedCpuTime = 0.0;
  369. Second aggregatedGpuTime = 0.0;
  370. constexpr U32 kBenchmarkFramesToGatherBeforeFlush = 60;
  371. U32 benchmarkFramesGathered = 0;
  372. File benchmarkCsvFile;
  373. CoreString benchmarkCsvFileFilename;
  374. if(benchmarkMode)
  375. {
  376. benchmarkCsvFileFilename.sprintf("%s/Benchmark.csv", m_settingsDir.cstr());
  377. ANKI_CHECK(benchmarkCsvFile.open(benchmarkCsvFileFilename, FileOpenFlag::kWrite));
  378. ANKI_CHECK(benchmarkCsvFile.writeText("CPU, GPU\n"));
  379. }
  380. while(!quit)
  381. {
  382. {
  383. ANKI_TRACE_SCOPED_EVENT(Frame);
  384. const Second startTime = HighRezTimer::getCurrentTime();
  385. prevUpdateTime = crntTime;
  386. crntTime = (!benchmarkMode) ? HighRezTimer::getCurrentTime() : (prevUpdateTime + 1.0_sec / 60.0_sec);
  387. // Update
  388. ANKI_CHECK(Input::getSingleton().handleEvents());
  389. // User update
  390. ANKI_CHECK(userMainLoop(quit, crntTime - prevUpdateTime));
  391. ANKI_CHECK(SceneGraph::getSingleton().update(prevUpdateTime, crntTime));
  392. RenderQueue rqueue;
  393. SceneGraph::getSingleton().doVisibilityTests(rqueue);
  394. // Inject stats UI
  395. CoreDynamicArray<UiQueueElement> newUiElementArr;
  396. injectUiElements(newUiElementArr, rqueue);
  397. // Render
  398. TexturePtr presentableTex = GrManager::getSingleton().acquireNextPresentableTexture();
  399. ANKI_CHECK(MainRenderer::getSingleton().render(rqueue, presentableTex.get()));
  400. // Pause and sync async loader. That will force all tasks before the pause to finish in this frame.
  401. ResourceManager::getSingleton().getAsyncLoader().pause();
  402. // If we get stats exclude the time of GR because it forces some GPU-CPU serialization. We don't want to
  403. // count that
  404. Second grTime = 0.0;
  405. if(benchmarkMode || g_displayStatsCVar.get() > 0) [[unlikely]]
  406. {
  407. grTime = HighRezTimer::getCurrentTime();
  408. }
  409. GrManager::getSingleton().swapBuffers();
  410. if(benchmarkMode || g_displayStatsCVar.get() > 0) [[unlikely]]
  411. {
  412. grTime = HighRezTimer::getCurrentTime() - grTime;
  413. }
  414. RebarTransientMemoryPool::getSingleton().endFrame();
  415. UnifiedGeometryBuffer::getSingleton().endFrame();
  416. GpuSceneBuffer::getSingleton().endFrame();
  417. GpuVisibleTransientMemoryPool::getSingleton().endFrame();
  418. GpuReadbackMemoryPool::getSingleton().endFrame();
  419. // Update the trace info with some async loader stats
  420. U64 asyncTaskCount = ResourceManager::getSingleton().getAsyncLoader().getCompletedTaskCount();
  421. ANKI_TRACE_INC_COUNTER(RsrcAsyncTasks, asyncTaskCount - m_resourceCompletedAsyncTaskCount);
  422. m_resourceCompletedAsyncTaskCount = asyncTaskCount;
  423. // Now resume the loader
  424. ResourceManager::getSingleton().getAsyncLoader().resume();
  425. // Sleep
  426. const Second endTime = HighRezTimer::getCurrentTime();
  427. const Second frameTime = endTime - startTime;
  428. g_cpuTotalTime.set((frameTime - grTime) * 1000.0);
  429. if(!benchmarkMode) [[likely]]
  430. {
  431. const Second timerTick = 1.0_sec / Second(g_targetFpsCVar.get());
  432. if(frameTime < timerTick)
  433. {
  434. ANKI_TRACE_SCOPED_EVENT(TimerTickSleep);
  435. HighRezTimer::sleep(timerTick - frameTime);
  436. }
  437. }
  438. // Benchmark stats
  439. else
  440. {
  441. aggregatedCpuTime += frameTime - grTime;
  442. aggregatedGpuTime += 0; // TODO
  443. ++benchmarkFramesGathered;
  444. if(benchmarkFramesGathered >= kBenchmarkFramesToGatherBeforeFlush)
  445. {
  446. aggregatedCpuTime = aggregatedCpuTime / Second(kBenchmarkFramesToGatherBeforeFlush) * 1000.0;
  447. aggregatedGpuTime = aggregatedGpuTime / Second(kBenchmarkFramesToGatherBeforeFlush) * 1000.0;
  448. ANKI_CHECK(benchmarkCsvFile.writeTextf("%f,%f\n", aggregatedCpuTime, aggregatedGpuTime));
  449. benchmarkFramesGathered = 0;
  450. aggregatedCpuTime = 0.0;
  451. aggregatedGpuTime = 0.0;
  452. }
  453. }
  454. // Stats
  455. if(g_displayStatsCVar.get() > 0)
  456. {
  457. #if ANKI_PLATFORM_MOBILE
  458. if(MaliHwCounters::isAllocated())
  459. {
  460. MaliHwCountersOut out;
  461. MaliHwCounters::getSingleton().sample(out);
  462. g_maliGpuActive.set(out.m_gpuActive);
  463. g_maliGpuReadBandwidth.set(out.m_readBandwidth);
  464. g_maliGpuWriteBandwidth.set(out.m_writeBandwidth);
  465. }
  466. #endif
  467. StatsUi& statsUi = *static_cast<StatsUi*>(m_statsUi.get());
  468. const StatsUiDetail detail = (g_displayStatsCVar.get() == 1) ? StatsUiDetail::kFpsOnly : StatsUiDetail::kDetailed;
  469. statsUi.setStatsDetail(detail);
  470. StatsSet::getSingleton().endFrame();
  471. }
  472. ++GlobalFrameIndex::getSingleton().m_value;
  473. if(benchmarkMode) [[unlikely]]
  474. {
  475. if(GlobalFrameIndex::getSingleton().m_value >= g_benchmarkModeFrameCountCVar.get())
  476. {
  477. quit = true;
  478. }
  479. }
  480. }
  481. #if ANKI_TRACING_ENABLED
  482. static U64 frame = 1;
  483. CoreTracer::getSingleton().flushFrame(frame++);
  484. #endif
  485. }
  486. if(benchmarkMode) [[unlikely]]
  487. {
  488. ANKI_CORE_LOGI("Benchmark file saved in: %s", benchmarkCsvFileFilename.cstr());
  489. }
  490. return Error::kNone;
  491. }
  492. void App::injectUiElements(CoreDynamicArray<UiQueueElement>& newUiElementArr, RenderQueue& rqueue)
  493. {
  494. const U32 originalCount = rqueue.m_uis.getSize();
  495. if(g_displayStatsCVar.get() > 0 || m_consoleEnabled)
  496. {
  497. const U32 extraElements = (g_displayStatsCVar.get() > 0) + (m_consoleEnabled != 0);
  498. newUiElementArr.resize(originalCount + extraElements);
  499. if(originalCount > 0)
  500. {
  501. memcpy(&newUiElementArr[0], &rqueue.m_uis[0], rqueue.m_uis.getSizeInBytes());
  502. }
  503. rqueue.m_uis = WeakArray<UiQueueElement>(newUiElementArr);
  504. }
  505. U32 count = originalCount;
  506. if(g_displayStatsCVar.get() > 0)
  507. {
  508. newUiElementArr[count].m_userData = m_statsUi.get();
  509. newUiElementArr[count].m_drawCallback = [](CanvasPtr& canvas, void* userData) -> void {
  510. static_cast<StatsUi*>(userData)->build(canvas);
  511. };
  512. ++count;
  513. }
  514. if(m_consoleEnabled)
  515. {
  516. newUiElementArr[count].m_userData = m_console.get();
  517. newUiElementArr[count].m_drawCallback = [](CanvasPtr& canvas, void* userData) -> void {
  518. static_cast<DeveloperConsole*>(userData)->build(canvas);
  519. };
  520. ++count;
  521. }
  522. }
  523. void App::initMemoryCallbacks(AllocAlignedCallback& allocCb, void*& allocCbUserData)
  524. {
  525. if(ANKI_STATS_ENABLED && g_displayStatsCVar.get() > 1)
  526. {
  527. allocCb = statsAllocCallback;
  528. allocCbUserData = this;
  529. }
  530. else
  531. {
  532. // Leave the default
  533. }
  534. }
  535. void App::setSignalHandlers()
  536. {
  537. auto handler = [](int signum) -> void {
  538. const char* name = nullptr;
  539. switch(signum)
  540. {
  541. case SIGABRT:
  542. name = "SIGABRT";
  543. break;
  544. case SIGSEGV:
  545. name = "SIGSEGV";
  546. break;
  547. #if ANKI_POSIX
  548. case SIGBUS:
  549. name = "SIGBUS";
  550. break;
  551. #endif
  552. case SIGILL:
  553. name = "SIGILL";
  554. break;
  555. case SIGFPE:
  556. name = "SIGFPE";
  557. break;
  558. }
  559. if(name)
  560. printf("Caught signal %d (%s)\n", signum, name);
  561. else
  562. printf("Caught signal %d\n", signum);
  563. U32 count = 0;
  564. printf("Backtrace:\n");
  565. backtrace([&count](CString symbol) {
  566. printf("%.2u: %s\n", count++, symbol.cstr());
  567. });
  568. ANKI_DEBUG_BREAK();
  569. };
  570. signal(SIGSEGV, handler);
  571. signal(SIGILL, handler);
  572. signal(SIGFPE, handler);
  573. #if ANKI_POSIX
  574. signal(SIGBUS, handler);
  575. #endif
  576. // Ignore for now: signal(SIGABRT, handler);
  577. }
  578. } // end namespace anki