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