Diferença entre "buffer" e "array" no OpenGL?

12

Quando leio o doc no webGL ou OpenGL, podemos observar alguns padrões de como os nomes de funções e objetos são usados. Mas não consigo entender a diferença entre objeto de buffer e uma matriz.

Existem "objetos de buffer de vértice", "objetos de matriz de vértice" e até algum tipo de "matriz de buffer" ou "buffer de matriz".

No contexto do OpenGL, quando algo é "array" e quando deve ser chamado de "buffer"?

coobitar
fonte
Alguma perspectiva, pense em buscar dados pela rede e armazenar um log de tudo o que é recebido. Você precisa ler o soquete e colocar os dados recebidos em algum lugar para que você possa transmiti-los, isso é um buffer. Geralmente, pode ser um tipo de lista simples com escopo local e alocação dinâmica. Às vezes, é tão simples quanto um char* buffer = socketRead();(pseudocódigo). O log, por outro lado, dura todo o ciclo de vida do aplicativo. Então, você cria uma matriz em algum lugar e começa a ler o soquete, sempre que obtém dados que gravam essa parte na matriz, fornecendo uma lista clara de todos os dados recebidos.
Kevin

Respostas:

5

A nomeação de Vertex Array Object é um tanto infeliz. Existem três coisas diferentes que aparecem (costumavam aparecer) em / com / em torno de seu aplicativo e que são (foram, historicamente) nomeadas de maneira diferente, com "matriz" ou "buffer" no nome (bem, também há objetos framebuffer, mas eu vou ignorar isso).

  1. Dados que residem em seu aplicativo, formal e factualmente, mas que são puxados pelo OpenGL de uma só vez (em oposição ao vértice por vértice). Era uma vez o que você chamaria de matriz de vértices .
    A intenção disso era tornar o acesso mais eficiente, já que o OpenGL podia copiar tudo de uma só vez, em um momento bem definido, quando você prometia que os dados eram consistentes e enviá-los pelo AGP ou qualquer outro bloco. Isso não existe mais.
  2. Dados obscurecidos e acessíveis por um identificador que pode ser "vinculado", ou seja, ativado. Os dados podem, de fato, residir na memória principal ou na placa de vídeo, ou ser movidos para uma região mapeada por PCIe, o que for, mas de qualquer maneira que você não possua formalmente (mesmo que esteja fisicamente na RAM e se os dados vieram do seu aplicativo) ) - a menos que você o tenha "mapeado" atualmente através da API correspondente, recuperando um ponteiro gravável (e às vezes legível). Você também tem uma capacidade limitada de controlar o que acontece com os dados (você pode dar algumas dicas, mas é praticamente isso).
    O OpenGL pode mover esses dados mais ou menos livremente, e você só pode / pode copiar de / para o buffer através da API correspondente ou acessar os dados enquanto estão sendo mapeados. Isso é o que você chama de objeto de buffer ( objeto de buffer de vértice, se ele contiver vértices, mas realmente não precisa, também pode ser dados de imagem ou uniformes, apenas os vértices foram os primeiros a serem suportados, uma vez).
    A intenção disso é garantir que o OpenGL possa (em princípio) fazer o que quiser, pode até empurrar o buffer sobre o PCIe especulativamente antes mesmo de desenhar. Isso funciona porque você não possui os dados (o OpenGL possui!) E só pode acessá-los através da API especificada; portanto, é sabido sempre que os dados são válidos. O driver pode até optar por jogar fora a memória buffer na placa gráfica quando precisar de memória para algo diferente e depois restaurá-la de sua cópia secreta quando necessário.
  3. Um equívoco realmente estúpido para o qual um nome muito melhor seria algo como conjunto de buffers ou conjunto de descritores, esse é o infame objeto de matriz de vértices . Do seu ponto de vista, é apenas um conjunto de identificadores de buffer agrupados sob outro identificador obscuro (que você pode vincular). Por acaso, a realidade é um pouco mais complicada. De fato, o VAO está muito mais próximo de como o hardware real funciona. As placas gráficas possuem um número pequeno (geralmente algo como 2, 4 ou 8) de conjuntos de descritores (não apenas para buffers, mas também para samplers) com tantas e muitas entradas em cada uma, entre as quais elas podem alternar com muita eficiência .
    Agora, a intenção do objeto de matriz de vértices é reduzir o número de chamadas de API e o número de verificações de consistência que o OpenGL deve fazer internamente e, é claro, usar o hardware como ele funciona. Se você vincular 5 buffers, cada um deverá passar por algumas verificações possivelmente caras, e cada um deles será candidato a falhas de cache no driver, além de que cada um deles precisará se comunicar com a placa gráfica para alterar um descritor, etc. Para vincular um VAO, o driver pode (geralmente) simplesmente alternar o descritor definido na placa de vídeo e pronto.
