opengl: glFlush () vs. glFinish ()

105

Estou tendo problemas para distinguir a diferença prática entre ligar glFlush()e glFinish().

Os documentos dizem isso glFlush()e glFinish()enviarão todas as operações em buffer para OpenGL para que se possa ter certeza de que todas serão executadas, a diferença é que glFlush()retorna imediatamente onde como glFinish()blocos até que todas as operações sejam concluídas.

Depois de ler as definições, concluí que, se fosse usá- glFlush()las, provavelmente teria o problema de enviar mais operações ao OpenGL do que ele poderia executar. Então, só para tentar, troquei meu glFinish()por a glFlush()e eis que meu programa rodou (pelo que eu pude ver) exatamente o mesmo; taxas de quadros, uso de recursos, tudo era o mesmo.

Portanto, estou me perguntando se há muita diferença entre as duas chamadas ou se meu código não faz com que elas sejam executadas de maneira diferente. Ou onde um deve ser usado contra o outro. Também descobri que o OpenGL teria alguma chamada glIsDone()para verificar se todos os comandos em buffer para a glFlush()estão completos ou não (para que não se enviem operações para o OpenGL mais rápido do que podem ser executadas), mas não consegui encontrar tal função .

Meu código é o loop de jogo típico:

while (running) {
    process_stuff();
    render_stuff();
}
Jay.lee
fonte

Respostas:

93

Lembre-se de que esses comandos existem desde os primeiros dias do OpenGL. glFlush garante que os comandos OpenGL anteriores devem ser concluídos em tempo finito ( especificações do OpenGL 2.1 , página 245). Se você desenhar diretamente no buffer frontal, isso deve garantir que os drivers OpenGL comecem a desenhar sem muito atraso. Você pode pensar em uma cena complexa que aparece objeto após objeto na tela, quando você chama glFlush após cada objeto. No entanto, ao usar o buffer duplo, glFlush praticamente não tem efeito algum, pois as alterações não serão visíveis até que você troque os buffers.

glFinish não retorna até que todos os efeitos dos comandos emitidos [...] anteriormente sejam totalmente realizados . Isso significa que a execução do seu programa espera aqui até que cada pixel seja desenhado e o OpenGL não tenha mais nada a fazer. Se você renderizar diretamente para o buffer frontal, glFinish é a chamada a ser feita antes de usar as chamadas do sistema operacional para tirar screenshots. É muito menos útil para buffer duplo, porque você não vê as alterações que forçou a concluir.

Portanto, se você usar buffer duplo, provavelmente não precisará de glFlush nem de glFinish. SwapBuffers direciona implicitamente as chamadas OpenGL para o buffer correto, não há necessidade de chamar glFlush primeiro . E não se preocupe em enfatizar o driver OpenGL: glFlush não irá engasgar com muitos comandos. Não é garantido que esta chamada retorne imediatamente (seja lá o que isso signifique), então pode levar qualquer tempo que for necessário para processar seus comandos.

Malte Clasen
fonte
1
O que você quer dizer com glFlush "deve ser concluído em tempo FINITO" e mais tarde dizer que glFlush não engasgará porque "pode ​​levar QUALQUER tempo para processar seus comandos"? (Caps meus) Não são mutuamente exclusivos?
Praxiteles
1
Contanto que termine em algum ponto, ele atende aos critérios de tempo FINITO. Alguns motoristas não operam totalmente esta chamada.
Chrisvarnz
SwapBuffers é específico do contexto e só precisa liberar o contexto do thread de chamada. Se renderizar vários contextos, cada um deve ser esvaziado manualmente, pois não é garantido que aconteça ao trocar os buffers por outro contexto.
Andreas
22

