Partilhar via


Copiando e acessando dados de recursos (Direct3D 10)

Não é mais necessário pensar em recursos como sendo criados na memória de vídeo ou na memória do sistema. Se o runtime deve gerenciar a memória ou não. Graças à arquitetura do novo WDDM (Windows Display Driver Model), os aplicativos agora criam recursos Direct3D 10 com diferentes uso sinalizadores para indicar como o aplicativo pretende usar os dados do recurso. O novo modelo de driver virtualiza a memória usada pelos recursos; Em seguida, torna-se responsabilidade do sistema operacional / driver / gerenciador de memória para colocar recursos na área de memória mais eficiente possível, dado o uso esperado.

O caso padrão é que os recursos estejam disponíveis para a GPU. Claro, dito isso, há momentos em que os dados do recurso precisam estar disponíveis para a CPU. Copiar dados de recursos para que o processador apropriado possa acessá-los sem afetar o desempenho requer algum conhecimento de como os métodos da API funcionam.

Copiando dados de recursos

Os recursos são criados na memória quando o Direct3D executa uma chamada Create. Eles podem ser criados na memória de vídeo, memória do sistema, ou qualquer outro tipo de memória. Como o modelo de driver WDDM virtualiza essa memória, os aplicativos não precisam mais controlar em que tipo de recursos de memória são criados.

Idealmente, todos os recursos estariam localizados na memória de vídeo para que a GPU possa ter acesso imediato a eles. No entanto, às vezes é necessário que a CPU leia os dados do recurso ou que a GPU acesse os dados do recurso para os quais a CPU gravou. O Direct3D 10 lida com esses diferentes cenários solicitando que o aplicativo especifique um uso e, em seguida, oferece vários métodos para copiar dados de recursos quando necessário.

Dependendo de como o recurso foi criado, nem sempre é possível acessar diretamente os dados subjacentes. Isso pode significar que os dados do recurso devem ser copiados do recurso de origem para outro recurso acessível pelo processador apropriado. Em termos de Direct3D 10, os recursos padrão podem ser acessados diretamente pela GPU, os recursos dinâmicos e de preparo podem ser acessados diretamente pela CPU.

Uma vez que um recurso tenha sido criado, o seu de uso não pode ser alterado. Em vez disso, copie o conteúdo de um recurso para outro recurso que foi criado com um uso diferente. O Direct3D 10 fornece essa funcionalidade com três métodos diferentes. Os dois primeiros métodos( ID3D10Device::CopyResource e ID3D10Device::CopySubresourceRegion) são projetados para copiar dados de recursos de um recurso para outro. O terceiro método (ID3D10Device::UpdateSubresource) foi projetado para copiar dados da memória para um recurso.

Existem dois tipos principais de recursos: mapeáveis e não mapeáveis. Os recursos criados com usos dinâmicos ou temporários são mapeáveis, enquanto os recursos criados com usos padrão ou imutáveis não são mapeáveis.

A cópia de dados entre recursos não mapeáveis é muito rápida porque este é o caso mais comum e foi otimizado para ter um bom desempenho. Como esses recursos não são diretamente acessíveis pela CPU, eles são otimizados para que a GPU possa manipulá-los rapidamente.

Copiar dados entre recursos mapeáveis é mais problemático porque o desempenho dependerá do uso com o qual o recurso foi criado. Por exemplo, a GPU pode ler um recurso dinâmico com bastante rapidez, mas não pode gravar nesses recursos, e a GPU não pode ler ou gravar diretamente em recursos de estágio.

As aplicações que pretendem copiar dados de um recurso com utilização padrão para um recurso com utilização temporária (para permitir que a CPU leia os dados, ou seja, o problema de leitura da GPU) devem fazê-lo com cuidado. Consulte Acesso a dados de recursos para obter mais detalhes sobre este último caso.

Acesso a dados de recursos

O acesso a um recurso requer o mapeamento do mesmo; mapeamento essencialmente significa que o aplicativo está tentando dar à CPU acesso à memória. O processo de mapeamento de um recurso para que a CPU possa acessar a memória subjacente pode causar alguns gargalos de desempenho e, por esse motivo, deve-se ter cuidado sobre como e quando executar essa tarefa.

O desempenho pode ser interrompido se o aplicativo tentar mapear um recurso no momento errado. Se o aplicativo tentar acessar os resultados de uma operação antes que ela seja concluída, ocorrerá uma interrupção do pipeline.

Executar uma operação de mapa no momento errado pode potencialmente causar uma queda severa no desempenho, forçando a GPU e a CPU a sincronizarem entre si. Essa sincronização ocorrerá se o aplicativo quiser acessar um recurso antes que a GPU termine de copiá-lo em um recurso que a CPU possa mapear.

A CPU só consegue ler recursos criados com o sinalizador D3D10_USAGE_STAGING. Como os recursos criados com esse sinalizador não podem ser definidos como saídas do pipeline, se a CPU quiser ler os dados em um recurso gerado pela GPU, os dados deverão ser copiados para um recurso criado com o sinalizador de preparo. O aplicativo pode fazer isso usando o ID3D10Device::CopyResource ou ID3D10Device::CopySubresourceRegion métodos para copiar o conteúdo de um recurso para outro. O aplicativo pode então obter acesso a esse recurso chamando o método Map apropriado. Quando o acesso ao recurso não for mais necessário, o aplicativo deverá chamar o método Unmap correspondente. Por exemplo, ID3D10Texture2D::Map e ID3D10Texture2D::Unmap. Os diferentes métodos Map retornam alguns valores específicos dependendo dos sinalizadores de entrada. Consulte a secção de Comentários do Mapa para obter detalhes.

