Como a CPU e a GPU interagem na exibição de gráficos de computador?

58

Aqui você pode ver uma captura de tela de um pequeno programa C ++ chamado Triangle.exe com um triângulo rotativo baseado na API OpenGL.

insira a descrição da imagem aqui

É certo que um exemplo muito básico, mas acho que é aplicável a outras operações de placas gráficas.

Fiquei curioso e queria conhecer todo o processo, clicando duas vezes no Triangle.exe no Windows XP até ver o triângulo girando no monitor. O que acontece, como a CPU (que primeiro lida com o .exe) e a GPU (que finalmente gera o triângulo na tela) interagem?

Eu acho que envolvido na exibição desse triângulo rotativo é principalmente o seguinte hardware / software, entre outros:

Hardware

  • HDD
  • Memória do sistema (RAM)
  • CPU
  • Memória de vídeo
  • GPU
  • tela de LCD

Programas

  • Sistema operacional
  • API do DirectX / OpenGL
  • Driver Nvidia

Alguém pode explicar o processo, talvez com algum tipo de fluxograma para ilustração?

Não deve ser uma explicação complexa que cubra todas as etapas (acho que isso iria além do escopo), mas uma explicação que um profissional de TI intermediário pode seguir.

Tenho certeza de que muitas pessoas que se autodenominariam profissionais de TI não poderiam descrever esse processo corretamente.

JohnnyFromBF
fonte
Seu dilema terminaria se você pudesse considerar a GPU apenas uma extensão da CPU!
KawaiKx

Respostas:

55

Decidi escrever um pouco sobre o aspecto da programação e como os componentes se comunicam. Talvez isso possa esclarecer algumas áreas.

A apresentação

O que é preciso para ter essa imagem única, que você postou em sua pergunta, desenhada na tela?

Existem várias maneiras de desenhar um triângulo na tela. Para simplificar, vamos assumir que nenhum buffer de vértice foi usado. (Um buffer de vértice é uma área da memória onde você armazena coordenadas.) Vamos assumir que o programa simplesmente disse ao pipeline de processamento de gráficos sobre cada vértice (um vértice é apenas uma coordenada no espaço) em uma linha.

Mas , antes que possamos desenhar qualquer coisa, primeiro precisamos executar alguns andaimes. Veremos o porquê mais tarde:

// Clear The Screen And The Depth Buffer
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 

// Reset The Current Modelview Matrix
glMatrixMode(GL_MODELVIEW); 
glLoadIdentity();

// Drawing Using Triangles
glBegin(GL_TRIANGLES);

  // Red
  glColor3f(1.0f,0.0f,0.0f);
  // Top Of Triangle (Front)
  glVertex3f( 0.0f, 1.0f, 0.0f);

  // Green
  glColor3f(0.0f,1.0f,0.0f);
  // Left Of Triangle (Front)
  glVertex3f(-1.0f,-1.0f, 1.0f);

  // Blue
  glColor3f(0.0f,0.0f,1.0f);
  // Right Of Triangle (Front)
  glVertex3f( 1.0f,-1.0f, 1.0f);

// Done Drawing
glEnd();

Então o que isso fez?

Quando você escreve um programa que deseja usar a placa gráfica, geralmente escolhe algum tipo de interface para o driver. Algumas interfaces bem conhecidas para o driver são:

  • OpenGL
  • Direct3D
  • CUDA

Neste exemplo, ficaremos com o OpenGL. Agora, sua interface com o driver é o que fornece todas as ferramentas necessárias para que o seu programa fale com a placa gráfica (ou com o driver, que depois fala com a placa).

Essa interface é obrigada a fornecer algumas ferramentas . Essas ferramentas assumem a forma de uma API que você pode chamar do seu programa.

Essa API é o que vemos sendo usado no exemplo acima. Vamos olhar mais de perto.

Andaimes

Antes de realmente fazer qualquer desenho real, você terá que realizar uma configuração . Você precisa definir sua janela de exibição (a área que será renderizada), sua perspectiva (a câmera em seu mundo), que anti-aliasing você estará usando (para suavizar as bordas do triângulo) ...

