This section covers topics including reducing memory use, improving ActionScript performance, and improving overall performance with GFx.
We expect complex, full-screen SWF interfaces to use less than 1 MB of RAM, varying based on size of the input file and complexity of animation. It is possible to separate animation sequences and UI screens into multiple files loaded separately, keeping the memory use low. Flash files are designed to be very compact and efficient, making Scaleform GFx ideal for memory-constrained environments. Be aware of the following when creating files for optimum performance:
You may get these messages if memory was leaked by the ActionScript runtime in GFx 2.2 and below. GFx 2.2 and below doesn't have general garbage collection. However, this is not an issue for GFx 3.0 where we introduced garbage collection mechanism. For GFx 2.2 and below you will need to modify your data structures to avoid such situations.
Here is an example of code that will produce a leak unless one of the object references is explicitly disconnected:
Code:
var o1 = new Object; var o2 = new Object; o1.a = o2; o2.a = o1;
It is usually possible to rework the program logic by using hash table lookups/etc instead of direct references; however, there is no general easy fix for this situation. If you are having trouble identifying where the leak occurs we recommend commenting out your ActionScript logic a piece at a time until the problem goes away. Once it does, you will be able to identify a spot where a problem occurs.
Common things that may result in memory leaks include the use of singletons, as well as the use of standard Flash UI Components. We recommend that you modify your ActionScript logic and/or create custom components to resolve this issue.
A large user interface is typically best broken into separate Flash files. For example, an MMORPG’s trading room would be one Flash interface and the life-meter HUD would be another. On the ActionScript side, such different files can be loaded through loadMovie or MovieClipLoader APIs.
These functions enable the application to load only the interfaces necessary at any given time and free content when no longer needed. GFx supports multithreaded loading and playback, so loading background multiple Flash files will not adversely impact performance if extra cores of CPU cycles are available.
The base memory footprint of GFx is usually in the 700K–2M range for executable code, depending on included components and target platform. In addition to the exe code size, you will want to add the size of the dynamically allocated data, which depends on the content you load (including vectors and images), tessellation screen size and settings, and ActionScript allocations.
Realistic amounts of dynamic data necessary for game may fall in the following ranges:
Simple game HUD overlay: 400K–1.2M Startup/Menu screen with animations and several screens: 800K–4M Simple game fully in ActionScript with assets (Packman): 700K–1.5M Prolonged vector animation with constantly changing assets: 1M–10M+
The footprint may vary depending on the platform you're developing for (for example, GFx will add ~1.6M to your executable size on PS3 and Xbox 360, assuming release build and no RTTI/exception handling enabled for the GFx lib).
The GFx executable footprint can be reduced by not using the optional components such as JPEGLIB, ZLIP, LIBPNG and XML. JPEG and ZLIB are enabled by default, while PNG and XML libraries are not. Starting with GFx 2.2 references to JPEG and ZLIB can be removed by passing null to GFxLoader constructor instead of the default GFxJPEGSupport and GFxZLIBSupport interfaces. In the earlier SDKs, taking out these libraries required modification of GConfig.h and a recompile.
Texture memory is currently being used for text caching, gradients, and image textures. Since image texture use is infrequent in Flash, little texture memory is usually used. If you do use a lot of image textures, you can run the GFxExport tool to extract those images to TGA or DDS format (depending on whether you plan to use texture compression). After the images are generated, you can select all of the generated files to see their total size—this size will be very close to the actual amount of video memory used. In GFx 2.2, we plan on adding detailed statistics reporting that will allow you to examine what your memory is used for at runtime. Furthermore in GFx 3.0, all of the shape geometry will be cached in video memory buffers, which will account for the largest amount of video memory use. There will be an API to constrain the total video memory used to a fixed limit. Alternatively, we can also provide a series of callbacks that can be used to integrate video memory cache management with the rest of the engine.
The best way to do update multiple items is to first use SetVariableArray to pass all of the data to ActionScript and then call a single Invoke to apply all of this data. Similarly, if you need to obtain a large amount of data from ActionScript you can call GetVariableArray from within the fscommand handler. You should also structure your art assets and your C++ interface to support such batch updates efficiently.
There are three things you can do:
The block allocator is called by the standard allocator to allocate large chunks of memory that it will use for small allocations. These chunks are usually 1.5M to 2M in size (modifiable through the GStandardAllocator::Buddy_TableSize constant if you have source). Overriding the standard allocator directly will cause the block allocator to not be used. however, we recommend not doing so unless you need control over the small allocations.
The GDebugAllocator is a special allocator class that can track extra information, such as file and line number, about every allocation that takes place. It is only used in debug builds and relies on the other two allocators, but it adds extra memory checking and leak detection at the cost of debug-time overhead. You can override it if you need to do your own memory leak tracking and need the extra information.
Yes, with full source code you can easily provide your own memory management by simply overriding a small set of Alloc/Free functions. Many game developers do this, however, we do recommend comparing our allocator to your own, as we have worked hard to provide an optimized allocation system.
You can replace the block allocator using the GMemory::SetAllocator method. If you decide to use our block allocator, you can change the block sizes it reserves by changing GStandardAllocator::Buddy_TableSize constant in GStandardAllocator.h (doing so requires licensed full-source access). Our allocator uses a buddy system that has an advantage of high performance and low memory fragmentation, at the cost of ~20% extra memory use for block rounding.
With the final version of GFx 2.2 we plan on adding a more compact and convenient memory interface, with detailed control of alignment, dedicated heaps to handle fragmentation, and full memory reporting. With GFx 3.0, developers will also be able to set fixed limits on tessellation pools so data is discarded and regenerated on demand to keep memory low.
To get the rendering triangle information / draw primitive (DP) count you need to create the GRenderer::Stats object from GRenderer, as done in FxPlayerWin32.cpp.
Code:
GRenderer::Stats renderStats; pRenderer->GetRenderStats(&renderStats,true);
GetRenderStats call obtains the running statistics, resetting them to zero based if the second argument is true. If you query rendering statistics between each GFxMovieView::Display call, you can obtain data separately for each movie view. Note that these counts are displayed in the GFxPlayer HUD if you press the F2 key.
For memory, you can install your own allocator with tracking as done in FxPlayerWin32.cpp when FXPLAYER_MEMORY_TRACKSIZES is defined. For our standard allocators, you can also query the memory allocation statistics by using the GMemory::GetAllocatorStats call.
The above call will only report the total memory used by GFx; there is currently no way to obtain memory usage per file/movie view. Some customers have implemented custom tracking per movie by wrapping all GFx calls in the tracker (e.g., CreateMovie, CreateInstance, Invoke, Advance, Display, SetVariable); however, that requires quite a bit of workWe are significantly improving our memory tracking, beginning with GFx 2.2.