Perbedaan kinerja sangat besar saat menggunakan drawImage dengan IMG vs CANVAS

8

Saya mengumpulkan beberapa tes sederhana yang membuat gambar ke kanvas. Satu merender dari IMG, sementara yang lain merender dari CANVAS offscreen. Anda dapat melihat kode dan hasilnya di sini: http://jsperf.com/canvas-rendering/2

Di sebagian besar peramban, rendering dari gambar jauh lebih cepat daripada rendering dari kanvas, kecuali di Chrome, di mana situasinya terbalik. Adakah yang bisa menjelaskan alasan perbedaannya? Bagaimanapun, kami membuat data piksel yang sama ke tujuan yang sama.

alekop
sumber
2
Saya tidak begitu yakin ini pertanyaan, atau setidaknya satu yang bisa kami jawab. Selain itu, melihat pengujian Anda, tampaknya hanya yang lain benar-benar lambat dalam merender objek kanvas, alih-alih Chrome yang berada di luar kebiasaan karena rendering gambar lebih lambat.
Matt Kemp
Tetapi mengapa ada perbedaan sama sekali ketika dalam kedua kasus mereka memberikan data yang sama? Dan fakta bahwa setidaknya satu browser utama memiliki karakteristik kinerja yang berlawanan berarti kita perlu menerapkan dua jalur kode dalam penyaji kami.
alekop
Bisakah Anda menambahkan rendering tes tanpa buffercanvas dan tag img? Akan menarik untuk dilihat.
justanotherhobbyist
@Hustlerinc: Maksud Anda render dari kanvas ke dirinya sendiri? Apa buktinya? Semua grafis gim dimuat dari gambar, jadi Anda harus menggunakan gambar di beberapa titik dalam proses.
alekop
@alekop Tidak, maksudku melewatkan kanvas offscreen dan hanya menggunakan satu kanvas. Saya pikir di web itu harus membuat rendering lebih cepat, tetapi tidak punya bukti untuk itu. Dan terlalu malas / tidak berpengalaman untuk melakukan tes sendiri.
justanotherhobbyist

Jawaban:

9

OK, saya menemukan jawabannya. Hampir. Ini sebenarnya cukup jelas, dan saya merasa agak bodoh karena tidak segera menyadarinya. Saat Anda menelepon drawImage(src, 0, 0)tanpa menentukan lebar / tinggi, ia akan menarik seluruh wilayah src, yang dalam kasus ini jauh lebih besar (kanvasnya 320x420 versus img pada 185x70). Jadi dalam kasus kanvas browser melakukan lebih banyak pekerjaan, yang menjelaskan kinerja lebih lambat. Saya masih bingung dengan skor Chrome yang lebih tinggi dengan src yang lebih besar.

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

Saya telah memposting versi terbaru yang menggunakan wilayah yang sama, dan perbedaannya jauh lebih dekat. http://jsperf.com/canvas-rendering/5

Saya masih tidak bisa menjelaskan mengapa ada perbedaan, tetapi sekarang cukup kecil sehingga saya tidak begitu peduli.

alekop
sumber
Pada Chrome 43 dengan Windows 8 dan Intel Graphics HD, crash ketika tes pertama berjalan. Ketika tes selesai drawImage (img) adalah pemenang yang jelas, drawImage (kanvas) menjadi 94% lebih lambat.
Șerban
Firefox 38 berjalan dengan lancar, tidak ada masalah sama sekali, dan kedua tes sudah dekat.
Șerban
Jika Anda skala gambar Anda itu sakit kinerja juga @alekop ( developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/… )
Jersh
4

Chrome kemungkinan akan menggunakan akselerasi perangkat keras.

Buat kanvas 240x240 dan jalankan percobaan Anda di Chrome lalu buat kanvas 300x300 dan lakukan lagi. Kanvas yang lebih besar saya perkirakan lebih cepat karena akselerasi perangkat keras muncul setelah 256x256 dan chrome menggunakan perangkat lunak ketika ukurannya lebih kecil.

Juga layak untuk menunjukkan bahwa -webkit-transform: translateZ (0) mematikan akselerasi perangkat keras.

Saya belum menguji salah satu di atas; Saya hanya tahu ini karena salah satu insinyur chrome mengomentari bug yang saya laporkan dalam chrome ketika Anda melewati ambang perangkat keras dan perangkat lunak dengan secara dinamis mengubah ukuran kanvas dari yang lebih besar ke yang lebih kecil daripada batas 256x256 atau sebaliknya. Solusi untuk bug ini adalah mematikan akselerasi menggunakan translateZ seperti yang disebutkan di atas.

Dalam kasus saya, saya tidak mengizinkan pengguna untuk mengubah ukuran kurang dari 256x256.

John
sumber
Bergantian off akselerasi hardware? Bukankah itu menyalakannya?
gilbert-v
1

Terkadang gambar mungkin dimuat ke memori GPU dan kanvas dalam memori host. Dalam hal ini ketika Anda menggambar dari gambar ke kanvas data gambar harus disalin terlebih dahulu ke memori host dan kemudian ke kanvas.

Saya memperhatikan perilaku semacam itu dengan Chrome, ketika saya sedang menulis proyek yang memuat lebih dari 100 juta piksel gambar dan kemudian membacanya sebagian ke kanvas 256x256 kecil ( http://elhigu.github.io/canvas-image-tiles/ ).

Dalam proyek itu jika saya menggambar langsung dari tag gambar ke kanvas di Chrome, memori selalu melonjak hingga ~ 1,5GB ketika menggambar dimulai dan kemudian ketika menggambar berakhir, memori dibebaskan lagi, bahkan gambar sumber 250 megapiksel itu ditampilkan sepanjang waktu di halaman.

Saya memperbaiki masalah dengan menulis gambar sekali ke kanvas besar (ukuran yang sama dengan gambar) dan kemudian menggambar kanvas yang lebih kecil dari sana (saya juga membuang gambar setelah mengubahnya menjadi kanvas).

Mikael Lepistö
sumber
0

Tidak bisa menjelaskan perbedaannya, tapi saya tidak setuju

Dan fakta bahwa setidaknya satu browser utama memiliki karakteristik kinerja yang berlawanan berarti kita perlu menerapkan dua jalur kode dalam penyaji kami. - alekop

Jika Anda melihat hasil pada js.pref perbedaan dalam krom cukup halus. Saya akan tetap dengan hanya rendering dari gambar jika memungkinkan.

drenith
sumber
Masalahnya adalah saya mengandalkan kanvas dari layar untuk membuat gambar yang kompleks. Sebagai contoh, saya merender frame animasi karakter ke buffer layar, dan kemudian merender hal-hal seperti pakaian / baju besi / senjata di atas. Permainan kemudian merender dari kanvas komposit, alih-alih merender ulang semua detail untuk setiap karakter, setiap frame. Dengan kinerja kanvas-ke-kanvas yang begitu buruk di browser non-Chrome, saya harus merender komposit kembali menjadi gambar. Ini bukan akhir dunia, tetapi saya berharap ada solusinya.
alekop
0

Ukuran gambar adalah 185 * 70 tetapi kami membuat kanvas dengan ukuran, saya pikir ini akan membuang-buang kinerja, jadi saya mengatur ukuran kanvas offscreen sama seperti gambar. Dan perbedaannya lebih dekat.

var g_offscreenCanvas = createCanvas(185, 70);

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

shuizhongyuemin
sumber