NW/Node Webkit - Image decodes even if it is already visible - javascript

I am currently working on a JavaScript(pure js) based game. The game contains 5 large sprite sheets(e.g. 2861 × 768 and 4096 × 4864). When the game starts, all 5 sprite sheets are preloaded to canvas elements. Three of those 5 sprites represent together one animation, where each sprite contains 75 frames. When one sprite ends with its animation, I hide it and display the next sprite. When the second sprite finishes animating, I hide it and display the third/last one.
When the second or third sprite is about to be displayed, a small delay of 0.5 s - 1 s happens. The image is being decoded.
It is not something that happens just the first time, it is something that happens always. And that animation repeats every 5 minutes, and the small delay happens always.
The reason why I'm using canvas elements for preloading is that I thought WebKit would just throw away decoded images for some time being unused and that the canvas element would prevent WebKit from deleting it from memory. But that does not work.
I've tried almost every optimization I'm aware of. I have even refactored all my CSS by removing descendant selectors etc.
The renderer I'm using to draw those animations is built by myself and it is working perfect, so that could not be the problem, since it is working very well in Firefox.
EDIT [2016/03/04]:
I made a mode with canvas and the result is even worse. It laggs a lot. And the delay remains the same. Only in NW, the problem does not persist in Chrome neither in Firefox.
Canvas mode - Lags:
Default(HTML) mode - Works perfect:
Codepen: My renderer http://codepen.io/anon/pen/JXPWXX
Note: If i hide those other elements with opacity:0.2 rather than opacity:0, the problem does not happen. But, I can not hide them like that since they remain still visible. They shouldn't be visible. If I add opacity:0.01 it is not visible and the problem does not happen in Chrome, but still persists in NW.
In NW, when I swtich from opacity:0.2 to opacity:1, an image decode is being processed. The same thing does not happen in Chrome browser.
I am using the following version:
nw.js v0.12.3
io.js v1.2.0
Chromium 41.0.2272.76
commit hash: 591068b-b48a69e-27b6800-459755a-2bdc251-1764a45
The three image sprites are 14.4MB, 14.9MB and 15.5MB size. Each sprite contains 75 frames.
Why could this be happening and how can I prevent it?

Try to switch to google-chrome directly since the new nw version is probably released 19.04.2016. After that NW will hopefully keep up with every Chromium release.
You should not have the same problems in Chrome.

Given that keeping Webkit thinking the image is still displayed makes the problem disappear (as your opacity experiment shows), I'd move it nearly completely out of the visible area, with only a single transparent row overlapping with the viewport (using overflow hidden).
Note that an unpacked 4000x4000 sprite sheet will use 64 Megabytes of RAM (4 bytes (=RGBA) per pixel), so perhaps it might be better to make sure the next image gets "warmed up" a bit ahead of time, without keeping all of them unpacked all the time?

I'd recommend using idata = ctx.getImageData(0, 0, canvas.width, canvas.height) to retrieve the data array from the canvases, then ctx.putImageData(idata, 0, 0) to switch between sprites, rather than hiding the canvases.

Related

Can I force the browser to rasterize elements before they become visible?

Call me crazy, but I'm working on a game using vanilla DOM and TypeScript. It's not action-heavy, but there are some animations going on, which are driven from JavaScript (too complex to do in CSS).
I'm already applying the common trick of translateZ(0) (called null transform hack or less accurately silver bullet) to pull animated elements into their own rendering layer, and I'm animating nothing besides compositor-only properties (transform and opacity). This works beautifully: during the game, everything feels buttery smooth, even on older mobile devices.
The problem is during the start of the game. At that moment, about 70 individually transformed and animated elements enter the page one by one over the space of a few seconds, by animating their opacity from 0 to nonzero. This is causing visible stutter during the animation.
My guess was that Chrome is being too clever here, and will only rasterize each element at the moment it first becomes visible. A quick check with the dev tools confirms this, as there's a lot of rasterization going on even during the animation:
I would prefer to rasterize all these elements once, before the animation starts, and only then trigger the animation. But how can I force rasterization of elements that aren't yet visible, without showing a flash of them to the user?
I'd be happy with a Chrome-only approach, but cross-browser would be even better.

Canvas flickers if size is over 256px on Android (Cordova)

