Divide Framework 0.1
A free and open-source 3D Framework under heavy development
Loading...
Searching...
No Matches
Kernel.cpp
Go to the documentation of this file.
1
2
3#include "config.h"
4
5#include "Headers/Kernel.h"
8
17#include "GUI/Headers/GUI.h"
35
36namespace Divide
37{
38
39namespace
40{
41 constexpr U32 g_printTimerBase = 15u;
42 constexpr U8 g_warmupFrameCount = 8u;
43
46 constexpr U8 g_renderThreadCount = 1u;
47
49};
50
51size_t Kernel::TotalThreadCount( const TaskPoolType type ) noexcept
52{
53 constexpr U8 g_renderThreadPoolSize = 6u;
54 constexpr U8 g_assetLoadingPoolSize = 4u;
55 constexpr U8 g_backupThreadPoolSize = 2u;
56
57 DIVIDE_ASSERT(g_totalWorkerCount >= g_mininumTotalWorkerCount);
58 static_assert(g_renderThreadPoolSize + g_assetLoadingPoolSize + g_backupThreadPoolSize + g_renderThreadCount < g_mininumTotalWorkerCount);
59
60 switch ( type )
61 {
62 case TaskPoolType::ASSET_LOADER: return g_assetLoadingPoolSize;
63 case TaskPoolType::RENDERER: return g_renderThreadPoolSize;
64 case TaskPoolType::LOW_PRIORITY: return g_backupThreadPoolSize;
65 case TaskPoolType::HIGH_PRIORITY: return std::max(g_totalWorkerCount - g_backupThreadPoolSize - g_assetLoadingPoolSize - g_renderThreadPoolSize - g_renderThreadCount, 4u);
66 case TaskPoolType::COUNT: break;
67 }
68
69 return g_totalWorkerCount;
70}
71
72Kernel::Kernel(const I32 argc, char** argv, Application& parentApp)
73 : _platformContext(parentApp)
74 , _appLoopTimerMain(Time::ADD_TIMER("Main Loop Timer"))
75 , _appLoopTimerInternal(Time::ADD_TIMER("Internal Main Loop Timer"))
76 , _frameTimer(Time::ADD_TIMER("Total Frame Timer"))
77 , _appIdleTimer(Time::ADD_TIMER("Loop Idle Timer"))
78 , _appScenePass(Time::ADD_TIMER("Loop Scene Pass Timer"))
79 , _sceneUpdateTimer(Time::ADD_TIMER("Scene Update Timer"))
80 , _sceneUpdateLoopTimer(Time::ADD_TIMER("Scene Update Loop timer"))
81 , _cameraMgrTimer(Time::ADD_TIMER("Camera Manager Update Timer"))
82 , _flushToScreenTimer(Time::ADD_TIMER("Flush To Screen Timer"))
83 , _preRenderTimer(Time::ADD_TIMER("Pre-render Timer"))
84 , _postRenderTimer(Time::ADD_TIMER("Post-render Timer"))
85 , _splashScreenUpdating( false )
86 , _argc(argc)
87 , _argv(argv)
88{
89 _platformContext.init(*this);
90 InitConditionalWait(_platformContext);
91
102
103 _projectManager = std::make_unique<ProjectManager>(*this); // Scene Manager
104 _renderPassManager = std::make_unique<RenderPassManager>(*this, _platformContext.gfx());
105}
106
108{
109 DIVIDE_ASSERT(projectManager() == nullptr && renderPassManager() == nullptr, "Kernel destructor: not all resources have been released properly!");
110}
111
113 bool expected = false;
114 if (!_splashScreenUpdating.compare_exchange_strong(expected, true)) {
115 return;
116 }
117
118 DisplayWindow& window = _platformContext.mainWindow();
120 window.decorated(false);
121 WAIT_FOR_CONDITION(window.setDimensions(_platformContext.config().runtime.splashScreenSize));
122
123 window.centerWindowPosition();
124 window.hidden(false);
126
127 _splashScreen = std::make_unique<GUISplash>( "divideLogo.jpg", _platformContext.config().runtime.splashScreenSize );
128
129 _platformContext.app().windowManager().drawToWindow(window);
130 _splashScreen->render(_platformContext.gfx());
131 _platformContext.app().windowManager().flushWindow();
132}
133
135{
136 DisplayWindow& window = _platformContext.mainWindow();
137 const vec2<U16> previousDimensions = window.getPreviousDimensions();
138 _splashScreenUpdating = false;
139
140 window.changeToPreviousType();
141 window.decorated(true);
142 WAIT_FOR_CONDITION(window.setDimensions(previousDimensions));
143 window.setPosition(vec2<I32>(-1));
144
145 if (window.type() == WindowType::WINDOW && _platformContext.config().runtime.maximizeOnStart)
146 {
147 window.maximized(true);
148 }
150
151 _splashScreen.reset();
152}
153
154void Kernel::idle(const bool fast, const U64 deltaTimeUSGame, const U64 deltaTimeUSApp )
155{
157
159 {
160 Locale::Idle();
161 }
162
163 _platformContext.idle(fast, deltaTimeUSGame, deltaTimeUSApp );
164
165 _projectManager->idle();
166 Script::idle();
167
168 if (!fast && --g_printTimer == 0) {
170 g_printTimer = g_printTimerBase;
171 }
172
173 if constexpr(Config::Build::ENABLE_EDITOR) {
174 const bool freezeLoopTime = _platformContext.editor().simulationPaused() && _platformContext.editor().stepQueue() == 0u;
175 _timingData.freezeGameTime(freezeLoopTime);
176 _platformContext.app().mainLoopPaused(freezeLoopTime);
177
178 }
179}
180
182{
184
185 {
187
190 {
192 };
193
194 if (!keepAlive())
195 {
196 // exiting the rendering loop will return us to the last control point
197 _platformContext.app().mainLoopActive(false);
198
199 if (!projectManager()->saveActiveScene(true, false))
200 {
202 }
203 return;
204 }
205
207 {
208 // Check for any file changes (shaders, scripts, etc)
210 }
211
212 // Update internal timer
213 _platformContext.app().timer().update();
214
215 keepAlive(true);
216
217 // Update time at every render loop
219
220 FrameEvent evt = {};
221 evt._time._app._currentTimeUS = _timingData.appCurrentTimeUS();
222 evt._time._app._deltaTimeUS = _timingData.appTimeDeltaUS();
223
224 evt._time._game._currentTimeUS += _timingData.gameCurrentTimeUS();
225 evt._time._game._deltaTimeUS = _timingData.gameTimeDeltaUS();
226
227 {
228 _platformContext.componentMask( to_base( PlatformContext::SystemComponentType::ALL ) );
229 {
231
232 // Launch the FRAME_STARTED event
233 if (!frameListenerMgr().createAndProcessEvent(FrameEventType::FRAME_EVENT_STARTED, evt))
234 {
235 keepAlive(false);
236 }
237
238 // Process the current frame
239 if (!mainLoopScene(evt))
240 {
241 keepAlive(false);
242 }
243
244 // Launch the FRAME_PROCESS event (a.k.a. the frame processing has ended event)
245 if (!frameListenerMgr().createAndProcessEvent(FrameEventType::FRAME_EVENT_PROCESS, evt))
246 {
247 keepAlive(false);
248 }
249 }
250
251 if (!frameListenerMgr().createAndProcessEvent(FrameEventType::FRAME_EVENT_ENDED, evt))
252 {
253 keepAlive(false);
254 }
255
256 if (_platformContext.app().RestartRequested() || _platformContext.app().ShutdownRequested() )
257 {
258 keepAlive(false);
259 }
260
261 const ErrorCode err = _platformContext.app().errorCode();
262
263 if (err != ErrorCode::NO_ERR)
264 {
266 keepAlive(false);
267 }
268 }
269
270 if (platformContext().debug().enabled())
271 {
272 static bool statsEnabled = false;
273 // Turn on perf metric measuring 2 seconds before perf dump
275 {
276 statsEnabled = platformContext().gfx().queryPerformanceStats();
277 platformContext().gfx().queryPerformanceStats(true);
278 }
279 // Our stats should be up to date now
281 {
282 Console::printfn(platformContext().debug().output().c_str());
283 if (!statsEnabled)
284 {
285 platformContext().gfx().queryPerformanceStats(false);
286 }
287 }
288
290 {
291 _platformContext.gui().modifyText("ProfileData", platformContext().debug().output(), true);
292 }
293 }
294
296 {
298 {
299 DisplayWindow& window = _platformContext.mainWindow();
300 NO_DESTROY static string originalTitle = window.title();
301
302 F32 fps = 0.f, frameTime = 0.f;
303 _platformContext.app().timer().getFrameRateAndTime(fps, frameTime);
304 const Str<256>& activeSceneName = _projectManager->activeProject()->getActiveScene()->resourceName();
305 constexpr const char* buildType = Config::Build::IS_DEBUG_BUILD ? "DEBUG" : Config::Build::IS_PROFILE_BUILD ? "PROFILE" : "RELEASE";
306 constexpr const char* titleString = "[{} - {}] - {} - {} - {:5.2f} FPS - {:3.2f} ms - FrameIndex: {} - Update Calls : {} - Alpha : {:1.2f}";
307 window.title(titleString,
308 buildType,
309 Names::renderAPI[to_base(_platformContext.gfx().renderAPI())],
310 originalTitle.c_str(),
311 activeSceneName.c_str(),
312 fps,
313 frameTime,
315 _timingData.updateLoops(),
316 _timingData.alpha());
317 }
318 }
319
320 {
323 }
324 }
325
326 // Cap FPS
327 const I16 frameLimit = _platformContext.config().runtime.frameRateLimit;
328 if (frameLimit > 0)
329 {
330 const F32 elapsedMS = Time::MicrosecondsToMilliseconds<F32>(_appLoopTimerMain.get());
331 const F32 deltaMilliseconds = std::floorf(elapsedMS - (elapsedMS * 0.015f));
332 const F32 targetFrameTime = 1000.0f / frameLimit;
333
334 if (deltaMilliseconds < targetFrameTime)
335 {
336 //Sleep the remaining frame time
338 std::this_thread::sleep_for(std::chrono::milliseconds(to_I32(targetFrameTime - deltaMilliseconds)));
340 }
341 }
342}
343
345{
347
349 {
351 // Update cameras. Always use app timing as pausing time would freeze the cameras in place
352 // ToDo: add a speed slider in the editor -Ionut
354 }
355
356 if (_platformContext.mainWindow().minimized())
357 {
358 idle(false, 0u, evt._time._app._deltaTimeUS );
360 return true;
361 }
362
363 if (!_platformContext.app().freezeRendering())
364 {
365 {
367
368 const U8 playerCount = _projectManager->activePlayerCount();
369
370 _timingData.updateLoops(0u);
371
372 constexpr U8 MAX_FRAME_SKIP = 4u;
373
374 {
376 _projectManager->activeProject()->getActiveScene()->processGUI( evt._time._game._deltaTimeUS, evt._time._app._deltaTimeUS );
377 }
378
379 while (_timingData.accumulator() >= FIXED_UPDATE_RATE_US)
380 {
381 PROFILE_SCOPE("Run Update Loop", Profiler::Category::IO);
382 // Everything inside here should use fixed timesteps, apart from GFX updates which should use both!
383 // Some things (e.g. tonemapping) need to resolve even if the simulation is paused (might not remain true in the future)
384
385 if (_timingData.updateLoops() == 0u)
386 {
388 }
389
390 // Flush any pending threaded callbacks
391 for (U8 i = 0u; i < to_U8(TaskPoolType::COUNT); ++i)
392 {
393 _platformContext.taskPool(static_cast<TaskPoolType>(i)).flushCallbackQueue();
394 }
395
396 // Update scene based on input
397 {
398 PROFILE_SCOPE("Process input", Profiler::Category::IO );
399 for (U8 i = 0u; i < playerCount; ++i) {
400 PROFILE_TAG("Player index", i);
401 _projectManager->activeProject()->getActiveScene()->processInput(i, evt._time._game._deltaTimeUS, evt._time._app._deltaTimeUS );
402 }
403 }
404
405 // process all scene events
406 {
407 PROFILE_SCOPE("Process scene events", Profiler::Category::IO );
408 _projectManager->activeProject()->getActiveScene()->processTasks( evt._time._game._deltaTimeUS, evt._time._app._deltaTimeUS );
409 }
410
411 // Update the scene state based on current time (e.g. animation matrices)
412 _projectManager->updateSceneState( evt._time._game._deltaTimeUS, evt._time._app._deltaTimeUS );
413 // Update visual effect timers as well
415
416 _timingData.updateLoops(_timingData.updateLoops() + 1u);
417 _timingData.accumulator(_timingData.accumulator() - FIXED_UPDATE_RATE_US);
418
419 const U8 loopCount = _timingData.updateLoops();
420 if (loopCount == 1u)
421 {
423 }
424 else if (loopCount == MAX_FRAME_SKIP)
425 {
426 _timingData.accumulator(FIXED_UPDATE_RATE_US);
427 break;
428 }
429 }
430 }
431 }
432
434 {
435 U32 retryCount = 0;
437 {
439 {
440 break;
441 }
442 }
443 }
444
445 GFXDevice::FrameInterpolationFactor(_timingData.alpha());
446
447 // Update windows and get input events
449
450 // Update the graphical user interface
451 _platformContext.gui().update( evt._time._game._deltaTimeUS );
452
453 if constexpr(Config::Build::ENABLE_EDITOR)
454 {
455 _platformContext.editor().update( evt._time._app._deltaTimeUS );
456 }
457
458 return presentToScreen(evt);
459}
460
461static void ComputeViewports(const Rect<I32>& mainViewport, vector<Rect<I32>>& targetViewports, const U8 count) {
463
464 const I32 xOffset = mainViewport.x;
465 const I32 yOffset = mainViewport.y;
466 const I32 width = mainViewport.z;
467 const I32 height = mainViewport.w;
468
469 targetViewports.resize(count);
470 if (count == 0) {
471 return;
472 } else if (count == 1) { //Single Player
473 targetViewports[0].set(mainViewport);
474 return;
475 } else if (count == 2) { //Split Screen
476 const I32 halfHeight = height / 2;
477 targetViewports[0].set(xOffset, halfHeight + yOffset, width, halfHeight);
478 targetViewports[1].set(xOffset, 0 + yOffset, width, halfHeight);
479 return;
480 }
481
482 // Basic idea (X - viewport):
483 // Odd # of players | Even # of players
484 // X X | X X
485 // X | X X
486 // |
487 // X X X | X X X
488 // X X | X X X
489 // etc
490 // Always try to match last row with previous one
491 // If they match, move the first viewport from the last row
492 // to the previous row and add a new entry to the end of the
493 // current row
494
495 using ViewportRow = vector<Rect<I32>>;
496 using ViewportRows = vector<ViewportRow>;
497 ViewportRows rows;
498
499 // Allocates storage for a N x N matrix of viewports that will hold numViewports
500 // Returns N;
501 const auto resizeViewportContainer = [&rows](const U32 numViewports) {
502 //Try to fit all viewports into an appropriately sized matrix.
503 //If the number of resulting rows is too large, drop empty rows.
504 //If the last row has an odd number of elements, center them later.
505 const U8 matrixSize = to_U8(minSquareMatrixSize(numViewports));
506 rows.resize(matrixSize);
507 std::for_each(std::begin(rows), std::end(rows), [matrixSize](ViewportRow& row) { row.resize(matrixSize); });
508
509 return matrixSize;
510 };
511
512 // Remove extra rows and columns, if any
513 const U8 columnCount = resizeViewportContainer(count);
514 const U8 extraColumns = columnCount * columnCount - count;
515 const U8 extraRows = extraColumns / columnCount;
516 for (U8 i = 0u; i < extraRows; ++i) {
517 rows.pop_back();
518 }
519 const U8 columnsToRemove = extraColumns - extraRows * columnCount;
520 for (U8 i = 0u; i < columnsToRemove; ++i) {
521 rows.back().pop_back();
522 }
523
524 const U8 rowCount = to_U8(rows.size());
525
526 // Calculate and set viewport dimensions
527 // The number of columns is valid for the width;
528 const I32 playerWidth = width / columnCount;
529 // The number of rows is valid for the height;
530 const I32 playerHeight = height / to_I32(rowCount);
531
532 for (U8 i = 0u; i < rowCount; ++i) {
533 ViewportRow& row = rows[i];
534 const I32 playerYOffset = playerHeight * (rowCount - i - 1);
535 for (U8 j = 0; j < to_U8(row.size()); ++j) {
536 const I32 playerXOffset = playerWidth * j;
537 row[j].set(playerXOffset, playerYOffset, playerWidth, playerHeight);
538 }
539 }
540
541 //Slide the last row to center it
542 if (extraColumns > 0)
543 {
544 ViewportRow& lastRow = rows.back();
545 const I32 screenMidPoint = width / 2;
546 const I32 rowMidPoint = (to_I32(lastRow.size()) * playerWidth) / 2;
547 const I32 slideFactor = screenMidPoint - rowMidPoint;
548 for (Rect<I32>& viewport : lastRow) {
549 viewport.x += slideFactor;
550 }
551 }
552
553 // Update the system viewports
554 U8 idx = 0u;
555 for (const ViewportRow& row : rows) {
556 for (const Rect<I32>& viewport : row) {
557 targetViewports[idx++].set(viewport);
558 }
559 }
560}
561
562static Time::ProfileTimer& GetTimer(Time::ProfileTimer& parentTimer, vector<Time::ProfileTimer*>& timers, const U8 index, const char* name) {
563 while (timers.size() < to_size(index) + 1)
564 {
565 timers.push_back(&Time::ADD_TIMER( Util::StringFormat<string>( "{} {}", name, timers.size() ).c_str() ));
566 parentTimer.addChildTimer(*timers.back());
567 }
568
569 return *timers[index];
570}
571
574
576
577 {
579 if (!frameListenerMgr().createAndProcessEvent(FrameEventType::FRAME_PRERENDER, evt)) {
580 return false;
581 }
582 }
583
584 const U8 playerCount = _projectManager->activePlayerCount();
585
586 const auto& backBufferRT = _platformContext.gfx().renderTargetPool().getRenderTarget( RenderTargetNames::BACK_BUFFER );
587 const Rect<I32> mainViewport{0, 0, to_I32(backBufferRT->getWidth()), to_I32(backBufferRT->getHeight())};
588
589 if (_prevViewport != mainViewport || _prevPlayerCount != playerCount) {
590 ComputeViewports(mainViewport, _targetViewports, playerCount);
591 _prevViewport.set(mainViewport);
592 _prevPlayerCount = playerCount;
593 }
594
595
596 RenderPassManager::RenderParams renderParams{};
597 renderParams._sceneRenderState = &_projectManager->activeProject()->getActiveScene()->state()->renderState();
598
599 for (U8 i = 0u; i < playerCount; ++i)
600 {
602 renderParams._playerPass = i;
603
604 if (!frameListenerMgr().createAndProcessEvent(FrameEventType::FRAME_SCENERENDER_START, evt))
605 {
606 return false;
607 }
608
609 renderParams._targetViewport = _targetViewports[i];
610
611 {
612 Time::ProfileTimer& timer = GetTimer(_flushToScreenTimer, _renderTimer, i, "Render Timer");
613 renderParams._parentTimer = &timer;
614 Time::ScopedTimer time2(timer);
615 _renderPassManager->render(renderParams);
616 }
617
618 if (!frameListenerMgr().createAndProcessEvent(FrameEventType::FRAME_SCENERENDER_END, evt))
619 {
620 return false;
621 }
622 }
623
624 {
626 if(!frameListenerMgr().createAndProcessEvent(FrameEventType::FRAME_POSTRENDER, evt))
627 {
628 return false;
629 }
630 }
631
632 for (U32 i = playerCount; i < to_U32(_renderTimer.size()); ++i)
633 {
635 _renderTimer.erase(begin(_renderTimer) + i);
636 }
637
638 return true;
639}
640
641// The first loops compiles all the visible data, so do not render the first couple of frames
643{
644 Console::printfn(LOCALE_STR("START_RENDER_LOOP"));
645
646 _timingData.freezeGameTime(true);
647
648 for (U8 i = 0u; i < g_warmupFrameCount; ++i)
649 {
650 onLoop();
651 }
652 _timingData.freezeGameTime(false);
653
655
657
659}
660
661
662ErrorCode Kernel::initialize(const string& entryPoint)
663{
664 const SysInfo& systemInfo = const_sysInfo();
666 {
668 }
669
670 g_printTimer = g_printTimerBase;
671
672 // Don't log parameter requests
673 _platformContext.paramHandler().setDebugOutput(false);
674 // Load info from XML files
675 Configuration& config = _platformContext.config();
676 loadFromXML( config, Paths::g_xmlDataLocation, entryPoint.c_str() );
677
678 if (Util::FindCommandLineArgument(_argc, _argv, "disableRenderAPIDebugging"))
679 {
682 }
683 if (Util::FindCommandLineArgument(_argc, _argv, "disableAssertOnRenderAPIError"))
684 {
686 }
687 if (Util::FindCommandLineArgument(_argc, _argv, "disableAPIExtensions"))
688 {
689 config.debug.renderer.useExtensions = false;
690 }
691
693 {
694 Console::printfn( LOCALE_STR( "START_APPLICATION_PROJECT_ARGUMENT" ) , config.startupProject.c_str() );
695 }
696
698 {
700 }
701
702 g_totalWorkerCount = std::max( config.runtime.maxWorkerThreads > 0 ? config.runtime.maxWorkerThreads : std::thread::hardware_concurrency(), g_mininumTotalWorkerCount);
703
704 _platformContext.pfx().apiID(PXDevice::PhysicsAPI::PhysX);
705 _platformContext.sfx().apiID(SFXDevice::AudioAPI::SDL);
706
707 ASIO::SET_LOG_FUNCTION([](const std::string_view msg, const bool isError)
708 {
709 if (isError)
710 {
711 Console::errorfn(string(msg).c_str());
712 }
713 else
714 {
715 Console::printfn(string(msg).c_str());
716 }
717 });
718
719 Console::printfn( LOCALE_STR( "START_APPLICATION_WORKING_DIRECTORY" ) , systemInfo._workingDirectory.string() );
720
721 Console::printfn( LOCALE_STR( "START_RENDER_INTERFACE" ) ) ;
722
723 _platformContext.server().init(static_cast<U16>(443), "127.0.0.1", true);
724
725 if (!_platformContext.client().connect(config.serverAddress, 443))
726 {
727 _platformContext.client().connect("127.0.0.1", 443);
728 }
729
730 Locale::ChangeLanguage(config.language.c_str());
731
732 Console::printfn(LOCALE_STR("START_RENDER_INTERFACE"));
733
734 const RenderAPI renderingAPI = static_cast<RenderAPI>(config.runtime.targetRenderingAPI);
735
736 ErrorCode initError = Attorney::ApplicationKernel::SetRenderingAPI(_platformContext.app(), renderingAPI);
737
738 if (initError != ErrorCode::NO_ERR)
739 {
740 return initError;
741 }
742
743 ResourceCache::Init(renderingAPI, _platformContext);
745
747 initError = _platformContext.gfx().initRenderingAPI(_argc, _argv, renderingAPI);
748
749 // If we could not initialize the graphics device, exit
750 if (initError != ErrorCode::NO_ERR)
751 {
752 return initError;
753 }
754
755 { // Start thread pools
756 std::atomic_size_t threadCounter = TotalThreadCount(TaskPoolType::COUNT) - g_renderThreadCount;
757
758 for ( U8 i = 0u; i < to_base(TaskPoolType::COUNT); ++i)
759 {
760 const TaskPoolType poolType = static_cast<TaskPoolType>(i);
761 if (!_platformContext.taskPool( poolType ).init(
762 TotalThreadCount( poolType ),
763 [&threadCounter, poolType, &ctx = _platformContext](const std::thread::id& threadID)
764 {
765 Attorney::PlatformContextKernel::onThreadCreated( ctx, poolType, threadID, false);
766 threadCounter.fetch_sub(1);
767 }))
768 {
770 }
771 }
772
773 WAIT_FOR_CONDITION(threadCounter.load() == 0);
774 }
775
776 initError = _platformContext.gfx().postInitRenderingAPI(config.runtime.resolution);
777 // If we could not initialize the graphics device, exit
778 if (initError != ErrorCode::NO_ERR)
779 {
780 return initError;
781 }
782
783 SceneEnvironmentProbePool::OnStartup(_platformContext.gfx());
784 _inputConsumers.fill(nullptr);
785
786 if constexpr(Config::Build::ENABLE_EDITOR)
787 {
788 _inputConsumers[to_base(InputConsumerType::Editor)] = &_platformContext.editor();
789 }
790
791 _inputConsumers[to_base(InputConsumerType::GUI)] = &_platformContext.gui();
792 _inputConsumers[to_base(InputConsumerType::Scene)] = _projectManager.get();
793
794 // Add our needed app-wide render passes. RenderPassManager is responsible for deleting these!
795 _renderPassManager->setRenderPass(RenderStage::SHADOW, { });
796 _renderPassManager->setRenderPass(RenderStage::REFLECTION, { RenderStage::SHADOW });
797 _renderPassManager->setRenderPass(RenderStage::REFRACTION, { RenderStage::SHADOW });
798 _renderPassManager->setRenderPass(RenderStage::DISPLAY, { RenderStage::REFLECTION, RenderStage::REFRACTION });
799 _renderPassManager->setRenderPass(RenderStage::NODE_PREVIEW, { RenderStage::REFLECTION, RenderStage::REFRACTION });
800
801 Console::printfn(LOCALE_STR("SCENE_ADD_DEFAULT_CAMERA"));
802
803 WindowManager& winManager = _platformContext.app().windowManager();
804 winManager.mainWindow()->addEventListener(WindowEvent::LOST_FOCUS, [mgr = _projectManager.get()](const DisplayWindow::WindowEventArgs& )
805 {
806 mgr->onChangeFocus(false);
807 return true;
808 });
809 winManager.mainWindow()->addEventListener(WindowEvent::GAINED_FOCUS, [mgr = _projectManager.get()](const DisplayWindow::WindowEventArgs& )
810 {
811 mgr->onChangeFocus(true);
812 return true;
813 });
814
816 ProjectManager::OnStartup(_platformContext);
817 // Initialize GUI with our current resolution
818 initError = _platformContext.gui().init(_platformContext);
819 if ( initError != ErrorCode::NO_ERR )
820 {
821 return initError;
822 }
823
825
826 Console::printfn(LOCALE_STR("START_SOUND_INTERFACE"));
827 initError = _platformContext.sfx().initAudioAPI();
828 if (initError != ErrorCode::NO_ERR)
829 {
830 return initError;
831 }
832
833 Console::printfn(LOCALE_STR("START_PHYSICS_INTERFACE"));
834 initError = _platformContext.pfx().initPhysicsAPI(Config::TARGET_FRAME_RATE, config.runtime.simSpeed);
835 if (initError != ErrorCode::NO_ERR)
836 {
837 return initError;
838 }
839
840 Str<256> startupProject = config.startupProject.c_str();
841 if constexpr ( Config::Build::IS_EDITOR_BUILD )
842 {
843 startupProject = Config::DEFAULT_PROJECT_NAME;
844 Console::printfn(LOCALE_STR("START_FRAMEWORK_EDITOR"), startupProject.c_str() );
845 }
846 else
847 {
848 Console::printfn( LOCALE_STR( "START_FRAMEWORK_GAME" ), startupProject.c_str() );
849 }
850
851 ProjectIDs& projects = Attorney::ProjectManagerKernel::init(_projectManager.get());
852 if ( projects.empty() )
853 {
854 Console::errorfn( LOCALE_STR( "ERROR_PROJECTS_LOAD" ) );
856 }
857
858 ProjectID targetProject{};
859 for ( auto& project : projects )
860 {
861 if (project._name == startupProject)
862 {
863 targetProject = project;
864 break;
865 }
866 }
867
868 while ( targetProject._guid == 0 )
869 {
870 targetProject = projects.front();
871 Console::warnfn( LOCALE_STR( "WARN_PROJECT_NOT_FOUND" ), startupProject.c_str(), targetProject._name.c_str() );
872 startupProject = targetProject._name;
873 }
874
875 if ( targetProject._guid == 0 )
876 {
877 Console::errorfn( LOCALE_STR( "WARN_PROJECT_NOT_FOUND" ), startupProject.c_str(), targetProject._name.c_str() );
879 }
880
881 initError = _projectManager->loadProject( targetProject, false );
882
883 if ( initError != ErrorCode::NO_ERR )
884 {
885 return initError;
886 }
887
888 idle(true, 0u, 0u);
889
890 if (!_projectManager->loadComplete())
891 {
892 Console::errorfn(LOCALE_STR("ERROR_SCENE_LOAD_NOT_CALLED"), startupProject.c_str() );
894 }
895
896 _platformContext.gui().addText("ProfileData", // Unique ID
898 ._x = RelativeValue{
899 ._scale = 0.75f,
900 ._offset = 0.0f
901 },
902 ._y = RelativeValue{
903 ._scale = 0.2f,
904 ._offset = 0.0f
905 }
906 }, // Position
908 UColour4(255, 50, 0, 255), // Colour
909 "", // Text
910 true, // Multiline
911 12); // Font size
912
913 ShadowMap::initShadowMaps(_platformContext.gfx());
914
915 _renderPassManager->postInit();
916
917 if constexpr (Config::Build::ENABLE_EDITOR)
918 {
919 if (!_platformContext.editor().init(config.runtime.resolution))
920 {
922 }
923 _projectManager->addSelectionCallback([ctx = &_platformContext](const PlayerIndex idx, const vector<SceneGraphNode*>& nodes)
924 {
925 ctx->editor().selectionChangeCallback(idx, nodes);
926 });
927 }
928
929 Console::printfn(LOCALE_STR("INITIAL_DATA_LOADED"));
930
931 return initError;
932}
933
935{
936 Console::printfn(LOCALE_STR("STOP_KERNEL"));
937
938 _platformContext.config().save();
939
940 for (U8 i = 0u; i < to_U8(TaskPoolType::COUNT); ++i)
941 {
942 _platformContext.taskPool( static_cast<TaskPoolType>(i)).waitForAllTasks(true);
943 }
944
945 if constexpr (Config::Build::ENABLE_EDITOR)
946 {
947 _platformContext.editor().toggle(false);
948 }
949
950 ProjectManager::OnShutdown(_platformContext);
952 _projectManager.reset();
953
954 ShadowMap::destroyShadowMaps(_platformContext.gfx());
955 _renderPassManager.reset();
956
957 SceneEnvironmentProbePool::OnShutdown(_platformContext.gfx());
959 _platformContext.terminate();
961 for ( U8 i = 0u; i < to_U8( TaskPoolType::COUNT ); ++i )
962 {
963 _platformContext.taskPool( static_cast<TaskPoolType>(i) ).shutdown();
964 }
966 Console::printfn(LOCALE_STR("STOP_ENGINE_OK"));
967}
968
970 Attorney::GFXDeviceKernel::onWindowSizeChange(_platformContext.gfx(), params);
971
972 if constexpr (Config::Build::ENABLE_EDITOR) {
973 _platformContext.editor().onWindowSizeChange(params);
974 }
975}
976
978 _projectManager->onResolutionChange(params);
979
980 Attorney::GFXDeviceKernel::onResolutionChange(_platformContext.gfx(), params);
981
983 _platformContext.gui().onResolutionChange(params);
984 }
985
986 if constexpr(Config::Build::ENABLE_EDITOR) {
987 _platformContext.editor().onResolutionChange(params);
988 }
989}
990
991#pragma region Input Management
992void Kernel::remapAbsolutePosition( Input::MouseEvent& eventInOut ) const noexcept
993{
994 vec2<I32> absPositionIn = { eventInOut.state().X.abs, eventInOut.state().Y.abs };
995
996 const Rect<I32> renderingViewport = _platformContext.mainWindow().renderingViewport();
997 CLAMP_IN_RECT(absPositionIn.x, absPositionIn.y, renderingViewport);
998
1000 _platformContext.editor().running() &&
1001 !_platformContext.editor().hasFocus())
1002 {
1003 const Rect<I32> previewRect = _platformContext.editor().scenePreviewRect( false );
1004 absPositionIn = COORD_REMAP( absPositionIn, previewRect, renderingViewport );
1005 if ( !previewRect.contains(absPositionIn) )
1006 {
1007 CLAMP_IN_RECT( absPositionIn.x, absPositionIn.y, renderingViewport );
1008 eventInOut.inScenePreviewRect(true);
1009 }
1010 }
1011
1012 const vec2<U16> resolution = _platformContext.gfx().renderingResolution();
1013 absPositionIn = COORD_REMAP( absPositionIn, renderingViewport, { 0, 0, to_I32( resolution.width ), to_I32( resolution.height ) } );
1015 state.X.abs = absPositionIn.x;
1016 state.Y.abs = absPositionIn.y;
1017}
1018
1020{
1022 !_projectManager->wantsMouse() &&
1024 {
1025 return true;
1026 }
1027
1028 Input::MouseMoveEvent remapArg = arg;
1029 remapAbsolutePosition( remapArg );
1030
1031 for (U8 i = 1u; i < to_base(InputConsumerType::COUNT); ++i)
1032 {
1033 if (_inputConsumers[i] && _inputConsumers[i]->mouseMoved(remapArg))
1034 {
1035 return true;
1036 }
1037 }
1038
1039 return false;
1040}
1041
1044 !_projectManager->wantsMouse() &&
1046 {
1047 return true;
1048 }
1049
1050 Input::MouseButtonEvent remapArg = arg;
1051 remapAbsolutePosition( remapArg );
1052
1053 for (U8 i = 1u; i < to_base(InputConsumerType::COUNT); ++i)
1054 {
1055 if (_inputConsumers[i] && _inputConsumers[i]->mouseButtonPressed(remapArg))
1056 {
1057 return true;
1058 }
1059 }
1060
1061 return false;
1062}
1063
1066 !_projectManager->wantsMouse() &&
1068 {
1069 return true;
1070 }
1071
1072 Input::MouseButtonEvent remapArg = arg;
1073 remapAbsolutePosition( remapArg );
1074
1075 for (U8 i = 1u; i < to_base(InputConsumerType::COUNT); ++i)
1076 {
1077 if (_inputConsumers[i] && _inputConsumers[i]->mouseButtonReleased(remapArg))
1078 {
1079 return true;
1080 }
1081 }
1082
1083 return false;
1084}
1085
1087 for (auto inputConsumer : _inputConsumers) {
1088 if (inputConsumer && inputConsumer->onKeyDown(key)) {
1089 return true;
1090 }
1091 }
1092
1093 return false;
1094}
1095
1097 for (auto inputConsumer : _inputConsumers) {
1098 if (inputConsumer && inputConsumer->onKeyUp(key)) {
1099 return true;
1100 }
1101 }
1102
1103 return false;
1104}
1105
1107 for (auto inputConsumer : _inputConsumers) {
1108 if (inputConsumer && inputConsumer->joystickAxisMoved(arg)) {
1109 return true;
1110 }
1111 }
1112
1113 return false;
1114}
1115
1117 for (auto inputConsumer : _inputConsumers) {
1118 if (inputConsumer && inputConsumer->joystickPovMoved(arg)) {
1119 return true;
1120 }
1121 }
1122
1123 return false;
1124}
1125
1127 for (auto inputConsumer : _inputConsumers) {
1128 if (inputConsumer && inputConsumer->joystickButtonPressed(arg)) {
1129 return true;
1130 }
1131 }
1132
1133 return false;
1134}
1135
1137 for (auto inputConsumer : _inputConsumers) {
1138 if (inputConsumer && inputConsumer->joystickButtonReleased(arg)) {
1139 return true;
1140 }
1141 }
1142
1143 return false;
1144}
1145
1147 for (auto inputConsumer : _inputConsumers) {
1148 if (inputConsumer && inputConsumer->joystickBallMoved(arg)) {
1149 return true;
1150 }
1151 }
1152
1153 return false;
1154}
1155
1157 for (auto inputConsumer : _inputConsumers) {
1158 if (inputConsumer && inputConsumer->joystickAddRemove(arg)) {
1159 return true;
1160 }
1161 }
1162
1163 return false;
1164}
1165
1167 for (auto inputConsumer : _inputConsumers) {
1168 if (inputConsumer && inputConsumer->joystickRemap(arg)) {
1169 return true;
1170 }
1171 }
1172
1173 return false;
1174}
1175
1177 for (auto inputConsumer : _inputConsumers) {
1178 if (inputConsumer && inputConsumer->onTextEvent(arg)) {
1179 return true;
1180 }
1181 }
1182
1183 return false;
1184}
1185#pragma endregion
1186};
1187
#define WAIT_FOR_CONDITION(...)
#define LOCALE_STR(X)
Definition: Localization.h:91
#define SCOPE_EXIT
#define DIVIDE_ASSERT(...)
#define NO_DESTROY
#define DIVIDE_UNEXPECTED_CALL()
#define PROFILE_SCOPE_AUTO(CATEGORY)
Definition: Profiler.h:87
#define PROFILE_TAG(NAME,...)
Definition: Profiler.h:88
#define PROFILE_SCOPE(NAME, CATEGORY)
Definition: Profiler.h:86
char * argv[]
Definition: main.cpp:8
static void SET_LOG_FUNCTION(const LOG_CBK &cbk)
Definition: ASIO.cpp:118
Class that provides an interface between our framework and the OS (start/stop, display support,...
Definition: Application.h:97
static ErrorCode SetRenderingAPI(Application &app, const RenderAPI api)
Definition: Application.h:203
static void onWindowSizeChange(GFXDevice &device, const SizeChangeParams &params)
Definition: GFXDevice.h:587
static void update(GFXDevice &device, const U64 deltaTimeUSFixed, const U64 deltaTimeUSApp)
Definition: GFXDevice.h:597
static void onResolutionChange(GFXDevice &device, const SizeChangeParams &params)
Definition: GFXDevice.h:592
static void onThreadCreated(const PlatformContext &context, const TaskPoolType poolType, const std::thread::id &threadID, const bool isMainRenderThread)
static bool networkUpdate(Divide::ProjectManager *manager, const U64 frameCount)
static ProjectIDs & init(Divide::ProjectManager *manager)
static void currentPlayerPass(Divide::ProjectManager *manager, const PlayerIndex idx)
static void initPostLoadState(Divide::ProjectManager *manager) noexcept
static void UseTextureDDSCache(const bool state) noexcept
Definition: Texture.h:167
static void DestroyPool()
Definition: Camera.cpp:139
static void InitPool()
Definition: Camera.cpp:130
static void Update(U64 deltaTimeUS)
Definition: Camera.cpp:107
bool hidden() const noexcept
bool maximized() const noexcept
bool decorated() const noexcept
void centerWindowPosition()
Centering is also easier via SDL.
void setPosition(I32 x, I32 y, bool global=false, bool offset=false)
Window positioning is handled by SDL.
bool setDimensions(U16 width, U16 height)
width and height get adjusted to the closest supported value
const char * title() const noexcept
void changeType(WindowType newType)
vec2< U16 > getPreviousDimensions() const noexcept
static D64 FrameInterpolationFactor() noexcept
Definition: GFXDevice.h:339
static U64 FrameCount() noexcept
Definition: GFXDevice.h:340
static MouseState & state(MouseEvent &evt) noexcept
U8 _prevPlayerCount
Definition: Kernel.h:180
bool joystickAddRemove(const Input::JoystickEvent &arg) override
Definition: Kernel.cpp:1156
~Kernel() override
Definition: Kernel.cpp:107
bool joystickAxisMoved(const Input::JoystickEvent &arg) override
Joystick axis change.
Definition: Kernel.cpp:1106
void warmup()
Definition: Kernel.cpp:642
void startSplashScreen()
Definition: Kernel.cpp:112
ErrorCode initialize(const string &entryPoint)
Definition: Kernel.cpp:662
char ** _argv
Definition: Kernel.h:177
FORCE_INLINE PlatformContext & platformContext() noexcept
Definition: Kernel.h:129
Time::ProfileTimer & _frameTimer
Definition: Kernel.h:161
Time::ProfileTimer & _postRenderTimer
Definition: Kernel.h:169
bool onTextEvent(const Input::TextEvent &arg) override
Generated by text events (e.g. for SDL: SDL_TEXTEDITING and SDL_TEXTINPUT)
Definition: Kernel.cpp:1176
bool mouseButtonReleased(const Input::MouseButtonEvent &arg) override
Mouse button released.
Definition: Kernel.cpp:1064
bool joystickRemap(const Input::JoystickEvent &arg) override
Definition: Kernel.cpp:1166
bool mouseMoved(const Input::MouseMoveEvent &arg) override
Mouse moved.
Definition: Kernel.cpp:1019
Time::ProfileTimer & _sceneUpdateLoopTimer
Definition: Kernel.h:165
bool joystickPovMoved(const Input::JoystickEvent &arg) override
Joystick direction change.
Definition: Kernel.cpp:1116
bool onKeyUp(const Input::KeyEvent &key) override
Key released.
Definition: Kernel.cpp:1096
void onWindowSizeChange(const SizeChangeParams &params)
Definition: Kernel.cpp:969
void shutdown()
Definition: Kernel.cpp:934
Time::ProfileTimer & _sceneUpdateTimer
Definition: Kernel.h:164
bool presentToScreen(FrameEvent &evt)
Definition: Kernel.cpp:572
Time::ProfileTimer & _appScenePass
Definition: Kernel.h:163
static size_t TotalThreadCount(TaskPoolType type) noexcept
Definition: Kernel.cpp:51
bool onKeyDown(const Input::KeyEvent &key) override
Key pressed.
Definition: Kernel.cpp:1086
Time::ProfileTimer & _cameraMgrTimer
Definition: Kernel.h:166
bool joystickButtonPressed(const Input::JoystickEvent &arg) override
Joystick button pressed.
Definition: Kernel.cpp:1126
Time::ProfileTimer & _appIdleTimer
Definition: Kernel.h:162
Time::ProfileTimer & _appLoopTimerInternal
Definition: Kernel.h:160
void onLoop()
Our main application rendering loop: Call input requests, physics calculations, pre-rendering,...
Definition: Kernel.cpp:181
std::array< InputAggregatorInterface *, to_base(InputConsumerType::COUNT)> _inputConsumers
Definition: Kernel.h:155
bool mouseButtonPressed(const Input::MouseButtonEvent &arg) override
Mouse button pressed.
Definition: Kernel.cpp:1042
void remapAbsolutePosition(Input::MouseEvent &eventInOut) const noexcept
Definition: Kernel.cpp:992
bool joystickButtonReleased(const Input::JoystickEvent &arg) override
Joystick button released.
Definition: Kernel.cpp:1136
Time::ProfileTimer & _flushToScreenTimer
Definition: Kernel.h:167
Kernel(I32 argc, char **argv, Application &parentApp)
Definition: Kernel.cpp:72
Time::ProfileTimer & _preRenderTimer
Definition: Kernel.h:168
std::unique_ptr< GUISplash > _splashScreen
Definition: Kernel.h:173
bool mainLoopScene(FrameEvent &evt)
Definition: Kernel.cpp:344
FORCE_INLINE FrameListenerManager & frameListenerMgr() noexcept
Definition: Kernel.h:128
Time::ProfileTimer & _appLoopTimerMain
Definition: Kernel.h:159
bool joystickBallMoved(const Input::JoystickEvent &arg) override
Definition: Kernel.cpp:1146
void idle(bool fast, U64 deltaTimeUSGame, U64 deltaTimeUSApp)
Definition: Kernel.cpp:154
void stopSplashScreen()
Definition: Kernel.cpp:134
vector< Rect< I32 > > _targetViewports
Definition: Kernel.h:157
void onResolutionChange(const SizeChangeParams &params)
Definition: Kernel.cpp:977
vector< Time::ProfileTimer * > _renderTimer
Definition: Kernel.h:170
std::atomic_bool _splashScreenUpdating
Definition: Kernel.h:172
Rect< I32 > _prevViewport
Definition: Kernel.h:179
GFXDevice & gfx() noexcept
static bool OnStartup(PlatformContext &context)
static bool OnShutdown(PlatformContext &context)
bool contains(U xIn, U yIn) const noexcept
Definition: MathVectors.h:1465
static void OnFrameEnd()
static void OnFrameStart()
static void Init(RenderAPI renderAPI, PlatformContext &context)
static void PrintLeakedResources()
static void OnStartup(GFXDevice &context)
static void OnShutdown(GFXDevice &context)
static void idle()
Definition: Script.cpp:62
static bool OnStartup()
Definition: Script.cpp:65
static bool OnShutdown()
Definition: Script.cpp:92
static void initShadowMaps(GFXDevice &context)
Definition: ShadowMap.cpp:73
static void destroyShadowMaps(GFXDevice &context)
Definition: ShadowMap.cpp:239
void addChildTimer(ProfileTimer &child)
static void removeTimer(ProfileTimer &timer)
void set(const T *v) noexcept
set the 4 components of the vector manually using a source pointer to a (large enough) array
Definition: MathVectors.h:1241
constexpr bool ENABLE_EDITOR
Definition: config.h:66
constexpr bool IS_SHIPPING_BUILD
Definition: config.h:60
constexpr bool IS_DEBUG_BUILD
Definition: config.h:55
constexpr bool IS_PROFILE_BUILD
Definition: config.h:56
constexpr bool IS_EDITOR_BUILD
Definition: config.h:65
constexpr U16 NETWORK_SEND_FREQUENCY_HZ
How often should the client send messages to the server.
Definition: config.h:185
constexpr U16 NETWORK_SEND_RETRY_COUNT
How many times should we try to send an update to the server before giving up?
Definition: config.h:188
constexpr U16 TARGET_FRAME_RATE
Application desired framerate for physics and input simulations.
Definition: config.h:97
constexpr char DEFAULT_PROJECT_NAME[]
Definition: config.h:197
constexpr size_t REQUIRED_RAM_SIZE_IN_BYTES
Definition: config.h:111
constexpr const char * DROID_SERIF_BOLD
Definition: TextLabel.h:48
ErrorCode ChangeLanguage(const char *newLanguage)
void Idle()
perform maintenance tasks
static const char * renderAPI[]
constexpr Optick::Category::Type IO
Definition: Profiler.h:68
constexpr Optick::Category::Type Graphics
Definition: Profiler.h:60
U64 ElapsedMicroseconds() noexcept
ProfileTimer & ADD_TIMER(const char *timerName)
constexpr T Seconds(T a)
Definition: MathHelper.inl:647
const char * ErrorCodeToString(const ErrorCode err) noexcept
Definition: ErrorCodes.h:122
bool ExtractStartupProject(int argc, char **argv, string &projectOut, const char *arg_prefix="--")
bool FindCommandLineArgument(int argc, char **argv, const char *target_arg, const char *arg_prefix="--")
Handle console commands that start with a forward slash.
Definition: AIProcessor.cpp:7
constexpr U32 to_U32(const T value)
int32_t I32
vector< ProjectID > ProjectIDs
uint8_t U8
int16_t I16
void CLAMP_IN_RECT(T &inout_x, T &inout_y, T rect_x, T rect_y, T rect_z, T rect_w) noexcept
Definition: MathHelper.inl:162
static Time::ProfileTimer & GetTimer(Time::ProfileTimer &parentTimer, vector< Time::ProfileTimer * > &timers, const U8 index, const char *name)
Definition: Kernel.cpp:562
void SetThreadPriority(ThreadPriority priority)
static void ComputeViewports(const Rect< I32 > &mainViewport, vector< Rect< I32 > > &targetViewports, const U8 count)
Definition: Kernel.cpp:461
eastl::vector< Type > vector
Definition: Vector.h:42
const SysInfo & const_sysInfo() noexcept
uint16_t U16
vec2< T > COORD_REMAP(vec2< T > input, const Rect< T > &in_rect, const Rect< T > &out_rect) noexcept
Definition: MathHelper.h:172
constexpr U8 to_U8(const T value)
constexpr size_t to_size(const T value)
constexpr U32 minSquareMatrixSize(U32 elementCount) noexcept
Definition: MathHelper.inl:229
vec4< U8 > UColour4
Definition: MathHelper.h:72
constexpr I32 to_I32(const T value)
void InitConditionalWait(PlatformContext &context) noexcept
uint32_t U32
uint64_t U64
constexpr U64 FIXED_UPDATE_RATE_US
constexpr auto to_base(const Type value) -> Type
float floorf(const float in)
struct Divide::Configuration::Debug::Cache cache
struct Divide::Configuration::Debug::Renderer renderer
struct Divide::Configuration::Runtime runtime
struct Divide::Configuration::Debug debug
static NO_INLINE void errorfn(const char *format, T &&... args)
static NO_INLINE void warnfn(const char *format, T &&... args)
static void Flush()
Definition: Console.cpp:126
static NO_INLINE void printfn(const char *format, T &&... args)
struct Divide::FrameEvent::Time::Impl _game
struct Divide::FrameEvent::Time::Impl _app
struct Divide::FrameEvent::Time _time
static RenderTargetID BACK_BUFFER
Definition: GFXDevice.h:196
StringReturnType< N > string() const noexcept
Definition: ResourcePath.h:64
ResourcePath _workingDirectory
size_t _availableRamInBytes