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