Guiliani  Version 2.5 revision 6773 (build 33)
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
Invalidating and redrawing

Page contents

Introduction

Guiliani features a smart redraw algorithm, which only updates those regions of the screen that have actually changed between the previous and the current frame. This significantly reduces the overall amount of graphic operations per frame, thus leading to smoother animations and reduced CPU load.

The algorithm is based on the concept of invalidated areas, which means rectangular regions of the screen, which need to be redrawn, because their visualization has changed for some reason (e.g. an object has changed its state, or an animation is in progress). An area will be marked as invalidated either automatically by the framework - this is typically the case for standard actions such as state-changes of an object - or manually by the GUI developer who issues an InvalidateArea() call on an object. This will then internally invalidate the area currently covered by that object.

Guiliani internally handles a list of these invalidated areas and ensures that every object intersecting with one of these areas will be redrawn during the next frame. This is done by traversing the object tree and calling all affected object's DoDraw() implementations.

Redraw example

Consider the following 'Hello world' example GUI:

helloguiliani_cap.png

This GUI contains the following controls, from top to bottom:

These are contained in a composite object which in turn is a child of a CGUI object. More information about parent and child objects can be found in Controls and Application set-up.

Take a closer look at the buttons. The 'Close' button has a slightly thicker outline than the 'Update' button. This is because it is focused (more information on focusing can be found in Focusing). If the user presses the 'down' button, the focus is transferred from the 'Close' to the 'Update' button. Even more detailed, the 'Close' button loses the focus and the 'Update' button receives the focus. This change of state is shown to the user by drawing the buttons in the respective style.

The framework invalidates a control whenever its focus state changes. This is done by calling CGUIObject::InvalidateArea. In the case described above, both buttons are invalidated. The framework stores the invalidated areas and takes care of calling each intersecting controls' DoDraw method. What does this mean for our example?

Take a look at this abstracted view of the same GUI:

helloguiliani_abstract.png

Each of the rectangles represents a CGUIObject. The CGUI object and the main container actually occupy the same area on the screen, but the CGUI object does not have a graphical representation (its DoDraw method does nothing).

When the framework draws the frame after both of the buttons have been invalidated, the following things happen:

  1. The main container's DoDraw method is called. The main container only draws a white background rectangle by calling CGfxWrap::FilledRect. The framework clips this drawing operation to be no larger than the invalidated area, which means that even if the container paints a rectangle as large as itself, only the invalidated button's area pixels are manipulated.
  2. The 'Close' button's DoDraw method is called. The button draws an image by calling CGfxWrap::BlitImg and prints its text afterwards.
  3. The main container's DoDraw method is called again, but this time it is clipped against the area of the second button.
  4. The 'Update' button is drawn similar to the 'Close' button.

In this way, the Guiliani works through all invalidated areas, redrawing everything that is necessary from back to front.

Optimize your application's performance

Understanding the way in which Guiliani's redraw mechanism works allows you to optimize your application's performance. This section will give you advice on how to implement your own controls and how to design your GUI in order to get the best achievable performance from your target system.

The most important factors influencing your GUI's performance are:

  • the size of invalidated area,
  • the number of overlapping objects inside the invalidated area, and
  • the complexity of drawn graphics.

The size of the invalidated area

The larger the region of the screen which is marked as invalidated, the more graphics operations will typically occur during the redraw cycle. If only few objects overlap the invalidated area, then large parts of the GUI's object tree will not even be processed and be completely excluded from the drawing process. It is therefore desirable to keep the overall size of the invalidated region as small as possible.

Invalidations either happen implicitly within the framework as a reaction to certain events (e.g. the user has clicked on a button) or explicitly by user code which is calling InvalidateArea() on an object. If you are calling InvalidateArea() within your code, for example because you wish your object to be redrawn, then you should make sure not to invalidate more than necessary. If for example you are having a CompositeObject with five child objects in it, and only one of these children has changed its visualization for some reason, then you should avoid calling InvalidateArea() on the CompositeObject, and instead call it directly on the affected child object.

Another potential cause for unexpectedly large redraw areas are partially overlapping invalidated regions. While non-overlapping invalidated regions will be kept independent by the framework's algorithm, partially overlapping ones will be merged into one larger rectangle, encapsulating the former two. For the GUI developer this means that it is desirable to make sure that GUI elements are not accidentally overlapping by a few pixels, as this may cause unnecessary drawing overhead.

Overlapping objects inside the invalidated area

Objects within a GUI may overlap. This is perfectly legal and will be handled by the framework's drawing code, which ensures that these objects get drawn in the correct order, from back to front - thus implementing an implicit z-ordering. It may have a negative impact on performance, though, if several objects are stacked on top of each other. Take for example the following use case:

Redraw_Overlap_example.png

When updating the marked 100x100 pixel region you will actually be drawing much more than just these 100x100 pixels, since the drawing code of the background image, the Play button, the Pop-up's background image and the progress bar will be executed. Since all of them can potentially (and actually do) have transparencies in them, the framework can not optimize anything here. It is therefore in the GUI developer's responsibility to avoid such constellations where possible.

Complexity of drawn graphics

This last point is quite obvious: More complex graphics will also require more time to be displayed. Drawing a simple rectangle is going to consume less time than drawing a breath-taking fully-featured 3D animation. Generally whatever happens inside of an object's DoDraw() method lies completely within that control's responsibility. The framework is going to call the drawing code at the right time and it will set the clipping rectangle, so that no drawing will occur outside of the object's area and/or the invalidated area, but nothing more. Control developers should therefore optimize their specific drawing code as far as possible. It is also advisable to group complex controls into smaller entities of CGUIObjects and CGUICompositeObjects, so that the framework's algorithms can potentially clip away objects which are for some reason not affected anyway (e.g. because they lay outside of the invalidated region, their parents are invisible, or they are not visible because of preceding clipping operations).