Mas não vamos olhar para nada disso. Vamos dar uma olhada nas coisas que você terá que fazer em todos os quadros . Gostar:

Limpando a tela

O pipeline de gráficos não limpará a tela de todos os quadros. Você terá que contar. Por quê? Isso é por que:

insira a descrição da imagem aqui

Se você não limpar a tela, basta desenhar sobre ela todos os quadros. É por isso que ligamos glClearcom o GL_COLOR_BUFFER_BITaparelho. O outro bit ( GL_DEPTH_BUFFER_BIT) diz ao OpenGL para limpar o buffer de profundidade . Esse buffer é usado para determinar quais pixels estão na frente (ou atrás) de outros pixels.

Transformação

insira a descrição da imagem aqui
Fonte da imagem

Transformação é a parte em que pegamos todas as coordenadas de entrada (os vértices de nosso triângulo) e aplicamos nossa matriz ModelView. Essa é a matriz que explica como nosso modelo (os vértices) é girado, dimensionado e convertido (movido).

Em seguida, aplicamos nossa matriz de projeção. Isso move todas as coordenadas para que elas fiquem de frente para nossa câmera.

Agora, nos transformamos mais uma vez, com nossa matriz Viewport. Fazemos isso para dimensionar nosso modelo para o tamanho de nosso monitor. Agora temos um conjunto de vértices prontos para serem renderizados!

Voltaremos à transformação um pouco mais tarde.

Desenhando

Para desenhar um triângulo, podemos simplesmente dizer ao OpenGL para iniciar uma nova lista de triângulos chamando glBegincom a GL_TRIANGLESconstante.
Também há outras formas que você pode desenhar. Como uma faixa triangular ou um ventilador triangular . Essas são principalmente otimizações, pois exigem menos comunicação entre a CPU e a GPU para desenhar a mesma quantidade de triângulos.

Depois disso, podemos fornecer uma lista de conjuntos de 3 vértices que devem compor cada triângulo. Cada triângulo usa três coordenadas (como estamos no espaço 3D). Além disso, também forneço uma cor para cada vértice, chamando glColor3f antes de chamar glVertex3f.

A sombra entre os três vértices (os três cantos do triângulo) é calculada automaticamente pelo OpenGL . Ele interpola a cor por toda a face do polígono.

Interação

Agora, quando você clica na janela. O aplicativo precisa capturar apenas a mensagem da janela que sinaliza o clique. Em seguida, você pode executar qualquer ação em seu programa que desejar.

Isso fica muito mais difícil quando você deseja começar a interagir com sua cena 3D.

Você primeiro precisa saber claramente em qual pixel o usuário clicou na janela. Depois, levando em consideração sua perspectiva , você pode calcular a direção de um raio, a partir do ponto em que o mouse clica na cena. Você pode calcular se algum objeto em sua cena se cruza com esse raio . Agora você sabe se o usuário clicou em um objeto.

Então, como você faz girar?

Transformação

Estou ciente de dois tipos de transformações geralmente aplicadas:

  • Transformação baseada em matriz
  • Transformação óssea

A diferença é que os ossos afetam vértices únicos . Matrizes sempre afetam todos os vértices desenhados da mesma maneira. Vejamos um exemplo.

Exemplo

Anteriormente, carregamos nossa matriz de identidade antes de desenhar nosso triângulo. A matriz de identidade é aquela que simplesmente não fornece nenhuma transformação . Então, o que quer que eu desenhe, é afetado apenas pela minha perspectiva. Portanto, o triângulo não será rotacionado.

Se eu quiser girar agora, eu poderia fazer as contas sozinho (na CPU) e simplesmente chamar glVertex3fcom outras coordenadas (que são giradas). Ou eu poderia deixar a GPU fazer todo o trabalho, ligando glRotatefantes de desenhar:

// Rotate The Triangle On The Y axis
glRotatef(amount,0.0f,1.0f,0.0f);               

amounté claro que é apenas um valor fixo. Se você deseja animar , precisará acompanhar amounte aumentá-lo a cada quadro.

Então, espere, o que aconteceu com toda a conversa da matriz antes?

