A História do OpenGL vs DirectX

Enquanto escrevia um texto sobre gamedev, me peguei traduzindo a história do OpenGL e do DirectX, que segue abaixo para fins de arquivamento devido ao trabalho que deu.

O original (em inglês) pode ser encontrado aqui.

O Início do Conflito

Um dia, no início da década de 90, a Microsoft olhou ao seu redor. Eles viram o SNES e o Sega Genesis sendo incríveis, possuindo bastantes jogos de ação e tudo mais. E então eles viram o DOS. Desenvolvedores codavam para o DOS assim como faziam para jogos de console: diretamente no metal. Só que, diferente de videogames comuns, no entanto, onde um desenvolvedor que faz um jogo de SNES sabe exatamente as especificações de seu hardware, os desenvolvedores para o DOS tinham que escrever código para múltiplas configurações. E isso é mais difícil do que parece.

E a Microsoft tinha um problema ainda maior: Windows. Entenda, o Windows queria possuir o hardware, diferente do DOS que deixava os desenvolvedores fazerem o que quisessem. Possuir o hardware era necessário para que houvesse cooperação entre aplicações. Cooperação é exatamente o que desenvolvedores de jogos odeiam porque isso toma recursos de hardware que eles poderiam estar usando para serem incríveis.

De forma a promover o desenvolvimento de jogos no Windows, a Microsoft precisava de uma API uniforme que fosse de baixo nível, rodasse no Windows sem ser retardada por ele e, mais importante, multi-hardware. Uma única API para todos os hardwares gráficos, sonoros e de entrada.

Assim, o DirectX nasceu.

Os Aceleradores 3D nasceram alguns meses depois. E Microsoft teve mais um problema. DirectDraw, o componente gráfico do DirectX, apenas lidava com gráficos 2D: alocando memória gráfica e fazendo movimentos de bits entre diferentes seções alocadas da memória.

Então a Microsoft comprou um pouco de middleware e o maquiou como sendo o Direct3D versão 3. Isso foi universalmente injuriante, e por uma boa razão: olhar para o código do D3D v3 era como encarar a Arca da Aliança.

O John Carmack, da Id Software (atualmente na Oculus) deu uma olhada nesse lixo e disse “Foda-se!” e decidiu escrever outra API: OpenGL.

Outra parte do monstro de muitas cabeças era também que a Microsoft esteve trabalhando com a SGI em uma implementação OpenGL para o Windows. A ideia aqui era cortejar desenvolvedores de aplicações GL comuns: workstations. Ferramentas CAD, modelagens, esses tipos de coisas. Jogos eram a coisa mais distante na cabeça da Microsoft. Isso era primariamente uma coisa do Windows NT, mas a Microsoft decidiu adicionar ao Windows 95 também.

De forma a aliciar os desenvolvedores de workstations para Windows, a Microsoft tentou suborná-los com acesso à estas placas gráficas 3D novinhas. A Microsoft, então, implementou o protocolo ICD (Installable Client Driver): uma fabricante de placa gráfica poderia substituir a implementação OpenGL via software da Microsoft por uma baseada em hardware. O código poderia usar uma implementação de hardware do OpenGL se esta estivesse disponível.

Nos primeiros dias, as placas de vídeo a nível de consumidor não possuiam suporte para o OpenGL. Isso não impediu que Carmack portasse Quake para OpenGL (GLQuake) em sua workstation SGI. Do leia-me do GLQuake:

Teoricamente, glquake irá rodar em qualquer implementação opengl que suporte extensões de objetos texturais, mas a não ser que seja um hardware bem potente que acelere todo o necessário, o jogo não ficará aceitável. Se ele tem que usar qualquer caminho de emulação de software, a performance ficará bem abaixo de um frame por segundo.
Agora (março de 97), o único hardware com implementação opengl que pode rodar glquake razoavelmente é uma integraph realizm, que é uma placa MUITO cara. A 3dlabs esteve melhorando sua performance significantemente, mas, com os drivers disponívels, ainda não é bom o suficiente para jogar. Parte dos drivers atuais da 3dlabs para glint e as placas permedia podem também derrubar o NT quando saindo de fullscreen, então não recomendo rodar glquake em hardware da 3dlabs.
A 3dfx fez uma opengl32.dll que implementa tudo que o glquake necessita, mas não é uma implementação total do opengl. É improvável que outras aplicações opengl funcionem bem com isso, então considere basicamente um “driver do glquake”.

