Guest User

Untitled

a guest
Jun 28th, 2026
73
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Diff 7.22 KB | None | 0 0
  1. diff --git a/src/gui/game/GameView.cpp b/src/gui/game/GameView.cpp
  2. index 48f2c91d..d3b7b62c 100644
  3. --- a/src/gui/game/GameView.cpp
  4. +++ b/src/gui/game/GameView.cpp
  5. @@ -2544,6 +2544,10 @@ void GameView::OnDraw()
  6.         //FPS and some version info
  7.         StringBuilder fpsInfo;
  8.         fpsInfo << Format::Precision(2) << "FPS: " << ui::Engine::Ref().GetFps();
  9. +       if (sim)
  10. +       {
  11. +           fpsInfo << " Threads: " << sim->GetActiveSimulationThreads() << "/" << sim->GetThreadCount();
  12. +       }
  13.  
  14.         if (showDebug)
  15.         {
  16. diff --git a/src/simulation/Simulation.cpp b/src/simulation/Simulation.cpp
  17. index 89b169a3..d39efe32 100644
  18. --- a/src/simulation/Simulation.cpp
  19. +++ b/src/simulation/Simulation.cpp
  20. @@ -21,9 +21,44 @@
  21.  #include <numbers>
  22.  #include <set>
  23.  #include <stack>
  24. +#include <mutex>
  25. +#include <thread>
  26. +#include <condition_variable>
  27. +#include <atomic>
  28.  
  29.  namespace
  30.  {
  31. +   struct SimulationImpl;
  32. +
  33. +   struct SimulationThreadPool
  34. +   {
  35. +       struct Worker
  36. +       {
  37. +           std::thread thread;
  38. +           std::mutex mutex;
  39. +           std::condition_variable cv;
  40. +           bool hasWork = false;
  41. +           bool stop = false;
  42. +
  43. +           SimulationImpl *sim = nullptr;
  44. +           int start = 0;
  45. +           int end = 0;
  46. +       };
  47. +
  48. +       std::vector<std::unique_ptr<Worker>> workers;
  49. +       std::mutex joinMutex;
  50. +       std::condition_variable joinCv;
  51. +       std::atomic<int> activeWorkers{0};
  52. +
  53. +   public:
  54. +       SimulationThreadPool();
  55. +       ~SimulationThreadPool();
  56. +       void RunParallel(SimulationImpl *sim, int start, int end, int active);
  57. +       int GetThreadCount() const { return workers.size(); }
  58. +   };
  59. +
  60. +   static SimulationThreadPool globalThreadPool;
  61. +
  62.     struct SimulationImpl : public Simulation
  63.     {
  64.         struct Neighbourhood
  65. @@ -39,7 +74,117 @@ namespace
  66.         bool TransitionPhase(int i, const Neighbourhood &neighbourhood);
  67.  
  68.         void UpdateParticles(int start, int end) final override;
  69. +       void UpdateParticlesSerial(int start, int end);
  70. +       int GetThreadCount() const final override;
  71. +       int GetActiveSimulationThreads() const final override;
  72.     };
  73. +
  74. +   SimulationThreadPool::SimulationThreadPool()
  75. +   {
  76. +       int numThreads = std::thread::hardware_concurrency();
  77. +       if (numThreads < 1)
  78. +           numThreads = 1;
  79. +       for (int i = 0; i < numThreads; ++i)
  80. +       {
  81. +           auto w = std::make_unique<Worker>();
  82. +           w->thread = std::thread([this, workerPtr = w.get()]() {
  83. +               while (true)
  84. +               {
  85. +                   std::unique_lock<std::mutex> lock(workerPtr->mutex);
  86. +                   workerPtr->cv.wait(lock, [workerPtr]() { return workerPtr->hasWork || workerPtr->stop; });
  87. +                   if (workerPtr->stop)
  88. +                   {
  89. +                       break;
  90. +                   }
  91. +
  92. +                   // Do work
  93. +                   workerPtr->sim->UpdateParticlesSerial(workerPtr->start, workerPtr->end);
  94. +
  95. +                   workerPtr->hasWork = false;
  96. +                   lock.unlock();
  97. +
  98. +                   if (--activeWorkers == 0)
  99. +                   {
  100. +                       joinCv.notify_one();
  101. +                   }
  102. +               }
  103. +           });
  104. +           workers.push_back(std::move(w));
  105. +       }
  106. +   }
  107. +
  108. +   SimulationThreadPool::~SimulationThreadPool()
  109. +   {
  110. +       for (auto &w : workers)
  111. +       {
  112. +           {
  113. +               std::lock_guard<std::mutex> lock(w->mutex);
  114. +               w->stop = true;
  115. +           }
  116. +           w->cv.notify_one();
  117. +           if (w->thread.joinable())
  118. +           {
  119. +               w->thread.join();
  120. +           }
  121. +       }
  122. +   }
  123. +
  124. +   void SimulationThreadPool::RunParallel(SimulationImpl *sim, int start, int end, int active)
  125. +   {
  126. +       int totalRange = std::min(end, active) - start;
  127. +       if (totalRange <= 0)
  128. +       {
  129. +           return;
  130. +       }
  131. +
  132. +       int numWorkers = workers.size();
  133. +       int chunkSize = (totalRange + numWorkers - 1) / numWorkers;
  134. +       if (chunkSize < 1000)
  135. +       {
  136. +           // Too small, run sequentially on main thread
  137. +           sim->UpdateParticlesSerial(start, end);
  138. +           return;
  139. +       }
  140. +
  141. +       activeWorkers = numWorkers;
  142. +
  143. +       for (int i = 0; i < numWorkers; ++i)
  144. +       {
  145. +           auto &w = workers[i];
  146. +           int wStart = start + i * chunkSize;
  147. +           int wEnd = std::min(wStart + chunkSize, end);
  148. +
  149. +           {
  150. +               std::lock_guard<std::mutex> lock(w->mutex);
  151. +               w->sim = sim;
  152. +               w->start = wStart;
  153. +               w->end = wEnd;
  154. +               w->hasWork = true;
  155. +           }
  156. +           w->cv.notify_one();
  157. +       }
  158. +
  159. +       // Wait for all workers to finish
  160. +       std::unique_lock<std::mutex> lock(joinMutex);
  161. +       joinCv.wait(lock, [this]() { return activeWorkers == 0; });
  162. +   }
  163. +
  164. +   int SimulationImpl::GetThreadCount() const
  165. +   {
  166. +       return globalThreadPool.GetThreadCount();
  167. +   }
  168. +
  169. +   int SimulationImpl::GetActiveSimulationThreads() const
  170. +   {
  171. +       if (ensureDeterminism)
  172. +           return 1;
  173. +       int totalRange = parts.active;
  174. +       int numWorkers = globalThreadPool.GetThreadCount();
  175. +       int chunkSize = (totalRange + numWorkers - 1) / numWorkers;
  176. +       if (chunkSize < 1000)
  177. +           return 1;
  178. +       return numWorkers;
  179. +   }
  180.  }
  181.  
  182.  static float remainder_p(float x, float y)
  183. @@ -1245,6 +1390,7 @@ int Simulation::try_move(int i, int x, int y, int nx, int ny)
  184.             parts[ID(r)].tmp = (int)((parts[ID(r)].temp-73.15f)/100+1);
  185.             if (parts[ID(r)].tmp>=CHANNELS) parts[ID(r)].tmp = CHANNELS-1;
  186.             else if (parts[ID(r)].tmp<0) parts[ID(r)].tmp = 0;
  187. +           std::lock_guard<std::recursive_mutex> lock(simulationMutex);
  188.             for ( nnx=0; nnx<80; nnx++)
  189.                 if (!portalp[parts[ID(r)].tmp][count][nnx].type)
  190.                 {
  191. @@ -1754,6 +1900,7 @@ Simulation::GetNormalResult Simulation::get_normal_interp<false, const Simulatio
  192.  
  193.  void Simulation::kill_part(int i)//kills particle number i
  194.  {
  195. +   std::lock_guard<std::recursive_mutex> lock(simulationMutex);
  196.     if (i < 0 || i >= NPART)
  197.         return;
  198.    
  199. @@ -1797,6 +1944,7 @@ void Parts::Free(int i)
  200.  // Returns true if the particle was killed
  201.  bool Simulation::part_change_type(int i, int x, int y, int t)
  202.  {
  203. +   std::lock_guard<std::recursive_mutex> lock(simulationMutex);
  204.     if (x<0 || y<0 || x>=XRES || y>=YRES || i>=NPART || t<0 || t>=PT_NUM || !parts[i].type)
  205.         return false;
  206.  
  207. @@ -1842,6 +1990,7 @@ bool Simulation::part_change_type(int i, int x, int y, int t)
  208.  //tv = Type (PMAPBITS bits) + Var (32-PMAPBITS bits), var is usually 0
  209.  int Simulation::create_part(int p, int x, int y, int t, int v)
  210.  {
  211. +   std::lock_guard<std::recursive_mutex> lock(simulationMutex);
  212.     int i, oldType = PT_NONE;
  213.  
  214.     auto &sd = SimulationData::CRef();
  215. @@ -2290,6 +2439,18 @@ SimulationImpl::Neighbourhood SimulationImpl::GetNeighbourhood(int i) const
  216.  }
  217.  
  218.  void SimulationImpl::UpdateParticles(int start, int end)
  219. +{
  220. +   if (ensureDeterminism)
  221. +   {
  222. +       UpdateParticlesSerial(start, end);
  223. +   }
  224. +   else
  225. +   {
  226. +       globalThreadPool.RunParallel(this, start, end, parts.active);
  227. +   }
  228. +}
  229. +
  230. +void SimulationImpl::UpdateParticlesSerial(int start, int end)
  231.  {
  232.     //the main particle loop function, goes over all particles.
  233.     auto &sd = SimulationData::CRef();
  234. diff --git a/src/simulation/Simulation.h b/src/simulation/Simulation.h
  235. index b401999c..fcb0e32a 100644
  236. --- a/src/simulation/Simulation.h
  237. +++ b/src/simulation/Simulation.h
  238. @@ -20,6 +20,7 @@
  239.  #include <array>
  240.  #include <memory>
  241.  #include <optional>
  242. +#include <mutex>
  243.  
  244.  constexpr int CHANNELS = int(MAX_TEMP - 73) / 100 + 2;
  245.  
  246. @@ -269,6 +270,8 @@ public:
  247.     virtual ~Simulation();
  248.  
  249.     void EnableNewtonianGravity(bool enable);
  250. +   virtual int GetThreadCount() const { return 1; }
  251. +   virtual int GetActiveSimulationThreads() const { return 1; }
  252.  
  253.     FrameTime *frameTime = nullptr;
  254.  
  255. @@ -280,4 +283,7 @@ private:
  256.     void ResetNewtonianGravity(GravityInput newGravIn, GravityOutput newGravOut);
  257.     void DispatchNewtonianGravity();
  258.     void UpdateGravityMask();
  259. +
  260. +public:
  261. +   std::recursive_mutex simulationMutex;
  262.  };
  263.  
Advertisement
Add Comment
Please, Sign In to add comment