Creación de una firma raíz

Las firmas raíz son una estructura de datos compleja que contiene estructuras anidadas. Estos se pueden definir mediante programación mediante la definición de estructura de datos siguiente (que incluye métodos para ayudar a inicializar miembros). Como alternativa, se pueden crear en lenguaje de sombreado de alto nivel (HLSL), lo que ofrece la ventaja de que el compilador validará pronto que el diseño es compatible con el sombreador.

La API para crear una firma raíz toma una versión serializada (independiente, libre de punteros) de la descripción del diseño que se describe a continuación. Se proporciona un método para generar esta versión serializada a partir de la estructura de datos de C++, pero otra manera de obtener una definición de firma raíz serializada es recuperarla de un sombreador compilado con una firma raíz.

Si desea aprovechar las optimizaciones de controladores para los descriptores de firma raíz y los datos, consulte Root Signature Version 1.1

Tipos de enlace de tabla descriptor

La enumeración D3D12_DESCRIPTOR_RANGE_TYPE define los tipos de descriptores a los que se puede hacer referencia como parte de una definición de diseño de tabla descriptor.

Es un intervalo para que, por ejemplo, si parte de una tabla de descriptores tiene 100 SRV, ese intervalo se puede declarar en una entrada en lugar de 100. Por lo tanto, una definición de tabla descriptor es una colección de intervalos.

typedef enum D3D12_DESCRIPTOR_RANGE_TYPE
{
  D3D12_DESCRIPTOR_RANGE_TYPE_SRV,
  D3D12_DESCRIPTOR_RANGE_TYPE_UAV,
  D3D12_DESCRIPTOR_RANGE_TYPE_CBV,
  D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER
} D3D12_DESCRIPTOR_RANGE_TYPE;

Intervalo de descriptores

La estructura D3D12_DESCRIPTOR_RANGE define un intervalo de descriptores de un tipo determinado (como SRV) dentro de una tabla de descriptores.

Normalmente D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND , la macro se puede usar para el OffsetInDescriptorsFromTableStart parámetro de D3D12_DESCRIPTOR_RANGE. Esto significa anexar el intervalo de descriptores que se va a definir después del anterior en la tabla descriptor. Si la aplicación quiere usar descriptores de alias o, por algún motivo, quiere omitir las ranuras, puede establecerse OffsetInDescriptorsFromTableStart en cualquier desplazamiento que desee. La definición de intervalos superpuestos de diferentes tipos no es válido.

El conjunto de registros de sombreador especificados por la combinación de RangeType, NumDescriptors, BaseShaderRegister, y RegisterSpace no puede entrar en conflicto ni superponerse entre las declaraciones de una firma raíz que tengan D3D12_SHADER_VISIBILITY comunes (consulte la sección visibilidad del sombreador a continuación).

Diseño de tabla descriptor

La estructura D3D12_ROOT_DESCRIPTOR_TABLE declara el diseño de una tabla de descriptores como una colección de intervalos de descriptores que comienzan en un desplazamiento determinado de un montón de descriptores. No se permiten muestreadores en la misma tabla de descriptores que CBV/UAV/SRV.

Esta estructura se usa cuando el tipo de ranura de firma raíz se establece en D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE.

Para establecer una tabla de descriptores de gráficos (CBV, SRV, UAV, Sampler), use ID3D12GraphicsCommandList::SetGraphicsRootDescriptorTable.

Para establecer una tabla de descriptores de proceso, use ID3D12GraphicsCommandList::SetComputeRootDescriptorTable.

Constantes raíz

La estructura D3D12_ROOT_CONSTANTS declara constantes insertadas en la firma raíz que aparecen en sombreadores como un búfer de constantes.

Esta estructura se usa cuando el tipo de ranura de firma raíz se establece en D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS.

Descriptor raíz

La estructura D3D12_ROOT_DESCRIPTOR declara descriptores (que aparecen en sombreadores) insertados en la firma raíz.

