Para vaporização do buffer de vértice, glBufferSubData múltiplo VS órfão?

8

Eu estava aprendendo OpenGL recentemente. Nos jogos, precisamos atualizar a posição dos objetos do jogo com frequência, e eles entram e saem da tela constantemente. Portanto, na renderização, precisamos atualizar o buffer de vértice com bastante frequência.

No contexto do OpenGL, uma maneira intuitiva é usar glBufferSubData para atualizar aqueles que foram alterados.

Mas também li on-line um truque chamado Orphaning, que cria novos dados de buffer e carrega todos os dados dos vértices nele.

Também devido ao custo das chagnes do estado e do upload, vários glBufferSubData também podem custar mais.

Aqui está a minha pergunta,

  1. Qual método é melhor?
  2. As barracas são realmente importantes neste caso?
  3. As mudanças de estado e o custo de upload são realmente importantes nesse caso?

Obrigado!

YiFeng
fonte
Por que você deseja fazer o upload novamente da geometria quando na verdade só é possível atualizar a matriz? em outras palavras, você não precisa recarregar os vértices quando apenas as transformações precisam ser alteradas.
usar o seguinte
@ concept3d Sim, você está correto! Cometi um erro ao escrever as descrições dos problemas. Obrigado por me lembrar disso! Eu atualizei as descrições já.
YiFeng 10/11/14

Respostas:

9

Essa é uma pergunta complexa, com muitos pequenos detalhes que realmente importam; o desempenho varia de acordo com a plataforma e o aplicativo. Portanto, você deve analisar possíveis gargalos antes de investir em otimizações.

Dito isto, em primeiro lugar, presumo que você deve reduzir o máximo de uploads e atualizações, por exemplo, usar instanciamento.

Em segundo lugar, observe que as GPUs não podem transferir buffers e renderizar ao mesmo tempo, portanto, todos os comandos OpenGL na fila de comandos são processados ​​sequencialmente pelo dispositivo. Existem diferentes maneiras de copiar dados e / ou torná-los disponíveis para serem usados ​​pela GPU.

Existem várias maneiras de transmitir dados para a GPU

1- glBufferDataou glBufferSubDatamétodo

Usando glBufferDataou glBufferSubDataé como memcpy. você passa um ponteiro e a operação de DMA pode ser executada, como eu disse, porque a memória pode ser fixada na memória da CPU e usada diretamente pela GPU, sem que ocorra uma transferência de memória para a GPU, dependendo do sinalizador de uso (GL_STREAM). Na opinião, você deve tentar isso primeiro porque é mais simples de implementar.

2- obter um ponteiro para a memória interna usando glMapBuffer

Se o descrito acima não for bom o suficiente, você poderá usar glMapBufferum ponteiro para a memória interna e usá-lo para preencher diretamente o buffer, isso é bom para operações de leitura e gravação de arquivos, pois é possível mapear diretamente os dados do arquivo na memória da GPU em vez de copiar primeiro para um buffer temporário. Se você não deseja mapear o buffer inteiro, pode usar o glMapBufferRangeque pode ser usado para mapear uma parte do buffer.

Um truque é criar um buffer grande, use a primeira metade para renderização e a segunda metade para atualização.

3- Órfão de buffer

Em relação ao órfão de buffer, isso pode ser feito usando glBufferData com nulo e os mesmos parâmetros que possuía. O driver retornará o bloco de memória assim que não for usado. E será usado pela próxima chamada glBufferData (nenhuma memória nova será alocada).

Todos os métodos mencionados causam muita sincronização cara, novamente as GPUs não podem transferir buffers e renderizar ao mesmo tempo.

4- Unsynchronized Buffers

O método mais rápido (e mais difícil de acertar) é usar buffers sem sincronização com os quais você pode usar GL_MAP_UNSYNCHRONIZED_BITsinalizadores glMapBufferRange; o problema é que nenhuma sincronização é feita; portanto, podemos fazer o upload de dados para um buffer que começa a ser usado e, portanto, estragar tudo. Você pode usar vários buffers com bit unsync para facilitar um pouco as coisas.

concept3d
fonte
0

Eu faço de outra maneira. Pode haver algo errado lá. Algumas coisas perigosas escondidas.

(Eu uso C # + OpenTK.GameWindow)

Para instanciar o objeto, eu uso um objeto de buffer de matriz separado para o conjunto de matrizes de modelo para cada instância. No vértice compartilhado:

#version 400
layout (location = 0) in vec3 position;
layout (location = 1) in vec3 normal;
layout (location = 2) in vec2 texcoord;
layout (location = 4) in mat4 model_matrix;

uniform mat4 proj_matrix, view_matrix;

No meu código C #, matrizes são armazenadas em uma matriz flutuante float[] mat4_array

Em seguida, vinculo a matriz ao objeto de buffer de matriz:

public void bind_data_instances()
{
    GL.BindBuffer(BufferTarget.ArrayBuffer, id_InstanceBO);
    GL.BufferData(BufferTarget.ArrayBuffer, mat4_array.Length * sizeof(float), mat4_array, BufferUsageHint.DynamicDraw);
}

A cada quadro, as matrizes do modelo são atualizadas. Para atualizar mat4_array, basta ligar para:

Buffer.BlockCopy(mat4_array_new, 0, mat4_array, 0, sizeof(float) * mat4_models.Length);

e renderize a cena. usando GL.DrawElementsInstanced.

Não há chamadas OpenGL adicionais e funciona perfeitamente.

Dennis
fonte
Qual é o objetivo de ter a matriz de modelo como atributo de vértice?
Anton Duzenko
É para o desenho do objeto de instância. E é mais rápido que uma matriz passada como uma variável uniforme.
Dennis