opengl: glFlush () vs. glFinish ()

105

Saya mengalami masalah dalam membedakan perbedaan praktis antara menelepon glFlush()dan glFinish().

Docs mengatakan bahwa glFlush()dan glFinish()akan mendorong semua operasi yang di-buffer ke OpenGL sehingga seseorang dapat yakin semuanya akan dieksekusi, perbedaannya adalah yang glFlush()langsung mengembalikan di mana sebagai glFinish()blok sampai semua operasi selesai.

Setelah membaca definisinya, saya membayangkan bahwa jika saya menggunakannya glFlush(), saya mungkin akan mengalami masalah dalam mengirimkan lebih banyak operasi ke OpenGL daripada yang bisa dijalankannya. Jadi, hanya untuk mencoba, saya menukar saya glFinish()untuk a glFlush()dan lihatlah, program saya berjalan (sejauh yang saya tahu), persis sama; frekuensi gambar, penggunaan sumber daya, semuanya sama.

Jadi saya bertanya-tanya apakah ada banyak perbedaan antara kedua panggilan tersebut, atau jika kode saya membuatnya berjalan tidak berbeda. Atau di mana salah satu harus digunakan vs. yang lain. Saya juga membayangkan bahwa OpenGL akan memiliki beberapa panggilan glIsDone()untuk memeriksa apakah semua perintah yang di-buffer untuk a glFlush()lengkap atau tidak (jadi seseorang tidak mengirim operasi ke OpenGL lebih cepat daripada yang dapat dijalankan), tetapi saya tidak dapat menemukan fungsi seperti itu .

Kode saya adalah loop permainan yang khas:

while (running) {
    process_stuff();
    render_stuff();
}
jay.lee
sumber

Jawaban:

93

Ingatlah bahwa perintah ini ada sejak awal OpenGL. glFlush memastikan bahwa perintah OpenGL sebelumnya harus diselesaikan dalam waktu yang terbatas ( spesifikasi OpenGL 2.1 , halaman 245). Jika Anda menggambar langsung ke buffer depan, ini akan memastikan bahwa driver OpenGL mulai menggambar tanpa terlalu banyak penundaan. Anda dapat membayangkan adegan kompleks yang muncul objek demi objek di layar, saat Anda memanggil glFlush setelah setiap objek. Namun, saat menggunakan buffering ganda, glFlush praktis tidak berpengaruh sama sekali, karena perubahan tidak akan terlihat sampai Anda menukar buffer.

glFinish tidak kembali sampai semua efek dari perintah yang dikeluarkan sebelumnya [...] sepenuhnya terwujud . Ini berarti eksekusi program Anda menunggu di sini sampai setiap piksel terakhir diambil dan OpenGL tidak ada lagi yang bisa dilakukan. Jika Anda merender langsung ke buffer depan, glFinish adalah panggilan yang harus dilakukan sebelum menggunakan panggilan sistem operasi untuk mengambil screenshot. Ini jauh kurang berguna untuk buffering ganda, karena Anda tidak melihat perubahan yang Anda paksa untuk selesaikan.

Jadi, jika Anda menggunakan buffering ganda, Anda mungkin tidak memerlukan glFlush atau glFinish. SwapBuffers secara implisit mengarahkan panggilan OpenGL ke buffer yang benar, tidak perlu memanggil glFlush terlebih dahulu . Dan jangan keberatan untuk menekankan driver OpenGL: glFlush tidak akan tersedak terlalu banyak perintah. Tidak ada jaminan bahwa panggilan ini segera kembali (apa pun artinya), sehingga dapat memerlukan waktu kapan saja diperlukan untuk memproses perintah Anda.

Malte Clasen
sumber
1
Apa maksud Anda glFlush "harus selesai dalam waktu FINITE" dan kemudian mengatakan, bahwa glFlush tidak akan tersedak karena "ini dapat memakan waktu KAPAN SAJA untuk memproses perintah Anda"? (Caps mine) Bukankah itu saling eksklusif?
Praxiteles
1
Selama itu selesai di beberapa titik, itu memenuhi kriteria waktu HINGGA. Beberapa driver tidak menyetujui panggilan ini sepenuhnya.
chrisvarnz
SwapBuffers adalah konteks khusus dan hanya perlu membersihkan konteks utas panggilan. Jika merender beberapa konteks, masing-masing harus di-flush secara manual karena tidak perlu terjadi saat menukar buffer melalui konteks lain.
Andreas
22

