Onaluf.org http://www.onaluf.org write something smart here! fr http://www.onaluf.org/fr/rss mohiboen onaluf@onaluf.org Performance of per-pixel image access for the JavaScript canvas/fr/entry/132010-05-14 00:00:00

(Désolé mais cet article n'existe qu'en anglais)

Last week I spent some time optimizing a small canvas demo that I've done with some good results (~40% speedup in Google Chrome). During those hacking session I came up with a general hack that can lead to a big increase in performance. So I thought that it was worth looking at it in details.

The problem

HTML 5 canvas element is a formidable tool and for a lot of things it's really fast (just look at chrome experiments or canvas demo for some nice example). However there is one domain where the speed you can achieve is less than satisfactory: per-pixel writing. Let say you want to fill the canvas surface with pixels generated by some calculation, you will have to write every pixel of the canvas with some new values. The resulting code would look like that

canvas = document.getElementById("canvas"); context = canvas.getContext("2d"); image = context.getImageData(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); var pixels = SCREEN_WIDTH*SCREEN_HEIGHT; while(--pixels){ image.data[4*i+0] = r; // Red value image.data[4*i+1] = g; // Green value image.data[4*i+2] = b; // Blue value image.data[4*i+3] = a; // Alpha value } context.putImageData(image, 0, 0);

For each pixel of the image we have to write 4 values (one for each color and the alpha channel) so if this operations turn out to be slow, this loop will be very inefficient.

The Hack

After listening to a YUI talk about javascript performance insisting on how DOM access was slow and should be avoided I wondered if every write to an element of the data array was a DOM access. This would explain the slowness of it. So I came up with this idea: Why not "detaching" the image's data array to manipulate it and copy it back before rendering. It turned out that it provided a nice speed-up in chrome, so much in fact that I made a simple benchmark to isolate the effect from the big mess of the application I was optimizing.


canvas = document.getElementById("canvas"); context = canvas.getContext("2d"); image = context.getImageData(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); var pixels = SCREEN_WIDTH*SCREEN_HEIGHT; var imageData = image.data; // here we detach the pixels array from DOM while(--pixels){ imageData[4*i+0] = r; // Red value imageData[4*i+1] = g; // Green value imageData[4*i+2] = b; // Blue value imageData[4*i+3] = a; // Alpha value } image.data = imageData; // And here we attache it back context.putImageData(image, 0, 0);

The advantage of this hack is that it is very easy to apply in any situation of this kind.

The Benchmark

I added to this benchmark a test for something else that I noticed earlier on the development of the same application: going from a globally scoped to a more "Crockfordy" namespaced one may sometime have negative effect on performance. So the benchmark tests for a combination of the following: global vs namespaced methods and standard vs "detached" access to the image's data. To run the benchmark just go there and wait for the four tests to end. They are run one after the other, each in its own frame.

Results

There is more information here as is it seems at first sight. The first thing is that caching the image's data array is always a good idea (red values are always under blues values and green always under oranges values). I can only speculate regarding to why that is... it probably has to do with the cost associated with "crossing the DOM bridge" as describe in the talk I mentioned earlier.

You may wonder "What if instead of just writing the value of each pixel I need to read them first?". Well I did too because intuitively it would seams that this should be an even better candidate for this kind of optimisation. It turned out that this give the exact same result as when you're just writing the value. This would seams to indicate that accessing a value of the data array don't require a "flush" of the pending modification on the page (which make sense since the change to those data are not dirrectly reflected on the page).

The second interesting point is: using namespace has a big impact on performance. This as probably to do with the way modern compilers use tracing to speed things up. It seams that namespacing can make it harder for the compiler to find a valid trace through the code. Indeed, if you look at the second result for Firefox without JIT (tracing disabled) you can see that globally scope function loose their advantage.

There may well be a way to use namespace that doesn't break the tracing but I didn't find any.

Final Word

This is just a simple benchmark. Testing javascript is not a simple task and many factors can come into play. Furthermore those results are of no use comparing performances between browsers. This small article is there to start a discussion and I am open to any comments. Post your results if you use a browser not mentioned here!

As always the code is under a MIT License, so don't hesitate to roll you own version of this benchmark!

Brushes App pour iPhone/fr/entry/122009-08-12 00:00:00

J'utilise depuis quelques jours Brushes sur mon iPhone et je doit dire qu'après un petit temps d'accoutumance cette application est très sympa à utiliser!

Voici un petit exemple de ce que j'ai réussit a dessiner. La vidéo est générée par un outils gratuits qui tourne sous OS X et est capable de lire les dessins généré par brushes.

Voxel Spacing/fr/entry/112009-07-17 00:00:00

Après quelques heures de développement voici une petite experience en javascript qui utilise le canvas et ImageData. Vous trouverez plus d'info sur voxel.onaluf.org.

ps. pour la capture d'écran j'ai tricher un peux, il s'agit d'une résolution ou la demo n'est pas vraiement fluide...

gameQuery 0.1/fr/entry/102008-06-25 22:09:37

Je viens de publier sur le site de plugins pour jQuery une version préliminaire de gameQuery. Il s'agit d'un plugin qui rend le développement de jeux en javascript plus facile. Le but est de fournir une alternative a flash dans certaine situations.

L'usage de jQuery et de ce plugin permet une approche plus haut niveau et cross-browser de la gestion de l'affichage du jeux.

Vous pouvez trouver la page pricipale de gameQuery ici

Voici une demo technique de l'usage de gameQuery:

K-Stet/fr/entry/92008-05-18 13:28:35

K-Stet est un petit jeux en OpenGL que j'ai écris il y a bon moment. Il n'est pas vraiment finit et ne le sera probablement jamais... Le code est fournit. Il est construit sur le framework OpenGL et utilise SDL pour la lectures des images, il ne devrait donc pas être trop compliqué de le compiler sur n'importe quelle plate-forme cependant seul les binaire win32 sont fournis.

Le but de ce projet était de tester une idée de gameplay qui s'est révélée ne pas être aussi amusante que prévu. Dans ce jeux vous contrôlez la rotation d'une plate-forme sut laquelle tombe des goûtes d'eau colorées. Le but est de guider les goûtes vers le trous de la même couleur. Essayez-le, peut-être que vous le trouver plus amusant que moi! Comme d'habitude, les commentaire sont bienvenue.

K-Stet 0.15
K-Stet 0.1
K-Stet 0.05