Como as outras respostas sugeriram, realmente não há uma boa resposta de acordo com as especificações. A intenção geral glFlush()é que, após chamá-lo, a CPU host não tenha nenhum trabalho relacionado ao OpenGL a fazer - os comandos terão sido enviados para o hardware gráfico. A intenção geral do glFinish()é que, depois de retornar, não resta trabalho restante e os resultados devem estar disponíveis também em todas as APIs não OpenGL apropriadas (por exemplo, leituras do framebuffer, capturas de tela, etc ...). Se isso é realmente o que acontece, depende do driver. A especificação permite uma tonelada de latitude quanto ao que é legal.

Andy Ross
fonte
18

Eu sempre fiquei confuso sobre esses dois comandos também, mas esta imagem deixou tudo claro para mim: Flush vs Finish Aparentemente, alguns drivers de GPU não enviam os comandos emitidos para o hardware, a menos que um certo número de comandos tenha sido acumulado. Neste exemplo, esse número é 5 .
A imagem mostra vários comandos OpenGL (A, B, C, D, E ...) que foram emitidos. Como podemos ver na parte superior, os comandos ainda não foram emitidos, porque a fila ainda não está cheia.

No meio, vemos como glFlush()afeta os comandos enfileirados. Diz ao driver para enviar todos os comandos enfileirados ao hardware (mesmo se a fila ainda não estiver cheia). Isso não bloqueia o thread de chamada. Apenas sinaliza ao driver que podemos não estar enviando comandos adicionais. Portanto, esperar que a fila se encha seria uma perda de tempo.

Na parte inferior, vemos um exemplo usando glFinish(). Quase faz a mesma coisa que glFlush(), exceto que faz o thread de chamada esperar até que todos os comandos tenham sido processados ​​pelo hardware.

Imagem retirada do livro "Advanced Graphics Programming Using OpenGL".

Tara
fonte
Eu realmente sei o que glFlushe glFinishfaço, e não posso dizer o que essa imagem está dizendo. O que está do lado esquerdo e do lado direito? Além disso, essa imagem foi lançada em domínio público ou sob alguma licença que permite publicá-la na Internet?
Nicol Bolas
Eu deveria ter elaborado um pouco. Sobre a licença: Na verdade, não tenho certeza. Me deparei com o PDF no google, enquanto fazia pesquisas.
Tara
7

Se você não viu nenhuma diferença de desempenho, significa que você está fazendo algo errado. Como alguns outros mencionaram, você não precisa chamar nenhum dos dois, mas se chamar glFinish, estará perdendo automaticamente o paralelismo que a GPU e a CPU podem alcançar. Deixe-me mergulhar mais fundo:

Na prática, todo o trabalho que você envia para o driver é agrupado e enviado para o hardware potencialmente muito mais tarde (por exemplo, na hora do SwapBuffer).

Então, se você está chamando glFinish, você está essencialmente forçando o driver a enviar os comandos para a GPU (que ele agrupou até então, e nunca pediu à GPU para trabalhar), e paralisar a CPU até que os comandos enviados sejam completamente executado. Portanto, durante todo o tempo em que a GPU funciona, a CPU não (pelo menos neste segmento). E o tempo todo que a CPU faz seu trabalho (principalmente comandos em lote), a GPU não faz nada. Então sim, glFinish deve prejudicar seu desempenho. (Esta é uma aproximação, já que os drivers podem começar a ter a GPU funcionando em alguns comandos se muitos deles já estiverem em lote. Não é típico, pois os buffers de comando tendem a ser grandes o suficiente para conter muitos comandos).

Agora, por que você chamaria glFinish então? As únicas vezes que usei foram quando tinha erros de driver. Na verdade, se um dos comandos que você envia para o hardware travar a GPU, sua opção mais simples para identificar qual comando é o culpado é chamar glFinish após cada sorteio. Dessa forma, você pode restringir o que exatamente desencadeia a falha

Como uma observação lateral, APIs como Direct3D não oferecem suporte a um conceito de acabamento.

