Enorme differenza di prestazioni quando si utilizza drawImage con IMG vs CANVAS


8

Ho messo insieme un paio di semplici test che rendono un'immagine su una tela. Uno esegue il rendering da un IMG, mentre l'altro esegue il rendering da un TELA fuori schermo. Puoi vedere il codice e i risultati qui: http://jsperf.com/canvas-rendering/2

Nella maggior parte dei browser il rendering da un'immagine è molto più veloce del rendering da un'area di disegno, tranne in Chrome, dove la situazione è invertita. Qualcuno può spiegare il motivo delle differenze? Dopotutto, stiamo eseguendo il rendering degli stessi dati pixel nella stessa destinazione.


2
Non sono davvero sicuro che questa sia una domanda, o almeno una a cui possiamo rispondere. A parte questo, guardando il tuo test sembra che solo gli altri siano molto lenti nel rendere gli oggetti della tela, piuttosto che Chrome sia fuori dall'ordinario per rendere le immagini più lente.
Matt Kemp,

Ma perché c'è una differenza quando in entrambi i casi vengono visualizzati gli stessi dati? E il fatto che almeno un browser principale abbia le caratteristiche di prestazioni opposte significa che dobbiamo implementare due percorsi di codice nei nostri renderer.
alekop,

Potresti aggiungere al rendering di test senza buffercanvas e tag img? Sarebbe interessante da vedere.
justanotherhobbyist,

@hustlerinc: vuoi dire rendering da una tela su se stesso? Cosa proverebbe? Tutta la grafica del gioco viene caricata dalle immagini, quindi è necessario utilizzare un'immagine ad un certo punto del processo.
alekop,

@alekop No, intendo saltare la tela fuori schermo e usare solo una tela. Penso che nel web dovrebbe rendere il rendering più veloce, ma non ne ho le prove. E troppo pigro / inesperto per fare il test da solo.
justanotherhobbyist,

Risposte:


9

OK, l'ho capito. Quasi. In realtà è abbastanza ovvio, e mi sento un po 'stupido per non averlo notato subito. Quando chiami drawImage(src, 0, 0)senza specificare larghezza / altezza, disegna l'intera regione src, che in questo caso è molto più grande (la tela è 320x420 contro l'img a 185x70). Quindi nel caso canvas il browser sta facendo molto più lavoro, il che spiega le prestazioni più lente. Sono ancora perplesso dal punteggio più alto di Chrome con il più grande src.

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

Ho pubblicato una versione aggiornata che utilizza le stesse regioni e le differenze sono molto più vicine. http://jsperf.com/canvas-rendering/5

Non riesco ancora a spiegare perché c'è una differenza, ma ora è abbastanza piccolo che non mi interessa davvero.


Su Chrome 43 con Windows 8 e Intel Graphics HD si arresta in modo anomalo quando viene eseguito il primo test. Al termine del test drawImage (img) è il vincitore chiaro, drawImage (canvas) è più lento del 94%.
Șerban

Firefox 38 funziona senza problemi, nessun problema, ed entrambi i test sono vicini.
Șerban

Se ridimensioni le tue immagini, anche le prestazioni danneggiano @alekop ( developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/… )
Jersh

4

È probabile che Chrome utilizzi l'accelerazione hardware.

Crea una tela 240x240 ed esegui l'esperimento in Chrome, quindi crea una tela 300x300 e fallo di nuovo. La tela più grande mi aspetto di essere più veloce a causa dell'accelerazione hardware che inizia dopo 256x256 e Chrome utilizza software quando le dimensioni sono inferiori.

Vale anche la pena sottolineare che -webkit-transform: translateZ (0) disattiva l'accelerazione hardware.

Non ho testato nessuno dei precedenti; Lo so solo per il fatto che uno degli ingegneri di Chrome ha commentato un bug che ho segnalato in Chrome quando si supera la soglia hardware e software ridimensionando dinamicamente il canvas da più grande a più piccolo del limite 256x256 o viceversa. La soluzione a questo bug era disattivare l'accelerazione usando la translateZ come menzionato sopra.

Nel mio caso, semplicemente non consentivo agli utenti di ridimensionare meno di 256x256.


Turns off accelerazione hardware? Non lo accende?
Gilbert-V,

1

A volte le immagini possono essere caricate nella memoria GPU e nell'area di disegno nella memoria host. In tal caso, quando si disegna da un'immagine all'area di disegno, i dati dell'immagine devono essere prima copiati nella memoria host e quindi nell'area di disegno.

Ho notato quel tipo di comportamento con Chrome, quando stavo scrivendo un progetto che carica immagini di oltre 100 milioni di pixel e poi ne legge parti su piccole tele 256x256 ( http://elhigu.github.io/canvas-image-tiles/ ).

In quel progetto se passassi direttamente dal tag immagine alla tela in Chrome, la memoria saliva sempre a ~ 1,5 GB quando il disegno iniziava e poi quando il disegno terminava la memoria veniva liberata di nuovo, anche quell'immagine sorgente da 250 megapixel veniva mostrata continuamente nella pagina.

Ho risolto il problema scrivendo l'immagine una volta su tela grande (stessa dimensione con l'immagine) e quindi disegnando tela più piccola da lì (ho anche gettato via l'immagine dopo averla convertita in tela).


0

Non riesco a spiegare le differenze, ma non sono d'accordo

E il fatto che almeno un browser principale abbia le caratteristiche di prestazioni opposte significa che dobbiamo implementare due percorsi di codice nei nostri renderer. - alekop

Se guardi i risultati su js.pref, le differenze in chrome sono abbastanza sottili. Se possibile, mi limiterei a eseguire il rendering da un'immagine.


Il problema è che mi affido alle tele fuori schermo per comporre immagini complesse. Ad esempio, sto trasformando i fotogrammi di animazione di un personaggio in un buffer fuori schermo e quindi eseguendo il rendering di cose come vestiti / armature / armi. Il gioco esegue quindi il rendering dalla tela composita, piuttosto che dal rendering di tutti questi dettagli per ciascun personaggio, ogni fotogramma. Dato che le prestazioni canvas-to-canvas sono così scarse nei browser non Chrome, dovrei ripristinare il composito in un'immagine. Non è la fine del mondo, ma speravo che ci fosse una soluzione alternativa.
alekop,

0

Le dimensioni dell'immagine sono 185 * 70 ma creiamo una tela con dimensioni, penso che questo sprecherà alcune prestazioni, quindi ho impostato le dimensioni della tela fuori schermo uguali all'immagine. E la differenza è più vicina.

var g_offscreenCanvas = createCanvas(185, 70);

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

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.