opengl: glFlush () vs. glFinish ()


105

Ho difficoltà a distinguere la differenza pratica tra chiamata glFlush()e glFinish().

I documenti lo dicono glFlush()e glFinish()invieranno tutte le operazioni memorizzate nel buffer a OpenGL in modo che si possa essere certi che verranno eseguite tutte, con la differenza che glFlush()restituisce immediatamente dove come glFinish()blocchi fino al completamento di tutte le operazioni.

Dopo aver letto le definizioni, ho pensato che se dovessi usarle glFlush()probabilmente mi sarei imbattuto nel problema di inviare più operazioni a OpenGL di quante ne potesse eseguire. Quindi, solo per provare, ho scambiato il mio glFinish()con a glFlush()ed ecco, il mio programma ha funzionato (per quanto ho potuto vedere), esattamente lo stesso; frame rate, utilizzo delle risorse, tutto era uguale.

Quindi mi chiedo se ci sia molta differenza tra le due chiamate o se il mio codice non le fa funzionare in modo diverso. O dove dovrebbe essere usato uno rispetto all'altro. Ho anche pensato che OpenGL avrebbe avuto qualche chiamata come glIsDone()controllare se tutti i comandi memorizzati nel buffer per a glFlush()sono completi o meno (quindi non si inviano operazioni a OpenGL più velocemente di quanto possano essere eseguite), ma non sono riuscito a trovare tale funzione .

Il mio codice è il tipico loop di gioco:

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

Risposte:


93

Tieni presente che questi comandi esistono sin dai primi giorni di OpenGL. glFlush garantisce che i precedenti comandi OpenGL debbano essere completati in un tempo finito ( specifiche OpenGL 2.1 , pagina 246). Se si disegna direttamente nel front buffer, questo assicurerà che i driver OpenGL inizino a disegnare senza troppo ritardo. Potresti pensare a una scena complessa che appare oggetto dopo oggetto sullo schermo, quando chiami glFlush dopo ogni oggetto. Tuttavia, quando si utilizza il doppio buffering, glFlush non ha praticamente alcun effetto, poiché le modifiche non saranno visibili finché non si scambiano i buffer.

glFinish non ritorna finché tutti gli effetti dei comandi precedentemente emessi [...] non sono completamente realizzati . Ciò significa che l'esecuzione del programma attende qui fino a quando non viene disegnato l'ultimo pixel e OpenGL non ha più nulla da fare. Se esegui il rendering direttamente nel front buffer, glFinish è la chiamata da effettuare prima di utilizzare le chiamate del sistema operativo per acquisire schermate. È molto meno utile per il doppio buffering, perché non vedi le modifiche che hai costretto a completare.

Quindi, se usi il doppio buffering, probabilmente non avrai bisogno né di glFlush né di glFinish. SwapBuffers indirizza implicitamente le chiamate OpenGL al buffer corretto, non è necessario chiamare prima glFlush . E non preoccuparti di sottolineare il driver OpenGL: glFlush non soffoca con troppi comandi. Non è garantito che questa chiamata ritorni immediatamente (qualunque cosa significhi), quindi può impiegare tutto il tempo necessario per elaborare i tuoi comandi.


1
Cosa intendi con il fatto che glFlush "deve essere completato in tempo FINITO" e poi dice che glFlush non si strozzerà perché "può richiedere QUALSIASI tempo necessario per elaborare i tuoi comandi"? (Tappi miei) Non si escludono a vicenda?
Prassitele

1
Finché ad un certo punto finisce, soddisfa i criteri di tempo FINITO. Alcuni conducenti non effettuano questa chiamata del tutto.
chrisvarnz

SwapBuffers è specifico del contesto e deve solo svuotare il contesto del thread chiamante. Se si esegue il rendering di più contesti, ciascuno dovrebbe essere svuotato manualmente poiché non è garantito che accada quando si scambiano i buffer in un altro contesto.
Andreas

22

