®
Improving Flash Performance
Amitt Mahajan, Zynga
SOCIAL SPEED
®
®
My Background
• Worked on Gears of War & Unreal Engine 3 at Epic
• Co-founder / CTO, MyMiniLife• Flash Virtual World, Acquired by Zynga in 2009
• Director of Engineering, Zynga• Co-creator & Lead Developer, FarmVille/Treasure Isle• Built the first prototype of CityVille
• Created ZEngine and ExampleVille, Zynga’s common game engine and game framework
®
®
High-Level Takeaways
Load time impacts new installs
Runtime performance impacts retention
Gather real data to know where to spend your time
Try to optimize as low-level as possible and build checks into your build process
Performance deteriorates over-time and needs to be continually re-examined
®
®
Loading
• Extended load-time causes people to navigate away from your game
• We define load-time as the period the player has to wait for the app to become interactive
®
®
Common Reasons for Loading Delays
• Downloading your game SWF
• Waiting for Facebook API to respond
• Round-trip for player data from game servers
• Asset size and number of assets
• Computation (depth-sorting, parsing XML, etc.)
®
®
Measuring Load-Times
• FireBug can be used for asset load measurement
• The Flash Builder profiler can give insight into where load-time computation is occurring
• Link-exports can determine why your SWF is a certain size
®
®
Using Link-Export
• Flash’s compiler provides a link-export option to export size breakdown of a SWF
mxmlc -link-report=C:\YourLinkReport.xml YourApp.as
®
®
Using Link-Export
• We use a tool* to visualize the link-report and identify assets to remove from the SWF
* http://www.kahunaburger.com/2008/03/08/air-link-report-visualizer/
®
®
Reducing Waits on Social Networks
• Social network data retrievals significantly impact load-times
• Ideally, don’t require social network data at all to load the game
• A compromise is to pre-fetch social network data server-side and then embed it into Javascript
®
®
Normal Facebook Data Flow
Your Canvas Callback
Initial Page Load
Render Flash HTML
apps.facebook.com/yourapp
users.getLoggedInUser()
friends.get()friends.get()
GAME SERVER (PHP) CLIENT (FLASH) FACEBOOK
®
®
Optimized Facebook Data Flow
Your Canvas Callback
Initial Page LoadFetch all Facebook Data
Render Flash HTML & Data JSON
apps.facebook.com/yourapp
users.getLoggedInUser()friends.get()
Retrieve User/Friend Data From JS JSON object
GAME SERVER (PHP) CLIENT (FLASH) FACEBOOK
®
®
Optimizing Initial Asset Load
• Set the bar high for what needs to be loaded at all
• Both asset size and number of assets matter
• Version assets and use correct HTTP headers to ensure that assets will be cached by the browser
Expires: Fri, 24 Feb 2012 03:06:45 GMTCache-Control: max-age=2592000
®
®
Optimizing Asset Size
• Use low-res assets first and stream in high-res assets later
• Pack PNG assets into SWF files for additional compression
PNG: 20kb SWF (50%): 4.2kb SWF (30%): 2.5kb
®
®
Asset Bundling
• Bundle similar assets together in a SWF to avoid a DNS look-up and HTTP connection round-trip for each asset
road.swfroad1.png road2.png
road3.png road4.png
road1 road2
road3 road4
Add assets to a SWF library and compile
®
®
Multiple Asset Domains
• HTTP spec has a request limit per domain, can get around this by having DNS aliases
FlashClient ++loadCount%4
assets0.farmville.com/pig.png
assets1.farmville.com/pig.png
assets2.farmville.com/pig.png
assets3.farmville.com/pig.png
Load pig.png
LoadingManager.load()
®
®
Pre-Computing Static Game Data
• XML & JSON can be expensive to parse and download
• Can pre-compile this data into Flash SWF files for download and processing savings
Raw Static Data (XML, JSON, Text,
etc.)
Auto-generated .as file that embeds data
in a Dictionary
Compressed .SWF file
Generate AS3 Code
MXMLC LoadedData
Download &Load Class
®
®
Runtime Performance
• Runtime performance is defined by perceived responsiveness of the application
• Sluggish performance hampers users enjoyment of the game
• Runtime performance issues also reduce interactivity
®
®
How Performance Affects Virality
0-4 5-8 9-12 13-16 17-20 21-24 25+
FPS
# Of Social Actions
®
®
Common Causes of Slow Performance
• Poorly authored assets• Vector instead of Bitmap, Excessive Animation
• Unoptimized game loops• Iterating over all game objects, excessive object
allocation/deallocation
• Flash VM CPU hogging• Expensive graphics filters, continually updating display list
®
®
Measuring Runtime Performance
• FPS is a good starting metric to make visible on dev builds
• The Flash Builder profiler is solid and the normal profile/fix issues cycle works great for AS3
• It is harder to see where draw-time is going, redraw regions help
®
®
Redraw Regions
• Redraw regions show you where your render time is going and can help identify problems
®
®
Authoring Assets
• Vector assets render slower than bitmaps
• Bitmap assets have larger file sizes than vector
• A hybrid-approach can give the best of both worlds
®
®
Farmville Pig
• This asset killed our frame-rate due to unnecessary vectors
*Vector images courtesy of Justin Church (byxb.com)
®
®
®
®
®
®
Vector / Bitmap Hybrid
• All assets are downloaded by the Flash client as vector
• At load-time, generate rasterized bitmap sprite sheets at the lowest possible resolution that avoids scaling
• If necessary, re-generate bitmaps at higher resolutions
®
®
Bitmap Sprite Sheets
• Alternative to vector-based animation• Uses fast pixel-based operations
CityVille_ConstructionWorker.png
®
®
No-Draw Sprite Sheets
• First, we pre-split the Bitmap
BitmapData.copyPixels()
Original Bitmap
One Bitmap Data Object per Frame
®
®
No-Draw Sprite Sheets
Instance of an Animated Object
Bitmap Object .bitmapData
Shared BitmapData Objects
®
®
Flash CPU Usage
• Flash can consumes CPU and slow down browser performance
• High CPU usage causes slow responses from social networks
®
®
Optimizing the Game Loop
• Have a single ENTER_FRAME event that drives game updates
• Split the updating of objects across frames
• Controlling the game loop also lets you control simulation speeds by adjusting time delta
®
®
Game Loop Flow
ENTER_FRAMEcallback
Game.updateWorld()Choose bucket in
round-robin fashion
Objects[0]
Objects[4]
Objects[8]
Objects[3]
Objects[7]
Objects[11]
Objects[1]
Objects[4]
Objects[9]
Objects[2]
Objects[6]
Objects[10]
Call .update(timeDelta) on each object in bucket
Bucket 0 Bucket 1 Bucket 2 Bucket 3
®
®
Screen Freezing
• Swap Flash with Static Bitmap to reduce CPU
Freeze background processing & replace with
BitmapFB.ui()
Un-freeze background processing & show Flash
again
®
®
Optimization Best Practices
• Optimization for social games is on-going, even post-launch
• Build-in detection of bad assets and code into your build process
• Have your client report back performance metrics, and actually use the data to fix issues
• Continually monitor your load-time and initial download size; They tend to increase over time