iMSTK
Interactive Medical Simulation Toolkit
imstkSimulationManager.cpp
1 /*
2 ** This file is part of the Interactive Medical Simulation Toolkit (iMSTK)
3 ** iMSTK is distributed under the Apache License, Version 2.0.
4 ** See accompanying NOTICE for details.
5 */
6 
7 #include "imstkSimulationManager.h"
8 #include "imstkMacros.h"
9 #include "imstkTimer.h"
10 #include "imstkViewer.h"
11 
12 #include <thread>
13 DISABLE_WARNING_PUSH
14  DISABLE_WARNING_PADDING
15 #include <tbb/task_group.h>
16 DISABLE_WARNING_POP
17 
18 namespace imstk
19 {
20 void
21 SimulationManager::start()
22 {
23  // Modules can cause a full exit internally
24  // particularly needed for viewers which contain OS event loop, so when the window
25  // exit message happens all modules need to stop
26  for (auto module : m_modules)
27  {
28  connect<Event>(module, &Module::end,
29  std::static_pointer_cast<SimulationManager>(shared_from_this()),
30  &SimulationManager::requestStop);
31  }
32 
33  // Initialization
34  for (auto syncModule : m_syncModules)
35  {
36  syncModule->init();
37  }
38  for (auto adaptiveModule : m_adaptiveModules)
39  {
40  adaptiveModule->init();
41  }
42  // Initialize viewer last (dependence on above)
43  for (auto viewer : m_viewers)
44  {
45  viewer->init();
46  }
47 
48  // Start parallel modules
49  tbb::task_group tasks;
50  std::vector<std::thread> threads(m_asyncModules.size());
51  {
52  if (m_threadType == ThreadingType::TBB)
53  {
54  for (auto module : m_asyncModules)
55  {
56  tasks.run([this, module]() { runModuleParallel(module); });
57  }
58  tasks.wait();
59  }
60  else if (m_threadType == ThreadingType::STL)
61  {
62  for (size_t i = 0; i < m_asyncModules.size(); i++)
63  {
64  std::shared_ptr<Module> module = m_asyncModules[i];
65  threads[i] = std::thread(std::bind(&SimulationManager::runModuleParallel, this, module));
66  }
67  }
68  }
69 
70  waitForInit();
71 
72  postEvent(Event(SimulationManager::starting()));
73 
74  // Start the game loop
75  {
76  const double desiredDt_ms = m_desiredDt * 1000.0; // ms
77  m_numSteps = 0;
78  double accumulator = 0.0;
79  StopWatch timer;
80  timer.start();
81  bool running = true;
82 
83  // Mark all as running but async modules
84  for (auto module : m_viewers)
85  {
86  m_running[module.get()] = true;
87  }
88  for (auto module : m_syncModules)
89  {
90  m_running[module.get()] = true;
91  }
92  for (auto module : m_adaptiveModules)
93  {
94  m_running[module.get()] = true;
95  }
96 
97  while (running)
98  {
99  const int newState = simState;
100  if (newState == ModuleDriverStopped)
101  {
102  running = false;
103  continue;
104  }
105 
106  const double passedTime = timer.getTimeElapsed();
107  timer.start();
108 
109  if (newState == ModuleDriverPaused)
110  {
111  continue;
112  }
113 
114  // Accumulate the real time passed
115  accumulator += passedTime;
116 
117  // Compute number of steps we can take (total time previously took / desired time step)
118  {
119  // Divided out and floor
120  m_numSteps = static_cast<int>(accumulator / desiredDt_ms);
121  // accumulator now contains remainder time
122  accumulator = accumulator - m_numSteps * desiredDt_ms;
123  m_dt = desiredDt_ms;
124 
125  // Flatten out the remainder over our desired dt
127  {
128  if (m_numSteps != 0)
129  {
130  m_dt += accumulator / m_numSteps;
131  accumulator = 0.0; // Remove remainder time
132  }
133  }
134  m_dt *= 0.001; // ms->s
135  }
136 
137  //printf("%d steps at %f\n", m_numSteps, m_dt);
138 
139  // Optional smoothening + loss here
140 
141  // Actual game loop
142  {
143  for (auto syncModule : m_syncModules)
144  {
145  syncModule->setDt(m_dt);
146  syncModule->update();
147  }
148 
149  for (auto adaptiveModule : m_adaptiveModules)
150  {
151  adaptiveModule->setDt(m_dt);
152  for (int currStep = 0; currStep < m_numSteps; currStep++)
153  {
154  // Process system & input events (ie: VR, hmd pose, mkd, OS msgs updates)
155  for (auto viewer : m_viewers)
156  {
157  viewer->processEvents();
158  }
159  adaptiveModule->update();
160  }
161  }
162 
163  for (auto viewer : m_viewers)
164  {
165  viewer->setDt(m_numSteps * m_dt);
166  viewer->update();
167  }
168  }
169  }
170  }
171 
172  postEvent(Event(SimulationManager::ending()));
173 
174  if (m_threadType == ThreadingType::TBB)
175  {
176  }
177  else if (m_threadType == ThreadingType::STL)
178  {
179  for (size_t i = 0; i < threads.size(); i++)
180  {
181  threads[i].join();
182  }
183  }
184 
185  for (auto module : m_modules)
186  {
187  m_running[module.get()] = false;
188  module->uninit();
189  }
190 }
191 
192 void
193 SimulationManager::addModule(std::shared_ptr<Module> module)
194 {
195  ModuleDriver::addModule(module);
196 
197  if (std::shared_ptr<Viewer> viewer = std::dynamic_pointer_cast<Viewer>(module))
198  {
199  m_viewers.push_back(viewer);
200  return;
201  }
202 
203  if (module->getExecutionType() == Module::ExecutionType::SEQUENTIAL)
204  {
205  m_syncModules.push_back(module);
206  }
207  else if (module->getExecutionType() == Module::ExecutionType::PARALLEL)
208  {
209  m_asyncModules.push_back(module);
210  }
211  else if (module->getExecutionType() == Module::ExecutionType::ADAPTIVE)
212  {
213  m_adaptiveModules.push_back(module);
214  }
215 }
216 
217 void
219 {
221  m_viewers.clear();
222  m_syncModules.clear();
223  m_asyncModules.clear();
224  m_adaptiveModules.clear();
225 }
226 
227 void
228 SimulationManager::runModuleParallel(std::shared_ptr<Module> module)
229 {
230  module->init();
231 
232  waitForInit();
233 
234  m_running[module.get()] = true;
235  while (m_running[module.get()])
236  {
237  // ModuleDriver state will stop/pause/run all modules
238  const int newState = simState;
239  if (newState == ModuleDriverStopped)
240  {
241  m_running[module.get()] = false;
242  }
243  else if (newState == ModuleDriverRunning)
244  {
245  std::shared_ptr<Viewer> viewer = std::dynamic_pointer_cast<Viewer>(module);
246  if (viewer != nullptr)
247  {
248  viewer->processEvents();
249  }
250 
251  module->update();
252  }
253  }
254 }
255 
256 void
257 SimulationManager::requestStop(Event* e)
258 {
259  Module* module = static_cast<Module*>(e->m_sender);
260  if (module != nullptr)
261  {
262  requestStatus(ModuleDriverStopped);
263  m_running[module] = false;
264  }
265 }
266 } // namespace imstk
double m_desiredDt
Desired timestep.
Base class for events which contain a type, priority, and data priority defaults to 0 and uses a grea...
virtual void addModule(std::shared_ptr< Module > module)
Add a module to run.
Compound Geometry.
void addModule(std::shared_ptr< Module > module) override
Add a module to run.
virtual void clearModules()
Remove all modules.
std::vector< std::shared_ptr< Module > > m_adaptiveModules
Modules that update adpatively to keep up with real time.
std::vector< std::shared_ptr< Module > > m_asyncModules
Modules that run on completely other threads without restraint.
Base class for viewer that manages render window and the renderers Creates backend-specific renderers...
Definition: imstkViewer.h:43
std::vector< std::shared_ptr< Module > > m_syncModules
Modules called once per update.
bool m_useRemainderTimeDivide
Whether to divide out remainder time or not.
double m_dt
Actual timestep.
void clearModules() override
Remove all modules.
void waitForInit()
Wait for all modules to init.
void postEvent(const T &e)
Emits the event Direct observers will be immediately called, in sync Queued observers will receive th...
Base class for imstk module system. A module defines something that is updated, and can be paused/res...
Definition: imstkModule.h:21