Usando identificadores DDI Context-Local

Esta seção se aplica somente ao Windows 7 e posterior e ao Windows Server 2008 R2 e versões posteriores do sistema operacional Windows.

Cada objeto (por exemplo, recurso, sombreador e assim por diante) tem identificadores DDI locais de contexto.

Suponha que um objeto seja usado com três contextos adiados. Nessa situação, quatro identificadores referem-se ao mesmo objeto (um identificador para cada contexto adiado e outro identificador para o contexto imediato). Como cada contexto pode ser manipulado por um thread simultaneamente, um identificador local de contexto garante que vários threads de CPU não concorram sobre memória semelhante (intencionalmente ou não). Identificadores locais de contexto também são intuitivos porque o driver provavelmente deve modificar grande parte desses dados que são logicamente associados por contexto de qualquer maneira (por exemplo, o objeto pode ser associado pelo contexto e assim por diante).

Ainda há a distinção de um identificador de contexto imediato versus um identificador de contexto adiado. Em particular, o identificador de contexto imediato é garantido como o primeiro identificador alocado e o último identificador que é destruído. O identificador de contexto imediato correspondente é fornecido durante a "abertura" de cada identificador de contexto adiado para vinculá-los juntos. Atualmente, não há nenhum conceito de um objeto ter um identificador DDI por dispositivo (ou seja, um identificador que é criado antes e destruído após o identificador de contexto imediato e seria referenciado apenas em ordem pela criação do identificador de contexto).

Alguns identificadores têm relações de dependência com outros identificadores (por exemplo, as exibições têm uma dependência do recurso correspondente). A garantia de ordenação de criação e destruição que existe para o contexto imediato também é estendida para identificadores de contexto adiados (ou seja, o runtime cria um identificador de recurso local de contexto antes que o runtime crie qualquer identificador de exibição local de contexto para esse recurso e o runtime destrói um identificador de recurso local de contexto depois que o runtime destrói todos os identificadores de exibição local de contexto para esse recurso). Quando o runtime cria um identificador local de contexto, o runtime também fornece os identificadores de dependência local de contexto correspondentes.

Organização de dados do driver

Há algumas preocupações sobre a organização de dados de driver que precisam de atenção. Assim como o Direct3D versão 10, a localidade adequada dos dados pode reduzir as falhas de cache entre a API e o driver. A localidade adequada dos dados também pode impedir a desativação do cache, que ocorre quando várias partes de dados acessados com frequência resolve ao mesmo índice de cache e esgotam a associação do cache. A DDI foi projetada desde a versão 10 do Direct3D para ajudar a evitar que esses problemas se manifestem pelo driver informando à API quanta memória o driver precisa para satisfazer um identificador e a API atribuindo o valor do identificador. No entanto, novas preocupações relacionadas a threads afetam o design de DDI no período de tempo do Direct3D versão 11.

Naturalmente, os identificadores locais de contexto fornecem uma maneira de associar dados de objeto por contexto, o que evita problemas de contenção entre threads. No entanto, como esses dados são replicados para cada contexto adiado, o tamanho desses dados é uma grande preocupação. Isso fornece a racionalização natural para compartilhar dados somente leitura entre o identificador de contexto imediato e os identificadores de contexto adiados. Durante a criação de identificadores de contexto adiados, o identificador de contexto imediato é fornecido para estabelecer a conexão entre identificadores. No entanto, todos os dados localizados fora dos identificadores de contexto adiados obtêm benefícios de localidade com dados de API e o nível adicional de indireção para dados somente leitura impede que os benefícios de localidade se estendam para os dados somente leitura. Alguns dados somente leitura podem ser replicados em cada região do identificador de contexto se os benefícios de localidade justificarem a duplicação de dados. No entanto, a memória que faz backup de cada identificador de contexto adiado deve ser considerada como um prêmio que pode valer a pena realocar dados que não sejam diferentes do identificador se esses dados forem relativamente grandes e não forem acessados com tanta frequência quanto outros dados. O ideal é que o tipo de dados associado a cada identificador de contexto adiado seja todos os dados de alta frequência de qualquer maneira; portanto, os dados não seriam grandes o suficiente para considerar a realocação necessária. Naturalmente, o driver deve equilibrar essas motivações conflitantes.

Para tornar o design de dados do driver compatível com eficiência com o Direct3D versão 10, mas não divergente na implementação, os dados somente leitura devem ser localizados contíguos (mas ainda separados de e depois) dos dados imediatos do identificador de contexto. Se o driver usar esse design, o driver deverá estar ciente de que o preenchimento de linha de cache é necessário entre os dados imediatos do identificador de contexto e os dados somente leitura. Como um thread pode manipular cada contexto manipula dados com frequência (se não simultaneamente), as penalidades de compartilhamento falso ocorrem entre os dados imediatos do identificador de contexto e os dados de identificador de contexto adiados se o preenchimento de linha de cache não for usado. O design do driver deve estar ciente das penalidades de compartilhamento falso que se manifestam se os ponteiros forem estabelecidos e percorridos regularmente entre as regiões de memória do identificador de contexto.

O runtime do Direct3D usa o seguinte DDI do Direct3D 11 para identificadores locais de contexto adiado:

Para que o runtime do Direct3D recupere o tamanho do identificador de contexto adiado exigido pelo driver, as funções DDI anteriores devem ser usadas. Imediatamente após a criação de um objeto para o contexto imediato, o runtime chama CalcDeferredContextHandleSize para consultar o driver quanto à quantidade de espaço de armazenamento que o driver requer para satisfazer identificadores de contexto adiados para esse objeto. No entanto, a API do Direct3D deve ajustar seu alocador de memória CLS determinando quantos tamanhos de identificador exclusivos e seus valores são acessados; O runtime chama a função CheckDeferredContextHandleSizes do driver para obter essas informações. Portanto, durante a instanciação do dispositivo, a API solicita uma matriz de tamanhos de identificador de contexto adiados por sondagem dupla. A primeira sondagem é solicitar quantos tamanhos são retornados, enquanto a segunda sondagem passa em uma matriz para recuperar o valor de cada tamanho. O driver deve indicar a quantidade de memória necessária para atender a um identificador junto com o tipo de identificador. O driver pode retornar vários tamanhos associados a um tipo de identificador específico. No entanto, é indefinido que o driver retorne um valor de CalcDeferredContextHandleSize que também não foi retornado correspondentemente na matriz CheckDeferredContextHandleSizes .

Quanto à criação dos identificadores DDI, os métodos create no contexto adiado são usados. Por exemplo, examine as funções CreateBlendState(D3D10_1) e DestroyBlendState . O HDEVICE naturalmente aponta para o contexto adiado apropriado (em comparação com o contexto imediato); outros ponteiros de estrutura CONST são NULL (supondo que o objeto não tenha dependências); e, o identificador D3D10DDI_HRT* é um identificador D3D10DDI_H* para o objeto de contexto imediato correspondente.

Para objetos que têm dependências (por exemplo, exibições têm uma relação de dependência em seu recurso correspondente), o ponteiro de estrutura que fornece o identificador de dependência não é NULL. No entanto, o único membro válido da estrutura é o identificador de dependência; enquanto que o restante dos membros são preenchidos com zero. Por exemplo, o ponteiro D3D11DDIARG_CREATESHADERRESOURCEVIEW em uma chamada para a função CreateShaderResourceView(D3D11) do driver não será NULL quando o runtime chamar essa função em um contexto adiado. Nesta chamada CreateShaderResourceView(D3D11), o runtime atribui o identificador local de contexto apropriado para o recurso ao membro hDrvResource do D3D11DDIARG_CREATESHADERRESOURCEVIEW. O resto dos membros do D3D11DDIARG_CREATESHADERRESOURCEVIEW, no entanto, estão cheios de zero.

O código de exemplo a seguir mostra como o runtime do Direct3D converte a solicitação de criação de um aplicativo e o primeiro uso do contexto adiado para chamadas para o driver de exibição do modo de usuário para criar contextos imediatos versus adiados. A chamada do aplicativo para ID3D11Device::CreateTexture2D inicia o código de runtime na seção "Criação de Recursos" a seguir. A chamada do aplicativo para ID3D11Device::CopyResource inicia o código de runtime na seção "Uso adiado do recurso de contexto".

// Device Create
 IC::pfnCheckDeferredContextHandleSizes( hIC, &u, NULL );
pArray = malloc( u * ... );
IC::pfnCheckDeferredContextHandleSizes( hIC, &u, pArray );

// Resource Create
 s = IC::pfnCalcPrivateResourceSize( hIC, &Args );
pICRHandle = malloc( s );
 IC::pfnCreateResource( hIC, &Args, pICRHandle, hRTResource );
 s2 = IC::pfnCalcDeferredContextHandleSize( hIC, D3D10DDI_HT_RESOURCE, pICRHandle );

// Deferred Context Resource Usage
pDCRHandle = malloc( s2 );
 DC::pfnCreateResource( hDC, NULL, pDCRHandle, pICRHandle );

Problemas com pfnSetErrorCb

Nenhuma das funções de criação retorna um código de erro, o que teria sido ideal para o modelo de threading do Direct3D versão 11. Todas as funções de criação usam pfnSetErrorCb para recuperar códigos de erro do driver. Para maximizar a compatibilidade com o modelo de driver direct3D versão 10, novas funções de criação de DDI que retornam códigos de erro não foram introduzidas. Em vez disso, o driver deve continuar a usar o dispositivo unificado/contexto imediato D3D10DDI_HRTCORELAYER identificador com pfnSetErrorCb durante as funções de criação. Quando o driver dá suporte a listas de comandos, o driver deve usar o pfnSetErrorCb apropriado associado ao contexto correspondente. Ou seja, os erros de contexto adiados devem ir para a chamada de contexto adiada específica para pfnSetErrorCb com o identificador correspondente e assim por diante.

Contextos adiados podem retornar E_OUTOFMEMORY por meio de uma chamada para pfnSetErrorCb de funções DDI que anteriormente só permitiam D3DDDIERR_DEVICEREMOVED (como Draw, SetBlendState e assim por diante), uma vez que as demandas de memória de contexto adiadas crescem perpetuamente a cada chamada para uma função DDI. A API do Direct3D dispara uma remoção de contexto local para ajudar o driver com esse caso de falha, que efetivamente descarta a lista de comandos parcialmente criada. O aplicativo continua a determinar que está gravando uma lista de comandos; no entanto, quando o aplicativo eventualmente chama a função FinishCommandList , FinishCommandList retorna um código de falha de E_OUTOFMEMORY.