Barreiras reforçadas do D3D12
A interface DDI para barreiras reforçadas está disponível no Windows 11, versão 22H2 WDK (WDDM 3.0). Para usar barreiras reforçadas em 22H2 (ou versões anteriores do sistema operacional), você deve instalar o Versão prévia 1.706.4 do SDK de agilidade.
As barreiras reforçadas do D3D12 oferecem aos desenvolvedores um controle independente sobre a sincronização de trabalho da GPU, transições de layout de textura e liberação de cache (acesso à memória do recurso). Esse recurso oferece um conjunto de APIs e DDIs do Direct3D que dão aos desenvolvedores controle independente sobre a sincronização de trabalho da GPU, transições de layout de textura e liberação de cache (acesso à memória do recurso).
As barreiras reforçadas substituem as barreiras de recursos legadas por tipos mais expressivos. Elas oferecem os seguintes recursos:
- Menos latência de sincronização.
- Uma redução nas liberações excessivas de cache.
- Nenhuma regra misteriosa de Promoção e Decadência.
- Uso de alias de recursos rápido e flexível (diversas topologias de uso de alias).
- Descarte durante a transição da barreira.
- Suporte para leitura/gravação simultânea, incluindo a cópia do mesmo recurso (autocópia).
- Suporte para comandos assíncronos Descartar, Copiar, Resolver e Limpar.
As barreiras reforçadas não são mais simples que as barreiras de recursos herdadas, mas são menos ambíguas, facilitando o uso dos desenvolvedores.
Relatórios de suporte de barreira reforçada
Atualmente, o recurso de barreiras reforçadas não é um requisito de hardware ou driver. Um driver indica suporte definindo o membro EnhancedBarriersSupported de D3D12DDI_D3D12_OPTIONS_DATA_0089 como TRUE.
- D3D12DDI_FEATURE_VERSION_VIDEO_0088_0 é o número da versão que define a implementação anterior completa dos marcos da barreira reforçada do D3D12 que foram introduzidos no Windows 11.
Funções de retorno de chamada da barreira reforçada do D3D12
Os drivers que indicam suporte para barreiras reforçadas implementam as seguintes funções de retorno de chamada:
- PFND3D12DDI_BARRIER_0088
- PFND3D12DDI_CREATEHEAPANDRESOURCE_0088
- PFND3D12DDI_CALCPRIVATEHEAPANDRESOURCESIZES_0088
- PFND3D12DDI_CHECKRESOURCEALLOCATIONINFO_0088
Detalhes do design
Os drivers normalmente trabalham com barreiras de recursos herdados usando três operações separadas:
- Sincronize o trabalho da GPU.
- Execute todas as operações de liberação de cache necessárias.
- Faça as alterações de layout necessárias.
As barreiras reforçadas dão aos desenvolvedores a capacidade de controlar cada uma dessas operações separadamente.
Tipos de barreiras reforçadas
Existem três tipos de barreiras reforçadas:
As barreiras com intervalos substituem as barreiras de recursos legadas. As barreiras com intervalos são fornecidas para que as barreiras de recursos legadas possam ser totalmente implementadas sem perda de desempenho perceptível.
Todos os tipos de barreira controlam a sincronização de trabalho da GPU e os tipos de acesso de leitura ou gravação antes e depois da barreira.
As barreiras de textura também gerenciam o layout dos sub-recursos de textura. A seleção de sub-recursos pode ser mostrada como um intervalo de fatias mip, matriz e plano, além da opção mais conhecida "uma ou todas" usada pelas barreiras de recursos legadas.
As barreiras de buffer e as barreiras globais controlam apenas a sincronização e o acesso a recursos e não afetam o layout do recurso (os buffers não têm um layout). As barreiras globais afetam toda a memória armazenada em cache, o que as torna caras, por isso só devem ser usadas quando uma barreira com mais escopo for insuficiente.
Barreiras de textura
- Controle a liberação do cache, o layout da memória e a sincronização de sub-recursos de textura.
- Elas só devem ser usadas com recursos de textura.
- Habilite a seleção de um único sub-recurso, todos os sub-recursos ou um intervalo coerente de sub-recursos (ou seja, intervalo de mip e intervalo de matriz).
- É necessário fornecer um indicador de recurso válido e não NULL.
Barreiras de buffer
- Controle a liberação e a sincronização do cache para recursos de buffer.
- Elas só devem ser usadas com recursos de buffer.
- Ao contrário das texturas, os buffers têm apenas um único sub-recurso sem um layout que pode ser transferido.
- É necessário fornecer um indicador de recurso válido e não NULL.
Barreiras globais
- Controle a liberação e a sincronização do cache para todos os tipos de acesso a recursos indicados em uma única fila de comandos.
- Elas não têm efeito no layout da textura.
- Elas são necessárias para proporcionar uma funcionalidade semelhante às barreiras NULL UAV herdadas e às barreiras de uso de alias NULL/NULL.
Como as barreiras globais não fazem a transição do layout de textura, não é possível usá-las para transições que, de outra forma, exigiriam uma alteração de layout. Por exemplo, uma barreira global não pode ser usada para fazer a transição de uma textura de acesso não simultâneo de D3D12DDI_BARRIER_ACCESS_RENDER_TARGET para D3D12DDI_BARRIER_ACCESS_SHADER_RESOURCE, já que isso também exigiria uma alteração de D3D12DDI_BARRIER_LAYOUT_RENDER_TARGET para D3D12DDI_BARRIER_LAYOUT_SHADER_RESOURCE.
Sincronização
Os processadores gráficos são projetados para executar o máximo de tarefas possível simultaneamente. Qualquer trabalho de GPU que dependa do trabalho anterior da GPU deve ser sincronizado antes de acessar dados dependentes.
A interface de barreira reforçada usa valores explícitos de SyncBefore e SyncAfter como máscaras de campo de bits lógicos. Antes de uma barreira ser executada, é preciso aguardar a conclusão de todos os escopos de comando SyncBefore anteriores. Da mesma forma, uma barreira deve bloquear todos os escopos de SyncAfter subsequentes até que a barreira esteja completa. D3D12DDI_BARRIER_SYNC especifica o escopo de sincronização do trabalho da GPU em relação à barreira.
Para mais informações, consulte a especificação Barreiras reforçadas.
Transições de layout
Os sub-recursos de textura podem usar layouts diferentes para vários métodos de acesso. Por exemplo, as texturas geralmente são compactadas ao serem usadas como um destino de renderização ou estêncil de profundidade e geralmente são descompactadas para comandos de leitura ou cópia do sombreador. As barreiras de textura usam os valores LayoutBefore e LayoutAfter D3D12DDI_BARRIER_LAYOUT para descrever transições de layout.
As transições de layout são necessárias apenas para texturas, sendo assim, são expressas apenas na estrutura de dados em D3D12DDI_TEXTURE_BARRIER.
LayoutBefore e LayoutAfter devem ser compatíveis com o tipo de fila que executa a barreira. Por exemplo, uma fila de computação não pode fazer a transição de um sub-recurso para dentro ou fora de D3D12DDI_BARRIER_LAYOUT_RENDER_TARGET.
Para fornecer uma ordenação de barreira bem definida, o layout de um sub-recurso após a conclusão de uma sequência de barreiras é o LayoutAfter final.
Transições de acesso
Como muitas operações de gravação de GPU são armazenadas em cache, qualquer barreira, de um acesso de gravação a outro ou a um acesso somente leitura, pode exigir uma liberação de cache. As APIs de barreira reforçadas usam transições de acesso para indicar que a memória de um sub-recurso precisa se tornar visível para um novo tipo de acesso específico. Assim como as transições de layout, algumas transições de acesso podem não ser necessárias quando a memória do sub-recurso associado já está acessível para o uso desejado.
As transições de acesso são expressas da seguinte forma:
- Para texturas, como parte da estrutura D3D12DDI_TEXTURE_BARRIER.
- Para buffers, como parte da estrutura D3D12DDI_BUFFER_BARRIER.
As transições de acesso não executam a sincronização. Espera-se que a sincronização entre acessos dependentes seja tratada usando os valores certos SyncBefore e SyncAfter na barreira.
Um AccessBefore que se torna visível para um AccessAfter específico não garante que a memória do recurso também esteja disponível para outro tipo de acesso. Por exemplo:
MyTexBarrier.AccessBefore=D3D12DDI_BARRIER_ACCESS_UNORDERED_ACCESS;
MyTexBarrier.AccessAfter=D3D12DDI_BARRIER_ACCESS_SHADER_RESOURCE;
Essa transição de acesso indica que um acesso subsequente de leitura do sombreador depende de uma gravação de acesso não ordenado anterior. No entanto, se o hardware conseguir ler os recursos de sombreador diretamente do cache do UAV, o driver poderá não liberar o cache do UAV.
D3D12DDI_BARRIER_ACCESS_COMMON
D3D12DDI_BARRIER_ACCESS_COMMON é um tipo de acesso especial que indica qualquer acesso compatível com o layout. A transição para D3D12DDI_BARRIER_ACCESS_COMMON significa que os dados de sub-recursos devem estar disponíveis para qualquer acesso compatível com o layout depois de uma barreira. Como os buffers não têm layout, D3D12DDI_BARRIER_ACCESS_COMMON significa simplesmente qualquer acesso compatível com buffer.
Especificar D3D12DDI_BARRIER_ACCESS_COMMON como AccessBefore em uma barreira implica o conjunto de todos os tipos de acesso de gravação. O uso de D3D12DDI_BARRIER_ACCESS_COMMON como AccessBefore não é recomendado, porque pode resultar em liberações de cache caras e não intencionais. Em vez disso, os desenvolvedores são incentivados a usar apenas os bits de acesso de gravação mais restritos para limitar adequadamente a sobrecarga da barreira. Um aviso de camada de depuração é emitido quando AccessBefore é definido como D3D12DDI_BARRIER_ACCESS_COMMON.
Acesso simultâneo de fila única
As barreiras reforçadas permitem operações simultâneas de leitura/gravação no mesmo buffer ou textura de acesso simultâneo na mesma fila de comandos.
Buffers e recursos de acesso simultâneo sempre deram suporte ao acesso de gravação de uma fila com acessos de leitura simultâneos que não dependem de uma ou mais outras filas. Esse suporte existe porque os recursos sempre usam o layout COMMON e não têm riscos de leitura/gravação, pois as leituras não devem depender de gravações simultâneas. (As regras de barreira de recursos herdados não permitem combinar bits de estado de gravação com nenhum outro bit de estado. Sendo assim, os recursos não podem ser lidos e gravados simultaneamente na mesma fila usando barreiras de recursos herdadas.)
A política de um gravador por vez ainda se aplica, pois não é possível ter linhas de cache sobrepostas com duas regiões de gravação aparentemente não sobrepostas.
Intervalos de sub-recursos
É comum que os desenvolvedores queiram fazer a transição de uma variedade de sub-recursos. Um exemplo é fazer a transição de uma cadeia de mip completa para uma determinada matriz de textura ou um único nível de mip para todas as fatias da matriz. As barreiras reforçadas permitem que os desenvolvedores façam a transição logicamente de intervalos adjacentes de sub-recursos usando a estrutura D3D12DDI_BARRIER_SUBRESOURCE_RANGE. (As barreiras de transição de estado de recurso herdado fornecem apenas aos desenvolvedores a opção de fazer a transição de todos os estados de sub-recursos ou um único estado de sub-recurso atomicamente.)
Layouts de fila direta e de computação
Os seguintes layouts de barreira reforçada serão os mesmos para filas diretas e de computação:
- D3D12DDI_BARRIER_LAYOUT_GENERIC_READ
- D3D12DDI_BARRIER_LAYOUT_UNORDERED_ACCESS
- D3D12DDI_BARRIER_LAYOUT_SHADER_RESOURCE
- D3D12DDI_BARRIER_LAYOUT_COPY_SOURCE
- D3D12DDI_BARRIER_LAYOUT_COPY_DEST
Um sub-recurso em um desses layouts pode ser usado em filas diretas ou de computação sem uma transição de layout.
Em alguns hardwares, as barreiras de transição de layout em filas diretas poderão ser significativamente mais rápidas se os acessos anteriores ou subsequentes também estiverem em filas diretas. É altamente recomendável acessar recursos em filas diretas usando os seguintes layouts:
- D3D12DDI_BARRIER_LAYOUT_DIRECT_QUEUE_GENERIC_READ
- D3D12DDI_BARRIER_LAYOUT_DIRECT_QUEUE_UNORDERED_ACCESS
- D3D12DDI_BARRIER_LAYOUT_DIRECT_QUEUE_SHADER_RESOURCE
- D3D12DDI_BARRIER_LAYOUT_DIRECT_QUEUE_COPY_SOURCE
- D3D12DDI_BARRIER_LAYOUT_DIRECT_QUEUE_COPY_DEST
As variantes de layout DIRECT_QUEUE não são compatíveis com filas de computação e não podem ser usadas em barreiras de lista de comandos de computação. No entanto, elas são compatíveis com operações de computação em filas diretas.
Acesso sem barreiras
Como não deve haver comandos pendentes ou operações de liberação de cache entre os limites de ExecuteCommandLists, os buffers podem ser acessados inicialmente em um escopo de ExecuteCommandLists sem uma barreira. Da mesma forma, os sub-recursos de textura também podem ser acessados inicialmente sem uma barreira sob as seguintes condições:
- O layout do sub-recurso é compatível com o tipo de acesso.
- Todos os metadados de compactação necessários são inicializados.
Os sub-recursos de textura no layout D3D12DDI_BARRIER_LAYOUT_COMMON (ou um layout comum específico da fila, como D3D12DDI_BARRIER_LAYOUT_DIRECT_QUEUE_COMMON) sem operações de leitura ou gravação potencialmente pendentes podem ser acessados em um fluxo de comando ExecuteCommandLists sem uma barreira por meio dos seguintes tipos de acesso:
- D3D12DDI_BARRIER_ACCESS_SHADER_RESOURCE
- D3D12DDI_BARRIER_ACCESS_COPY_SOURCE
- D3D12DDI_BARRIER_ACCESS_COPY_DEST
Além disso, um buffer ou textura com um layout comum específico da fila pode usar D3D12DDI_BARRIER_ACCESS_UNORDERED_ACCESS sem uma barreira.
Buffers e texturas de acesso simultâneo (texturas criadas com o sinalizador D3D12DDI_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS) podem ser acessados inicialmente em um fluxo de comando ExecuteCommandLists sem uma barreira usando qualquer um destes tipos de acesso:
- D3D12DDI_BARRIER_ACCESS_VERTEX_BUFFER
- D3D12DDI_BARRIER_ACCESS_CONSTANT_BUFFER
- D3D12DDI_BARRIER_ACCESS_INDEX_BUFFER
- D3D12DDI_BARRIER_ACCESS_RENDER_TARGET
- D3D12DDI_BARRIER_ACCESS_UNORDERED_ACCESS
- D3D12DDI_BARRIER_ACCESS_SHADER_RESOURCE
- D3D12DDI_BARRIER_ACCESS_STREAM_OUTPUT
- D3D12DDI_BARRIER_ACCESS_INDIRECT_ARGUMENT
- D3D12DDI_BARRIER_ACCESS_COPY_DEST
- D3D12DDI_BARRIER_ACCESS_COPY_SOURCE
- D3D12DDI_BARRIER_ACCESS_RESOLVE_DEST
- D3D12DDI_BARRIER_ACCESS_RESOLVE_SOURCE
- D3D12DDI_BARRIER_ACCESS_PREDICATION
Os acessos subsequentes também podem ser feitos sem uma barreira com apenas um tipo de acesso de gravação. No entanto, exceto para D3D12DDI_BARRIER_ACCESS_RENDER_TARGET, as barreiras devem ser usadas na liberação de gravações sequenciais para o mesmo recurso.
Cópia de autorrecurso
Embora não esteja exclusivamente relacionada às barreiras reforçadas, a permissão de cópias de uma região de um sub-recurso para outra região sem interseção é um recurso altamente solicitado. Ao usar as barreiras reforçadas, um sub-recurso com um layout comum pode ser usado como origem e destino na mesma chamada CopyBufferRegion ou CopyTextureRegion. As cópias entre as regiões de memória de origem e destino que se cruzam produzem resultados indefinidos. A camada de depuração PRECISA ser validada quando comparada a esses resultados. (O design da barreira de recursos herdados não permite que um sub-recurso fique no estado D3D12DDI_RESOURCE_STATE_COPY_SOURCE e D3D12DDI_RESOURCE_STATE_COPY_DEST ao mesmo tempo, ou seja, não pode copiar para si mesmo.)
Inicialização de metadados de recursos inseridos
O design de barreira de recursos herdados requer que o comando Limpar, Copiar ou Descartar inicialize recursos de textura com alias recém-colocados e ativados antes de usá-los como um recurso de Destino de renderização ou Estêncil de profundidade. Esse requisito ocorre porque os recursos Destino de renderização e Estêncil de profundidade normalmente usam metadados de compactação que devem ser inicializados para que os dados sejam válidos. O mesmo vale para texturas reservadas com mapeamento de blocos recém-atualizado.
As barreiras reforçadas são compatíveis com uma opção para Descartar como parte de uma barreira. As transições de layout de barreira de D3D12DDI_BARRIER_LAYOUT_UNDEFINED para qualquer layout potencialmente compactado (por exemplo, D3D12DDI_BARRIER_LAYOUT_RENDER_TARGET, D3D12DDI_BARRIER_LAYOUT_DEPTH_STENCIL D3D12DDI_BARRIER_LAYOUT_UNORDERED_ACCESS) PRECISAM inicializar metadados de compactação quando D3D12DDI_TEXTURE_BARRIER_FLAG_DISCARD estiver presente no membro D3D12DDI_TEXTURE_BARRIER::Flags.
Além dos recursos de Destino de renderização/Estêncil de profundidade, há otimizações de compactação de textura UAV semelhantes aos aprimoramentos não suportados pelo modelo de barreira herdado.
Ordenação de barreira
As barreiras são enfileiradas em ordem de encaminhamento (API-call order, barrier-group-index, barrier-array-index). Várias barreiras no mesmo sub-recurso devem funcionar como se as barreiras fossem concluídas na ordem enfileirada.
Barreiras enfileiradas com escopos SyncAfter correspondentes, que possivelmente gravam na mesma memória, precisam concluir todas as gravações na ordem da fila. Esse requisito evita corridas de dados em barreiras que têm suporte ao uso de alias de recursos. Por exemplo, uma barreira que desativa um recurso precisa liberar todos os caches antes de outra barreira que ativa outro recurso na mesma memória, possivelmente limpando os metadados.