opengl: glFlush () ile glFinish () karşılaştırması


105

Arama glFlush()ile arasındaki pratik farkı ayırt etmekte güçlük çekiyorum glFinish().

Dokümanlar şunu söylüyor glFlush()ve glFinish()tüm tamponlu işlemleri OpenGL'ye itecek, böylece hepsinin yürütüleceğinden emin olabilir, aradaki fark, tüm işlemler tamamlanana kadar bloklar halinde glFlush()hemen glFinish()dönmesidir.

Tanımları okuduktan sonra, kullanacak glFlush()olursam, muhtemelen OpenGL'ye yürütebileceğinden daha fazla işlem gönderme problemiyle karşılaşacağımı düşündüm . Yani, sadece denemek için, glFinish()a glFlush()ve lo'yu değiştirdim ve işte, programım çalıştı (söyleyebildiğim kadarıyla), tamamen aynı; kare hızları, kaynak kullanımı, her şey aynıydı.

Bu yüzden iki arama arasında çok fark olup olmadığını veya kodumun onları farklı çalıştırıp çalıştırmadığını merak ediyorum. Veya birinin diğerine göre nerede kullanılması gerektiği. Ayrıca glIsDone(), OpenGL'nin a için tüm arabelleğe alınmış komutların glFlush()tamamlanmış olup olmadığını kontrol etmek gibi bir çağrısı olacağını düşündüm (bu nedenle, OpenGL'ye çalıştırılabileceklerinden daha hızlı işlemler gönderilmez), ancak böyle bir işlev bulamadım .

Benim kodum tipik oyun döngüsü:

while (running) {
    process_stuff();
    render_stuff();
}

Yanıtlar:


93

Bu komutların OpenGL'nin ilk günlerinden beri var olduğunu unutmayın. glFlush, önceki OpenGL komutlarının sınırlı bir sürede tamamlanmasını sağlar ( OpenGL 2.1 özellikleri , sayfa 245). Doğrudan ön tampona çekerseniz, bu OpenGL sürücülerinin çok fazla gecikme olmadan çizime başlamasını sağlayacaktır. Her nesneden sonra glFlush çağırdığınızda, ekranda nesneden sonra görünen karmaşık bir sahne düşünebilirsiniz. Bununla birlikte, çift arabelleğe alma kullanılırken, değişiklikler arabellekleri değiştirene kadar görünmeyeceğinden glFlush'ın pratikte hiçbir etkisi yoktur.

glFinish , önceden verilen komutlardan [...] tüm etkiler tam olarak gerçekleştirilinceye kadar geri dönmez . Bu, programınızın yürütülmesinin her son piksel çizilene ve OpenGL'nin yapacak başka bir şeyi kalmayana kadar burada beklediği anlamına gelir. Doğrudan ön arabelleğe işliyorsanız, ekran görüntüsü almak için işletim sistemi çağrılarını kullanmadan önce glFinish yapmanız gereken çağrıdır. Çift arabelleğe alma için çok daha az kullanışlıdır, çünkü tamamlamaya zorladığınız değişiklikleri görmezsiniz.

Dolayısıyla, çift arabellek kullanırsanız, muhtemelen ne glFlush ne de glFinish'e ihtiyacınız olmayacaktır. SwapBuffers, OpenGL çağrılarını dolaylı olarak doğru arabelleğe yönlendirir, önce glFlush'ı çağırmaya gerek yoktur . Ve OpenGL sürücüsünü vurgulamaktan çekinme: glFlush çok fazla komutta boğulmayacaktır. Bu çağrının hemen geri döneceği garanti edilmez (ne anlama gelirse gelsin), bu nedenle komutlarınızı işlemesi gereken her zaman alabilir.


1
GlFlush'ın "SON sürede tamamlanması gerektiği" ve daha sonra glFlush'un "komutlarınızı işlemesi HERHANGİ BİR zaman alabileceği" için boğulmayacağını söylemesi ne demek? (Caps benim) Bunlar birbirini dışlamıyor mu?
Praxiteles

1
Bir noktada bitirdiği sürece FINITE zaman kriterini karşılar. Bazı sürücüler bu çağrıyı tamamen yapmaz.
chrisvarnz

SwapBuffers içeriğe özeldir ve yalnızca çağıran iş parçacığı bağlamını temizlemesi gerekir. Birden fazla bağlamı işliyorsanız, arabellekleri başka bir bağlamla değiştirirken meydana geleceği garanti edilmediğinden, her biri manuel olarak temizlenmelidir.
Andreas

22

