Creating a Graphics Resource Adaptation

This topic provides information about creating a graphics resource adaptation.

Target audience: Device creators.

Note: The Graphics Resource and Graphics Resource Adaptation components are deprecated in Symbian^3 and will be removed in Symbian^4 (S^4). However, new Graphics Resource Interface and Implementation components are planned for S^4. The new interface component will provide a similar but reduced API that is optimized for sharing images across processes.

Required background

This topic assumes an understanding of the material contained in the following:

Packaging the DLL

When packaging the adaptation, the following rules apply:

  • The graphics resource adaptation must be packaged as a DLL with the name graphicsresourceadapter.dll.

  • The second and third UIDs must be 0x1000008D and 0x10285A71, respectively.

  • All capabilities except TCB (Trusted Computing Base) are required.

  • The NOEXPORTLIBRARY keyword must be used in the MMP file to prevent the generation of LIB/DSO files during the build process.

Here is a snippet of an example MMP file:

TARGET graphicsresourceadapter.dll
UID 0x1000008D 0x10285A71
VENDORID <Your vendor ID in hex>

The reference adaptation component has one implementation for the emulator and several implementations for different hardware configurations. These latter ones have different names and the one that is to be used in the device is renamed to graphicsresourceadapter.dll during the ROM building process. You may want to use a similar approach. See Selection of Adaptations for more information.

Because the Graphics Resource Adaptation component is optional, the Graphics Resource component provides a stub implementation for platform security reasons. When a functional adaptation is not present, the stub implementation is compiled into a DLL called graphicsresourceadapter.dll and is installed in the ROM in order to prevent a SIS file installing a rogue DLL with this name.

The exported function

The graphics resource adaptation DLL is a polymorphic interface DLL that must export a single function at ordinal position one. The function that must be exported is MSgDriverAdapter::New(). Here is its definition from the stub implementation:

EXPORT_C TInt MSgDriverAdapter::New(MSgDriverAdapter*& /*aPtr*/)
    return KErrNotSupported;

This simply returns KErrNotSupported to indicate that the graphics resource functionality is not supported. This is because, as mentioned above, the stub implementation is used for platform security reasons when no functional Graphics Resource adaptation is present.

In a functional implementation, the function must create a multi-threaded heap, allocate from it and initialize a new instance of the singleton class that implements the MSgDriverAdapter interface and return it in the pointer argument.

Implementing the interfaces

The Graphics Resource Component provides a Hardware Adaptation Interface (HAI), which separates the generic and adaptation parts. This defines a set of interfaces for which the adaptation must provide an implementation. As long as the implementation conforms to the interface specifications and contracts, you are free to write the adaptation as you see fit.

The following is a class diagram of the interfaces.

Figure 1. Figure 1: The graphics resource adapter interfaces

Driver adapter. You must provide a concrete driver adapter class that implements the MSgDriverAdapter interface. This is a singleton class. A single instance of it is created for each process that uses the Graphics Resource API. The singleton is responsible for creating image adapter and image collection adapter objects.

Image adapter. You must provide at least one concrete image adapter class that implements the MSgImageAdapter interface. You can provide as many concrete image adapter classes as needed. The driver adapter singleton creates an instance of the appropriate image adapter class for each image that is created or opened by the process.

There are three different ways in which an image adapter object can be created:

  • A call to MSgDriverAdapter::CreateImage(), which creates an image and its image adapter object.

  • A call to MSgDriverAdapter::OpenDrawable(), which opens an existing image for which the ID is known. This also creates an image adapter object for the image if one does not already exist within the process. This function enables images to be shared between processes. Note that the driver adapter singleton does not create image adapter objects in a process (A) for images that are created by another process (B) if they are not opened in process A.

  • A call to MSgImageCollectionAdapter::OpenImage(), which creates an image adapter object to encapsulate a surface buffer belonging to an existing image collection.

The implementation of these functions must use the shared multi-threaded heap owned by the driver adapter singleton for all allocations and perform any initialization that is required.

Image collection adapter. You must provide a concrete image collection adapter class that implements the MSgImageCollectionAdapter interface. The driver adapter singleton creates an instance of this class for each image collection that is created by the process.

