Kapan komputasi shader lebih efisien daripada pixel shader untuk pemfilteran gambar?

37

Operasi pemfilteran gambar seperti pengaburan, SSAO, bloom dan sebagainya biasanya dilakukan menggunakan pixel shader dan operasi "kumpulkan", di mana setiap pemanggilan shader pixel mengeluarkan sejumlah tekstur yang diambil untuk mengakses nilai-nilai piksel tetangga, dan menghitung nilai satu pixel dari hasil. Pendekatan ini memiliki inefisiensi teoretis di mana banyak pengambilan berlebihan dilakukan: doa shader terdekat akan mengambil ulang banyak dari texels yang sama.

Cara lain untuk melakukannya adalah dengan komputasi shader. Ini memiliki potensi keuntungan karena dapat berbagi sejumlah kecil memori di sekelompok doa shader. Misalnya, Anda dapat meminta setiap doa mengambil satu texel dan menyimpannya dalam memori bersama, lalu menghitung hasilnya dari sana. Ini mungkin atau mungkin tidak lebih cepat.

Pertanyaannya adalah dalam keadaan apa (jika pernah) apakah metode compute-shader sebenarnya lebih cepat daripada metode pixel-shader? Apakah ini tergantung pada ukuran kernel, operasi penyaringan seperti apa, dll? Jelas jawabannya akan bervariasi dari satu model GPU ke yang lain, tapi saya tertarik mendengar jika ada tren umum.

Nathan Reed
sumber
Saya pikir jawabannya adalah "selalu" jika komputasi shader dilakukan dengan benar. Ini bukan hal sepele untuk dicapai. Compute shader juga lebih cocok daripada pixel shader secara konseptual untuk algoritma pemrosesan gambar. Namun pixel shader memberikan lebih sedikit kelonggaran untuk menulis filter yang berkinerja buruk.
bernie
@bernie Bisakah Anda mengklarifikasi apa yang diperlukan agar penghitung komputasi "dilakukan dengan benar"? Mungkin menulis jawaban? Selalu bagus untuk mendapatkan lebih banyak perspektif tentang masalah ini. :)
Nathan Reed
2
Sekarang lihat apa yang kau lakukan padaku! :)
bernie
Selain berbagi pekerjaan di utas, kemampuan untuk menggunakan async compute adalah salah satu alasan utama untuk menggunakan compute shaders.
JarkkoL

Jawaban:

23

Keuntungan arsitektural dari komputasi shader untuk pemrosesan gambar adalah mereka melewatkan langkah ROP . Sangat mungkin bahwa menulis dari pixel shaders melewati semua perangkat keras pencampuran biasa bahkan jika Anda tidak menggunakannya. Komputasi shaders secara umum melewati jalur yang berbeda (dan seringkali lebih langsung) ke memori, sehingga Anda dapat menghindari kemacetan yang seharusnya Anda miliki. Saya pernah mendengar tentang kemenangan kinerja yang cukup besar yang dikaitkan dengan ini.

Kerugian arsitektural dari komputasi shader adalah bahwa GPU tidak lagi tahu item pekerjaan yang dipensiunkan ke piksel mana. Jika Anda menggunakan pixel shading pipeline, GPU memiliki peluang untuk mengemas pekerjaan ke warp / wavefront yang menulis ke area target render yang bersebelahan dalam memori (yang mungkin berupa ubin Z-order atau sesuatu seperti itu untuk kinerja alasan). Jika Anda menggunakan jalur komputasi, GPU mungkin tidak lagi bekerja dalam batch yang optimal, yang mengarah ke penggunaan bandwidth yang lebih banyak.

Anda mungkin dapat mengubah pengemasan warp / wavefront yang diubah menjadi keuntungan lagi, meskipun, jika Anda tahu bahwa operasi khusus Anda memiliki substruktur yang dapat Anda manfaatkan dengan mengemas pekerjaan terkait ke dalam grup utas yang sama. Seperti yang Anda katakan, secara teori Anda dapat memberikan contoh perangkat keras sampling dengan mengambil sampel satu nilai per lajur dan menempatkan hasilnya dalam memori bersama grup untuk jalur lain untuk diakses tanpa pengambilan sampel. Apakah ini kemenangan tergantung pada seberapa mahal memori grup Anda: jika lebih murah daripada cache tekstur tingkat terendah, maka ini mungkin menang, tetapi tidak ada jaminan untuk itu. GPU sudah menangani cukup baik dengan mengambil tekstur yang sangat lokal (karena kebutuhan).

