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