Search public documentation:
When developing Flash content for use with the Scaleform GFx integration in Unreal Engine 3, there are a number of considerations and optimizations that should be followed and implemented in order to get the best performance. This document details many of these best practices.
Draw primitives (DPs) are 3D mesh objects created by GFx to render 2D Flash elements, such as a group of shapes on the same layer. Each draw primitive is rendered independently, incurring a significant cost. In general, display performance will tend to decrease linearly as more DPs are introduced into the scene; therefore it is a good practice to keep the DP count as low as possible. The number of draw primitives can be determined via the GFxPlayer HUD by pressing the (F2) key, which brings up the AMP HUD summary screen. This screen displays triangle counts, DPs, memory use and other optimization information. The following are a few facts to help keep draw primitive count low:
- Gradient fills can increase the number of DPs if several of them are used in a shape at the same time.
- Vector graphics with solid fills and no strokes on the same layer are very cheap. Only one DP is required to represent any amount of these types of shapes on a single layer, even if those shapes use different colors.
- Each vector shape (or group of vector shapes) on its own layer requires one DP.
- Strokes are more expensive than fills, unless all strokes are the same solid color.
- Each dissimilar (different color) solid stroke on a layer adds one DP.
- Empty movie clips do not require a DP; however, a movie clip with objects in it will require the amount of DPs dictated by those objects.
- Alpha, Blends, and Transparency effects do not affect the number of DPs required; however, they will have an impact on rendering performance.
- Each bitmap/texture on the Stage requires one DP.
- Each text field requires at least one DP. Adding a border/background will add one more DP. Although in most cases all of text glyphs will be rendered from texture with one DP, large text may end up being rendered as vector shapes with each glyph using is a separate primitive. If clipping of vector glyphs is necessary then the text field will use a mask, which adds one more DP. Masks currently have a significant performance cost and clipped text fields will compound this penalty.
- Rather than hiding movie clips, it is best to delete them completely from the timeline when not in use, otherwise they may take up processing time during Advance.
- Avoid excessive nesting of movie clips, as this will affect performance.
- If it is necessary to hide a movie clip then use _visible=false rather than _alpha=0. Make sure you have stopped the animation in hidden movie clips by calling the "stop()" function. Otherwise, invisible animation will still take place and affect performance.
Flash content can be created with vector art as well as images and GFx can seamlessly render both vector and bitmap graphics. However, each type has its advantages and disadvantages. The decision to use vector or bitmap graphics is not always clear, and often depends on several factors. This section discusses some of the differences between vector and bitmap graphics to help make content authoring decisions. Vector graphics maintain their smooth shapes when scaled in size, unlike bitmaps images that can appear box-like, or pixelated, when scaled. But unlike bitmaps, vector graphics require more processing power to generate. Although simple solid-color shapes will usually be as fast as bitmaps, complex vector graphics with many triangles, shapes and fills can be expensive to render. Consequently, heavy use of vector shapes can sometimes reduce overall application performance. As a rule of thumb, any vector shape that generates more than 200 triangles is probably best converted to a bitmap. Bitmap graphics can be a better choice for some applications because they don't require as much processing time to render as vectors, however, bitmap graphics significantly increase the amount of memory required, compared to vector graphics.
Vectors graphics are more compact than other image formats because vectors define the math (points, curves, and fills) required to render the image at runtime rather than the raw graphic (pixel) data of a bitmap. However, converting the vector data to the final image is time consuming and must be done whenever there is significant change in appearance or scale of a graphic. If the movie clip contains complex shape outlines that change every frame, animations may run slowly. The following are several guidelines to help render vector graphics efficiently:
- Experiment with converting complex vector graphics to bitmaps and test how this affects performance.
- Keep the following in mind when using alpha blends.
- Solid-filled strokes are cheaper to compute than alpha-blended strokes, since they can use a much more efficient algorithm.
- Avoid using transparency (alpha). Flash must check all pixels underneath a transparent shape, which slows rendering down considerably. To hide a clip, set its _visible property to false rather than setting the _alpha property to 0. Graphics render fastest when their _alpha is set to 100. Setting the movie clip's timeline to an empty keyframe (so that the movie clip has no content to show) is usually an even faster option. Sometimes Flash still tries to render invisible clips; move the clip off stage by setting its _x and _y properties to a position off the visible stage, in addition to setting the _visible property to false, so Flash doesn't try to draw it at all. * Optimize vector shapes.
- In using vector graphics try to simplify the shapes as much as possible eliminating redundant points. This will reduce the amount of calculations that the player has to compute for each vector shape.
- Use primitive vectors including circles, squares, and lines.
- Flash's drawing performance is tied to how many points are drawn per frame. Optimize shapes with the Modify -> Shape submenu, then select either Smooth, Straighten or Optimize (depending on the graphic in question) to reduce the number of points required to draw it. This helps reduce the mesh data that is created by the GFx vector tessellation code.
- Corners are cheaper than curves.
- Avoid complex vectors with too many curves and points.
- Corners can be mathematically simpler to render than curves. When possible, stick with flat edges, especially with very small vector shapes. Curves can be simulated in this way.
- Use gradient fills and gradient strokes sparingly.
- Avoid shape outlines (strokes).
- Whenever possible, do not use strokes with vector shapes, because doing so increases the number of rendered lines.
- Outlines around vector images have a performance hit.
- Whereas a fill has only an outside shape to render, outlines have an inside and an outside to render. This requires twice as much work to draw a line over a fill.
- Minimize the use of Flash’s Drawing API. It may cause significant performance overhead if used unnecessarily. If needed, use the Drawing API to draw on a movie clip once. There are no performance penalties for rendering such a custom movie clip.
- Limit the use of masks. The masked pixels will still use up rendering time and have a negative impact on performance even though they are not drawn. Multiple masks compound the impact relative to the number of masks used. Note that in many cases the visual effect that artists use masks for does not require a mask. In particular, it is common to use a mask to cut a shape out of a bitmap. The same thing can be achieved much more efficiently by applying a bitmap fill to a shape directly in Flash Studio. This also provides the added benefit of Scaleform’s patent-pending EdgeAA anti-aliasing.
- Convert multiple objects into one shape whenever possible to avoid generating extra draw primitives.
- After a shape is created, it can be translated, rotated and blended with no additional memory use. However, bringing in new large shapes or doing significant scaling will consume more memory from tessellation.
- With EdgeAA enabled, shapes built out of multiple solid colors will render faster than those built out of multiple gradients/bitmaps. Caution is advised when connecting gradients/bitmaps within one shape, as this will cause the number of draw primitives to increase quickly.
The first step in creating optimized and streamlined animations or graphics is to outline and plan your project before its creation. Specify a target for the file size, memory usage and length of the animations that you want to create, and test throughout the development process to ensure that you are on track. In addition to draw primitives described earlier, a significant factor that affects rendering performance is the total surface area drawn. Every time a visible shape or bitmap is placed on the Stage, it needs to be rendered even if it is hidden by other overlapping shapes, consuming video card fill-rate. Although today’s video cards are an order of magnitude faster than software Flash, large overlapping alpha-blended objects on screen can still greatly reduce performance, especially on lower-end and older hardware. For this reason it is important to flatten overlapping shapes and bitmaps, and explicitly hide obscured or clipped-off objects. When hiding objects, it is best to set the _visible property of a movie clip instance to false instead of changing the _alpha level to 0 in a SWF file. Although GFx will not draw objects with _alpha value of 0, their children may still incur CPU processing cost due to animation and ActionScript. If the instance visibility is set to false, then there is potential for CPU cycles and memory savings, which can give your SWF files smoother animations and provide better overall performance for your application. Instead of unloading and possibly reloading assets, set the _visible property to false, which is much less processor-intensive. The following are several guidelines to help render bitmap graphics efficiently:
- Consider loading and unloading large bitmaps through ActionScript as needed.
- It is important to keep track of the number and size of image files being used in your UI. Use the 'stat gfx' command in UE3 to see how much memory is being used by GFx.
- Avoid overlapping big bitmaps since this affects fill-rate performance. Overlapping large areas of transparent bitmaps can also lead to bad performance.
- Import bitmap graphics at the size that they will be used in the application; don't import large graphics and scale them down in Flash, as this wastes file size and runtime memory.
When adding animation to an application, consider the frame rate of the FLA file. It can affect the performance of the final SWF file. Setting a frame rate too high can lead to performance problems, especially when many assets are used or AS is used to create animations that are ticked with the document’s frame rate. However, the frame rate setting also affects how smoothly the animation plays. For example, an animation set to 12 frames per second (FPS) plays 12 frames of the timeline each second. If the document's frame rate is set to 24 FPS, the animation appears to animate more smoothly than if it is set to 12 FPS. However, an animation at 24 FPS also plays twice as fast as it would at 12 FPS, so the total duration (in seconds) would be half the duration. Therefore, in order to make a 5-second animation using a higher frame rate, additional frames are required to fill those five seconds than at a lower frame rate, which raises the total file size. Note: When using an onEnterFrame event handler to create scripted animations, the animation runs at the document's frame rate, similar to creating a motion tween on the timeline. An alternative to the onEnterFrame event handler is setInterval. Instead of depending on frame rate, functions are called at a specified interval of milliseconds. Like onEnterFrame, the more frequently setInterval is used to call a function, the more resource intensive the animation will be. Use the lowest possible frame rate that renders a smooth animation at runtime. This will help reduce the performance hit on the processor. Try not to use a frame rate that's more than 30 to 40 FPS; high frame rates beyond this point increate CPU cost and do not greatly improve the animation smoothness. In most cases, Flash UI can be safely set to half the target frame rate of the underlying game. The following are several guidelines to help design and create efficient animations:
- The number of objects on the stage and how fast things move affect the overall performance.
- If there are a large amount of movie clips on the stage and they are required to switch on/off quickly, then _visible = true/false should be used to control their visibility instead of attaching/removing the movie clips.
- Pay close attention to the use of tweens.
- Avoid tweening too many items simultaneously. Reduce the number of tweens and/or sequence animation so that one begins when another ends.
- Use timeline motion tween instead of the standard Flash Tween class when possible because it has much less performance overhead.
- Scaleform recommends the use of CLIK Tween class (gfx.motion.Tween) instead of the standard Flash Tween class since it is smaller, faster and cleaner.
- Keep the framerate low, as the difference between high and low framerates are often not noticable. The higher the framerate, the smoother the animation, but the performance impact increases. A game running at 60 frames per second does not require a Flash file set to 60 FPS. The Flash framerate should be the minimum required to produce the necessary visual effects.
- Transparency and gradients are processor intensive tasks and should be used sparingly.
- Make the area of focus well designed and animated, then reduce animation and effects in other areas of the screen.
- Pause passive background animations (e.g., subtle background effects) during transitions.
- Test adding/removing animated elements to weigh their impact on performance.
- Use tween easing wisely. On slower hardware it can create an "appearance" of lag.
- Avoid shape morphing animations, such as transforming a circle into a square, as they are very CPU intensive operations. Shape tweens (morphing) have a very significant CPU hit because the shape is recomputed every frame; the cost of the hit will depend on the complexity of the shape (number of edges, curves and intersections). It may still be useable for some scenarios, but profile it to verify that the cost is acceptable; the cost of a four-triangle tween may be acceptable. Essentially, there are performance/memory trade-offs to be aware of. Displaying the regular shape causes tessellation and caching of the shape so that it is displayed efficiently in future frames. With a morph, the trade-off is changed, since any change in the shape causes the old mesh to be released and a new one created.
- The most efficient animations are translation and rotation. It is best to avoid scaling animations, as they may cause retessellation (which can have a noticeable performance hit) and the resulting mesh may consume more memory.
- Text glyph font sizes should be smaller than the font cache manager SlotHeight or the size planned to use with gfxexport (the default is 48 pixels). If a larger font is used, then vectors will be used and consequently be much slower because of many resulting DPs (each vector glyph generates a DP).
- Turn off the border and background of a text field if possible, since this will save one draw primitive.
- Updating a text field's content every frame is one of the biggest performance-sapping actions that can be easily avoided. Instead, change text field values only when their content truly changes or at the slowest rate possible. For example, when updating a timer that displays seconds, it is not necessary to update it at a frame rate of 30 FPS. Instead, record the old value and reassign the value of the text field only when the new value is different from the previous value.
- Do not use variables linked to a text field ("TextField.variable" property), since the text field will retrieve and compare the variable every frame, thus affecting performance.
- Minimize updating text by reassigning the “htmlText” property. Parsing HTML is a relatively expensive process.
- Use gfxexport with options –fc, -fcl, -fcm to compact fonts in order to save memory on glyph shapes (especially, if Asian fonts are embedded). See the “Font Overview” document for more details.
- Embed only the necessary symbols for fonts or use the fontlib mechanism if localization is necessary (again, please refer to the “Font Overview” document).
- Use the smallest number of TextField objects necessary, combining multiple items into one whenever possible. A single text field can typically be rendered with one DP even if it uses different colors and font styles.
- Avoid scaling text fields or using large font sizes; after a certain size the text field will switch to vector glyphs and each vector glyph becomes a draw primitive. If clipping is necessary (only part of a vector glyph is visible) then a mask will be used. Masks are slow and add an extra draw primitive. Clipping of rasterized glyphs does not require a mask.
- Make sure the glyph cache size is large enough to hold all (or most) used glyphs. If the cache size is not enough then some glyphs might disappear, or there will be a serious performance hit due to frequent rerasterization of glyphs.
- Using text effects such as blur, drop shadow or knockout filters require extra space in the font cache, and also affects performance. Minimize the use of text filters if possible.
- Avoid using text field underlining since this adds an extra draw primitive.