Damon
fonte
8

Um objeto de matriz de vértice (VAO) é um objeto que contém um ou mais objetos de buffer de vértice e foi projetado para armazenar as informações de um objeto renderizado completo.

(puxado de khronos )

Cada buffer tende a constituir um atributo de uma matriz de vértices (objeto). Um VAO pode conter muitos atributos de vértice (por exemplo, posição, cor, UV). Cada um pode ser mantido em seu próprio buffer, em que buffer indica uma série não formatada de bytes contíguos e onde você precisa especificar explicitamente o tamanho (tipo) por elemento de buffer para as chamadas OpenGL do lado da CPU e para o sombreamento do lado da GPU.

Essa é uma maneira. As outras maneiras pelas quais isso pode funcionar são:

  • Todos os atributos são armazenados intercalados em um único buffer, OU
  • Alguns dos atributos existem em seus próprios buffers dedicados, enquanto outros compartilham buffers.

O diagrama abaixo ilustra esses dois últimos casos.

insira a descrição da imagem aqui

Conclusão: se a frase "matriz de vértices" for usada não qualificada no OpenGL, você poderá assumir que significa VAO, que, em um contexto OpenGL (especificamente), é uma coisa muito diferente de um buffer.

EDIT re seu comentário: GL_ARRAY_BUFFERindica uma intenção de usar esse objeto de buffer para dados de atributo de vértice, conforme descrito acima. Isso ocorre porque os buffers não são usados apenas para atributos de vértice. No entanto, como é o caso de uso mais comum e você está perguntando sobre os VAOs, não vou entrar nos outros; aqui, no entanto, há uma lista dos outros tipos de buffers que podem ser configurados.

Engenheiro
fonte
Portanto, os buffers são: 1. residem na GPU, 2. na maioria das vezes contêm um tipo de dados (apenas vértice, apenas cor ect), 3.Data é intercalado, ou seja, 111122223333 ect. 4. não forneça método para acessar dados (não buffer [2] ou buffer [vertex_3434]) Agora, as matrizes são: 1.collection de buffers, 2.store informações sobre como analisar os buffers que eles contêm. , o tamanho de um elemento, compensações, para que os dados de buffers pode ser acessado corretamente direita.?
coobit
1. Existem buffers nas duas extremidades e na transferência entre a CPU e a GPU (potencialmente para frente e para trás); caso contrário, como você preencheria os dados a serem carregados na GPU ao carregar uma malha do disco ?. Sim, os elementos são do tipo uniforme em todo o buffer, mas, dependendo da tecnologia que você está usando, cada elemento do buffer pode ser um structtipo primitivo ou . Os dados podem ser intercalados ou completamente uniformes, por buffer. Você pode indexá-los, assim como em uma matriz C tradicional na CPU. Matriz objetos (usar esta terminologia correta ou acabar confundindo-se!) ... (continua abaixo)
Engenheiro
2. Sim, e você precisa garantir explicitamente que suas declarações de buffer no shader correspondam às especificações definidas no seu VAO no lado da CPU: "Todo o estado relacionado à definição de dados usada pelo processador de vértice está encapsulado em um vértice objeto de matriz ". (khronos docs)
Engineer
Então, só para acertar mais as unhas ... Como as pessoas trabalhavam antes da AO usando apenas BO? Ou a AO sempre esteve presente no OpenGL e é apenas o VAO que foi introduzido posteriormente à VBO?
coobit
@coobit io7m.com/documents/history-vertex-spec - fornece uma idéia das diferenças entre o pipeline fixo (old school) OpenGL, 3Dfx etc. e o pipeline moderno e programável OpenGL e Direct3D.
Engenheiro de
5