Jika Anda memiliki tahap perantara dalam operasi di mana Anda ingin berbagi hasil, mungkin lebih masuk akal untuk menggunakan memori grup (karena Anda tidak dapat kembali ke perangkat keras pengambilan sampel tekstur tanpa benar-benar menuliskan hasil antara Anda ke memori). Sayangnya, Anda juga tidak dapat bergantung pada hasil dari grup utas lainnya, sehingga tahap kedua harus membatasi diri hanya pada apa yang tersedia di ubin yang sama. Saya pikir contoh kanonik di sini adalah menghitung pencahayaan rata-rata layar untuk eksposur otomatis. Saya juga bisa membayangkan menggabungkan upampling tekstur dengan beberapa operasi lain (karena upampling, tidak seperti downsampling dan kabur, tidak tergantung pada nilai di luar ubin yang diberikan).

John Calsbeek
sumber
Saya sangat meragukan ROP menambahkan overhead kinerja jika blending dinonaktifkan.
GroverManheim
@ GroverManheim Tergantung pada arsitektur! Langkah penggabungan output / ROP juga harus berurusan dengan jaminan pemesanan meskipun blending dinonaktifkan. Dengan segitiga layar penuh tidak ada bahaya pemesanan aktual, tetapi perangkat keras mungkin tidak tahu itu. Mungkin ada jalur cepat khusus di perangkat keras, tetapi mengetahui dengan pasti bahwa Anda memenuhi syarat untuk itu ...
John Calsbeek
10

John sudah menulis jawaban yang bagus jadi pertimbangkan jawaban ini sebagai perpanjangan dari jawabannya.

Saya saat ini banyak bekerja dengan komputasi shader untuk algoritma yang berbeda. Secara umum, saya telah menemukan bahwa komputasi shader dapat jauh lebih cepat daripada pixel shader yang setara atau mengubah alternatif berbasis umpan balik.

Setelah Anda membungkus kepala Anda di sekitar cara menghitung shader bekerja, mereka juga jauh lebih masuk akal dalam banyak kasus. Menggunakan pixel shaders untuk memfilter suatu gambar memerlukan pengaturan framebuffer, mengirim simpul, menggunakan beberapa tahapan shader, dll. Mengapa ini diperlukan untuk memfilter gambar? Digunakan untuk merender paha depan layar penuh untuk pemrosesan gambar tentu saja merupakan satu-satunya alasan "sah" untuk terus menggunakannya menurut saya. Saya yakin bahwa seorang pendatang baru di bidang grafik komputasi akan menemukan komputasi bayangan lebih cocok secara alami untuk pemrosesan gambar daripada rendering ke tekstur.

Pertanyaan Anda mengacu pada pemfilteran gambar secara khusus sehingga saya tidak akan menguraikan terlalu banyak pada topik lain. Dalam beberapa pengujian kami, hanya menyiapkan umpan balik transformasi atau mengalihkan objek framebuffer untuk dirender menjadi tekstur dapat menimbulkan biaya kinerja sekitar 0,2 ms. Ingatlah bahwa ini tidak termasuk render apa pun! Dalam satu kasus, kami menyimpan algoritma yang sama persis untuk porting shader dan melihat peningkatan kinerja yang nyata.

Saat menggunakan compute shaders, lebih banyak silikon pada GPU dapat digunakan untuk melakukan pekerjaan yang sebenarnya. Semua langkah tambahan ini diperlukan saat menggunakan rute pixel shader:

  • Rakitan vertex (membaca atribut titik, pembagi titik, konversi jenis, memperluasnya ke vec4, dll.)
  • Vertex shader perlu dijadwalkan tidak peduli seberapa minimal itu
  • Rasterizer harus menghitung daftar piksel untuk menaungi dan menginterpolasi output vertex (mungkin hanya coord tekstur untuk pemrosesan gambar)
  • Semua status yang berbeda (uji kedalaman, uji alfa, gunting, blending) harus ditetapkan dan dikelola

