Großer Leistungsunterschied bei Verwendung von drawImage mit IMG vs CANVAS

8

Ich habe ein paar einfache Tests zusammengestellt, die ein Bild auf eine Leinwand rendern. Einer rendert von einem IMG, während der andere von einem Offscreen-CANVAS rendert. Sie können den Code und die Ergebnisse hier sehen: http://jsperf.com/canvas-rendering/2

In den meisten Browsern ist das Rendern von einem Bild viel schneller als das Rendern von einer Leinwand, außer in Chrome, wo die Situation umgekehrt ist. Kann jemand den Grund für die Unterschiede erklären? Schließlich rendern wir dieselben Pixeldaten an dasselbe Ziel.

Alekop
quelle
2
Ich bin mir nicht sicher, ob dies eine Frage ist oder zumindest eine, die wir beantworten könnten. Abgesehen davon sieht es bei Ihrem Test so aus, als ob nur die anderen beim Rendern von Canvas-Objekten sehr langsam sind, anstatt dass Chrome ungewöhnlich ist, um Bilder langsamer zu rendern.
Matt Kemp
Aber warum gibt es überhaupt einen Unterschied, wenn in beiden Fällen dieselben Daten gerendert werden? Und die Tatsache, dass mindestens ein Hauptbrowser die entgegengesetzten Leistungsmerkmale aufweist, bedeutet, dass wir zwei Codepfade in unseren Renderern implementieren müssen.
Alekop
Könnten Sie das Test-Rendering ohne Buffercanvas und img-Tag ergänzen? Wäre interessant zu sehen.
Justanotherhobbyist
@hustlerinc: Du meinst von einer Leinwand auf sich selbst rendern? Was würde das beweisen? Alle Grafiken des Spiels werden aus Bildern geladen, sodass Sie zu einem bestimmten Zeitpunkt ein Bild verwenden müssen.
Alekop
@alekop Nein, ich meine, die Offscreen-Leinwand überspringen und nur eine Leinwand verwenden. Ich denke, im Web sollte es das Rendern beschleunigen, aber es gibt keinen Beweis dafür. Und zu faul / unerfahren, um den Test selbst durchzuführen.
Justanotherhobbyist

Antworten:

9

OK, ich habe es herausgefunden. Fast. Es ist eigentlich ganz offensichtlich, und ich fühle mich ein bisschen dumm, weil ich das nicht sofort bemerkt habe. Wenn Sie drawImage(src, 0, 0)ohne Angabe von Breite / Höhe aufrufen , wird der gesamte src-Bereich gezeichnet, der in diesem Fall viel größer ist (die Zeichenfläche ist 320 x 420 gegenüber dem Bild bei 185 x 70). Im Canvas-Fall erledigt der Browser also viel mehr Arbeit, was die langsamere Leistung erklärt. Ich bin immer noch verwirrt über die höhere Punktzahl von Chrome mit dem größeren src.

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

Ich habe eine aktualisierte Version veröffentlicht, die dieselben Regionen verwendet, und die Unterschiede sind viel enger. http://jsperf.com/canvas-rendering/5

Ich kann immer noch nicht erklären, warum es einen Unterschied gibt, aber es ist jetzt klein genug, dass es mich nicht wirklich interessiert.

Alekop
quelle
Unter Chrome 43 mit Windows 8 und Intel Graphics HD stürzt es ab, wenn der erste Test ausgeführt wird. Wenn der Test beendet ist, ist drawImage (img) der klare Gewinner, wobei drawImage (canvas) 94% langsamer ist.
Șerban
Firefox 38 läuft reibungslos, überhaupt keine Probleme, und beide Tests sind nahe beieinander.
Șerban
Wenn Sie Ihre Bilder skalieren, beeinträchtigt dies auch die Leistung @alekop ( developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/… )
Jersh
4

Chrome verwendet wahrscheinlich die Hardwarebeschleunigung.

Erstellen Sie eine Leinwand 240 x 240 und führen Sie Ihr Experiment in Chrome aus. Erstellen Sie dann eine Leinwand 300 x 300 und wiederholen Sie den Vorgang. Die größere Leinwand, von der ich erwarte, dass sie schneller ist, da die Hardwarebeschleunigung nach 256 x 256 einsetzt und Chrome Software verwendet, wenn die Größen kleiner sind.