Neste exemplo simples, não precisamos nos preocupar com matrizes. Simplesmente chamamos glRotatefe isso cuida de tudo isso para nós.

glRotateproduz uma rotação de anglegraus ao redor do vetor xyz. A matriz atual (consulte glMatrixMode ) é multiplicada por uma matriz de rotação com o produto substituindo a matriz atual, como se glMultMatrix fosse chamado com a seguinte matriz como argumento:

x 2 ⁡ 1 - c + cx ⁢ y ⁡ 1 - c - z ⁢ sx ⁢ z - 1 - c + y ⁢ s 0 y ⁢ x ⁡ 1 - c + z ⁢ sy 2 ⁡ 1 - c + cy ⁢ z ⁡ 1 - c - x 0 s 0 x ⁢ z y 1 - c - y ⁢ sy ⁢ z ⁡ 1 - c + x ⁢ sz 2 ⁡ 1 - c + c 0 0 0 0 1

Bem, obrigado por isso!

Conclusão

O que se torna óbvio é que há muita conversa com o OpenGL. Mas não está nos dizendo nada. Onde está a comunicação?

A única coisa que o OpenGL está nos dizendo neste exemplo é quando terminar . Toda operação levará um certo tempo. Algumas operações demoram incrivelmente tempo, outras são incrivelmente rápidas.

Enviar um vértice para a GPU será tão rápido que eu nem saberia como expressá-lo. Enviar milhares de vértices da CPU para a GPU, todos os quadros, provavelmente não é problema.

A limpeza da tela pode levar um milissegundo ou pior (lembre-se de que você normalmente tem apenas 16 milissegundos de tempo para desenhar cada quadro), dependendo do tamanho da sua viewport. Para limpá-lo, o OpenGL precisa desenhar todos os pixels da cor que você deseja limpar, que podem ser milhões de pixels.

Fora isso, podemos apenas perguntar ao OpenGL sobre os recursos do nosso adaptador gráfico (resolução máxima, anti-aliasing máximo, profundidade máxima de cor, ...).

Mas também podemos preencher uma textura com pixels, cada um com uma cor específica. Cada pixel possui um valor e a textura é um "arquivo" gigante preenchido com dados. Podemos carregar isso na placa de vídeo (criando um buffer de textura), depois carregar um shader , dizer a ele para usar nossa textura como entrada e executar alguns cálculos extremamente pesados ​​em nosso "arquivo".

Podemos então "renderizar" o resultado de nossa computação (na forma de novas cores) em uma nova textura.

É assim que você pode fazer a GPU funcionar para você de outras maneiras. Presumo que o CUDA tenha um desempenho semelhante a esse aspecto, mas nunca tive a oportunidade de trabalhar com ele.

Nós realmente apenas tocamos um pouco o assunto todo. A programação de gráficos 3D é um inferno.

insira a descrição da imagem aqui
Fonte da imagem

Der Hochstapler
fonte
38

É difícil entender exatamente o que você não entende.

A GPU possui uma série de registros que o BIOS mapeia. Isso permite que a CPU acesse a memória da GPU e instrua a GPU a executar operações. A CPU conecta valores nesses registradores para mapear parte da memória da GPU para que a CPU possa acessá-la. Em seguida, ele carrega instruções nessa memória. Em seguida, ele grava um valor em um registro que informa à GPU para executar as instruções que a CPU carregou em sua memória.

As informações consistem no software que a GPU precisa executar. Este software é fornecido com o driver e, em seguida, o driver lida com a responsabilidade dividida entre a CPU e a GPU (executando partes do seu código nos dois dispositivos).

O driver gerencia uma série de "janelas" na memória da GPU na qual a CPU pode ler e gravar. Geralmente, o padrão de acesso envolve as instruções de gravação da CPU ou informações na memória da GPU mapeada e, em seguida, instruindo a GPU, através de um registro, a executar essas instruções ou processar essas informações. As informações incluem lógica de sombreador, texturas e assim por diante.

