Consultas (Direct3D 9)
Existem vários tipos de consultas que são projetadas para consultar o status dos recursos. O status de um determinado recurso inclui o status da unidade de processamento gráfico (GPU), o status do driver ou o status do tempo de execução. Para entender a diferença entre os diferentes tipos de consulta, você precisa entender os estados da consulta. O diagrama de transição de estado a seguir explica cada um dos estados de consulta.
O diagrama mostra três estados, cada um definido por círculos. Cada uma das linhas sólidas são eventos controlados por aplicativo que causam uma transição de estado. A linha tracejada é um evento controlado por recursos que alterna uma consulta do estado emitido para o estado sinalizado. Cada um destes estados tem uma finalidade diferente:
- O estado sinalizado é como um estado ocioso. O objeto de consulta foi gerado e está aguardando que o aplicativo emita a consulta. Depois que uma consulta for concluída e transferida de volta para o estado sinalizado, a resposta à consulta poderá ser recuperada.
- O estado do edifício é como uma área de preparação para uma consulta. A partir do estado de construção, uma consulta foi emitida (chamando D3DISSUE_BEGIN), mas ainda não transitou para o estado emitido. Quando um aplicativo emite um fim de consulta (chamando D3DISSUE_END), a consulta transita para o estado emitido.
- O estado emitido significa que o recurso que está sendo consultado tem controle da consulta. Quando o recurso termina seu trabalho, o recurso faz a transição da máquina de estado para o estado sinalizado. Durante o estado emitido, o aplicativo deve sondar para detetar a transição para o estado sinalizado. Quando a transição para o estado sinalizado ocorre, GetData retorna o resultado da consulta (por meio de um argumento) para o aplicativo.
A tabela a seguir lista os tipos de consulta disponíveis.
Tipo de consulta | Evento de Emissão | Buffer GetData | Tempo de execução | Início implícito da consulta |
---|---|---|---|---|
HORÁRIOS DE LARGURA DE BANDA | D3DISSUE_BEGIN, D3DISSUE_END | D3DDEVINFO_D3D9BANDWIDTHTIMINGS | Varejo/Depuração | N/A |
CACHEUTILIZAÇÃO | D3DISSUE_BEGIN, D3DISSUE_END | D3DDEVINFO_D3D9CACHEUTILIZATION | Varejo/Depuração | N/A |
EVENTO | D3DISSUE_END | BOOL | Varejo/Depuração | CreateDevice |
INTERFACETIMINGS | D3DISSUE_BEGIN, D3DISSUE_END | D3DDEVINFO_D3D9INTERFACETIMINGS | Varejo/Depuração | N/A |
OCLUSÃO | D3DISSUE_BEGIN, D3DISSUE_END | DWORD | Varejo/Depuração | N/A |
HORÁRIOS DAS CONDUTAS | D3DISSUE_BEGIN, D3DISSUE_END | D3DDEVINFO_D3D9PIPELINETIMINGS | Varejo/Depuração | N/A |
GESTOR DE RECURSOS | D3DISSUE_END | D3DDEVINFO_ResourceManager | Apenas depuração | o momento |
CARIMBO DE DATA/HORA | D3DISSUE_END | UINT64 | Varejo/Depuração | N/A |
TIMESTAMPDISJOINT | D3DISSUE_BEGIN, D3DISSUE_END | BOOL | Varejo/Depuração | N/A |
TIMESTAMPFREQ | D3DISSUE_END | UINT64 | Varejo/Depuração | N/A |
VCACHE | D3DISSUE_END | D3DDEVINFO_VCACHE | Varejo/Depuração | CreateDevice |
VERTEXSTATS | D3DISSUE_END | D3DDEVINFO_D3DVERTEXSTATS | Apenas depuração | o momento |
VERTEXTIMINGS | D3DISSUE_BEGIN, D3DISSUE_END | D3DDEVINFO_D3D9STAGETIMINGS | Varejo/Depuração | N/A |
Algumas das consultas exigem um evento de início e fim, enquanto outras exigem apenas um evento de fim. As consultas que exigem apenas um evento final começam quando ocorre outro evento implícito (listado na tabela). Todas as consultas retornam uma resposta, exceto a consulta de evento cuja resposta é sempre TRUE. Um aplicativo usa o estado da consulta ou o código de retorno de GetData.
Criar uma consulta
Antes de criar uma consulta, você pode verificar se o tempo de execução oferece suporte a consultas chamando CreateQuery com um ponteiro NULL como este:
IDirect3DQuery9* pEventQuery;
// Create a device pointer m_pd3dDevice
// Create a query object
HRESULT hr = m_pd3dDevice->CreateQuery(D3DQUERYTYPE_EVENT, NULL);
Esse método retorna um código de êxito se uma consulta puder ser criada; caso contrário, ele retorna um código de erro. Quando CreateQuery for bem-sucedido, você poderá criar um objeto de consulta como este:
IDirect3DQuery9* pEventQuery;
m_pd3dDevice->CreateQuery(D3DQUERYTYPE_EVENT, &pEventQuery);
Se essa chamada for bem-sucedida, um objeto de consulta será criado. A consulta está essencialmente ociosa no estado sinalizado (com uma resposta não inicializada) aguardando para ser emitida. Quando terminar a consulta, libere-a como qualquer outra interface.
Emitir uma consulta
Um aplicativo altera um estado de consulta emitindo uma consulta. Aqui está um exemplo de emissão de uma consulta:
IDirect3DQuery9* pEventQuery;
m_pD3DDevice->CreateQuery(D3DQUERYTYPE_EVENT, &pEventQuery);
// Issue a Begin event
pEventQuery->Issue(D3DISSUE_BEGIN);
or
// Issue an End event
pEventQuery->Issue(D3DISSUE_END);
Uma consulta no estado sinalizado transitará assim quando emitida:
Tipo de Problema | Transições de consulta para o . . . |
---|---|
D3DISSUE_BEGIN | Estado do edifício. |
D3DISSUE_END | Estado emitido. |
Uma consulta no estado de construção transitará desta forma quando emitida:
Tipo de Problema | Transições de consulta para o . . . |
---|---|
D3DISSUE_BEGIN | (Sem transição, permanece no estado de construção. Reinicia o colchete de consulta.) |
D3DISSUE_END | Estado emitido. |
Uma consulta no estado emitido transitará desta forma quando emitida:
Tipo de Problema | Transições de consulta para o . . . |
---|---|
D3DISSUE_BEGIN | Estado de construção e reinicia o colchete de consulta. |
D3DISSUE_END | Estado emitido após abandonar a consulta existente. |
Verifique o estado da consulta e obtenha a resposta à consulta
GetData faz duas coisas:
- Retorna o estado da consulta no código de retorno.
- Retorna a resposta à consulta em pData.
De cada um dos três estados de consulta, aqui estão os códigos de retorno GetData:
Estado da consulta | Código de retorno GetData |
---|---|
Sinalizado | S_OK |
Edifício | Código de erro |
Emitido | S_FALSE |
Por exemplo, quando uma consulta está no estado emitido e a resposta à consulta não está disponível, GetData retorna S_FALSE. Quando o recurso termina seu trabalho e o aplicativo emite um fim de consulta, o recurso faz a transição da consulta para o estado sinalizado. A partir do estado sinalizado, GetData retorna S_OK o que significa que a resposta à consulta também é retornada em pData. Por exemplo, aqui está a sequência de eventos para retornar o número de pixels (ou amostras quando a multiamostragem está ativada) desenhados em uma sequência de renderização:
- Crie a consulta.
- Emita um evento start.
- Desenhe alguma coisa.
- Emita um evento final.
Segue-se a sequência de código correspondente:
IDirect3DQuery9* pOcclusionQuery;
DWORD numberOfSamplesDrawn;
m_pD3DDevice->CreateQuery(D3DQUERYTYPE_OCCLUSION, &pOcclusionQuery);
// Add an end marker to the command buffer queue.
pOcclusionQuery->Issue(D3DISSUE_BEGIN);
// API render loop
...
Draw(...)
...
// Add an end marker to the command buffer queue.
pOcclusionQuery->Issue(D3DISSUE_END);
// Force the driver to execute the commands from the command buffer.
// Empty the command buffer and wait until the GPU is idle.
while(S_FALSE == pOcclusionQuery->GetData( &numberOfSamplesDrawn,
sizeof(DWORD), D3DGETDATA_FLUSH ))
;
// To get the number of pixels drawn when multisampling is enabled,
// divide numberOfSamplesDrawn by the sample count of the render target.
Estas linhas de código fazem várias coisas:
- Ligue para GetData para retornar o número de pixels/amostras desenhadas.
- Especifique D3DGETDATA_FLUSH para permitir que o recurso faça a transição da consulta para o estado sinalizado.
- Sonde o recurso de consulta chamando GetData de um loop. Contanto que GetData retorne S_FALSE, isso significa que o recurso ainda não retornou a resposta.
O valor de retorno de GetData essencialmente informa em que estado a consulta está. Os valores possíveis são S_OK, S_FALSE e um erro. Não chame GetData em uma consulta que esteja no estado de construção.
- S_OK significa que o recurso (GPU ou driver, ou tempo de execução) foi concluído. A consulta está retornando ao estado sinalizado. A resposta (se houver) está sendo retornada por GetData.
- S_FALSE significa que o recurso (GPU ou driver, ou tempo de execução) ainda não pode retornar uma resposta. Isso pode ser porque a GPU não está concluída ou ainda não viu o trabalho.
- Um erro significa que a consulta gerou um erro do qual não pode se recuperar. Este pode ser o caso se o dispositivo for perdido durante uma consulta. Uma vez que uma consulta gerou um erro (diferente de S_FALSE), a consulta deve ser recriada, o que reiniciará a sequência de consulta a partir do estado sinalizado.
Em vez de especificar D3DGETDATA_FLUSH, que fornece mais informações de data up-to, você pode fornecer zero, que é uma verificação mais leve se a consulta estiver no estado emitido. O fornecimento de zero fará com que GetData não libere o buffer de comandos. Por esse motivo, deve-se tomar cuidado para evitar loops infinitos (consulte GetData para obter detalhes). Como o tempo de execução enfileira o trabalho no buffer de comandos, D3DGETDATA_FLUSH é um mecanismo para liberar o buffer de comandos para o driver (e, portanto, a GPU; consulte Criação de perfil precisa de chamadas de API do Direct3D (Direct3D 9)). Durante a liberação do buffer de comando, uma consulta pode fazer a transição para o estado sinalizado.
Exemplo: uma consulta de evento
Uma consulta de evento não suporta um evento start.
- Crie a consulta.
- Emita um evento final.
- Sondagem até que a GPU esteja ociosa.
- Emita um evento final.
IDirect3DQuery9* pEventQuery = NULL;
m_pD3DDevice->CreateQuery(D3DQUERYTYPE_EVENT, &pEventQuery);
// Add an end marker to the command buffer queue.
pEventQuery->Issue(D3DISSUE_END);
// Empty the command buffer and wait until the GPU is idle.
while(S_FALSE == pEventQuery->GetData( NULL, 0, D3DGETDATA_FLUSH ))
;
... // API calls
// Add an end marker to the command buffer queue.
pEventQuery->Issue(D3DISSUE_END);
// Force the driver to execute the commands from the command buffer.
// Empty the command buffer and wait until the GPU is idle.
while(S_FALSE == pEventQuery->GetData( NULL, 0, D3DGETDATA_FLUSH ))
;
Esta é a sequência de comandos que uma consulta de evento usa para criar o perfil de chamadas de interface de programação de aplicativo (API) (consulte Definindo com precisão o perfil de chamadas de API do Direct3D (Direct3D 9)). Essa sequência usa marcadores para ajudar a controlar a quantidade de trabalho no buffer de comandos.
Observe que os aplicativos devem prestar especial atenção ao grande custo associado à liberação do buffer de comandos, pois isso faz com que o sistema operacional mude para o modo kernel, incorrendo assim em uma penalidade de desempenho considerável. Os aplicativos também devem estar cientes do desperdício de ciclos de CPU aguardando a conclusão das consultas.
As consultas são uma otimização a ser usada durante a renderização para aumentar o desempenho. Portanto, não é benéfico gastar tempo esperando que uma consulta seja concluída. Se uma consulta for emitida e se os resultados ainda não estiverem prontos no momento em que o aplicativo verificar se eles estiverem prontos, a tentativa de otimização não foi bem-sucedida e a renderização deve continuar normalmente.
O exemplo clássico disso é durante o Abate de Oclusão. Em vez do enquanto loop acima, um aplicativo usando consultas pode implementar o abate de oclusão para verificar se uma consulta foi concluída no momento em que precisa do resultado. Se a consulta não tiver sido concluída, continue (como o pior cenário) como se o objeto que está sendo testado não estivesse ocluído (ou seja, fosse visível) e renderize-o. O código seria semelhante ao seguinte.
IDirect3DQuery9* pOcclusionQuery = NULL;
m_pD3DDevice->CreateQuery( D3DQUERYTYPE_OCCLUSION, &pOcclusionQuery );
// Add a begin marker to the command buffer queue.
pOcclusionQuery->Issue( D3DISSUE_BEGIN );
... // API calls
// Add an end marker to the command buffer queue.
pOcclusionQuery->Issue( D3DISSUE_END );
// Avoid flushing and letting the CPU go idle by not using a while loop.
// Check if queries are finished:
DWORD dwOccluded = 0;
if( S_FALSE == pOcclusionQuery->GetData( &dwOccluded, sizeof(DWORD), 0 ) )
{
// Query is not done yet or object not occluded; avoid flushing/wait by continuing with worst-case scenario
pSomeComplexMesh->Render();
}
else if( dwOccluded != 0 )
{
// Query is done and object is not occluded.
pSomeComplexMesh->Render();
}
Tópicos relacionados