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