Guiliani  Version 2.5 revision 7293 (documentation build 13)
Performance Optimization

Page contents

Introduction

This chapter shall give a guideline on how to optimize GUI-applications in general, and those built using Guiliani in particular. Performance tuning is a complex topic which depends on countless factors and can vary drastically from platform to platform, but some advice can be given on what has proven to be "good practice".

Performance Analysis

Before diving into detail-optimizations a high-level analysis should clarify where it actually makes sense to invest time in further optimizations. If for instance the majority of performance gets lost while accessing a slow flash file system, it is a waste of time to optimize all blit operations within the GUI.

We recommend the following steps:

  1. Check the User Interface's performance independent of any application tasks running in parallel.
  2. Measure speed of file-system access (e.g. loading data from flash) in a tiny test-program outside of the UI
  3. Measure memory throughput in a tiny test-program outside of the UI
  4. Find out how long basic graphic-functionality (e.g. flipping front and back buffers) takes outside of the UI
  5. Deactivate speicific features in the UI and check the impact on performance. E.g. disable Font-Rendering by NOT creating a Font-Wrapper, disable Input by using a CGUIPlaybackInput etc.

Smart invalidation

Guiliani offers you the possibility to increase performance by only updating small sections of the screen, as opposed to always redraw the entire screen. Which areas of the screen have to be redrawn can be controlled by calling CGUIObject::InvalidateArea() on the respective objects, or calling CGfxWrap::InvalidateRect() for specific rectangles on the screen. At the end of each frame, Guiliani will redraw all objects overlapping with the areas marked as "invalidated". It is therefore recommended to only invalidate those areas on the screen, where a visual update is really required.

Overlapping Objects

If objects are overlapping (i.e. one object partially covers another object which lies behind it), this will usually cause both objects to be redrawn, EVEN if the object on top is completely opaque. This is because the framework has no possiblity to find out about the opacity of an object (since it can not look into your DoDraw() code) and will always draw all objects within an invalidated area from back to front. It is thus desirable to avoid overlapping objects, or explicitly setting objects invisible (via CGUIObject::SetInvisible()) when you know they are fully covered by other objects.

Blitting

On many systems (especially those without graphics accelerators) blitting will be the most expensive and performance critical aspect of your user interface. In order to achieve the best possible performance, make sure you follow these rules of thumb:

  1. Avoid Rotation. Rotation is generally very costly. Stick with un-rotated blits wherever possible.
  2. Avoid transparencies. If you do not need it, strip any alpha-channels of your image resources. Be aware that even a single transparent pixel will add an alpha-layer to an entire image.
  3. Avoid Stretch-Blits. Make sure you do not accidently stretch bitmaps. E.g. ensure that buttons are of EXACTLY the same size as the images you assign to them.
  4. Avoid Subpixel-Coordinates. Stick with full pixel coordinates where possible. On some renderers it is possible to explicitly deactivate subpixel-accuracy (e.g. CGfxWrapeGML::SetSubpixelEnabled()) to achieve a noticeable performance boost.

Performance of text

Text tends to be time-consuming on most combinations of Renderers and Font-Engines. Whether text performance is the bottleneck in your application can easily be found out by temporarily deactivating text output. If it is indeed a critical aspect in your application performance, try the following:

  1. Reduce the total amount of simultaneous text on the screen.
  2. Increase Cache-Size of your Font-Wrapper. Many Font-Wrappers offer the possibility to reserve a given amount of memory for caching fonts/glyphs. This is usually handed over as a parameter during the intial call to CreateInstance at initialization of the application (e.g. see CFntWrapFreeType)
  3. Avoid Multiline Text and resizing of text areas. Automatic word-wrap is a complex operation which should be avoided when possible.
  4. Some Font-Engines (e.g. FreeType) may repeatedly access files to load glyphs. Slow file-operations can thus also result in reduced text performance.

Refreshing the screen

While it is usually a lot faster to refresh the screen by only copying those regions from the back- to the front buffer which have actually changed it can have the opposite effect in certain scenarios. If for instance you are having a big number of independent invalidated rectangles and a copy from the back- to the front-buffer takes a significant amount of time, then it might be preferrable to combine all these single refreshs into one big refresh at the end of the frame.

This can be done by deriving a custom Graphics-Wrapper from the wrapper which you are using and re-implementing the CGfxWrap::Refresh() method. The method's parameter (CGfxWrap::RefreshCall_t) tells you which refresh the current call represents, and you can thus implement a simple full-screen refresh for the last refresh of the frame, only.

Optimizing a control's DoDraw()

The framework will make sure that only the DoDraw() methods of those controls is called which are relevant for the current invalidated region. This means it will not call DoDraw() for objects which lie outside of these regions, or that are currently invisible.

Nevertheless, a single DoDraw() method may contain complex drawing code that could be optimized further. In such cases you may wish to retrieve the current clipping rectangle from the graphics wrapper via CGfxWrap::GetCliprect() and use this to skip sections within your DoDraw() that will not be visible anyway.