Amitt Mahajan discusses techniques used by Zynga in FarmVille and CityVille to improve the loading and runtime performance of their games.
(Originally presented at GDC 2011)
2. 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
3. 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
4. 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
5. 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.)
6. 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
7. Using Link-Export Flash’s compiler provides a link-export option to export size breakdown of a SWF mxmlc -link-report=C:ourLinkReport.xmlYourApp.as
8. 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/
9. 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
10. Normal Facebook Data Flow GAME SERVER (PHP) CLIENT (FLASH) FACEBOOK Your CanvasCallback apps.facebook.com/yourapp Initial Page Load users.getLoggedInUser() Render Flash HTML friends.get() friends.get()
11. Optimized Facebook Data Flow GAME SERVER (PHP) CLIENT (FLASH) FACEBOOK Your CanvasCallback apps.facebook.com/yourapp Initial Page Load Fetch all Facebook Data users.getLoggedInUser() friends.get() Render Flash HTML & Data JSON Retrieve User/Friend Data From JS JSON object
12. 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 GMT Cache-Control: max-age=2592000
13. 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
14. Asset Bundling Bundle similar assets together in a SWF to avoid a DNS look-up and HTTP connection round-trip for each asset road.swf road1.png road2.png road1 road2 Add assets to a SWF library and compile road4.png road3.png road4 road3
15. Multiple Asset Domains HTTP spec has a request limit per domain, can get around this by having DNS aliases assets0.farmville.com/pig.png LoadingManager.load() ++loadCount%4 assets1.farmville.com/pig.png Load pig.png Flash Client assets2.farmville.com/pig.png assets3.farmville.com/pig.png
16. 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 Loaded Data Generate AS3 Code Download& Load Class MXMLC
17. 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
19. 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
20. 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
21. Redraw Regions Redraw regions show you where your render time is going and can help identify problems
22. 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
23. Farmville Pig This asset killed our frame-rate due to unnecessary vectors *Vector images courtesy of Justin Church (byxb.com)
24.
25.
26. 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
27. Bitmap Sprite Sheets Alternative to vector-based animation Uses fast pixel-based operations CityVille_ConstructionWorker.png
28. No-Draw Sprite Sheets First, we pre-split the Bitmap Original Bitmap BitmapData.copyPixels() One Bitmap Data Object per Frame
29. No-Draw Sprite Sheets Shared BitmapData Objects Instance of an Animated Object Bitmap Object .bitmapData
30. Flash CPU Usage Flash can consumes CPU and slow down browser performance High CPU usage causes slow responses from social networks
31. 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
32. Game Loop Flow ENTER_FRAME callback Game.updateWorld() Choose bucket in round-robin fashion Bucket 0 Bucket 1 Bucket 2 Bucket 3 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
33. Screen Freezing Swap Flash with Static Bitmap to reduce CPU Freeze background processing & replace with Bitmap FB.ui() Un-freeze background processing & show Flash again
34. 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