The purpose of the image collection class is to provide interoperability with the composition engine and Surface Update Server. Therefore, unlike image adapters and platform-specific resource adapters, which you are free to implement as you see fit, you must implement the image collection adapter in terms of surfaces that are compatible with the Surface Manager. The implementation of the MSgImageCollectionAdapter::SurfaceId() function must return the surface ID of the image collection's surface.

There are two different methods that create image collections:

  • MSgDriverAdapter::CreateImageCollection(). This creates a single image collection and its associated image collection adapter object.

  • MSgDriverAdapter::CreateImageCollections(). This creates a number of image collections that share a single memory chunk (in order to save memory if only one of the image collections will be in use at a time). Although they share a memory chunk, each of these image collections has its own surface ID and image collection adapter object. This function also creates the associated image collection adapter objects.

The implementation of these functions must use the shared multi-threaded heap owned by the driver adapter singleton for all allocations and perform any initialization that is required.

For interoperability with applications that expect images, you must provide an implementation of MSgImageCollectionAdapter::OpenImage(). This wraps up a surface buffer as an image by creating a suitable image adapter object for it.

Resource types

The Symbian reference adaptation provides only one type of resource—the image (2D pixel buffer). However, you can define additional types of resources—for example, for handling vector data. A UID is used to identify the resource type. The KSgImageTypeUid constant represents the UID for images. This is defined as follows:

const TUid KSgImageTypeUid = {0x10285A73};

If you create a new resource type, you must obtain a UID from Symbian Signed to represent its runtime type. The implementation of the MSgDrawableAdapter::DrawableType() function must return the UID of the resource type.

Related to the resource type is the handle type. This constrains the type of resource that can be opened by calls to the MSgDriverAdapter::OpenDrawable() function. The implementation of this function must check that the actual runtime type of the resource to be opened is compatible with the handle that will contain a reference to it. The function must return an error if the runtime resource type is not compatible with the specified handle type.

Two handle types are defined by Symbian:

  • The image handle type, which can contain a reference to an image resource only. This has the same UID as the image resource type (mentioned above).

  • The drawable handle type, which can contain a reference to an image or any platform-specific resource type. This is defined as follows:

    const TUid KSgDrawableTypeUid = {0x102858EB};

Reference counting

All of the adapter objects apart from the driver adapter singleton must be reference counted as follows:

  • Each time that an adapter object is created, its reference count must be set to one.

  • For resources that can be shared between processes, the implementation of MSgDriverAdapter::OpenDrawable() checks for the existence of the resource adapter object in the process in which the resource is to be opened. If the adapter object exists in that process, the implementation increments its reference count. If the adapter object does not exist in that process, the implementation creates one and sets its reference count to one.

  • All reference-counted adapter objects are destroyed in the same way—the implementation of the Close() function decrements the reference count and destroys the adapter object when the reference count reaches zero. When the adapter object is destroyed, the reference count on the underlying resource must also be decremented and the underlying resource must be destroyed when its reference count reaches zero.

    The Close() function is part of the MSgResourceAdapter interface, from which MSgImageCollectionAdapter and MSgDrawableAdapter (and ultimately MSgImageAdapter) inherit.

The following diagram shows an image that is shared between two processes—one of which has two handles to the image and the other a single handle. When all of the handles to the image are closed, the image itself is destroyed.

Figure 2. Figure 2: Reference counting resources and adapter objects

Thread safety

All of the adapter objects can potentially be shared between all of the threads in the process. This means that they must all be thread safe. Normally you implement thread safety by using mutexes. The reference implementation handles this by using a single mutex per process to synchronize access to all of the adapter objects in the process.

When running on multiple processors, such as symmetric multiprocessing (SMP), using a separate mutex for each adapter object might provide a more efficient solution. This approach can improve performance by avoiding unnecessary serialization but it has drawbacks. For example, each additional mutex increases the memory usage. In addition multiple mutexes are more complex to program and require very careful program design in order to prevent the occurrence of deadlocks and other synchronization problems.

All of the adapter objects, including the driver adapter singleton, can potentially be created in the context of one thread and destroyed in the context of a different thread. Therefore they must be allocated from a multi-threaded heap shared between all the threads in the process and owned by the driver adapter singleton.

Extension interfaces

The MSgDrawableAdapter::GetInterface() function provides an optional mechanism for platforms to extend the HAI by defining extension interfaces. These can be accessed by using the RSgDrawable::GetInterface() method, which delegates to MSgDrawableAdapter::GetInterface().