Share via


Visão geral da associação de recursos

As chaves para entender a associação de recursos no DirectX 12 são os conceitos de descritores, tabelas de descritor, heaps de descritor e assinaturas raiz.

Recursos e o pipeline de gráficos

Os recursos de sombreador (como texturas, tabelas constantes, imagens, buffers e assim por diante) não são associados diretamente ao pipeline do sombreador; Em vez disso, eles são referenciados por meio de um descritor. Um descritor é um objeto pequeno que contém informações sobre um recurso.

Os descritores são agrupados para formar tabelas de descritores. Cada tabela de descritor armazena informações sobre um intervalo de tipos de recurso. Há muitos tipos diferentes de recursos. Os recursos mais comuns são:

  • CBVs (exibições de buffer constantes)
  • UAVs (exibições de acesso não ordenadas)
  • SRVs (exibições de recursos de sombreador)
  • Amostradores

Os descritores SRV, UAV e CBVs podem ser combinados na mesma tabela de descritores.

Os pipelines gráficos e de computação obtêm acesso aos recursos fazendo referência a tabelas de descritores por índice.

As tabelas de descritor são armazenadas em um heap de descritor. Os heaps de descritor conterão, idealmente, todos os descritores (em tabelas de descritor) para que um ou mais quadros sejam renderizados. Todos os recursos serão armazenados em heaps de modo de usuário.

Outro conceito é o de uma assinatura raiz. A assinatura raiz é uma convenção de associação, definida pelo aplicativo, que é usada por sombreadores para localizar os recursos aos quais eles precisam de acesso. A assinatura raiz pode armazenar:

  • Índices para tabelas de descritor em um heap de descritor, em que o layout da tabela do descritor foi predefinido.
  • Constantes, para que os aplicativos possam associar constantes definidas pelo usuário ( conhecidas como constantes raiz) diretamente a sombreadores sem precisar passar por descritores e tabelas de descritores.
  • Um número muito pequeno de descritores diretamente dentro da assinatura raiz, como uma CBV (exibição de buffer constante) que muda por desenho, evitando assim que o aplicativo precise colocar esses descritores em um heap de descritor.

Em outras palavras, a assinatura raiz fornece otimizações de desempenho adequadas para pequenas quantidades de dados que mudam por desenho.

O design do Direct3D 12 para associação o separa de outras tarefas, como gerenciamento de memória, gerenciamento de tempo de vida do objeto, acompanhamento de estado e sincronização de memória (consulte Diferenças no modelo de associação do Direct3D 11). A associação do Direct3D 12 foi projetada para ser de baixa sobrecarga e otimizada para as chamadas à API que são feitas com mais frequência. Ele também é escalonável entre hardwares de baixa extremidade e high-end e escalonável desde o mais antigo (o pipeline mais linear do Direct3D 11) até as abordagens mais recentes (mais paralelas) à programação do mecanismo gráfico.

Tipos de recursos e exibições

Os tipos de recursos são os mesmos do Direct3D 11, ou seja:

  • Texture1D e Texture1DArray
  • Texture2D e Texture2DArray, Texture2DMS, Texture2DMSArray
  • Texture3D
  • Buffers (tipado, estruturado e bruto)

As exibições de recursos são semelhantes, mas ligeiramente diferentes do Direct3D 11, as exibições de buffer de vértice e índice foram adicionadas.

  • Modo de exibição de buffer constante (CBV)
  • Exibição de acesso não ordenado (UAV)
  • Exibição de recurso de sombreador (SRV)
  • Amostradores
  • RtV (Modo de Exibição de Destino de Renderização)
  • Exibição de estêncil de profundidade (DSV)
  • Exibição de buffer de índice (IBV)
  • Exibição de buffer de vértice (VBV)
  • Exibição de saída de fluxo (SOV)

Somente os quatro primeiros desses modos de exibição são realmente visíveis para sombreadores, consulte Heaps de descritor visível do sombreador e heaps de descritor visível não sombreador.

Fluxo de controle de associação de recursos

