Graphics rendering is generally divided into hardware-accelerated rendering and software rendering. In hardware-accelerated rendering, the rendering is performed by a dedicated graphics accelerator (Graphics Processing Unit, GPU), while in software rendering the rendering is performed by the device main processor (Central Processing Unit, CPU). Switching between GPU and CPU rendering involves synchronizing pixel data between these two processing units, which in practice means pixel data transfer.
Since Symbian^3, transferring data from the GPU to CPU or the other way around degrades performance, since the processing units do not share the same memory. Avoid data transfers between the GPU and CPU if possible.
Figure: Graphics memory architecture
Symbian^3 onwards rendering for Java APIs is performed in the following ways:
LCDUI
eSWT
Scalable 2D Vector Graphics
Mobile 3D Graphics
Nokia UI API
LCDUI Graphics
implementation is based on software
rendering (CPU). All graphics handled in the rendering commands (draw,
fill, and copy methods of Graphics
) are rasterized
to a bitmap in CPU memory. This applies to all targets of Graphics
, namely Canvas
, GameCanvas
, Image,
and CustomItem
. At the end of a Canvas
, GameCanvas
, or CustomItem
paint cycle, the bitmap is transferred from CPU to GPU.
It
is faster for a MIDlet to use the drawRGB
method
once and pass a larger RGB data array than for it to call the method
several times, passing a smaller data array.
GC
(org.eclipse.swt.graphics.GC
) drawing to eSWT Controls
is hardware-accelerated. GC
drawing to eSWT Images
uses software
rendering.
In case an eSWT application needs to intensively
draw Images
to Controls
,
it is recommended that it uses immutable Images
, since they provide better performance than mutable Images
. Image
pixel data is stored in the CPU memory,
and is transferred and cached to the GPU memory when Image
is drawn for the first time. Pixel data transfer is avoided on redraw
if Image
has not been modified. Modifying the Image
forces pixel data transfer to GPU again on redraw.
Scalable 2D Vector
Graphics API (M2G) uses software rendering. For all targets
(Graphics
of Canvas
, GameCanvas
, Image,
and CustomItem
) M2G content is rasterized to a bitmap in the
CPU memory.
The Mobile 3D Graphics
API (M3G) is fully on hardware accelerated and uses hardware-accelerated
rendering surfaces instead of bitmaps. Due to the fact that Graphics
and M3G use different types of rendering targets,
the LCDUI Canvas
implementation supports a hardware-accelerated
rendering surface when M3G is used for rendering on Canvas
(see "2D and 3D mixed" in the following figure). The main motivation
is to minimize the need for pixel data transfer between CPU and GPU,
and to allow a pure M3G use case to achieve optimal performance. The
rendering target type, hardware-accelerated surface or bitmap, is
automatically selected based on the APIs (Graphics and/or M3G) used
by the MIDlet when painting on Canvas
. The following
figure illustrates these scenarios.
Figure: Rendering scenarios
It is visible from the figure that best performance is achieved
using only Graphics
or M3G. Using only M3G gives
full hardware acceleration benefits and is the most optimal use case.
For more information, see the following examples on how a game scene
background can be rendered efficiently with using only M3G.
Clearing background with M3G
When a MIDlet renders
M3G content, use Background
for the following purposes
to ensure maximum performance:
Clearing the Canvas
area
Drawing a 2D image as a background for M3G content
Avoid using Graphics
methods for the
use cases, since they result in decreased performance. The following
code snippet shows the wrong and correct way for clearing the Canvas
area.
Graphics3D myG3D = Graphics3D.getInstance(); // // Wrong: using Graphics methods drops performance // protected void paint(Graphics g) { // Clear Canvas with Graphics.fillRect(), // poor performance in 2D + M3G mixing case g.setColor(0x00000000); g.fillRect(0, 0, getWidth(), getHeight()); myG3D.bindTarget(g); // ... render the scene ... myG3D.releaseTarget(); } // // Ok: M3G Background used for clearing Canvas // Background myBackground = new Background(); myBackground.setColor(0x00000000); myBackground.setColorClearEnabled(true); protected void paint(Graphics g) { myG3D.bindTarget(g); // Clear Canvas with Background object, // no performance penalty myG3D.clear(myBackground); // ... render the scene ... myG3D.releaseTarget(); }
The following code snippet shows the wrong and correct way for drawing a 2D background image.
Graphics3D myG3D = Graphics3D.getInstance(); // // Wrong: using Graphics methods drops performance // Image myImage = Image.createImage("/image.png"); protected void paint(Graphics g) { g.drawImage(myImage, 0, 0, Graphics.TOP | Graphics.LEFT); myG3D.bindTarget(g); // ... render the scene ... myG3D.releaseTarget(); } // // Ok: M3G Background used // Image myImage = Image.createImage("/image.png"); Image2D myImage2D = new Image2D ( Image2D.RGB, myImage); Background myBackground = new Background(); myBackground.setImage(myImage2D); protected void paint(Graphics g) { myG3D.bindTarget(g); myG3D.clear(myBackground); // ... render the scene ... myG3D.releaseTarget(); }
Optimizing Graphics and M3G mixing
3D
Java games commonly mix M3G and Graphics
rendering.
A typical use case is to render a game scene with M3G and use Graphics
content on top of the scene. For example, the
content drawn with Graphics
can include game
score and status information in upper part of the Canvas
and Command
labels at the bottom of Canvas
. For this kind of a use case the Canvas
(and GameCanvas
) implementation optimizes the
quantity of pixels drawn with Graphics
that need
to be transferred from a bitmap to a hardware-accelerated surface.
Canvas
implementation internally uses two
rectangular areas to identify which parts of the Canvas
surface need to be updated with Graphics
drawn
content. The first rectangle is dynamically resized so that it is
the smallest rectangular area that covers all Graphics
rendering in upper half of Canvas
. The second
rectangle is dynamically resized so that it is the smallest rectangular
area that covers all Graphics
rendering in lower
half of Canvas
.
The red boxes in the following
figures show how Canvas
resizes its rectangles
in mixed M3G and Graphics
rendering. In the Figure: Update rectangles the MIDlet first renders M3G to the
entire Canvas
area and then uses Graphics.drawString()
to add text on top. Iin Figure: Upper rectangle expanded , the upper rectangle is expanded to cover text in the middle part
of the Canvas
. This causes a considerably larger
area to be converted to a hardware-accelerated surface. The best performance
in mixing can be achieved by rendering with Graphics
only to the smallest possible areas at the top and/or bottom of
the Canvas
.
Figure: Update rectangles
Figure: Upper rectangle expanded
In cases where an Image
needs to be
drawn frequently to Canvas
, the best performance
is achieved by using immutable Image
. This allows
the implementation to cache image data in graphics memory, enabling
faster redraws.
Rendering M3G to image targets
The M3G API is fully hardware-accelerated and always renders to hardware-accelerated
surfaces in the GPU memory, whereas LCDUI Images
reside in CPU memory. When Image
is used as
M3G rendering target, the CPU memory needs to be synchronized with
the GPU each time M3G rendering starts and ends, that is, when Graphics3D.bindTarget
and Graphics3D.releaseTarget
are called. This makes Image
as a M3G rendering
target slower than Canvas
. Because of this, using
mutable Image
for example as a game MIDlet back
buffer is not recommended.
MMAPI video and M3G content on Canvas
If there is MMAPI video and M3G content on Canvas
at the same time, Canvas
is
not able to use its hardware accelerated surface for M3G rendering.
In this case Canvas
equals to Image
as a M3G rendering target. This means that the CPU memory needs
to be synchronized with the GPU memory every time M3G rendering starts
and ends. This might cause the overall user interface performance
to be poor.
The Nokia UI API uses software rendering. DirectGraphics.getPixels
is slow if used for Canvas
with M3G content, as synchronization is needed
between CPU and GPU memory.
Due to the limited amount of graphics memory in devices, MIDlets have to stop rendering when switching to the background, that is, when another application moves to the foreground. This allows the implementation to free all used GPU resources. If a MIDlet fails to stop rendering when moving to the background and graphics memory is running too low, the system terminates the MIDlet as a last resort to free resources, enabling it to serve the foreground application. This is especially important with MIDlets using M3G, because in general 3 D graphics require a considerable amount of graphics memory.
If a MIDlet has a separate
thread for triggering Canvas
repaints or performing GameCanvas
rendering, the MIDlet needs to stop the thread
when Canvas.hideNotify()
is called.
Use only Graphics or M3G, if possible.
If mixing Graphics and M3G is needed, limit the number of switches between Graphics and M3G within a paint cycle.
Use M3G for clearing the scene background.
Use immutable Images
instead of mutable Images
.
Stop rendering when a MIDlet is in the background.