Diğer cevapların da ima ettiği gibi, şartnameye göre gerçekten iyi bir cevap yok. Genel amacı, glFlush()çağrıldıktan sonra ana bilgisayar CPU'sunun OpenGL ile ilgili yapacak hiçbir işi olmayacak - komutlar grafik donanımına gönderilecek. Genel amacı, glFinish()geri döndükten sonra hiçbir iş kalmaması ve sonuçların da tüm uygun OpenGL olmayan API'ların (örneğin çerçeve arabelleğinden okumalar, ekran görüntüleri vb.) Mevcut olması gerektiğidir. Bunun gerçekten olup olmadığı sürücüye bağlıdır. Spesifikasyon, neyin yasal olduğu konusunda bir ton enlem sağlar.


18

Bu iki komut konusunda da her zaman kafam karışmıştı, ancak bu görüntü bana her şeyi açıkladı: Flush vs Finish Görünüşe göre bazı GPU sürücüleri, belirli sayıda komut biriktirilmedikçe verilen komutları donanıma göndermiyor. Bu örnekte, sayı olduğu 5 .
Resim, verilen çeşitli OpenGL komutlarını (A, B, C, D, E ...) gösterir. En üstte görebileceğimiz gibi, komutlar henüz verilmiyor çünkü kuyruk henüz dolu değil.

Ortada glFlush()sıraya alınmış komutları nasıl etkilediğini görüyoruz . Sürücüye sıradaki tüm komutları donanıma göndermesini söyler (sıra henüz dolu olmasa bile). Bu, arayan iş parçacığını engellemez. Yalnızca sürücüye herhangi bir ek komut göndermeyebileceğimizi bildirir. Bu nedenle kuyruğun dolmasını beklemek zaman kaybı olur.

Altta kullanarak bir örnek görüyoruz glFinish(). glFlush()Çağıran iş parçacığının donanım tarafından tüm komutlar işlenene kadar beklemesi dışında neredeyse aynı şeyi yapar.

Görüntü "OpenGL Kullanarak Gelişmiş Grafik Programlama" kitabından alınmıştır.


Aslında ne yapıp ne yaptığımı biliyorum glFlushve glFinishbu görüntünün ne dediğini anlayamıyorum. Sol tarafta ve sağda ne var? Ayrıca, bu görüntü Kamu Alanında mı yoksa İnternette yayınlamanıza izin veren bir lisans altında mı yayınlandı?
Nicol Bolas

Biraz detaylandırmalıydım. Lisans hakkında: Aslında tam olarak emin değilim. Araştırma yaparken Google'da PDF'ye rastladım.
Tara

7

Herhangi bir performans farkı görmediyseniz, yanlış bir şey yaptığınız anlamına gelir. Bazılarının da bahsettiği gibi, sizin de aramanıza gerek yok, ancak glFinish'i çağırırsanız, GPU ve CPU'nun elde edebileceği paralelliği otomatik olarak kaybedersiniz. Daha derine dalayım:

Pratikte, sürücüye gönderdiğiniz tüm işler toplu olarak işlenir ve muhtemelen daha sonra donanıma gönderilir (örneğin, SwapBuffer zamanında).

Dolayısıyla, glFinish'i arıyorsanız, aslında sürücüyü komutları GPU'ya itmeye zorluyorsunuz (o zamana kadar gruplandırılmış ve GPU'nun üzerinde çalışmasını hiç istememiş) ve komutlar tamamen itilene kadar CPU'yu durduruyorsunuz. idam edildi. Dolayısıyla, GPU'nun çalıştığı tüm süre boyunca CPU çalışmaz (en azından bu iş parçacığında). Ve CPU her zaman işini yapar (çoğunlukla toplu komutlar), GPU hiçbir şey yapmaz. Yani evet, glFinish performansınıza zarar verir. (Bu bir yaklaşımdır, çünkü sürücülerin çoğu zaten toplu halde GPU'yu bazı komutlar üzerinde çalıştırmaya başlayabilirler. Yine de, komut arabellekleri epeyce komut tutacak kadar büyük olma eğiliminde olduğundan bu tipik değildir).

Şimdi, o zaman neden glFinish'i arayasınız? Kullandığım tek zamanlar sürücü hatalarım olduğu zamandı. Gerçekten, donanıma gönderdiğiniz komutlardan biri GPU'yu çökertirse, suçluyu belirlemek için en basit seçeneğiniz her Draw'dan sonra glFinish'i çağırmaktır. Bu şekilde, çökmeyi tam olarak neyin tetiklediğini daraltabilirsiniz.

Bir yan not olarak, Direct3D gibi API'ler bir Finish konseptini hiç desteklemiyor.


5
"Şimdi, neden glFinish'i çağırasınız o zaman". Kaynakları (ör. Dokular) bir iş parçacığına yüklüyorsanız ve bunları wglShareLists veya benzerleri aracılığıyla paylaşarak başka bir iş parçacığında işlemek için kullanıyorsanız, işleme iş parçacığına eşzamansız olarak yüklenmiş olanı işlemenin ücretsiz olduğunu bildirmeden önce glFinish'i çağırmanız gerekir.
Marco Mp

