Grande différence de performances lors de l'utilisation de drawImage avec IMG vs CANVAS


8

J'ai mis en place quelques tests simples qui rendent une image sur une toile. L'un rend à partir d'un IMG, tandis que l'autre rend à partir d'un CANVAS hors écran. Vous pouvez voir le code et les résultats ici: http://jsperf.com/canvas-rendering/2

Dans la plupart des navigateurs, le rendu à partir d'une image est beaucoup plus rapide que le rendu à partir d'un canevas, sauf dans Chrome, où la situation est inversée. Quelqu'un peut-il expliquer la raison des différences? Après tout, nous rendons les mêmes données de pixels à la même destination.


2
Je ne suis pas vraiment sûr que ce soit une question, ou du moins une réponse à laquelle nous pourrions répondre. En dehors de cela cependant, en regardant votre test, il semble que seuls les autres sont vraiment lents à rendre les objets de canevas, plutôt que Chrome soit hors de l'ordinaire pour rendre les images plus lentement.
Matt Kemp

Mais pourquoi y a-t-il une différence quand dans les deux cas ils rendent les mêmes données? Et le fait qu'au moins un navigateur majeur ait la caractéristique de performance opposée signifie que nous devons implémenter deux chemins de code dans nos moteurs de rendu.
alekop

Pourriez-vous ajouter au rendu de test sans buffercanvas et balise img? Serait intéressant de voir.
justanotherhobbyist

@hustlerinc: Vous voulez dire le rendu d'une toile sur elle-même? Qu'est-ce que cela prouverait? Tous les graphiques du jeu sont chargés à partir d'images, vous devez donc utiliser une image à un moment donné du processus.
alekop

@alekop Non, je veux dire sauter le canevas hors écran et utiliser un seul canevas. Je pense que dans le Web, cela devrait rendre le rendu plus rapide, mais je n'ai pas de preuve pour cela. Et trop paresseux / inexpérimenté pour faire le test moi-même.
justanotherhobbyist

Réponses:


9

OK, je l'ai compris. Presque. C'est en fait assez évident, et je me sens un peu stupide de ne pas l'avoir remarqué tout de suite. Lorsque vous appelez drawImage(src, 0, 0)sans spécifier de largeur / hauteur, il dessine toute la région src, qui dans ce cas est beaucoup plus grande (le canevas est de 320x420 contre l'img à 185x70). Ainsi, dans le cas du canevas, le navigateur fait beaucoup plus de travail, ce qui explique le ralentissement des performances. Je suis toujours perplexe devant le score plus élevé de Chrome avec le plus grand src.

dest.drawImage(src, x, y) // bad
dest.drawImage(src, x, y, w, h, destX, destY, w, h) // good

J'ai publié une version mise à jour qui utilise les mêmes régions, et les différences sont beaucoup plus proches. http://jsperf.com/canvas-rendering/5

Je ne peux toujours pas expliquer pourquoi il y a une différence, mais elle est maintenant suffisamment petite pour que je m'en fiche vraiment.


Sur Chrome 43 avec Windows 8 et Intel Graphics HD, il se bloque lors du premier test. Lorsque le test se termine, drawImage (img) est clairement le gagnant, drawImage (canvas) étant 94% plus lent.
Șerban

Firefox 38 fonctionne sans problème, aucun problème et les deux tests sont proches.
Șerban

Si vous redimensionnez vos images, cela nuit également aux performances @alekop ( developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/… )
Jersh

4

Chrome est susceptible d'utiliser l'accélération matérielle.

Créez un canevas 240x240 et exécutez votre test dans Chrome, puis créez un canevas 300x300 et recommencez. La toile plus grande que je m'attends à être plus rapide en raison du fait que l'accélération matérielle se déclenche après 256x256 et que Chrome utilise un logiciel lorsque les tailles sont inférieures.

Il convient également de souligner que -webkit-transform: translateZ (0) désactive l'accélération matérielle.

Je n'ai testé aucun des éléments ci-dessus; Je le sais uniquement en raison du fait qu'un des ingénieurs de Chrome a commenté un bogue que j'ai signalé dans Chrome lorsque vous franchissez le seuil matériel et logiciel en redimensionnant dynamiquement le canevas de plus grand à plus petit que la limite 256x256 ou vice-versa. La solution à ce bogue était de désactiver l'accélération en utilisant translateZ comme mentionné ci-dessus.

Dans mon cas, je n'ai tout simplement pas autorisé les utilisateurs à redimensionner moins de 256x256.


Tours hors accélération matérielle? Ça ne l’allume pas?
gilbert-v

1

Parfois, les images peuvent être chargées dans la mémoire du GPU et dans la mémoire de l'hôte. Dans ce cas, lorsque vous dessinez de l'image vers le canevas, les données d'image doivent être copiées d'abord dans la mémoire hôte, puis dans le canevas.

J'ai remarqué ce genre de comportement avec Chrome, lorsque j'écrivais un projet qui charge plus de 100 millions d'images pixel, puis en lit des parties sur un petit canevas 256 x 256 ( http://elhigu.github.io/canvas-image-tiles/ ).

Dans ce projet, si je dessinais directement de la balise d'image vers le canevas dans Chrome, la mémoire sautait toujours à ~ 1,5 Go au début du dessin, puis à la fin du dessin, la mémoire était à nouveau libérée, même cette image source de 250 mégapixels était affichée tout le temps dans la page.

J'ai résolu le problème en écrivant une fois l'image sur une grande toile (même taille avec l'image), puis en dessinant une petite toile à partir de là (j'ai également jeté l'image après l'avoir convertie en toile).


0

Je ne peux pas expliquer les différences, mais je ne suis pas d'accord avec

Et le fait qu'au moins un navigateur majeur ait la caractéristique de performance opposée signifie que nous devons implémenter deux chemins de code dans nos moteurs de rendu. - alekop

Si vous regardez les résultats sur le js.pref, les différences de chrome sont assez subtiles. Je préfère le rendu à partir d'une image lorsque cela est possible.


Le problème est que je compte sur des toiles hors écran pour composer des images complexes. Par exemple, je rend les images d'animation d'un personnage dans un tampon hors écran, puis je rends des choses comme des vêtements / armures / armes sur le dessus. Le jeu effectue ensuite un rendu à partir de la toile composite, plutôt que de restituer tous ces détails pour chaque personnage, chaque image. Avec des performances toile à toile si médiocres dans les navigateurs non Chrome, je devrais restituer le composite dans une image. Ce n'est pas la fin du monde, mais j'espérais qu'il existe une solution.
alekop

0

La taille de l'image est de 185 * 70 mais nous créons un canevas avec la taille, je pense que cela gâchera certaines performances, donc j'ai réglé la taille du canevas hors écran de la même manière que l'image. Et la différence est plus proche.

var g_offscreenCanvas = createCanvas(185, 70);

http://jsperf.com/canvas-rendering/60

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.