Essa terminologia está enraizada na história do OpenGL. O que é importante lembrar é que, para a maioria das versões GL relevantes aqui, o OpenGL foi desenvolvido de forma incremental e adicionando novas funcionalidades a uma API já existente, em vez de alterá-la.

A primeira versão do OpenGL não possuía nenhum desses tipos de objetos. O desenho foi conseguido através da emissão de várias chamadas glBegin / glEnd, e um problema com esse modelo era que ele era muito ineficiente em termos de sobrecarga de chamada de função.

O OpenGL 1.1 deu os primeiros passos para resolver isso, introduzindo matrizes de vértices. Em vez de especificar diretamente os dados dos vértices, você agora pode obtê-los de matrizes C / C ++ - daí o nome. Portanto, uma matriz de vértices é exatamente isso - uma matriz de vértices e o estado GL necessário para especificá-los.

A próxima grande evolução veio com o GL 1.5 e permitiu armazenar dados da matriz de vértices na memória da GPU, em vez da memória do sistema ("lado do cliente"). Um ponto fraco da especificação da matriz de vértices GL 1.1 era que o conjunto completo de dados de vértices precisava ser transferido para a GPU toda vez que você queria usá-los; se já estivesse na GPU, essa transferência poderia ser evitada e possíveis ganhos de desempenho.

Portanto, um novo tipo de objeto GL foi criado para permitir o armazenamento desses dados na GPU. Assim como um objeto de textura é usado para armazenar dados de textura, um objeto de buffer de vértice armazena dados de vértice. Este é realmente apenas um caso especial de um tipo de objeto de buffer mais geral que pode armazenar dados não específicos.

A API para usar objetos de buffer de vértice era suportada por piggy na API de matrizes de vértice já existente, e é por isso que você vê coisas estranhas como converter deslocamentos de bytes em ponteiros nela. Portanto, agora temos uma API de matrizes de vértices que apenas armazena estado, com os dados sendo originados de objetos de buffer em vez de matrizes na memória.

Isso nos leva quase ao fim de nossa história. A API resultante era bastante detalhada quando se tratava de especificar o estado da matriz de vértices; portanto, outro caminho de otimização era criar um novo tipo de objeto que coletasse todo esse estado, permitisse várias alterações de estado da matriz de vértices em uma única chamada de API e permitisse GPUs potencialmente realizar otimizações devido à capacidade de saber qual estado seria usado com antecedência.

Digite o objeto da matriz de vértices, que coleta tudo isso junto.

Então, para resumir, uma matriz de vértices começou a vida como uma coleção de estado e dados (armazenados em uma matriz) para serem desenhados. Um buffer de vértice substitui o armazenamento da matriz na memória por um tipo de objeto GL, deixando a matriz de vértice apenas no estado. Um objeto de matriz de vértice é apenas um objeto de contêiner para esse estado, permitindo que ele seja alterado mais facilmente e com menos chamadas de API.

Maximus Minimus
fonte
0

Eu não trabalho com o OpenGL há algum tempo, então só posso estar meio certo. De um modo geral: os buffers armazenam uma matriz de memória não formatada. Uma matriz é um termo geral de memória contígua.

Um buffer precisa estar vinculado ao contexto, enquanto uma matriz é apenas uma matriz de dados. Se bem me lembro, os dados no buffer devem ser copiados para a placa gráfica (daí a ligação).

Espero que isso ajude um pouco

Juicef
fonte
O que é GL_ARRAY_BUFFER então? Por que foi chamado assim? De acordo com você hipótese é "memória contígua não formatado" :)
coobit
Bem, este exemplo em particular é apenas um ID para um buffer (ao qual você vincula sua matriz). O buffer de matriz (no seu exemplo) é usado para atributos de vértice, portanto, basicamente, você vincula sua matriz de atributos de vértice a um buffer. Parece confuso, então deixe-me dar um exemplo. Você tem alguma matriz no lado da CPU que pode ser cor, normal, posições etc., e agora deseja que a GPU acesse. É quando o bindBuffer entra, basicamente mapeando seu "cpu-array" para o "gpu-array".
Juicef #
Quanto ao motivo pelo qual foi chamado, não posso responder a isso. Eu presumo que é porque você tem uma matriz de dados diferentes que se passa lá dentro, cor, normal, etc.
Juicef