Como o OpenGL funciona no nível mais baixo?

Eu entendo como escrever programas OpenGL / DirectX, e eu conheço a matemática e as coisas conceituais por trás disso, mas estou curioso sobre como a comunicação da CPU GPU funciona em um nível baixo.

Digamos que eu tenha um programa OpenGL escrito em C que exiba um triângulo e gire a câmera em 45 graus. Quando eu compilo este programa, ele será transformado em uma série de ioctl-calls, e o driver gpu então envia os comandos apropriados para o gpu, onde toda a lógica de girar o triângulo e configurar os pixels apropriados na cor apropriada é conectada dentro? Ou o programa será compilado em um “programa gpu” que é carregado no gpu e calcula a rotação, etc.? Ou algo completamente diferente?

Edit : Alguns dias depois eu encontrei esta série de artigos, que basicamente responde à pergunta: http://fgiesen.wordpress.com/2011/07/01/a-trip-through-the-graphics-pipeline-2011-part- 1 /

Essa pergunta é quase impossível de responder porque o OpenGL por si só é apenas uma API de front-end e, desde que uma implementação atenda à especificação e o resultado esteja de acordo com isso, ela pode ser feita da maneira que você desejar.

A questão pode ter sido: Como um driver OpenGL funciona no nível mais baixo. Agora, isso é novamente impossível de responder em geral, já que um driver está intimamente ligado a alguma peça de hardware, que pode fazer coisas novamente, no entanto, o desenvolvedor projetou.

Então a pergunta deveria ser: “Como se parece em média nos bastidores do OpenGL e do sistema gráfico?”. Vamos ver isso de baixo para cima:

  1. No nível mais baixo, há algum dispositivo gráfico. Hoje em dia estas são GPUs que fornecem um conjunto de registros controlando sua operação (que registra exatamente é dependente do dispositivo) tem alguma memory de programa para shaders, memory em massa para dados de input (vértices, texturas, etc.) e um canal de E / S para o resto do sistema sobre o qual recebe / envia dados e streams de comandos.

  2. O driver gráfico acompanha o estado das GPUs e todos os programas aplicativos de resources que fazem uso da GPU. Também é responsável pela conversão ou qualquer outro processamento dos dados enviados pelos aplicativos (converter texturas no formato pixel suportado pela GPU, compilar shaders no código de máquina da GPU). Além disso, fornece uma interface abstrata e dependente do driver para os programas aplicativos.

  3. Depois, há o driver / biblioteca cliente OpenGL dependente do driver. No Windows, este é carregado por proxy através do opengl32.dll, em sistemas Unix, isto reside em dois locais:

    • Módulo GLX X11 e driver GLX dependente do driver
    • e /usr/lib/libGL.so pode conter algum material dependente do driver para renderização direta

    No MacOS X, este é o “OpenGL Framework”.

    É essa parte que traduz as chamadas do OpenGL como você faz em chamadas para as funções específicas do driver na parte do driver descrita em (2).

  4. Finalmente, a biblioteca real da API OpenGL, opengl32.dll no Windows e no Unix /usr/lib/libGL.so; isso basicamente apenas repassa os comandos para a implementação do OpenGL apropriada.

Como a comunicação real acontece não pode ser generalizada:

No Unix, a conexão 3 < -> 4 pode acontecer através de Sockets (sim, pode, e passa pela rede se você quiser) ou através da Memória Compartilhada. No Windows, a biblioteca de interface e o cliente do driver são carregados no espaço de endereço do processo, de modo que não há muita comunicação, mas simples chamadas de function e passagem de variável / ponteiro. No MacOS X isso é semelhante ao Windows, apenas que não há separação entre a interface OpenGL e o driver client (essa é a razão pela qual o MacOS X é tão lento para acompanhar as novas versões do OpenGL, sempre requer uma atualização completa do sistema operacional para entregar o novo estrutura).

