Portar do Direct3D 11 para o Direct3D 12
Esta seção fornece algumas diretrizes sobre a portabilidade de um mecanismo gráfico personalizado do Direct3D 11 para o Direct3D 12.
- Criação de dispositivos
- Recursos comprometidos
- Recursos reservados
- Upload de dados
- Sombreadores e objetos de sombreador
- Enviando trabalho para a GPU
- Sincronização de CPU/GPU
- Associação de Recursos
- Estado do recurso
- Cadeias de troca
- Renderização de função fixa
- Probabilidades e extremidades
- Tópicos relacionados
Criação de dispositivos
O Direct3D 11 e o Direct3D 12 compartilham um padrão de criação de dispositivo semelhante. Os drivers existentes do Direct3D 12 são todos D3D_FEATURE_LEVEL_11_0 ou melhores, portanto, você pode ignorar os níveis de recursos mais antigos e as limitações associadas.
Lembre-se também de que, com o Direct3D 12, você deve enumerar explicitamente as informações do dispositivo usando interfaces DXGI. No Direct3D 11, você pode encadear de volta para o dispositivo DXGI do dispositivo Direct3D e isso não tem suporte para o Direct3D 12.
A criação de um dispositivo de software WARP no Direct3D 12 é feita fornecendo um adaptador explícito obtido de IDXGIFactory4::EnumWarpAdapter. O dispositivo WARP para Direct3D 12 está disponível apenas em sistemas com o recurso opcional Ferramentas Gráficas habilitado.
Observação
Não há equivalente a D3D11CreateDeviceAndSwapChain. Mesmo com o Direct3D 11, desencorajamos o uso dessa função, pois geralmente é melhor criar o dispositivo e a cadeia de troca em etapas distintas.
Recursos comprometidos
Os objetos criados com as seguintes interfaces no Direct3D 11 são convertidos no que são chamados de "recursos confirmados" no Direct3D 12. Um recurso confirmado é um recurso que tem espaço de endereço virtual e páginas físicas associadas a ele. Esse é um conceito do Modelo de Memória WDD2 (Driver de Dispositivo 2) do Microsoft Windows, no qual o Direct3D 12 se baseia.
Recursos do Direct3D 11:
- ID3D11Recurso
- ID3D11Buffer e ID3D11Device::CreateBuffer
- ID3D11Texture1D e ID3D11Device:CreateTexture1D
- ID3D11Texture2D e ID3D11Device::CreateTexture2D
- ID3D11Texture3D e ID3D11Device::CreateTexture3D
No Direct3D 12, todos eles são representados por ID3D12Resource e ID3D12Device::CreateCommittedResource.
Recursos reservados
Recursos reservados são recursos em que apenas o espaço de endereço virtual foi alocado, a memória física não é alocada até que haja uma chamada para ID3D12Device::CreateHeap. Esse é essencialmente o mesmo conceito que os recursos em blocos no Direct3D 11.
Os sinalizadores (D3D11_RESOURCE_MISC_FLAG) usados no Direct3D 11 para configurar recursos em blocos e, em seguida, mapeá-los para a memória física.
- D3D11_RESOURCE_MISC_TILED
- D3D11_RESOURCE_MISC_TILE_POOL
Upload de dados
No Direct3D 11, há a aparência de uma única linha do tempo (chamadas após uma sequência, como dados inicializados com D3D11_SUBRESOURCE_DATA, em seguida, uma chamada é feita para ID3D11DeviceContext::UpdateSubresource e, em seguida, uma chamada para ID3D11DeviceContext::Map). O número de cópias criadas dos dados não é óbvio para um desenvolvedor do Direct3D 11.
No Direct3D 12, há duas linhas do tempo, a linha do tempo da GPU (configurada por chamadas para CopyTextureRegion e CopyBufferRegion da memória mapeável) e a linha do tempo da CPU (determinada por chamadas para Map). As funções auxiliares são fornecidas (no arquivo d3dx12.h) chamadas Updatesubresources que usam uma linha do tempo compartilhada. Há várias variações dessa função auxiliar, uma que usa ID3D12Device::GetCopyableFootprints, outra que usa um mecanismo de alocação de heap e outra que usa um mecanismo de alocação de pilha. Essas funções auxiliares copiam recursos para a GPU e a CPU, por meio de uma área de preparo intermediária da memória.
Normalmente, a GPU e a CPU têm sua própria cópia de um recurso vinculada à sua própria linha do tempo. A abordagem de linha do tempo compartilhada também mantém duas cópias.
Sombreadores e objetos de sombreador
No Direct3D 11, há muita criação de objetos de sombreador e estado e a definição do estado desses objetos, usando os métodos de criação ID3D11Device e os métodos de conjunto ID3D11DeviceContext. Normalmente, um grande número de chamadas é feito para esses métodos, que são combinados em tempo de desenho pelo driver para definir o estado correto do pipeline.
No Direct3D 12, essa configuração do estado do pipeline foi combinada em um único objeto (CreateComputePipelineState para um mecanismo de computação e CreateGraphicsPipelineState para um mecanismo gráfico), que é anexado a uma lista de comandos antes da chamada de desenho com uma chamada para SetPipelineState.
Essas chamadas substituem todas as chamadas individuais para definir sombreadores, layout de entrada, estado de combinação, estado do rasterizador, estado do estêncil de profundidade e assim por diante, no Direct3D 11
- Métodos do dispositivo 11:
CreateInputLayout
,CreateXShader
,CreateDepthStencilState
eCreateRasterizerState
. - Métodos do Contexto do Dispositivo 11:
IASetInputLayout
,xxSetShader
,OMSetBlendState
OMSetDepthStencilState
, eRSSetState
.
Embora o Direct3D 12 possa dar suporte a blobs de sombreador compilados mais antigos, os sombreadores devem ser criados usando o Modelo de Sombreador 5.1 com as APIs FXC/D3DCompile ou usando o Modelo de Sombreador 6 usando o compilador DXIL DXC. Você deve validar o suporte ao Shader Model 6 com CheckFeatureSupport e D3D12_FEATURE_SHADER_MODEL.
Enviando trabalho para a GPU
no Direct3D 11, há pouco controle sobre como o trabalho é enviado, ele é amplamente tratado pelo driver, embora algum controle seja habilitado por meio das chamadas ID3D11DeviceContext::Flush e IDXGISwapChain1::P resent1.
No Direct3D 12, o envio de trabalho é muito explícito e controlado pelo aplicativo. A construção principal para enviar o trabalho é o ID3D12GraphicsCommandList, que é usado para registrar todos os comandos de aplicativos (e é bastante semelhante em conceito ao contexto adiado ID3D11). O repositório de backup para uma lista de comandos é fornecido pelo ID3D12CommandAllocator, que permite que o aplicativo gerencie a utilização de memória da lista de comandos expondo a memória que o driver Direct3D 12 usará para armazenar a lista de comandos.
Por fim, o ID3D12CommandQueue é uma fila de primeiro a entrar, primeiro a sair, que armazena a ordem correta das listas de comandos para envio à GPU. Somente quando uma lista de comandos tiver concluído a execução na GPU, a próxima lista de comandos da fila será enviada pelo driver.
No Direct3D 11, não há nenhum conceito explícito de uma fila de comandos. Na configuração comum do Direct3D 12, a lista de comandos D3D12_COMMAND_LIST_TYPE_DIRECT aberta no momento para o quadro atual pode ser considerada análoga ao contexto imediato do Direct3D 11. Isso fornece muitas das mesmas funções.
D3D11DeviceContext | ID3D12GraphicsLista de comandos |
---|---|
ClearDepthStencilView | ClearDepthStencilView |
ClearRenderTargetView | ClearRenderTargetView |
ClearUnorderedAccess* | ClearUnorderedAccess* |
Desenhar, DrawInstanced | DrawInstanced |
DrawIndexed, DrawIndexedInstanced | DrawIndexedInstanced |
Expedir | Expedir |
IASetInputLayout, xxSetShader, etc. | SetPipelineState |
OMSetBlendState | OMSetBlendFactor |
OMSetDepthStencilState | OMSetStencilRef |
OMSetRenderTargets | OMSetRenderTargets |
RSSetViewports | RSSetViewports |
RSSetScissorRects | RSSetScissorRects |
IASetPrimitiveTopology | IASetPrimitiveTopology |
IASetVertexBuffers | IASetVertexBuffers |
IASetIndexBuffer | IASetIndexBuffer |
ResolveSubresource | ResolveSubresource |
CopySubresourceRegion | CopyBufferRegion |
UpdateSubresource | CopyTextureRegion |
CopyResource | CopyResource |
Observação
Uma lista de comandos criada com D3D12_COMMAND_LIST_TYPE_BUNDLE é semelhante a um contexto adiado. O Direct3D 12 também dá suporte à capacidade de acessar alguns recursos de um contexto imediato simultâneo à renderização por meio de D3D12_COMMAND_LIST_TYPE_COPY e D3D12_COMMAND_LIST_TYPE_COMPUTE tipos de lista de comandos.
Sincronização de CPU/GPU
No Direct3D 11, a sincronização de CPU/GPU era amplamente automática e não havia necessidade de o aplicativo manter o status da memória física.
no Direct3D 12, o aplicativo deve gerenciar as duas linhas do tempo (CPU e GPU) explicitamente. Isso requer que as informações sejam mantidas, pelo aplicativo, sobre quais recursos são exigidos pela GPU e por quanto tempo. Isso também significa que o aplicativo é responsável por garantir que o conteúdo dos recursos (recursos confirmados, heaps, alocadores de comando, por exemplo) não seja alterado até que a GPU termine de usá-los.
O objeto principal para sincronizar as linhas do tempo é o objeto ID3D12Fence. A operação das cercas é bastante simples, elas permitem que a GPU sinalize quando concluiu uma tarefa. A GPU e a CPU podem sinalizar e podem esperar em cercas.
Normalmente, a abordagem é que, ao enviar uma lista de comandos para execução, um sinal de fence é transmitido pela GPU após a conclusão (quando termina de ler os dados), permitindo que a CPU reutilize ou destrua os recursos.
No Direct3D 11, o sinalizador ID3D11DeviceContext::Map D3D11_MAP_WRITE_DISCARD essencialmente tratava cada recurso como um suprimento infinito de memória no qual o aplicativo poderia gravar (um processo conhecido como "renomeação"). No Direct3D 12, novamente, o processo é explícito: memória adicional precisa ser alocada e cercas devem ser usadas para sincronizar as operações. Buffers de anel (consistindo em buffers grandes) podem ser uma boa técnica para isso, consulte o cenário de buffer de anel no Gerenciamento de Recursos Baseado em Fence.
Associação de Recursos
Os modos de exibição no Direct3D 11 (modos de exibição de recursos de sombreador, modos de exibição de destino de renderização e assim por diante) foram amplamente substituídos no Direct3D 12 pelo conceito de um descritor. Os métodos de criação ainda existem no Direct3D 12 (como CreateShaderResourceView e CreateRenderTargetView), que são chamados depois que o heap do descritor foi criado, para gravar os dados no heap. A associação no Direct3D 12 agora é tratada por identificadores de descritor descritos em uma assinatura raiz e enviada usando os métodos SetGraphicsRootDescriptorTable ou SetComputeRootDescriptorTable.
As assinaturas raiz detalham mapeamentos entre o número do slot de assinatura raiz e as tabelas de descritor, em que a tabela de descritor pode conter referências a recursos disponíveis para sombreadores de vértice, sombreadores de pixel e outros sombreadores, como buffers constantes, exibições de recursos de sombreador e amostradores. Essa flexibilidade desconecta o espaço de registro HLSL do espaço de associação de API no Direct3D 12, ao contrário do Direct3D 11, em que há um mapeamento de um para um entre eles.
Uma das implicações desse sistema é que o aplicativo é responsável por renomear tabelas de descritores, o que permite que os desenvolvedores entendam o custo de desempenho de alterar até mesmo um único descritor por chamada de desenho.
Um novo recurso do Direct3D 12 é que um aplicativo pode controlar quais descritores são compartilhados entre quais estágios de sombreador. No Direct3D 11, os recursos, como UAVs, são compartilhados entre todos os estágios do sombreador. Ao permitir que os descritores sejam desabilitados para determinados estágios do sombreador, os registros usados pelos descritores que foram desabilitados estão disponíveis para serem usados por descritores habilitados para um estágio de sombreador específico.
A tabela a seguir mostra um exemplo de assinatura raiz.
Slot de parâmetro raiz | Entrada da tabela de descritor |
---|---|
0 | Intervalo de descritor VS b0-b13 |
1 | Intervalo do descritor VS t0-t127 |
2 | Intervalo do descritor VS s0-s16 |
3 | Intervalo de descritores PS b0-b13 |
... | |
14 | Intervalo do descritor DS s0-16 |
15 | Intervalo de descritores compartilhados u0-u63 |
Estado do recurso
No Direct3D 11, o estado do recurso não é mantido pelo aplicativo, mas pelo driver.
No Direct3D 12, manter o estado do recurso torna-se responsabilidade do aplicativo, para habilitar o paralelismo completo na gravação de listas de comandos: o aplicativo deve lidar com as linhas do tempo de gravação para listas de comandos (o que pode ser feito em paralelo) e as linhas do tempo de execução que devem ser sequenciais.
Uma transição de estado de recurso é tratada pelo método ResourceBarrier . Principalmente, o aplicativo deve informar o driver quando o uso de recursos estiver mudando. Por exemplo, se um recurso estiver sendo usado como um destino de renderização e, em seguida, for usado como entrada para um sombreador de vértice na próxima chamada de desenho, isso poderá exigir uma pequena paralisação na operação de GPU para concluir a operação de destino de renderização antes de lidar com o sombreador de vértice.
Esse sistema permite a sincronização refinada (a GPU é interrompida) do pipeline gráfico, bem como liberações de cache e possivelmente algumas alterações de layout de memória (como exibição de destino de renderização para descompactação de exibição de estêncil de profundidade).
Isso é conhecido como barreira de transição. Há outros tipos de barreiras, no Direct3D 11, o ID3D11DeviceContext2::TiledResourceBarrier permitiu que a mesma memória física fosse usada por dois recursos em blocos diferentes. No Direct3D 12, isso é conhecido como uma "barreira de alias". As barreiras de aliasing podem ser usadas para recursos lado a lado e colocados no Direct3D 12. Além disso, há a barreira do UAV. No Direct3D 11, todas as operações de expedição e desenho do UAV devem ser serializadas, mesmo que essas operações possam ser canalizadas ou trabalhar em paralelo. Para o Direct3D 12, essa restrição é removida pela adição de uma barreira UAV. Uma barreira UAV garante que as operações UAV sejam sequenciais, portanto, se uma segunda operação exigir que a primeira seja concluída, a segunda será forçada a esperar pela adição da barreira. A operação padrão para UAVs é simplesmente que as operações prossigam o mais rápido possível.
Claramente, há ganhos de desempenho se uma carga de trabalho puder ser paralelizada.
Cadeias de troca
A cadeia de troca DXGI é a base para cadeias de troca no Direct3D 11 e 12. Há algumas pequenas diferenças, no Direct3D 11, os três tipos de cadeia de troca são SEQUENTIAL, DISCARD e FLIP_SEQUENTIAL. Para o Direct3D 12, há apenas dois tipos: FLIP_SEQUENTIAL e FLIP_DISCARD. Conforme observado acima, você deve criar explicitamente sua cadeia de troca por meio de IDXGIFactory4 ou posterior e usar a mesma interface para qualquer enumeração de adaptador.
No Direct3D 11, há rotação automática de backbuffer: apenas uma exibição de destino de renderização é necessária para o buffer de fundo 0. No Direct3D 12, a rotação do buffer é explícita, é necessário haver uma exibição de destino de renderização para cada buffer traseiro. Use o método IDXGISwapChain3::GetCurrentBackBufferIndex para selecionar para qual renderizar. Novamente, essa flexibilidade adicional permite uma maior paralelização.
Observação
Embora existam várias maneiras de configurar seu aplicativo, geralmente os aplicativos têm um ID3D12CommandAllocator por buffer de cadeia de troca. Isso permite que o aplicativo prossiga com a criação de um conjunto de comandos para o próximo quadro enquanto a GPU renderiza o anterior.
Renderização de função fixa
No Direct3D 11, havia alguns métodos que simplificavam várias operações de nível superior, como GenerateMips (criando cadeias mip completas) e DrawAuto (usando a saída de fluxo como entrada de sombreador sem entrada adicional do aplicativo). Esses métodos não estão disponíveis no Direct3D 12, o aplicativo precisa lidar com essas operações criando sombreadores para executá-las.
Probabilidades e extremidades
A tabela a seguir mostra vários recursos semelhantes entre o Direct3D 11 e o 12, mas não são idênticos.
Direct3D 11 | Direct3D 12 |
---|---|
ID3D11Consulta | ID3D12QueryHeap permite que as consultas sejam agrupadas, reduzindo o custo. |
ID3D11Predicado | A predicação agora é habilitada por ter dados em um buffer totalmente transparente. O objeto ID3D11Predicate do Direct3D 11 é substituído por ID3D12Resource::Map, que deve seguir uma chamada para ResolveQueryData e uma operação de sincronização de GPU usando uma cerca para aguardar que os dados estejam prontos. Consulte Predicação. |
Contador oculto UAV / SO | O aplicativo é responsável pela alocação e gerenciamento de contadores SO/UAV. Consulte Contadores de Saída de Fluxo e Contadores de UAV. |
MinLOD dinâmico de recursos (nível mínimo de detalhe) | Isso foi movido para o descritor SRV MinLOD estático. |
Draw*Indireto/DispatchIndirect | Os métodos indiretos de desenho são todos mesclados em um método ExecuteIndirectus. |
Os formatos DepthStencil são intercalados | Os formatos DepthStencil são planares. Por exemplo, um formato de 24 bits de profundidade, 8 bits de estêncil seriam armazenados no formato 24/8/24/8... etc no Direct3D 11, mas como 24/24/24 ... seguido por 8/8/8 ... no Direct3D 12. Observe que cada plano é seu próprio sub-recurso em D3D12 (consulte Sub-recursos). |
ResizeTilePool | Os recursos reservados podem ser mapeados para vários heaps. Quando um pool de blocos teria sido aumentado no D3D11, um heap adicional pode ser alocado no D3D12. |