Redraw Drawing

This topic provides background information about redraw drawing and tips for updating old code to comply with the recommendation that all drawing is migrated to redraw drawing.

Variant: Both (ScreenPlay and non-ScreenPlay). Target audience: Application developers.

About Redraw Drawing

Symbian recommends that all CWindowGc drawing is redraw drawing, which means that it takes place between RWindow::BeginRedraw() and RWindow::EndRedraw() calls. The UI Control Framework (CONE) automatically takes care of this for you if you use the CCoeControl::DrawNow(const TRect &)const and CCoeControl::DrawDeferred()const methods.

For example, suppose an application wants to write the text "Hello!" to a window and then add a red diagonal line below it (A in following diagram). When this is performed as redraw drawing, the application calculates the bounding rectangles of the "Hello!" text and the red line (B). For each one, the application passes the rectangle to RWindow::BeginRedraw(const TRect &), then calls the draw commands and afterwards calls RWindow::EndRedraw().

This has the advantage that the Window Server knows that the area of the window that has the "Hello!" text is not affected by the drawing of the red line. There are also advantages when some or all of the window needs to be repainted. For example, suppose another window (such as an OK dialog box) appears above the window (as shown in C). When the dialog box is closed, the Window Server repaints the screen behind the dialog box. Because in this example the OK dialog box was entirely within the red line's bounding rectangle, the Window Server simply replays that rectangle's drawing operations (D). This is more efficient than repainting the whole window.

However, in earlier versions of Symbian you could, for example, draw the red line without bracketing it within BeginRedraw() and EndRedraw() calls. This is called non-redraw drawing. In ScreenPlay, each non-redraw drawing operation triggers the Window Server to invalidate the entire window. This means that the client application must then perform a full window redraw, as shown in the next diagram.

Figure 1. Non-redraw drawing sequence

This is less efficient than the Window Server replaying the draw operations for the affected area.

Symbian recommends that all drawing is now performed as redraw drawing. Typically this involves dividing the window up into rectangular areas that represent different aspects of the user interface—for example, as shown in the following diagram. If anything spoils the screen, the Window Server then only needs to redraw the corresponding portions of the user interface.

In earlier versions of Symbian, before the introduction of the CCoeControl::DrawNow(const TRect &)const overload, developers sometimes used non-redraw drawing to update a small part of a control. This technique has often been used for virtual keyboards and calendar controls. This type of use case is now particularly unsuited to non-redraw drawing because in ScreenPlay each non-redraw drawing command triggers a server-initiated client redraw of the entire window. This can be very slow if the window requires many drawing operations to redraw the scene.

Symbian recommends that all non-redraw drawing is changed to redraw drawing. Migrating non-redraw drawing to redraw drawing means that you must enclose all drawing operations between RWindow::BeginRedraw() and RWindow::EndRedraw() calls or migrate it to use the CCoeControl::DrawNow() and CCoeControl::DrawDeferred() methods. Sometimes this causes some subtle changes in behavior, as explained in the Notes below.

Restrictions

  1. You must supply only drawing operations between the BeginRedraw() and EndRedraw() calls. For example, you must not change the window extent between a BeginRedraw() and EndRedraw() call.

  2. For opaque windows, you must define pixel content for the entire area being redrawn. For example, when calling CCoeControl::DrawNow(const TRect &)const, you must supply drawing operations for the entire rectangle passed to the function. Similarly when calling the CCoeControl::DrawNow()const overload, you must supply drawing operations for the entire window. However, this is not necessary for transparent windows.

  3. You must not call any functions that can leave between the BeginRedraw() and EndRedraw() calls. This is because drawing operations must never leave.

Note: A redrawer's RunL() function must never perform any non-redraw drawing. See Server-Initiated vs. Application-Initiated Redrawing for more information.

Notes

Performance

You can improve performance by passing the smallest possible bounding rectangle to the RWindow::BeginRedraw() and CCoeControl::DrawNow() methods. If this still results in poor performance, because, for example, there is a large amount of incremental screen updating, the recommended solution in ScreenPlay is to render to a surface.

High frequency rendering

It is possible for a client to provide batches of redraw drawing (that is, drawing bracketed in BeginRedraw() and EndRedraw() calls) for the same region of a window faster than they can be displayed on the screen. When this happens, the Window Server may discard the drawing commands in one or more of the earlier batches of redraw drawing and simply draw the pixels defined in a later redraw batch to the screen. This can result in missing frames, but is only likely to be noticeable in an application like a game that performs high-frequency rendering.

The solution is to call RWsSession::Finish() after each EndRedraw() call. This slows the client so that it does not generate pixel content faster than the Window Server can display it on the screen.

To summarize: When you want to ensure that every single frame is shown on the screen, call Finish() after each EndRedraw() call.

Delay of execution

After you migrate non-redraw drawing to redraw drawing, the execution of the drawing operations is deferred compared to previously. This means that if the arguments of the drawing operations result in a panic, the panic also appears later than before.

CRemoteGc usage

Device creators can use CRemoteGc to create a data buffer containing a series of drawing operations. These can then be executed to display the drawing on the screen by using CCommandBuffer::Play().

For example, consider a weather program that has one sub-system responsible for deciding the drawing operations that comprise a weather symbol and another sub-system that places weather symbols at different locations on the screen. Here we potentially have a Cloud symbol buffer created once and played many times.

The points to note are:

  • Any BeginRedraw() and EndRedraw() commands that are placed into the CRemoteGc buffer are not transferred to the target window when the buffer is played

  • You must bracket any call to CCommandBuffer::Play() within BeginRedraw() and EndRedraw() calls.

It may be puzzling to understand why any BeginRedraw() and EndRedraw() commands would be placed in the CRemoteGc buffer at all. This facility is to allow the commands already in the CRemoteGc buffer to be replaced with new drawing operations. In the weather example, it might be done to allow a "7o Celsius" label to partially occlude a cloud graphic placed earlier into the CRemoteGc buffer.

Configuring the emulator to panic clients that ignore the convention

In both ScreenPlay and the non-ScreenPlay variant, you can configure the TechView emulator to panic clients that ignore the convention (described above) that all drawing operations are performed as redraw drawing.

To enable this feature, add the following line to the epoc32/data/epoc.ini file:

debug_wserv_exe_EnforceRedrawCallingConvention 1

This feature is disabled if you do not specify this parameter or if you set it to zero, like this:

debug_wserv_exe_EnforceRedrawCallingConvention 0

The feature takes effect in debug emulator (WINSCW) builds only—it never affects ARM builds.

When a Window Server panic code 79 (EWservPanicWindowBeginRedrawNotCalled) then occurs, it means that a non-redraw drawing operation has occurred.

However, the Window Server buffers client requests. This means that the drawing that is at fault may have been issued by the client some time earlier. It is therefore helpful to enable autoflushing, because this removes the buffering between the client issuing the drawing operations and the Window Server processing them. This makes it easier to identify the drawing operation that is at fault.

There are three ways to enable autoflushing:

  1. To enable autoflushing globally across all clients, define __AUTO_FLUSH in client/client.h and re-compile the Window Server code.

  2. To enable autoflushing in your client-side code only, call RWsSession::SetAutoFlush(ETrue).

  3. To enable autoflushing on an ad hoc basis in the TechView emulator, press Ctrl-Alt-Shift-F.