Placas 3D: Shaders, stream processors, TMUs e ROPs

Os shaders são pequenos programas programas utilizados pelos jogos e aplicativos de renderização recentes para executar operações específicas dentro das imagens, criando efeitos diversos, fazendo com que objetos se movam ou sejam distorcidos de forma realística ou simplesmente criando objetos muito detalhados, que seriam complexos demais para criar usando apenas polígonos e texturas.

Um caso clássico é o cabelo dos personagens, onde só é possível obter um resultado realístico desenhando cada fio separadamente e fazendo com que eles se movam de forma mais ou menos independente, acompanhando os movimentos do personagem. Outros usos comuns são efeitos de explosões, grama (não o desenho das folhas propriamente ditas, mas sobretudo a movimentação), folhas de árvores e outros efeitos e detalhes diversos em objetos. Os shaders fazem um trabalho muito bom em todas essas áreas, consumindo relativamente pouco poder de processamento e oferecendo animações realísticas.

Em poucas palavras, os shaders permitem adicionar “profundidade” e realismo aos objetos da cena, contrastando com a aparência lisa e pouco realista dos gráficos usados em jogos antigos.

Antes dos shaders, todas as placas 3D ofereciam apenas um conjunto de efeitos pré-programados, que eram processados por componentes especializados dentro do pipeline gráfico da GPU. A lista incluía efeitos de luz, cálculo de perspectiva, filtros de textura e outros, uma lista de truques que foi evoluindo juntamente com as novas versões do DirectX e do OpenGL.

O grande problema era que os gráficos ficavam limitados e estes truques pré-programados, o que limitava muito o que se podia fazer, resultando em gráficos pouco realistas. Em um exemplo simplista, seria como se você precisasse se comunicar usando um vocabulário de apenas 100 palavras. Para complicar, a lista dos truques era fixa, por isso era necessário desenvolver novas GPUs e atualizar as APIs sempre que novos efeitos eram introduzidos.

Essa especialização era necessária para que as GPUs da época pudessem oferecer um desempenho aceitável, mas não resultava em gráficos muito convincentes, como você pode notar rodando qualquer jogo antigo, anterior a 2005. O uso dos pipelines de renderização e das caixinhas de truques pré-programados continuou até a era DirectX 7 (incluindo jogos como o Medal of Honour e o Call of Duty 1 e GPUs como as GeForce 2):

As coisas começaram a mudar com o DirectX 8.0, que trouxe o suporte a shaders programáveis, permitindo a criação de pequenos programas que adicionam novos efeitos à lista de truques suportados pela GPU. O DirectX 8.0 possuía muitas limitações com relação aos shaders, a começar pelo limite de apenas 128 instruções, que limitava o uso a efeitos simples.

As coisas melhoraram com o DirectX 9.0 (lançado em 2002), que trouxe suporte ao Shader Model 2.0, que incorporou muitas melhorias. Entretanto, a primeira versão definitiva veio em 2004, com o DirectX 9.0c, que trouxe o suporte ao Shader Model 3, uma versão sensivelmente aprimorada, que continua sendo usado pela maioria dos jogos recentes (safra de 2009), resistindo aos avanços do DirectX 10.

Embora as versões do Shader Model sejam um recurso independente, elas são atreladas às versões do DirectX. Ao ler que uma determinada placa suporta o Shader Model 4.1, por exemplo, você pode presumir que se trata de uma placa compatível com o DirectX 10.1 e vice-versa:

DirectX 8.0: Shader Model 1
DirectX 9.0: Shader Model 2
DirectX 9.0c: Shader Model 3
DirectX 10: Shader Model 4
DirectX 10.1: Shader Model 4.1
DirectX 11: Shader Model 5.0

Embora pareçam ser apenas uma superficialidade, os shaders foram os grandes responsáveis pelo aumento na qualidade gráfica dos jogos de alguns anos para cá e se tornaram o recurso mais importante em qualquer GPU, assumindo o posto que anteriormente era do fill-rate.

Até o shader model 3 (DirectX 9.0c), existiam dois tipos de shaders: os vertex shaders e os pixel shaders. Os vertex shaders trabalham na estrutura dos objetos 3D que compõe a imagem, adicionando efeitos e criando animações, como no caso da grama ou dos cabelos, por exemplo.

Os pixel shaders atuam na etapa de renderização da imagem, analisando a estrutura dos objetos, as fontes de luz, cores e outras variáveis e usando estas informações para criar efeitos de luz e sombra, realce de cores, reflexos e outros efeitos bastante realísticos, complementando o trabalho iniciado pelos vertex shaders. Esta é considerada a parte mais importante do trabalho, já que determina a qualidade das imagens que serão finalmente mostradas no monitor e é também a parte que consome mais processamento.

Em placas das geração DirectX 9 (GeForce 7xxx e Radeon X1xxx) eles são processados em unidades independentes da placa, as vertex shader units (ou vertex processors) e as pixel shader units (ou pixels processors), que são especializadas no processamento de cada um dos dois tipos.