Bahbar
fonte
5
Em "Agora, por que você chamaria glFinish então". Se você estiver carregando recursos (por exemplo: texturas) em um thread e usando-os para renderizar em outro, com compartilhamento por meio de wglShareLists ou semelhantes, você precisa chamar glFinish antes de sinalizar para o thread de renderização que está livre para renderizar o que foi carregado de forma assíncrona.
Marco Mp
Como eu disse abaixo, isso parece uma solução alternativa para um bug de driver. Não consigo encontrar nenhuma menção específica a este requisito. No windows o driver deve travar, no mac é necessário fazer CGLLockContext. Mas usar o glFinish parece muito errado. Para saber quando uma textura é válida, você deve fazer seu próprio bloqueio ou evento em qualquer caso.
starmole
1
opencl opengl interop docs recommend glFinish for synchronization "Antes de chamar clEnqueueAcquireGLObjects, o aplicativo deve garantir que todas as operações GL pendentes que acessam os objetos especificados em mem_objects foram concluídas. Isso pode ser realizado portavelmente emitindo e aguardando a conclusão de um comando glFinish em todos Contextos GL com referências pendentes a esses objetos "
Mark Essel
1
você não "precisa" chamar glFinish, você pode usar objetos de sincronização.
chrisvarnz
Apenas para adicionar, desde a resposta original: Algumas implementações de GL começaram a usar flush / finish para sincronizar entre contextos compartilhados em diferentes threads: developer.apple.com/library/ios/documentation/3DDrawing/… - Mais notavelmente Apple IOS. Acho que é fundamentalmente outro uso indevido da API. Mas esta é uma advertência à minha resposta original: Em algumas implementações, acabamento / limpeza são necessários. Mas como e por que a implementação é definida, não as especificações do OpenGL.
starmole de
7

glFlush realmente data de um modelo cliente-servidor. Você envia todos os comandos gl através de um pipe para um servidor gl. Esse tubo pode amortecer. Assim como qualquer arquivo ou E / S de rede pode armazenar em buffer. glFlush diz apenas "envie o buffer agora, mesmo que ainda não esteja cheio!". Em um sistema local, isso quase nunca é necessário porque uma API OpenGL local dificilmente fará um buffer e apenas emite comandos diretamente. Além disso, todos os comandos que causam a renderização real farão um flush implícito.

O glFinish, por outro lado, foi feito para medir o desempenho. Tipo de PING para o servidor GL. Ele faz um roundtrips de um comando e espera até que o servidor responda "Estou ocioso".

Hoje em dia, os motoristas locais modernos têm ideias bastante criativas sobre o que significa estar ocioso. É "todos os pixels foram desenhados" ou "minha fila de comandos tem espaço"? Além disso, como muitos programas antigos espalhavam glFlush e glFinish por todo o código sem razão, como codificação vodu, muitos drivers modernos simplesmente os ignoram como uma "otimização". Não posso culpá-los por isso, realmente.

Resumindo: trate glFinish e glFlush como nenhum ops na prática, a menos que você esteja codificando para um antigo servidor SGI OpenGL remoto.

