Ayer durante el estreno de Disruptive News, en el chat un tal Radalov dejo ir un enlace a un paper que me parecio extremadamente interesante. El Paper se llama » Reduced Precision for Hardware Ray Tracing in GPUs». El resumen inicial del paper dice así:

Proponemos un sistema de ray tracing de alto rendimiento integrado en la GPU. Lo presentamos y hacemos un nuevo análisis del recorrido de los rayos en ejes alineados con los BVH. Ee análisis permite el recorrido por hardware utilizando aritmética de precisión reducida. Tambíen proponenemos una nueva técnica de cacha para la planificación del recorrido del rayo. Con el añadido de nuestra compacta unidad de función fija para el recorrido (de los rayos) y el mecanismo de cache, mostramos como las arquitecturas GPU actuales están bien preparadas para la aceleración por hardware del ray tracing, requiriendo solo pequeñas modificaciones para proveer un alto rendimiento. Utilizando los recursos existentes de la GPU omos capaces de mantener todos los rayos y la planificación del tráfico dentro de las caches del chip. Hemos hecho simulaciones para tener una estimación del rendimiento de nuestra arquitectura. Nuestro sistema consigue una ratio de media de 3.4 millone de ray por segundo mientras hacemo path tracing a nuestras escenas.

¿Que significa esto? Pues el Paper habla de una unidad como el RT Core/Tree Traversal Unit que tienen las Nvidia Turing. El Paper es de 2014 por lo que han llovido ya uno cinco años. Es del año 2014 pero el paper es interesante porque cuadra con lo que estamos viendo con el tema del uso de estructuras BVH para acelerar el Raytracing y el motivo por el cual son necesarias las llamadas «Traversal Units» que es como son llamadas en el paper dichas unidades. Pero antes de ir al cambio en el hardware tengamos en cuenta lo que dice el paper sobre los requisitos computaciones de calcular la intersección de los rayos con los objetos, algo que se aplica a cualquier GPU:

No voy a comentar todos los elementos del paper y voy a ir al que realmente nos interesa que es el del hardware:

Mientras que creemos que nuestra arquitectura es adecada para su integración a una (amplia) gama de GPU, será de ayuda para el análisis seleccionar un sistema concreto. Hemos escogido utilizar una hipotética GPU similar a la AMD’s Hawaii (R9-290X) como plataforma de ejemplo para este paper.

No olvidemos que la AMD Hawaii es GCN 1.2… Es decir, estamos hablando de un paper que va sobre la integración de una unidad equivalente al RT Core en un hardware que es casi el mismo que el de las consolas de la actual generación y sabemos que tanto Microsoft como Sony en la siguiente generación continuaran utilizando con tal de mantener la compatibilidad hacia atrás. Tened en cuenta que Hawaii es una arquitectura Pre-Vega (GFX8), pero veamos los cambios que harían en la GPU:

Hawaii esta organizada en 44 Nucleos, cada una de ellas con 4 unidades SIMT-16. Ejecuta instrucciones con un ancho lógico de 64 hilos SIMT y tiene una latencia por ALU de 4 núcleos para la mayoría de instrucciones de precisión simple (FP32). Utilizamos las unidades programables para la intersección rayo-polígono y el shading.

Obviamente los 44 núcleos son las 44 Compute Units:

Hay que tener en cuenta que las Traversal Units tanto en el ejemplo del que estamos hablando como el de Nvidia no realizan el calculo de la intersección realmente sino que lo aceleran a recorrer rapidamente la estructura BVH por el hecho que las GPUs al contrario de las CPUs son extremadamente lentas a la hora de recorrer estructuras de datos en arbol como son los BVH.

Los cambios al igual que ocurre con la unidad SM de Turing están realizados sobre la CU que es su equivalente de AMD y supone por si misma un nucleo completo. El problema es que por influencia del marketing llamamos a las unidades FP32 como núcleos cuando realmente son ALUs.

