Sou um programador de gráficos iniciante e tenho me perguntado recentemente - como os dados do modelo (malhas e materiais) fluem do aplicativo (memória da CPU) para a placa gráfica (memória da GPU?)? Digamos que eu tenha um modelo estático (por exemplo, um prédio) que eu carrego e configurei uma vez e não mude ao longo da vida útil do aplicativo.
- Seus dados são enviados para a memória da GPU apenas uma vez e ficam lá para sempre?
- Quando o modelo é realmente renderizado em cada quadro, os processadores da GPU precisam buscar seus dados sempre da memória da GPU? O que eu quero dizer é - se eu tivesse dois modelos renderizados várias vezes cada - importaria se eu renderizasse o primeiro várias vezes e depois o segundo várias vezes ou se eu renderizasse o primeiro apenas uma vez, o segundo apenas uma vez e manteve intercalando assim? Eu poderia chamar essa pergunta de "fluxo de dados interno da GPU" nesse sentido.
- Obviamente, as placas gráficas têm RAM limitada - quando não pode conter todos os dados de modelo necessários para renderizar 1 quadro, acho que ele continua buscando (alguns) da RAM da CPU em cada quadro, está correto?
Sei que há muitos livros e outras coisas sobre isso na internet, mas talvez você tenha algumas orientações gerais rápidas sobre como gerenciar esse fluxo de dados (quando enviar o que e quanto, quando e como renderizar)?
Edit: Eu esqueci de fazer uma distinção: há o envio dos dados para a GPU e há a configuração / ligação dos buffers como atuais . O último causa algum fluxo de dados?
Edit2: Depois de ler o post de Raxvan, gostaria de distinguir algumas ações:
- criação de buffer com inicialização (como ele disse que eu posso armazenar os dados na CPU ou na GPU)
- atualização de dados do buffer (que eu acredito que é simples quando os dados são mantidos na memória RAM da CPU e requer a busca da GPU na memória RAM da CPU (e depois de volta) quando mantidos na memória RAM da GPU)
- vincular o buffer como ativo (é apenas uma maneira de informar à API que eu quero que esse buffer seja renderizado na próxima chamada de empate e não faz nada sozinho ?)
- Chamada de chamada da API (aqui, eu gostaria de ouvir de você o que realmente acontece lá)
Respostas:
Geralmente sim, mas o driver é livre para fazer o que é "ideal", os dados podem ser armazenados na VRAM ou na RAM ou apenas armazenados em cache. Aqui está um atrículo que explica o que realmente acontece com o fluxo VBO .
Por exemplo, se foi sinalizado como um buffer dinâmico openGL (por exemplo, VBO), é mais provável que seja armazenado na RAM. A GPU usa acesso direto à memória (DMA) para acessar o ram diretamente, sem a intervenção da CPU; isso é controlado pelo controlador DMA na placa gráfica e no driver gráfico e é executado no modo kernel.
Assim como as CPUs, as GPUs têm permissão para reordenar as instruções da GPU e as operações de acesso à memória (leia-se: execução fora de ordem ); portanto, é provável que a GPU lide com o cenário mencionado acessando a memória que está no cache (normalmente acessada recentemente ), mas às vezes não pode fazer isso.
Você não quer que isso aconteça. Mas, independentemente disso, a GPU começará a mover a memória entre a RAM e a VRAM (o processador de comando na GPU é responsável por isso), o que tornará a renderização muito mais lenta, o que fará com que a GPU pare, porque precisará aguardar os dados para ser copiado de / para V / RAM.
As GPUs contêm um buffer de comando e todos os comandos da API são submetidos a esse buffer; observe que isso pode acontecer simultaneamente com os dados que estão sendo copiados para a GPU. O buffer do anel de comando é uma fila de comunicação entre a CPU e a GPU ; qualquer comando que precise ser executado precisa ser enviado à fila para que possa ser executado pela GPU. Assim como qualquer operação que vincule novos buffers, deve ser enviada à gpu para que ela possa acessar algum local da memória.
Essa é uma das razões pelas quais o glBegin / glEnd foi reprovado, o envio de novos comandos precisa de sincronização de fila (usando barreiras / cercas de memória).
Quanto aos seus outros pontos:
Você pode alocar um buffer sem inicialização e mantê-lo para uso posterior. Ou você pode alocar um buffer e copiar dados ao mesmo tempo (falando sobre o nível da API).
Você pode usar o glMapBuffer para atualizar a memória no lado da GPU. se a memória será copiada da / para a RAM não é realmente um padrão do padrão e variará bastante, dependendo do fornecedor, tipo de GPU e driver.
Meu segundo ponto na questão principal cobre isso.
Pense na ligação como usar o
this
ponteiro em qualquer linguagem orientada a objetos, embora não seja exatamente a mesma, quaisquer chamadas de API consequentes serão relativas a esse buffer de ligação.fonte
Em geral, o limite e o envolvimento da CPU versus GPU são específicos na plataforma, mas a maioria deles segue este modelo: a CPU possui alguma ram, a GPU também e você pode mover a memória (em alguns casos, a ram é compartilhada, exceto para o por uma questão de simplicidade, vamos nos ater aos carneiros).
primeiro ponto : os dados que você inicializa pode optar por mantê-los na RAM da CPU ou na GPU, e as vantagens são para ambas. Quando você renderiza algo, a GPU precisa fazer o trabalho pesado, portanto, é óbvio que os dados que já estão no mem GPU fornecerão melhor desempenho. para a CPU, ele deve primeiro enviar os dados para a GPU (que pode optar por mantê-los por um tempo) e depois executar a renderização.
segundo ponto : há muitos truques na renderização, mas a principal maneira de fazer é com polígonos. Em um quadro, a gpu renderiza os objetos feitos de polígonos um por um e, após terminar, a GPU envia a imagem para a tela. Não existe um conceito como objetos, existem apenas polígonos e a maneira como você os reúne criará uma imagem. o trabalho da GPU é projetar esses polígonos de 3d para 2d e aplicar efeito (se desejado). Os polígonos seguem apenas o caminho CPU-> GPU-> SCREEN ou GPU-> SCREEN diretamente (se os polígonos já estiverem na ram da GPU)
terceiro ponto : quando você renderiza animações, por exemplo, é melhor manter os dados próximos à CPU, porque lá ele faz o trabalho pesado, não seria ideal manter os dados na GPU, movê-los para a CPU e voltar a cada quadro. Existem muitos outros exemplos como esse para contar, mas em geral todos os dados permanecerão próximos de quem estiver fazendo os cálculos. Normalmente, você deseja mover o máximo de dados possível para a ram da GPU para obter desempenho.
O envio real dos dados para a gpu é feito pela API que você usa (directx / opengl ou outra) e o conceito de ligação e coisas assim são apenas abstrações, para que a API entenda o que você deseja fazer.
Edite para a edição:
buffer creation with initialisation
: é como a diferença entre inta = new int[10]
ea[0] = 0,a[1] = 1.... etc
quando você cria um buffer, abre espaço para os dados e, quando inicia os dados, coloca as coisas que deseja.buffer data update
se estiver no ram da CPU, vocêvertex * vertices
poderá jogar com ele; se não estiver lá, será necessário movê-lo da GPUvertex * vertices = map(buffer_id);
(o mapa é uma função mitológica que deve mover os dados da GPU para a ram da CPU, também tem seu opostobuffer_id = create_buffer(vertices)
;binding the buffer as active
é apenas um conceito que eles chamam debinding
renderização é um processo complexo e é como chamar uma função com 10000 parâmetros. Vinculação é apenas um termo usado para dizer qual buffer vai para onde. Não existe mágica real por trás desse termo, ele não converte, move ou realoca buffers, apenas informa ao driver que, na próxima chamada de empate, use esse buffer.API draw call
Depois de toda a amarração e fixação, esse é o lugar onde a borracha encontra a estrada. A chamada de empate pegará todos os dados (ou os IDs que apontam para os dados) que você especificou, os enviou para a GPU (se necessário) e informará a GPU para começar a triturar os números. Isso não é inteiramente verdade em todas as plataformas, existem muitas diferenças, mas, para simplificar, o sorteio diz à GPU para ... empatar.fonte
A resposta mais correta é: depende de como você a programa, mas é uma boa coisa para se preocupar. Embora as GPUs tenham se tornado incrivelmente rápidas, a largura de banda de e para a RAM da GPU não é e será o gargalo mais frustrante.
Espero que sim. Para velocidade de renderização, você deseja que o máximo possível de dados fique na GPU, em vez de reenviá-lo a cada quadro. Os VBOs atendem exatamente a esse propósito. Existem VBO's estáticas e dinâmicas, sendo a primeira melhor para modelos estáticos e a segunda melhor para modelos cujos vértices mudam a cada quadro (por exemplo, um sistema de partículas). Mesmo quando se trata de VBOs dinâmicos, você não deseja reenviar todos os vértices a cada quadro; apenas os que estão mudando.
No caso de sua construção, os dados do vértice permaneceriam lá, e a única coisa que muda são suas matrizes (modelo / mundo, projeção e exibição).
No caso de um sistema de partículas, criei um VBO dinâmico grande o suficiente para armazenar o número máximo de partículas que jamais existirão para esse sistema. Cada quadro eu envio os dados para as partículas emitidas, juntamente com alguns uniformes, e é tudo. Quando desenho, posso especificar um ponto inicial e final nesse VBO, para não precisar excluir dados de partículas. Só posso dizer que não desenhe isso.
O ato de enviar várias chamadas de draw em vez de apenas uma é um limite muito maior. Confira a renderização instanciada; isso pode ajudá-lo bastante e tornar a resposta a essa pergunta inútil. Eu tive alguns problemas com o driver que ainda não resolvi, mas se você conseguir fazê-lo funcionar, o problema será resolvido.
Você não quer ficar sem RAM da GPU. Se você fizer isso, mude as coisas para não mudar. No cenário hipotético em que você se esgota, provavelmente cairá de alguma forma, mas nunca vi isso acontecer, por isso sinceramente não sei.
Não há fluxo de dados significativo, não. Existe algum custo para isso, mas isso é verdade para todas as linhas de código que você escreve. Descobrir quanto custa você é, novamente, para que serve o perfil.
A resposta de Raxvan parece boa, mas não é muito precisa. No OpenGL, a criação do buffer não reserva nenhum espaço. Se você deseja reservar espaço sem passar nenhum dado, pode chamar glBufferData e apenas passar nulo. (Veja a seção de notas aqui .)
Eu estou supondo que você quer dizer glBufferData, ou outras funções como essa, certo? É aqui que a transferência real de dados ocorre. (A menos que você passe nulo, como acabei de dizer no último parágrafo.)
Sim, mas pode fazer um pouco mais do que isso. Por exemplo, se você vincular um VAO (objeto de matriz de vértices), vincular um VBO, esse VBO se tornará vinculado ao VAO. Mais tarde, se você vincular esse VAO novamente e chamar glDrawArrays, ele saberá qual VBO desenhar.
Observe que, embora muitos tutoriais façam você criar um VAO para cada VBO, fui informado que esse não é o uso pretendido. Supostamente, você deve criar um VAO e usá-lo em todos os VBO que possuam os mesmos atributos. Ainda não tentei isso, então não posso dizer com certeza se é melhor ou pior.
O que acontece aqui é bem direto (da nossa perspectiva). Digamos que você vincule um VAO e chame glDrawArrays. Você especifica um ponto inicial e uma contagem, e ele executa seu sombreador de vértice para cada vértice nesse intervalo, que por sua vez passa suas saídas pela linha. Todo esse processo é outro ensaio, no entanto.
fonte