Come le altre risposte hanno accennato, non c'è davvero una buona risposta secondo le specifiche. L'intento generale di glFlush()è che dopo averlo chiamato, la CPU host non avrà alcun lavoro relativo a OpenGL da fare: i comandi saranno stati inviati all'hardware grafico. L'intento generale di glFinish()è che dopo che ritorna, non rimane lavoro rimanente, e i risultati dovrebbero essere disponibili anche per tutte le API non OpenGL appropriate (es. Letture dal framebuffer, screenshot, ecc ...). Se questo è davvero ciò che accade dipende dal conducente. La specifica consente un sacco di libertà su ciò che è legale.


18

Ero sempre confuso anche su questi due comandi, ma questa immagine mi ha chiarito tutto: a Flush vs Finish quanto pare alcuni driver GPU non inviano i comandi emessi all'hardware a meno che non sia stato accumulato un certo numero di comandi. In questo esempio quel numero è 5 .
L'immagine mostra vari comandi OpenGL (A, B, C, D, E ...) che sono stati emessi. Come possiamo vedere in alto, i comandi non vengono ancora emessi, perché la coda non è ancora piena.

Nel mezzo vediamo come glFlush()influisce sui comandi in coda. Indica al driver di inviare tutti i comandi in coda all'hardware (anche se la coda non è ancora piena). Questo non blocca il thread chiamante. Segnala semplicemente al driver che potremmo non inviare alcun comando aggiuntivo. Quindi aspettare che la coda si riempia sarebbe una perdita di tempo.

In basso vediamo un esempio che utilizza glFinish(). Fa quasi la stessa cosa di glFlush(), tranne per il fatto che fa attendere il thread chiamante fino a quando tutti i comandi sono stati elaborati dall'hardware.

Immagine tratta dal libro "Advanced Graphics Programming Using OpenGL".


In realtà so cosa glFlushe glFinishfaccio, e non posso dire cosa stia dicendo quell'immagine. Cosa c'è sul lato sinistro e sul lato destro? Inoltre, quell'immagine è stata rilasciata nel pubblico dominio o con una licenza che ti consente di pubblicarla su Internet?
Nicol Bolas

Avrei dovuto elaborare un po '. Sulla licenza: in realtà non ne sono del tutto sicuro. Mi sono imbattuto nel PDF su Google, mentre facevo ricerche.
Tara

7

Se non hai notato alcuna differenza di prestazioni, significa che stai facendo qualcosa di sbagliato. Come alcuni altri hanno menzionato, non è necessario nemmeno chiamare, ma se chiami glFinish, perdi automaticamente il parallelismo che la GPU e la CPU possono ottenere. Fammi approfondire:

In pratica, tutto il lavoro che invii al driver viene raggruppato e inviato all'hardware potenzialmente molto più tardi (ad esempio al momento di SwapBuffer).

Quindi, se stai chiamando glFinish, stai essenzialmente costringendo il driver a inviare i comandi alla GPU (che fino ad allora era in batch e non ha mai chiesto alla GPU di funzionare), e blocca la CPU fino a quando i comandi inviati non sono completamente eseguito. Quindi durante tutto il tempo in cui la GPU funziona, la CPU no (almeno su questo thread). E per tutto il tempo in cui la CPU fa il suo lavoro (principalmente comandi di batch), la GPU non fa nulla. Quindi sì, glFinish dovrebbe danneggiare la tua performance. (Questa è un'approssimazione, poiché i driver potrebbero iniziare a far funzionare la GPU su alcuni comandi se molti di essi erano già in batch. Non è tipico, tuttavia, poiché i buffer dei comandi tendono ad essere abbastanza grandi da contenere un bel po 'di comandi).

Ora, perché allora dovresti chiamare glFinish? Le uniche volte che l'ho usato sono state quando avevo dei bug del driver. In effetti, se uno dei comandi che invii all'hardware blocca la GPU, l'opzione più semplice per identificare quale comando è il colpevole è chiamare glFinish dopo ogni estrazione. In questo modo, puoi restringere ciò che fa scattare esattamente l'incidente

Come nota a margine, API come Direct3D non supportano affatto il concetto di Finish.


