Suporte a listas de comandos
Esta seção se aplica somente ao Windows 7 e versões posteriores e ao Windows Server 2008 R2 e versões posteriores do Windows.
O tempo de execução do Direct3D usa a seguinte DDI do Direct3D 11 para listas de comandos:
A função CalcPrivateCommandListSize determina o tamanho da região privada da memória do driver de exibição do modo de usuário para uma lista de comandos.
A função CreateCommandList cria uma lista de comandos.
A função RecycleCommandList recicla uma lista de comandos.
A função RecycleCreateCommandList cria uma lista de comandos e torna um identificador DDI não utilizado anteriormente completamente válido novamente.
A função DestroyCommandList destrói uma lista de comandos.
A função RecycleDestroyCommandList notifica o driver de que a destruição leve de uma lista de comandos é necessária.
A função CommandListExecute executa uma lista de comandos.
A semântica das funções CommandListExecute, CalcPrivateCommandListSize, CreateCommandList e DestroyCommandList é principalmente autoexplicativa, com base em outras funções DDI semelhantes e na documentação da API para o DDI correspondente.
Depois que o tempo de execução do Direct3D chama com êxito a função CreateCommandList ou RecycleCreateCommandList do driver no contexto adiado especificado no membro hDeferredContext da estrutura D3D11DDIARG_CREATECOMMANDLIST para a qual o parâmetro pCreateCommandList aponta, o tempo de execução do Direct3D executa a seguinte sequência de destruição no contexto adiado:
O tempo de execução do Direct3D "fecha" todos os identificadores de objeto adiado abertos. Esses identificadores ainda podem parecer vinculados ao contexto adiado.
O tempo de execução destrói o contexto adiado.
Durante a chamada para CreateCommandList ou RecycleCreateCommandList, todas as chamadas que o driver faz para as funções de retorno de chamada DDI de atualização de estado continuam a divulgar o estado atual do contexto adiado. No entanto, durante o "fechamento" e a destruição do contexto adiado, todas as chamadas para a DDI de atualização de estado refletem que nada está vinculado (ou seja, imediatamente após a chamada para CreateCommandList ou RecycleCreateCommandList, tudo é implicitamente desvinculado).
Um contexto adiado também pode ser abandonado explicitamente pelo aplicativo ou devido a uma condição de erro pela API ou pelo driver. Para esses casos, o tempo de execução do Direct3D executa a seguinte sequência:
O tempo de execução do Direct3D chama a função AbandonCommandList do driver.
O tempo de execução desvincula os identificadores do contexto adiado um a um.
O tempo de execução "fecha" todos os identificadores de objeto adiado abertos.
O tempo de execução recicla ou destrói o contexto adiado.
A sequência anterior é semelhante à sequência de destruição de um contexto imediato. A chamada para a função AbandonCommandList do driver fornece uma oportunidade para o driver aplicar o estado no que preferir.
Durante a chamada para a função CommandListExecute do driver, ele deve fazer a transição do estado do contexto adiado para torná-lo equivalente ao estado quando o dispositivo foi criado. Essa operação também é conhecida como uma operação de limpeza de estado. Durante a chamada para a função CommandListExecute do driver, no entanto, todas as chamadas que o driver faz para as funções de retorno de chamada DDI de atualização de estado ainda refletem o estado do que foi vinculado durante a última chamada DDI para uma função do driver. Durante a próxima chamada DDI para uma função do driver, todas as chamadas que o driver faz para as funções de retorno de chamada DDI de atualização de estado mostram o estado atual como completamente vazio, o que reflete a transição de estado implícita de CommandListExecute. Esse fato difere ligeiramente da semântica e do comportamento típicos das funções de retorno de chamada DDI de atualização de estado. Se o driver tivesse chamado uma função de retorno de chamada DDI de atualização de estado durante uma chamada para uma das funções SetShader do driver, a função de retorno de chamada DDI de atualização de estado apareceria como já vinculada ao novo sombreador que está sendo vinculado. Essa divergência do comportamento de retorno de chamada DDI de atualização de estado fornece mais flexibilidade ao driver para refletir o estado antigo durante CommandListExecute.
A API do Direct3D versão 11 garante que nenhuma consulta tenha sido manipulada (ou seja, tenha QueryBegin ou QueryEnd chamada nela) pela lista de comandos e tenha sido apenas "iniciada" pelo contexto que tenta executar a lista de comandos. A API também garante que nenhuma lista de comandos que registrou o mapa de um recurso dinâmico seja executada em um contexto que tenha o mesmo recurso mapeado atualmente. Antes de um aplicativo chamar a função FinishCommandList o tempo de execução do Direct3D chama a função DDI QueryEnd e ResourceUnmap do driver em qualquer consulta ou recurso dinâmico que ainda mantenha uma consulta iniciada ou um recurso mapeado aberto porque FinishCommandList encerra implicitamente intervalos de consulta e desmapeia qualquer recurso mapeado.
Otimização para pequenas listas de comandos
Uma otimização de reciclagem de memória para listas de comandos de pequena quantidade de memória pode ser importante para reduzir a contenção entre chamadas de função DDI de listas de comandos e para reduzir a sobrecarga de processamento de chamadas necessária para listas de comandos. A sobrecarga de processamento inerente a cada lista de comandos é significativa. Essa otimização destina-se a listas de comandos em que a sobrecarga de processamento necessária para as listas de comandos domina o tempo de CPU e o espaço de memória necessários para as listas de comandos. Uma lista de comandos de pequena quantidade de memória é, por exemplo, um único comando gráfico, como CopyResource. A quantidade de memória necessária para CopyResource é de dois ponteiros. No entanto, CopyResource ainda requer a mesma quantidade de processamento de chamada de lista de comandos que uma lista de comandos de grande quantidade de memória. Quando listas de comandos de pequena quantidade de memória são geradas em alta frequência, a sobrecarga de processamento necessária para que o tempo de execução chame as funções CreateCommandList, DestroyCommandList, CreateDeferredContext e DestroyDevice(D3D10) do driver (para contexto adiado) torna-se cada vez mais importante. A memória mencionada aqui é a memória do sistema que contém estruturas de dados do driver, o que inclui a memória para identificadores DDI.
A função RecycleCommandList do driver deve notificá-lo quando os identificadores do driver saem de uso (mas ainda não forem excluídos) e quando os identificadores de driver não utilizados anteriormente são reutilizados. Essa notificação se aplica aos identificadores de lista de comandos e de contexto adiado. A única memória que o driver deve reciclar é a memória para a qual o identificador DDI aponta. Enquanto o objetivo de RecycleCommandList é reciclar a memória que está associada ao identificador, para fins de eficiência, o driver tem total flexibilidade para escolher qual memória reciclar. O driver não pode alterar o tamanho da região de memória para a qual a lista de comandos de contexto imediato manipula pontos. Esse tamanho é o valor de retorno de CalcPrivateCommandListSize. O driver também não pode alterar o tamanho da região de memória para a qual o identificador local da lista de comandos aponta. Esse tamanho é o valor de retorno de CalcDeferredContextHandleSize.
As funções DDI RecycleCreateCommandList e RecycleCreateDeferredContext do driver devem retornar códigos de erro de falta de memória como valores E_OUTOFMEMORY HRESULT. Essas funções não fornecem esses códigos de erro por meio de chamadas para a função pfnSetErrorCb. Esse requisito de driver impede que o tempo de execução tenha que usar a sincronização em todo o dispositivo para observar erros de contexto imediatos dessas funções de driver de tipo de criação. Observar esses erros seria uma fonte de contenção catastrófica para listas de comandos de pequena quantidade de memória.
As distinções entre as funções RecycleDestroyCommandList, RecycleCommandList e RecycleCreateCommandList do driver são importantes. Entre seus recursos estão:
RecycleDestroyCommandList
O tempo de execução chama a função RecycleDestroyCommandList do driver para notificá-lo de que a destruição leve é necessária. Ou seja, o driver ainda não deve desalocar a memória para o identificador de lista de comandos DDI. A função RecycleDestroyCommandList do driver é encadeada livremente, assim como a função DestroyCommandList do driver.
RecycleCommandList
A função RecycleCommandList do driver informa ao driver que o tempo de execução integrou um identificador de lista de comandos de volta ao cache de contexto adiado. Em seguida, a função fornece ao driver uma oportunidade de integrar a memória associada à lista de comandos de volta ao cache de contexto adiado. O tempo de execução chama a função RecycleCommandList do driver a partir do thread de contexto adiado. A função DDI RecycleCommandList reduz a necessidade de o driver executar sua própria sincronização.
RecycleCreateCommandList
O tempo de execução chama a função RecycleCreateCommandList do driver para tornar um identificador DDI não utilizado anteriormente completamente válido novamente.
Essas funções DDI de reciclagem fornecem oportunidades de otimização para ajudar a reciclar recursos para listas de comandos de pequena quantidade de memória. O pseudocódigo a seguir mostra a implementação do tempo de execução por meio do fluxo de chamadas de função da API para a DDI:
::FinishCommandList()
{
// Empty InterlockedSList, integrating into the cache
Loop { DC::pfnRecycleCommandList }
If (Previously Destroyed CommandList Available)
{ IC::pfnRecycleCreateCommandList }
else
{
IC::pfnCalcPrivateCommandListSize
IC::pfnCreateCommandList
IC::pfnCalcDeferredContextHandleSize(D3D11DDI_HT_COMMANDLIST)
}
Loop { DC::pfnDestroy* (context-local handle destroy) }
IC::pfnRecycleCreateDeferredContext
}
...
Sporadic: DC::pfnCreate* (context-local open during first-bind per CommandList)
CommandList::Destroy()
{
// If DC still alive, almost always recycle:
If (DC still alive)
{ IC::pfnRecycleDestroyCommandList }
Else
{ IC::pfnDestroyCommandList }
// Add to InterlockedSList
}
O diagrama de estado a seguir mostra a validade de um identificador de lista de comandos DDI de contexto imediato. O estado verde representa um identificador que pode ser usado com CommandListExecute.