Esse era o nascimento dos drivers miniGL. Eles evoluíram para implementações OpenGL completas eventualmente, quando o hardware ficou poderoso o suficiente para implementar a maior parte das funcionalidades do OpenGL em hardware. A nVidia foi a primeira a oferecer a implementação total da OpenGL. Muitos outros fabricantes resistiam, o que é um dos motivos pelos quais os desenvolvedores preferiam Direct3D: eles eram compatíveis em um largo acervo de hardware. Eventualmente, apenas a nVidia e a ATI (agora AMD) restaram e ambas tinham uma boa implementação OpenGL.

A Ascenção do OpenGL

Logo, a batalha estava feita: Direct3D vs OpenGL. É uma história bem interessante, considerando o quão ruim D3D v3 era.

O Comitê Revisor da Arquitetura OpenGL (do inglês, ARB – Architectural Review Board) é uma organização responsável por manter o OpenGL. Eles emitem um número de extensões, mantem o repositório destas e criam novas versões da API. O ARB é um comitê feito por muitas empresas da indústrica gráfica, assim como várias desenvolvedoras de sistemas operacionais. Microsoft e Apple, por várias vezes, já foram membros do ARB.

A 3Dfx veio com voodoo2. Este foi o primeiro hardware que poderia fazer multitexturamento, o que é algo que o OpenGL não podia fazer antes. Enquanto a 3Dfx era fortemente contra o OpenGL, a nVidia, desenvolvedora do próximo chip gráfico de multitexturamento (TNT1) o amava. Então o ARB gerou uma nova extensão: GL_ARB_multitextura que permitia acesso ao multitexturamento.

Enquanto isso, o Direct3D v5 era lançado. Agora, o D3D tinha de fato virado uma API ao invés de algo que um gato vomitou. O problema? Sem multitexturamento.

Opa.

Agora, isso não machuchou nem tanto quanto deveria porque pessoas não usavam tanto multitexturamento. Não diretamente. Multitexturamento atingia a performance um pouco e, em muitos casos, não valia a pena comparado à multi-passagem. E, claro, desenvolvedores de jogos adoram ter certeza que seus jogos funcionam bem em hardwares antigos, que não tinham multitexturamento, então muitos jogos foram feitos sem ele.

O D3D, assim, recebeu uma pequeno alívio.

O tempo passa e a nVidia lança a GeForce 256 (não a GeForce GT-250; a primeira GeForce de fato), encerrando a competição em placas gráficas pelos próximos dois anos. O maior ponto de venda era a capacidade de realizar Vertex Transform e Lightning (T&L) direto no hardware. Não apenas isso, a nVidia amava tanto o OpenGL de forma que o motor T&L deles era de fato OpenGL. Quase literalmente; como entendo, parte de seus registradores na verdade tomavam os enumeradores OpenGL diretamente como valores.

O Direct3D v6 é lançado. Pelo menos tinha multitexturas, mas… sem T&L no hardware. OpenGL sempre teve um fluxo (pipeline) T&L, mesmo que antes da 256 isso tenha sido implementado em software. Então foi bem fácil para a nVidia converter a implementação em software para uma solução em hardware. Somente na versão 7 que o D3D finalmente teria suporte à T&L no hardware.

Alvorada dos Shaders, Crepúsculo do OpenGL

Então, a GeForece 3 foi lançada. E muitas coisas aconteceram ao mesmo tempo.

A Microsoft tinha decidido que eles não se atrasariam novamente. Então ao invés de esperar pelo que a nVidia estava fazendo e copiar depois, eles tomaram a posição de ir até eles e conversar. E então se apaixonaram e tiveram um console juntos.

Um divórcio bagunçado foi realizado posteriormente, mas isso é outra história.

O que isso significou para o PC foi que a GeForce 3 veio simultaneamente com D3D v8. E não é difícil ver como a GeForce 3 influenciou os shaders do D3D 8. Os pixels shaders do Shader Model 1.0 eram extremamente específicos ao hardware da nVidia. Não havia qualquer tentativa de pelo menos tentar abstrair o hardware da nVidia; o SM 1.0 era apenas o que a GeForce 3 fazia.

Quando a ATI entrou na corrida de performance nas placas gráficas com a Radeon 8500, havia um problema. O fluxo de processamento da 8500 era mais poderoso que o da NVIDIA. Então a Microsoft emitiu o Shader Model 1.1, que era basicamente “o que a 8500 fizer”.

Isso pode soar como uma falha da parte do D3D. Mas falhas e sucessos são questão de degraus. E falhas épicas estavam acontecendo na terra do OpenGL.

