Some Android devices extremely slow when rendering canvas elements - javascript

Im developing an app for android devices, and found that samsung galaxy S4 specifically, has extremely poor performance when app/web page uses canvas.
Odd thing is, that its not always the case.
I have tested 2 sample apps.
http://ie.microsoft.com/testdrive/Performance/FishIETank/Default.html
and
http://ie.microsoft.com/testdrive/Graphics/TouchEffects/Default.html
the first one works fine, and outperforms my Nokia (which is dual core) and is expected. However, the other demo, is almost completely unresponsive and framerate is close to 1, where as all other devices render it fine.
Since the first app runs well and the other one doesnt, it beggs the question, why ?
First one has no event listeners, where as the other one has touch listeners. Could touchmove be the cause instead of canvas...or is that demo using some canvas features that the other one isnt, and thus has poor performance.
I have read lots of topics about this issue, and none seem to have answer. Most are many months old...so i thought ill make a new topic.
Is there any way to solve the canvas issue on Samsung S4 ... and potentially other android devices running 4.2.x.
If any StackOverflow users here has S4, can you test both demos and confirm my observations?

I strongly suspect this is not a Canvas specific issue, but a requestAnimationFrame issue. The first animation does not attempt to use requestAnimationFrame, but the second does, in this file on line 206.
Android Browser on firmwares <= 4.2 does not support requestAnimationFrame, and instead uses setTimeout, dividing one second by specified frame rate in Hz, which executes renders in the normal event loop.
setTimeout does not execute at the exact time in milliseconds requested, but enqueues the event in the loop at the time specified. If the event loop is hung by other javascript on the page, or the single-core device decides something else is more important, the runtime is very vulnerable to de-prioritization without the requestAnimationFrame API, and the callbacks enqueued using setTimeout will stutter and bunch. More on setTimeout resolution and timing.
Unfortunately, you are at the mercy of the event queue if you are (1) going with this Canvas-based approach and (2) on a platform that does not support requestAnimationFrame. Here is the reference table for what browsers support the feature.
Cheers!

Related

How to determine if video frame is ready for composition?

I've found some similar question on Stackoverflow but it does not really address my problem.
I'm playing multiple videos into a WebGL texture, one after another. It's based on user input, something like a web-based VJ tool.
Copying is done easily, and I have internal clock that is sync with same fps like the video I'm playing (eg. 30fps) and frames are updated correctly. Exactly like one of the answers offered in the above mentioned question. All that works well.
Texture is updated with:
gl.texSubImage2D(gl.TEXTURE_2D,0,0,0,this.gl.RGBA,this.gl.UNSIGNED_BYTE,video);
My problem is how to detect when the very first frame is available for composition. Video does not start playback immediately in the real environment (eg. average 4G, video on CDN), but sometimes takes 1s or more to start playback (to buffer sufficient data).
If I attempt to start updating texture prior the first frame is available, I get WebGL error thrown:
WebGL: INVALID_VALUE: texSubImage2D: no video
I'm aware of video.load(); method that can be called in advance (eg. on user interaction), however I have ~50 video files that I need to play (in unknown order as it depends on user input), and older phones (like iPhone 7) have major performance drop when I do that, to the point Safari sometimes crashes.
I'm looking for a reliable way to determine when video started actual playback. Events such as onplay don't seem to fire when first frame is available, but much earlier.
I have also tried ontimeupdate event, but that one does not seem to fire when first frame is available for composition, but earlier as well, just like onplay. I can see the event fired, and I start updating texture when it's fired for the first time, but at the beginning of updates it generates WebGL error (for about 0.5-1s, depending on network speed) until video actually shows the first frame. Once buffered, no errors are thrown.
This issue is more visible in 4G/mobile network. Also tested in Chrome with throttling speed. Un-throttled speed will get me 1-4 WebGL warnings prior showing first frame, while eg. 12mbps throttled will give me 100-200 warnings, prior video frame is presented.
I've seen requestVideoFrameCallback but it doesn't have coverage I need (iOS Safari not even planned anytime soon).
I'm trying to avoid updating texture if video frame is not ready for composition, but can't find a reliable way to determine when it is ready.
Any help is highly appreciated!
Alright, I have found a solution at listening on playing event.
I was listening on play and on timeupdate, didn't think of on playing being so different.
It does fire after the first frame is available for composition and now I don't have anymore those WebGL errors. Tested on an Android 10 device as well as on iOS 14.5 iPhone 7 device.

Test for memory issues with website on a device?