5
Su "Ora, perché dovresti chiamare glFinish allora". Se si stanno caricando risorse (ad esempio: texture) su un thread e le si utilizza per eseguire il rendering su un altro, con condivisione tramite wglShareLists o simili, è necessario chiamare glFinish prima di segnalare al thread di rendering che è libero di eseguire il rendering di ciò che è stato caricato in modo asincrono.
Marco Mp

Come ho detto di seguito, sembra una soluzione alternativa ai bug del driver. Non riesco a trovare alcuna menzione specifica di questo requisito. Su Windows il driver deve prendere un lucchetto, su Mac devi fare CGLLockContext. Ma usare glFinish sembra molto sbagliato. Per sapere quando una texture è valida devi in ​​ogni caso creare il tuo blocco o evento.
Starmole

1
opencl opengl interop docs consiglia glFinish per la sincronizzazione "Prima di chiamare clEnqueueAcquireGLObjects, l'applicazione deve assicurarsi che tutte le operazioni GL in sospeso che accedono agli oggetti specificati in mem_objects siano state completate. Questa operazione può essere eseguita in modo portabile emettendo e aspettando il completamento di un comando glFinish su tutti Contesti GL con riferimenti in sospeso a questi oggetti "
Mark Essel

1
non "è necessario" chiamare glFinish, è possibile utilizzare oggetti di sincronizzazione.
chrisvarnz

Solo per aggiungere, dalla risposta originale: alcune implementazioni GL hanno iniziato a utilizzare flush / finish per sincronizzare tra contesti condivisi su thread diversi: developer.apple.com/library/ios/documentation/3DDrawing/… - In particolare Apple IOS. Penso che sia fondamentalmente un altro uso improprio dell'API. Ma questo è un avvertimento alla mia risposta originale: su alcune implementazioni sono necessari finish / flush. Ma il come e il perché viene definita l'implementazione, non le specifiche OpenGL.
Starmole

7

glFlush risale davvero a un modello client server. Tutti i comandi gl vengono inviati tramite pipe a un server gl. Quella pipa potrebbe tamponare. Proprio come qualsiasi file o rete i / o potrebbe bufferizzare. glFlush dice solo "invia il buffer adesso, anche se non è ancora pieno!". Su un sistema locale questo non è quasi mai necessario perché è improbabile che un'API OpenGL locale si bufferizzi da sola e invii direttamente i comandi. Inoltre, tutti i comandi che causano il rendering effettivo eseguiranno un flush implicito.

glFinish d'altra parte è stato creato per la misurazione delle prestazioni. Una specie di PING al server GL. Esegue il roundtrip di un comando e attende finché il server non risponde "Sono inattivo".

Al giorno d'oggi, i conducenti locali moderni hanno idee piuttosto creative su cosa significhi essere inattivi. È "tutti i pixel sono disegnati" o "la mia coda comandi ha spazio"? Anche perché molti vecchi programmi hanno spruzzato glFlush e glFinish in tutto il codice senza motivo come codifica voodoo, molti driver moderni li ignorano semplicemente come "ottimizzazione". Non posso biasimarli per questo, davvero.

Quindi, in sintesi: considera glFinish e glFlush come nessuna operazione in pratica a meno che tu non stia codificando per un antico server SGI OpenGL remoto.


4
Sbagliato. Come nel commento che ho lasciato nella risposta sopra: se stai caricando risorse (es: textures) su un thread e le usi per renderizzare su un altro, con condivisione tramite wglShareLists o simili, devi chiamare glFinish prima di segnalare al thread di rendering che è libero di eseguire il rendering di ciò che è stato caricato in modo asincrono. E non è una no-op come hai detto.
Marco Mp

Veramente? Puoi sostenere la necessità di chiamare glFinish prima di utilizzare un oggetto condiviso con un collegamento alle specifiche? Riesco a vedere totalmente un driver difettoso che ne ha bisogno. E quel tipo di ritorno a quello che ho detto. Le persone usano glFinish per aggirare i driver difettosi. Per questo motivo i driver che funzionano correttamente lo ignorano per le prestazioni. Il caso d'uso delle specifiche originali di profiling (e rendering a buffer singolo) non funziona più.
Starmole