A nVidia amava o OpenGL, então quando o GeForce 3 foi lançada, eles lançaram extensões OpenGL. Extensões proprietárias OpenGL: apenas da NVIDIA. Naturalmente, quando a 8500 apareceu, não pôde usar nenhuma delas.

Né, então pelo menos na terra do D3D 8, você podia pelo menos rodar seus shaders da SM 1.0 em hardware da ATI. Claro, você teria que escrever novos shaders para tomar vantagem da 8500, mas pelo menos seu código funcionava.

De forma a ter shaders de qualquer time no OpenGL da Radeon 8500, a ATI tinha que escrever um número de extensões OpenGL. Extensões proprietárias OpenGL: apenas da ATI. Então você necessitava um código da nVidia e um código da ATI apenas para ter shaders.

Agora você pode perguntar: “Onde estava o Comitê do OpenGL, cuja função era manter o OpenGL atual?”. Onde muitos comitês costumam acabar: sendo idiotas.

Entenda, mencionei o ARB_multitexture acima porque ele pesa em tudo isso. O ARB parecia (de uma perspectiva de alguém de fora) que queria evitar a ideia de shaders. Eles perceberam que se jogassem configurabilidade suficiente em um fluxo de função-fixa, eles poderiam igualar a habilidade de um fluxo de shader.

Então o ARB lançava extensão após extensão. Cada extensão com as palavras “texture_env” era mais uma maneira de corrigir este design que envelhecia. Veja o registro: entre ARB e extensões EXT, haviam oito dessas feitas. Muitas foram promovidas à versões principais do OpenGL.

A Microsoft era parte do ARB nesta época; eles saíram perto de quando o D3D 9 foi lançado. Então é inteiramente possível que eles estivessem trabalhando para sabotar o OpenGL de alguma forma. Eu pessoalmente duvido dessa teoria por dois motivos. Um, eles teriam que ter ajuda dos outros membros para fazer isso, já que cada membro tem apenas um voto. E, mais importante, dois, o ARB não necessitava da ajuda da Microsoft para estragar tudo. Veremos isso mais a frente.

Eventualmente o comitê, provavelmente sob ameaças da ATI e da nVidia (ambos membros ativos) eventualmente perdeu o juízo o suficiente para criar shaders ao estilo assembly.

Quer algo mais estúpido?

T&L no hardware. Algo que o OpenGL tinha primeiro. Bem, é interessante. Para ter a maior performance possível de T&L no hardware, você precisa armazenar seus dados de vertex na GPU. Afinal, é a GPU que quer usar essa informação.

No D3D v7, Microsoft introduziu o conceito de Vertex Buffers. Eles alocavam trilhos de memória de GPU para armazenar dados de vertex.

Quer saber quando OpenGL conseguiu algo equivalente a isso? Ah, a nVidia, sendo uma amante de todas as coisas do OpenGL (enquanto fossem extensões proprietárias da NVIDIA) lançou a extensão de Vertex Array Range quando a GeForce 256 foi lançada. Mas quando o ARB decidiu dar funcionalidade similar?

Dois anos depois. Isso foi após eles terem aprovado vertex shaders e fragment shaders (pixel, no idioma D3D). Esse foi o tempo que o ARB levou para desenvolver uma solução multi-plataforma para armazenar dados de vertex na memória da GPU. Novamente, algo que T&L de hardware necessita para atingir performance máxima.

Um Idioma para Arruiná-los

Então, o ambiente de desenvolvimento do OpenGL esteve fraturado por um tempo. Sem shaders multi-hardware, sem armazenamento de vertex em GPUs multi-hardware, enquanto os usuários D3D aproveitavam de ambos. Poderia ficar pior?

Você… pode dizer que sim. Conheça a 3D Labs.

Quem são eles, você pergunta? Eles são uma empresa falida que considero ser os verdadeiros assassinos do OpenGL. Claro, o inepticidade do ARB fez OpenGL vulnerável quando ele deveria estar dominando o D3D. Mas a 3D Labs é provavelmente o maior motivo para mim do estado atual do OpenGL no mercado. O que eles fizeram de fato para causar isso?

Criaram a OpenGL Shading Language.

A 3D Labs era uma empresa que estava morrendo. Suas caras GPUs estavam sendo marginalizadas pela pressão crescente da nVidia no mercado de workstations. E diferente da nVidia, a 3d Labs não tinha presença no mercado principal de consumo; se a nVidia vencesse, eles morriam.

E foi isso que aconteceu.