Esta estructura se usa cuando el tipo de ranura de firma raíz se establece D3D12_ROOT_PARAMETER_TYPE_CBVen , D3D12_ROOT_PARAMETER_TYPE_SRV o D3D12_ROOT_PARAMETER_TYPE_UAV.

Visibilidad del sombreador

El miembro de D3D12_SHADER_VISIBILITY enumeración establecida en el parámetro de visibilidad del sombreador de D3D12_ROOT_PARAMETER determina qué sombreadores ven el contenido de una ranura de firma raíz determinada. El proceso siempre usa _ALL (ya que solo hay una fase activa). Los gráficos pueden elegir, pero si usa _ALL, todas las fases del sombreador ven lo que esté enlazado en la ranura de firma raíz.

Un uso de la visibilidad del sombreador es ayudar con los sombreadores que se crean esperando enlaces diferentes por fase de sombreador mediante un espacio de nombres superpuesto. Por ejemplo, un sombreador de vértices puede declarar:

Texture2D foo : register(t0);

y el sombreador de píxeles también pueden declarar:

Texture2D bar : register(t0);

Si la aplicación realiza un enlace de firma raíz a t0 VISIBILITY_ALL, ambos sombreadores ven la misma textura. Si el sombreador define realmente quiere que cada sombreador vea texturas diferentes, puede definir dos ranuras de firma raíz con VISIBILITY_VERTEX y _PIXEL. Independientemente de cuál sea la visibilidad en una ranura de firma raíz, siempre tiene el mismo costo (solo en función de lo que es SlotType) hacia un tamaño de firma raíz máximo fijo.

En el hardware D3D11 de gama baja, también se tiene en cuenta SHADER_VISIBILITY cuando se validan los tamaños de las tablas de descriptores en un diseño raíz, ya que algunos hardware D3D11 solo pueden admitir una cantidad máxima de enlaces por fase. Estas restricciones solo se imponen cuando se ejecutan en hardware de bajo nivel y no limitan hardware más moderno en absoluto.

Si una firma raíz tiene varias tablas de descriptores definidas que se superponen entre sí en el espacio de nombres (los enlaces de registro al sombreador) y cualquiera de ellas especifica _ALL para la visibilidad, el diseño no es válido (se producirá un error en la creación).

Definición de firma raíz

La estructura D3D12_ROOT_SIGNATURE_DESC puede contener tablas de descriptores y constantes insertadas, cada tipo de ranura definido por la estructura de D3D12_ROOT_PARAMETER y la enumeración D3D12_ROOT_PARAMETER_TYPE.

Para iniciar una ranura de firma raíz, consulte los métodos SetComputeRoot*** y SetGraphicsRoot*** de ID3D12GraphicsCommandList.

Los muestreadores estáticos se describen en la firma raíz mediante la estructura D3D12_STATIC_SAMPLER .

Varios marcadores limitan el acceso de determinados sombreadores a la firma raíz, consulte D3D12_ROOT_SIGNATURE_FLAGS.

Serialización o deserialización de la estructura de datos de firma raíz

Los métodos descritos en esta sección se exportan mediante D3D12Core.dll y proporcionan métodos para serializar y deserializar una estructura de datos de firma raíz.

El formulario serializado es lo que se pasa a la API al crear una firma raíz. Si se ha creado un sombreador con una firma raíz en ella (cuando se agrega esa funcionalidad), el sombreador compilado contendrá una firma raíz serializada en ella.

Si una aplicación genera de forma procesal una estructura de datos D3D12_ROOT_SIGNATURE_DESC , debe crear el formulario serializado mediante D3D12SerializeRootSignature. Salida de que se puede pasar a ID3D12Device::CreateRootSignature.

Si una aplicación ya tiene una firma raíz serializada o tiene un sombreador compilado que contiene una firma raíz y desea detectar mediante programación la definición de diseño (conocida como "reflexión"), se puede llamar a D3D12CreateRootSignatureDeserializer . Esto genera una interfaz ID3D12RootSignatureDeserializer , que contiene un método para devolver la estructura de datos D3D12_ROOT_SIGNATURE_DESC deserializada. La interfaz posee la duración de la estructura de datos deserializada.

