Diferença de desempenho enorme ao usar drawImage com IMG vs CANVAS

8

Eu montei alguns testes simples que renderizam uma imagem em uma tela. Um processa a partir de um IMG, enquanto o outro processa a partir de uma tela fora da tela. Você pode ver o código e os resultados aqui: http://jsperf.com/canvas-rendering/2

Na maioria dos navegadores, a renderização de uma imagem é muito mais rápida que a renderização de uma tela, exceto no Chrome, onde a situação é inversa. Alguém pode explicar o motivo das diferenças? Afinal, estamos renderizando os mesmos dados de pixel no mesmo destino.

alekop
fonte
2
Não tenho muita certeza de que seja uma pergunta ou pelo menos uma que possamos responder. Além disso, olhando para o seu teste, parece que apenas os outros são realmente lentos em renderizar objetos de tela, em vez de o Chrome ser incomum para renderizar imagens mais lentamente.
Matt Kemp
Mas por que há alguma diferença quando, em ambos os casos, eles estão renderizando os mesmos dados? E o fato de pelo menos um navegador principal ter a característica de desempenho oposta significa que precisamos implementar dois caminhos de código em nossos renderizadores.
12123 alekop
Você poderia adicionar à renderização de teste sem buffercanvas e tag img? Seria interessante ver.
justanotherhobbyist
@ustustinc: Você quer dizer renderizar de uma tela para si mesma? O que isso provaria? Todos os gráficos do jogo são carregados de imagens, então você precisa usar uma imagem em algum momento do processo.
alekop
@alekop Não, quero dizer pular a tela fora da tela e usar apenas uma tela. Eu acho que na web isso deve tornar a renderização mais rápida, mas não há provas disso. E com preguiça / inexperiência para fazer o teste sozinho.
justanotherhobbyist

Respostas:

9

OK, eu descobri. Quase. Na verdade, é bastante óbvio, e me sinto um pouco idiota por não perceber isso imediatamente. Quando você chama drawImage(src, 0, 0)sem especificar largura / altura, ela desenha toda a região src, que neste caso é muito maior (a tela é 320x420 versus a img em 185x70). Portanto, no caso da tela, o navegador está fazendo muito mais trabalho, o que explica o desempenho mais lento. Ainda estou intrigado com a pontuação mais alta do Chrome com o src maior.

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

Publiquei uma versão atualizada que usa as mesmas regiões e as diferenças são muito mais próximas. http://jsperf.com/canvas-rendering/5

Ainda não consigo explicar por que há uma diferença, mas agora é pequena o suficiente para que eu realmente não me importo.

alekop
fonte
No Chrome 43 com Windows 8 e Intel Graphics HD, ele falha quando o primeiro teste é executado. Quando o teste termina, drawImage (img) é o vencedor, sendo drawImage (canvas) 94% mais lento.
Șerban #
O Firefox 38 funciona sem problemas, sem problemas e os dois testes estão próximos.
Șerban #
Se você dimensionar suas imagens dói desempenho bem @alekop ( developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/... )
Jersh
4

É provável que o Chrome use a aceleração de hardware.

Crie uma tela de 240 x 240 e execute sua experiência no Chrome. Crie uma tela de 300 x 300 e faça-a novamente. A tela maior que eu espero ser mais rápida, devido ao fato de a aceleração do hardware aparecer após 256x256 e o ​​chrome usar software quando os tamanhos forem menores.

Também vale ressaltar que -webkit-transform: translateZ (0) desativa a aceleração de hardware.

Eu não testei nenhuma das opções acima; Eu só sei disso devido ao fato de um dos engenheiros do Chrome ter comentado um bug relatado no Chrome quando você ultrapassa o limite de hardware e software, redimensionando dinamicamente a tela de maior para menor que o limite de 256x256 ou vice-versa. A solução para esse bug foi desativar a aceleração usando o translateZ, como mencionado acima.

No meu caso, simplesmente não permiti que os usuários redimensionassem menos de 256x256.

John
fonte
Voltas fora a aceleração de hardware? Não liga?
Gilbert-v
1

Às vezes, as imagens podem ser carregadas na memória da GPU e na tela da memória do host. Nesse caso, quando você desenha da imagem para a tela, os dados da imagem devem ser copiados primeiro para hospedar a memória e depois para a tela.

Percebi esse tipo de comportamento com o Chrome, quando eu escrevia um projeto que carrega mais de 100 milhões de imagens de pixel e, em seguida, lê partes delas em telas pequenas de 256x256 ( http://elhigu.github.io/canvas-image-tiles/ ).

Nesse projeto, se eu desenhasse diretamente da tag de imagem para a tela no Chrome, a memória sempre aumentava para ~ 1,5 GB quando o desenho era iniciado e, quando o desenho terminava, a memória era liberada novamente, mesmo a imagem de origem de 250 megapixels era mostrada o tempo todo na página.

Corrigi o problema gravando uma vez a imagem em tela grande (mesmo tamanho da imagem) e desenhando uma tela menor a partir daí (também joguei a imagem fora depois de convertê-la em tela).

Mikael Lepistö
fonte
0

Não posso explicar as diferenças, mas eu discordo de

E o fato de pelo menos um navegador principal ter a característica de desempenho oposta significa que precisamos implementar dois caminhos de código em nossos renderizadores. - alekop

Se você observar os resultados no js.pref, as diferenças no chrome são bastante sutis. Eu ficaria com apenas renderização de uma imagem, quando possível.

drenith
fonte
O problema é que estou contando com telas fora da tela para compor imagens complexas. Por exemplo, estou transformando os quadros de animação de um personagem em um buffer fora da tela e, em seguida, processando coisas como roupas / armaduras / armas por cima. O jogo é renderizado a partir da tela composta, em vez de renderizar novamente todos esses detalhes para cada personagem, cada quadro. Como o desempenho tela a tela é tão ruim em navegadores que não são Chrome, eu teria que renderizar o composto novamente em uma imagem. Não é o fim do mundo, mas eu esperava que houvesse uma solução alternativa.
13123 alekop
0

O tamanho da imagem é 185 * 70, mas criamos uma tela com tamanho. Acho que isso desperdiçará um pouco de desempenho. Por isso, defino o tamanho da tela fora da tela igual à imagem. E a diferença está mais próxima.

var g_offscreenCanvas = createCanvas(185, 70);

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

shuizhongyuemin
fonte