Então, em uma aposta para continuar relevante em um mundo que não queria seus produtos, a 3D Labs apareceu em uma certa Game Developer Conference (GDC) com apresentações do que eles chamavam de “OpenGL 2.0”. Isso seria uma reescrita completa, do zero, da API do OpenGL. E isso fazia sentido: havia bastante sujeira na API naquela época (nota: essa sujeira ainda existe). Apenas repare como carregamentos de texturas e amarramento (binding) funcionam; é semi-arcano.

Parte da proposta deles era uma shading language. Naturalmente. No entanto, diferente das extensões multi-plataformas atuais do ARB, a shading language deles era “alto-nível” (C é alto nível para uma shading language. Sim, verdade).

Agora a Microsoft estava trabalhando em sua própria shading language de alto nível. onde eles, em toda a imaginação coletiva da Microsoft, chamavam de… High Level Shading Language (HLSL). Mas a abordagem deles era fundamentalmente diferente.

O maior problema com a shader language da 3D Labs era que ela era embarcada. Entenda, a HLSL era uma linguagem que a Microsoft definiu. Eles lançavam um compilador para ela e ela gerava o código assembly do Shader Model 2.0 (ou posteriores Shader Models) que você alimentaria o D3D. Nos dias do D3D v9, a HLSL nunca era tocada diretamente pelo D3D. Era uma ótima abstração, mas puramente opcional. E um desenvolvedor sempre tinha a oportunidade de ir atrás do compilar e polir a saída para máxima performance.

A linguagem da 3D Labs não tinha nada disso. Você dava ao driver uma linguagem parecida com C e ela produzia um shader. Fim da história. Não um shader assembly, não algo que você alimentava outra coisa. O objeto OpenGL representava um shader.

O que isso significava era que os usuários OpenGL estavam vulneráveis às vagarosidades de desenvolvedores que ainda estavam aprendendo a compilar linguagens do nível de assembly. Bugs de compilação corriam soltos pela recém-batizada OpenGL Shading Language (GLSL). O que é pior, se você conseguisse que um shader compilasse em múltiplas plataformas corretamente (não trivial), você ainda estava sujeito aos optimizadores do dia. Que não eram tão ótimos quanto podiam ser.

Enquanto esta era a maior falha da GLSL, não era a única. De longe.

No D3D, e em outras velhas linguagens assembly no OpenGL, você poderia misturar e combinar vertex e fragment (pixel) shaders. Enquanto eles comunicassem com a mesma interface, você poderia usar qualquer vertex shader com qualquer fragment shader compatível. E ainda haviam níveis de incompatibilidade que eles poderiam aceitar; um vertex shader poderia escrever uma saída que um fragment shader não lesse. E assim em diante.

A GLSL não tinha nada disso. Vertex shaders e fragment shaders eram unidos no que a 3D Labs chamava de “objeto de programa”. Então se você quisesse compartilhar os programas dos vertex e do fragment, você tinha que construir múltiplos objetos de programas. E isso causava o segundo problema.

A 3D Labs pensava que estava sendo inteligente. Eles basearam o modelo de compilação do GLSL em C/C++. Você pega um arquivo .c ou .cpp e compila em um arquivo de objeto. Então você pega um ou mais arquivos de objetos e os linka a um programa. Então é assim que o GLSL compila: você compila seu shader (vertex ou fragment) em um objeto de shader. Então coloca esses shaders em um objeto de programa e os linka para criar seu programa.

Enquanto isso permitia ideias legais em potencial que continham código extra que os shaders principais podiam chamar, o que isso significava na prática era que os shaders compilavam duas vezes. Uma no estágio de compilação e outra no estágio de linking. O compilador da nVidia em particular era conhecido por basicamente rodar a compilação duplamente. Ele não gerava qualquer tipo de objeto intermediário, apenas compilava uma vez e jogava fora a resposta, então compilava de novo no tempo de linkagem.

Então mesmo que você quisesse linkar seu vertex shader à dois diferentes fragment shaders, você tinha que fazer um monte de compilação a mais que no D3D. Especialmente porque a compilação de uma linguagem similar à C era totalmente feita offline, não ao início da execução do programa.

Haviam outros problemas com a GLSL. Talvez pareceria errado jogar toda a culpa na 3D Labs, já que o ARB eventualmente aprovou a ideia e incorporou a linguagem (mas nada da iniciativa “OpenGL 2.0” deles). Mas foi ideia deles.

