Consultas (Direct3D 9)

Hay varios tipos de consultas que están diseñadas para consultar el estado de los recursos. El estado de un recurso determinado incluye el estado de la unidad de procesamiento gráfico (GPU), el estado del controlador o el estado en tiempo de ejecución. Para comprender la diferencia entre los distintos tipos de consulta, debe comprender los estados de la consulta. En el diagrama de transición de estado siguiente se explica cada uno de los estados de consulta.

diagrama que muestra las transiciones entre estados de consulta

El diagrama muestra tres estados, cada uno definido por círculos. Cada una de las líneas sólidas son eventos controlados por la aplicación que provocan una transición de estado. La línea discontinua es un evento controlado por recursos que cambia una consulta del estado emitido al estado señalado. Cada uno de estos estados tiene un propósito diferente:

  • El estado señalado es como un estado inactivo. Se ha generado el objeto de consulta y espera a que la aplicación emita la consulta. Una vez que una consulta se ha completado y vuelto al estado señalado, se puede recuperar la respuesta a la consulta.
  • El estado de creación es como un área de almacenamiento provisional para una consulta. Desde el estado de creación, se ha emitido una consulta (llamando a D3DISSUE_BEGIN), pero aún no ha pasado al estado emitido. Cuando una aplicación emite un extremo de consulta (llamando a D3DISSUE_END), la consulta pasa al estado emitido.
  • El estado emitido significa que el recurso que se consulta tiene el control de la consulta. Una vez que el recurso finaliza su trabajo, el recurso pasa la máquina de estado al estado señalado. Durante el estado emitido, la aplicación debe sondear para detectar la transición al estado señalado. Una vez que se produce la transición al estado señalado, GetData devuelve el resultado de la consulta (a través de un argumento) a la aplicación.

En la tabla siguiente se enumeran los tipos de consulta disponibles.

Tipo de consulta Evento issue Búfer GetData Tiempo de ejecución Inicio implícito de la consulta
BANDWIDTHTIMINGS D3DISSUE_BEGIN, D3DISSUE_END D3DDEVINFO_D3D9BANDWIDTHTIMINGS Retail/Debug N/D
CACHEUTILIZATION D3DISSUE_BEGIN, D3DISSUE_END D3DDEVINFO_D3D9CACHEUTILIZATION Retail/Debug No aplicable
EVENTO D3DISSUE_END BOOL Retail/Debug CreateDevice
INTERFACETIMINGS D3DISSUE_BEGIN, D3DISSUE_END D3DDEVINFO_D3D9INTERFACETIMINGS Retail/Debug No aplicable
OCLUSIÓN D3DISSUE_BEGIN, D3DISSUE_END DWORD Retail/Debug No aplicable
PIPELINETIMINGS D3DISSUE_BEGIN, D3DISSUE_END D3DDEVINFO_D3D9PIPELINETIMINGS Retail/Debug No aplicable
RESOURCEMANAGER D3DISSUE_END D3DDEVINFO_ResourceManager Solo depuración Presente
timestamp D3DISSUE_END UINT64 Retail/Debug N/D
TIMESTAMPDISJOINT D3DISSUE_BEGIN, D3DISSUE_END BOOL Retail/Debug No aplicable
TIMESTAMPFREQ D3DISSUE_END UINT64 Retail/Debug No aplicable
VCACHE D3DISSUE_END D3DDEVINFO_VCACHE Retail/Debug CreateDevice
VERTEXSTATS D3DISSUE_END D3DDEVINFO_D3DVERTEXSTATS Solo depuración Presente
VERTEXTIMINGS D3DISSUE_BEGIN, D3DISSUE_END D3DDEVINFO_D3D9STAGETIMINGS Retail/Debug No aplicable

 

Algunas de las consultas requieren un evento begin y end, mientras que otros solo requieren un evento end. Las consultas que solo requieren un evento final comienzan cuando se produce otro evento implícito (que se muestra en la tabla). Todas las consultas devuelven una respuesta, excepto la consulta de eventos cuya respuesta siempre es TRUE. Una aplicación usa el estado de la consulta o el código de retorno de GetData.

Crear una consulta

Antes de crear una consulta, puede comprobar si el tiempo de ejecución admite consultas mediante una llamada a CreateQuery con un puntero NULL como este:

IDirect3DQuery9* pEventQuery;

// Create a device pointer m_pd3dDevice

// Create a query object
HRESULT hr = m_pd3dDevice->CreateQuery(D3DQUERYTYPE_EVENT, NULL);

Este método devuelve un código correcto si se puede crear una consulta; de lo contrario, devuelve un código de error. Una vez que CreateQuery se realiza correctamente, puede crear un objeto de consulta de la siguiente manera:

IDirect3DQuery9* pEventQuery;
m_pd3dDevice->CreateQuery(D3DQUERYTYPE_EVENT, &pEventQuery);

Si esta llamada se realiza correctamente, se crea un objeto de consulta. La consulta está esencialmente inactiva en el estado señalado (con una respuesta no inicializada) en espera de emitirse. Cuando haya terminado con la consulta, suelte como cualquier otra interfaz.

Emitir una consulta

Una aplicación cambia un estado de consulta mediante la emisión de una consulta. Este es un ejemplo de emisión de una 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);

Una consulta en el estado señalado pasará de esta forma cuando se emita:

Tipo de problema Realiza transiciones de consulta a . . .
D3DISSUE_BEGIN Estado de creación.
D3DISSUE_END Estado emitido.

 

Una consulta en el estado de creación pasará de esta forma cuando se emita:

Tipo de problema Realiza transiciones de consulta a . . .
D3DISSUE_BEGIN (Sin transición, permanece en el estado de creación. Reinicia el corchete de consulta).
D3DISSUE_END Estado emitido.

 

Una consulta en el estado emitido pasará de esta forma cuando se emita:

Tipo de problema Realiza transiciones de consulta a . . .
D3DISSUE_BEGIN El estado de creación y reinicia el corchete de consulta.
D3DISSUE_END Estado emitido después de abandonar la consulta existente.

 

Compruebe el estado de la consulta y obtenga la respuesta a la consulta.

GetData hace dos cosas:

  1. Devuelve el estado de consulta en el código de retorno.
  2. Devuelve la respuesta a la consulta en pData.

A partir de cada uno de los tres estados de consulta, estos son los códigos de retorno GetData :

Estado de la consulta Código de retorno getData
Señalado S_OK
Compilación Código de error
Emitido S_FALSE

 

Por ejemplo, cuando una consulta está en estado emitido y la respuesta a la consulta no está disponible, GetData devuelve S_FALSE. Cuando el recurso finaliza su trabajo y la aplicación ha emitido un extremo de consulta, el recurso pasa la consulta al estado señalado. A partir del estado señalado, GetData devuelve S_OK lo que significa que la respuesta a la consulta también se devuelve en pData. Por ejemplo, esta es la secuencia de eventos para devolver el número de píxeles (o muestras cuando está habilitado el muestreo múltiple) dibujado en una secuencia de representación:

  • Crear la consulta.
  • Emita un evento begin.
  • Dibuje algo.
  • Emita un evento final.

A continuación se muestra la secuencia de código correspondiente:

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 líneas de código hacen varias cosas:

  • Llame a GetData para devolver el número de píxeles o muestras dibujados.
  • Especifique D3DGETDATA_FLUSH para permitir que el recurso realice la transición de la consulta al estado señalado.
  • Sondee el recurso de consulta llamando a GetData desde un bucle. Siempre que GetData devuelva S_FALSE, esto significa que el recurso aún no ha devuelto la respuesta.

El valor devuelto de GetData básicamente indica en qué estado es la consulta. Los valores posibles son S_OK, S_FALSE y un error. No llame a GetData en una consulta que se encuentra en el estado de creación.

  • S_OK significa que el recurso (GPU o controlador o tiempo de ejecución) ha finalizado. La consulta vuelve al estado señalado. GetData devuelve la respuesta (si existe).
  • S_FALSE significa que el recurso (GPU o controlador o tiempo de ejecución) todavía no puede devolver una respuesta. Esto puede deberse a que la GPU no ha finalizado o aún no ha visto el trabajo.
  • Un error significa que la consulta ha generado un error desde el que no se puede recuperar. Esto podría ser el caso si el dispositivo se pierde durante una consulta. Una vez que una consulta ha generado un error (distinto de S_FALSE), la consulta debe volver a crearse, lo que reiniciará la secuencia de consulta desde el estado señalado.

En lugar de especificar D3DGETDATA_FLUSH, que proporciona información más actualizada, puede proporcionar cero, que es una comprobación más ligera si la consulta está en estado emitido. Proporcionar cero hará que GetData no vacíe el búfer de comandos. Por este motivo, se debe tener cuidado para evitar bucles infinitos (consulte GetData para obtener más información). Dado que el tiempo de ejecución pone en cola el trabajo en el búfer de comandos, D3DGETDATA_FLUSH es un mecanismo para vaciar el búfer de comandos en el controlador (y, por tanto, la GPU; consulte Generación de perfiles precisa de llamadas API de Direct3D (Direct3D 9)). Durante el vaciado del búfer de comandos, una consulta puede pasar al estado señalado.

Ejemplo: una consulta de eventos

Una consulta de eventos no admite un evento begin.

  • Crear la consulta.
  • Emita un evento final.
  • Sondee hasta que la GPU esté inactiva.
  • Emita un 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 es la secuencia de comandos que usa una consulta de eventos para generar perfiles de llamadas a la interfaz de programación de aplicaciones (API) (consulte Generación de perfiles precisa de llamadas API de Direct3D (Direct3D 9)). Esta secuencia usa marcadores para ayudar a controlar la cantidad de trabajo en el búfer de comandos.

Tenga en cuenta que las aplicaciones deben prestar especial atención al gran costo asociado al vaciado del búfer de comandos, ya que esto hace que el sistema operativo cambie al modo kernel, lo que conlleva una penalización de rendimiento considerable. Las aplicaciones también deben tener en cuenta la desperdiciación de ciclos de CPU esperando a que se completen las consultas.

Las consultas son una optimización que se usará durante la representación para aumentar el rendimiento. Por lo tanto, no es beneficioso dedicar tiempo a esperar a que finalice una consulta. Si se emite una consulta y si los resultados aún no están listos en el momento en que la aplicación los comprueba, el intento de optimización no se realizó correctamente y la representación debería continuar como normal.

El ejemplo clásico de esto es durante la selección de oclusión. En lugar del bucle while anterior, una aplicación que usa consultas puede implementar la selección de oclusión para comprobar si una consulta ha finalizado en el momento en que necesita el resultado. Si la consulta no ha finalizado, continúe (como escenario peor) como si el objeto que se está probando no se ocluye (es decir, es visible) y lo represente. El código tendría un aspecto similar al siguiente.

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();
}

Temas avanzados