Gran diferencia de rendimiento al usar drawImage con IMG vs CANVAS


8

Reuní un par de pruebas simples que representan una imagen en un lienzo. Uno representa desde un IMG, mientras que el otro representa desde un CANVAS fuera de pantalla. Puede ver el código y los resultados aquí: http://jsperf.com/canvas-rendering/2

En la mayoría de los navegadores, la representación desde una imagen es mucho más rápida que la representación desde un lienzo, excepto en Chrome, donde la situación se invierte. ¿Alguien puede explicar la razón de las diferencias? Después de todo, estamos representando los mismos datos de píxeles en el mismo destino.


2
No estoy realmente seguro de que esta sea una pregunta, o al menos una que podamos responder. Sin embargo, aparte de eso, al observar su prueba, parece que solo los demás son realmente lentos para representar objetos de lienzo, en lugar de que Chrome sea fuera de lo común para renderizar imágenes más lentamente.
Matt Kemp

Pero, ¿por qué hay alguna diferencia cuando en ambos casos están representando los mismos datos? Y el hecho de que al menos un navegador principal tenga la característica de rendimiento opuesta significa que necesitamos implementar dos rutas de código en nuestros renderizadores.
alekop

¿Podría agregar al renderizado de prueba sin buffercanvas ni etiqueta img? Sería interesante verlo.
justanotherhobbyist

@hustlerinc: ¿Te refieres a renderizar desde un lienzo sobre sí mismo? ¿Qué probaría eso? Todos los gráficos del juego se cargan a partir de imágenes, por lo que debes usar una imagen en algún momento del proceso.
alekop

@alekop No, me refiero a omitir el lienzo fuera de pantalla y solo usar un lienzo. Creo que en la web debería hacer que el renderizado sea más rápido, pero no tengo pruebas de ello. Y demasiado vago / inexperto para hacer la prueba yo mismo.
justanotherhobbyist

Respuestas:


9

OK, lo descubrí. Casi. En realidad es bastante obvio, y me siento un poco tonto por no notar esto de inmediato. Cuando llama drawImage(src, 0, 0)sin especificar el ancho / alto, dibuja toda la región src, que en este caso es mucho más grande (el lienzo es 320x420 frente al img a 185x70). Entonces, en el caso del lienzo, el navegador está haciendo mucho más trabajo, lo que explica el rendimiento más lento. Todavía estoy desconcertado por la puntuación más alta de Chrome con el src más grande.

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

He publicado una versión actualizada que usa las mismas regiones, y las diferencias están mucho más cercanas. http://jsperf.com/canvas-rendering/5

Todavía no puedo explicar por qué hay una diferencia, pero ahora es lo suficientemente pequeña como para que realmente no me importe.


En Chrome 43 con Windows 8 e Intel Graphics HD, se bloquea cuando se ejecuta la primera prueba. Cuando finaliza la prueba, drawImage (img) es el claro ganador, drawImage (canvas) es un 94% más lento.
banerban

Firefox 38 funciona sin problemas, sin ningún problema, y ​​ambas pruebas están cerca.
banerban

Si escala sus imágenes, también perjudica el rendimiento @alekop ( developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/… )
Jersh

4

Es probable que Chrome use aceleración de hardware.

Cree un lienzo 240x240 y ejecute su experimento en Chrome, luego cree un lienzo 300x300 y vuelva a hacerlo. El lienzo más grande que espero sea más rápido debido al hecho de que la aceleración de hardware se activa después de 256x256 y Chrome usa software cuando los tamaños son menores.

También vale la pena señalar que -webkit-transform: translateZ (0) desactiva la aceleración de hardware.

No he probado ninguno de los anteriores; Solo sé esto debido al hecho de que uno de los ingenieros de Chrome comentó un error que informé en Chrome cuando cruzas el umbral de hardware y software cambiando el tamaño dinámicamente del lienzo de mayor a menor que el límite de 256x256 o viceversa. La solución a este error fue desactivar la aceleración utilizando el translateZ como se mencionó anteriormente.

En mi caso, simplemente no permití que los usuarios redimensionaran menos de 256x256.


¿Apaga la aceleración de hardware? ¿No lo enciende?
gilbert-v

1

A veces, las imágenes pueden cargarse en la memoria de la GPU y el lienzo en la memoria del host. En ese caso, cuando dibuja desde la imagen al lienzo, los datos de la imagen deben copiarse primero en la memoria del host y luego en el lienzo.

Noté ese tipo de comportamiento con Chrome, cuando estaba escribiendo un proyecto que carga más de 100 millones de imágenes de píxeles y luego lee partes de ellas en un pequeño lienzo de 256x256 ( http://elhigu.github.io/canvas-image-tiles/ ).

En ese proyecto, si dibujaba directamente de la etiqueta de la imagen al lienzo en Chrome, la memoria siempre saltaba a ~ 1.5GB cuando comenzaba el dibujo y luego, cuando terminaba el dibujo, la memoria se liberaba nuevamente, incluso esa imagen de origen de 250 megapíxeles se mostraba todo el tiempo en la página.

Solucioné el problema escribiendo la imagen una vez en un lienzo grande (del mismo tamaño que la imagen) y luego dibujando un lienzo más pequeño desde allí (también tiré la imagen después de convertirla en lienzo).


0

No puedo explicar las diferencias, pero no estoy de acuerdo con

Y el hecho de que al menos un navegador principal tenga la característica de rendimiento opuesta significa que necesitamos implementar dos rutas de código en nuestros renderizadores. - alekop

Si observa los resultados en js.pref, las diferencias en Chrome son bastante sutiles. Me quedaría con solo renderizar desde una imagen cuando sea posible.


El problema es que confío en lienzos fuera de pantalla para componer imágenes complejas. Por ejemplo, renderizo los cuadros de animación de un personaje en un búfer fuera de la pantalla, y luego renderizo cosas como ropa / armadura / armas encima. El juego luego se representa desde el lienzo compuesto, en lugar de volver a representar todos estos detalles para cada personaje, cada cuadro. Dado que el rendimiento de lienzo a lienzo es tan pobre en los navegadores que no son de Chrome, tendría que volver a convertir el compuesto en una imagen. No es el fin del mundo, pero esperaba que haya una solución alternativa.
alekop

0

El tamaño de la imagen es 185 * 70, pero creamos un lienzo con tamaño, creo que esto desperdiciará algo de rendimiento, por lo que configuré el tamaño del lienzo fuera de la pantalla igual que la imagen. Y la diferencia está más cerca.

var g_offscreenCanvas = createCanvas(185, 70);

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

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.