Optimizing Non-Native Orientation Windows in Symbian^3

This topic describes a hardware-dependent performance optimization that is relevant for application developers working on Symbian^3 devices on which rotation at the compositor level is expensive. The optimization is not relevant for device hardware that does not suffer from the cost of compositor level rotation.

This topic follows on from the introduction to native and non-native window orientation in Symbian-Specific Behavior.

Note. For the sake of clarity this topic assumes the device display panel has a native orientation of portrait. Therefore a landscape RWindow and eglWindowSurface are considered to be in non-native orientation. See Modifications for Khronos rendering: handling different screen sizes and rotation at the end of the topic for further information.

Which applications will benefit from the optimization?

The potential candidates for using FixNativeOrientation windows are application developers who wish to use a full-screen eglWindowSurface in landscape orientation. A typical use case is a game that can only operate in landscape. For some devices this scenario causes a rotation cost in the compositor, resulting in a reduced frame rate and possibly an increase in graphics memory usage.

If you are working on such a device and you have a use case where you wish to avoid the performance overhead and maintain as fast a frame rate as possible then you can use the FixNativeOrientation() function described in this topic.

The effect of the optimization

When an application creates an RWindow in landscape orientation, by default the associated eglWindowSurface is also in landscape. In this situation the orientation of the eglWindowSurface is not the same as the native orientation of the display panel. As a result the compositor may suffer a rotation cost when rendering output to the display panel.

Calling FixNativeOrientation() when you create the RWindow addresses this problem by allowing the application to use a landscape RWindow along with a portrait eglWindowSurface. The eglWindowSurface remains in the same orientation as the display panel and the expensive compositor rotation is avoided.

The diagram shows the result of a successful call to FixNativeOrientation().

Figure: Effect of calling FixNativeOrientation()

Only the orientation of the eglWindowSurface changes as a result of the call. The compositor still presents a landscape orientation and there is no difference in the way the Window Server interacts with the landscape RWindow. The abstract view of the application content also remains in landscape orientation and pointer events are sent to the application accordingly.

As a result of calling FixNativeOrientation() it is necessary for the application to carry out geometric operations to compensate for the mismatch between the orientation of the application content and the orientation of the eglWindowSurface. Geometric rotation and translation are required when carrying out OpenVG or OpenGL ES rendering from the application to the eglWindowSurface. The calculations will vary depending on which of the Khronos APIs are used. On devices where this optimization is required, the cost of these geometric operations is very small in comparison to the cost of rotation by the compositor that has been avoided.

When to use a FixNativeOrientation() window

  • You are writing a client application using a full-screen eglWindowSurface in non-native orientation (assumed to be landscape in this topic). The window will be positioned at the origin and never resized.

  • The device hardware suffers from expensive compositor rotation and the performance requirements of your application mean you need to avoid this cost.

How to setup the RWindow

Client applications wishing to use the optimization should perform the following:
  1. Have a CWsScreenDevice active and set up in the landscape orientation.

  2. Create an RWindow with a landscape, full screen, origin based, window.

  3. Immediately call RWindow::FixNativeOrientation() to activate the orientation independent behaviour.

  4. Activate the window .

  5. Create an eglWindowSurface with the window.

Note. There a number of important constraints on the use of this API. See the list of constraints below.

Handling FixNativeOrientation() error codes

It is important to check the error return code from FixNativeOrientation(). Only proceed to use the window if the return code is KErrNone.

If the function returns KErrNotSupported, the feature is not supported. As the optimization is not available, you should close the window and create your window again as normal without calling FixNativeOrientation().

In the case of any other return code such as KErrNoMemory, you should abort window creation. Typically your context would be the ConstructL() of a CCoeControl, so you would call User::Leave(KErrNoMemory).

Effect of FixNativeOrientation() on pointer event handling

For an application that you have written to run in landscape (i.e. non-native) orientation no new actions are required to handle pointer events as a result of calling RWindow::FixNativeOrientation(). Pointer events are sent to the application as before, in the landscape orientation.

When the device is in landscape, the application uses a CWsScreenDevice to find out from the Window Server that the screen has a landscape aspect ratio. Normally, an application wishing to create a full screen window would create a window with a landscape aspect ratio. Assuming a landscape screen size of 640 x 360 the size of the window would be defined by TRect(0,0,640,360). This stays the same for FixNativeOrientation windows.

Modifications required for Khronos rendering

Creating an eglWindowSurface on a landscape full-screen FixNativeOrientation window results in a portrait sized surface. Assuming a screen size of 640 x 360 the RWindow has extent TRect(0,0,640,360) and the eglWindowSurface has size 360, 640.

Khronos rendering into the surface using OpenVG or OpenGL ES will reach the screen in a native orientation (portrait). As a result a translate and counter-rotation is required to put non-native (landscape) rendering into the native (portrait) surface.

Here is an example for OpenVG rendering:

vgSeti(VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE);
vgLoadIdentity();
if (iFixNativeOrientationEnabled && iLandscapeRendering)
        {
        vgRotate(-90.0f);
        vgTranslate(0.0f, -640.0f);
        }

See Modifications for Khronos rendering: handling different screen sizes and rotation for a more flexible example.

Constraints on the use of FixNativeOrientation() windows

