Guiliani  Version 2.6 revision 7293 (documentation build 12)
Rendering to multiple hardware graphic layers

Introduction

Some applications, particularly in the field of multimedia, require the graphical user interface to share the screen with other video sources. Many hardware platforms targeted at such markets will offer dedicated hardware support for these use-cases by allowing the software to render to several independent hardware graphics layers. Those will then be composited to form the final video image which will be visible to the user.

How to use Multi-Layer support

If your application needs to display additional content from different sources integrated into the GUI, you can use the CGUILayerContainer-control. In general the topic multilayer-support is often integrated into the hardware via so-called Display-Controllers. They maintain and display various contents on the display with advanced possibilities, like alpha-blending or chroma-keying. The access to these Display-Controllers is abstracted via the DC-Wrapper in Guiliani. The CGUILayerContainer-control uses the DC-Wrapper (CDCWrap) as part of Guiliani to achieve multi-layer.

To define and manage different layers containing data from different sources in your application, use the CGUILayerContainer which uses the internal CDCWrap to mix and display the contents on the screen. Simply place container-controls, where you want to display the contents, as you do with "normal" CGUICompositeObjects.

When using CGUILayerContainer, it is also possible to mix user-content and Guiliani-content inside the same layer. The user-content will always be displayed in the background and Guiliani-control on top of it.

Note: Since different layers are on the same hierarchy-level for the Display-Controller, it is not advisable - though possible - to encapsulate CGUILayerContainer into one another. The displayed result might not always be the one expected. The drawing-order displayed in the GSE or the PC-Simulation might be different from the platform-specific drawing-order of layers via the display-controller.

Technical Details

To understand how Multi-Layer works you need to know how Guiliani sees each layer.

In general a layer (sometimes called surface or canvas) is a region on the screen where objects are rendered independently from the rest of the screen. This region does not necessarily start at the origin 0,0 and does not need to have the size of the whole screen. It can be moved or blended with other contents of the screen to receive a modern impression of the GUI. Also this region has its own part of memory which is used to render and display the contents.

Each layer is represented by several attributes, like position, size, alpha, pixel-format, etc. In addition to this attributes the layer contains one or more buffers for the data. This is especially necessary for user-generated content which should be displayed on the screen - seamlessly embedded into the rest of the GUI. To see all possible attributes refer to LayerConfiguration_t.

Note: size and pixel-format of the layer can't be changed during runtime, because they are linked to the memory allocated for the internal buffers. visibility, position, alpha and chroma-key and be changed without any restrictions.

Before you can use a layer in your application, you need to create a configuration for this layer and add it to the Display-Controller-wrapper. This should be done during start-up of the application. After that the Graphics-Wrapper should be informed to use the DC-Wrapper to access the memory for rendering and refreshing the contents of the screen.

Here is an architectural overview of the involved parts of Guiliani

By using the Display-Controller-wrapper Guiliani can access these layers and blit the contents into the respective buffers. If multiple buffers are used, they are automatically flipped after drawing is finished.

If all visible layers have been processed, the contents are updated on the screen using the plaform-specific implementation of the DC-Wrapper.

How to setup different layers

When using layers, Guiliani and the used Graphics-API have to be aware of these. The setup should be done where the platform-specific initialization is done. The needed steps are:

  • initialize a DC-Wrapper suitable for the platform
  • create the configuration of the used layers
  • add each layer to the DC-Wrapper
  • initialize the Graphics-API and tell it to use the DC-Wrapper as the sub-layer

Here is a small example:

// define size, number and BPP
#define LAYER2_BUFFER_BPP 2
#define LAYER2_BUFFER_NUM 2
#define LAYER2_WIDTH 320
#define LAYER2_HEIGHT 240
#define LAYER2_PADDED_WIDTH ((LAYER2_WIDTH + 31u) & ~31u)
uint8_t Layer2[LAYER2_BUFFER_NUM][LAYER2_PADDED_WIDTH * LAYER2_HEIGHT * LAYER2_BUFFER_BPP] __attribute__ ((section(".VRAM_SECTION0")));
// create dc-wrapper with desired screen-size
CDCWrapRVAPI::CreateInstance(FRAME_BUFFER_WIDTH, FRAME_BUFFER_HEIGHT);
// create layer-configuration
kLayer2.m_uiLayerID = <ID>;
kLayer2.m_iXPos = 0;
kLayer2.m_iYPos = 0;
kLayer2.m_uiWidth = LAYER2_WIDTH;
kLayer2.m_uiHeight = LAYER2_HEIGHT;
kLayer2.m_uiPaddedWidth = LAYER2_PADDED_WIDTH;
kLayer2.m_ePixFormat = <PIXFORMAT>;
kLayer2.m_uiNumBuffers = LAYER2_BUFFER_NUM;
kLayer2.m_pkBuffers[0] = Layer2[0];
kLayer2.m_pkBuffers[1] = Layer2[1];
kLayer2.m_bCached = true;
kLayer2.m_ubAlpha = 255;
// add layer
GETDC.AddLayer(kLayer2);
// instantiate graphics-api to use dc-wrapper
CGfxWrapeGML::CreateInstance(&CDCWrapRVAPI::GetInstance());
static eC_Bool CreateInstance(CGfxEnv *pkGfxEnv)
Definition: GUILayerConfig.h:37
eC_Int m_iXPos
x-position
Definition: GUILayerConfig.h:146
eC_UInt m_uiPaddedWidth
padded width if platform needs this
Definition: GUILayerConfig.h:150
eC_UByte m_ubAlpha
alpha-value for layer
Definition: GUILayerConfig.h:151
CGUICommonEnums::ImageType_t m_ePixFormat
pixelformat
Definition: GUILayerConfig.h:140
eC_Int m_iYPos
y-position
Definition: GUILayerConfig.h:147
eC_UInt m_uiWidth
width
Definition: GUILayerConfig.h:148
eC_Bool m_bCached
buffers are cached, if platform needs this
Definition: GUILayerConfig.h:145
eC_UInt m_uiLayerID
ID to access layer.
Definition: GUILayerConfig.h:138
volatile void * m_pkBuffers[cuiMaxNumberOfBuffers]
buffer
Definition: GUILayerConfig.h:142
eC_UInt m_uiNumBuffers
number of buffers
Definition: GUILayerConfig.h:141
eC_UInt m_uiHeight
height
Definition: GUILayerConfig.h:149

