NX Opción B (II)

Lo prometido es deuda, entramos en la segunda parte donde hablaremos de las diferentes opciones de GPU que puede incluir Nintendo en NX, pero antes de entrar en estas es importante tener en cuenta una serie de conceptos básicos, así que y perdonad por ello, los diferentes participantes quedan completamente al margen hasta la tercera entrada para no convertir la entrada en demasiado larga.

#1 La GPU y sus hilos de ejecución

Una GPU no funciona igual que una CPU, procesa decenas, centenares e incluso miles de hilos de ejecución al mismo tiempo y estos hilos tienen un vida muy corta, de unos pocos ciclos de reloj y se ejecutan sobre una primitiva gráfica (vertice, triangulo o pixel) o sobre un dato genérico (computación), cada uno de estos programas es considerado un “kernel” pero no es lo mismo que el kernel/núcleo de un SO.

Digamos que a cada stream processor de la consola le toca encargarse de un “kernel”, siendo el encargado de enviar los diferentes kernels a los diferentes grupos de stream processors de la GPU la unidad de planificación de la misma, si el planificador no tiene suficiente potencia como para hacer su trabajo entonces una parte de los stream processors estarán parados y no sacaremos provecho de ello. Obviamente si la carga de la lista de comandos de la GPU es la que es entonces no se puede hacer nada más, si la carga es excesiva entonces hace falta potenciar el planificador… ¿Poniendo uno más potente? No, poniendo varios, lo que permite la ejecución “multihilo” en la GPU, aunque ese termino es confuso desde el momento en que una GPU ya trabaja con múltiples hilos simultáneos, por lo que es mejor el termino “multicontexto”.

¿Pero cuantos hilos de ejecución podemos necesitar para la ejecución de los juegos de NX? Para ello voy a tomar a Wii U como referencia y voy a plantear NX como una evolución de Wii U en primer lugar, no en cuanto a arquitectura sino en cuanto a la complejidad gráfica de lo juegos.

WiiU

Acerca de la GPU de Wii U:

  • Trabaja con 192 hilos de ejecución.
  • Tiene una GPU del tipo OpenGL 3/DX10 y por tanto no soporta los llamados Compute Shaders y trabaja con un solo contexto en vez de varios.
  • De estos 192 hilos procesa unos 160 por tener esa cifra de Stream Processors.

¿Por qué tiene más hilos que unidades de ejecución? Simple, para que no falten datos e instrucciones para que los Stream Processors no se paren.

El caso es que si tomamos Wii U como una consola 720P30 y no olvidamos que el número de hilos de ejecución escalán directamente tanto hacía arriba como hacía abajo con la resolución de salida del sistema. Si por ejemplo nuestro interés es un sistema con la misma capacidad visual pero a 1080p60, tendremos que tener una GPU capaz de trabajar con 864 hilos de ejecución, la cosa también funcionaría a la inversa para las resoluciones más bajas.

#2 GPU y Stream Processors

Los planificadores envían los “kernels” a los Stream Processors por grupos, trabajando cada grupo con una serie de primitivas concretas del mismo tipo. El caso es que se aplica la misma operación sobre multiples datos que son del mismo tipo, es decir, si tenemos que operar una textura lo normal es que en cada pixel se aplique la misma instrucción.

En una CPU básica la configuración habitual es el procesamiento de un dato por cada instrucción, se trata de una ejecución escalar y por tanto solo hace falta una ALU para la ejecución del programa. A esto se le llama ejecución escalar.

CPUALU

Una GPU en cambio aplica la misma operación sobre multiples datos que son del mismo tipo, es decir, si tenemos que operar una textura lo normal es que en cada pixel se aplique la misma instrucción. Para ello necesitamos la capacidad de poder aplicar la misma instrucción a diversos datos y para ello necesitaremos diversas ALUs pero el proceso de captación y envió de instrucciones será unificado.

Supongamos por un momento que queremos operar con 8 componentes por instrucción, entonces el esquema de dicha ALU pasaría a ser el siguiente:

ALUGPU1

Los Stream Processors están agrupados en grupos cuyo número dependera de la arquitectura del fabricante y la configuración de estos grupos esta directamente relacionada con la forma en la que funcionan los planificadores de cada arquitectura, es decir, con la cantidad de grupos de kernels que los planificadores de la GPU pueden enviar a los stream processors, los cuales por otro lado simplemente se dedican a esperar y ejecutan lo que les va llegando.

