1//************************************ bs::framework - Copyright 2018 Marko Pintera **************************************//
2//*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********//
3#include "BsCoreApplication.h"
4
5#include "RenderAPI/BsRenderAPI.h"
6#include "Managers/BsRenderAPIManager.h"
7
8#include "Platform/BsPlatform.h"
9#include "RenderAPI/BsRenderWindow.h"
10#include "Math/BsVector2.h"
11#include "CoreThread/BsCoreObjectManager.h"
12#include "Scene/BsGameObjectManager.h"
13#include "Utility/BsDynLib.h"
14#include "Utility/BsDynLibManager.h"
15#include "Scene/BsSceneManager.h"
16#include "Importer/BsImporter.h"
17#include "Resources/BsResources.h"
18#include "Scene/BsSceneObject.h"
19#include "Utility/BsTime.h"
20#include "Input/BsInput.h"
21#include "Renderer/BsRendererManager.h"
22#include "Managers/BsGpuProgramManager.h"
23#include "Managers/BsMeshManager.h"
24#include "Managers/BsRenderWindowManager.h"
25#include "Renderer/BsRenderer.h"
26#include "Utility/BsDeferredCallManager.h"
27#include "CoreThread/BsCoreThread.h"
28#include "Localization/BsStringTableManager.h"
29#include "Profiling/BsProfilingManager.h"
30#include "Profiling/BsProfilerCPU.h"
31#include "Profiling/BsProfilerGPU.h"
32#include "Managers/BsQueryManager.h"
33#include "Threading/BsThreadPool.h"
34#include "Threading/BsTaskScheduler.h"
35#include "Profiling/BsRenderStats.h"
36#include "Utility/BsMessageHandler.h"
37#include "Managers/BsResourceListenerManager.h"
38#include "Managers/BsRenderStateManager.h"
39#include "Material/BsShaderManager.h"
40#include "Physics/BsPhysicsManager.h"
41#include "Physics/BsPhysics.h"
42#include "Audio/BsAudioManager.h"
43#include "Audio/BsAudio.h"
44#include "Animation/BsAnimationManager.h"
45#include "Renderer/BsParamBlocks.h"
46#include "Particles/BsParticleManager.h"
47#include "Particles/BsVectorField.h"
48
49namespace bs
50{
51 CoreApplication::CoreApplication(START_UP_DESC desc)
52 : mPrimaryWindow(nullptr), mStartUpDesc(desc), mRendererPlugin(nullptr), mIsFrameRenderingFinished(true)
53 , mSimThreadId(BS_THREAD_CURRENT_ID), mRunMainLoop(false)
54 {
55 // Ensure all errors are reported properly
56 CrashHandler::startUp();
57 }
58
59 CoreApplication::~CoreApplication()
60 {
61 mPrimaryWindow->destroy();
62 mPrimaryWindow = nullptr;
63
64 Importer::shutDown();
65 MeshManager::shutDown();
66 ProfilerGPU::shutDown();
67
68 SceneManager::shutDown();
69
70 Input::shutDown();
71
72 ct::ParamBlockManager::shutDown();
73 StringTableManager::shutDown();
74 Resources::shutDown();
75 GameObjectManager::shutDown();
76
77 // Audio manager must be released before the ResourceListenerManager, as any one-shot audio sources need to be
78 // destroyed since they implement the IResourceListener interface
79 AudioManager::shutDown();
80 ResourceListenerManager::shutDown();
81 RenderStateManager::shutDown();
82 ParticleManager::shutDown();
83 AnimationManager::shutDown();
84
85 // This must be done after all resources are released since it will unload the physics plugin, and some resources
86 // might be instances of types from that plugin.
87 PhysicsManager::shutDown();
88
89 RendererManager::shutDown();
90
91 // All CoreObject related modules should be shut down now. They have likely queued CoreObjects for destruction, so
92 // we need to wait for those objects to get destroyed before continuing.
93 CoreObjectManager::instance().syncToCore();
94 gCoreThread().update();
95 gCoreThread().submitAll(true);
96
97 unloadPlugin(mRendererPlugin);
98
99 RenderAPIManager::shutDown();
100 ct::GpuProgramManager::shutDown();
101 GpuProgramManager::shutDown();
102
103 CoreObjectManager::shutDown(); // Must shut down before DynLibManager to ensure all objects are destroyed before unloading their libraries
104 DynLibManager::shutDown();
105 Time::shutDown();
106 DeferredCallManager::shutDown();
107
108 CoreThread::shutDown();
109 RenderStats::shutDown();
110 TaskScheduler::shutDown();
111 ThreadPool::shutDown();
112 ProfilingManager::shutDown();
113 ProfilerCPU::shutDown();
114 MessageHandler::shutDown();
115 ShaderManager::shutDown();
116
117 MemStack::endThread();
118 Platform::_shutDown();
119
120 CrashHandler::shutDown();
121 }
122
123 void CoreApplication::onStartUp()
124 {
125 UINT32 numWorkerThreads = BS_THREAD_HARDWARE_CONCURRENCY - 1; // Number of cores while excluding current thread.
126
127 Platform::_startUp();
128 MemStack::beginThread();
129
130 ShaderManager::startUp(getShaderIncludeHandler());
131 MessageHandler::startUp();
132 ProfilerCPU::startUp();
133 ProfilingManager::startUp();
134 ThreadPool::startUp<TThreadPool<ThreadDefaultPolicy>>((numWorkerThreads));
135 TaskScheduler::startUp();
136 TaskScheduler::instance().removeWorker();
137 RenderStats::startUp();
138 CoreThread::startUp();
139 StringTableManager::startUp();
140 DeferredCallManager::startUp();
141 Time::startUp();
142 DynLibManager::startUp();
143 CoreObjectManager::startUp();
144 GameObjectManager::startUp();
145 Resources::startUp();
146 ResourceListenerManager::startUp();
147 GpuProgramManager::startUp();
148 RenderStateManager::startUp();
149 ct::GpuProgramManager::startUp();
150 RenderAPIManager::startUp();
151
152 mPrimaryWindow = RenderAPIManager::instance().initialize(mStartUpDesc.renderAPI, mStartUpDesc.primaryWindowDesc);
153
154 ct::ParamBlockManager::startUp();
155 Input::startUp();
156 RendererManager::startUp();
157
158 loadPlugin(mStartUpDesc.renderer, &mRendererPlugin);
159
160 // Must be initialized before the scene manager, as game scene creation triggers physics scene creation
161 PhysicsManager::startUp(mStartUpDesc.physics, mStartUpDesc.physicsCooking);
162 SceneManager::startUp();
163 RendererManager::instance().setActive(mStartUpDesc.renderer);
164 startUpRenderer();
165
166 ProfilerGPU::startUp();
167 MeshManager::startUp();
168 Importer::startUp();
169 AudioManager::startUp(mStartUpDesc.audio);
170 AnimationManager::startUp();
171 ParticleManager::startUp();
172
173 for (auto& importerName : mStartUpDesc.importers)
174 loadPlugin(importerName);
175
176 // Built-in importers
177 FGAImporter* fgaImporter = bs_new<FGAImporter>();
178 Importer::instance()._registerAssetImporter(fgaImporter);
179 }
180
181 void CoreApplication::runMainLoop()
182 {
183 mRunMainLoop = true;
184
185 while(mRunMainLoop)
186 {
187 // Limit FPS if needed
188 if (mFrameStep > 0)
189 {
190 UINT64 currentTime = gTime().getTimePrecise();
191 UINT64 nextFrameTime = mLastFrameTime + mFrameStep;
192 while (nextFrameTime > currentTime)
193 {
194 UINT32 waitTime = (UINT32)(nextFrameTime - currentTime);
195
196 // If waiting for longer, sleep
197 if (waitTime >= 2000)
198 {
199 Platform::sleep(waitTime / 1000);
200 currentTime = gTime().getTimePrecise();
201 }
202 else
203 {
204 // Otherwise we just spin, sleep timer granularity is too low and we might end up wasting a
205 // millisecond otherwise.
206 // Note: For mobiles where power might be more important than input latency, consider using sleep.
207 while(nextFrameTime > currentTime)
208 currentTime = gTime().getTimePrecise();
209 }
210 }
211
212 mLastFrameTime = currentTime;
213 }
214
215 gProfilerCPU().beginThread("Sim");
216
217 Platform::_update();
218 DeferredCallManager::instance()._update();
219 gTime()._update();
220 gInput()._update();
221 // RenderWindowManager::update needs to happen after Input::update and before Input::_triggerCallbacks,
222 // so that all input is properly captured in case there is a focus change, and so that
223 // focus change is registered before input events are sent out (mouse press can result in code
224 // checking if a window is in focus, so it has to be up to date)
225 RenderWindowManager::instance()._update();
226 gInput()._triggerCallbacks();
227 gDebug()._triggerCallbacks();
228
229 preUpdate();
230
231 // Trigger fixed updates if required
232 {
233 UINT64 step;
234 const UINT32 numIterations = gTime()._getFixedUpdateStep(step);
235
236 const float stepSeconds = step / 1000000.0f;
237 for (UINT32 i = 0; i < numIterations; i++)
238 {
239 fixedUpdate();
240 PROFILE_CALL(gSceneManager()._fixedUpdate(), "Scene fixed update");
241 PROFILE_CALL(gPhysics().fixedUpdate(stepSeconds), "Physics simulation");
242
243 gTime()._advanceFixedUpdate(step);
244 }
245 }
246
247 PROFILE_CALL(gSceneManager()._update(), "Scene update");
248 gAudio()._update();
249 gPhysics().update();
250
251 // Update plugins
252 for (auto& pluginUpdateFunc : mPluginUpdateFunctions)
253 pluginUpdateFunc.second();
254
255 postUpdate();
256
257 PerFrameData perFrameData;
258
259 // Evaluate animation after scene and plugin updates because the renderer will just now be displaying the
260 // animation we sent on the previous frame, and we want the scene information to match to what is displayed.
261 perFrameData.animation = AnimationManager::instance().update();
262 perFrameData.particles = ParticleManager::instance().update(*perFrameData.animation);
263
264 // Send out resource events in case any were loaded/destroyed/modified
265 ResourceListenerManager::instance().update();
266
267 // Trigger any renderer task callbacks (should be done before scene object update, or core sync, so objects have
268 // a chance to respond to the callback).
269 RendererManager::instance().getActive()->update();
270
271 gSceneManager()._updateCoreObjectTransforms();
272 PROFILE_CALL(RendererManager::instance().getActive()->renderAll(perFrameData), "Render");
273
274 // Core and sim thread run in lockstep. This will result in a larger input latency than if I was
275 // running just a single thread. Latency becomes worse if the core thread takes longer than sim
276 // thread, in which case sim thread needs to wait. Optimal solution would be to get an average
277 // difference between sim/core thread and start the sim thread a bit later so they finish at nearly the same time.
278 {
279 Lock lock(mFrameRenderingFinishedMutex);
280
281 while(!mIsFrameRenderingFinished)
282 {
283 TaskScheduler::instance().addWorker();
284 mFrameRenderingFinishedCondition.wait(lock);
285 TaskScheduler::instance().removeWorker();
286 }
287
288 mIsFrameRenderingFinished = false;
289 }
290
291 gCoreThread().queueCommand(std::bind(&CoreApplication::beginCoreProfiling, this), CTQF_InternalQueue);
292 gCoreThread().queueCommand(&Platform::_coreUpdate, CTQF_InternalQueue);
293 gCoreThread().queueCommand(std::bind(&ct::RenderWindowManager::_update, ct::RenderWindowManager::instancePtr()), CTQF_InternalQueue);
294
295 gCoreThread().update();
296 gCoreThread().submitAll();
297
298 gCoreThread().queueCommand(std::bind(&CoreApplication::frameRenderingFinishedCallback, this), CTQF_InternalQueue);
299
300 gCoreThread().queueCommand(std::bind(&ct::QueryManager::_update, ct::QueryManager::instancePtr()), CTQF_InternalQueue);
301 gCoreThread().queueCommand(std::bind(&CoreApplication::endCoreProfiling, this), CTQF_InternalQueue);
302
303 gProfilerCPU().endThread();
304 gProfiler()._update();
305 }
306
307 // Wait until last core frame is finished before exiting
308 {
309 Lock lock(mFrameRenderingFinishedMutex);
310
311 while (!mIsFrameRenderingFinished)
312 {
313 TaskScheduler::instance().addWorker();
314 mFrameRenderingFinishedCondition.wait(lock);
315 TaskScheduler::instance().removeWorker();
316 }
317 }
318 }
319
320 void CoreApplication::preUpdate()
321 {
322 // Do nothing
323 }
324
325 void CoreApplication::postUpdate()
326 {
327 // Do nothing
328 }
329
330 void CoreApplication::fixedUpdate()
331 {
332 // Do nothing
333 }
334
335 void CoreApplication::stopMainLoop()
336 {
337 mRunMainLoop = false; // No sync primitives needed, in that rare case of
338 // a race condition we might run the loop one extra iteration which is acceptable
339 }
340
341 void CoreApplication::quitRequested()
342 {
343 stopMainLoop();
344 }
345
346 void CoreApplication::setFPSLimit(UINT32 limit)
347 {
348 if(limit > 0)
349 mFrameStep = (UINT64)1000000 / limit;
350 else
351 mFrameStep = 0;
352 }
353
354 void CoreApplication::frameRenderingFinishedCallback()
355 {
356 Lock lock(mFrameRenderingFinishedMutex);
357
358 mIsFrameRenderingFinished = true;
359 mFrameRenderingFinishedCondition.notify_one();
360 }
361
362 void CoreApplication::startUpRenderer()
363 {
364 RendererManager::instance().initialize();
365 }
366
367 void CoreApplication::beginCoreProfiling()
368 {
369 gProfilerCPU().beginThread("Core");
370 }
371
372 void CoreApplication::endCoreProfiling()
373 {
374 ProfilerGPU::instance()._update();
375
376 gProfilerCPU().endThread();
377 gProfiler()._updateCore();
378 }
379
380 void* CoreApplication::loadPlugin(const String& pluginName, DynLib** library, void* passThrough)
381 {
382 DynLib* loadedLibrary = gDynLibManager().load(pluginName);
383 if(library != nullptr)
384 *library = loadedLibrary;
385
386 void* retVal = nullptr;
387 if(loadedLibrary != nullptr)
388 {
389 if (passThrough == nullptr)
390 {
391 typedef void* (*LoadPluginFunc)();
392
393 LoadPluginFunc loadPluginFunc = (LoadPluginFunc)loadedLibrary->getSymbol("loadPlugin");
394
395 if (loadPluginFunc != nullptr)
396 retVal = loadPluginFunc();
397 }
398 else
399 {
400 typedef void* (*LoadPluginFunc)(void*);
401
402 LoadPluginFunc loadPluginFunc = (LoadPluginFunc)loadedLibrary->getSymbol("loadPlugin");
403
404 if (loadPluginFunc != nullptr)
405 retVal = loadPluginFunc(passThrough);
406 }
407
408 UpdatePluginFunc loadPluginFunc = (UpdatePluginFunc)loadedLibrary->getSymbol("updatePlugin");
409
410 if (loadPluginFunc != nullptr)
411 mPluginUpdateFunctions[loadedLibrary] = loadPluginFunc;
412 }
413
414 return retVal;
415 }
416
417 void CoreApplication::unloadPlugin(DynLib* library)
418 {
419 typedef void (*UnloadPluginFunc)();
420
421 UnloadPluginFunc unloadPluginFunc = (UnloadPluginFunc)library->getSymbol("unloadPlugin");
422
423 if(unloadPluginFunc != nullptr)
424 unloadPluginFunc();
425
426 mPluginUpdateFunctions.erase(library);
427 gDynLibManager().unload(library);
428 }
429
430 SPtr<IShaderIncludeHandler> CoreApplication::getShaderIncludeHandler() const
431 {
432 return bs_shared_ptr_new<DefaultShaderIncludeHandler>();
433 }
434
435 CoreApplication& gCoreApplication()
436 {
437 return CoreApplication::instance();
438 }
439}
440