Merge "Revert public API changes. BATCH property." into devel/master
[platform/core/uifw/dali-demo.git] / examples / homescreen-benchmark / homescreen-benchmark.cpp
1 /*
2  * Copyright (c) 2016 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/devel-api/actors/actor-devel.h>
21 #include <sstream>
22 #include <iostream>
23
24 using namespace Dali;
25 using Dali::Toolkit::TextLabel;
26
27 namespace
28 {
29 enum IconType
30 {
31   IMAGEVIEW,
32   CHECKBOX
33 };
34
35 const char* IMAGE_PATH_PREFIX               ( DEMO_IMAGE_DIR "application-icon-" );
36 const char* IMAGE_PATH_POSTFIX              ( ".png" );
37 const int   TOTAL_ICON_DEFINITIONS          ( 147 );
38
39 const char* BACKGROUND_IMAGE                ( DEMO_IMAGE_DIR "background-3.jpg" );
40 const float PAGE_SCALE_FACTOR_X             ( 0.95f );
41 const float PAGE_SCALE_FACTOR_Y             ( 0.95f );
42 const float PAGE_DURATION_SCALE_FACTOR      ( 10.0f ); ///< Time-scale factor, larger = animation is slower
43
44 const float DEFAULT_OPT_ROW_COUNT           ( 5 );
45 const float DEFAULT_OPT_COL_COUNT           ( 4 );
46 const float DEFAULT_OPT_PAGE_COUNT          ( 10 );
47 const bool  DEFAULT_OPT_USE_TABLEVIEW       ( true );
48 const bool  DEFAULT_OPT_BATCHING_ENABLED    ( true );
49 const bool  DEFAULT_OPT_ICON_LABELS         ( true );
50 const IconType  DEFAULT_OPT_ICON_TYPE       ( IMAGEVIEW );
51
52 // The image/label area tries to make sure the positioning will be relative to previous sibling
53 const float IMAGE_AREA                      ( 0.60f );
54 const float LABEL_AREA                      ( 0.50f );
55
56
57
58 /**
59  * Random words used as unique application names.
60  * The number matches the value of TOTAL_ICON_DEFINITIONS.
61  */
62 const char* DEMO_APPS_NAMES[] =
63 {
64   "Achdyer",   "Aughm",       "Cerl",       "Daril",      "Emgha",     "Ghatan",     "Issum",     "Lertan",    "Mosorrad",
65   "Achtortor", "Aughtheryer", "Certin",     "Darpban",    "Emiton",    "Gibanis",    "Itenthbel", "Liadem",    "Mosraye",
66   "Ackirlor",  "Awitad",      "Checerper",  "Dasrad",     "Emworeng",  "Hatdyn",     "K'ackves",  "Liathar",   "Mosth",
67   "Ackptin",   "Banengon",    "Chegit",     "Deeqskel",   "Endnys",    "Heesban",    "Kagdra",    "Liephden",  "Neabar",
68   "Aighte",    "Banhinat",    "Cheirat",    "Delurnther", "Enessray",  "Hesub",      "Kalbankim", "Likellor",  "Neerdem",
69   "Akala",     "Belrisash",   "Che'rak",    "Denalda",    "Engyer",    "Hinkelenth", "Kal'enda",  "Loightmos", "Nichqua",
70   "Alealdny",  "Bilorm",      "Cheves",     "Derynkel",   "En'rady",   "Hirryer",    "Kimest",    "Loromum",   "Nudraough",
71   "Angash",    "Bleustcer",   "Chiperath",  "Deurnos",    "Enthount",  "Ideinta",    "Kimundeng", "Lorr",      "Nuyim",
72   "Anglor",    "Bliagelor",   "Chralerack", "Doyaryke",   "Enundem",   "Im'eld",     "Koachlor",  "Lortas",    "Nycha",
73   "Anveraugh", "Blorynton",   "Chram",      "Draithon",   "Essina",    "Ina'ir",     "Kuren",     "Lyerr",     "Nyia",
74   "Ardangas",  "Booten",      "Clyimen",    "Drantess",   "Faughald",  "Ing'moro",   "Kygver",    "Maustbur",  "Nyjac",
75   "Ardug",     "Bripolqua",   "Coqueang",   "Druardny",   "Fiummos",   "Ingormess",  "Kyning",    "Menvor",    "Nystondar",
76   "Ardworu",   "Bryray",      "Craennther", "Dynsaytor",  "Garash",    "Ingshy",     "Laiyach",   "Meusten",   "Okine",
77   "Ascerald",  "Burust",      "Cykage",     "Dytinris",   "Garight",   "Issath",     "Lasuzu",    "Mirodskel", "Oldit",
78   "Ash'ach",   "Cataikel",    "Dalek",      "Eeni",       "Garrynath", "Issendris",  "Lekew",     "Morhatrod", "Om'mose",
79   "Athiund",   "Cerilwar",    "Darhkel",    "Elmryn",     "Ghalora",   "Issey",      "Lerengom",  "Moserbel",  "Onye",
80   "Ososrak",   "Pecertin",    "Perrd"
81 };
82
83 // This code comes from command-line-options.cpp. the reason it's here is to
84 // keep consistent the extra-help formatting when '--help' used.
85 void PrintHelp( const char * const opt, const char * const optDescription)
86 {
87   const std::ios_base::fmtflags flags = std::cout.flags();
88   std::cout << std::left << "  -";
89   std::cout.width( 18 );
90   std::cout << opt;
91   std::cout << optDescription;
92   std::cout << std::endl;
93   std::cout.flags( flags );
94 }
95
96 }
97
98 /**
99  * @brief This example is a benchmark that mimics the paged applications list of the homescreen application.
100  */
101 class HomescreenBenchmark : public ConnectionTracker
102 {
103 public:
104
105   // Config structure passed to the constructor. It makes easier to increase number
106   // of setup parameters if needed.
107   struct Config
108   {
109     Config() :
110       mRows( DEFAULT_OPT_ROW_COUNT ),
111       mCols( DEFAULT_OPT_COL_COUNT ),
112       mPageCount( DEFAULT_OPT_PAGE_COUNT ),
113       mTableViewEnabled( DEFAULT_OPT_USE_TABLEVIEW ),
114       mBatchingEnabled( DEFAULT_OPT_BATCHING_ENABLED ),
115       mIconLabelsEnabled( DEFAULT_OPT_ICON_LABELS ),
116       mIconType( DEFAULT_OPT_ICON_TYPE )
117     {
118     }
119
120     int  mRows;
121     int  mCols;
122     int  mPageCount;
123     bool mTableViewEnabled;
124     bool mBatchingEnabled;
125     bool mIconLabelsEnabled;
126     IconType mIconType;
127   };
128
129   // animation script data
130   struct ScriptData
131   {
132     ScriptData( int pages, float duration, bool flick )
133     : mPages( pages ),
134       mDuration( duration ),
135       mFlick( flick )
136     {
137     }
138
139     int   mPages;    ///< Number of pages to scroll
140     float mDuration; ///< Duration
141     bool  mFlick;    ///< Use flick or 'one-by-one' scroll
142   };
143
144   HomescreenBenchmark( Application& application, const Config& config )
145   : mApplication( application ),
146     mConfig( config ),
147     mScriptFrame( 0 ),
148     mCurrentPage( 0 )
149   {
150     // Connect to the Application's Init signal.
151     mApplication.InitSignal().Connect( this, &HomescreenBenchmark::Create );
152   }
153
154   ~HomescreenBenchmark()
155   {
156   }
157
158   // The Init signal is received once (only) during the Application lifetime.
159   void Create( Application& application )
160   {
161     // Create benchmark script
162     CreateScript();
163
164     // Get a handle to the stage
165     Stage stage = Stage::GetCurrent();
166
167     mScrollParent = Actor::New();
168     mScrollParent.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
169     mScrollParent.SetAnchorPoint( AnchorPoint::CENTER );
170     mScrollParent.SetParentOrigin( ParentOrigin::CENTER );
171
172     // create background
173     Toolkit::ImageView background = Toolkit::ImageView::New( BACKGROUND_IMAGE );
174     Stage::GetCurrent().Add( background );
175     background.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
176     background.SetAnchorPoint( AnchorPoint::CENTER );
177     background.SetParentOrigin( ParentOrigin::CENTER );
178
179     PopulatePages();
180
181     stage.Add( mScrollParent );
182
183     // Respond to a click anywhere on the stage.
184     stage.GetRootLayer().TouchSignal().Connect( this, &HomescreenBenchmark::OnTouch );
185   }
186
187   bool OnTouch( Actor actor, const TouchData& touch )
188   {
189     // Quit the application.
190     mApplication.Quit();
191     return true;
192   }
193
194   Actor AddPage()
195   {
196     // Create root page actor.
197     Actor pageActor;
198
199     if( mConfig.mTableViewEnabled )
200     {
201       Toolkit::TableView tableView = Toolkit::TableView::New( mConfig.mRows, mConfig.mCols );
202
203       // Create geometry batcher for table view.
204       tableView.SetBackgroundColor( Vector4( 0.0f, 0.0f, 0.0f, 0.5f ) );
205       pageActor = tableView;
206     }
207     else
208     {
209       pageActor = Toolkit::Control::New();
210       pageActor.SetProperty( Toolkit::Control::Property::BACKGROUND_COLOR, Vector4( 0.0f, 0.0f, 0.0f, 0.5f ) );
211     }
212
213     pageActor.SetParentOrigin( ParentOrigin::CENTER );
214     pageActor.SetAnchorPoint( AnchorPoint::CENTER );
215     pageActor.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
216     pageActor.SetSizeModeFactor( Vector3( PAGE_SCALE_FACTOR_X, PAGE_SCALE_FACTOR_Y, 1.0f ) );
217     return pageActor;
218   }
219
220   Toolkit::ImageView CreateImageView( const unsigned int currentIconIndex )
221   {
222     // Create empty image to avoid early renderer creation
223     Toolkit::ImageView imageView = Toolkit::ImageView::New();
224
225     // Auto-generate the Icons image URL.
226     Property::Map map;
227     std::stringstream imagePath;
228     imagePath << IMAGE_PATH_PREFIX << currentIconIndex << IMAGE_PATH_POSTFIX;
229     map[ Dali::Toolkit::ImageVisual::Property::URL ] = imagePath.str();
230
231     // Enable/disable batching
232     map[ Toolkit::ImageVisual::Property::BATCHING_ENABLED ] = mConfig.mBatchingEnabled;
233
234     imageView.SetProperty( Toolkit::ImageView::Property::IMAGE, map );
235     imageView.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
236     imageView.SetSizeScalePolicy( SizeScalePolicy::FIT_WITH_ASPECT_RATIO );
237     imageView.SetAnchorPoint( AnchorPoint::CENTER );
238     imageView.SetParentOrigin( ParentOrigin::CENTER );
239     imageView.SetSizeModeFactor( Vector3( IMAGE_AREA, IMAGE_AREA, 1.0f ) );
240
241     return imageView;
242   }
243
244   Toolkit::Button CreateButton( const unsigned int currentIconIndex )
245   {
246     Toolkit::CheckBoxButton button = Toolkit::CheckBoxButton::New();
247     button.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
248     button.SetSizeScalePolicy( SizeScalePolicy::FIT_WITH_ASPECT_RATIO );
249     button.SetAnchorPoint( AnchorPoint::CENTER );
250     button.SetParentOrigin( ParentOrigin::CENTER );
251     button.SetProperty( Toolkit::Button::Property::SELECTED, ( currentIconIndex % 2 == 0 ) ); // Select half the button
252
253     return button;
254   }
255
256   void AddIconsToPage( Actor page )
257   {
258     Size stageSize( Stage::GetCurrent().GetSize() );
259     const float scaledHeight = stageSize.y * PAGE_SCALE_FACTOR_Y;
260     const float scaledWidth = stageSize.x * PAGE_SCALE_FACTOR_X;
261     const float PADDING = stageSize.y / 64.0f;
262     const float ROW_HEIGHT = ( scaledHeight - (PADDING*2.0f) ) / static_cast<float>( mConfig.mRows );
263     const float COL_WIDTH = ( scaledWidth - (PADDING*2.0f) ) / static_cast<float>( mConfig.mCols );
264
265     Vector2 dpi = Stage::GetCurrent().GetDpi();
266
267     static int currentIconIndex = 0;
268
269     for( int y = 0; y < mConfig.mRows; ++y )
270     {
271       for( int x = 0; x < mConfig.mCols; ++x )
272       {
273         // Create parent icon view
274         Toolkit::Control iconView = Toolkit::Control::New();
275         iconView.SetAnchorPoint( AnchorPoint::TOP_LEFT );
276         iconView.SetParentOrigin( ParentOrigin::TOP_LEFT );
277
278         if( !mConfig.mTableViewEnabled )
279         {
280           float rowX = x * COL_WIDTH + PADDING;
281           float rowY = y * ROW_HEIGHT + PADDING;
282           iconView.SetSize( Vector3( COL_WIDTH, ROW_HEIGHT, 1.0f ) );
283           iconView.SetPosition( Vector3( rowX, rowY, 0.0f ) );
284         }
285         else
286         {
287           iconView.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
288           iconView.SetSizeScalePolicy( SizeScalePolicy::FIT_WITH_ASPECT_RATIO );
289         }
290
291         Actor icon;
292
293         switch( mConfig.mIconType )
294         {
295           case CHECKBOX:
296           {
297             icon = CreateButton( currentIconIndex );
298             break;
299           }
300           case IMAGEVIEW:
301           {
302             icon = CreateImageView( currentIconIndex );
303             break;
304           }
305         }
306
307         if( mConfig.mIconLabelsEnabled )
308         {
309           // create label
310           Toolkit::TextLabel textLabel = Toolkit::TextLabel::New( DEMO_APPS_NAMES[currentIconIndex] );
311           textLabel.SetAnchorPoint( AnchorPoint::TOP_CENTER );
312           textLabel.SetParentOrigin( ParentOrigin::BOTTOM_CENTER );
313           textLabel.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::ALL_DIMENSIONS );
314           textLabel.SetProperty( Toolkit::TextLabel::Property::TEXT_COLOR, Vector4( 1.0f, 1.0f, 1.0f, 1.0f ) ); // White.
315           textLabel.SetProperty( Toolkit::TextLabel::Property::POINT_SIZE, ( ( static_cast<float>( ROW_HEIGHT * LABEL_AREA ) * 72.0f )  / dpi.y ) * 0.25f );
316           textLabel.SetProperty( Toolkit::TextLabel::Property::HORIZONTAL_ALIGNMENT, "CENTER" );
317           textLabel.SetProperty( Toolkit::TextLabel::Property::VERTICAL_ALIGNMENT, "TOP" );
318           icon.Add( textLabel );
319         }
320
321         iconView.Add( icon );
322         page.Add( iconView );
323
324         // We only have images and names for a certain number of icons.
325         // Wrap around if we have used them all.
326         if( ++currentIconIndex == TOTAL_ICON_DEFINITIONS )
327         {
328           currentIconIndex = 0;
329         }
330       }
331     }
332   }
333
334   void CreateScript()
335   {
336     const int lastPage = mConfig.mPageCount - 1;
337     const int halfA = lastPage / 2;
338     const int halfB = lastPage / 2 + lastPage % 2;
339     mScriptFrameData.push_back( ScriptData( lastPage,  1.5f, true  ) );
340     mScriptFrameData.push_back( ScriptData( -lastPage, 1.5f, true  ) );
341     mScriptFrameData.push_back( ScriptData( halfA,     1.0f, true  ) );
342     mScriptFrameData.push_back( ScriptData( halfB,     1.0f, true  ) );
343     mScriptFrameData.push_back( ScriptData( -lastPage, 0.5f, false ) );
344     mScriptFrameData.push_back( ScriptData( halfA,     0.5f, false ) );
345     mScriptFrameData.push_back( ScriptData( halfB,     1.0f, true  ) );
346     mScriptFrameData.push_back( ScriptData( -halfA,    1.0f, true  ) );
347     mScriptFrameData.push_back( ScriptData( 1,         0.1f, true  ) );
348     mScriptFrameData.push_back( ScriptData( -1,        0.1f, true  ) );
349     mScriptFrameData.push_back( ScriptData( 1,         0.1f, true  ) );
350     mScriptFrameData.push_back( ScriptData( -1,        0.1f, true  ) );
351     mScriptFrameData.push_back( ScriptData( 1,         0.1f, true  ) );
352     mScriptFrameData.push_back( ScriptData( -1,        0.1f, true  ) );
353     mScriptFrameData.push_back( ScriptData( halfA,     1.0f, true  ) );
354   }
355
356   void PopulatePages()
357   {
358     Vector3 stageSize( Stage::GetCurrent().GetSize() );
359
360     for( int i = 0; i < mConfig.mPageCount; ++i )
361     {
362       // Create page.
363       Actor page = AddPage();
364
365       // Populate icons.
366       AddIconsToPage( page );
367
368       // Move page 'a little bit up'.
369       page.SetParentOrigin( ParentOrigin::CENTER );
370       page.SetAnchorPoint( AnchorPoint::CENTER );
371       page.SetPosition( Vector3( stageSize.x * i, 0.0f, 0.0f ) );
372       mScrollParent.Add( page );
373
374       if( mConfig.mTableViewEnabled && mConfig.mBatchingEnabled )
375       {
376         page.SetProperty( DevelActor::Property::BATCH_PARENT, true );
377       }
378     }
379
380     mScrollParent.SetOpacity( 1.0f );
381     mScrollParent.SetScale( Vector3::ONE );
382
383     // Fade in.
384     ShowAnimation();
385   }
386
387   void ShowAnimation()
388   {
389     mShowAnimation = Animation::New( 1.0f );
390     mShowAnimation.AnimateTo( Property( mScrollParent, Actor::Property::COLOR_ALPHA ), 1.0f, AlphaFunction::EASE_IN_OUT );
391     mShowAnimation.AnimateTo( Property( mScrollParent, Actor::Property::SCALE ), Vector3::ONE, AlphaFunction::EASE_IN_OUT );
392     mShowAnimation.FinishedSignal().Connect( this, &HomescreenBenchmark::OnAnimationEnd );
393     mShowAnimation.Play();
394   }
395
396   void ScrollPages(int pages, float duration, bool flick)
397   {
398     duration *= PAGE_DURATION_SCALE_FACTOR;
399     Vector3 stageSize( Stage::GetCurrent().GetSize() );
400     mScrollAnimation = Animation::New( duration );
401     if( flick )
402     {
403       mScrollAnimation.AnimateBy( Property( mScrollParent, Actor::Property::POSITION ), Vector3( -stageSize.x * pages, 0.0f, 0.0f ), AlphaFunction::EASE_IN_OUT );
404     }
405     else
406     {
407       int totalPages = abs( pages );
408       for( int i = 0; i < totalPages; ++i )
409       {
410         mScrollAnimation.AnimateBy( Property( mScrollParent, Actor::Property::POSITION ), Vector3( pages < 0 ? stageSize.x : -stageSize.x, 0.0f, 0.0f ), AlphaFunction::EASE_IN_OUT, TimePeriod( duration * i, duration ) );
411       }
412     }
413     mScrollAnimation.FinishedSignal().Connect( this, &HomescreenBenchmark::OnAnimationEnd );
414     mScrollAnimation.Play();
415     mCurrentPage += pages;
416   }
417
418   void OnAnimationEnd( Animation& source )
419   {
420     if( mScriptFrame < mScriptFrameData.size() )
421     {
422       ScriptData& frame = mScriptFrameData[mScriptFrame];
423       ScrollPages( frame.mPages, frame.mDuration, frame.mFlick );
424       ++mScriptFrame;
425     }
426     else
427     {
428       mApplication.Quit();
429     }
430   }
431
432 private:
433
434   Application&                mApplication;
435   Actor                       mScrollParent;
436   Animation                   mShowAnimation;
437   Animation                   mScrollAnimation;
438   Config                      mConfig;
439   std::vector<ScriptData>     mScriptFrameData;
440   size_t                      mScriptFrame;
441   int                         mCurrentPage;
442 };
443
444 void RunTest( Application& application, const HomescreenBenchmark::Config& config, bool printHelpAndExit )
445 {
446   HomescreenBenchmark test( application, config );
447
448   if( printHelpAndExit )
449   {
450     PrintHelp( "c<num>",               " Number of columns" );
451     PrintHelp( "r<num>",               " Number of rows" );
452     PrintHelp( "p<num>",               " Number of pages ( must be greater than 1 )" );
453     PrintHelp( "-disable-tableview",   " Disables the use of TableView for layouting (must be enabled for batching)" );
454     PrintHelp( "-disable-batching",    " Disables geometry batching" );
455     PrintHelp( "-disable-icon-labels", " Disables labels for each icon" );
456     PrintHelp( "-use-checkbox",        " Uses checkboxes for icons" );
457     return;
458   }
459
460   application.MainLoop();
461 }
462
463 // Entry point for Linux & Tizen applications.
464 int DALI_EXPORT_API main( int argc, char **argv )
465 {
466   // Default settings.
467   HomescreenBenchmark::Config config;
468
469   bool printHelpAndExit = false;
470
471   for( int i = 1 ; i < argc; ++i )
472   {
473     std::string arg( argv[i] );
474     if( arg.compare( 0, 2, "-r" ) == 0 )
475     {
476       config.mRows = atoi( arg.substr( 2 ).c_str() );
477     }
478     else if( arg.compare( 0, 2, "-c" ) == 0 )
479     {
480       config.mCols = atoi( arg.substr( 2 ).c_str() );
481     }
482     else if( arg.compare( 0, 2, "-p" ) == 0 )
483     {
484       config.mPageCount = atoi( arg.substr( 2 ).c_str() );
485     }
486     else if( arg.compare( "--disable-tableview" ) == 0 )
487     {
488       config.mTableViewEnabled = false;
489     }
490     else if( arg.compare( "--disable-batching" ) == 0 )
491     {
492       config.mBatchingEnabled = false;
493     }
494     else if( arg.compare( "--disable-icon-labels" ) == 0 )
495     {
496       config.mIconLabelsEnabled = false;
497     }
498     else if( arg.compare( "--use-checkbox" ) == 0 )
499     {
500       config.mIconType = CHECKBOX;
501     }
502     else if ( arg.compare( "--help" ) == 0 )
503     {
504       printHelpAndExit = true;
505     }
506   }
507
508   Application application = Application::New( &argc, &argv );
509
510   RunTest( application, config, printHelpAndExit );
511
512   return 0;
513 }