Anda bisa berargumen bahwa semua keunggulan kinerja yang disebutkan sebelumnya dapat dinegasikan oleh driver pintar. Anda benar. Driver seperti itu dapat mengidentifikasi bahwa Anda membuat quad-screen penuh tanpa pengujian mendalam, dll. Dan mengonfigurasi "jalur cepat" yang melompati semua pekerjaan tidak berguna yang dilakukan untuk mendukung pixel shaders. Saya tidak akan terkejut jika beberapa driver melakukan ini untuk mempercepat pass post-processing di beberapa game AAA untuk GPU spesifik mereka. Tentu saja Anda bisa melupakan perawatan seperti itu jika Anda tidak mengerjakan game AAA.

Apa yang tidak bisa dilakukan pengemudi adalah menemukan peluang paralelisme yang lebih baik yang ditawarkan oleh jalur pipa penghitung komputasi. Ambil contoh klasik dari filter gaussian. Menggunakan compute shaders, Anda dapat melakukan sesuatu seperti ini (memisahkan filter atau tidak):

  1. Untuk setiap kelompok kerja, bagi sampel pengambilan gambar sumber di seluruh ukuran kelompok kerja dan simpan hasilnya ke memori bersama grup.
  2. Hitung output filter menggunakan hasil sampel yang disimpan dalam memori bersama.
  3. Tulis ke tekstur output

Langkah 1 adalah kuncinya di sini. Dalam versi pixel shader, gambar sumber disampel beberapa kali per piksel. Dalam versi komputasi shader, setiap sumber texel dibaca hanya sekali di dalam kelompok kerja. Pembacaan tekstur biasanya menggunakan cache berbasis ubin, tetapi cache ini masih jauh lebih lambat daripada memori bersama.

Filter gaussian adalah salah satu contoh sederhana. Algoritma penyaringan lainnya menawarkan peluang lain untuk berbagi hasil perantara di dalam kelompok kerja menggunakan memori bersama.

Namun ada tangkapan. Compute shaders membutuhkan penghalang memori eksplisit untuk menyinkronkan outputnya. Ada juga lebih sedikit perlindungan untuk melindungi terhadap akses memori yang salah. Untuk programmer dengan pengetahuan pemrograman paralel yang baik, compute shaders menawarkan lebih banyak fleksibilitas. Namun fleksibilitas ini berarti bahwa lebih mudah untuk memperlakukan penghitung bayangan seperti kode C ++ biasa dan menulis kode lambat atau salah.

Referensi

bernie
sumber
Paralelisme pengambilan sampel yang ditingkatkan yang Anda gambarkan menarik - Saya memiliki cairan sim yang sudah diterapkan dengan compute shaders dengan banyak contoh beberapa sampel per piksel. Menggunakan memori grup bersama untuk melakukan pengambilan sampel tunggal dengan penghalang memori seperti yang Anda jelaskan tampak hebat, tapi saya menutup telepon sedikit - bagaimana cara mengakses piksel tetangga ketika mereka akan jatuh dalam kelompok kerja yang berbeda? misalnya, jika saya memiliki domain simulasi 64x64, sebarkan pada pengiriman (2,2,1) numthreads (16,16,1), bagaimana piksel dengan id.xy == [15,15] mendapatkan piksel tetangganya ?
Tossrock
Dalam hal ini, saya melihat 2 pilihan utama. 1) menambah ukuran grup lebih dari 64 dan hanya menulis hasil untuk 64x64 piksel. 2) sampel pertama 64 + nX64 + n dibagi entah bagaimana dalam kelompok kerja 64x64 Anda dan kemudian gunakan kisi "input" yang lebih besar untuk perhitungan. Solusi terbaik tergantung pada kondisi spesifik Anda dan saya sarankan Anda menulis pertanyaan lain untuk info lebih lanjut karena komentar kurang cocok untuk ini.
bernie
3

Saya tersandung di blog ini: Hitung Optimasi Shader untuk AMD

Mengingat trik apa yang dapat dilakukan dalam menghitung shader (yang khusus hanya untuk menghitung shader) saya ingin tahu apakah pengurangan paralel pada komputasi shader lebih cepat daripada pada pixel shader. Saya mengirim email ke penulis, Wolf Engel, untuk menanyakan apakah dia telah mencoba pixel shader. Dia menjawab bahwa ya dan kembali ketika dia menulis posting blog, versi shader komputasi jauh lebih cepat daripada versi pixel shader. Dia juga menambahkan bahwa hari ini perbedaannya bahkan lebih besar. Jadi ternyata ada kasus di mana menggunakan compute shader bisa menjadi keuntungan besar.

maksimal
sumber