Seperti yang diisyaratkan oleh jawaban lain, sebenarnya tidak ada jawaban yang bagus sesuai spesifikasi. Maksud umum dari glFlush()adalah bahwa setelah memanggilnya, CPU host tidak akan memiliki pekerjaan terkait OpenGL yang harus dilakukan - perintah akan didorong ke perangkat keras grafis. Maksud umum dari glFinish()adalah bahwa setelah ia kembali, tidak ada pekerjaan yang tersisa, dan hasilnya harus tersedia juga semua API non-OpenGL yang sesuai (misalnya membaca dari framebuffer, screenshot, dll ...). Apakah itu benar-benar yang terjadi tergantung pada pengemudi. Spesifikasi memungkinkan banyak kebebasan tentang apa yang legal.

Andy Ross
sumber
18

Saya selalu bingung tentang kedua perintah itu juga, tetapi gambar ini menjelaskan semuanya kepada saya: Siram vs Selesai Rupanya beberapa driver GPU tidak mengirim perintah yang dikeluarkan ke perangkat keras kecuali sejumlah perintah telah terakumulasi. Dalam contoh ini angka itu adalah 5 .
Gambar menunjukkan berbagai perintah OpenGL (A, B, C, D, E ...) yang telah dikeluarkan. Seperti yang bisa kita lihat di bagian atas, perintah belum dikeluarkan, karena antrian belum penuh.

Di tengah kita melihat bagaimana glFlush()mempengaruhi antrian perintah. Ini memberitahu pengemudi untuk mengirim semua perintah antrian ke perangkat keras (bahkan jika antrian belum penuh). Ini tidak memblokir utas panggilan. Ini hanya memberi sinyal kepada pengemudi bahwa kami mungkin tidak mengirimkan perintah tambahan apa pun. Oleh karena itu menunggu antrian terisi akan membuang-buang waktu.

Di bagian bawah kami melihat contoh menggunakan glFinish(). Ini hampir melakukan hal yang sama glFlush(), kecuali bahwa itu membuat utas panggilan menunggu sampai semua perintah telah diproses oleh perangkat keras.

Gambar diambil dari buku "Advanced Graphics Programming Using OpenGL".

Tara
sumber
Saya sebenarnya tahu apa glFlushdan glFinishmelakukan, dan saya tidak tahu apa yang dikatakan gambar itu. Ada apa di sisi kiri dan kanan? Selain itu, apakah gambar itu dirilis di Domain Publik atau di bawah beberapa lisensi yang memungkinkan Anda mempostingnya di Internet?
Nicol Bolas
Saya seharusnya menjelaskan sedikit. Tentang lisensi: Saya sebenarnya tidak yakin. Saya menemukan PDF di google, saat melakukan penelitian.
Tara
7

Jika Anda tidak melihat perbedaan kinerja apa pun, itu berarti Anda melakukan kesalahan. Seperti yang disebutkan beberapa orang lain, Anda juga tidak perlu memanggil, tetapi jika Anda memanggil glFinish, maka Anda secara otomatis kehilangan paralelisme yang dapat dicapai oleh GPU dan CPU. Biarkan saya menyelami lebih dalam:

Dalam praktiknya, semua pekerjaan yang Anda serahkan ke driver dikelompokkan, dan dikirim ke perangkat keras secara potensial nanti (misalnya pada waktu SwapBuffer).

Jadi, jika Anda memanggil glFinish, pada dasarnya Anda memaksa pengemudi untuk mendorong perintah ke GPU (yang dikumpulkan sampai saat itu, dan tidak pernah meminta GPU untuk bekerja), dan menghentikan CPU sampai perintah yang didorong sepenuhnya dieksekusi. Jadi selama GPU bekerja, CPU tidak (setidaknya di thread ini). Dan sepanjang waktu CPU melakukan tugasnya (kebanyakan perintah batch), GPU tidak melakukan apapun. Jadi ya, glFinish seharusnya merusak kinerja Anda. (Ini adalah perkiraan, karena driver mungkin mulai menjalankan GPU pada beberapa perintah jika banyak di antaranya sudah di-batch. Ini tidak umum, karena buffer perintah cenderung cukup besar untuk menampung cukup banyak perintah).

Sekarang, mengapa Anda memanggil glFinish? Satu-satunya saat saya menggunakannya adalah ketika saya memiliki bug driver. Memang, jika salah satu perintah yang Anda kirimkan ke perangkat keras membuat GPU rusak, maka opsi paling sederhana Anda untuk mengidentifikasi perintah mana yang menjadi penyebabnya adalah dengan memanggil glFinish setelah setiap Draw. Dengan begitu, Anda bisa mempersempit apa yang sebenarnya memicu kecelakaan itu

Sebagai catatan tambahan, API seperti Direct3D tidak mendukung konsep Selesai sama sekali.