¿Y que hay de la computación y de los contextos múltiples?Aquí es donde tenemos que entrar en otra parte, pensad que de momento estamos hablando de una GPU genérica.

#3 Compute Shaders

Una vez explicado los conceptos básicos, toca hablar de los Compute Shaders. ¿Como sabe una GPU que los datos a procesar son de un tipo concreto? En realidad no lo sabe a no ser que se lo marquemos nosotros, solo proceso una serie de ceros y unos, por lo que en realidad podemos colocar cualquier tipo de dato en una GPU y lo ejecutará.

¿Entonces que sentido tiene diferenciar los tipos de “Shader”? Básicamente porque según el tipo de datos de entrada en una GPU el camino a seguir de estos es diferente y por lo tanto lo es el resultado, es decir, un vertice no es tratado de la misma manera que una textura, de la misma manera que un dato de computación general tampoco. Esto se ve muy bien en el diagrama del OpenGL 4.3 donde se ven los diferentes pasos según el origen de los datos.

OGLCS

Es decir, cuando utilizamos la GPU en modo “computación” el camino de datos que sigue es completamente distinto, es como si en una fábrica un tipo de producto para su fabricación siguiese un camino completamente distinto. Es decir, los Compute Shaders acceden directamente a las unidades de ejecución sin tener que pasar por todo el proceso gráfico y por tanto estos nos permiten utilizar las unidades de ejecución de la GPU para otras tareas que no sean las gráficas. Es decir, hablamos de datos que no son vertices, ni triangulos, ni pixeles y de ahi que su camino de datos es distinto y se acceda con ellos directamente a los shaders.

PlatformModel

Normalmente, los Compute Shaders se suelen utilizar en el contexto de la GPU como co-procesador de la CPU para operaciones que trabajan con muchos datos por instrucción y por tanto se ejecutan mejor en una GPU que en una CPU. En estas situación la GPU trabaja de forma coherente en el mismo espacio de memoria que la CPU y por tanto necesita acceder a dicho espacio. En la entrada anterior os explique los diferentes tipos de uncore en relación a esto, parece ser que en los dispositivos basados en ARM para ahorrarse los males de cabeza se ha acabado por hacer que no haya un espacio de memoria coherente por un lado y no-coherente por otro, sino que todo el espacio de memoria es coherente.

¿Aplicaciones posibles de los Compute Shaders?

  • Apoyo para el procesamiento de físicas, detección de colisiones en el juego.
  • Efectos de post-procesado, es decir, manipular la imagen final ya generada que se encuentra en el búfer de imagen frontal.

Cabe destacar que en el caso de las consolas de Nintendo desde GameCube a Wii U, la CPU tiene acceso al búfer de imagen, obviamente esto en Wii U es una inutilidad porque la CPU no tiene potencia suficiente, pero Nintendo puede utilizar los Compute Shaders y la GPU en modo “proposito general” y como co-procesador para los efectos de post-procesado y ampliar esa función de su API en la siguiente consola.

A nivel de APIs, los Compute Shaders pasaron a llamarse así a partir del Shader Model 5.0 que apareció en DX11 y OpenGL 4.3, pero antes ya existían APIs de computación como CUDA, OpenCL y DirectCompute, la diferencia es que antes de DX11/OGL 4.3 las GPUs solo soportaban un solo contexto y por tanto un cambio de contexto no era posible sin recortar ampliamente el rendimiento.

¿La forma de dar soporte a más de un contexto? Añadir planificadores adicionales en la GPU. ¿Pero que ocurre si añadimos los Compute Shaders? Pues que el número de hilos e ejecución aumenta. ¿A que viene hablar de ello? Pues porque Nintendo ya insinuo en un Iwata Pregunta que estaban interesados en este aspecto y llevan tiempo investigando en ello.

Iwata: Obtenéis la información más confidencial de Nintendo a tiempo.

Alex: Exactamente. Los desarrolladores de la sede central de Nintendo tienen que invertir su tiempo desarrollando la propia plataforma, por lo que nos gustaría explorar aquellas áreas para las que no tienen tiempo. Por ejemplo las posibilidades que se abren debido a la combinación de tecnologías de nube29 y nuevos paradigmas como la programación de GPU para un uso general30.Creo que ahora mismo nos encontramos en el lugar idóneo para crear las mejores nuevas ideas, pero tenemos que ser muy audaces y ambiciosos para ello. Por eso me alegro tanto de estar en NERD y no en ningún otro lugar.