Aşağıda söylediğim gibi, bu bir sürücü hatası geçici çözümü gibi görünüyor. Bu gereksinimle ilgili herhangi bir özel ifade bulamıyorum. Windows'ta sürücünün kilitlenmesi gerekir, Mac'te CGLLockContext yapmanız gerekir. Ancak glFinish'i kullanmak çok yanlış görünüyor. Bir dokunun ne zaman geçerli olduğunu bilmek için her durumda kendi kilidinizi veya etkinliğinizi yapmanız gerekir.
yıldız deliği

1
opencl opengl interop docs senkronizasyon için glFinish'i önerir "clEnqueueAcquireGLObjects'i çağırmadan önce, uygulama mem_objects'te belirtilen nesnelere erişen bekleyen GL işlemlerinin tamamlandığından emin olmalıdır. Bu nesnelere yönelik bekleyen referanslar içeren GL bağlamları "
Mark Essel

1
glFinish'i çağırmanız "gerekmiyor", nesnelerin senkronizasyonunu kullanabilirsiniz.
chrisvarnz

Eklemek gerekirse, orijinal yanıttan beri: Bazı GL uygulamaları, farklı iş parçacıklarındaki paylaşılan bağlamlar arasında eşitleme yapmak için flush / finish kullanmaya başladı: developer.apple.com/library/ios/documentation/3DDrawing/… - En önemlisi Apple IOS. Bence temelde API'nin başka bir kötüye kullanımı. Ama bu benim orijinal cevabım için bir uyarı: Bazı uygulamalarda bitiş / boşaltma gereklidir. Ancak OpenGL spesifikasyonu değil, uygulamanın nasıl ve neden tanımlandığı.
yıldız deliği

7

glFlush, gerçekten bir istemci sunucu modeline dayanmaktadır. Tüm gl komutlarını bir boru aracılığıyla bir gl sunucusuna gönderirsiniz. Bu boru tampon olabilir. Tıpkı herhangi bir dosya veya ağ g / ç'si arabelleğe alabilir. glFlush yalnızca "henüz dolu olmasa bile tamponu şimdi gönder!" der. Yerel bir sistemde buna neredeyse hiç gerek yoktur, çünkü yerel bir OpenGL API kendisini arabelleğe alma olasılığı düşüktür ve sadece doğrudan komutlar verir. Ayrıca gerçek oluşturmaya neden olan tüm komutlar örtük bir yıkama gerçekleştirir.

GlFinish ise performans ölçümü için yapılmıştır. GL sunucusuna bir tür PING. Bir komutu tersine çevirir ve sunucu "Boştayım" yanıtını verene kadar bekler.

Günümüzde modern, yerel sürücüler, atıl olmanın ne anlama geldiği konusunda oldukça yaratıcı fikirlere sahipler. "Tüm pikseller çizilmiş" mi yoksa "komut kuyruğumda boşluk var" mı? Ayrıca, birçok modern sürücü voodoo kodlaması, onları bir "optimizasyon" olarak görmezden geldiğinden, birçok eski programın kodlarına sebepsiz olarak glFlush ve glFinish serpmesi nedeniyle. Onları bunun için suçlayamam, gerçekten.

Özetle: Eski bir uzak SGI OpenGL sunucusu için kod yazmıyorsanız, hem glFinish hem de glFlush uygulamasında hiçbir işlem yokmuş gibi davranın.


4
Yanlış. Yukarıdaki cevapta bıraktığım yorumda olduğu gibi: Bir iş parçacığına kaynakları (ör. Dokular) yüklüyorsanız ve bunları wglShareLists veya benzerleri aracılığıyla paylaşarak diğerinde işlemek için kullanıyorsanız, işleme iş parçacığına sinyal vermeden önce glFinish'i çağırmanız gerekir. eşzamansız olarak yüklenmiş olanı oluşturmanın ücretsiz olduğunu. Ve dediğin gibi işlemsiz değil.
Marco Mp

Gerçekten mi? Bunu, bazı özellik bağlantılarıyla paylaşılan bir nesne kullanmadan önce glFinish'i çağırma ihtiyacını yedekleyebilir misiniz? Buna ihtiyacı olan bir buggy sürücüsünü tamamen görebiliyorum. Ve bu, söylediklerime geri dönüyor. İnsanlar buggy sürücüleri etrafında çalışmak için glFinish kullanıyor. Bu nedenle, düzgün çalışan sürücüler performans için onu görmezden geliyor. Profil oluşturma (ve tek arabelleğe alınmış oluşturma) orijinal spesifikasyon kullanım durumu artık çalışmıyor.
yıldız deliği