El archivo de registros (RF) esta compuesta por un gran número de memorias indexables, independientes y de alto ancho de banda. Tomada en sutotal, el archivo de registros (RF) permite 176 accesos direrentes por núcleo (4 segmentos por núcleo) y provee un ancho de banda pico de 33 TB/s. Lo utilizamos para almacenar los rayos en cola y pasar los datos entre las etapas del pipeline.

No, no tiene nada de especial ni es un añadido raro, hace referencia a lo que en el diagrama de la Compute Unit es llamado «Vector Registers» que son los registros al que las ALUs/Unidades FP32 acceden para realizar sus calculos. Hay 176 accesos porque tenemos 44 CUs y 4 unidades SIMT16 por núcleo y cada unidad SIMD accede a su archivo de registros correspondiente. No hay ningún cambio en esta parte del hardware.

Cada núcleo contiene 64KB de un scratpad local (LDS) compartido por las unidades SIMT. Este Scrachpad contiene unos 32 bancos y tiene capacidad atómica remota en cada banco, y puede cargar o almacenar 4 bytes en cada banco por cada ciclo de reloj. En nuestro sistema el LDS proveee la cola y los punteros de cabeza para la sección local del núcleo del archivo de registro y filtra los mensajes de selección del treelet (QF). También lo utilizamos para almacenar los datos de los poligonos durante la intersección y el shading.

Los Treelets no son otra cosa que las ramas subindexadas del BVH que recordemos que se ordena en forma de arbol.

Hemos de tener en cuenta que Hawaii al ser una arquitectura Pre-Vega no rasteriza por tiles y no ordena la geometria antes de la rasterización, estamos hablando de la integración del Ray-Tracing en una GCN Pre-Vega por lo que el mecanismo de ordenar y almacenar los datos de la geometría será diferente que en las GPUs Vega y Post-Vega. Como ya comente en la entrada anterior de este tema, en el caso de Turing al tener el rasterizado por tiles lo que acabamos obteniendo es la información de la geometría ordenada por cada tile de manera automatizada a través del rasterizador. En el caso del paper con Hawaii no nos dicen como se consigue pero supongo que será a través de un algoritmo via Compute Shaders antes de la rasterización dado que Hawaii no ordena los elementos de la escena antes del rasterizado sino después del texturizado perdiendo por completo la información geométrica.

Esto le da una explicación a la integración del Tile Shading/DSBR… Si tanto AMD como Nvidia sabían de la necesidad de calcular los «treelet» o subsecciones del arbol que es el BVH entonces tiene sentido el cambio a una arquitectura middle sort con tal de tener la geometria ordenada antes de la rasterización dado que es importante de cara al raytracing.

Todos los núcleos en Hawaii comparten un scratchpad global, el GDS, que también es de 64KB y tiene 32 bancos. El GDS contiene una más sofistica unidad lógica remota que la LDS, la cual se extiende para proveer una selección global del treelet (TS).

La GDS se encuentra fuera de las Compute Units, fuera de los Compute Engines (grupos de CUs) e incluso fuera de los Shader Engines. Se encuentra entre las unidades de planificación global y los Shader Engines y es la memoria de comunicación entre ambas partes y es utilizada por los Shader Engines con tal de tomar la información de las listas de cosas a hacer creada por el procesador de comandos para gráficos y computación.

En el caso que nos ocupa hemos de tener en cuenta que la estructura BVH en conjunto hace referencia a la geometria global de la escena. El GDS lo que almacena no es toda la información del BVH sino que tenemos que ver esto como una serie de arboles binarios (treelets) dentro de otro arbol binario como es el BVH por lo que en el GDS lo que se almacenaría son los nodos en referencia a los diferentes treelets/subnodos.

