[dali_2.3.27] Merge branch 'devel/master'
[platform/core/uifw/dali-demo.git] / examples / perf-view-creation / perf-view-creation-example.cpp
1 /*
2  * Copyright (c) 2022 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 // EXTERNAL INCLUDES
19 #include <dali-toolkit/dali-toolkit.h>
20 #include <dali-toolkit/devel-api/visuals/color-visual-properties-devel.h>
21 #include <dali-toolkit/devel-api/visuals/visual-properties-devel.h>
22 #include <dali/integration-api/debug.h>
23 #include <dali/integration-api/trace.h>
24
25 #include <chrono>
26 #include <iostream>
27 #include <list>
28 #include <thread>
29
30 // INTERNAL INCLUDES
31 #include "shared/utility.h"
32
33 using namespace Dali;
34 using namespace Dali::Toolkit;
35 using namespace std;
36
37 namespace
38 {
39 enum class ControlTestType
40 {
41   // clang-format off
42   COLOR = 0,            ///< Test with simple color
43   IMAGE,                ///< Test with simple image
44   TEXT,                 ///< Test with simple text label
45   ROUNDED_COLOR,        ///< Test with rounded color
46   BORDER_COLOR,         ///< Test with borderline color
47   ROUNDED_BORDER_COLOR, ///< Test with rounded borderline color
48   BLUR_COLOR,           ///< Test with blur color
49   ROUNDED_BLUR_COLOR,   ///< Test with rounded blur color
50   TYPE_MAX,
51   // clang-format on
52 };
53
54 const char* TestTypeString(ControlTestType type)
55 {
56   // clang-format off
57   switch(type)
58   {
59     case ControlTestType::COLOR:               return "COLOR";
60     case ControlTestType::IMAGE:               return "IMAGE";
61     case ControlTestType::TEXT:                return "TEXT";
62     case ControlTestType::ROUNDED_COLOR:       return "ROUNDED COLOR";
63     case ControlTestType::BORDER_COLOR:        return "BORDER COLOR";
64     case ControlTestType::ROUNDED_BORDER_COLOR:return "ROUNDED BORDER COLOR";
65     case ControlTestType::BLUR_COLOR:          return "BLUR COLOR";
66     case ControlTestType::ROUNDED_BLUR_COLOR:  return "ROUNDED BLUR COLOR";
67     default:                                   return "UNKNOWN";
68   }
69   // clang-format on
70 }
71
72 // NOTE : Due to the image load is bottleneck on target, we just use single small image.
73 const char* IMAGE_PATH[] = {
74   DEMO_IMAGE_DIR "gallery-small-1.jpg",
75 };
76
77 constexpr uint32_t NUM_IMAGES = sizeof(IMAGE_PATH) / sizeof(char*);
78
79 constexpr uint32_t ROWS_COUNT(40);
80 constexpr uint32_t COLUMNS_COUNT(40);
81 constexpr uint32_t TOTAL_COLUMNS_COUNT(80);
82 constexpr uint32_t DURATION_PER_COLUMNS(50); // miliseconds.
83 // Increase animation time cause OnTick time can be delayed.
84 constexpr uint32_t DURATION_OF_ANIMATION(DURATION_PER_COLUMNS*(COLUMNS_COUNT * 4 / 3)); // miliseconds.
85
86 // We should render same type of views in some timing.
87 static_assert(COLUMNS_COUNT * 2 <= TOTAL_COLUMNS_COUNT);
88
89 constexpr float VIEW_MARGIN_RATE = 0.2f;
90
91 // copy from dali-adaptor time-service.cpp
92 void GetNanoseconds(uint64_t& timeInNanoseconds)
93 {
94   // Get the time of a monotonic clock since its epoch.
95   auto epoch = std::chrono::steady_clock::now().time_since_epoch();
96
97   auto duration = std::chrono::duration_cast<std::chrono::nanoseconds>(epoch);
98
99   timeInNanoseconds = static_cast<uint64_t>(duration.count());
100 }
101
102 Control CreateColor()
103 {
104   Control bgView = Control::New(Control::ControlBehaviour::DISABLE_STYLE_CHANGE_SIGNALS);
105   bgView.SetBackgroundColor(Color::YELLOW);
106   return bgView;
107 }
108
109 Control CreateImage(uint32_t& imageCount)
110 {
111   Control bgView = ImageView::New(Control::ControlBehaviour::DISABLE_STYLE_CHANGE_SIGNALS, IMAGE_PATH[imageCount++ % NUM_IMAGES]);
112   return bgView;
113 }
114
115 Control CreateTextLabel()
116 {
117   Control bgView = TextLabel::New(Control::ControlBehaviour::DISABLE_STYLE_CHANGE_SIGNALS, "Hello, World!");
118   return bgView;
119 }
120
121 Control CreateRoundedColor()
122 {
123   Control bgView = Control::New(Control::ControlBehaviour::DISABLE_STYLE_CHANGE_SIGNALS);
124
125   Property::Map map;
126   map[Visual::Property::TYPE]                      = Visual::COLOR;
127   map[ColorVisual::Property::MIX_COLOR]            = Color::YELLOW;
128   map[DevelVisual::Property::CORNER_RADIUS]        = 0.5f;
129   map[DevelVisual::Property::CORNER_RADIUS_POLICY] = Visual::Transform::Policy::RELATIVE;
130
131   bgView[Control::Property::BACKGROUND] = map;
132
133   return bgView;
134 }
135
136 Control CreateBorderColor(const float& requiredBorderlineWidth)
137 {
138   Control bgView = Control::New(Control::ControlBehaviour::DISABLE_STYLE_CHANGE_SIGNALS);
139
140   Property::Map map;
141   map[Visual::Property::TYPE]                  = Visual::COLOR;
142   map[ColorVisual::Property::MIX_COLOR]        = Color::YELLOW;
143   map[DevelVisual::Property::BORDERLINE_WIDTH] = requiredBorderlineWidth;
144   map[DevelVisual::Property::BORDERLINE_COLOR] = Color::RED;
145
146   bgView[Control::Property::BACKGROUND] = map;
147
148   return bgView;
149 }
150
151 Control CreateRoundedBorderColor(const float& requiredBorderlineWidth)
152 {
153   Control bgView = Control::New(Control::ControlBehaviour::DISABLE_STYLE_CHANGE_SIGNALS);
154
155   Property::Map map;
156   map[Visual::Property::TYPE]                      = Visual::COLOR;
157   map[ColorVisual::Property::MIX_COLOR]            = Color::YELLOW;
158   map[DevelVisual::Property::CORNER_RADIUS]        = 0.5f;
159   map[DevelVisual::Property::CORNER_RADIUS_POLICY] = Visual::Transform::Policy::RELATIVE;
160   map[DevelVisual::Property::BORDERLINE_WIDTH]     = requiredBorderlineWidth;
161   map[DevelVisual::Property::BORDERLINE_COLOR]     = Color::RED;
162
163   bgView[Control::Property::BACKGROUND] = map;
164
165   return bgView;
166 }
167
168 Control CreateBlurColor(const float& requiredBlurRadius)
169 {
170   Control bgView = Control::New(Control::ControlBehaviour::DISABLE_STYLE_CHANGE_SIGNALS);
171
172   Property::Map map;
173   map[Visual::Property::TYPE]                  = Visual::COLOR;
174   map[ColorVisual::Property::MIX_COLOR]        = Color::YELLOW;
175   map[DevelColorVisual::Property::BLUR_RADIUS] = requiredBlurRadius;
176
177   bgView[Control::Property::BACKGROUND] = map;
178
179   return bgView;
180 }
181
182 Control CreateRoundedBlurColor(const float& requiredBlurRadius)
183 {
184   Control bgView = Control::New(Control::ControlBehaviour::DISABLE_STYLE_CHANGE_SIGNALS);
185
186   Property::Map map;
187   map[Visual::Property::TYPE]                      = Visual::COLOR;
188   map[ColorVisual::Property::MIX_COLOR]            = Color::YELLOW;
189   map[DevelVisual::Property::CORNER_RADIUS]        = 0.5f;
190   map[DevelVisual::Property::CORNER_RADIUS_POLICY] = Visual::Transform::Policy::RELATIVE;
191   map[DevelColorVisual::Property::BLUR_RADIUS]     = requiredBlurRadius;
192
193   bgView[Control::Property::BACKGROUND] = map;
194
195   return bgView;
196 }
197
198 /**
199  * @brief Statistic container that we can get average / sum / min/ max.
200  *
201  * @tparam T Type of data. T should define add, compare, div-by-int, numeric_limits<T>::min and max
202  */
203 template<typename T>
204 struct Statistic
205 {
206   static constexpr double trimRate = 0.34;
207
208   std::vector<T> v;
209   int            vcnt;
210   T              vsum;
211   T              vmax;
212   T              vmin;
213   Statistic()
214   {
215     Clear();
216   }
217
218   void Clear()
219   {
220     v.clear();
221     vcnt = 0;
222     vsum = 0;
223     vmax = std::numeric_limits<T>::min();
224     vmin = std::numeric_limits<T>::max();
225   }
226
227   void Add(T x)
228   {
229     v.emplace_back(x);
230     vsum += x;
231     vcnt++;
232     vmax = std::max(vmax, x);
233     vmin = std::min(vmin, x);
234   }
235
236   double GetAverage()
237   {
238     if(vcnt == 0) return 0.0;
239     return static_cast<double>(vsum) / vcnt;
240   }
241
242   double GetTrimedAverage()
243   {
244     if(vcnt == 0) return 0.0;
245     std::sort(v.begin(), v.end());
246     T   trimVsum   = 0;
247     int removedCnt = static_cast<int>(vcnt * trimRate * 0.5); // floor
248     int trimVcnt   = vcnt - removedCnt * 2;
249     if(trimVcnt == 0)
250     {
251       trimVcnt += 2;
252       removedCnt--;
253     }
254     for(int i = removedCnt; i < vcnt - removedCnt; i++)
255     {
256       trimVsum += v[i];
257     }
258
259     return static_cast<double>(trimVsum) / trimVcnt;
260   }
261 };
262
263 DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_PERF_VIEW_CREATION_SAMPLE, true);
264
265 } // namespace
266
267 /**
268  * Test application to compare performance between various type of Views creation time & manually created Renderers
269  */
270 class PerfViewCreation : public ConnectionTracker
271 {
272 public:
273   PerfViewCreation(Application& application)
274   : mApplication(application),
275     mRowsCount(ROWS_COUNT),
276     mColumnsCount(COLUMNS_COUNT),
277     mTotalColumnsCount(TOTAL_COLUMNS_COUNT),
278     mDurationPerColumns(DURATION_PER_COLUMNS),
279     mDurationOfAnimation(DURATION_OF_ANIMATION),
280     mTestType(ControlTestType::COLOR)
281   {
282     // Connect to the Application's Init signal
283     mApplication.InitSignal().Connect(this, &PerfViewCreation::Create);
284   }
285
286   ~PerfViewCreation() = default;
287
288   // The Init signal is received once (only) during the Application lifetime
289   void Create(Application& application)
290   {
291     GetNanoseconds(mAppStartTime);
292
293     // Get a handle to the window
294     mWindow = application.GetWindow();
295     mWindow.SetBackgroundColor(Color::WHITE);
296     mWindowSize = mWindow.GetSize();
297
298     mWindow.GetRootLayer().SetProperty(Layer::Property::DEPTH_TEST, false);
299
300     mSize = Vector3(mWindowSize.x / mColumnsCount, mWindowSize.y / mRowsCount, 0.0f);
301
302     Timer timer = Timer::New(mDurationPerColumns);
303     timer.TickSignal().Connect(this, &PerfViewCreation::OnTick);
304     mTimerList.push_back(timer);
305
306     mCreationStatistic.Clear();
307
308     mCreateCount = 0;
309     mDeleteCount = 0;
310     mImageCount  = 0;
311
312     timer.Start();
313
314     // Respond to key events
315     mWindow.KeyEventSignal().Connect(this, &PerfViewCreation::OnKeyEvent);
316   }
317
318   bool OnTick()
319   {
320     CreateColumnView();
321     if(mCreateCount < mColumnsCount)
322     {
323       // Start next phase.
324       Timer timer = Timer::New(mDurationPerColumns);
325       timer.TickSignal().Connect(this, &PerfViewCreation::OnTick);
326       mTimerList.push_back(timer);
327
328       timer.Start();
329     }
330     return false;
331   }
332   void CreateColumnView()
333   {
334     uint64_t startTime;
335     uint64_t endTime;
336
337     GetNanoseconds(startTime);
338     DALI_TRACE_BEGIN(gTraceFilter, "DALI_SAMPLE_PERF_VIEW_CREATION");
339
340     Control columnView = Control::New(Control::ControlBehaviour::DISABLE_STYLE_CHANGE_SIGNALS);
341     columnView.SetBackgroundColor(Color::BLUE);
342     columnView[Actor::Property::PARENT_ORIGIN] = ParentOrigin::TOP_LEFT;
343     columnView[Actor::Property::ANCHOR_POINT]  = AnchorPoint::TOP_LEFT;
344     columnView[Actor::Property::SIZE]          = Vector2(mSize.x, (float)mWindowSize.y);
345     columnView[Actor::Property::POSITION]      = Vector2(mSize.x * (mCreateCount % mColumnsCount), -(float)mWindowSize.y);
346     for(uint32_t i = 0; i < mRowsCount; ++i)
347     {
348       Control bgView;
349       switch(mTestType)
350       {
351         case ControlTestType::COLOR:
352         default:
353         {
354           bgView = CreateColor();
355           break;
356         }
357         case ControlTestType::IMAGE:
358         {
359           bgView = CreateImage(mImageCount);
360           break;
361         }
362         case ControlTestType::TEXT:
363         {
364           bgView = CreateTextLabel();
365           break;
366         }
367         case ControlTestType::ROUNDED_COLOR:
368         {
369           bgView = CreateRoundedColor();
370           break;
371         }
372         case ControlTestType::BORDER_COLOR:
373         {
374           bgView = CreateBorderColor(std::min(mSize.x, mSize.y) * VIEW_MARGIN_RATE);
375           break;
376         }
377         case ControlTestType::ROUNDED_BORDER_COLOR:
378         {
379           bgView = CreateRoundedBorderColor(std::min(mSize.x, mSize.y) * VIEW_MARGIN_RATE);
380           break;
381         }
382         case ControlTestType::BLUR_COLOR:
383         {
384           bgView = CreateBlurColor(std::min(mSize.x, mSize.y) * VIEW_MARGIN_RATE * 0.5f);
385           break;
386         }
387         case ControlTestType::ROUNDED_BLUR_COLOR:
388         {
389           bgView = CreateRoundedBlurColor(std::min(mSize.x, mSize.y) * VIEW_MARGIN_RATE * 0.5f);
390           break;
391         }
392       }
393
394       bgView[Actor::Property::PARENT_ORIGIN] = ParentOrigin::TOP_LEFT;
395       bgView[Actor::Property::ANCHOR_POINT]  = AnchorPoint::TOP_LEFT;
396       bgView[Actor::Property::SIZE]          = Vector2(mSize.x * (1.0f - VIEW_MARGIN_RATE), mSize.y * (1.0f - VIEW_MARGIN_RATE));
397       bgView[Actor::Property::POSITION]      = Vector2(mSize.x * VIEW_MARGIN_RATE * 0.5f, mSize.y * VIEW_MARGIN_RATE * 0.5f + mSize.y * i);
398       columnView.Add(bgView);
399     }
400
401     mWindow.GetRootLayer().Add(columnView);
402     mCreatingControlList.push_back(columnView);
403
404     // Add appearing animation
405     Animation appearingAnimation = Animation::New(mDurationOfAnimation * 0.001f);
406     appearingAnimation.AnimateTo(Property(columnView, Actor::Property::POSITION_Y), 0.0f);
407     appearingAnimation.FinishedSignal().Connect(this, &PerfViewCreation::OnAppearAnimationFinished);
408     appearingAnimation.Play();
409
410     mCreatingAnimationList.push_back(appearingAnimation);
411
412     GetNanoseconds(endTime);
413
414     DALI_TRACE_END(gTraceFilter, "DALI_SAMPLE_PERF_VIEW_CREATION");
415
416     // Append duration of creation time.
417     mCreationStatistic.Add((endTime - startTime) / 1000000.0);
418
419     mCreateCount++;
420
421     if(mCreateCount % mTotalColumnsCount == 0)
422     {
423       DALI_LOG_ERROR("Average of creation %d DALI(%s) : %.6lf ms\n", mRowsCount, TestTypeString(mTestType), mCreationStatistic.GetTrimedAverage());
424       mCreationStatistic.Clear();
425       mTestType = static_cast<ControlTestType>((static_cast<int>(mTestType) + 1) % static_cast<int>(ControlTestType::TYPE_MAX));
426     }
427   }
428
429   bool OnTouch(Actor actor, const TouchEvent& touch)
430   {
431     // quit the application
432     mApplication.Quit();
433     return true;
434   }
435
436   void OnKeyEvent(const KeyEvent& event)
437   {
438     if(event.GetState() == KeyEvent::DOWN)
439     {
440       if(IsKey(event, Dali::DALI_KEY_ESCAPE) || IsKey(event, Dali::DALI_KEY_BACK))
441       {
442         mApplication.Quit();
443       }
444     }
445   }
446
447   void OnAppearAnimationFinished(Animation& animation)
448   {
449     // We can assume that front of mControlList must be disappearing.
450     auto currentControl = mCreatingControlList.front();
451     mCreatingControlList.pop_front();
452
453     // Dereference timer safety
454     if(!mTimerList.empty())
455     {
456       mTimerList.pop_front();
457     }
458
459     // Dereference animation safety
460     mCreatingAnimationList.pop_back();
461
462     mRemovingControlList.push_back(currentControl);
463
464     if(mCreateCount < mTotalColumnsCount * (static_cast<int>(ControlTestType::TYPE_MAX)))
465     {
466       CreateColumnView();
467     }
468
469     // Add disappearing animation
470     Animation disappearingAnimation = Animation::New(mDurationOfAnimation * 0.001f);
471     disappearingAnimation.AnimateTo(Property(currentControl, Actor::Property::POSITION_Y), (float)mWindowSize.y);
472     disappearingAnimation.FinishedSignal().Connect(this, &PerfViewCreation::OnDisappearAnimationFinished);
473     disappearingAnimation.Play();
474
475     mRemovingAnimationList.push_back(disappearingAnimation);
476   }
477   void OnDisappearAnimationFinished(Animation& animation)
478   {
479     // We can assume that front of mControlList must be deleted.
480     mRemovingControlList.front().Unparent();
481     mRemovingControlList.pop_front();
482
483     // Dereference animation safety
484     mRemovingAnimationList.pop_back();
485
486     mDeleteCount++;
487
488     // If all controls are deleted, quit this application. byebye~
489     if(mDeleteCount == mTotalColumnsCount * (static_cast<int>(ControlTestType::TYPE_MAX)))
490     {
491       GetNanoseconds(mAppEndTime);
492
493       DALI_LOG_ERROR("Duration of all app running time : %.6lf ms\n", (mAppEndTime - mAppStartTime) / 1000000.0);
494       mApplication.Quit();
495     }
496   }
497
498 private:
499   Application& mApplication;
500   Window       mWindow;
501   Vector2      mWindowSize;
502
503   std::list<Control>   mCreatingControlList;
504   std::list<Control>   mRemovingControlList;
505   std::list<Animation> mCreatingAnimationList;
506   std::list<Animation> mRemovingAnimationList;
507   std::list<Timer>     mTimerList;
508
509   Vector3 mSize;
510
511   const uint32_t mRowsCount;
512   const uint32_t mColumnsCount;
513   const uint32_t mTotalColumnsCount;
514
515   const uint32_t mDurationPerColumns;
516   const uint32_t mDurationOfAnimation;
517
518   ControlTestType mTestType;
519
520   uint32_t mCreateCount = 0;
521   uint32_t mDeleteCount = 0;
522   uint32_t mImageCount  = 0;
523
524   uint64_t mAppStartTime = 0;
525   uint64_t mAppEndTime   = 0;
526
527   Statistic<double> mCreationStatistic;
528 };
529
530 int DALI_EXPORT_API main(int argc, char** argv)
531 {
532   Application application = Application::New(&argc, &argv);
533
534   PerfViewCreation test(application);
535   application.MainLoop();
536
537   return 0;
538 }