2
Bozuk dokularla uğraşmak zorunda kalmanın kişisel deneyimi artı şunun : highorderfun.com/blog/2011/05/26/… Eğer düşünürseniz, muteks veya diğer eşzamanlamalara sahip olmaktan çok farklı olmadığı için mantıklıdır. iş parçacıkları arasında api - bir bağlamın paylaşılan bir kaynağı kullanabilmesi için önce diğerinin bu kaynağın yüklenmesini tamamlaması gerekir, dolayısıyla arabelleğin boşaltıldığından emin olma ihtiyacı doğar. Hata yerine teknik özelliklerin daha çok köşe durumunu düşünüyorum, ancak bu ifade için kanıtım yok :)
Marco Mp

Harika cevap, teşekkürler. Özellikle "pek çok eski program voodoo olarak kodlarının tamamına glFlush ve glFinish serpiştirdi".
akauppi

Gerçekten operasyon yok mu? GlFinish ve / veya glFlush, yüksek yoğunluklu görüntü işleme veya GPU tabanlı matematik ortak işleme için çok değerli değil mi? Örneğin, tüm piksel hesaplamalarının yazıldığından emin olmak için bir glFinish çağırdıktan sonra , GPU hesaplamasının sonuçlarını piksel arabelleğinden CPU'ya okumayız. Bir şey mi kaçırıyoruz?
Praxiteles

5

Buraya bir göz atın . Kısaca şöyle diyor:

glFinish (), glFlush () ile aynı etkiye sahiptir ve eklenen tüm komutlar yerine getirilene kadar glFinish () engelleyecektir.

Başka bir makale diğer farklılıkları açıklamaktadır:

  • Takas işlevleri (çift arabelleğe alınmış uygulamalarda kullanılır) komutları otomatik olarak temizler, böylece arama yapmaya gerek yoktur glFlush
  • glFinish OpenGL'yi olağanüstü komutlar gerçekleştirmeye zorlar, bu kötü bir fikirdir (örneğin VSync ile)

Özetlemek gerekirse, bu, takas-arabellek uygulamanızın komutları otomatik olarak temizlememesi dışında, çift arabelleğe almayı kullanırken bu işlevlere bile ihtiyacınız olmadığı anlamına gelir.


Evet doğru, ancak belge , örneğin pencere sistemiyle ilgili olarak sadece birkaç farkla her ikisinin de aynı etkiye sahip olduğunu söylüyor . Ama yine de cevabımı düzenledim;)
AndiDog

Nüans için güzel seslenme. GlFlush () ve THEN glFinish () - (yukarıdaki her şeyi okuduktan sonra sorduğumuz bir soru)
Praxiteles

4

Tamponun durumunu sorgulamanın bir yolu yok gibi görünüyor. Orada bu elma uzatma aynı amaca hizmet edebileceği, ancak çapraz platform görünmüyor (. Denemediyseniz) hızlı bakışta, önce görünüyor flushiçinde çit komutu itin ediyorum sana; daha sonra arabellekte hareket ederken bu çitin durumunu sorgulayabilirsiniz.

flushKomutları arabelleğe almadan önce kullanıp kullanamayacağınızı merak ediyorum , ancak aradığınız bir sonraki çerçeveyi oluşturmaya başlamadan önce finish. Bu, GPU çalışırken bir sonraki kareyi işlemeye başlamanıza izin verir, ancak geri döndüğünüzde bitmezse, finishher şeyin yeni bir durumda olduğundan emin olmak için engellenir.

Bunu denemedim ama kısa süre sonra yapacağım.

Oldukça CPU ve GPU kullanımına sahip eski bir uygulamada denedim. (Başlangıçta kullanıldı finish.)

flushSonunda ve finishbaşlangıçta olarak değiştirdiğimde , acil bir sorun olmadı. (Her şey yolunda görünüyordu!) Muhtemelen CPU'nun GPU'da beklememesi nedeniyle programın yanıt verme hızı arttı. Kesinlikle daha iyi bir yöntem.

Karşılaştırma için, finishedçerçevenin başından ayrıldım, ayrıldım flushve aynısını gerçekleştirdim.

Bu yüzden use derdim flushve finishçünkü çağrı sırasında arabellek boş olduğunda, finishperformans vuruşu olmaz. Tahminimce tampon dolu olsaydı finishyine de istemelisin .


0

Soru şudur: Kodunuzun OpenGL komutları yürütülürken çalışmaya devam etmesini mi, yoksa yalnızca OpenGL komutlarınız çalıştırıldıktan sonra mı çalışmasını istiyorsunuz?

Bu, ağ gecikmeleri gibi durumlarda, yalnızca görüntüler çizildikten sonra belirli konsol çıktılarının alınması veya benzeri durumlarda önemli olabilir .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.