2
Esperienza personale di avere a che fare con trame danneggiate, oltre a questo: higherorderfun.com/blog/2011/05/26/… Se ci pensi, ha senso, tuttavia, poiché non è molto diverso dall'avere mutex o altra sincronizzazione. api tra i thread: prima che un contesto possa utilizzare una risorsa condivisa, l'altro deve completare il caricamento di quella risorsa, quindi è necessario assicurarsi che il buffer sia stato svuotato. Penso più al caso d'angolo delle specifiche piuttosto che al bug, ma non ho prove per questa affermazione :)
Marco Mp

Risposta fantastica, grazie. Soprattutto "molti vecchi programmi cospargevano glFlush e glFinish in tutto il loro codice senza motivo come voodoo".
akauppi

Non sono davvero operazioni? GlFinish e / o glFlush non sono molto utili per l'elaborazione di immagini ad alta intensità o per il coprocessing matematico basato su GPU? Ad esempio, non leggiamo i risultati del calcolo della GPU dal buffer di pixel alla CPU fino a dopo aver chiamato un glFinish per garantire che tutti i calcoli di pixel siano stati scritti. Ci manca qualcosa?
Prassitele

5

Dai un'occhiata qui . In breve, dice:

glFinish () ha lo stesso effetto di glFlush (), con l'aggiunta che glFinish () si bloccherà fino a quando tutti i comandi inviati non saranno stati eseguiti.

Un altro articolo descrive altre differenze:

  • Le funzioni di scambio (utilizzate nelle applicazioni a doppio buffer) scaricano automaticamente i comandi, quindi non è necessario chiamare glFlush
  • glFinish forza OpenGL a eseguire comandi eccezionali, il che è una cattiva idea (ad esempio con VSync)

Per riassumere, questo significa che non hai nemmeno bisogno di queste funzioni quando usi il doppio buffering, a meno che la tua implementazione di swap-buffer non scarichi automaticamente i comandi.


Sì, giusto, ma il documento dice che entrambi hanno lo stesso effetto con solo poche differenze riguardanti il ​​sistema di finestre, per esempio. Ma comunque ho modificato la mia risposta;)
AndiDog

Bel richiamo alla sfumatura. Chiarisce se è necessario chiamare glFlush () e THEN glFinish () - (una domanda che avevamo dopo aver letto tutto sopra.)
Prassitele

4

Non sembra esserci un modo per interrogare lo stato del buffer. C'è questa estensione Apple che potrebbe servire allo stesso scopo, ma non sembra multipiattaforma (non l'ho provata). A una rapida occhiata, sembra che prima di flushinserire il comando fence; è quindi possibile interrogare lo stato di quella barriera mentre si muove attraverso il buffer.

Mi chiedo se potresti usare flushprima di eseguire il buffering dei comandi, ma prima di iniziare a eseguire il rendering del fotogramma successivo che chiami finish. Ciò ti consentirebbe di iniziare a elaborare il fotogramma successivo mentre la GPU funziona, ma se non viene completato entro il momento in cui torni, finishsi bloccherà per assicurarti che tutto sia in uno stato nuovo.

Non l'ho provato, ma lo farò a breve.

L'ho provato su una vecchia applicazione che ha un uso abbastanza uniforme di CPU e GPU. (Originariamente utilizzato finish.)

Quando l'ho cambiato flushalla fine e finishall'inizio, non ci sono stati problemi immediati. (Tutto sembrava a posto!) La reattività del programma è aumentata, probabilmente perché la CPU non era bloccata in attesa della GPU. Sicuramente un metodo migliore.

Per confronto, ho rimosso finisheddall'inizio del fotogramma, uscendo flush, e ha eseguito lo stesso.

Quindi direi use flushe finish, perché quando il buffer è vuoto alla chiamata a finish, non vi è alcun calo di prestazioni. E immagino che se il buffer fosse pieno dovresti volerlo finishcomunque.


0

La domanda è: vuoi che il tuo codice continui a essere eseguito mentre i comandi OpenGL vengono eseguiti o solo dopo che i tuoi comandi OpenGL sono stati eseguiti.

Questo può essere importante nei casi, come i ritardi di rete, per avere un determinato output della console solo dopo che le immagini sono state disegnate o simili.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.