Já faz dois anos desde que os rivais apresentaram suas mais recentes ofertas ao mercado. Se você leu o artigo anterior e acabou de começar a ler este aqui, eu presumo que você ainda está esperando 'a coisa' que faz o PS2 ser tão poderoso quanto parecia na época. Agora, deixe-me introduzir um conjunto de componentes muito importantes que a Sony colocou no Emotion Engine, as Unidades de Processamento Vetorial ou 'VPU' (Vector Processing Units).
Arquitetura
Uma Unidade de Processamento Vetorial é um pequeno processador independente projetado para operar vetores, e em particular, vetores com quatro floats. Esses processadores são tão rápidos que eles gastam apenas um ciclo por operação, o que pode ser extremamente conveniente para o processamento de geometria. Apesar disso, eles apresentam um comportamento não padronizado similar à FPU da CPU.
VPUs são feitos dos seguintes componentes:
- Algumas Memórias da Unidade Vetorial ou 'VU Mem' (Vector Unit Memory): São usadas como espaço de trabalho para as unidades vetoriais. Elas guardam valores que precisam ser operados e/ou os resultados de operações anteriores.
- Uma Unidade Vetorial (Vector Unit): O núcleo do processador. Contém um pouco de memória (chamada Micromemória) para guardar um programa (chamado Microprograma) que instrui a unidade sobre como operar os dados encontrados na 'VU Mem'.
- Ela implementa um conjunto de instruções 64 bits e a unidade de execução é dividida em duas subunidades paralelas. A primeira multiplica ou adiciona floats, enquanto a outra divide floats ou opera inteiros. Isso permite operar tanto floats quanto inteiros simultaneamente.
- Uma Interface Vetorial (Vector Interface): automaticamente descomprime dados vetoriais vindos da memória principal em um formato que a Unidade Vetorial consiga entender. Essa unidade também consegue transferir microprogramas para a micromemória.
Funcionalidade
Para começar a funcionar, a Unidade Vetorial precisa de um 'pontapé inicial'. Para isso, a CPU principal fica encarregada de fornecer o microcódigo.
Há duas VPUs dentro do Emotion Engine, mas elas são organizadas de forma diferente, dando lugar a diferentes usos e otimizações.
Unidade de Processamento Vetorial 0
A primeira VPU, a VPU0, está posicionada entre a CPU e a outra unidade vetorial (VPU1). Ela possui um papel 'auxiliar' para a CPU principal.
A VPU0 possui dois modos de operação:
- Micromode: Esse é o 'modo tradicional'. A VPU vai executar de forma independente 'microinstruções' de um microprograma guardado na micromemória.
- Macromode. A VPU0 se torna o 'COP2' da CPU principal e executa 'macroinstruções', recebidas da CPU principal por um barramento dedicado de 128 bits.
- Macroinstruções têm a mesma funcionalidade das microinstruções, porém utilizam opcodes diferentes. No entanto, a unidade de execução da VPU não fica mais dividida (o que significa que ela só pode executar uma instrução por vez).
- Apesar desse modo não utilizar completamente todos os componentes da VPU0, ele ainda acelera as operações vetoriais da CPU. Além disso, em termos de simplicidade, um coprocessador é mais fácil de programar que uma unidade independente (algo que os programadores de PC vão achar útil).
O mapa de memória da VPU0 também tem acesso a alguns registradores e flags da outra VPU, presumivelmente para verificar seu estado ou ler rapidamente os resultados de algumas operações feitas pela outra VPU.
Unidade de Processamento Vetorial 1
A segunda VPU, a VPU1, é uma versão melhorada da VPU0 com o quádruplo de micromemória e memória VU. Além disso, essa unidade inclui um componente adicional chamado Unidade de Função Elementar ou 'EFU' (Elementary Function Unit) que acelera a execução de funções exponenciais e trigonométricas.
A VPU1 está localizada entre a VPU0 e a Interface Gráfica (o 'portão' para a GPU), então ela possui barramentos adicionais que fornecem geometria para a GPU o mais rápido possível e sem utilizar o barramento principal.
No entanto, devido à sua localização, a VPU1 apenas opera em micromode.
É óbvio que essa VPU foi projetada para operações trigonométricas, e ela pode ser usada como um pré-processador para a GPU. Logo, ela é geralmente responsável pelo fornecimento dos famosos Display Lists.
Mundos infinitos
Uma abordagem útil que pode ser explorada com essas unidades é a geração procedural. Em outras palavras, ao invés de construir uma cena utilizando geometria definida por código, deixe que as VPUs a gerem utilizando algoritmos. Neste caso, a VPU calcula funções matemáticas para produzir a geometria que é então interpretada pela GPU (i.e. triângulos, linhas, quadriláteros, etc.) e, finalmente, usada para desenhar a cena.
Comparado com o uso de dados explícitos, o conteúdo procedural é ideal para tarefas paralelizadas, libera largura de banda, e é dinâmico (programadores podem definir parâmetros para obter resultados diferentes) . Muitas áreas podem se beneficiar fortemente desta técnica:
- Superfícies complexas (ex: esferas e rodas).
- Renderização de mundos (ex: terrenos, partículas, árvores).
- Curvas de Bézier (uma equação muito popular na computação gráfica utilizada para desenhar curvas), elas são convertidas em caminhos de Bézier (geometria explícita) e suportam diferentes graus de precisão baseado no nível de detalhe necessário.
Por outro lado, o conteúdo procedural pode ter problemas com animações e, se o algoritmo é muito complexo, a VPU pode não gerar a geometria no tempo exigido.
Em resumo, geração procedural não é uma técnica nova, mas graças às VPUs, essa técnica abre portas para maiores otimizações e gráficos mais sofisticados. Contudo, não é uma técnica simples de implementar e a equipe de P&D da Sony publicou vários artigos descrevendo diferentes abordagens para utilizá-la em seu console.
Você define o fluxo de trabalho
Com essas novas adições, programadores agora possuem muita flexibilidade para desenvolver seus motores gráficos. Para ajudar nesta tarefa, a Sony gastou recursos adicionais para elaborar e documentar modelos eficientes de pipeline. Os exemplos a seguir são pipelines gráficas otimizadas para diferentes tipos de cargas de trabalho :
No primeiro exemplo, o design Paralelo, a CPU é combinada com a VPU0 em macromode para produzir geometria em paralelo com a VPU1. O conjunto CPU/VPU0 faz uso total do scratchpad e da cache para evitar usar o barramento principal, que a VPU1 depende para buscar dados da memória principal. No final, ambos os grupos de renderização simultaneamente enviam seus respectivos Display Lists à GPU.
O segundo exemplo, o design Serial, propõe uma abordagem diferente onde o grupo CPU/VPU0 trabalha como um preprocessador para a VPU1. A primeira etapa irá buscar e processar toda a geometria que a VPU1 vai posteriormente transformar em uma Display List.
Esses foram, até agora, exemplos do ponto de vista teórico, porém para explicar uma implementação mais 'prática', eu vou referenciar um vídeo publicado por Jon Burton que fala sobre o desenvolvimento de um de seus jogos de PS2 .

Crash Bandicoot: The Wrath of Cortex (2001). Partículas formam o fogo da vela e a luz que vem das janelas de vidro.
O antigo diretor da Travellers Tales explicou como seu time obteve um sistema de partículas totalmente encapsulado dentro da VPU1. Em poucas palavras, a VPU1 focou em ler uma base de dados previamente preenchida em sua memória VU, e a base de dados foi usada para calcular as coordenadas de partículas a todo instante sem depender de nenhum outro componente. O resultado da operação poderia ser transformado em Display Lists e enviado imediatamente.
Com essa abordagem, a CPU ficou significativamente descarregada, permitindo-lhe realizar outras tarefas como IA e física.
Há vários outros exemplos por aí, mas resumindo as coisas: cabe agora ao programador encontrar a configuração mais otimizada, e isso é um ponto positivo.