A comunicação entre 3 < -> 2 pode passar por ioctl, ler / escrever ou mapear alguma memory no espaço de endereço do processo e configurar a MMU para acionar algum código de driver sempre que forem feitas alterações nessa memory. Isto é bastante semelhante em qualquer sistema operacional, uma vez que você sempre tem que cruzar o limite do kernel / userland: Por fim, você passa por algum syscall.

A comunicação entre o sistema e a GPU acontece através do barramento periférico e dos methods de access definidos, de modo PCI, AGP, PCI-E, etc., que funcionam através de E / S de porta-E / S, memory mapeada, DMA, IRQs.

Quando eu compilo este programa, ele será transformado em uma série de ioctl-calls, e o driver gpu então envia os comandos apropriados para o gpu, onde toda a lógica de girar o triângulo e configurar os pixels apropriados na cor apropriada é conectada dentro? Ou o programa será compilado em um “programa gpu” que é carregado no gpu e calcula a rotação, etc.?

Você não está longe. Seu programa chama o driver cliente instalável (que não é realmente um driver, é uma biblioteca compartilhada do userspace). Isso usará o ioctl ou um mecanismo similar para passar dados ao driver do kernel.

Para a próxima parte, depende do hardware. Placas de vídeo mais antigas tinham o que é chamado de “pipeline de function fixa”. Havia espaços de memory dedicados na placa de vídeo para matrizes e hardware dedicado para pesquisa de textura, mistura, etc. O driver de vídeo carregaria os dados e sinalizadores corretos para cada uma dessas unidades e configuraria o DMA para transferir seus dados de vértice (posição , cor, coordenadas de textura, etc).

O hardware mais novo tem núcleos de processador (“shaders”) dentro da placa de vídeo, que diferem da sua CPU, pois cada um deles roda muito mais lentamente, mas muitos deles trabalham em paralelo. Para essas placas de vídeo, o driver prepara os binários do programa para serem executados nos sombreadores da GPU.

Seu programa não é compilado para qualquer GPU em particular; é apenas vinculado dinamicamente a uma biblioteca que implementará o OpenGL. A implementação real pode envolver o envio de comandos OpenGL para a GPU, a execução de fallbacks de software, a compilation de shaders e o envio para a GPU, ou até mesmo a utilização de fallbacks de shader para comandos OpenGL. O cenário gráfico é bastante complicado. Felizmente, as ligações isolam-no da maior parte da complexidade dos controladores, deixando os implementadores de controladores livres para utilizarem as técnicas que considerarem adequadas.

Os compiladores / linkers C / C ++ fazem exatamente uma coisa: eles convertem arquivos de texto em uma série de opcodes específicos da máquina que são executados na CPU. OpenGL e Direct3D são apenas APIs C / C ++; eles não podem magicamente converter seu compilador / vinculador C / C ++ em um compilador / vinculador para a GPU.

Cada linha de código C / C ++ que você escreve será executada na CPU. As chamadas para o OpenGL / Direct3D chamarão as bibliotecas C / C ++, estáticas ou dinâmicas, conforme o caso.

O único lugar em que um “programa gpu” entra em jogo é se o seu código cria explicitamente shaders. Ou seja, se você fizer as chamadas da API para o OpenGL / D3D, isso causará a compilation e a vinculação de shaders. Para fazer isso, você (em tempo de execução, não em tempo de compilation C / C ++) gera ou carrega strings que representam shaders em alguma linguagem shader. Em seguida, você os empurra pelo compilador de shaders e recupera um object nessa API que representa esse shader. Em seguida, você aplica um ou mais shaders a um comando de renderização específico. Cada uma dessas etapas acontece explicitamente na direção do seu código C / C ++, que, como dito anteriormente, é executado na CPU.

Muitas linguagens de sombreamento usam a syntax semelhante a C / C ++. Mas isso não os torna equivalentes ao C / C ++.