La imagen no corresponde al paper del que estoy hablando y se que para muchos esto les va a sonar algo complicado, pero hemos de tener en cuenta que en el BVH pese a que al contrario del Octree y el KD-Tree no dividimos el espacio de manera regular a través del espacio si que lo hacemos a través de la geometria que lo contiene en forma de arbol también. La idea no es tener un arbol general sino un arbol general (BVH) compuesto por una serie de arboles más pequeños y asignados cada uno a una Compute Unit (Treelets). En el caso de las GPUs Vega y Post-Vega en el caso de AMD tenemos ya la división de la geometria de la escena por tiles y a cada tile le corresponde un treelet. ¿significa esto que el concepto del paper no es posible? No, sirve para la comunicación entre treelets, por ejemplo para cuando un rayo de luz va más allá de su area de intersección.

Cada núcleo tiene unos 16KB de Cache de datos L1. La cual puede servir hasta 4 cargas de 16 bytes por ciclo de reloj, con nuestro formato de BVH comprimido cada cache L1 puede proveer hasta 4 nodos por ciclo de reloj a las traversal units en el núcleo.

En la entrada de hace unos días comente como la Cache L1 y el equivalente en la LDS de Nvidia en el salto de Pascal a Turing se han unificado en una sola memoria.

El motivo de ello tiene sentido si tenemos en cuenta que la LDS esta por debajo en la jerarquia respecto a la cache L1 y al ser una memoria scratchpad necesita un acceso explicito a la misma. En el Paper que nos ocupa dichas memorias siguen estándo separadas como ocurre en la arquitectura GCN actualmente. Pero tenemos ya de entrada que la Traversal Unit del paper al igual que la TTU/RT Core esta conectada a la cache L1.

En las Turing de Nvidia como se puede ver en este diagrama la TTU/RT Core esta conectada a la cache L1. En realidad es el único sitio al que puede estar conectado con tal de poder operar.

También tenemos 1MB de Cache L2 que esta compartido por todos los núcleos. En nuestro sistema, ambos controladores de cache están modificados para añadir un tipo de carga hit-only.

Y aquí empezamos ya con lo cambios a nivel de hardware, la cache L1 y la cache L2 están modificadas en este sentido, no voy a entrar en detalle sobre este tipo de modificación.

En todos los núcleos hay un kernel dispatch complex (el lanzador). Hemos extendido dicho complejo para que incluya la habilidad de hacer una cola de rayos para atravesar lo rayos, la intersección y el shading.

Es decir, un cambio en el planificador de la Compute Unit, la idea es que generamos cada nuevo rayo a través de un shader que los invoca ya sean primarios o secundarios. Pues bien, esto entra dentro del pipeline de cada CU y por tanto dentro del planificador:

Cuando se traza un rayo lo primero que se hace es comprobar teniendo en cuenta los datos que tenemos del BVH sobre la situación de la geometria de la escena es si impacta o no y a partir de ahí se realiza el cálculo correspondiente.

En nuestro sistema introducimos una register transpose unit (RT) a cada archivo de registros de cada núcleo. Nuestras Traversal Units almacenan un solo rayo en un solo registro SIMD colocando diferentes componentes en cada nodo. Esto permite que el rayo al completo sea recuperado en solo acceso a lo registros. La transpose unit permite una conversión eficiente de los rayos a la organización de la unidad SIMD, lo cual es necesario antes de que el software (shader) pueda operar con ellos.

Es decir, pese a que a la organización general de la Compute Unit sigue siendo la misma, con tal de dar soporte al Raytracing a tiempo real en una GPU de arquitectura GCN por hardware tenemos que hacer cambios importantes en los registros, la cache L1 y la cache L2 y añadir en el caso de lo registros un nuevo tipo de unidad con tal de permitir que las unidades SIMT/SIMD puedan realizar el cálculo de la intersección. Este es un nuevo tipo de unidad de función fija que no existía en la Compute Unit con anterioridad y por lo visto se trata de una pieza esencial.

