Alterações do Direct3D 10
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.
As seções a seguir descrevem como o Direct3D 11 foi alterado do Direct3D 10.
Funções de retorno de chamada de driver para Kernel-Mode Services
As funções de retorno de chamada específicas do dispositivo fornecidas pelo runtime do Direct3D versão 11 na estrutura D3DDDI_DEVICECALLBACKS quando o runtime chama a função CreateDevice(D3D10) do driver de exibição no modo de usuário isolam o driver de identificadores de kernel e assinaturas de função de kernel. O runtime do Direct3D versão 11 altera a semântica de retorno de chamada e, portanto, a implementação das funções de retorno de chamada para dar suporte a um modo de operação de thread livre, enquanto os runtimes de versão anteriores do Direct3D não suportavam um modo de operação de thread livre. As regras para a operação de modo de thread livre se aplicam depois que o driver indica que ele dá suporte ao modo de thread livre (D3D11DDICAPS_FREETHREADED); caso contrário, as regras anteriores fortemente restritas se aplicam. Para obter informações sobre como o driver indica suporte para o modo de thread livre, consulte Threading e Listas de comandos. As seguintes restrições ainda existem para o Direct3D versão 11:
Somente um único thread pode funcionar em um HCONTEXT por vez. As funções de retorno de chamada existentes que atualmente usam um HCONTEXT são pfnPresentCb, pfnRenderCb, pfnEscapeCb, pfnDestroyContextCb, pfnWaitForSynchronizationObjectCb e pfnSignalSynchronizationObjectCb. Portanto, se mais de um thread chamar essas funções de retorno de chamada e usar o mesmo HCONTEXT, o driver deverá sincronizar as chamadas com as funções de retorno de chamada. Atender a esse requisito é bastante natural porque essas funções de retorno de chamada provavelmente serão chamadas apenas do thread que manipula o contexto imediato.
O driver deve chamar as seguintes funções de retorno de chamada somente durante chamadas para as seguintes funções de driver usando os mesmos threads que chamaram essas funções de driver:
pfnAllocateCb: o driver deve chamar pfnAllocateCb no thread que chamou a função CreateResource(D3D11) do driver quando recursos compartilhados são criados. As alocações não compartilhadas regulares com o dispositivo são totalmente livres.
pfnPresentCb: o driver deve chamar pfnPresentCb somente durante chamadas para a função PresentDXGI do driver.
pfnSetDisplayModeCb: o driver deve chamar pfnSetDisplayModeCb somente durante chamadas para a função SetDisplayModeDXGI do driver.
pfnRenderCb: o driver deve chamar pfnRenderCb no thread que chamou a função Flush(D3D10) do driver. Essa restrição é bastante natural devido às restrições de HCONTEXT.
A função de retorno de chamada pfnDeallocateCb merece menção especiais porque o driver não precisa chamar pfnDeallocateCb antes que o driver retorne de sua função DestroyResource(D3D10) para a maioria dos tipos de recursos. Como DestroyResource(D3D10) é uma função de thread livre, o driver deve adiar a destruição do objeto até que o driver possa garantir com eficiência que nenhuma referência de contexto imediata existente permaneça (ou seja, o driver deve chamar pfnRenderCb antes de pfnDeallocateCb). Essa restrição se aplica até mesmo a recursos compartilhados ou a qualquer outra função de retorno de chamada que usa HRESOURCE para complementar o uso de HRESOURCE com pfnAllocateCb. No entanto, essa restrição não se aplica às primárias. Para obter mais informações sobre exceções primárias, consulte Exceções primárias. Como alguns aplicativos podem exigir a aparência de destruição síncrona, o driver deve garantir que ele chame pfnDeallocateCb para quaisquer recursos compartilhados destruídos anteriormente durante uma chamada para sua função Flush(D3D10). Um driver também deve limpar todos os objetos destruídos anteriormente (somente aqueles que não param o pipeline) durante uma chamada para sua função Flush(D3D10) ; o driver deve fazer isso para garantir que o runtime chame Flush(D3D10) como um mecanismo oficial para limpar objetos destruídos adiados para esses poucos aplicativos que podem exigir esse mecanismo. Para obter mais informações sobre esse mecanismo, consulte Destruição Adiada e Liberação(D3D10). O driver também deve garantir que todos os objetos para os quais a destruição foi adiada sejam totalmente destruídos antes que a função DestroyDevice(D3D10) do driver retorne durante a limpeza.
Preterir a capacidade de permitir a modificação de DDIs Free-Threaded
Para o Direct3D versão 11, o conceito de nível de API de um dispositivo de exibição e um contexto imediato ainda são agrupados no nível DDI pelo conceito herdado de um dispositivo de exibição. Esse agrupamento de dispositivo de exibição e contexto imediato maximiza a compatibilidade com DDIs de versão anterior (como o Direct3D versão 10 DDI) e reduz a rotatividade de driver ao dar suporte a várias versões de APIs por meio de várias versões de DDIs. No entanto, esse agrupamento de dispositivo de exibição e contexto imediato resulta em uma DDI mais confusa porque os domínios de threading não são extremamente explícitos. Em vez disso, para entender os requisitos de threading de várias interfaces e as funções dentro dessas interfaces, os desenvolvedores de driver devem consultar a documentação.
Um recurso principal da API do Direct3D versão 11 é que ele permite que vários threads insiram funções de criação e destruição simultaneamente. Esse recurso é incompatível com permitir que o driver troque os ponteiros da tabela de funções para criar e destruir, como a semântica DDI do Direct3D versão 10 para funções especificadas em D3D10DDI_DEVICEFUNCS e D3D10_1DDI_DEVICEFUNCS permitidas. Portanto, depois que o driver retorna os ponteiros de função para create (CreateDevice(D3D10), o driver não deve tentar alterar o comportamento modificando esses ponteiros de função específicos quando o driver é executado sob a DDI do Direct3D versão 11 e enquanto o driver dá suporte ao threading DDI. Essa restrição se aplica a todas as funções de dispositivo que começam com pfnCreate, pfnOpen, pfnDestroy, pfnCalcPrivate e pfnCheck. Todas as demais funções de dispositivo estão fortemente associadas ao contexto imediato. Como um único thread manipula o contexto imediato de cada vez, é bem definido continuar permitindo que o driver troque as entradas da tabela de funções de contexto imediatas.
pfnRenderCb versus pfnPerformAmortizedProcessingCb
As funções de API do Direct3D versão 10 conectaram a função de retorno de chamada de kernel pfnRenderCb do runtime do Direct3D para executar o processamento amortizado (ou seja, em vez de executar determinadas operações para cada chamada de função de API, o driver executou operações amortizadas para cada tantas chamadas de função de API). A API normalmente usa essa oportunidade para cortar marcas d'água altas e liberar sua fila de destruição de objetos adiada, entre outras coisas.
Para permitir que as funções de retorno de chamada do kernel sejam o mais livres possível para o driver, a API do Direct3D não usa mais pfnRenderCb quando o driver dá suporte à DDI do Direct3D versão 11. Portanto, os drivers que dão suporte à DDI do Direct3D versão 11 devem chamar manualmente a função de retorno de chamada do kernel pfnPerformAmortizedProcessingCb do mesmo thread que inseriu a função DDI do driver depois que o driver envia um buffer de comando no contexto imediato (ou frequência semelhante). Como a operação deve cortar marcas d'água altas, seria vantajoso fazer isso antes que o driver gere preâmbulos de buffer de comando ao aproveitar as funções de retorno de chamada DDI de atualização de estado.
Além disso, o driver deve estar ciente do problema de amortização da API e tentar equilibrar com que frequência ele usa a função de retorno de chamada de kernel pfnPerformAmortizedProcessingCb . Em um extremo, o driver pode causar excesso de processamento. Por exemplo, se o driver sempre chamou pfnPerformAmortizedProcessingCb duas vezes (back-to-back), possivelmente devido ao uso de vários mecanismos, seria mais eficiente para o driver chamar pfnPerformAmortizedProcessingCb apenas uma vez. Por outro lado, o driver pode não permitir que a API direct3D faça qualquer trabalho para um quadro inteiro se o driver nunca chamou pfnPerformAmortizedProcessingCb, possivelmente devido a um design alternado de renderização de quadro. O driver não precisa chamar pfnPerformAmortizedProcessingCb com mais frequência do que naturalmente, pois isso é um exagero (por exemplo, se o driver não chamou pfnPerformAmortizedProcessingCb em um período de 1 milissegundo, deve ser hora de bombear a API). O driver é necessário para determinar apenas quais das chamadas pfnRenderCb existentes devem ser acompanhadas por pfnPerformAmortizedProcessingCb e, naturalmente, estar em conformidade com a semântica de threading da operação.
Para drivers que dão suporte a listas de comandos, esses drivers também devem chamar pfnPerformAmortizedProcessingCb de contextos adiados sempre que esses drivers ficam sem espaço (uma frequência semelhante à de cada liberação imediata de contexto). O runtime do Direct3D versão 11 espera, pelo menos, cortar suas marcas d'água altas durante essa operação. Como a semântica de threading relacionada ao pfnRenderCb foi relaxada para o Direct3D versão 11, problemas de simultaneidade devem ser resolvidos para permitir que o Direct3D versão 11 continue a conectar o pfnRenderCb, sem restrições.
Novo código de erro DDI
O código de erro D3DDDIERR_APPLICATIONERROR é criado para permitir que os drivers participem da validação em que a API do Direct3D versão 11 não participou. Anteriormente, se o driver retornasse o código de erro E_INVALIDARG, isso faria com que a API gerasse uma exceção. A presença da camada de depuração causaria a saída de depuração e indicaria que o driver havia retornado um erro interno. A saída de depuração sugere ao desenvolvedor que o driver tinha um bug. Se o driver retornar D3DDDIERR_APPLICATIONERROR, a camada de depuração determinará que o aplicativo está com falha.
Exigir retroativamente Free-Threaded DDIs CalcPrivate
O Direct3D versão 11 requer retroativamente funções de driver que começam com pfnCalcPrivate em funções DDI do Direct3D versão 10 para serem threaded livres. Esse requisito retroativo corresponde ao comportamento da DDI do Direct3D versão 11 para sempre exigir que as funções pfnCalcPrivate* e pfnCalcDeferredContextHandleSize sejam livres em thread, mesmo que o driver indique que não dá suporte ao threading DDI. Para obter mais informações sobre esse requisito retroativo, consulte Exigir retroativamente Free-Threaded DDIs CalcPrivate.
Destruição adiada e liberação D3D10
Como todas as funções destroy agora são livres de thread, o runtime do Direct3D não pode liberar um buffer de comando durante a destruição. Portanto, as funções destroy devem adiar a destruição real de um objeto até que o driver possa garantir que o thread que manipula o contexto imediato não dependa mais desse objeto para sobreviver. Cada método de contexto imediato discreto não pode usar a sincronização com eficiência para resolver esse problema de destruição; Portanto, o driver deve usar a sincronização somente quando libera um buffer de comando. O runtime do Direct3D também usa esse mesmo design quando precisa lidar com problemas semelhantes.
Devido à ratificação da destruição adiada, o runtime do Direct3D defende que os aplicativos que não podem tolerar soluções alternativas de destruição adiada usem mecanismos explícitos. Portanto, o driver deve processar sua fila de destruição adiada durante chamadas para sua função Flush (D3D10) (mesmo que o buffer de comando esteja vazio) para garantir que esses mecanismos realmente funcionem.
Esses aplicativos que exigem uma forma de destruição síncrona devem usar um dos seguintes padrões, dependendo do peso de uma destruição necessária:
Depois que o aplicativo garante que todas as dependências desse objeto sejam liberadas (ou seja, listas de comandos, exibições, middle ware e assim por diante), o aplicativo usa o seguinte padrão:
Object::Release(); // Final release ImmediateContext::ClearState(); // Remove all ImmediateContext references as well. ImmediateContext::Flush(); // Destroy all objects as quickly as possible.
O seguinte padrão é uma destruição mais pesada:
Object::Release(); // Final release ImmediateContext::ClearState(); // Remove all ImmediateContext references as well. ImmediateContext::Flush(); ImmediateContext::End( EventQuery ); while( S_FALSE == ImmediateContext::GetData( EventQuery ) ) ; ImmediateContext::Flush(); // Destroy all objects, completely.
Exceções primárias
As primárias são recursos que o runtime cria em chamadas para a função CreateResource(D3D11) do driver. O runtime cria um primário definindo o membro pPrimaryDesc da estrutura D3D11DDIARG_CREATERESOURCE como um ponteiro válido para uma estrutura DXGI_DDI_PRIMARY_DESC . As primárias têm as seguintes exceções notáveis em relação às alterações anteriores do Direct3D 10 para o Direct3D 11:
As funções CreateResource(D3D11) e DestroyResource(D3D10) do driver para primárias não são de thread livre e compartilham o domínio de threading de contexto imediato. A simultaneidade ainda pode existir com funções que começam com pfnCreate e pfnDestroy, que inclui CreateResource(D3D11) e DestroyResource(D3D10). No entanto, a simultaneidade não pode existir com CreateResource(D3D11) e DestroyResource(D3D10) para primárias. Por exemplo, o driver pode detectar que uma chamada para sua função CreateResource(D3D11) ou DestroyResource(D3D10) é para um primário e, assim, determinar que ela pode usar ou tocar com segurança a memória de contexto imediata durante a chamada de função.
A destruição primária não pode ser adiada pelo runtime do Direct3D e o driver deve chamar a função pfnDeallocateCb adequadamente dentro de uma chamada para a função DestroyResource(D3D10) do driver.