Estoy trabajando en un pequeño videojuego que incluye un bucle de dibujo y experimenté un comportamiento extraño con MS Edge (pero también ocurre de otra manera en Chrome).
En un momento en particular, quiero obtener datos sobre píxeles y usar, solo una vez , ctx.getImageData()
un aumento de memoria de la CPU en este preciso momento (sé que esta función es costosa)... Pero durante todo el siguiente iteraciones del bucle, el tiempo de pintura aumenta drásticamente.
Aquí está el enlace: http://millgraphik.alwaysdata.net/cpu-test/
Y el código: http://millgraphik.alwaysdata.net/cpu-test/js/canvas.js
Puede probar con MS Edge, iniciar el registro de rendimiento justo después de cargar la página. getImageData se activa aproximadamente 3 segundos después de la carga de la página. Aquí está el resultado de la prueba de rendimiento:
en verde, la duración de la pintura
Entonces, ¿cuál podría ser la razón de este aumento del tiempo de pintura?
Chrome y Edge se basan actualmente en Chromium, que utiliza GPU para acelerar las operaciones de lienzo 2D. Si navega a edge://flags
o chrome://flags
(en Chrome) y deshabilita el lienzo 2D acelerado , notará que el comportamiento lento comienza instantáneamente en lugar de después de la llamada a getImageData()
. Esto se debe a que el lienzo ahora, de forma predeterminada, lo procesa su CPU.
Pero, ¿por qué comienza rápido y luego se ralentiza después de getImageData()
? Al usar la aceleración de GPU, parece que Chrome al menos cambia al renderizador basado en CPU después de usar getImageData()
.
Aquí hay una cita de un comentario de ticket de error de Chromium relacionado:
...
El problema en Chrome es que cada llamada a getImageData genera una lectura de la GPU, lo cual es una operación lenta. Tenemos una heurística de rendimiento que cambia los lienzos al modo de representación de software (sin GPU) cuando getImageData se usa mucho. El problema es que la heurística busca casos en los que se utilice getImageData en tres fotogramas de animación consecutivos. Necesitamos cambiar esa regla para que atrape casos como este.
Por lo tanto, su código está activando cualquier heurística que Chromium esté usando actualmente para detectar llamadas frecuentes a getImageData()
.
Pasamos a usar gpu para hacer todas las operaciones relacionadas con el lienzo hace unos años. Un problema que tenemos es que la GPU es significativamente más lenta que la CPU para ejecutar getImageData, pero la GPU es más rápida que la CPU para todas las demás operaciones de lienzo. Así que tomamos una decisión, la primera llamada a getImageData se ejecuta en la GPU y todas las llamadas posteriores a getImageData se ejecutan en la CPU. Esto se debe a que creemos que los usuarios pueden hacer más llamadas getImageData y queremos ser rápidos para manejarlas.
El resultado de la representación de software (CPU) es ligeramente diferente al resultado de la representación de GPU, esa es la diferencia que ha observado en las dos llamadas getImageData.
La bandera que encontraste, te permite especificar si getImageData se usará con frecuencia. Si lo hace, use la CPU; de lo contrario, use GPU (lo que significa que llamar a getImageData no provocará el cambio de renderizador). Así que claramente, ¡esto solucionaría el problema!
Todavía no estamos listos para lanzar esta característica, todavía en etapa de prueba. Planeamos lanzarlo en M94-95. Este problema habrá desaparecido para entonces.
¡Gracias por los comentarios!
Creé un lienzo fuera de pantalla con display: none;
y obtenga mis datos de píxeles con getImageData()
en este lienzo fuera de pantalla. Parece mantener la pintura con GPU.
Aquí está la nueva prueba: http://millgraphik.alwaysdata.net/cpu-test2/
y el código http://millgraphik.alwaysdata.net/cpu-test2/js/canvas.js
EDITAR: una mejor solución con un fragmento de lienzo: http://millgraphik.alwaysdata.net/cpu-test3/js/canvas.js
y la prueba de rendimiento en Edge (similar en Chrome):
¿Qué te parece esta nueva forma?