E aqui está a parte realmente triste: a 3D Labs estava correta (na maior parte). GLSL não é uma linguagem de shaders baseada em vetores da mesma forma que HLSL era na época. Isso porque o hardware da 3D Labs era escalar (similar aos hardwares modernos da NVIDIA), mas eles estavam ultimamente certos na direção que muitos fabricantes de hardware seguiram.

Eles estavam corretos em ir com um modelo de compilação online para uma linguagem de “alto nível”. O D3D até mudou para isso eventualmente.

O problema era que a 3D Labs estava correta na hora errada. E tentando conjurar o futuro muito cedo, em tentando ser à prova do futuro, eles deixaram de lado o presente. Soa similar de como OpenGL sempre teve a possibilidade para a funcionalidade de T&L. Exceto que o fluxo do T&L do OpenGL ainda era útil antes do T&L de hardware, enquanto a GLSL era um fardo antes que o mundo o entendesse.

A GLSL é uma boa linguagem agora. Mas para a época? Era horrível. E o OpenGL sofreu por isso.

Caindo em Torno de uma Apoteose

Enquanto mantenho que a 3D Labs deu o golpe fatal, foi o próprio ARB que botaria a última unha no caixão.

Esta é uma história que você já deve ter ouvido. Próximo ao OpenGL 2.1, o OpenGL estava com um problema. Havia muito código legado. A API não era mais fácil de usar. Haviam 5 maneiras de se fazer as coisas e nenhuma ideia de qual era a mais rápida. Você podia “aprender” OpenGL com tutoriais simples, mas não aprendia realmente a API que te daria performance e poderes reais.

Então o ARB decidiu tentar outra reinvenção do OpenGL. Isso era similar ao “OpenGL 2.0” da 3D Labs, mas melhor porque o ARB estava por trás. Eles chamavam de “Longs Peak”.

O que há de ruim por gastar um tempo para melhorar a API? Era ruim porque Microsoft se deixou vulnerável. Olha, este era o momento que o Windows Vista estava lançando.

Com o Vista, a Microsoft decidiu instituir algumas das mudanças muito necessárias nos displays gráficos. Eles forçaram os drivers a se submeterem ao sistema operacional para virtualização de memória gráfica e várias outras coisas.

Enquanto é possível debater os méritos disso ou se isso era possível, o fato é que: a Microsoft instituiu que o D3D 10 seria apenas do Vista (e acima). Mesmo que você tivesse hardware capaz de rodar o D3D 10, você não poderia rodar aplicações D3D 10 sem também rodar o Vista.

Você também pode lembrar que o Vista… é, bem, digamos apenas que não deu muito certo. Então você tinha um sistema operacional que tinha um subdesempenho, uma nova API que rodava apenas naquele SO e uma nova geração de hardware que necessitava daquela API e SO para fazer qualquer coisa que ser mais rápido que a geração anterior.

No entanto, desenvolvedores podiam acessar recursos do nível do D3D 10 através do OpenGL. Bem, eles poderiam se o ARB não estivesse ocupado trabalhando no Longs Peak.

Basicamente, o ARB passou um bom ano e meio a dois anos trabalhando para fazer a API melhor. Na hora que o OpenGL 3.0 apareceu, a adoção do Vista já havia encerrado e o Windows 7 já aparecia para colocar o Vista atrás deles, e a maioria dos desenvolvedores de jogos não se importavam com esses recursos do nível D3D 10 de qualquer forma. Afinal, o hardware do D3D 10 rodava aplicações D3D 9 tranquilamente. E com o aparecimento de ports PC-para-console (ou desenvolvedores de PC mudando de barco para desenvolvimento de consoles. Escolha), desenvolvedores não necessitavam desses recursos do D3D 10.

Agora, se os desenvolvedores tivessem acesso à estes recursos mais cedo pelo OpenGL em máquinas Windows XP, então o desenvolvimento do OpenGL teria recebido um muito-necessário empurrão. Mas o ARB perdeu sua chance. E quer saber a pior parte?

Apesar de ter gasto dois preciosos anos tentando reescrever a API do zero… eles ainda falharam e apenas reverteram para o status quo (exceto por um mecanismo de depreciação).

Então não apenas o ARB perdeu uma janela crucial de oportunidade, eles sequer terminaram a tarefa que os fizeram perder essa chance. Uma falha épica em todos os sentidos.

E este é o conto entre do OpenGL vs Direct3D. Um conto de oportunidades perdidas, estupidez bruta, cegueira proposital e simples tolices.

Publicado por

Daniel Araújo

Redator-chefe do próprio blog. Escreve bem sobre absolutamente nada, tem opinião sobre absolutamente tudo. Ninguém se importa mesmo assim.

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *