[Tizen] Print top5 longest time spend animation finished callbacks + Minor code clean
[platform/core/uifw/dali-core.git] / dali / internal / event / animation / animation-playlist.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/internal/event/animation/animation-playlist.h>
20
21 // INTERNAL INCLUDES
22 #include <dali/integration-api/debug.h>
23 #include <dali/integration-api/trace.h>
24 #include <dali/internal/event/animation/animation-impl.h>
25 #include <dali/internal/update/animation/scene-graph-animation.h>
26 #include <dali/public-api/common/vector-wrapper.h>
27
28 #ifdef TRACE_ENABLED
29 #include <chrono>
30 #include <thread>
31 #include <cmath>
32 #endif
33
34 namespace
35 {
36 DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_PERFORMANCE_MARKER, false);
37
38 #ifdef TRACE_ENABLED
39 uint64_t GetNanoseconds()
40 {
41   // Get the time of a monotonic clock since its epoch.
42   auto epoch = std::chrono::steady_clock::now().time_since_epoch();
43
44   auto duration = std::chrono::duration_cast<std::chrono::nanoseconds>(epoch);
45
46   return static_cast<uint64_t>(duration.count());
47 }
48 #endif
49 }
50
51 namespace Dali
52 {
53 namespace Internal
54 {
55 AnimationPlaylist* AnimationPlaylist::New()
56 {
57   return new AnimationPlaylist();
58 }
59
60 AnimationPlaylist::AnimationPlaylist() = default;
61
62 AnimationPlaylist::~AnimationPlaylist() = default;
63
64 void AnimationPlaylist::AnimationCreated(Animation& animation)
65 {
66   mAnimations.PushBack(&animation);
67 }
68
69 void AnimationPlaylist::AnimationDestroyed(Animation& animation)
70 {
71   auto iter = mAnimations.Find(&animation);
72   DALI_ASSERT_ALWAYS(iter != mAnimations.End() && "Animation not found");
73
74   mAnimations.Erase(iter);
75 }
76
77 void AnimationPlaylist::OnPlay(Animation& animation)
78 {
79   Dali::Animation handle = Dali::Animation(&animation);
80   auto            iter   = mPlaylist.lower_bound(handle);
81   if(iter != mPlaylist.end() && (*iter).first == handle)
82   {
83     // Just increase reference count.
84     ++(iter->second);
85   }
86   else
87   {
88     mPlaylist.insert(iter, {handle, 1u});
89   }
90 }
91
92 void AnimationPlaylist::OnClear(Animation& animation)
93 {
94   Dali::Animation handle = Dali::Animation(&animation);
95   auto            iter   = mPlaylist.find(handle);
96
97   // Animation might be removed when NotifyCompleted called.
98   if(DALI_LIKELY(iter != mPlaylist.end()))
99   {
100     // Just decrease reference count. But if reference count is zero, remove it.
101     if(--(iter->second) == 0u)
102     {
103       mPlaylist.erase(iter);
104     }
105   }
106 }
107
108 void AnimationPlaylist::NotifyProgressReached(NotifierInterface::NotifyId notifyId)
109 {
110   Dali::Animation handle; // Will own handle until all emits have been done.
111
112   auto* animation = GetEventObject(notifyId);
113   if(DALI_LIKELY(animation))
114   {
115     // Check if this animation hold inputed scenegraph animation.
116     DALI_ASSERT_DEBUG(animation->GetSceneObject()->GetNotifyId() == notifyId);
117
118     handle = Dali::Animation(animation);
119     animation->EmitSignalProgressReached();
120   }
121 }
122
123 void AnimationPlaylist::NotifyCompleted(CompleteNotificationInterface::ParameterList notifierIdList)
124 {
125   std::vector<Dali::Animation> finishedAnimations; // Will own handle until all emits have been done.
126
127 #ifdef TRACE_ENABLED
128   std::vector<std::pair<uint64_t, uint32_t>> animationFinishedTimeChecker;
129
130   uint64_t start = 0u;
131   uint64_t end = 0u;
132 #endif
133
134   DALI_TRACE_BEGIN_WITH_MESSAGE_GENERATOR(gTraceFilter, "DALI_ANIMATION_FINISHED", [&](std::ostringstream& oss) {
135     oss << "[n:" << notifierIdList.Count() << "]";
136   });
137
138   for(const auto& notifierId : notifierIdList)
139   {
140     auto* animation = GetEventObject(notifierId);
141     if(DALI_LIKELY(animation))
142     {
143       // Check if this animation hold inputed scenegraph animation.
144       DALI_ASSERT_DEBUG(animation->GetSceneObject()->GetNotifyId() == notifierId);
145
146       // Update loop count. And check whether animation was finished or not.
147       if(animation->HasFinished())
148       {
149         finishedAnimations.push_back(Dali::Animation(animation));
150
151         // The animation may be present in mPlaylist - remove if necessary
152         // Note that the animation "Finish" signal is emitted after Stop() has been called
153         OnClear(*animation);
154       }
155     }
156   }
157
158   // Now it's safe to emit the signals
159   for(auto& animation : finishedAnimations)
160   {
161 #ifdef TRACE_ENABLED
162     if(gTraceFilter && gTraceFilter->IsTraceEnabled())
163     {
164       start = GetNanoseconds();
165     }
166 #endif
167     GetImplementation(animation).EmitSignalFinish();
168 #ifdef TRACE_ENABLED
169     if(gTraceFilter && gTraceFilter->IsTraceEnabled())
170     {
171       end = GetNanoseconds();
172       animationFinishedTimeChecker.emplace_back(end - start, GetImplementation(animation).GetSceneObject()->GetNotifyId());
173     }
174 #endif
175   }
176
177   DALI_TRACE_END_WITH_MESSAGE_GENERATOR(gTraceFilter, "DALI_ANIMATION_FINISHED", [&](std::ostringstream& oss) {
178     oss << "[f:" << finishedAnimations.size() << ",";
179
180     std::sort(animationFinishedTimeChecker.rbegin(), animationFinishedTimeChecker.rend());
181     auto topCount = std::min(5u, static_cast<uint32_t>(animationFinishedTimeChecker.size()));
182
183     oss << "top" << topCount;
184     for(auto i = 0u; i < topCount; ++i)
185     {
186       oss << "(" << static_cast<float>(animationFinishedTimeChecker[i].first) / 1000000.0f << "ms,";
187       oss << animationFinishedTimeChecker[i].second << ")";
188     }
189     oss << "]";
190   });
191 }
192
193 uint32_t AnimationPlaylist::GetAnimationCount()
194 {
195   return mAnimations.Count();
196 }
197
198 Dali::Animation AnimationPlaylist::GetAnimationAt(uint32_t index)
199 {
200   if(index >= mAnimations.Count())
201   {
202     DALI_LOG_ERROR("Animation index is out of bounds.\n");
203     return Dali::Animation();
204   }
205
206   // This will spend a lot of time. But GetAnimationAt API will be called very rarely.
207   Animation* ret = nullptr;
208   for(auto iter : mAnimations)
209   {
210     if(index == 0u)
211     {
212       ret = iter;
213       break;
214     }
215     --index;
216   }
217   DALI_ASSERT_DEBUG(ret != nullptr);
218   return Dali::Animation(ret);
219 }
220
221 } // namespace Internal
222
223 } // namespace Dali