Observação

Quando a aplicação chama o método Map, recebe um ponteiro para os dados do recurso a aceder. O tempo de execução garante que o ponteiro tenha um alinhamento específico, dependendo do nível de recurso . Para D3D_FEATURE_LEVEL_10_0 ou superior, o ponteiro é alinhado a 16 bytes. Para menos de D3D_FEATURE_LEVEL_10_0, o ponteiro é alinhado a 4 bytes. O alinhamento de 16 bytes permite que o aplicativo execute operações SSEotimizadas nos dados nativamente, sem realinhamento ou cópia.

 

Considerações sobre desempenho

É melhor pensar em um PC como uma máquina funcionando como uma arquitetura paralela com dois tipos principais de processadores: uma ou mais CPUs e uma ou mais GPUs. Como em qualquer arquitetura paralela, o melhor desempenho é alcançado quando cada processador é agendado com tarefas suficientes para evitar que fique ocioso e quando o trabalho de um processador não está esperando o trabalho de outro.

O pior cenário para o paralelismo GPU/CPU é a necessidade de forçar um processador a esperar pelos resultados do trabalho feito por outro. O Direct3D 10 tenta remover esse custo tornando os métodos ID3D10Device::CopyResource e ID3D10Device::CopySubresourceRegion assíncronos; a cópia não foi necessariamente executada no momento em que o método retorna. O benefício disto é que a aplicação não paga o custo de desempenho associado à efetiva cópia dos dados até que a CPU aceda aos dados, que é quando o Map é chamado. Se o método Map for chamado depois que os dados tiverem sido realmente copiados, nenhuma perda de desempenho ocorrerá. Por outro lado, se o método Map for chamado antes que os dados tenham sido copiados, ocorrerá uma interrupção do pipeline.

As chamadas assíncronas no Direct3D 10 (que são a grande maioria dos métodos, e especialmente as chamadas de renderização) são armazenadas no que é chamado de buffer de comando. Esse buffer é interno ao driver gráfico e é usado para chamadas em lote para o hardware subjacente para que a dispendiosa mudança do modo de usuário para o modo kernel no Microsoft Windows ocorra o mais raramente possível.

O buffer de comando é liberado, causando assim uma mudança de modo usuário/kernel, em uma das quatro situações, que são as seguintes.

  1. Presente é chamado.
  2. ID3D10Device::Flush é chamado.
  3. O buffer de comandos está cheio; seu tamanho é dinâmico e é controlado pelo sistema operacional e pelo driver gráfico.
  4. A CPU requer acesso aos resultados de um comando aguardando para ser executado no buffer de comandos.

Das quatro situações acima, a número quatro é a mais crítica para o desempenho. Se o aplicativo emitir uma chamada ID3D10Device::CopyResource ou ID3D10Device::CopySubresourceRegion, essa chamada será enfileirada no buffer de comandos. Se a aplicação tentar mapear o recurso de transição que foi o alvo da chamada de cópia antes que o buffer de comandos tenha sido esvaziado, irá ocorrer uma paragem de canalização porque não só a chamada ao método Copy precisa de ser executada, mas todos os outros comandos armazenados no buffer de comandos também devem ser executados. Isso fará com que a GPU e a CPU sincronizem porque a CPU estará aguardando para acessar o recurso de preparo enquanto a GPU está esvaziando o buffer de comandos e, finalmente, preenchendo o recurso de que a CPU precisa. Quando a GPU terminar a cópia, a CPU começará a acessar o recurso de preparação, mas durante esse tempo, a GPU ficará ociosa.

Fazer isso com frequência em tempo de execução prejudicará gravemente o desempenho. Por esse motivo, o mapeamento de recursos criados com uso padrão deve ser feito com cuidado. A aplicação precisa esperar tempo suficiente para que o buffer de comandos seja esvaziado e, assim, garantir que todos esses comandos tenham terminado de executar antes de tentar mapear o recurso de estágio correspondente. Quanto tempo deve esperar a aplicação? Pelo menos dois quadros, porque isso permitirá que o paralelismo entre a(s) CPU(s) e a GPU seja aproveitado ao máximo. A maneira como a GPU funciona é que, enquanto o aplicativo está processando o quadro N enviando chamadas para o buffer de comando, a GPU está ocupada executando as chamadas do quadro anterior, N-1.

Portanto, se um aplicativo quiser mapear um recurso que se origina na memória de vídeo e chama ID3D10Device::CopyResource ou ID3D10Device::CopySubresourceRegion no quadro N, essa chamada começará a ser executada no quadro N+1, quando o aplicativo estiver enviando chamadas para o próximo quadro. A cópia deve ser concluída quando a aplicação processa o quadro N+2.

Moldura Estado da GPU/CPU
N
  • Problemas de CPU renderizam chamadas para o quadro atual.
N+1
  • A GPU está a executar chamadas enviadas pela CPU durante o frame N.
  • Problemas na CPU afetam a execução de chamadas de renderização para o fotograma atual.
N+2
  • GPU terminou de executar chamadas enviadas da CPU durante o quadro N. Resultados prontos.
  • GPU executando chamadas enviadas da CPU durante o quadro N+1.
  • Problemas de CPU processam chamadas para o frame atual.
N+3
  • A GPU terminou de executar chamadas enviadas da CPU durante o quadro N+1. Resultados disponíveis.
  • GPU executando chamadas enviadas da CPU durante o quadro N+2.
  • Problemas de CPU renderizam chamadas para o quadro atual.
N+4 ...

 

Recursos (Direct3D 10)