There are a number of constraints on the use of the function:
  1. The RWindow on which you call FixNativeOrientation() must be full-screen. For a screen size of 640 x 360 the window must be 640 x 360.

  2. The RWindow must be positioned at the origin. The position of the window must be at (0,0) in screen co-ordinates. Non-origin position windows are not supported.

  3. Resizing of the eglWindowSurface is not supported. FixNativeOrientation() does not allow an eglWindowSurface to be resized after it has been created.

  4. Call FixNativeOrientation() immediately after setting the extent of the RWindow and before activating it.

  5. Ensure the application's CWsScreenDevice is not destroyed during the lifetime of the RWindow on which you call FixNativeOrientation(). The client will be panicked by the Window Server if this constraint is violated.

  6. On some devices, the optimization benefits of FixNativeOrientation() are not achieved if an external display is plugged in to the device.

  7. FixNativeOrientation() returns KErrNotSupported in an emulator environment.

Example

The following code example illustrates the steps involved in creating a FixNativeOrientation() window. The example assumes the native orientation of the display panel is portrait.

void CExampleApp::ConstructL()
    {
    RWsSession wsSession;
    User::LeaveIfError(wsSession.Connect());

    RGroupWindow groupWin = RWindowGroup(wsSession);
    User::LeaveIfError(groupWin.Construct(0x111, ETrue));
    groupWin.EnableScreenChangeEvents();

    // You must have a valid screen device before calling 
    // FixNativeOrientation().
    CWsScreenDevice* scrdev = new(ELeave) CWsScreenDevice(wsSession);
    User::LeaveIfError(scrdev->Construct(0));

    // If the device is in landscape, the application's screen device
    // should be in landscape too.
    const TSize screenSize = scrdev->SizeInPixels();

    // Attempt to create the RWindow with FixNativeOrientation enabled.
    TRAPD(err, ConstructWindowL(ETrue));
    switch (err)
        {
        case KErrNone:
            break;
        case KErrNotSupported:

            // The feature is not supported, so delete and recreate
            // the window without using FixNativeOrientation().
            delete iWindow;
            iWindow = NULL;
            ConstructWindowL(EFalse);
            break;
        default:
            User::Leave(err);
        }
        
    // Create an eglWindowSurface with the window.
    InitEgl();
    }

void CExampleApp::ConstructWindowL(TBool aFixNativeOrientation)
    {
    iWindow = new (ELeave) RWindow(wsSession);
    RWindow& window = *iWindow;
    window.Construct(iGroupWin1, 0x100);

    // Set the window to the current screen size. If the screen device
    // tells us we are in landscape, proceed to create a window with
    // landscape dimensions.
    window.SetExtent(TPoint(0, 0), screenSize);
    
    if (aFixNativeOrientation)
        {

        // Call RWindow::FixNativeOrientation() immediately after setting
        // the extent of the window.
        TInt err = window.FixNativeOrientation();

        // Handle the error code returned by FixNativeOrientation.
        switch (err)
            {
            case KErrNone:
                iFixNativeOrientationEnabled = ETrue;
                break;
            case KErrNotSupported:
            default:
                iFixNativeOrientationEnabled = EFalse;
                User::Leave(err);
            }
        }
    
    // Activate the window.
    window.Activate();
    wsSession.Flush();
    }

Modifications for Khronos rendering: handling different screen sizes and rotations

The basic example of Khronos rendering above makes the following constrained assumptions:
  1. The native orientation of all device display panels is portrait with dimensions of 360 x 640 pixels.

  2. The only orientations available are 0 degrees and 90 degrees.

The following code example illustrates the steps you can take to ensure that your application will work on devices that do not meet these constraints:
// An example that handles different screen dimensions and rotations.
vgSeti(VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE);
vgLoadIdentity();

// When FixNativeOrientation() is used, the application is responsible for
// rotating non-native rendering into surface.
if (iFixNativeOrientationEnabled)
    {
    switch(iOrientation)
        {
        case CFbsBitGc::EGraphicsOrientationNormal:
            break;
        case CFbsBitGc::EGraphicsOrientationRotated90:  
            vgTranslate(0.0f, surfaceHeight);
            vgRotate(-90.0f);
            break;
        case CFbsBitGc::EGraphicsOrientationRotated180:
            vgTranslate(surfaceWidth , surfaceHeight);
            vgRotate(180.0f);
            break;
        case CFbsBitGc::EGraphicsOrientationRotated270:
            vgTranslate(surfaceWidth, 0.0f);
            vgRotate(90.0f);
            break;
        }
    }

The following code shows how to obtain the size of the surface used in the transform above:

// Example of how to obtain surface dimensions for rotation transform.
EGLint surfaceWidth = 0;
EGLint surfaceHeight = 0;
eglQuerySurface(iEglDisplay, iEglSurface, EGL_WIDTH, &surfaceWidth);
eglQuerySurface(iEglDisplay, iEglSurface, EGL_HEIGHT, &surfaceHeight);

The rotation can be obtained from TPixelsAndRotation as obtained from the screen device when handling the screen change event:

void ExampleApp::HandleScreenDeviceChanged()
    {
    ...
    // Ask for the orientation and size of this new screen mode.
    TPixelsAndRotation pixelsAndRotation;
    Screen()->GetScreenModeSizeAndRotation(screenMode, pixelsAndRotation);
    const TSize screenSize = pixelsAndRotation.iPixelSize;
    const CFbsBitGc::TGraphicsOrientation rotation = pixelsAndRotation.iRotation;

    // Provide the vg renderer with current rotation.
    iVgRenderer->SetOrientation(rotation);
    ...
    }

Related concepts