David Schwartz
fonte
11
Obrigado pela sua explicação. Basicamente, o que eu não entendi é como o conjunto de instruções da CPU se comunica com o conjunto de instruções da GPU, mas obviamente é o driver que faz essa parte. Isso foi o que eu quis dizer com camadas de abstração.
precisa saber é o seguinte
2
Não há nenhum conjunto de instruções da CPU envolvido. O driver e o tempo de execução compilam seu CUDA, OpenGL, Direct3D etc. para programas / kernels de GPU nativos, que também são carregados na memória do dispositivo. O buffer de comando se refere àqueles como qualquer outro recurso.
Axel Gneiting 13/08/2012
2
Não sei a que programas você está se referindo (que são executados na gpu e estão incluídos nos drivers). A gpu é basicamente um hardware de função fixa, e os únicos programas que serão executados são shaders, fornecidos pelo aplicativo, não o driver. O driver apenas compila esses programas e os carrega na memória da gpu.
Ben Richards
11
@ sidran32: Por exemplo, na arquitetura Kepler da nVidia, kernels, fluxos e eventos são criados por software que roda na GPU, e não (geralmente) na CPU. O software da GPU também gerencia o RDMA. Todo esse software é carregado na memória da GPU pelo driver e é executado como um "mini-SO" na GPU que lida com o lado da GPU do par de cooperação CPU / GPU.
David Schwartz
@DavidSchwartz Esqueci as tarefas de computação da GPU. No entanto, eles ainda se comportam de maneira semelhante aos sombreadores, na implementação. Eu não chamaria isso de "mini-SO", pois não tem a mesma funcionalidade normalmente associada aos SOs. Ainda é um software muito especializado, já que a GPU não foi projetada como uma CPU (por um bom motivo).
Ben Richards
13

Fiquei curioso e queria conhecer todo o processo, clicando duas vezes no Triangle.exe no Windows XP até ver o triângulo girando no monitor. O que acontece, como a CPU (que primeiro lida com o .exe) e a GPU (que finalmente gera o triângulo na tela) interagem?

Vamos supor que você realmente saiba como um executável é executado em um sistema operacional e como esse executável é enviado da sua GPU para o monitor, mas não sabe o que está acontecendo no meio. Então, vamos dar uma olhada em um aspecto de hardware e estender ainda mais a resposta do aspecto do programador ...

Qual é a interface entre CPU e GPU?

Usando um driver , a CPU pode falar através de recursos da placa - mãe , como PCI, para a placa de vídeo e enviar comandos para executar algumas instruções da GPU, acessar / atualizar a memória da GPU , carregar um código a ser executado na GPU e mais ...

Mas você não pode realmente falar diretamente com o hardware ou o driver a partir do código; portanto, isso terá que acontecer por meio de APIs como OpenGL, Direct3D, CUDA, HLSL, CG. Enquanto o primeiro executa as instruções da GPU e / ou atualiza a memória da GPU, o último realmente executa o código na GPU, pois são linguagens de física / sombreador.

Por que executar código na GPU e não na CPU?

Embora a CPU seja boa em executar nossos programas diários de estação de trabalho e servidor, não se pensou muito em todos os gráficos brilhantes que você vê nos jogos da atualidade. Naquela época, havia renderizadores de software que faziam o truque de algumas coisas em 2D e 3D, mas eram muito limitantes. Então, aqui é onde a GPU entrou em jogo.

A GPU é otimizada para um dos cálculos mais importantes em gráficos, a Matrix Manipulation . Enquanto a CPU precisa calcular cada multiplicação em uma manipulação de matriz, uma por uma (mais tarde, itens como 3DNow! E SSE atualizados), a GPU pode fazer todas essas multiplicações de uma só vez! Paralelismo.

Mas os cálculos paralelos não são o único motivo, outro motivo é que a GPU está muito mais próxima da memória de vídeo, o que a torna muito mais rápida do que ter que fazer viagens de ida e volta pela CPU, etc.

Como essas instruções / memória / código da GPU mostram gráficos?

Há uma peça que falta para fazer tudo funcionar. Precisamos de algo que possamos escrever para que possamos ler e enviar para a tela. Podemos fazer isso criando um buffer de estrutura . Qualquer que seja a operação que você faça, você atualizará os pixels no buffer de quadros; além da localização, também contém informações sobre cor e profundidade.