toupeira
fonte
4
Errado. Como no comentário que deixei na resposta acima: Se você estiver carregando recursos (por exemplo: texturas) em um thread e usando-os para renderizar em outro, com compartilhamento por meio de wglShareLists ou semelhantes, você precisa chamar glFinish antes de sinalizar para o thread de renderização que é livre para renderizar o que foi carregado de forma assíncrona. E não é um ambiente autônomo como você disse.
Marco Mp
Realmente? Você pode comprovar a necessidade de chamar glFinish antes de usar um objeto compartilhado com algum link de especificação? Eu posso ver totalmente um driver de bug que precisa disso. E isso meio que volta ao que eu disse. As pessoas usam o glFinish para contornar drivers com erros. Por causa disso, os drivers que funcionam corretamente, ignoram o desempenho. O caso de uso da especificação original de criação de perfil (e renderização em buffer único) não funciona mais.
starmole
2
Experiência pessoal de ter que lidar com texturas corrompidas, mais isto: higherorderfun.com/blog/2011/05/26/… Se você pensar bem, faz sentido, entretanto, já que não é muito diferente de ter mutexes ou outra sincronização. api entre threads - antes que um contexto possa usar um recurso compartilhado, o outro deve completar o carregamento desse recurso, portanto, a necessidade de garantir que o buffer foi esvaziado. Eu penso mais no caso das especificações do que no bug, mas não tenho provas para essa afirmação :)
Marco Mp
Resposta incrível, obrigado. Especialmente "muitos programas antigos polvilhavam glFlush e glFinish por todo o código sem razão como vodu".
akauppi
É realmente sem ops? O glFinish e / ou glFlush não são altamente valiosos para processamento de imagem de alta intensidade ou coprocessamento matemático baseado em GPU? Por exemplo, nós não ler os resultados da computação GPU do buffer de pixel para a CPU até depois de chamar um glFinish para garantir que todos pixels cálculos foram escritos. Estamos perdendo alguma coisa?
Praxiteles
5

Dê uma olhada aqui . Resumindo, diz:

glFinish () tem o mesmo efeito que glFlush (), com a adição de que glFinish () irá bloquear até que todos os comandos enviados tenham sido executados.

Outro artigo descreve outras diferenças:

  • As funções de troca (usadas em aplicativos de buffer duplo) liberam automaticamente os comandos, portanto, não há necessidade de chamar glFlush
  • glFinish força o OpenGL a executar comandos excelentes, o que é uma má ideia (por exemplo, com VSync)

Resumindo, isso significa que você nem mesmo precisa dessas funções ao usar buffer duplo, exceto se sua implementação de swap-buffers não liberar automaticamente os comandos.

AndiDog
fonte
Sim, certo, mas o documento diz que ambos têm o mesmo efeito com apenas algumas diferenças em relação ao sistema de janelas, por exemplo. Mas de qualquer maneira eu editei minha resposta;)
AndiDog
Boa chamada para a nuance. Ele esclarece se é necessário chamar glFlush () e THEN glFinish () - (uma pergunta que tivemos depois de ler tudo acima.)
Praxiteles
4

Não parece haver uma maneira de consultar o status do buffer. Existe esta extensão da Apple que poderia servir ao mesmo propósito, mas não parece ser multiplataforma (ainda não tentei). À primeira vista, parece antes de flushvocê empurrar o comando fence; você pode então consultar o status dessa cerca conforme ela se move pelo buffer.

Gostaria de saber se você poderia usar flushantes de armazenar comandos em buffer, mas antes de começar a renderizar o próximo quadro que você chamar finish. Isso permitiria que você começasse a processar o próximo quadro enquanto a GPU funciona, mas se não for feito quando você voltar, finisho bloqueará para garantir que tudo esteja em um estado novo.

Eu não tentei isso, mas tentarei em breve.

Eu tentei em um aplicativo antigo que tem um uso bastante uniforme de CPU e GPU. (Usado originalmente finish.)

Quando mudei para flushno final e finishno início, não houve problemas imediatos. (Tudo parecia bem!) A capacidade de resposta do programa aumentou, provavelmente porque a CPU não estava parada esperando pela GPU. Definitivamente, um método melhor.

Para efeito de comparação, retirei finisheddo início do quadro, saindo flush, e o desempenho foi o mesmo.

Então, eu diria use flushe finish, porque quando o buffer está vazio na chamada para finish, não há impacto no desempenho. E estou supondo que se o buffer estivesse cheio, você deveria querer de finishqualquer maneira.

GManNickG
fonte
0

A questão é: você quer que seu código continue rodando enquanto os comandos OpenGL estão sendo executados, ou apenas rodando após seus comandos OpenGL terem sido executados.

Isso pode ser importante em casos, como em atrasos de rede, para ter determinada saída de console somente após as imagens terem sido desenhadas ou algo semelhante.

anon
fonte