SATURN – Cuarta aproximación: 3D, EL TALÓN DE AQUILES

saturn

 

INTRODUCCIÓN AL 3D

Las consolas clásicas estaban diseñadas para trabajar con sprites y tiles en un espacio 2D. Es decir, sobre un plano, representamos un mundo con personajes planos. Estos pueden moverse sobre ese plano, para ello nos basta con matemáticas básicas, el HW de las consolas clásicas basta para ello. Una posición en ese plano se define en una coordenada (x,y).

 

Imagen

Por supuesto, el aspecto podía ser plano o no, pero al final, internamente manejamos 2 coordenadas. Knight Lore de Spectrum, es un ejemplo de pseudo 3D.

Imagen

De ahí a Virtua Racing de Megadrive, hay un recorrido interesante, pero al final estos aparatos de 8 y 16 bits solían trabajar con dos dimensiones. Los sprites del juego pueden aparentar ser 3D pero no lo son, al fin y al cabo son sprites, ¿no?

Al no estar preparadas para 3D real, cuando se trataba de calcular y dibujar en un espacio 3D, o recurrimos a dopajes interesantes (chips externos) o la lentitud es la norma.

Cuando hablamos de 3D, añadimos una coordenada más, la “Z”, la profundidad o distancia hasta la “cámara”. Además dejamos atrás los sprites, ya que un objeto también va a tener profundidad. La representación básica de un punto en el espacio 3D se representa en una coordenada tipo (x,y,z):
Imagen

Para poder trabajar realmente en 3D, tenemos que tener una serie de herramientas que nos permitan hacer una serie de cálculos de forma rápida y precisa. Un objeto puede moverse sobre un plano o sobre otro, rotar de varias formas, acercarse o alejarse a la “cámara”. Es más, los objetos pueden permanecer quietos, y ser la cámara la que se mueva.

Los objetos son tridimensionales, cada vértice se encuentra en un punto distinto.

Además tenemos que representar ese 3D en una pantalla, que es un plano 2D, es necesario cálculos adicionales para crear la proyección de ese mundo 3D en 2D.

Además tenemos que ver qué se ve y qué no se ve, porque unos objetos pueden estar detrás de otros, total o parcialmente, o salirse de la vista de la cámara, total o parcialmente.

Las matemáticas necesarias son bastante más complicadas. Aquí se hace necesario el uso de matrices y cualquiera que haya tenido que hacer cálculos a mano con matrices sabe que es un buen dolor de muelas.

Saturn dispone del HW necesario para hacer operaciones matemáticas a gran velocidad. Tenemos que crear la función adecuada para hacer las multiplicaciones, sumas e ir pasando los datos. Esto podemos hacerlo en los SH-2 o en el SCU, tenemos ambas posibilidades, pero ni el VDP1 ni el VDP2 nos van a ayudar con esto. La geometría es cosa de los procesadores.

QUADS Y TRIÁNGULOS

Todos los objetos 3D que vemos en pantalla están hechos de pequeños objetos geométricos llamados primitivas. Quads y triángulos, son un ejemplo (hay otros),

La industria adoptó los triángulos como estándar por un motivo principal: todo polígono puede ser dividido en triángulos, pero un triángulo no puede ser dividido en otra cosa que no sea triángulos. Al final todo polígono puede ser descompuesto en triángulos. En cambio, con quads no puedes representar todo tipo de polígonos.

Dibujar triángulos es más fácil que dibujar otros polígonos (como los quads), menos lados y menos vértices. Los triángulos tienen propiedades interesantes que les dan ventajas sobre otros polígonos a la hora de dibujarlos y aplicar ciertos efectos sobre su superficie.

Al ser los triángulos la primitiva más simple, los algoritmos que trabajar con ellos pueden ser optimizados de forma más sencilla que si tratáramos con primitivas más complejas.

Trabajar con quads no es tecnología alienígena. La primera tarjeta NVIDIA, la NV1, comercializada como Diamond Edge 3D trabajaba con quads. Por eso SEGA hizo diversos ports de juegos de Saturn para PC para este tipo de tarjetas. No duraron mucho en el mercado y todos los fabricantes pasaron a usar triángulos como base.

AHORA SE VE, AHORA NO SE VE

En una escena 3D, y debido a la profundidad, hay objetos que pueden quedar por detrás de otros, total o parcialmente. Si establecemos un método que nos diga si un objeto no se ve, nos podríamos ahorrar su dibujado. Es lo que se llama Z-buffer

Ni Saturn ni Playstation tienen por HW nada que evite dibujar un objeto no visible.

Así que por defecto pintamos TODO. Pintaremos lo más cercano después, para pintar encima de lo ya pintado (lo lejano). Esta forma de pintar se denomina “algoritmo del pintor”, teniendo el desarrollador que establecer de forma manual lo que está lejos y lo que está cerca.
Imagen

Ni que decir que es un algoritmo muy poco eficiente.

