Solving black screens and corrupted graphics in HTML5 games on the Samsung S3

This is pretty much how I felt over the past few days debugging this

One thing we always try to do as HTML5 game developers is maximise performance. This isn’t an easy task as pretty much every handset is fighting against you when it comes to draw speeds and available RAM in the browser. So when I ordered a Samsung Galaxy S III – quite literally the pinnacle of the Samsung device range at the moment, I wasn’t expecting its beast-like technical specifications to give me any problems at all. Oh how wrong I was.

For some reason several of our games were either turning entirely black or parts of them were not displaying at all. They didn’t crash, there was no error log entry to look at, they just didn’t actually display. The game was still running however, sounds could be heard and touch events were still received – so if you knew where to touch you could still play it, it just wasn’t rendering anything. What followed was a full day and sleepless night spent debugging to find the solution. It was such a task I had to share it here to help fellow devs!

Debugging without a debugger

After many hours of head-scratching I finally tracked down the issue to a setting in the Android browser. The S3 runs Android 4.0.4 Ice Cream Sandwich, so there is no sign of Chrome yet. Incidentally this problem doesn’t exist on my Nexus 7 running Jellybean, so it appears to be confined to ICS and/or the Samsung phones.

If you navigate to about:debug in the browser it’ll activate the JavaScript console (you may need to restart the browser to see this). This is handy for poor-man’s debugging but another thing it does is to open up the Debug group in the Settings. Jump into here and you can find all kinds of useful things, from enabling visual redraw region indicators to turning on WebGL. However the culprit of the problem was the setting Enable OpenGL Rendering. This is turned on by default, which means all those millions of handsets out there in the wild will have this activated. If I turned the setting off all of the games ran perfectly, no issues at all. Turn it back on and some went black and others lost layers or sprites. At least I now had something to work from.

You’re probably thinking it was something trivial like “I bet his images weren’t sized to powers of 2” but if only the issue had been that simple. What it transpires to have been is that the browser can’t handle layered canvas objects when GPU acceleration is turned on. As I mentioned at the start we try to keep our games responsive, which isn’t always easy, but one way to do that is to make sure you keep screen re-draws to a minimum. So if we have a background scene to our games that doesn’t change very often we’ll render it to a canvas object and then layer the game foreground on-top of that, so we don’t have to waste time refreshing non-changing graphics every frame.

However it would appear that with OpenGL rendering enabled the S3 isn’t capable of compositing together two canvas layers if they are positioned over the top of each other via CSS.

When it can’t handle the rendering I’ve witnessed it do all of the following:

1) When it can’t render the canvas, for whatever reason, it hides it. It literally skips it when rendering the page, so it is still present in the DOM, still responds to touch events and the like, it just doesn’t visually appear.

2) Another anomaly I noticed was that sprite sheets and texture atlases no longer work. It’s incapable of grabbing a portion of an image when using the context.drawImage function. What happens is that the entire image is rendered, but shrunk down into the size specified in the drawImage call. So your entire sprite sheet appears.

3) Images appear stretched. Especially true for image sizes that don’t fit into ^ 2. The top left corner of the image is stretched down across the entire image size.

I’m not sure if these bugs are specific to the S3 or an Android 4.0.4 bug that may be present on other handsets too. It wasn’t just our games though, even GMail didn’t load properly and would black screen from time to time. Debugging this was a nightmare – google search turned up nothing, there was no handy StackOverflow answer waiting for me. Hence why I wrote this post!

How to solve it?

1) Disable OpenGL rendering. Obviously this isn’t a real solution, you can’t tell your players to do this and you can’t detect if OpenGL is present or not from JavaScript. But it’s a useful way to see if your game is suffering from this issue. If when you turn it off the game runs fine you know this is the cause.

2) Don’t layer DOM objects that are hardware accelerated! I hit this problem with 2 canvas elements on-top of each other, but I dare say it could happen with other types too. Video on-top of canvas for example, or hardware accelerated CSS3 on-top of canvas. Ideally if I had time I would research exactly the combinations that fail, but I don’t right now – if you do, please share your results in the comments.

