Como executar e sincronizar listas de comandos

No Microsoft Direct3D 12, o modo imediato das versões anteriores não está mais presente. Em vez disso, os aplicativos criam listas de comandos e pacotes e, em seguida, registram conjuntos de comandos de GPU. As filas de comandos são usadas para enviar listas de comandos a serem executadas. Esse modelo permite que os desenvolvedores tenham mais controle sobre o uso eficiente da GPU (unidade de processamento gráfico) e da CPU.

Visão geral da fila de comandos

As filas de comando do Direct3D 12 substituem o runtime e a sincronização de driver do envio de trabalho do modo imediato, anteriormente não exposto ao desenvolvedor, com APIs para gerenciar explicitamente a simultaneidade, o paralelismo e a sincronização. As filas de comandos fornecem as seguintes melhorias para os desenvolvedores:

  • Permite que os desenvolvedores evitem ineficiências acidentais causadas pela sincronização inesperada.
  • Permite que os desenvolvedores introduzam a sincronização em um nível mais alto em que a sincronização necessária pode ser determinada com mais eficiência e precisão. Isso significa que o runtime e o driver gráfico gastarão menos tempo paralelismo de engenharia reativamente.
  • Torna as operações caras mais explícitas.

Essas melhorias habilitam ou aprimoram os seguintes cenários:

  • Maior paralelismo – os aplicativos podem usar filas mais profundas para cargas de trabalho em segundo plano, como decodificação de vídeo, quando têm filas separadas para o trabalho em primeiro plano.
  • Trabalho de GPU assíncrona e de baixa prioridade – o modelo de fila de comandos permite a execução simultânea de operações atômicas e de trabalho de GPU de baixa prioridade que permitem que um thread de GPU consuma os resultados de outro thread não sincronizado sem bloqueio.
  • Trabalho de computação de alta prioridade – esse design permite que cenários que exigem a interrupção da renderização 3D façam uma pequena quantidade de trabalho de computação de alta prioridade para que o resultado possa ser obtido antecipadamente para processamento adicional na CPU.

Inicializando uma fila de comandos

As filas de comando podem ser criadas chamando ID3D12Device::CreateCommandQueue. Esse método usa um D3D12_COMMAND_LIST_TYPE indicando que tipo de fila deve ser criado e, portanto, que tipo de comandos podem ser enviados para essa fila. Lembre-se de que os pacotes só podem ser chamados de listas de comandos diretas e não podem ser enviados diretamente para uma fila. Os tipos de fila com suporte são:

Em geral, filas DIRECT e listas de comandos aceitam qualquer comando, filas computacionais e listas de comandos aceitam comandos relacionados à computação e cópia, e filas COPY e listas de comandos aceitam apenas comandos de cópia.

Executando listas de comandos

Depois de registrar uma lista de comandos e recuperar a fila de comandos padrão ou criar uma nova, execute listas de comandos chamando ID3D12CommandQueue::ExecuteCommandLists.

Os aplicativos podem enviar listas de comandos para qualquer fila de comandos de vários threads. O runtime executará o trabalho de serialização dessas solicitações na ordem de envio.

O runtime validará a lista de comandos enviada e removerá a chamada para ExecuteCommandLists se alguma das restrições for violada. As chamadas serão descartadas pelos seguintes motivos:

Acessando recursos de várias filas de comando

Há algumas regras impostas pelo runtime que restringem o acesso de recursos de várias filas de comando. Essas regras são as seguintes:

  1. Um recurso não pode ser gravado em várias filas de comando simultaneamente. Quando um recurso faz a transição para um estado gravável em uma fila, ele é considerado exclusivamente pertencente a essa fila e deve fazer a transição para um estado de leitura ou COMMON (consulte D3D12_RESOURCE_STATES) antes de poder ser acessado por outra fila.

  2. Quando em um estado de leitura, um recurso pode ser lido de várias filas de comando simultaneamente, inclusive entre processos, com base em seu estado de leitura.

Para obter mais informações sobre restrições de acesso a recursos e uso de barreiras de recursos para sincronizar o acesso aos recursos, consulte Usando barreiras de recursos para sincronizar estados de recursos.

Sincronizando a execução da lista de comandos usando cercas de fila de comandos

O suporte para várias filas de comando paralelas no Direct3D 12 oferece mais flexibilidade e controle sobre a priorização do trabalho assíncrono na GPU. Esse design também significa que os aplicativos precisam gerenciar explicitamente a sincronização do trabalho, especialmente quando as listas de comandos em uma fila dependem dos recursos que estão sendo operados por outra fila de comandos. Alguns exemplos disso incluem aguardar a conclusão de uma operação em uma fila de computação para que o resultado possa ser usado para uma operação de renderização na fila 3D e aguardar a conclusão de uma operação 3D para que uma operação na fila de computação possa acessar um recurso para gravação. Para habilitar a sincronização do trabalho entre filas, o Direct3D 12 usa o conceito de cercas, que são representadas na API pela interface ID3D12Fence .

Uma cerca é um inteiro que representa a unidade atual do trabalho que está sendo processada. Quando o aplicativo avança a cerca, chamando ID3D12CommandQueue::Signal, o inteiro é atualizado. Os aplicativos podem marcar o valor de uma cerca e determinar se uma unidade de trabalho foi concluída para decidir se uma operação subsequente pode ser iniciada.

Sincronizando recursos acessados por filas de comando

No Direct3D 12, a sincronização do estado de alguns recursos é implementada com barreiras de recursos. Em cada barreira de recursos, um aplicativo declara os estados antes e depois de um recurso. Um exemplo comum é que um recurso faça a transição entre uma exibição de recurso de sombreador para uma exibição de destino de renderização. Na maioria das vezes, essas barreiras de recursos são gerenciadas em listas de comandos. Quando as camadas de depuração são habilitadas, o sistema impõe que os estados antes e depois de todos os recursos correspondam, garantindo que o recurso esteja no estado correto para uma operação específica em uma transição de barreira.

Para obter mais informações sobre como sincronizar o estado do recurso, consulte Usando barreiras de recursos para sincronizar estados de recursos.

Suporte à fila de comandos para recursos em bloco

Os métodos para gerenciar recursos em bloco, que foram expostos por meio da interface ID3D11DeviceContext2 no Direct3D 11, são fornecidos pela interface ID3D12CommandQueue no Direct3D 12. Esses métodos incluem:

Método Descrição
CopyTileMappings Copia mapeamentos de um recurso em bloco de origem para um recurso de bloco de destino.
UpdateTileMappings Atualizações mapeamentos de locais de bloco em recursos em bloco para locais de memória em um heap de recursos.

Para obter mais informações sobre como usar recursos em bloco em aplicativos Direct3D 12, consulte Direct3D11 Tiled Resources.

Envio de trabalho no Direct3D 12