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 | |
49 | namespace 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 | |