Por lo tanto recapitulemos, teniendo en cuenta el nivel visual de Wii U a 720P30 necesitamos unos 192 “Kernels” entonces aplicando los Compute Shaders/GPGPU acabaremos necesitando (192+n) Kernels, donde n es el número de “kernels” que irían dedicados a los Compute Shaders.

#4 Warps  y Wavefronts

¿Que es un “Warp”? Es simple y llanamente un grupo de 32 “Kernels”, de ahi a que todas las GPUs de Nvidia tengan sus unidades de ejecución organizadas a través del número 32, esto se ve en la unidad de ejecución de la arquitectura Maxwell, el llamado SMM.

SMM

Tenemos cuatro grupos de 32 ALUs cada uno (llamadas Core en el diagrama), que son en concreto esto:

NvidiaCore

Lo cual no es más que esto:

ALUGPU1

Pero en versión Nvidia donde los planificadores envían los “kernels” en grupos de 32, de ahí las 32 ALUs, esta unidad es el equivalente a la Compute Unit de la arquitectura GCN de AMD cuya configuración es la siguiente:

Compute Unit

Pero aquí tenemos 64 Stream Processors, el motivo de ello es que los “kernels” son enviados en paquetes de 64 a los que AMD llama Wavefronts, los cuales son lo mismo que los Warp de AMD solo que de tamaño diferente. En realidad dependiendo del tipo de fabricante el tamaño del Wavefront/Warp varia, pero lo que me interesa es que tengáis el concepto claro más que nada y de forma genérica.

¿Pero cuantos ciclos se pasa un Wavefront haciendo su trabajo por cada paquete de “kernels” que se le envia? El escenario ideal es que lo hicieran en un ciclo, pero como mínimo tardan unos 4 ciclos de reloj… ¿el motivo? Los planificadores solo puede enviar un paquete de “kernels” por ciclo de reloj, obviamente no todos los paquetes de instrucciones serán igual de rápidos, no es lo mismo una simple suma que una operación trigonométrica por ejemplo, cada tipo de instrucción requiere una potencia de procesamiento distinto. Por otro lado si el número bloques de unidades de shaders es muy grande es cuando se empiezan a necesitar un mayor número de planificadores en la GPU para poder llegar a los que un solo planificador no puede, esto lleva a que se puedan ejecutar múltiples contextos dentro de una GPU, lo cual permite la combinación de gráficos+computación en vez de tener que escoger gráficos o computación en una GPU que es lo que ocurría antes. Es decir, antes de la llegada de OGL 4.3 y D3D11 teníamos un solo planificador en las GPUs que es como si tuviésemos un departamento de logística en una fábrica con una sola persona trabajando, cuando se colocaron dos planificadores/personas entonces se hizo el salto y realmente de lo que va la generación PS4+Xbox One en estos momentos a nivel técnico es de eso, del poder usar gráficos y computación simultaneamente.

En fin, volviendo a nuestro sistema antes he comentado que añadiendo los Compute Shaders a lo que es Wii U entonces para renderizar a 720P30 pasaríamos a necesitar unos (192+n “kernels”/hilos), pues esa n precisamente depende del tamaño del paquete de kernels, llamese warp o wavefronts, que soporte la arquitectura, obviamente el tamaño de n puede ser más grande pero ese sería el tamaño mínimo, tened en cuenta que esto no es lo que vamos a ver en NX sino el mínimo para soportar Compute Shaders y ejecutar los juegos a un nivel visual de Wii U a 1080p60.

  • Nvidia: 4.5*(192+32)= 1008 hilos de ejecución.
  • Qualcomm Adreno = 4,5*(192+64)= 1152 hilos de ejecución.
  • Imagination PowerVR: 4,5* (192+48)= 1080 hilos de ejecución.

Obviamente esta cifra no corresponde al número de Stream Processors ya que para evitar que esto se paren la lista de “kernels” ha de ser más grande que el número de Stream Processors. Ahora bien… ¿es posible alcanzar esas cifras con cada arquitectura? Aquí es donde ya entramos en cada una de las diferentes arquitecturas, pero esto ya lo comentaré más adelante.

#5 La API

El 100% de las GPU que van en conjunto de una CPU con conjunto de instrucciones ARM van a tener que ser compatibles con OpenGL ES. Ahora bien, si tomamos como referencia lo que son los gráficos de Wii U y escalamos desde allí entonces tenemos que tener una API del nivel de OpenGL 3.3, los motivos de ello los comente en su día.

