{"author_link":"\/users\/daanvanyperen","author_name":"daanvanyperen","author_uid":"daanvanyperen","comments":[],"epoch":1495383536,"event":"LD38","format":"md","ldjam_node_id":31981,"likes":9,"metadata":{"p_key":"94273","p_author":"daanvanyperen","p_authorkey":"1006977","p_urlkey":"307191","p_title":"Little Fortune Planet Bonus Breakdown - Part 7: LibGDX\/GWT performance tuning","p_cat":"LDJam ","p_event":"LD38","p_time":"1495383536","p_likes":"9","p_comments":"0","p_status":"WAYBACK","us_key":"1006977","us_name":"daanvanyperen","us_username":"daanvanyperen","event_start":"1492732800","event_key":"69","event_name":"LD 38"},"node":{"_collation":{"body_sanitizer":"TextUtils::SanitizeHTML via existing importer","event":"LD38","removed_author":false},"_superparent":9405,"_trust":1,"author":6977,"body":"[As mentioned before](https:\/\/ldjam.com\/events\/ludum-dare\/38\/little-fortune-planet\/little-fortune-plane-breakdown-part-1-brainstorming) **Little Fortune Planet was a bit of an experiment with non trivial, non optimized game design using LibGDX and GWT**. Loads of **nested loops** O(n2), and **dynamic textures**. The game ran horribly **slow on web**, so we set out **improving performance post jam**. \n\nDisclaimer: **Food for LibGDX\/GWT devs**, but still got some **meaty bits for others**. We used [LibGDX](https:\/\/libgdx.badlogicgames.com\/index.html), a java game development framework that targets a wide variety of platforms. Keeping a couple of *gotchas* in mind, it [transpiles](https:\/\/en.wikipedia.org\/wiki\/Source-to-source_compiler) your Java game (using Google Web Toolkit) to JavaScript fairly effortlessly. While the tooling is pretty good, it can be a real pain to profile.\n\n### Goals\n\n**Ideally the game would run at a steady 60 fps**. Let's see what is eating all our performance. If you are using [artemis-odb](https:\/\/github.com\/junkdog\/artemis-odb), grab the excellent [profiler plugin](https:\/\/github.com\/DaanVanYperen\/artemis-odb-contrib\/wiki\/Profiler-Plugin) made by [@piotr-j](https:\/\/github.com\/piotr-j) for low cost profiling in game.\n\n![profiler.png](\/\/\/raw\/14b\/1\/z\/48ef.png)\n\n**A target of 60 fps means we have ~16ms to spend per frame**. Currently on my high end machine **frames take at least double that to render on Chrome. Firefox and IE are even worse**. Throughout optimizing Firefox generally seemed to struggle with our game.\n\n### Profiling Your (LibGDX) Web Game\n\n**So how to proceed? We'll run Chrome's built in profiler (F12 - Performance tab)**. Firefox and IE dev tools have similar features, so use whichever you prefer. Let's see what a couple of seconds of record button yields:\n\n![obfuscate.png](\/\/\/raw\/14b\/1\/z\/48f0.png)\n\nOops! Looks like **our function names have been obfuscated**. We could use Google Web Toolkit's superdev mode to solve this for us, as it provides bridge between your Java naming and the browser dev tools, but since I'm not 100% sure on the side effects on profiling, **we'll keep things vanilla and disable the obfuscator instead**.\n\nAdd style PRETTY or DETAILED to ```html\/build.gradle``` in your libgdx project.\n\n```java\n    import de.richsource.gradle.plugins.gwt.Style\n    compiler {\n        optimize = 0\n        style = Style.DETAILED\n    }\n```\n\nAfter redeploying the game and rerunning the profiler we get:\n\n![profiler2.png](\/\/\/raw\/14b\/1\/z\/48f1.png)\n\n**Now we can read the function names! We'll run the profiler for a couple of seconds, and then sort 'Bottom-Up' to find hotspots in our code**. In our case **we're interested in functions that have a large amount of 'self-time', these functions are eating all our valuable frame time**.\n\n**In our game we've identified two possible areas for improvement: Simulating and rendering the planet.**\n\n## Improving Simulation Performance\n\n**Improving performance is relatively straightforward. Find the functions with the highest self-time and the least amount of effort to reach your set end goal.**\n\n![rngisevil.png](\/\/\/raw\/14b\/1\/z\/48f3.png)\n\nIn this case it seems **the GWT random number generation is the culprit of bad performance**. We use RNG heavily throughout the simulation; What direction does water flow this tick, where does stream spawn, etc. Since all simulation logic is run within a loop, this ends up being extremely expensive.\n\nWe could limit the uses of RNG but we don't want to rewrite everything or break the sim, so instead lets just **[precalculate our random numbers](https:\/\/xkcd.com\/221\/)** and see if we can **fake it**!\n\n\n```java\npublic class FauxRng {\n\n    private static final int PREGEN_COUNT = 10000;\n    private static int[] pregen = new int[PREGEN_COUNT];\n    private static int cursor = 0;\n\n    static {\n        Random random = new Random();\n        for (int i = 0; i < PREGEN_COUNT; i++) {\n            pregen[i] = random.nextInt(Integer.MAX_VALUE);\n        }\n    }\n\n    \/**\n     * Returns a random number between 0 and end (inclusive).\n     *\/\n    public static int random(int range) {\n        return nextInt(range + 1);\n    }\n\n    private static int nextInt(int i) {\n        cursor = (cursor + 1) % PREGEN_COUNT;\n        return pregen[cursor] % i;\n    }\n\n    \/**\n     * Returns a random number between start (inclusive) and end (inclusive).\n     *\/\n    static public int random(int start, int end) {\n        return start + nextInt(end - start + 1);\n    }\n\n    public static boolean randomBoolean() {\n        return nextInt(2) == 1;\n    }\n}\n```\n\nDon't show my boss.\n\nBesides this change we **limit the neighbor checks needed for each cell where possible**. There are also some things that won't hurt the simulation much if they are **run less often**, like **updates to the heat map**.\n\n## Improving Rendering Performance\n\nOn LibGDX **dynamic textures are generated using a Pixmap**, which is **fast on desktop, but slow as a snail on the \nbrowser**. Lets **check LibGDX's Pixmap implementation on web platform** to check out what is so costly about this.\n\n ```java\n        this.ensureCanvasExists();\n        if(this.blending == Pixmap.Blending.None) {\n            this.context.setFillStyle(clearColor);\n            this.context.setStrokeStyle(clearColor);\n            this.context.setGlobalCompositeOperation(\"destination-out\");\n            this.context.beginPath();\n            this.context.rect((double)x, (double)y, (double)width, (double)height);\n            this.fillOrStrokePath(drawType);\n            this.context.closePath();\n            this.context.setFillStyle(this.color);\n            this.context.setStrokeStyle(this.color);\n            this.context.setGlobalCompositeOperation(Composite.SOURCE_OVER);\n        }\n\n        this.context.beginPath();\n        this.context.rect((double)x, (double)y, (double)width, (double)height);\n        this.fillOrStrokePath(drawType);\n        this.context.closePath();\n        this.pixels = null;\n```\n\n**Yikes! Pixmap is backed by a browser canvas. Since we draw tens of thousands of pixels, a fat loop won't perform very well as a tight one would.**\n\n**If we could push an array of color data straight into the canvas it might perform better**. Google Web Toolkit allows you to directly hook into Javascript, [so lets write a little function to feed the canvas our pixel buffer](https:\/\/github.com\/DaanVanYperen\/odb-little-fortune-planet\/blob\/master\/html\/src\/game\/emu\/net\/mostlyoriginal\/game\/system\/planet\/MyScreenUtils.java). **This ended up fixing rendering times to 4ms on Chrome, but Firefox still struggled. Dang!**\n\n## Take a Break or Educate!?\n\n**Ultimately reading the google scriptures on the matter would help**, but I **tend to end up with too many browser tabs of interesting topics and no work done**. Sometimes taking a short break and giving your mind a rest can be as effective; got an idea! **Lets try something else**.\n\n**Since only a small amount of particles in our sim change each frame**, why not **keep track of those and only do the expensive pixel plotting calls when we need to?** We use an in-memory buffer outside of the GPU to track pixel changes, and plot changed pixels to a persistant ```FrameBuffer``` in GPU memory. \n\nDelta's **visualized looks something like this**:\n\n![5921aa9170f27591622802.gif](\/\/\/raw\/14b\/1\/z\/48f2.gif)\n\n**Success! We managed to drop rendering time to 1-4ms on our target browsers.**\n\n## Final Results\n\n**We've managed to get the time to generate a frame down to about 6-10ms, which is pretty good. We still hit the 30ms sometimes but overall the simulation is pretty smooth.**\n\n![muchbetter.gif](\/\/\/raw\/14b\/1\/z\/48fa.gif)\n\n**Wanna try it out yourself? [You can play the sandbox version here (WEB)](http:\/\/www.mostlyoriginal.net\/games\/littlefortuneplanetsandbox\/). Press P to open up the profiler.**\n\n![Zwfduhc.png](\/\/\/raw\/14b\/1\/z\/48f8.png)\n\n","comments":0,"created":"2017-05-21T13:14:03Z","files":[],"files-timestamp":0,"id":31981,"love":9,"love-timestamp":"2017-05-22T08:42:53Z","meta":[],"modified":"2017-05-22T08:42:53Z","name":"Little Fortune Planet Bonus Breakdown - Part 7: LibGDX\/GWT performance tuning","node-timestamp":"2017-05-21T16:55:43Z","parent":21490,"parents":[1,5,9,9405,21490],"path":"\/events\/ludum-dare\/38\/little-fortune-planet\/little-fortune-planet-bonus-breakdown-part-7-libgdxgwt-performance-tuning","published":"2017-05-21T16:18:56Z","scope":"public","slug":"little-fortune-planet-bonus-breakdown-part-7-libgdxgwt-performance-tuning","subsubtype":"","subtype":"","type":"post","version":86266},"node_metadata":{"n_key":"31981","n_urlkey":"307191","n_parent":"21490","n_path":"\/events\/ludum-dare\/38\/little-fortune-planet\/little-fortune-planet-bonus-breakdown-part-7-libgdxgwt-performance-tuning","n_slug":"little-fortune-planet-bonus-brea","n_type":"post","n_subtype":"","n_subsubtype":"","n_author":"6977","n_created":"1495372443","n_modified":"1495442573","n_version":"86266","n_status":"WAYBACK"},"source_url":"https:\/\/ldjam.com\/events\/ludum-dare\/38\/little-fortune-planet\/little-fortune-planet-bonus-breakdown-part-7-libgdxgwt-performance-tuning","text":"[As mentioned before](https:\/\/ldjam.com\/events\/ludum-dare\/38\/little-fortune-planet\/little-fortune-plane-breakdown-part-1-brainstorming) **Little Fortune Planet was a bit of an experiment with non trivial, non optimized game design using LibGDX and GWT**. Loads of **nested loops** O(n2), and **dynamic textures**. The game ran horribly **slow on web**, so we set out **improving performance post jam**. \n\nDisclaimer: **Food for LibGDX\/GWT devs**, but still got some **meaty bits for others**. We used [LibGDX](https:\/\/libgdx.badlogicgames.com\/index.html), a java game development framework that targets a wide variety of platforms. Keeping a couple of *gotchas* in mind, it [transpiles](https:\/\/en.wikipedia.org\/wiki\/Source-to-source_compiler) your Java game (using Google Web Toolkit) to JavaScript fairly effortlessly. While the tooling is pretty good, it can be a real pain to profile.\n\n### Goals\n\n**Ideally the game would run at a steady 60 fps**. Let's see what is eating all our performance. If you are using [artemis-odb](https:\/\/github.com\/junkdog\/artemis-odb), grab the excellent [profiler plugin](https:\/\/github.com\/DaanVanYperen\/artemis-odb-contrib\/wiki\/Profiler-Plugin) made by [@piotr-j](https:\/\/github.com\/piotr-j) for low cost profiling in game.\n\n![profiler.png](\/\/\/raw\/14b\/1\/z\/48ef.png)\n\n**A target of 60 fps means we have ~16ms to spend per frame**. Currently on my high end machine **frames take at least double that to render on Chrome. Firefox and IE are even worse**. Throughout optimizing Firefox generally seemed to struggle with our game.\n\n### Profiling Your (LibGDX) Web Game\n\n**So how to proceed? We'll run Chrome's built in profiler (F12 - Performance tab)**. Firefox and IE dev tools have similar features, so use whichever you prefer. Let's see what a couple of seconds of record button yields:\n\n![obfuscate.png](\/\/\/raw\/14b\/1\/z\/48f0.png)\n\nOops! Looks like **our function names have been obfuscated**. We could use Google Web Toolkit's superdev mode to solve this for us, as it provides bridge between your Java naming and the browser dev tools, but since I'm not 100% sure on the side effects on profiling, **we'll keep things vanilla and disable the obfuscator instead**.\n\nAdd style PRETTY or DETAILED to ```html\/build.gradle``` in your libgdx project.\n\n```java\n    import de.richsource.gradle.plugins.gwt.Style\n    compiler {\n        optimize = 0\n        style = Style.DETAILED\n    }\n```\n\nAfter redeploying the game and rerunning the profiler we get:\n\n![profiler2.png](\/\/\/raw\/14b\/1\/z\/48f1.png)\n\n**Now we can read the function names! We'll run the profiler for a couple of seconds, and then sort 'Bottom-Up' to find hotspots in our code**. In our case **we're interested in functions that have a large amount of 'self-time', these functions are eating all our valuable frame time**.\n\n**In our game we've identified two possible areas for improvement: Simulating and rendering the planet.**\n\n## Improving Simulation Performance\n\n**Improving performance is relatively straightforward. Find the functions with the highest self-time and the least amount of effort to reach your set end goal.**\n\n![rngisevil.png](\/\/\/raw\/14b\/1\/z\/48f3.png)\n\nIn this case it seems **the GWT random number generation is the culprit of bad performance**. We use RNG heavily throughout the simulation; What direction does water flow this tick, where does stream spawn, etc. Since all simulation logic is run within a loop, this ends up being extremely expensive.\n\nWe could limit the uses of RNG but we don't want to rewrite everything or break the sim, so instead lets just **[precalculate our random numbers](https:\/\/xkcd.com\/221\/)** and see if we can **fake it**!\n\n\n```java\npublic class FauxRng {\n\n    private static final int PREGEN_COUNT = 10000;\n    private static int[] pregen = new int[PREGEN_COUNT];\n    private static int cursor = 0;\n\n    static {\n        Random random = new Random();\n        for (int i = 0; i < PREGEN_COUNT; i++) {\n            pregen[i] = random.nextInt(Integer.MAX_VALUE);\n        }\n    }\n\n    \/**\n     * Returns a random number between 0 and end (inclusive).\n     *\/\n    public static int random(int range) {\n        return nextInt(range + 1);\n    }\n\n    private static int nextInt(int i) {\n        cursor = (cursor + 1) % PREGEN_COUNT;\n        return pregen[cursor] % i;\n    }\n\n    \/**\n     * Returns a random number between start (inclusive) and end (inclusive).\n     *\/\n    static public int random(int start, int end) {\n        return start + nextInt(end - start + 1);\n    }\n\n    public static boolean randomBoolean() {\n        return nextInt(2) == 1;\n    }\n}\n```\n\nDon't show my boss.\n\nBesides this change we **limit the neighbor checks needed for each cell where possible**. There are also some things that won't hurt the simulation much if they are **run less often**, like **updates to the heat map**.\n\n## Improving Rendering Performance\n\nOn LibGDX **dynamic textures are generated using a Pixmap**, which is **fast on desktop, but slow as a snail on the \nbrowser**. Lets **check LibGDX's Pixmap implementation on web platform** to check out what is so costly about this.\n\n ```java\n        this.ensureCanvasExists();\n        if(this.blending == Pixmap.Blending.None) {\n            this.context.setFillStyle(clearColor);\n            this.context.setStrokeStyle(clearColor);\n            this.context.setGlobalCompositeOperation(\"destination-out\");\n            this.context.beginPath();\n            this.context.rect((double)x, (double)y, (double)width, (double)height);\n            this.fillOrStrokePath(drawType);\n            this.context.closePath();\n            this.context.setFillStyle(this.color);\n            this.context.setStrokeStyle(this.color);\n            this.context.setGlobalCompositeOperation(Composite.SOURCE_OVER);\n        }\n\n        this.context.beginPath();\n        this.context.rect((double)x, (double)y, (double)width, (double)height);\n        this.fillOrStrokePath(drawType);\n        this.context.closePath();\n        this.pixels = null;\n```\n\n**Yikes! Pixmap is backed by a browser canvas. Since we draw tens of thousands of pixels, a fat loop won't perform very well as a tight one would.**\n\n**If we could push an array of color data straight into the canvas it might perform better**. Google Web Toolkit allows you to directly hook into Javascript, [so lets write a little function to feed the canvas our pixel buffer](https:\/\/github.com\/DaanVanYperen\/odb-little-fortune-planet\/blob\/master\/html\/src\/game\/emu\/net\/mostlyoriginal\/game\/system\/planet\/MyScreenUtils.java). **This ended up fixing rendering times to 4ms on Chrome, but Firefox still struggled. Dang!**\n\n## Take a Break or Educate!?\n\n**Ultimately reading the google scriptures on the matter would help**, but I **tend to end up with too many browser tabs of interesting topics and no work done**. Sometimes taking a short break and giving your mind a rest can be as effective; got an idea! **Lets try something else**.\n\n**Since only a small amount of particles in our sim change each frame**, why not **keep track of those and only do the expensive pixel plotting calls when we need to?** We use an in-memory buffer outside of the GPU to track pixel changes, and plot changed pixels to a persistant ```FrameBuffer``` in GPU memory. \n\nDelta's **visualized looks something like this**:\n\n![5921aa9170f27591622802.gif](\/\/\/raw\/14b\/1\/z\/48f2.gif)\n\n**Success! We managed to drop rendering time to 1-4ms on our target browsers.**\n\n## Final Results\n\n**We've managed to get the time to generate a frame down to about 6-10ms, which is pretty good. We still hit the 30ms sometimes but overall the simulation is pretty smooth.**\n\n![muchbetter.gif](\/\/\/raw\/14b\/1\/z\/48fa.gif)\n\n**Wanna try it out yourself? [You can play the sandbox version here (WEB)](http:\/\/www.mostlyoriginal.net\/games\/littlefortuneplanetsandbox\/). Press P to open up the profiler.**\n\n![Zwfduhc.png](\/\/\/raw\/14b\/1\/z\/48f8.png)\n\n","title":"Little Fortune Planet Bonus Breakdown - Part 7: LibGDX\/GWT performance tuning","wayback_source":[]}