Concentrando-se apenas em assinaturas raiz, descritores raiz, constantes raiz, tabelas de descritor e heaps de descritor, o fluxo de lógica de renderização para um aplicativo deve ser semelhante ao seguinte:

  • Crie um ou mais objetos de assinatura raiz – um para cada configuração de associação diferente de que um aplicativo precisa.
  • Crie sombreadores e estado do pipeline com os objetos de assinatura raiz com os qual eles serão usados.
  • Crie um (ou, se necessário, mais) heaps de descritor que conterão todos os descritores SRV, UAV e CBV para cada quadro de renderização.
  • Inicialize os heaps do descritor com descritores sempre que possível para conjuntos de descritores que serão reutilizados em vários quadros.
  • Para cada quadro a ser renderizado:
    • Para cada lista de comandos:
      • Defina a assinatura raiz atual a ser usada (e altere se necessário durante a renderização – o que raramente é necessário).
      • Atualize algumas constantes de assinatura raiz e/ou descritores de assinatura raiz para a nova exibição (como projeções de mundo/exibição).
      • Para cada item a ser desenhado:
        • Defina quaisquer novos descritores em heaps de descritor conforme necessário para renderização por objeto. Para heaps de descritor visíveis do sombreador, o aplicativo deve usar o espaço de heap do descritor que ainda não está sendo referenciado pela renderização que pode estar em versão de pré-lançamento, por exemplo, alocando espaço linearmente por meio do heap do descritor durante a renderização.
        • Atualize a assinatura raiz com ponteiros para as regiões necessárias dos heaps do descritor. Por exemplo, uma tabela de descritor pode apontar para alguns descritores estáticos (inalterados) inicializados anteriormente, enquanto outra tabela de descritor pode apontar para alguns descritores dinâmicos configurados para a renderização atual.
        • Atualize algumas constantes de assinatura raiz e/ou descritores de assinatura raiz para renderização por item.
        • Defina o estado do pipeline para o item a ser desenhado (somente se a alteração for necessária), compatível com a assinatura raiz associada no momento.
        • Draw
      • Repetir (próximo item)
    • Repetir (próxima lista de comandos)
    • Estritamente quando a GPU tiver terminado com qualquer memória que não será mais usada, ela poderá ser liberada. As referências dos descritores a ele não precisarão ser excluídas se a renderização adicional que usa esses descritores não for enviada. Portanto, a renderização subsequente pode apontar para outras áreas em heaps de descritores ou descritores obsoletos podem ser substituídos por descritores válidos para reutilizar o espaço de heap do descritor.
  • Repetir (próximo quadro)

Observe que outros tipos de descritor, RTVs (exibições de destino de renderização), DSV (exibições de estêncil de profundidade), IBVs (exibições de buffer de índice), VBVs (exibições de buffer de vértice) e exibições de saída de fluxo (SOV) são gerenciados de forma diferente. O driver manipula o controle de versão do conjunto de descritores associados a cada desenho durante a gravação da lista de comandos (semelhante à forma como as associações de assinatura raiz têm controle de versão pelo hardware/driver). Isso é diferente do conteúdo de heaps de descritor visíveis do sombreador, para os quais o aplicativo deve alocar manualmente por meio do heap, pois faz referência a diferentes descritores entre os sorteios. O controle de versão do conteúdo de heap que é visível para o sombreador é deixado para o aplicativo porque permite que os aplicativos façam coisas como reutilizar descritores que não são alterados ou usar grandes conjuntos estáticos de descritores e usar a indexação de sombreador (como por ID de material) para selecionar descritores a serem usados no heap do descritor ou usar combinações de técnicas para diferentes conjuntos de descritores. O hardware não está equipado para lidar com esse tipo de flexibilidade para os outros tipos de descritor (RTV, DSV, IBV, VBV, SOV).

Alocação

No Direct3D 12, o aplicativo tem controle de baixo nível sobre o gerenciamento de memória. Em versões anteriores do Direct3D, incluindo o Direct3D 11, haveria uma alocação por recurso. No Direct3D 12, o aplicativo pode usar a API para alocar um grande bloco de memória, maior do que qualquer objeto único precisaria. Depois que isso for feito, o aplicativo poderá criar descritores para apontar para seções desse grande bloco de memória. Esse processo de decidir o que colocar onde (blocos menores dentro do bloco grande) é conhecido como subalocação. Habilitar o aplicativo para fazer isso pode gerar ganhos no uso eficiente de computação e memória. Por exemplo, a renomeação de recursos é tornada obsoleta. No lugar disso, os aplicativos podem usar cercas para determinar quando um recurso específico está sendo usado e quando não é por isolamento em execuções de lista de comandos em que a lista de comandos requer o uso desse recurso específico.

Liberando recursos

Antes que qualquer memória associada ao pipeline possa ser liberada, a GPU deve ser concluída com ela.

Aguardar a renderização de quadro é provavelmente a maneira mais grosseira de ter certeza de que a GPU foi concluída. Em uma granularidade mais fina, você pode usar cercas novamente quando um comando é registrado em uma lista de comandos da qual você deseja acompanhar a conclusão, insira uma cerca imediatamente após ela. Em seguida, você pode fazer várias operações de sincronização com a cerca. Você envia um novo trabalho (listas de comandos) que aguarda até que uma cerca especificada tenha passado na GPU, o que indica que tudo antes de ser concluído ou você pode solicitar que um evento de CPU seja gerado quando a cerca tiver passado (que o aplicativo pode estar aguardando com um thread em suspensão). No Direct3D 11, isso era EnqueueSetEvent().