La versión que cumple con estos requisitos es a simple vista es el OpenGL ES 3.0 aunque no del todo, los motivos son los siguientes:

  • Añade el soporte de Multiple Render Targets, los cuales son esenciales para el rendering en diferido, estos han sido explotados enormemente en los motores gráficos juegos del nivel Wii U/Xbox 360/PS3. Los MRT son para que la gente lo entienda, la capacidad de poder trabajar con múltiples búfers de imagen en vez de uno solo ya que la GPU habitualmente trabaja con uno solo.
  • OpenGL ES 3.0 no soporta Geometry Shaders, esto es porque no soporta el set completo de OGL 3.3.
  • Tampoco soporta contextos múltiples ni tampoco Compute Shaders.

¿Entonces cual es la salida? Pues el ES 3.1, que por lo que su nombre indica es una evolución menor que el 3.0 y como su diagrama indica se equipara al nivel del OpenGL 4.3 en cuanto a funcionalidad.

khronos-opengl-es-gdc-2014-7-638

El motivo por el cual es una actualización menor es porque en realidad para dar soporte a los Compute Shaders los cambios que se han de hacer en una GPU es en los planificadores y que el acceso a memoria sea coherente. De la coherencia a la memoria se encarga el uncore del SoC, siendo la parte de los planificadores la única que necesitan cambiar solamente respecto a sus GPUs OGL ES 3.0, de ahí que la gran mayoría para dar el salto apenas hayan hecho cambios en sus arquitecturas.

#6 ROPS y Memoria

Los ROPS son las unidades encargadas de escribir en el búfer de imagen trasero, siendo el búfer delantero la imagen ya creada léida por el DAC de pantalla para ser enviada al televisor. Tienen una exigencia enorme sobre la memoria ya que del ancho de banda de la memoria depende la tasa de dibujado por lo que tiene que existir la capacidad de escribir en memoria igual a la tasa de relleno*información por pixel.

Este problema en una GPU de sobremesa donde se pueden colocar multiples chips de memoria en la placa no es problema, pero en un sistema embebido donde tenemos poco espacio hay que buscar otras soluciones para paliar la falta del ancho de banda siendo la que se utiliza en el caso de los dispositivos de bolsillo el renderizado por Tiles, es decir, en vez de tener un búfer trasero que tenga un tamaño muy grande lo que se hace es renderizar la escena por partes que se van resolviendo, esto permite tener un mini-búfer trasero dentro del chip que ocupa poco espacio en vez de tener uno completo.

tiling

La otra solución es la de utilizar una GPU convencional, estas requieren no solo de un buen ancho de banda sino que requieren poder almacenar el búfer de imagen dentro de la memoria y para ello se suelen utilizar tres soluciones distintas:

  • Un pozo de memoria externa aparte para la GPU que otorgue a esta el ancho de banda suficiente.
  • Memoria embebida dentro del chip.
  • Memoria externa lo suficientemente rápida.

El problema de la memoria embebida dentro del chip aparece cuando aparecen los MRT ya que entonces el tamaño del búfer trasero que es donde trabaja la GPU aumenta por el número de MRTs utilizados. En el caso de Wii U la consola tiene soporte de hasta 8 MRTs (6+Z+Cache de texturas), la información por pixel es de 4 bytes y la resolución a 720P por lo que son necesarios 32MB que es el tamaño de la eDRAM/MEM1 de la consola, no olvidemos lo que dije en la anterior entrada sobre la cache L3. En este caso podemos tener hasta 32MB de memoria, lo que es ideal para una consola con una resolución 720P, pero no lo es para resoluciones más altas si se quiere trabajar con ellas.

Y es aquí donde entramos en la necesidad de tener ancho de banda y densidad en la memoria, las opciones disponibles ya las he comentado en otras entradas, pero sobretodo no podemos olvidar que si hacemos caso a la patente que apareció hace dos días el sistema de memoria ha de estar en el mismo encapsulado que el SoC del sistema y las opciones existentes.

Por otro lado no podemos olvidar que el número de ROPS es lo que va a marcar la configuración de la memoria y que en cada arquitectura hay un equilibrio entre el número de ROPS y el número de Shaders. Sabemos de antemano cual es el número de Shaders/Stream Processors necesarios para unos gráficos de nivel de calidad Wii U, esto nos ayudará a escoger el modelo de GPU necesario.

En la tercera entrega os comentare las diferentes opciones que existen sobre la mesa en cuanto a procesadores gráficos.

Anuncios