Performance of the S3 was excellent especially given how large its screen is. It ran our games wonderfully, once they were fixed 😉 If your game just uses a single canvas with nothing else then you’re probably going to be ok. If you’ve got some huge texture sizes, then maybe not. Also the phone by default has a Power Saving mode enabled, which appears to mean that it will look to see if the page is made-up of lots of DOM objects and if so slows down to a crawl when updating them. Maybe to preserve battery life? But I did notice that several DOM only games ran at around 2 frames per second. So that’s another thing to be aware of.

Screen Grabbing from Android

Just quickly I wanted to give a shout-out to the excellent free tool Android Screen Capture for Windows. It hooks into the Android SDK and Dalvik Debug Monitor and allows you to stream images direct from the handset to your PC, which can be very handy indeed! The hardest part was getting the Samsung registered as a device on Windows 7. For that the Google USB driver won’t suffice, you need to go to the Samsung support site, scroll to the bottom of the page and pick the “Manuals and Downloads” tab, then use the link for “Software” to see the USB driver. Once the S3 appears in Dalvik you’re good to go.

 

Posted on September 7th 2012 at 3:32 pm by .
View more posts in HTML5. Follow responses via the RSS 2.0 feed.


7 Responses

Leave a comment
  • September 7th 2012 at 3:50 pm

    Good to know Rich, who doesn’t love un-debuggable bugs. Thanks

  • September 7th 2012 at 3:55 pm

    Yeah watch out for this on the SS2 project you guys are on! I’d get them to buy you a handset ASAP, it’s a bit of a ‘mare :) thankfully they’re only a few hundred quid off Amazon.

  • September 7th 2012 at 5:01 pm

    Throw in the fact that z-index’s w/ DOM elements (even unaccelerated) are screwed up on Galaxy Nexus + Galaxy 7 at specific zooms (not sure about S3) https://code.google.com/p/chromium/issues/detail?id=142613 Make’s HTML5 even on the new Androids a bundle of joy.

  • Jan Ambrožič
    October 26th 2012 at 4:03 pm

    I didn’t have the same problems but I had a lot of others canvas issues working on S3. One of them is described in this topic: http://bit.ly/P6OKxc

    Funny thing is also that I had totally different issues on S1, S2 and S3. Some things that worked in S2 didn’t work in S3 and vice versa. Really terrible stock browsers.
    I did however solved one of the issues (textures were not rendered correctly) by adding height and width (both! just width didn’t work) parameters in the CSS style of the canvas so that it match width and height properties of the canvas tag.

  • Mike Thomas
    November 30th 2012 at 9:50 pm

    Hi,

    I had a similar sounding problem using RGraph (an html5 graphing package) where some of the canvases would just disappear when the S3’s screen was touched even though no click/touch/etc events were on the canvas. The way I worked around it was to draw into the canvas element but then convert it to an img by canvas:

    iCharts.prototype.toImageHack = function (canvas) {
    var img = document.createElement (‘img’);
    img.src = canvas.toDataURL (“img/png”);
    img.width = canvas.width;
    img.height = canvas.height;
    // on some androids, it doesn’t seem to like the image data url so switch it back to the canvas
    // in that case. hacks all the way down.
    img.onerror = function (el) {
    img.style.display = ‘none’;
    canvas.style.display = ‘inline’;
    };
    return img;
    };

    and then just append the node to the appropriate container element.

  • May 4th 2013 at 5:27 pm

    Hey thanks for the article. Samsung S3 from Canada, fully updated, Chrome Beta with Hardware Accelerated canvas yields:

    ctx.clip() //not working, blocks appear when I wanted arcs
    ctx.arc() //potentionally one or the other above
    ctx.fillStyle = pattern //not visible
    ctx.fillStyle = gradient //not working
    ctx.stroke() // not working

    Disabling the Hardware Acceleration on Canvas using chrome://flags on Chrome Beta enables all of the above issues.

    Ironically works fine on HTC One X+, Nexus 4, Nexus 7, Nexus 10

    Thanks again!

  • Hagar
    June 22nd 2013 at 1:50 pm

    Didn’t help me:(

Make yourself heard