Vamos dar um exemplo em que você deseja desenhar um sprite de sangue (uma imagem) em algum lugar; primeiro, a própria textura da árvore é carregada na memória da GPU, o que facilita o redesenho quando desejado. Em seguida, para desenhar o sprite em algum lugar, podemos traduzi-lo usando vértices (colocando-o na posição correta), rasterizando-o (transformando-o de um objeto 3D em pixels) e atualizando o buffer de estrutura. Para ter uma idéia melhor, aqui está um fluxograma de pipeline do OpenGL da Wikipedia:

Esta é a essência principal de toda a ideia gráfica, mais pesquisas são trabalhos de casa para o leitor.

Tamara Wijsman
fonte
7

Para simplificar, podemos descrevê-lo assim. Alguns endereços de memória são reservados (pelo BIOS e / ou sistema operacional) não para RAM, mas para a placa de vídeo. Quaisquer dados gravados com esses valores (ponteiros) vão para o cartão. Portanto, em teoria, qualquer programa pode gravar diretamente na placa de vídeo apenas conhecendo o intervalo de endereços e é exatamente assim que era feito nos velhos tempos. Na prática, com sistemas operacionais modernos, isso é gerenciado pelo driver de vídeo e / ou pela biblioteca de gráficos na parte superior (DirectX, OpenGL etc.).

AZ.
fonte
11
-1 ele pergunta como uma chamada de API do DirectX da CPU pode se comunicar com a GPU, e sua resposta é "ela é gerenciada pelo driver e / ou DirectX" ? Isso também não explica como o código personalizado (ala CUDA) pode ser executado.
BlueRaja - Danny Pflughoeft 13/08
3
por favor aprenda a ler. Eu disse escrevendo para endereços de memória específicos reservados para GPU em vez de RAM. E isso explica como você pode executar tudo. Um intervalo de memória é registrado para um cartão. Tudo o que você escreve nesse intervalo vai para a GPU executando o processamento de vértices, CUDA, qualquer que seja.
AZ.
5

As GPUs são geralmente controladas por buffers de DMA. Ou seja, o driver compila os comandos que recebe do programa de espaço do usuário em um fluxo de instruções (alternar estado, desenhar dessa maneira, alternar contextos etc.), que são copiados para a memória do dispositivo. Em seguida, instrui a GPU a executar esse buffer de comando por meio de um registro PCI ou métodos similares.

Então, a cada chamada de empate etc. o que acontece é que o driver de espaço do usuário compilará o comando, que chamará o driver de espaço do kernel por uma interrupção e que finalmente enviará o buffer de comando para a memória do dispositivo e instruirá a GPU a iniciar a renderização.

Nos consoles, você pode se divertir fazendo tudo isso sozinho, especialmente no PS3.

Axel Gneiting
fonte
0

Eu acho que a CPU envia dados de vídeo para a GPU através do barramento e depois a GPU os exibe. Uma GPU mais rápida pode lidar com mais dados da CPU. Dessa maneira, parte do processamento de cpuoffload na GPU. Portanto, você obtém velocidade mais rápida nos jogos.

É como a RAM, onde a CPU armazena coisas para que possa carregar e processar rapidamente. Ambos tornam os jogos mais rápidos.

Ou a placa de som ou a placa de rede funcionam com o mesmo princípio, ou seja, obter dados e descarregar algum trabalho da CPU.

user583641
fonte
Isso duplica outra resposta e não adiciona novo conteúdo. Não poste uma resposta, a menos que você realmente tenha algo novo para contribuir.
DavidPostill
0

Eu acho que o op não tem certeza do que exatamente a CPU está dizendo para a placa gráfica fazer e por que os comandos relacionados a gráficos (como comandos opengl ou direct3d) não são enviados diretamente para a GPU.

A CPU apenas diz à GPU o que renderizar. Todas as instruções primeiro passam pela CPU onde são configuradas / inicializadas para que a GPU efetue a renderização.

Dave
fonte