Pagina inicial » como » Como a CPU e a GPU interagem para renderizar gráficos de computador?

    Como a CPU e a GPU interagem para renderizar gráficos de computador?

    A Unidade Central de Processamento (CPU) e a Unidade de Processamento Gráfico (GPU) do seu computador interagem a cada momento em que você está usando seu computador para fornecer uma interface visual nítida e responsiva. Leia para entender melhor como eles funcionam juntos.

    foto por sskennel.

    A sessão de perguntas e respostas de hoje nos é oferecida por cortesia do SuperUser - uma subdivisão do Stack Exchange, um agrupamento de sites de perguntas e respostas da comunidade..

    A questão

    Sathya, a leitora de superusuários, fez a seguinte pergunta:

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

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

    Eu estava apenas curioso e queria saber todo o processo de duplo clique em Triangle.exe no Windows XP até que eu possa ver o triângulo girando no monitor. O que acontece, como a CPU (que primeiro lida com o arquivo .exe) e a GPU (que finalmente gera o triângulo na tela) interagem?

    Eu acho que envolvido na exibição deste 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 DirectX / OpenGL
    • Nvidia Driver

    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 cada passo (suponha que isso vá além do escopo), mas uma explicação que um cara de TI intermediário pode seguir.

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

    A resposta

    Embora vários membros da comunidade tenham respondido à pergunta, Oliver Salzburg foi além e respondeu não apenas com uma resposta detalhada, mas também com excelentes gráficos de acompanhamento..

    Imagem por JasonC, disponível como papel de parede aqui.

    Ele escreve:

    Eu decidi escrever um pouco sobre o aspecto de programação e como os componentes conversam entre si. Talvez isso lance alguma luz em certas áreas.

    A apresentação

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

    Há muitas maneiras de desenhar um triângulo na tela. Por simplicidade, vamos supor que nenhum buffer de vértice foi usado. (UMA buffer de vérticeé uma área de memória onde você armazena as 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 temos que executar alguns andaimes. Veremos porque mais tarde:

    // Limpar a tela e o buffer de profundidade glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Redefinir a matriz de ModelView atual glMatrixMode (GL_MODELVIEW); glLoadIdentity (); // Desenho Usando Triângulos glBegin (GL_TRIANGLES); // vermelho glColor3f (1,0f, 0,0f, 0,0f); // Topo do Triângulo (Frente) glVertex3f (0.0f, 1.0f, 0.0f); // glColor3f verde (0,0f, 1,0f, 0,0f); // Esquerda do triângulo (frente) glVertex3f (-1.0f, -1.0f, 1.0f); // Blue glColor3f (0,0f, 0,0f, 1,0f); // Direita do triângulo (Front) glVertex3f (1.0f, -1.0f, 1.0f); // Feito Desenho glEnd ();

    Então, o que isso fez?

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

    • OpenGL
    • Direct3D
    • CUDA

    Para este exemplo, vamos ficar com o OpenGL. Agora seu interface para o driver é o que lhe dá todas as ferramentas que você precisa para fazer o seu programa conversa para a placa gráfica (ou o driver, que então conversas para o cartão).

    Esta interface é obrigada a dar-lhe certas Ferramentas. Essas ferramentas tomam 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.

    O andaime

    Antes que você possa realmente fazer qualquer desenho real, você terá que executar um configuração. Você tem que definir sua viewport (a área que será realmente renderizada), sua perspectiva Câmera em seu mundo), o anti-aliasing que você estará usando (para suavizar a borda do seu triângulo) ...

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

    Limpar a tela

    O pipeline gráfico não vai limpar a tela para você em cada quadro. Você terá que contar isso. Por quê? Isso é por que:

    Se você não limpar a tela, simplesmente desenhar cada quadro. É por isso que nós chamamos glClear com oGL_COLOR_BUFFER_BIT conjunto. O outro bit (GL_DEPTH_BUFFER_BIT) diz ao OpenGL para limpar o profundidadeamortecedor. Esse buffer é usado para determinar quais pixels estão na frente (ou atrás) de outros pixels.

    Transformação


    Fonte da imagem

    Transformação é a parte em que pegamos todas as coordenadas de entrada (os vértices do nosso triângulo) e aplicamos nossa matriz ModelView. Esta é a matriz que explica como nossos modelo (os vértices) são girados, dimensionados e traduzidos (movidos).

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

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

    Nós vamos voltar para a transformação um pouco mais tarde.

    Desenhando

    Para desenhar um triângulo, podemos simplesmente dizer ao OpenGL para iniciar um novo lista de triângulos chamando glBegin com o GL_TRIANGLES constante.
    Existem também outras formas que você pode desenhar. Como uma tira triangular ou um triângulo. Estas são principalmente otimizações, pois requerem 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 3 coordenadas (como estamos no espaço 3D). Além disso, também forneço cor para cada vértice, chamandoglColor3f antes chamando glVertex3f.

    A sombra entre os 3 vértices (os 3 cantos do triângulo) é calculada pelo OpenGLautomaticamente. Ele irá interpolar a cor em toda a face do polígono.

    Interação

    Agora, quando você clica na janela. O aplicativo só precisa capturar a mensagem da janela que sinaliza o clique. Então você pode executar qualquer ação no seu programa que você quer.

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

    Primeiro você precisa saber claramente em qual pixel o usuário clicou na janela. Então, levando o seu perspectivaem conta, você pode calcular a direção de um raio, desde o ponto do clique do mouse na sua cena. Você pode então calcular se algum objeto em sua cena 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 que são geralmente aplicadas:

    • Transformação baseada em matriz
    • Transformação baseada em osso

    A diferença é que ossos afetar único vértices. Matrizes sempre afetam todos os vértices desenhados da mesma maneira. Vamos ver um exemplo.

    Exemplo

    Mais cedo, nós carregamos nossa matriz de identidade antes de desenhar nosso triângulo. A matriz de identidade é aquela que simplesmente fornece sem transformação em absoluto. Então, o que quer que eu desenhe, só é afetado pela minha perspectiva. Então, o triângulo não será girado em tudo.

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

    // Rotate The Triangle On O eixo Y glRotatef (quantidade, 0.0f, 1.0f, 0.0f); 

    montante é, evidentemente, apenas um valor fixo. Se você quiser animar, você terá que acompanhar montantee aumentá-lo a cada quadro.

    Então, espere, o que aconteceu com toda a matriz falar mais cedo?

    Neste exemplo simples, não precisamos nos preocupar com matrizes. Nós simplesmente chamamos glRotatef e cuida de tudo isso para nós.

    glRotate produz uma rotação de ângulo graus em torno do vetor x y z. A matriz atual (seeglMatrixMode) é multiplicada por uma matriz de rotação com o produto substituindo a matriz atual, como ifglMultMatrix foram chamados com a seguinte matriz como seu 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 + c cy z ⁡ 1 - c - x ⁢ s 0 x ⁢ z ⁡ 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 para OpenGL. Mas não está dizendo nos qualquer coisa. Onde está a comunicação??

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

    Enviando um vértice para a GPU será tão rápido, eu nem sei como expressá-lo. O envio de milhares de vértices da CPU para a GPU, todos os quadros, provavelmente não tem problema algum.

    Limpar a tela pode levar um milissegundo ou pior (lembre-se, você normalmente só tem cerca de 16 milissegundos de tempo para desenhar cada quadro), dependendo de quão grande é o seu viewport. Para limpá-lo, o OpenGL precisa desenhar todos os pixels da cor que você deseja limpar, que podem ser milhões de pixels.

    Além disso, podemos apenas perguntar ao OpenGL sobre as capacidades do nosso adaptador gráfico (resolução máxima, anti-aliasing max, profundidade de cor máxima,…).

    Mas também podemos preencher uma textura com pixels, cada um com uma cor específica. Cada pixel mantém um valor e a textura é um "arquivo" gigante cheio de dados. Nós podemos carregar isso na placa gráfica (criando um buffer de textura), então carregar um shader, dizer ao shader para usar nossa textura como uma 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 formas. Eu presumo que o CUDA tenha um desempenho semelhante àquele aspecto, mas nunca tive a oportunidade de trabalhar com ele.

    Nós realmente apenas tocamos ligeiramente o assunto. Programação de gráficos 3D é um inferno de uma besta.


    Fonte da imagem


    Tem algo a acrescentar à explicação? Soe fora nos comentários. Quer ler mais respostas de outros usuários do Stack Exchange com experiência em tecnologia? Confira o tópico de discussão completo aqui.