I'm having a issue which is hard to debug. I'm using a Javascript library (the JQuery Flexslider plugin) in a number of different places on my site. It's all working fine except for one particular phone where it doesn't work and slows down everything on the page.
So far, I've only seen it happen on this one device. Other devices of the same type do not have the issue. This person has an iOS that's a few versions out of date and not much memory, so I think it's a memory issue.
An old hack was to move the carousel element that has the issue on the page with Javascript, but I want to find and fix the root issue.
How can I start debugging this? I'm not sure how to test for a memory issue on a device.
If you're on a Mac, then you can plug in and use remote debugging via Safari, where you'll have access to the tools, including the profiler (not sure the state of Safari support in Windows). There are numerous resources for showing how to remotely debug a device, unless it is a really old version of iOS you should be fine, you’ll have to enable the develop menu via settings but after that its plain sailing if you know your debugging tools.
I'd agree that it doesnt really sound like a memory issue, although jQuery tends to be hungry in that respect, I dont know the plugin in question but the quality of plugins is hugely variable in jQuery-land. Old phones and old versions of jQuery certainly never played well together.
When you say one phone, you mean one type of phone + iOS version? The question isnt clear, its almost reads like you have 2 identical phones/os's where 1 works and 1 does not.
If you use Chrome you can use the Heap profiler
First open your developer tool and start recording.
Next start using your page and try to replicate your issue, stop recording and review the stats.
This is likely not a memory issue, but a cpu issue. The way jQuery does animation is processor constrained on older dvices. Factors that are easiest to handle include:
size of the page (html length and complexity)
animation steps, length, and complexity
You have a couple of options here, but the simple answer is you are asking too much of the older processor. Assuming you are using this plugin http://www.woothemes.com/flexslider/ you could try disabling or simplifying some of the transition effects. animation and animationSpeed would be the first I would suggest.
If you are interested in not changing the experience for most users you could consider tying into the start and end functions on the callback api and checking the time it took to perform the first transition, then reinitialize a simpler version of the slideshow for that device.
The hard thing here is there isn't really a right answer. If one of the above options doesn't fix it you're likely looking at choosing/building a different slideshow, degrading the experience for everyone, or determining the best way you feel comfortable with choosing who gets the degraded experience.

Terrible lag in HTML5/JavaScript game when animating

My HTML5/JavaScript game (for the windows store) is lagging constantly! I have used barely any resources and I have used gsap for animating the bullets and mines. For example: http://203.81.207.90/default.html in Chrome enabling everything under rendering shows the lag immediately (the background and mines lag the most).
the problem is the tiny interval (10ms) used in most setInterval()'s.
increasing the interval should help and since javascript is a single threaded language, using lots of timers basically queue's them (performance issue) try using less timers by putting multiple functions in single timers
That's not lag. It's the browser trying to scroll when you press the right arrow key. Put e.preventDefault() at the start of your keydown handler.

How to control the framerate in KineticJS?

How can I control the rendering loop frame rate in KineticJS? The docs for Kinetic.Animation show a frame rate being passed to the render callback, and Kinetic.Tween seems to have no frame rate logic, but I don't see anyway to force, say, a 30fps when 60fps is possible.
Loads of context for the curious follows, but the question is that simple. If anyone reads on, other advice is welcome. If you already know the answer, don't waste your time reading on!
I'm developing a music app that combines some DOM-based GUI controls (current iteration using jQuery Mobile) and Canvas-based GUI controls (using KineticJS). The latter involve some animation. Because the animated elements are triggered by music playback, I'm using Kinetic.Tween to avoid the complexity of remembering how long a given note has been playing (which Kinetic.Animation would require doing).
This approach works great at 60fps in Chrome (on a fast machine) but is just slow enough on iOS 6.1 Safari (iPad 2) that manipulating controls while animations are happening gets a little janky. I'm not using WebGL (unless KineticJS or Chrome does this by default for canvas?), and that's not an option when I package for native UIWebView.
As I'm getting beyond prototype into wanting to make more committed tech decisions, I see the following options, in order of perceived goodness:
Figure out how to cap the frame rate. Because my animations heavily use alpha fades but do not involve motion, I believe I could get away with 20-30fps and look fine. Could also scale this up on faster devices.
Don't respond immediately to touch inputs, but add them to a queue which I poll at a constant interval and only use the freshest for things like touchmove. This has no impact on my non-interactive animated elements, but tackles the problem from the other direction, trying to reduce the load of user interaction. This would require making Kinetic controls static and manually tracking touch coordinates (not terrible effort if it actually helped).
Rewrite DOM-based GUI to canvas-based (KineticJS); rewrite WebAudio-based engine to HTML5 audio; leverage CocoonJS or Ejecta for GPU-acceleration. This means having to hand-code stuff like file choosers and nav menus and such (bad). Losing WebAudio is pretty serious as it eliminates features like DSP effects and very fine-grained, low-latency timing (which is working just fine on an iPad 2).
Rewrite the app to separate DOM based GUI and WebAudio from Canvas-based elements, leverage CocoonJS. I'm not sure if/how well this works out, but the fact that CocoonJS passes JavaScript code as strings between the 2 components makes me very skittish about how solid this idea is. It's probably doable, but best case I'm very tied to CocoonJS moving forwards. I don't like architecting this way, but maybe it's not as bad as it sounds?
Make animations less juicy. This is least good not because of its design impact but because, as it is, I'm only animating ~20 simple shapes at any time in my central view component, however they include transparency and span an area ~1000x300. Other components like sliders are similarly bare-bones. In other words, it's not very juicy right now.
Overcome severe allergy to Objective-C; forget about the browser, Android, and that other mobile OS. Have a fast app that performs natively and has shiny Apple-approved widgets. My biggest problem with this approach is not wanting to be stuck in Objective-C reality for years, skillset-wise. I just don't like it.
Buy an iPad 3 or later. Since I already am pretending Android doesn't exist (I don't have any devices to test), why not pretend no one still has iPad 2? I think this is passing the buck -- if I can get acceptable performance on iPad 2, I will feel confident about the app's performance as I add more features.
I may be overlooking options or otherwise naive about how to tackle this. Some would say what I'm trying to build is just silly. But it's working pretty well just not ready for prime time on the iPad 2.
Yes, you can control the Kinetic.Animation framerate
The Kinetic.Animation sends in a frame object which has a frame.time property.
That .time is a running timer that you can use to throttle your animation speed.
Here's an example that throttles the Kinetic.Animation: http://jsfiddle.net/m1erickson/Hn3cC/
var lastTime;
var frameDelay=1000;
var loop = new Kinetic.Animation(function(frame) {
var time = frame.time
if(!lastTime){lastTime=time;}
var elapsed = time-lastTime;
if(elapsed>=frameDelay){
// frameDelay has expired, so animate stuff now
// set lastTime for the next loop
lastTime=time;
}
}, layer);
loop.start();
Working from #markE's suggestions, I tried a few things and found a solution. It's ultimately not rocket science, but sharing what I figured out:
First, tried the hack of doubling Tween durations and targets, using a timer to stop them at 50%. This kinda sorta worked but was hard to get to look good and was pretty error prone in coding bogus targets like negative opacity or height or whatnot.
Second, having read the source to Tween and looked at docs again for Animation, decided I could locally use Animation instances instead of Tween instances, and allow the closure scope to hang onto the relevant note properties. Eventually got this working smoothly and finally realized a big Duh! which is that throttling the frame rate of several independently animating things does not in any way throttle the overall frame rate.
Lastly, decided to give my component a render() method that calls itself in a loop with requestAnimationFrame, exits immediately if called before my clamp time, and inside render() I update all objects in the Kinetic canvas and call layer.drawScene(). Because there is now only one animation, this drops frame rate to whatever I need and the app is fast on iPad 2 (looks exactly the same to my eyes too).
So Kinetic is still helping for its higher level canvas API, and so far my other control widgets are still easy code using Kinetic to handle user input and dragging, now performing much better as the big beast component is not eating up the CPU.
The short answer to my original question is that no, you can't lock the overall frame rate for very complex animations, but as Mark said, you can for anything that fits in a single Animation instance.
Note that I could have still used Animation without giving it a layer or explicitly calling any draw() methods, but since I'd still have to write all the logic to determine individual element's current visual state, there was no gain to doing this. What would be very useful would be if Tween could accept a parameter to not automatically render. This would simplify code like mine, as I could shorthand the animation on individual objects but still choose when to actually do the heavy lifting of rendering everything. Seeing how much this whole exercise gained in performance on the iPad 2, might be worth adding this option to the framework.

How to show something for a very brief and precise amount of time

I am developing a very game-like web application, and there is a particular requirement that is driving me nuts: I have to show an image for a very short (some hundreds of ms, sometimes even just 25ms, just enough for the user to perceive something when he/she already knows what to look for). This application has to work on Chrome, Firefox and IE9+, but also Android tablets and iPad.
The only reliable way of doing this in desktop browsers I have found is Flash. Using Javascript and html5 canvas in a lot of computers results in the items not even being rendered. On the other hand, Flash doesn't work on the iPad and some Androids, but in those devices, our tests show, the javascript way seems to work just fine. So this is the best solution I have found: grosso modo, if iPad then load_javascript_version() else load_flash_version().
Have you seen anything like this done before? Should I abandon any hope of finding a more maintainable solution?
Thanks a lot.
Haven't tried this particular use case, but a 2D library like KineticJS should be able to do what you need.
There are jQuery functions like timeout: http://www.jquery4u.com/jquery-functions/settimeout-example/ and delay: http://api.jquery.com/delay/
Are you using RequestAnimationFrame in your game loop? If not please read about it and ensure that your image is preloaded when you try to render it.
Mozilla dev RAF api
Most devices and browsers that have hardware accelerated canvas support, should be able to achive as fast as 60 fps fairly, which means you would be able to show an image for a single frame or about 16.7 ms.
I have managed to do similar things using the createjs library which can very easily be set to use the RequestAnimationFrame api.
Createjs ticker RAF

Categories