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".
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:
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.
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.
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:
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:
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.
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.