API de creación de firmas raíz

La API ID3D12Device::CreateRootSignature toma una versión serializada de una firma raíz.

Firma raíz en objetos de estado de canalización

Los métodos para crear el estado de la canalización (ID3D12Device::CreateGraphicsPipelineState y ID3D12Device::CreateComputePipelineState ) toman una interfaz ID3D12RootSignature opcional como parámetro de entrada (almacenado en una estructura de D3D12_GRAPHICS_PIPELINE_STATE_DESC ). Esto invalidará cualquier firma raíz que ya esté en los sombreadores.

Si se pasa una firma raíz a uno de los métodos de estado de la canalización de creación, esta firma raíz se valida con todos los sombreadores del ARCHIVO PARA que sean compatibles y se les da al controlador que se va a usar con todos los sombreadores. Si alguno de los sombreadores tiene una firma raíz diferente, se reemplaza por la firma raíz pasada en la API. Si no se pasa una firma raíz, todos los sombreadores pasados deben tener una firma raíz y deben coincidir; esto se proporcionará al controlador. Si se establece UNA SOLICITUD en una lista de comandos o agrupación, no se cambia la firma raíz. Esto se logra mediante los métodos SetGraphicsRootSignature y SetComputeRootSignature. Al invocarse draw(graphics)/dispatch(compute) de tiempo, la aplicación debe asegurarse de que el ARCHIVO ACTUAL coincide con la firma raíz actual; de lo contrario, el comportamiento no está definido.

Código para definir una firma raíz de la versión 1.1

En el ejemplo siguiente se muestra cómo crear una firma raíz con el siguiente formato:

RootParameterIndex Contenido Valores
[0] Constantes raíz: { b2 } (1 CBV)
[1] Tabla descriptor: { t2-t7, u0-u3 } (6 DMV + 4 UAV)
[2] CBV raíz: { b0 } (1 CBV, datos estáticos)
[3] Tabla descriptor: { s0-s1 } (2 Muestreadores)
[4] Tabla descriptor: { t8 - unbounded } (unbounded # of SRV, volatile descriptores)
[5] Tabla descriptor: { (t0, space1): sin enlazar } (unbounded # of SRV, volatile descriptores)
[6] Tabla descriptor: { b1 } (1 CBV, datos estáticos)

 

Si la mayoría de las partes de la firma raíz se usan la mayor parte del tiempo, puede ser mejor que tener que cambiar la firma raíz con demasiada frecuencia. Las aplicaciones deben ordenar las entradas de la firma raíz de la mayoría de las veces que cambian al menos. Cuando una aplicación cambia los enlaces a cualquier parte de la firma raíz, es posible que el controlador tenga que realizar una copia de parte o todo el estado de firma raíz, lo que puede convertirse en un costo notrivial cuando se multiplica por muchos cambios de estado.

Además, la firma raíz definirá un muestreador estático que realiza el filtrado de textura anisotrópico en el registro s3 del sombreador.

Una vez enlazada esta firma raíz, se pueden asignar tablas descriptores, CBV raíz y constantes al espacio de parámetros [0..6]. Por ejemplo, las tablas descriptores (intervalos en un montón de descriptores) se pueden enlazar en cada uno de los parámetros raíz [1] y [3..6].

CD3DX12_DESCRIPTOR_RANGE1 DescRange[6];

DescRange[0].Init(D3D12_DESCRIPTOR_RANGE_SRV,6,2); // t2-t7
DescRange[1].Init(D3D12_DESCRIPTOR_RANGE_UAV,4,0); // u0-u3
DescRange[2].Init(D3D12_DESCRIPTOR_RANGE_SAMPLER,2,0); // s0-s1
DescRange[3].Init(D3D12_DESCRIPTOR_RANGE_SRV,-1,8, 0,
                  D3D12_DESCRIPTOR_RANGE_FLAG_DESCRIPTORS_VOLATILE); // t8-unbounded
DescRange[4].Init(D3D12_DESCRIPTOR_RANGE_SRV,-1,0,1,
                  D3D12_DESCRIPTOR_RANGE_FLAG_DESCRIPTORS_VOLATILE); 
                                                            // (t0,space1)-unbounded
DescRange[5].Init(D3D12_DESCRIPTOR_RANGE_CBV,1,1,
                  D3D12_DESCRIPTOR_RANGE_FLAG_DATA_STATIC); // b1

CD3DX12_ROOT_PARAMETER1 RP[7];

RP[0].InitAsConstants(3,2); // 3 constants at b2
RP[1].InitAsDescriptorTable(2,&DescRange[0]); // 2 ranges t2-t7 and u0-u3
RP[2].InitAsConstantBufferView(0, 0, 
                               D3D12_ROOT_DESCRIPTOR_FLAG_DATA_STATIC); // b0
RP[3].InitAsDescriptorTable(1,&DescRange[2]); // s0-s1
RP[4].InitAsDescriptorTable(1,&DescRange[3]); // t8-unbounded
RP[5].InitAsDescriptorTable(1,&DescRange[4]); // (t0,space1)-unbounded
RP[6].InitAsDescriptorTable(1,&DescRange[5]); // b1

CD3DX12_STATIC_SAMPLER StaticSamplers[1];
StaticSamplers[0].Init(3, D3D12_FILTER_ANISOTROPIC); // s3
CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC RootSig(7,RP,1,StaticSamplers);
ID3DBlob* pSerializedRootSig;
CheckHR(D3D12SerializeVersionedRootSignature(&RootSig,pSerializedRootSig)); 

ID3D12RootSignature* pRootSignature;
hr = CheckHR(pDevice->CreateRootSignature(
    pSerializedRootSig->GetBufferPointer(),pSerializedRootSig->GetBufferSize(),
    __uuidof(ID3D12RootSignature),
    &pRootSignature));

En el código siguiente se muestra cómo se puede usar la firma raíz anterior en una lista de comandos de gráficos.

InitializeMyDescriptorHeapContentsAheadOfTime(); // for simplicity of the 
                                                 // example
CreatePipelineStatesAhreadOfTime(pRootSignature); // The root signature is passed into 
                                     // shader / pipeline state creation
...

ID3D12DescriptorHeap* pHeaps[2] = {pCommonHeap, pSamplerHeap};
pGraphicsCommandList->SetDescriptorHeaps(2,pHeaps);
pGraphicsCommandList->SetGraphicsRootSignature(pRootSignature);
pGraphicsCommandList->SetGraphicsRootDescriptorTable(
                        6,heapOffsetForMoreData,DescRange[5].NumDescriptors);
pGraphicsCommandList->SetGraphicsRootDescriptorTable(5,heapOffsetForMisc,5000); 
pGraphicsCommandList->SetGraphicsRootDescriptorTable(4,heapOffsetForTerrain,20000);
pGraphicsCommandList->SetGraphicsRootDescriptorTable(
                        3,heapOffsetForSamplers,DescRange[2].NumDescriptors);
pGraphicsCommandList->SetComputeRootConstantBufferView(2,pDynamicCBHeap,&CBVDesc);

MY_PER_DRAW_STUFF stuff;
InitMyPerDrawStuff(&stuff);
pGraphicsCommandList->SetGraphicsRoot32BitConstants(
                        0,RTSlot[0].Constants.Num32BitValues,&stuff,0);

SetMyRTVAndOtherMiscBindings();

for(UINT i = 0; i < numObjects; i++)
{
    pGraphicsCommandList->SetPipelineState(PSO[i]);
    pGraphicsCommandList->SetGraphicsRootDescriptorTable(
                    1,heapOffsetForFooAndBar[i],DescRange[1].NumDescriptors);
    pGraphicsCommandList->SetGraphicsRoot32BitConstant(0,i,drawIDOffset);
    SetMyIndexBuffers(i);
    pGraphicsCommandList->DrawIndexedInstanced(...);
}

Firmas raíz

Especificación de firmas de raíz en HLSL

Uso de una firma raíz