Saturn tiene, al menos, una característica y es que podemos marcar sprites con prioridad para que aparezca por encima de otros en todo momento. Si queremos evitar desperdiciar ciclos de CPU dibujando cosas que no vemos, hay implementar un algoritmo por parte del programador aunque claro, irónicamente, estos cálculos también costarán ciclos.

CÓMO TRABAJA SATURN EN 3D

Vamos a resumir el proceso 3D habitual de Saturn.

La CPU hace los cálculos necesarios (y/o la SCU si el desarrollador los tiene bien puestos) para calcular la geometría de la escena. Calcula entre otras cosas las posiciones de los vértices de cada polígono.

A continuación, dibujamos los quads (VDP1) tomando como posiciones los vértices antes calculados. Si los polígonos son texturados=sprites, para cada uno sampleamos la textura (su sprite) y la adaptamos al polígono (rotación,forma no regular,efectos). Si no son texturados, rellenaremos con el color elegido el interior del polígono. Si tenemos un degradado gouraud, calcularemos los valores y luego pintaremos el polígono.

El resultado queda en el framebuffer. VDP2 crea los fondos necesarios y con la información del framebuffer crea la imagen final.
Sin embargo, utilizar el VDP2 de forma inteligente puede ayudarnos al descargar de trabajo a la CPU y al VDP1 para simular entornos 3D.

Lo más habitual es que el suelo sea una capa abatida del VDP2 que éste rote y mueva. Además una o varias capas pueden ser usada para simular fondos y objetos (como edificios, árboles, rocas), escalando y rotando la capa donde están dichos “objetos”, dando sensación de profundidad.

Esto lo vemos p.e. en juegos de lucha, con capas que simulan suelo, techo y fondo. Sólo los personajes y algún objeto cercano están hechos en 3D.

IGNORANDO AL VPD1

Una de las cosas más curiosas que podemos encontrar es que algunos desarrolladores decidieron ignorar parte del hardware para obtener más rendimiento. Evitaban usar el VDP1. ¿Por qué lo hicieron? ¿Cómo lo hicieron?

Los SH-2 pueden acceder al VDP2 y a su VRAM directamente, sin pasar por el VDP1.
El SCU también puede pasar datos de la RAM principal a la VRAM del VDP2 (por DMA).

Imagen

La gracia está en que si manejamos de forma hábil esta capacidad de escribir en la VRAM del VDP2, podemos conseguir resultados notables. La idea es que los SH-2 “dibujen” los polígonos en la memoria principal, y luego pasar esto directamente a la VRAM del VDP2 (en una de las capas del VDP2, es decir, dibujando=escribiendo en una de sus memorias). Una vez hecho esto, el VDP2 hace su trabajo habitual (generar el suelo p.e.), combina sus capas y, magia, tenemos una imagen por la TV tan válida como si lo hubiese hecho el VDP1.

Al mismo tiempo podemos usar el VDP1 para dibujar algunos de los objetos mientras los SH-2 “dibujan” otros.

Esto es viable, pero como programador tendremos que evitar saturar a los procesadores con “dibujos”, al fin y al cabo tendrán que sobrar ciclos para manejar la IA del juego, enviar instrucciones para ir pidiendo datos del CD, controlar las puntuaciones, etc.

Como ejemplo tenemos a DOOM, que funciona (en parte) de esta forma. Aunque no es un ejemplo brillante, rinde bastante mal.

FALLOS DE DISEÑO

CÓNCAVO Y CONVEXO

Hablando de polígonos planos, Saturn puede dibujar quads cóncavos y convexos. Sin embargo, si hablamos cómo el VDP1 rellena dichos polígonos, los polígonos cóncavos pueden dar problemas, el relleno puede “salirse”. En la siguiente figura tenemos el polígono definido con sus vértices y el relleno en gris:

Imagen

No obstante lo habitual es usar quads convexos, matemáticamente un polígono convexo tiene una serie de propiedades que no tiene uno cónvaco.

RE-RE-RE-RENDERIZANDO

Recordemos esta ilustración:

Imagen

Sin problemas ¿verdad? Saturn recorre horizontalmente la textura (izquierda) y la adapta al polígono (derecha). El problema surge cuando rotamos un polígono, si tiene una forma inclinada, si el tamaño no es el mismo de la textura, si el tamaño de un lado no es el mismo del otro lado:

Imagen

¿Qué hace aquí el VDP1? Va a recorrer la textura de forma horizontal, línea por línea, pero el polígono está inclinado y cada línea tiene un tamaño distinto al anterior, tanto a lo largo como en altura. Por tanto, tal cual, no puedes poner la textura en el polígono, porque te quedarían huecos donde la consola no sabe exactamente qué poner.

El algoritmo interno del VDP1 lo que hace es “interpolar”, es decir, para cada pixel que no tiene info, va a coger un valor medio de los píxels que tiene arriba, abajo, izquierda y derecha. De esta forma no quedan agujeros.

Pero claro, hacer esto implica calcular múltiples veces el valor del color (de la textura) para muchos píxels. Muchos ciclos de trabajo del VDP1 se desperdiciarán.

Un problema similar con este polígono:
Imagen

En la zona del cuadrado azul, vamos a re-escribir una y otra vez el valor de la textura.