I'm developing a coloring mobile app using Cordova and the project https://github.com/theisensanders-wf/responsive-sketchpad
Everything works fine, but the canvas flickers if is not exactly 256px or less. I made some tests changing on the console the size with the Sketchpad.resize() function, and always over 256px the canvas starts flickering. I inserted it without modifications, on a document.ready listener as follows:
window.pad = new Sketchpad(document.getElementById('canvasDiv'));
By flicker I meant the lines seems to have an effect of being blurry and then rendered again, and that behavior repeats every 1 second.
I tried using requestAnimationFrame but that made the performance very poor in terms of refresh rate. It is a coloring app so it is important that the feeling of drawing feels real time.
I don't know if it still works but there's a plugin to improve canvas speed on android.
The only thing is that it hasn't been maintained for 4 years now...

Reading Chrome Frame Rate results in DevTools

As far as I know the white (empty) bar represents idle time.
I know I should focus so that the application would run (at best) > 60fps and at least >30fps
This is my bar:
As you can see, I'm repainting on scroll ( changing the background position of an image ).
The middle line marks 60fps in this case, and if you pay attention closely, there is a top line (which is 30fps).
I've compared my results with others out there, and it seems that I have rather large idle times. Is that okay ? I know that paint, javascript and anything else is bad, but what purpose does displaying idle times serve ? Should I attempt to get rid of it, if so, what should I watch out for ?
Also, there is a spike every now and then because of "Image Decode", is there anything I can do about it ?
Image Decode
You can reduce the size of the image, which will make the decodes shorter. You can also trick the browser into doing these decodes earlier, perhaps at a time when there's no activity and therefore won't hurt your FPS.
I would also kill these big images temporarily to see if that moves you from 30fps to 60fps.
Scroll handler
You should minimize the amount of work you're doing in your scroll handler. At most you should be grabbing one scroll metric from the DOM and a timestamp perhaps. Then do your adjustments to style in a requestAnimationFrame loop: http://www.hesslerdesign.com/blog/javascript/optimizing-javascript-performance-handling-coordinate-updates/
Heavy paints
background-position isn't optimized well in WebKit or Blink, especially for this sort of usecase. I would put the image into a div and just slide its position using a transform: translate(x,y) (and promote it to its own layer if possible.) If sucessful, you won't repaint the entire screen on every scroll.
30 FPS Aint bad
A solid 30FPS actually looks better than something that's jumping between 60 and 45 all the time. Curious.. Are you in Canary or on a retina screen?
In general, you're doing pretty okay on your hardware, but I would try to reduce those image decodes. And you can go further with the above if you want.

Adobe Edge issue - animation shows what looks like skid marks

New to using Adobe Edge, though very familiar with Flash/AS. I need to do animation sequence that is not flash-based, so trying out Adobe Edge. The animation itself is all done, everything works correctly. However, when the JS is loaded on the site and viewed (at least in Chrome/FF) when an object is easing out it creates black bars behind it - looks like it's skidding to a halt.
These disappear when anything is moved - background, any div, anything layered on top, etc. Can handle with setInterval flashing a transparent div on top of it, but seems a bit of a ridiculous way to solve. Has anybody else run into this? Is there a setting I'm missing?
Figured this out after ton of testing, etc. Basically the way Adobe Edge works - at least at this point - is that if you put the animation on top of something else, the background color will "leak" through the animation and show up as skid mark looking lines. After playing with every setting in their UI, nothing would handle. Final work around was to put a background image behind the animation that matched, so when it showed through it wasn't detectable.

smooth animation only in firefox 4

It seems that the following setup on jsfiddle only runs smooth in firefox 4.
http://jsfiddle.net/ynytc/39/
Im wondering why?
I thought maybe because of the hardware acceleration in firefox 4,
but without it still runs smooth.
So maybe someone knows why?
edit: compared to chrome,safari,ie8
You're animating over a 10 second period a change in size of only 20 pixels. That means that there's going to be only one integral dimension change every 1/2 second, which is exactly what I see in Chrome. The size of the image can't be set to fractions of a pixel (in all browsers). Firefox 4 seems to allow that, however; that's the only thing I can imagine anyway.
If you change your fiddle like this you'll see that Chrome is running the animation as fast as you could want it to, but the image only shifts when there's a whole integer change in the property value (width and height).
This is an interesting side effect that I hadn't encountered before.
It seems that other browsers can't apply subpixel scaling, so you only have 20 pixels to spread in 10 seconds, coming to a glorius 2 frames per second. The workaround for this would be to apply CSS transitions (if you don't need to support IE).
See example with CSS transitions here:
http://jsfiddle.net/nKfdB/

Categories

Resources