Note: when setting up the layer configuration, the order of the layers may be important. On some platforms the IDs which are used to access the different layers, also set the drawing order in which all layers are merged to the final screen. If it happens that content of a specific layer is overdrawn by content from another layer, they may have an incorrect drawing order as set by their ID.

How to update and display User-Content

In the application above there are three layers, two of which contain visual data generated by Guiliani and one layer containing user-generated content (e.g. Video-Data)

To update the user-generated content you will need to access the currently active render-buffer. If there is only one buffer used for the layer, this is always the same buffer and also points to the currently displayed buffer. After the buffer-contents are updated, Guiliani needs to know that there were changes in order to update the layer correctly. This is done by a call to InvalidateLayer()

Note: when accessing memory, it is vital not to write over the boundaries of the buffers. To get the size of the connected buffer of a layer, use the appropriate methods of CDCWrap to prevent access-violation when writing data.

Note: some platforms need a certain padding of memory for faster access. if the used platform needs padding the allocated buffer-sizes will differ from the visible size. the member m_uiPaddedWidth of LayerConfiguration_t should be adjusted accordingly to have proper access to the memory on the platform. m_uiWidth will contain the visible width of the layer.

because of this difference the width returned by GetLayerBufferSize will be the padded width which was used to allocate the memory. if no padding is needed m_uiPaddedWidth and m_uiWidth of LayerConfiguration_t will be the same.

// get current render-buffer
void* pkLayer = GETDC.GetRenderBuffer(2);
if (NULL != pkLayer)
{
eC_UInt uiWidth;
eC_UInt uiHeight;
eC_UByte ubBPP;
// get buffer sizes
GETDC.GetLayerBufferSize(2, uiWidth, uiHeight, ubBPP);
memset(pkLayer, ubValue++, uiWidth * uiHeight * ubBPP);
}
// invalidate layer
GETDC.InvalidateLayer(2);

Of course, if the layer is not used to display both user- and Guiliani-content, it can be manipulated directly using the DC-Wrapper via code in the user-application.

  • position
  • alpha-value
  • visibility
  • memory-contents

Note: When a layer is used by a CGUILayerContainer, the direct access to change position via the DC-Wrapper-API should not be used, since the CGUILayerContainer will move the layer to the correct position by itself. This might lead to unwanted artefacts during display.

Create your own platform-specific DC-Wrapper

Sometimes it may be necessary to adapt the behaviour of the DC-Wrapper to specific needs on a platform. In this case it is possible to derive a custom-made CDCWrap and implement platform-specific logic.

To use this DC-Wrapper simply change the class-name of the used DC-Wrapper inside the startup-code.

The following methods can be implemented to change the internal behaviour:

  • void InitLayer(const LayerConfiguration_t& kLayerConfig)

This method is called for each layer with its configuration which is added to the DC-Wrapper. Here the DC-Wrapper should set up platform-specific data and call platform-functions to create layers.

  • void UpdateLayer(const LayerConfiguration_t& eLayerConfig, const LayerUpdate_t& eUpdateReason)

This is called if an attribute of a layer has been changed (position, alpha, etc.). Depending on the second parameter the DC-Wrapper can react to different changes. See LayerUpdate_t for details.

  • eC_Bool Refresh(const eC_UInt& uiLayerID, void* pkRenderBuffer, void* pkDisplayBuffer, const eC_UInt& uiWidth, const eC_UInt& uiHeight)

Will be called for each layer during the refresh. here the DC-Wrapper should call platform-functions to display the layer at the appropriate position with all applied attributes

  • void EndOfRefresh()

This method is called at the of the refresh-cycle. This can be used to trigger a synchronization-mechanism with the screen.