Bahbar
sumber
5
Pada "Sekarang, mengapa Anda memanggil glFinish sama sekali". Jika Anda memuat sumber daya (misalnya: tekstur) di satu utas dan menggunakannya untuk merender di utas lain, dengan berbagi melalui wglShareLists atau sejenisnya, Anda perlu memanggil glFinish sebelum memberi isyarat ke utas rendering bahwa merender apa yang telah dimuat secara asinkron gratis itu gratis.
Marco Mp
Seperti yang saya katakan di bawah, ini memang terlihat seperti solusi bug driver. Saya tidak dapat menemukan spesifikasi yang menyebutkan persyaratan ini. Di windows, pengemudi harus mengunci, di mac Anda harus melakukan CGLLockContext. Tetapi menggunakan glFinish sepertinya sangat salah. Untuk mengetahui kapan tekstur valid, Anda harus melakukan penguncian atau acara Anda sendiri.
starmole
1
opencl opengl interop docs merekomendasikan glFinish untuk sinkronisasi "Sebelum memanggil clEnqueueAcquireGLObjects, aplikasi harus memastikan bahwa setiap operasi GL yang tertunda yang mengakses objek yang ditentukan dalam mem_objects telah selesai. Ini dapat diselesaikan secara portabel dengan mengeluarkan dan menunggu penyelesaian perintah glFinish di semua Konteks GL dengan referensi tertunda ke objek ini "
Mark Essel
1
Anda tidak "perlu" memanggil glFinish, Anda dapat menggunakan objek sinkronisasi.
chrisvarnz
Sebagai tambahan, karena jawaban asli: Beberapa implementasi GL mulai menggunakan flush / finish untuk menyinkronkan antara konteks bersama pada utas yang berbeda: developer.apple.com/library/ios/documentation/3DDrawing/… - Terutama Apple IOS. Saya pikir ini pada dasarnya adalah penyalahgunaan API lainnya. Tetapi ini adalah peringatan untuk jawaban asli saya: Pada beberapa implementasi, penyelesaian / penyiraman diperlukan. Tapi bagaimana dan mengapa implementasi ditentukan, bukan spesifikasi OpenGL.
starmole
7

glFlush benar-benar tanggal kembali ke model server klien. Anda mengirim semua perintah gl melalui pipa ke server gl. Pipa itu mungkin menyangga. Sama seperti file atau jaringan apa pun, i / o mungkin buffer. glFlush hanya mengatakan "kirim buffer sekarang, meskipun belum penuh!". Pada sistem lokal, hal ini hampir tidak pernah diperlukan karena OpenGL API lokal tidak mungkin menyangga dirinya sendiri dan hanya mengeluarkan perintah secara langsung. Juga semua perintah yang menyebabkan rendering aktual akan melakukan flush implisit.

glFinish di sisi lain dibuat untuk pengukuran kinerja. Jenis PING ke server GL. Ini melakukan bolak-balik perintah dan menunggu hingga server merespons "Saya menganggur".

Saat ini, pengemudi lokal memiliki ide yang cukup kreatif tentang apa artinya menganggur. Apakah "semua piksel digambar" atau "antrian perintah saya memiliki spasi"? Juga karena banyak program lama menaburkan glFlush dan glFinish di seluruh kodenya tanpa alasan sebagai pengkodean voodoo, banyak driver modern mengabaikannya sebagai "pengoptimalan". Tidak bisa menyalahkan mereka untuk itu, sungguh.

Singkatnya: Perlakukan glFinish dan glFlush sebagai praktik tanpa operasi kecuali Anda membuat kode untuk server OpenGL SGI jarak jauh kuno.

