[dali_2.3.21] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / particle-system / particle-emitter-impl.cpp
1 /*
2  * Copyright (c) 2023 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali-toolkit/internal/particle-system/particle-emitter-impl.h>
20
21 // INTERNAL INCLUDES
22 #include <dali-toolkit/internal/particle-system/particle-list-impl.h>
23 #include <dali-toolkit/internal/particle-system/particle-modifier-impl.h>
24 #include <dali-toolkit/internal/particle-system/particle-renderer-impl.h>
25 #include <dali-toolkit/internal/particle-system/particle-source-impl.h>
26
27 // EXTERNAL INCLUDES
28 #include <dali/devel-api/common/stage-devel.h>
29 #include <dali/devel-api/update/frame-callback-interface.h>
30 #include <memory>
31 #include <utility>
32
33 namespace Dali::Toolkit::ParticleSystem::Internal
34 {
35 constexpr uint32_t DEFAULT_PARTICLE_COUNT = 100u; ///< Default number of particles in system if not set by user
36
37 /**
38  * Particle system frame callback to run modifiers and sources
39  */
40 class FrameCallback : public Dali::FrameCallbackInterface
41 {
42 public:
43   /**
44    * @brief Constructor.
45    */
46   FrameCallback(Internal::ParticleEmitter* emitter)
47   : mEmitter(emitter)
48   {
49   }
50
51   ~FrameCallback() = default;
52
53 private:
54   bool Update(Dali::UpdateProxy& updateProxy, float elapsedSeconds) override
55   {
56     mEmitter->Update();
57     return true;
58   }
59
60   Internal::ParticleEmitter* mEmitter;
61 };
62
63 ParticleSystem::ParticleSource ParticleEmitter::GetSource() const
64 {
65   return mParticleSource;
66 }
67
68 void ParticleEmitter::SetSource(const ParticleSystem::ParticleSource& source)
69 {
70   mParticleStatusBits |= SOURCE_SET_STATUS_BIT;
71   mParticleSource = source;
72
73   // call the init function of source
74   GetImplementation(mParticleSource).GetUpdater().Init();
75 }
76
77 void ParticleEmitter::SetDomain(const ParticleSystem::ParticleDomain& domain)
78 {
79   mParticleStatusBits |= DOMAIN_SET_STATUS_BIT;
80   mParticleDomain = domain;
81 }
82
83 void ParticleEmitter::SetRenderer(const ParticleSystem::ParticleRenderer& renderer)
84 {
85   mParticleStatusBits |= RENDERER_SET_STATUS_BIT;
86   mParticleRenderer = renderer;
87   GetImplementation(mParticleRenderer).SetEmitter(this);
88 }
89
90 void ParticleEmitter::SetParticleCount(uint32_t maxParticleCount)
91 {
92   if(!mParticleList || maxParticleCount != GetImplementation(mParticleList).GetParticleCount())
93   {
94     // Default particle list has no data streams, it will replace old list
95     mParticleList = ParticleSystem::ParticleList::New(maxParticleCount,
96                                                       ParticleStream::POSITION_STREAM_BIT |
97                                                         ParticleStream::COLOR_STREAM_BIT |
98                                                         ParticleStream::VELOCITY_STREAM_BIT |
99                                                         ParticleStream::SCALE_STREAM_BIT |
100                                                         ParticleStream::LIFETIME_STREAM_BIT);
101   }
102 }
103
104 uint32_t ParticleEmitter::GetParticleCount()
105 {
106   return GetImplementation(mParticleList).GetParticleCount();
107 }
108
109 ParticleSystem::ParticleList& ParticleEmitter::GetParticleList()
110 {
111   return mParticleList;
112 }
113
114 uint32_t ParticleEmitter::AddModifier(const ParticleSystem::ParticleModifier& modifier)
115 {
116   mModifiers.emplace_back(modifier);
117   return mModifiers.size() - 1;
118 }
119
120 ParticleSystem::ParticleDomain ParticleEmitter::GetDomain() const
121 {
122   return mParticleDomain;
123 }
124
125 ParticleSystem::ParticleRenderer ParticleEmitter::GetRenderer() const
126 {
127   return mParticleRenderer;
128 }
129
130 ParticleSystem::ParticleModifier ParticleEmitter::GetModifierAt(uint32_t index)
131 {
132   return index < mModifiers.size() ? mModifiers[index] : ParticleSystem::ParticleModifier();
133 }
134
135 void ParticleEmitter::RemoveModifierAt(uint32_t index)
136 {
137   mModifiers.erase(mModifiers.begin() + index);
138 }
139
140 void ParticleEmitter::Start()
141 {
142   if(mActor && IsComplete() && !(mParticleStatusBits & SIMULATION_STARTED_STATUS_BIT))
143   {
144     if(mFrameCallback)
145     {
146       Stop();
147     }
148
149     GetImplementation(mParticleRenderer).Initialize();
150
151     mSystemStarted = true;
152     mParticleStatusBits &= ~(SIMULATION_STOPPED_STATUS_BIT | SIMULATION_PAUSED_STATUS_BIT);
153     mParticleStatusBits |= SIMULATION_STARTED_STATUS_BIT;
154     mFrameCallback = std::make_unique<FrameCallback>(this);
155
156     // Attach renderer to an actor
157     auto renderer = GetImplementation(mParticleRenderer).GetRenderer();
158     mActor.AddRenderer(renderer);
159     DevelStage::AddFrameCallback(Stage::GetCurrent(), *mFrameCallback, mActor);
160   }
161 }
162
163 void ParticleEmitter::Stop()
164 {
165   if(mActor && IsComplete() && (mParticleStatusBits & SIMULATION_STARTED_STATUS_BIT))
166   {
167     mSystemStarted = false;
168     mParticleStatusBits &= ~(SIMULATION_STARTED_STATUS_BIT | SIMULATION_PAUSED_STATUS_BIT);
169     mParticleStatusBits |= SIMULATION_STOPPED_STATUS_BIT;
170     auto renderer = GetImplementation(mParticleRenderer).GetRenderer();
171     mActor.RemoveRenderer(renderer);
172     DevelStage::RemoveFrameCallback(Stage::GetCurrent(), *mFrameCallback);
173   }
174 }
175
176 std::chrono::milliseconds ParticleEmitter::GetCurrentTimeMillis() const
177 {
178   std::chrono::milliseconds ms = std::chrono::duration_cast<std::chrono::milliseconds>(
179     std::chrono::system_clock::now().time_since_epoch());
180   return ms;
181 }
182
183 void ParticleEmitter::Update()
184 {
185   // Do not update if emitter setup isn't complete
186   if(!IsComplete())
187   {
188     return;
189   }
190
191   auto ms = GetCurrentTimeMillis();
192
193   if(mCurrentMilliseconds.count() == 0)
194   {
195     mCurrentMilliseconds = ms;
196   }
197
198   if(mLastUpdateMs.count() == 0)
199   {
200     mLastUpdateMs = ms;
201   }
202
203   float emissionDelta = 1.0f / float(mEmissionRatePerSecond); // time per one particle emission (TODO: add some randomness to it)
204
205   auto diffTime = double((ms - mCurrentMilliseconds).count()) / 1000.0;
206
207   uint32_t emissionCount = 0u;
208   if(diffTime >= emissionDelta)
209   {
210     emissionCount        = round(diffTime / emissionDelta);
211     mCurrentMilliseconds = ms;
212   }
213
214   // Update lifetimes and discard dead particles
215   auto& particles = mParticleList.GetActiveParticles();
216   auto  dt        = ms - mLastUpdateMs;
217   if(dt.count())
218   {
219     std::vector<int> toErase;
220     int              n = 0;
221     for(auto& p : particles)
222     {
223       auto& lifetime = p.Get<float>(ParticleStream::LIFETIME_STREAM_BIT);
224       lifetime -= (float(dt.count()) / 1000.0f);
225       if(lifetime <= 0.0f)
226       {
227         toErase.emplace_back(n);
228       }
229       ++n;
230     }
231
232     if(!toErase.empty())
233     {
234       int indexShift = 0;
235       for(auto& v : toErase)
236       {
237         GetImplementation(mParticleList).ReleaseParticle(v - indexShift);
238         ++indexShift;
239       }
240     }
241   }
242   mLastUpdateMs = ms;
243
244   // apply initial emission count
245   if(mSystemStarted)
246   {
247     emissionCount  = mEmissionCountOnStart;
248     mSystemStarted = false;
249   }
250
251   // Update source if there are any particles to be emitted
252   if(emissionCount)
253   {
254     // Apply active particles limiter
255     if(mActiveParticlesLimit && mParticleList.GetActiveParticleCount() + emissionCount > mActiveParticlesLimit)
256     {
257       emissionCount = mActiveParticlesLimit - mParticleList.GetActiveParticleCount();
258     }
259     UpdateSource(emissionCount);
260   }
261
262   // Update modifier stack
263   for(auto& modifier : mModifiers)
264   {
265     if(modifier)
266     {
267       // Parallel processing must be enabled in order to use MT mode
268       bool mt = GetImplementation(modifier).GetUpdater().IsMultiThreaded() && mParallelProcessing;
269
270       if(!mt) // single-threaded, update all particles in one go
271       {
272         GetImplementation(modifier).Update(mParticleList, 0, mParticleList.GetActiveParticleCount());
273       }
274       else
275       {
276         UpdateModifierMT(modifier);
277       }
278     }
279   }
280
281   UpdateDomain();
282 }
283
284 void ParticleEmitter::AttachTo(Actor actor)
285 {
286   mActor = std::move(actor);
287 }
288
289 Actor ParticleEmitter::GetActor() const
290 {
291   return mActor;
292 }
293
294 void ParticleEmitter::UpdateSource(uint32_t count)
295 {
296   GetImplementation(mParticleSource).Update(mParticleList, count);
297 }
298
299 void ParticleEmitter::UpdateModifierMT(Dali::Toolkit::ParticleSystem::ParticleModifier& modifier)
300 {
301   auto& threadPool    = GetThreadPool();
302   auto  workerThreads = threadPool.GetWorkerCount();
303   auto  activeCount   = mParticleList.GetActiveParticleCount();
304
305   // at least 10 particles per worker thread (should be parametrized)
306   // If less, continue ST
307   if(activeCount < workerThreads * 10)
308   {
309     GetImplementation(modifier).Update(mParticleList, 0, activeCount);
310     return;
311   }
312
313   auto partial = mParticleList.GetActiveParticleCount() / workerThreads;
314
315   // make tasks
316   struct UpdateTask
317   {
318     UpdateTask(Internal::ParticleModifier& modifier, ParticleSystem::ParticleList& list, uint32_t first, uint32_t count)
319     : mModifier(modifier),
320       mList(list),
321       mFirst(first),
322       mCount(count)
323     {
324     }
325
326     Internal::ParticleModifier&   mModifier;
327     ParticleSystem::ParticleList& mList;
328     uint32_t                      mFirst;
329     uint32_t                      mCount;
330
331     void Update()
332     {
333       mModifier.Update(mList, mFirst, mCount);
334     }
335   };
336
337   std::vector<UpdateTask> updateTasks;
338   updateTasks.reserve(workerThreads);
339   std::vector<Task> tasks;
340
341   for(auto i = 0u; i < workerThreads; ++i)
342   {
343     auto index = i * partial;
344     auto count = partial;
345     if(i == workerThreads - 1 && index + count < activeCount)
346     {
347       count = activeCount - index;
348     }
349
350     updateTasks.emplace_back(GetImplementation(modifier), mParticleList, index, count);
351     tasks.emplace_back([&task = updateTasks.back()](uint32_t n) { task.Update(); });
352   }
353
354   auto future = threadPool.SubmitTasks(tasks, 0);
355   future->Wait();
356 }
357
358 void ParticleEmitter::UpdateDomain()
359 {
360   // TODO
361 }
362
363 void ParticleEmitter::SetEmissionRate(uint32_t ratePerSecond)
364 {
365   mEmissionRatePerSecond = ratePerSecond;
366 }
367
368 uint32_t ParticleEmitter::GetEmissionRate() const
369 {
370   return mEmissionRatePerSecond;
371 }
372
373 void ParticleEmitter::EnableParallelProcessing(bool enabled)
374 {
375   mParallelProcessing = enabled;
376 }
377
378 bool ParticleEmitter::IsParallelProcessingEnabled() const
379 {
380   return mParallelProcessing;
381 }
382
383 void ParticleEmitter::SetInitialParticleCount(uint32_t count)
384 {
385   mEmissionCountOnStart = count;
386 }
387
388 uint32_t ParticleEmitter::GetInitialParticleCount() const
389 {
390   return mEmissionCountOnStart;
391 }
392
393 void ParticleEmitter::SetActiveParticlesLimit(uint32_t count)
394 {
395   mActiveParticlesLimit = count;
396 }
397
398 uint32_t ParticleEmitter::GetActiveParticlesLimit() const
399 {
400   return mActiveParticlesLimit;
401 }
402
403 ParticleSystem::ParticleEmitter::Status ParticleEmitter::GetStatus() const
404 {
405   auto statusMask = SIMULATION_STARTED_STATUS_BIT | SIMULATION_PAUSED_STATUS_BIT | SIMULATION_STOPPED_STATUS_BIT;
406   auto status     = (mParticleStatusBits & statusMask);
407
408   if(status & SIMULATION_PAUSED_STATUS_BIT)
409   {
410     return ParticleSystem::ParticleEmitter::Status::PAUSED;
411   }
412   else if(status & SIMULATION_STOPPED_STATUS_BIT)
413   {
414     return ParticleSystem::ParticleEmitter::Status::STOPPED;
415   }
416   else if(status & SIMULATION_STARTED_STATUS_BIT)
417   {
418     return ParticleSystem::ParticleEmitter::Status::STARTED;
419   }
420   else
421   {
422     return !IsComplete() ? ParticleSystem::ParticleEmitter::Status::INCOMPLETE : ParticleSystem::ParticleEmitter::Status::READY;
423   }
424 }
425
426 ParticleEmitter::ParticleEmitter()
427 {
428   // Necessary to be called to initialize internal ParticleList
429   SetParticleCount(DEFAULT_PARTICLE_COUNT);
430 }
431
432 ParticleEmitter::~ParticleEmitter()
433 {
434   if(mParticleRenderer)
435   {
436     GetImplementation(mParticleRenderer).PrepareToDie();
437   }
438 }
439
440 } // namespace Dali::Toolkit::ParticleSystem::Internal
441 namespace Dali::Toolkit::ParticleSystem
442 {
443 Dali::ThreadPool& GetThreadPool()
444 {
445   static std::unique_ptr<Dali::ThreadPool> gThreadPool{nullptr};
446   static std::once_flag                    onceFlag;
447
448   // Intialize thread pool if not there yet, make sure it happens once and it's synchronized!,
449   // NOTE: this function shouldn't be called from multiple thread anyway
450   if(!gThreadPool)
451   {
452     std::call_once(onceFlag, [&threadPool = gThreadPool] { threadPool = std::make_unique<Dali::ThreadPool>();
453                      threadPool->Initialize(4u); });
454   }
455
456   return *gThreadPool;
457 }
458 } // namespace Dali::Toolkit::ParticleSystem