Finalmente añadimos 4 Traversal Units a cada núcleo. Cada unidad esta compañada por un pequeño arechivo de registro con la capacidad de mantener los rayos suficientes con tal de reducir las latencias por un cache mis. Las 4 unidades en el núcleo comparten una pequeña cache.

Las Traversal Units son las encargadas de recorrer la unidad BVH… Al final la re-organización queda de la siguiente manera incluyendo los cambios:

Las partes marcadas en verde son las que se añadirían de nuevo, las Traversal Units están conectadas a la unidad LS (Load/Store) de la Compute Unit que es el mismo tipo de unidad de conecta la Cache L1 con las unidades de texturas. Por lo que nos encontramos con la misma organización que en Turing pero con una implementación algo distinta, en todo caso esto va a ser común en todas las GPUs cuya organización general es la misma en el fondo…

El algoritmo de función fija es descrito dentro del mismo paper para que cualquiera lo pueda implementar en su hardware.

Personalmente no me extrañaria que el RT Core/TTU de Nvidia se basará también en este paper y que por ello la implementación propuesta por Nvidia que ya se encuentra en el mercado no difiera mucho de la de AMD en terminos generales y viceversa.

Con esta plataforma de ejemplo tenemos un total de 176.000 millones de traversal steps por segundo. Teniendo en cuenta que todas las instrucciones vectoriales de la GPU consumen ranuras de emisión de la FPU, las traversal units proveen el equivalente a 4.4 TFLOPS de rendimiento adicional, pero ocupando solo el 4-8% del area ocupada por las actuales ALUs.

Tened en cuenta que Hawaii tiene una potencia de 5.9 TFLOPS en total sin tener en cuenta estos cambios, pero lo que nos interesa es la información implicita. Sabemos que cada «step» realizado por la Traversal Unit son 25 FLOPS, pero también sabemos que la cantidad de steps por rayo son unos 40 steps. ¿Como?

El Ray Tracing a tiempo real requiere un rendimiento sustancial en coma flotante. En esto efectos muchos rayos se deben invocar por pixel con tal de conseguir un aceptable nivel de ruido bajo. Con resolucione de pantalla por encima de los 2MPixeles y 50-100 rayos/pixel necesarios para un bajo ruido, podemos esperar unos 150 millones de rayos por fotograma. A unos 30hz y 40 pasos/rayo esto tiene una demanda en coma flotante impresionante, requiriendo casi 4.5 TFLOPS de potencia por software (en este caso sería via shaders) solo para la intersección de los rayos.

El propio documento en un punto nos lo aclara que ese es el ratio y por tanto hablamos de un total de 1000 FLOPS por rayo, lo cual coincide enormemente con lo reflejado en las diapositivas de Turing respecto al RT Core respecto a la potencia shader necesaria si no estuviesen las Traversal Units.

Esto coloca en perspectiva por completo la potencia necesaria para el Raytracing, recordad que hay diferentes tipos de rayos según el tipo de efecto que se quiera conseguir…

Es decir, se va a utilizar un tipo de rayo para los reflejos, otro para las sombras suaves, otro para la oclusión ambiental… Cada uno de estos rayos va a necesitar la increible de 1000 FLOPS por muestra si no existen las Traversal Units. La cantidad de muestras por efecto y los tipos de efecto se solventen por computación, pipeline gráfico convencional o a través del Raytracing dependerá de la elección del desarrollador de cada juego por lo que no podemos hablar de que el tema de los 50-100 rayos por pixeles no es una constante sino que va a depende directamente del juego, el paper solamente toma un valor constante con tal de poner en contexto la necesidad de la Traversal Unit.

Por otro lado, ha de quedar claro que los unicos cambios necesarios son solo en las Compute Units y en la Global Data Share, el resto de elementos de GPU no requieren de cambia alguno.

En fin, esto es todo, como siempre tenéis el Discord y los comentarios de la entrada para comentar.