O problema com essa arquitetura é que a proporção de vertex shaders e pixel shaders varia de acordo com a situação e também de acordo com o tipo de efeitos usados pela engine do jogo. Isso faz com que sempre ou as unidades de vertex shader ou as de pixel shader sejam subutilizadas, criando um gargalo. Visando reduzir o problema, os fabricantes optam frequentemente por utilizar um número maior de unidades de pixel shader (a GeForce 7800 possui 24 unidades de pixel shader, para apenas 8 unidades de vertex shader, por exemplo), mas essa também estava longe de ser uma solução ideal.

O Shader Model 4 (DirectX 10) introduziu um terceiro tipo de shader, os geometry shaders, destinados à criação de grupos de vértices, que permitem criar objetos usando quase que exclusivamente processamento da GPU, com pouca carga sobre o processador principal. A grande vantagem dos geometry shaders é a possibilidade de criar cenas com um volume muito maior de objetos diferentes, sem que a engine do jogo fique restrita aos recursos do processador principal.

Em vez de um exército de soldados idênticos (como no Rome Total War, por exemplo), você poderia ter um exército onde cada soldado tem uma fisionomias própria. O mesmo poderia ser aplicado a florestas, castelos e assim por diante. Tudo o que o projetista precisaria fazer seria escrever vários geometry shaders diferentes, criando variações dos objetos e fazer com que a placa os executasse em uma ordem específica, em vez de simplesmente repetir a renderização do mesmo objeto, como era feito tradicionalmente:

Este é o tipo de truque que também pode ser feito via software (como no caso do Empire total War, por exemplo), mas nesse caso a carga sobre o processador principal é muito maior, o que faz com que ele se torne um limitante muito antes da GPU.

Em vez de adicionarem um terceiro tipo de unidade dedicada (aumentando ainda mais o índice de ociosidade), tanto a nVidia quanto a ATI (e mais tarde também a Intel) optaram por migrar para arquiteturas unificadas, onde os vertex processors e pixels processors são substituídos por stream processors, que são capazes de executar vertex shaders, pixel shaders ou geometry shaders conforme a demanda.

Basicamente, cada uma destas unidades age como um pequeno processador de cálculos de ponto flutuante independente, que pode ser programado para executar praticamente qualquer tipo de operação. Isso abriu as portas para o uso da GPU como processador auxiliar, convertendo vídeos e executando tarefas diversas, uma possibilidade que pode ser com a ajuda do OpenCL, Brook+ e CUDA.

Embora o uso de unidades de shader unificadas não seja necessariamente um pré-requisito para o suporte ao DirectX 10, acabou coincidindo de todos os fabricantes adotarem a nova arquitetura, de forma que as duas coisas acabaram relacionadas.

Continuando, temos as unidades de processamento de texturas (Texture Mapping Units, ou TMUs) que trabalham em conjunto com as unidades de processamento de shaders, carregando texturas utilizadas na cena. Embora o trabalho das TMUs seja muito mais simples do que o das unidades de pixel shader, elas podem ser um limitante em algumas situações, sobretudo ao ativar o uso do Anisotropic Filtering, que resulta em um grande número de operações relacionadas ao carregamento de texturas.

O desempenho das unidades de texturas também é muito dependente do barramento com a memória, de forma que placas low-end, com chips de memória mais lentos e/ou um barramento mais estreito acabam sendo penalizadas, mesmo que o chipset usado seja o mesmo. Nesses casos a melhor opção é evitar o uso do Anisotropic Filtering e de outros efeitos que utilizem um grande volume de texturas.

É comum também que os fabricantes desenvolvam versões reduzidas de seus chipsets principais, com um volume menor de unidades de processamento, que são destinadas às placas de baixo custo.

O G94, usado na GeForce 9600 GT, por exemplo, possui apenas 32 unidades de processamento de texturas, contra as 64 do G92, a versão “completa”, usada na GeForce 9800 GTX (e outros modelos). Estes chipsets reduzidos são mais baratos de se produzir, o que, combinado com a redução no barramento com a memória (o que significa menos trilhas na placa) e outras economias, permite produzir placas bastante baratas, embora também mais lentas.

Os ROPs (Raster Operation Units) entram em ação no final do processo de renderização. Eles são responsáveis pela aplicação de filtros adicionais, dos algoritmos de antialiasing, cálculo de profundidade (Z-buffer) e outras operações.

Muitos chipsets antigos utilizam arquiteturas balanceadas, onde estão disponíveis o mesmo número de unidades de processamento de texturas (os TMUs), de processamento de pixel shaders e ROPs (um exemplo é o R300, usando na Radeon 9700, que possui 8 unidades de cada tipo), mas os chipsets recentes migraram para o uso de arquiteturas mais flexíveis, de forma a melhor distribuir o uso dos recursos internos.

O número de ROPs disponíveis na placa também não é um direto de performance, pois o volume de processamento executado por cada unidade varia muito de acordo com a arquitetura do chipset. Eles são mais exigidos (em relação aos demais componentes da placa) ao utilizar as opções mais pesadas de antialiasing e ao ativar o uso de mais filtros e efeitos diversos.

No caso de placas com um número menor de ROPs (como as GeForce 9500 GT, que possuem apenas 8 unidades, ou as GeForce 9400 GT, que possuem apenas 4), a melhor forma de evitar que os ROPs tornem-se um gargalo antes de outros componentes é desativar o uso de antialiasing.

Ver Mais

Esta postagem foi modificada pela última vez em 23/03/2011 16:00

Postagem relacionada