starmole
sumber
4
Salah. Seperti dalam komentar yang saya tinggalkan di jawaban di atas: Jika Anda memuat sumber daya (misalnya: tekstur) pada satu utas dan menggunakannya untuk merender di utas lain, dengan berbagi melalui wglShareLists atau serupa, Anda perlu memanggil glFinish sebelum memberi isyarat ke utas rendering bahwa merender apa yang telah dimuat secara asinkron itu gratis. Dan itu bukan larangan seperti yang Anda katakan.
Marco Mp
Betulkah? Dapatkah Anda mencadangkan kebutuhan untuk memanggil glFinish sebelum menggunakan objek bersama dengan beberapa tautan spesifikasi? Saya benar-benar dapat melihat pengemudi buggy yang membutuhkan ini. Dan semacam itu kembali ke apa yang saya katakan. Orang-orang menggunakan glFinish untuk mengatasi pengemudi buggy. Karena itu driver yang berfungsi dengan baik mengabaikannya untuk kinerja. Kasus penggunaan spesifikasi asli dari pembuatan profil (dan rendering buffer tunggal) tidak berfungsi lagi.
starmole
2
Pengalaman pribadi harus berurusan dengan tekstur yang rusak, ditambah ini: lebih tinggiorderfun.com/blog/2011/05/26/… Jika Anda memikirkannya, itu masuk akal, bagaimanapun, karena itu tidak jauh berbeda dari memiliki mutex atau sinkronisasi lainnya. api antara utas - sebelum konteks dapat menggunakan sumber daya bersama, yang lain harus menyelesaikan pemuatan sumber daya itu, sehingga kebutuhan untuk memastikan buffer telah dikosongkan. Saya lebih memikirkan kasus sudut dari spesifikasi daripada bug, tapi saya tidak punya bukti untuk pernyataan ini :)
Marco Mp
Jawaban yang luar biasa, terima kasih. Terutama "banyak program lama menaburkan glFlush dan glFinish di seluruh kodenya tanpa alasan sebagai voodoo".
akauppi
Apakah ini benar-benar tidak ada operasi? Bukankah glFinish dan / atau glFlush sangat berharga untuk pemrosesan gambar intensitas tinggi atau coprocessing matematika berbasis GPU? Misalnya, kami tidak membaca hasil komputasi GPU dari buffer piksel ke CPU sampai setelah memanggil glFinish untuk memastikan semua komputasi piksel telah ditulis. Apakah kita melewatkan sesuatu?
Praxiteles
5

Lihat di sini . Singkatnya, dikatakan:

glFinish () memiliki efek yang sama seperti glFlush (), dengan tambahan glFinish () akan memblokir hingga semua perintah yang dikirimkan telah dijalankan.

Artikel lain menjelaskan perbedaan lain:

  • Fungsi swap (digunakan dalam aplikasi buffer ganda) secara otomatis menghapus perintah, jadi tidak perlu menelepon glFlush
  • glFinish memaksa OpenGL untuk melakukan perintah yang luar biasa, yang merupakan ide yang buruk (misalnya dengan VSync)

Singkatnya, ini berarti Anda bahkan tidak memerlukan fungsi-fungsi ini saat menggunakan buffering ganda, kecuali jika implementasi swap-buffer Anda tidak secara otomatis menghapus perintah.

AndiDog
sumber
Ya benar, tetapi dokumen mengatakan bahwa keduanya memiliki efek yang sama dengan hanya sedikit perbedaan mengenai sistem jendela, misalnya. Tapi bagaimanapun saya mengedit jawaban saya;)
AndiDog
Panggilan bagus tentang nuansa. Ini menjelaskan apakah perlu memanggil glFlush () dan LALU glFinish () - (pertanyaan yang kami miliki setelah membaca semua di atas.)
Praxiteles
4

Sepertinya tidak ada cara untuk menanyakan status buffer. Ada ekstensi Apple ini yang dapat melayani tujuan yang sama, tetapi tampaknya tidak lintas platform (belum mencobanya.) Sekilas, tampaknya sebelum flushAnda memasukkan perintah pagar; Anda kemudian dapat menanyakan status pagar tersebut saat berpindah melalui buffer.

Saya ingin tahu apakah Anda dapat menggunakan flushsebelum melakukan buffering atas perintah, tetapi sebelum mulai membuat frame berikutnya yang Anda panggil finish. Ini akan memungkinkan Anda untuk mulai memproses frame berikutnya saat GPU berfungsi, tetapi jika tidak selesai pada saat Anda kembali, finishakan memblokir untuk memastikan semuanya dalam keadaan baru.

Saya belum mencoba ini, tetapi saya akan segera.

Saya telah mencobanya pada aplikasi lama yang memiliki penggunaan CPU & GPU yang cukup bagus. (Ini awalnya digunakan finish.)

Ketika saya mengubahnya menjadi flushdi akhir dan finishdi awal, tidak ada masalah langsung. (Semuanya tampak baik-baik saja!) Responsivitas program meningkat, mungkin karena CPU tidak berhenti menunggu GPU. Jelas metode yang lebih baik.

Sebagai perbandingan, saya menghapus finisheddari awal bingkai, pergi flush, dan itu melakukan hal yang sama.

Jadi saya akan mengatakan gunakan flushdan finish, karena ketika buffer kosong saat dipanggil finish, tidak ada kinerja yang dipukul. Dan saya menebak jika buffernya penuh, Anda pasti menginginkannya finish.

GManNickG
sumber
0

Pertanyaannya adalah: apakah Anda ingin kode Anda terus berjalan saat perintah OpenGL dijalankan, atau hanya untuk dijalankan setelah perintah OpenGL Anda dijalankan.

Ini dapat menjadi masalah dalam kasus-kasus, seperti penundaan jaringan, untuk memiliki keluaran konsol tertentu hanya setelah gambar diambil atau semacamnya.

segera
sumber