El estándar de la industria 3D es utilizar triángulos, podemos representar un triángulo en Saturn haciendo coincidir dos vértices en la misma posición, es una forma “rápida” de hacer funcionar un motor 3D en Saturn:

Imagen

¿Qué ocurre con el renderizado? Si lo hacemos tal cual funciona Saturn, quedará algo raro, todo muy estrecho cerca de los vértices 3 y 4. Para evitarlo los desarrolladores modificaban la textura original para que “quedara bien” al estrecharla. Pero en realidad lo que quiero destacar es que, para una figura aparentemente triangular, vamos a usar la textura en su totalidad, es decir, vamos a recorrerla entera aunque solo nos haga falta la mitad (suponiendo una textura rectangular). Más ciclos perdidos = Motor 3D poco eficiente.

 

KISS

El principio KISS establece que la mayoría de sistemas funcionan mejor si se mantienen simples que si se hacen complejos; por ello, la simplicidad debe ser mantenida como un objetivo clave del diseño, y cualquier complejidad innecesaria debe ser evitada.

“Un sólo procesador central, muy rápido, hubiese sido preferible. No creo que todos los desarrolladores tengan la habilidad para programar dos cpus, la mayoría sólo podrá obtener una vez y media la velocidad de un SH2. Sólo 1 de cada 100 programadores será lo suficientemente bueno como para obtener una potencia de 2 SH2” – Yu Suzuki durante el desarrollo de Virtua Fighter.

SEGA desde luego no aplicó este principio para su consola, demasiados chips, algunos demasiados específicos (VPDs) otros no lo suficiente para alguna tarea determinada (los SH-2 y el 3D).

Como comentado, luego simplificar y unificar chips es esencial para abaratar costes, pocos chips fueron unificados a lo largo de la vida de Saturn (SH-1 con HW del CD por ejemplo).

 

HERRAMIENTAS DE DESARROLLO

Esto no es un fallo HW, pero lo meto en esta sección. Contrariamente a lo que se cree, la diferencia entre la PlayStation y la Saturn no es abultada. Sony se preocupó de dar facilidades a los desarrolladores: herramientas sencillas, basadas en C, fáciles de comprender.

Debido a que el HW de Playstation era más que adecuado para el 3D, obtener buenos resultados era sencillo. Tenemos un procesador de geometría dentro del propio procesador (el GTE), con eso de un plumazo liberamos al procesador de un montón de trabajo, y como tenemos un montón de funciones de serie para el manejo de 3D, es pan comido desarrollar un motor 3D.

SEGA también ofrecía unas librerías para programar en C. Pero los programas resultantes eran de 2 a 5 veces más lentos que los hechos en ensamblador, en la práctica te obligaban a usar ensamblador. Estas herramientas se mejoraron más tarde, por las quejas de los desarrolladores. Para cuando estas llegaron, Sony liberó el acceso en ensamblador para utilizar el GTE, con lo cual podrías sacar más rendimiento al HW si te lo currabas, notándose la diferencia en el desempeño del 3D entre una consola y otra.

 

CUELLOS DE BOTELLA

Como todo HW Saturn tiene sus cuellos de botella, repasemos los más cantosos.

VDP1 es un chip fundamental para el tratamiento de sprites. Mientras más sprites, más efectos, más ampliaciones/rotaciones/etc más faena tiene. ¿Cómo podríamos haber mejorado esto? El VDP1 está conectado a su memoria VRAM, y es con ella con la que interactúa constantemente para luego escribir el resultado en el framebuffer.

Esa RAM es del tipo SDRAM. Este tipo de RAM es muy rápida si hacemos muchas lecturas seguidas, o muchas escrituras. Pero si combinamos ambas, el rendimiento cae en picado.

Por otro lado, los SH-2 son los responsables de generar la geometría en el 3D. Su capacidad es buena, pero tener que lidiar con las colisiones penaliza y mucho el rendimiento. Fue un fallo no darle algo de memoria propia adicional al SH-2 esclavo, los 4KB de caché están muy bien (dentro del contexto de la época), pero algo más hubiese sido mejor.

Tenemos 2 memorias diferentes que conforman la memoria principal, pero ambos están unidos al mismo bus. ¿Por qué no dar un acceso alternativo?

Es decir, si el SH-2 maestro quiere acceder a la “memoria alta” (un chip) y el SH-2 esclavo a la “memoria baja”, darles esa posibilidad implicaría que ambos estarían trabajando a la vez sin molestarse mucho. Esto aumentaría mucho el rendimiento general.

Por supuesto esto haría que los desarrolladores tendieran a utilizar las memorias por separado, pero creo que sería incluso mejor, menos confusión.

En su época, los vídeos pre-renderizados eran la moda. Sony incluyó HW dedicado en la Playstation, de nuevo, una facilidad para el desarrollador.

Por supuesto el “fallo de diseño” más importante es la geometría por quads, cuando todo lo relacionado con el 3D se trataba con triángulos, pero toda esta consola está construida en torno a ello, cambiar esto supone cambiarlo todo.