Es ist auch erwähnenswert, dass -webkit-transform: translateZ (0) die Hardwarebeschleunigung deaktiviert.

Ich habe keines der oben genannten getestet. Ich weiß das nur, weil einer der Chrome-Ingenieure einen Fehler kommentiert hat, den ich in Chrome gemeldet habe, als Sie den Hardware- und Software-Schwellenwert überschritten haben, indem Sie die Größe der Zeichenfläche dynamisch von größer auf kleiner als die 256x256-Grenze geändert haben oder umgekehrt. Die Lösung für diesen Fehler bestand darin, die Beschleunigung mit dem oben erwähnten translateZ auszuschalten.

In meinem Fall habe ich Benutzern einfach nicht erlaubt, die Größe von weniger als 256 x 256 zu ändern.

John
quelle
Turns off - Hardware - Beschleunigung? Schaltet es es nicht ein?
Gilbert-v
1

Manchmal werden Bilder in den GPU-Speicher und die Zeichenfläche im Host-Speicher geladen. In diesem Fall müssen beim Zeichnen von Bild zu Zeichenfläche die Bilddaten zuerst in den Hostspeicher und dann in die Zeichenfläche kopiert werden.

Ich habe diese Art von Verhalten bei Chrome bemerkt, als ich ein Projekt schrieb, das über 100 Millionen Pixelbilder lädt und dann Teile davon auf eine kleine Leinwand mit 256 x 256 ( http://elhigu.github.io/canvas-image-tiles/ ) liest .

Wenn ich in diesem Projekt in Chrome direkt vom Bild-Tag auf die Leinwand zeichnete, sprang der Speicher beim Starten des Zeichnens immer auf ~ 1,5 GB, und wenn das Zeichnen beendet wurde, wurde der Speicher wieder freigegeben, selbst das 250-Megapixel-Quellbild wurde die ganze Zeit auf der Seite angezeigt.

Ich habe das Problem behoben, indem ich das Bild einmal auf eine große Leinwand geschrieben habe (gleiche Größe wie das Bild) und dann eine kleinere Leinwand von dort gezeichnet habe (ich habe das Bild auch weggeworfen, nachdem ich es in Leinwand konvertiert hatte).

Mikael Lepistö
quelle
0

Ich kann die Unterschiede nicht erklären, aber ich würde dem nicht zustimmen

Und die Tatsache, dass mindestens ein Hauptbrowser die entgegengesetzten Leistungsmerkmale aufweist, bedeutet, dass wir zwei Codepfade in unseren Renderern implementieren müssen. - Alekop

Wenn Sie sich die Ergebnisse auf dem js.pref ansehen, sind die Unterschiede in Chrom ziemlich subtil. Wenn möglich, bleibe ich beim Rendern von einem Bild.

Drenith
quelle
Das Problem ist, dass ich mich auf Offscreen-Leinwände verlasse, um komplexe Bilder zu erstellen. Zum Beispiel rendere ich die Animationsrahmen eines Charakters in einen Offscreen-Puffer und rendere dann Dinge wie Kleidung / Rüstung / Waffen darüber. Das Spiel wird dann von der zusammengesetzten Leinwand gerendert, anstatt alle diese Details für jeden Charakter, jeden Frame neu zu rendern. Da die Leistung von Leinwand zu Leinwand in Nicht-Chrome-Browsern so schlecht ist, müsste ich den Verbund wieder in ein Bild rendern. Es ist nicht das Ende der Welt, aber ich hatte gehofft, dass es eine Problemumgehung gibt.
Alekop
0

Das Bild hat eine Größe von 185 * 70, aber wir erstellen eine Leinwand mit einer Größe. Ich denke, dies wird etwas Leistung verschwenden. Deshalb habe ich die Größe der Leinwand außerhalb des Bildschirms auf das gleiche Bild eingestellt. Und der Unterschied ist näher.

var g_offscreenCanvas = createCanvas(185, 70);

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

Shuizhongyuemin
quelle