Introducción al enlace de recursos
Las claves para comprender el enlace de recursos en DirectX 12 son los conceptos de descriptores, tablas de descriptores, montones de descriptores y firmas raíz.
Recursos y la canalización de gráficos
Los recursos del sombreador (como texturas, tablas constantes, imágenes, búferes, etc.) no se enlazan directamente a la canalización del sombreador; en su lugar, se hace referencia a ellos a través de un descriptor. Un descriptor es un objeto pequeño que contiene información sobre un recurso.
Los descriptores se agrupan para formar tablas de descriptores. Cada tabla descriptor almacena información sobre un intervalo de tipos de recursos. Hay muchos tipos diferentes de recursos. Los recursos más comunes son:
- Vistas de búfer de constantes (CBV)
- Vistas de acceso desordenadas (UAV)
- Vistas de recursos del sombreador (SRV)
- Muestras
Los descriptores SRV, UAV y CBV se pueden combinar en la misma tabla de descriptores.
Las canalizaciones de proceso y gráficos obtienen acceso a los recursos haciendo referencia a tablas descriptor por índice.
Las tablas descriptores se almacenan en un montón de descriptores. Idealmente, los montones de descriptores contendrán todos los descriptores (en tablas de descriptores) para que se represente uno o varios fotogramas. Todos los recursos se almacenarán en montones en modo de usuario.
Otro concepto es el de una firma raíz. La firma raíz es una convención de enlace, definida por la aplicación, que usan los sombreadores para localizar los recursos a los que necesitan acceso. La firma raíz puede almacenar:
- Indexa las tablas de descriptores en un montón de descriptores, donde se ha definido previamente el diseño de la tabla descriptor.
- Constantes, por lo que las aplicaciones pueden enlazar constantes definidas por el usuario (conocidas como constantes raíz) directamente a sombreadores sin tener que pasar por descriptores y tablas de descriptores.
- Un número muy pequeño de descriptores directamente dentro de la firma raíz, como una vista de búfer constante (CBV) que cambia por dibujo, lo que evita que la aplicación necesite colocar esos descriptores en un montón de descriptores.
En otras palabras, la firma raíz proporciona optimizaciones de rendimiento adecuadas para pequeñas cantidades de datos que cambian por dibujo.
El diseño de Direct3D 12 para el enlace lo separa de otras tareas, como la administración de memoria, la administración de la duración de objetos, el seguimiento de estado y la sincronización de memoria (consulte Diferencias en el modelo de enlace de Direct3D 11). El enlace de Direct3D 12 está diseñado para ser una sobrecarga baja y optimizada para las llamadas API que se realizan con más frecuencia. También es escalable en hardware de gama baja a alta gama y escalable desde versiones anteriores (la canalización de Direct3D 11 más lineal) hasta los enfoques más recientes (más paralelos) para la programación de motores gráficos.
Tipos de recursos y vistas
Los tipos de recursos son los mismos que Direct3D 11, es decir:
- Texture1D y Texture1DArray
- Texture2D y Texture2DArray, Texture2DMS, Texture2DMSArray
- Texture3D
- Búferes (tipados, estructurados y sin procesar)
Las vistas de recursos son similares, pero ligeramente diferentes de Las vistas de búfer de vértices e índices de Direct3D 11 se han agregado.
- Vista de búfer de constantes (CBV)
- Vista de acceso sin ordenar (UAV)
- Vista de recursos del sombreador (SRV)
- Muestras
- Vista de destino de representación (RTV)
- Vista galería de símbolos de profundidad (DSV)
- Vista de búfer de índice (IBV)
- Vista de búfer de vértices (VBV)
- Vista salida de flujo (SOV)
Solo los cuatro primeros de estas vistas son realmente visibles para los sombreadores, consulte Montones de descriptores visibles de sombreador y Montones de descriptores visibles que no son sombreadores.
Flujo de control de enlace de recursos
Centrarse solo en firmas raíz, descriptores raíz, constantes raíz, tablas de descriptores y montones de descriptores, el flujo de lógica de representación de una aplicación debe ser similar al siguiente:
- Cree uno o varios objetos de firma raíz: uno para cada configuración de enlace diferente que necesite una aplicación.
- Cree sombreadores y estado de canalización con los objetos de firma raíz con los que se usarán.
- Cree uno (o, si es necesario, más) montones de descriptores que contendrán todos los descriptores SRV, UAV y CBV para cada fotograma de representación.
- Inicialice los montones de descriptores con descriptores siempre que sea posible para conjuntos de descriptores que se reutilizarán en muchos marcos.
- Para cada fotograma que se va a representar:
- Para cada lista de comandos:
- Establezca la firma raíz actual que se va a usar (y cambie si es necesario durante la representación, que rara vez es necesaria).
- Actualice algunas constantes de firma raíz o descriptores de firma raíz para la nueva vista (como proyecciones de mundo o vista).
- Para cada elemento que se va a dibujar:
- Defina los nuevos descriptores en montones de descriptores según sea necesario para la representación por objeto. En el caso de los montones de descriptores visibles para sombreador, la aplicación debe asegurarse de usar el espacio del montón de descriptores al que aún no se hace referencia mediante la representación, por ejemplo, asignar linealmente espacio a través del montón del descriptor durante la representación.
- Actualice la firma raíz con punteros a las regiones necesarias del montón del descriptor. Por ejemplo, una tabla de descriptores podría apuntar a algunos descriptores estáticos (sin cambiar) inicializados anteriormente, mientras que otra tabla de descriptores podría apuntar a algunos descriptores dinámicos configurados para la representación actual.
- Actualice algunas constantes de firma raíz o descriptores de firma raíz para la representación por elemento.
- Establezca el estado de canalización para que el elemento se dibuje (solo si es necesario cambiar), compatible con la firma raíz enlazada actualmente.
- Dibujar
- Repetir (siguiente elemento)
- Repetir (siguiente lista de comandos)
- Estrictamente cuando la GPU haya terminado con cualquier memoria que ya no se use, se puede liberar. No es necesario eliminar las referencias de descriptores si no se envía ninguna representación adicional que use esos descriptores. Por lo tanto, la representación posterior puede apuntar a otras áreas en montones de descriptores o se pueden sobrescribir descriptores obsoletos con descriptores válidos para reutilizar el espacio del montón de descriptores.
- Para cada lista de comandos:
- Repetir (siguiente fotograma)
Tenga en cuenta que otros tipos de descriptores, vistas de destino de representación (RTV), vistas de galería de símbolos de profundidad (DSV), vistas de búfer de índice (IBV), vistas de búfer de vértices (VBV) y vistas de salida de flujo (SOV) se administran de forma diferente. El controlador controla el control de versiones del conjunto de descriptores enlazados para cada dibujo durante la grabación de la lista de comandos (similar a cómo el hardware o el controlador controlan los enlaces de firma raíz). Esto es diferente del contenido de los montones de descriptores visibles para sombreador, para los que la aplicación debe asignar manualmente a través del montón, ya que hace referencia a distintos descriptores entre draws. El control de versiones del contenido del montón visible para sombreador queda a la aplicación porque permite a las aplicaciones hacer cosas como reutilizar descriptores que no cambian, o usar grandes conjuntos estáticos de descriptores y usar la indexación de sombreador (por ejemplo, por identificador de material) para seleccionar descriptores que se usarán desde el montón de descriptores o usar combinaciones de técnicas para distintos conjuntos de descriptores. El hardware no está equipado para controlar este tipo de flexibilidad para los otros tipos de descriptores (RTV, DSV, IBV, VBV, SOV).
Suballocation
En Direct3D 12, la aplicación tiene un control de bajo nivel sobre la administración de memoria. En versiones anteriores de Direct3D, incluido Direct3D 11, habría una asignación por recurso. En Direct3D 12, la aplicación puede usar la API para asignar un bloque de memoria grande, mayor que cualquier objeto único. Una vez hecho esto, la aplicación puede crear descriptores para que apunten a secciones de ese bloque de memoria grande. Este proceso de decidir qué colocar (bloques más pequeños dentro del bloque grande) se conoce como suballocation. Permitir que la aplicación haga esto puede producir ganancias en el uso eficaz del cálculo y la memoria. Por ejemplo, el cambio de nombre de recursos se representa obsoleto. En lugar de esto, las aplicaciones pueden usar barreras para determinar cuándo se usa un recurso determinado y cuando no es mediante barreras en las ejecuciones de lista de comandos donde la lista de comandos requiere el uso de ese recurso determinado.
Liberar recursos
Antes de que se pueda liberar cualquier memoria enlazada a la canalización, la GPU debe terminar con ella.
Esperar la representación de fotogramas es probablemente la forma más gruesa de estar seguro de que la GPU ha terminado. En un grano más fino, puede volver a usar barreras, cuando un comando se registra en una lista de comandos de la que desea realizar un seguimiento de la finalización, inserte una valla inmediatamente después de ella. A continuación, puede realizar varias operaciones de sincronización con la barrera. Envía un nuevo trabajo (listas de comandos) que espera hasta que se haya pasado una barrera especificada en la GPU, lo que indica que todo antes de que se complete, o puede solicitar que se genere un evento de CPU cuando se haya superado la barrera (que la aplicación puede estar esperando con un subproceso en suspensión). En Direct3D 11, esto fue EnqueueSetEvent
().