Creating a Graphics Surface Tutorial

This tutorial first shows you how to create a composition surface using the Surface Manager API. It then shows you how to create second surface that shares the same memory.

Variant: ScreenPlay. Target audience: Device creators.

Required background

This tutorial assumes a background knowledge of the following:

Introduction

This topic shows you how to create one graphics surface and then another one that shares the same memory. Because the screen is either in portrait or landscape orientation, this approach is useful for handling two surfaces, one of which is used for the portrait orientation and the other for the landscape orientation.

For example, suppose Surface 1 is used for landscape orientation and Surface 2 for portrait orientation. When a rotation is in progress from landscape to portrait:

  • The first buffer in Surface 1 is in use as an input to the composition engine.

  • The renderer renders into the second buffer in Surface 2.

Because both surfaces share the same memory, the buffers in the two orientations must be at the same offsets in the chunk. This is shown in the following diagram.

Figure 1. Two double-buffered surfaces that share the same memory

In the example code below, some error handling has been omitted for brevity.

Creating the first surface

  1. Call RSurfaceManager::Open() to open a connection to the Surface Manager.

    RSurfaceManager surfaceManager;
    surfaceManager.Open();

    The Surface Manager is now ready for use.

  2. Specify the surface creation attributes using the TSurfaceCreationAttributes class.

    RSurfaceManager::TSurfaceCreationAttributesBuf bf;
    RSurfaceManager::TSurfaceCreationAttributes& attributes = bf();
        
    attributes.iSize.iWidth = 320;
    attributes.iSize.iHeight = 240;
    attributes.iBuffers = 2;
    attributes.iPixelFormat = EUidPixelFormatARGB_4444;    
    attributes.iStride = 640;
    attributes.iOffsetToFirstBuffer = 100;
    attributes.iAlignment = 4;
    attributes.iContiguous = EFalse;
    attributes.iCacheAttrib = TCacheAttribute::ECached; 
         
    // Create two user-defined hints.
    RSurfaceManager::THintPair hints[2];
    hints[0].Set(0x124578, 25, ETrue);
    hints[1].Set(0x237755, 50, ETrue);
        
    attributes.iHints = hints;
    attributes.iHintCount = 2;
  3. Call RSurfaceManager::CreateSurface(const TSurfaceCreationAttributesBuf&, TSurfaceId&) to create the first surface.

    This function takes the surface creation attributes as input and returns the new surface ID.

    TSurfaceId surfaceId;
    TInt err = surfaceManager.CreateSurface(bf, surfaceId);
  4. Call RSurfaceManager::MapSurface(const TSurfaceId&, RChunk&) to map the surface.

    This function takes the surface ID as input and returns a handle to the shared chunk.

    if (err == KErrNone)
        {
        // We have a surface, so map it in.
        RChunk chunk;
        err = surfaceManager.MapSurface(surfaceId, chunk);
        if ( err == KErrNone)
            { 
            // Get info about it
            RSurfaceManager::TInfoBuf buf;
            RSurfaceManager::TSurfaceInfoV01& surfaceInfo = buf(); 
           
            surfaceManager.SurfaceInfo(surfaceId, buf); 
            }

Creating the second surface

An overload of RSurfaceManager::CreateSurface() allows you to supply a shared chunk as an input parameter. The shared chunk must be successfully allocated by the calling code before it is passed in to the surface creation API. We will use the shared chunk returned in the last step.

  1. Specify the surface creation attributes in a TSurfaceCreationAttributes class.

    // We have a surface, so create an another surface with a different             
    // orientation.
             
    RSurfaceManager::TSurfaceCreationAttributesBuf bf2;
    attributes = bf2();
             
    // Initialize the surface creation attributes.  
    attributes.iSize.iWidth  = 240; // Change the orientation .
    attributes.iSize.iHeight = 320;   
    attributes.iBuffers = 2;
    attributes.iPixelFormat = EUidPixelFormatARGB_4444;    
    attributes.iStride = 480;    // Width has changed to 240.
         
    // Read the offset to the first buffer from the first surface.
    TInt offset;
    surfaceManager.GetBufferOffset(surfaceId, 0, offset); 
    attributes.iOffsetToFirstBuffer = offset;
            
    // iCacheAttribute and iContiguous are not used for already existing
    // chunks.
  2. Call RSurfaceManager::CreateSurface(const TSurfaceCreationAttributesBuf&, TSurfaceId&, const RChunk&) to create a surface in the specified shared chunk. This overload takes the surface creation attributes and the handle of the existing chunk (returned in step 4 above) as inputs and returns the new surface ID.

    TSurfaceId surfaceId1;
    err = surfaceManager.CreateSurface(bf2, surfaceId1, chunk);    
    if (err == KErrNone)
        {
        // Surface created successfully.
        }