Query (Direct3D 9)

Esistono diversi tipi di query progettate per eseguire query sullo stato delle risorse. Lo stato di una determinata risorsa include lo stato dell'unità di elaborazione grafica (GPU), lo stato del driver o lo stato di runtime. Per comprendere la differenza tra i diversi tipi di query, è necessario comprendere gli stati di query. Il diagramma di transizione dello stato seguente illustra ognuno degli stati della query.

diagramma che mostra le transizioni tra stati di query

Il diagramma mostra tre stati, ognuno definito da cerchi. Ognuna delle linee solide sono eventi basati sull'applicazione che causano una transizione dello stato. La riga tratteggiata è un evento basato su risorse che commuta una query dallo stato emesso allo stato segnalato. Ognuno di questi stati ha uno scopo diverso:

  • Lo stato segnalato è simile a uno stato inattiva. L'oggetto query è stato generato ed è in attesa che l'applicazione rilasci la query. Una volta completata una query e ripristinata lo stato segnalato, è possibile recuperare la risposta alla query.
  • Lo stato predefinito è simile a un'area di gestione temporanea per una query. Dallo stato di compilazione è stata emessa una query (chiamando D3DISSUE_BEGIN) ma non è ancora stata eseguita la transizione allo stato emesso. Quando un'applicazione genera un fine query (chiamando D3DISSUE_END), la query passa allo stato emesso.
  • Lo stato rilasciato indica che la risorsa sottoposta a query ha il controllo della query. Al termine del lavoro della risorsa, la risorsa passa il computer di stato allo stato segnalato. Durante lo stato emesso, l'applicazione deve eseguire il polling per rilevare la transizione allo stato segnalato. Dopo aver eseguito la transizione allo stato segnalato, GetData restituisce il risultato della query (tramite un argomento) all'applicazione.

Nella tabella seguente sono elencati i tipi di query disponibili.

Tipo di query Evento di problema Buffer GetData Runtime Inizio implicito della query
LARGHEZZA DI BANDATIMINGS D3DISSUE_BEGIN, D3DISSUE_END D3DDEVINFO_D3D9BANDWIDTHTIMINGS Retail/Debug N/D
CACHEUTILIZATION D3DISSUE_BEGIN, D3DISSUE_END D3DDEVINFO_D3D9CACHEUTILIZATION Retail/Debug N/D
EVENTO D3DISSUE_END BOOL Retail/Debug CreateDevice
INTERFACETIMINGS D3DISSUE_BEGIN, D3DISSUE_END D3DDEVINFO_D3D9INTERFACETIMINGS Retail/Debug N/D
OCCLUSIONE D3DISSUE_BEGIN, D3DISSUE_END DWORD Retail/Debug N/D
PIPELINETIMINGS D3DISSUE_BEGIN, D3DISSUE_END D3DDEVINFO_D3D9PIPELINETIMINGS Retail/Debug N/D
RESOURCEMANAGER D3DISSUE_END D3DDEVINFO_ResourceManager Solo debug Presente
timestamp D3DISSUE_END UINT64 Retail/Debug N/D
TIMESTAMPDISJOINT D3DISSUE_BEGIN, D3DISSUE_END BOOL Retail/Debug N/D
TIMESTAMPFREQ D3DISSUE_END UINT64 Retail/Debug N/D
VCACHE D3DISSUE_END D3DDEVINFO_VCACHE Retail/Debug CreateDevice
VERTEXSTATS D3DISSUE_END D3DDEVINFO_D3DVERTEXSTATS Solo debug Presente
VERTEXTIMINGS D3DISSUE_BEGIN, D3DISSUE_END D3DDEVINFO_D3D9STAGETIMINGS Retail/Debug N/D

 

Alcune query richiedono un evento di inizio e fine, mentre altri richiedono solo un evento finale. Le query che richiedono solo un evento finale iniziano quando si verifica un altro evento implicito (elencato nella tabella). Tutte le query restituiscono una risposta, ad eccezione della query evento la cui risposta è sempre TRUE. Un'applicazione usa lo stato della query o il codice restituito di GetData.

Creare una query

Prima di creare una query, è possibile verificare se il runtime supporta le query chiamando CreateQuery con un puntatore NULL simile al seguente:

IDirect3DQuery9* pEventQuery;

// Create a device pointer m_pd3dDevice

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

Questo metodo restituisce un codice riuscito se è possibile creare una query; in caso contrario, restituisce un codice di errore. Dopo aver completato CreateQuery , è possibile creare un oggetto query simile al seguente:

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

Se la chiamata ha esito positivo, viene creato un oggetto query. La query è essenzialmente inattiva nello stato segnalato (con una risposta non inizializzata) in attesa di essere rilasciata. Al termine della query, rilasciarla come qualsiasi altra interfaccia.

Eseguire una query

Un'applicazione modifica lo stato di una query eseguendo una query. Di seguito è riportato un esempio di esecuzione di una query:

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 query nello stato segnalato eseguirà una transizione simile alla seguente quando viene eseguita:

Tipo di problema Esegue la transizione della query a . . .
D3DISSUE_BEGIN Stato di costruzione.
D3DISSUE_END Stato emesso.

 

Una query nello stato di compilazione passerà come segue quando viene eseguita:

Tipo di problema Esegue la transizione della query a . . .
D3DISSUE_BEGIN (Nessuna transizione, rimane nello stato dell'edificio. Riavvia la parentesi quadra di query.
D3DISSUE_END Stato emesso.

 

Una query nello stato emesso eseguirà una transizione simile alla seguente quando viene eseguita:

Tipo di problema Esegue la transizione della query a . . .
D3DISSUE_BEGIN Lo stato di compilazione e riavvia la parentesi quadre di query.
D3DISSUE_END Stato emesso dopo aver abbandonato la query esistente.

 

Controllare lo stato della query e ottenere la risposta alla query

GetData esegue due operazioni:

  1. Restituisce lo stato della query nel codice restituito.
  2. Restituisce la risposta alla query in pData.

Da ognuno dei tre stati di query, ecco i codici restituiti GetData :

Stato delle query Codice restituito GetData
Segnalato S_OK
Compilazione Codice di errore
Rilasciato S_FALSE

 

Ad esempio, quando una query è nello stato emesso e la risposta alla query non è disponibile, GetData restituisce S_FALSE. Quando la risorsa termina il proprio lavoro e l'applicazione ha emesso una fine della query, la risorsa passa allo stato segnalato. Dallo stato segnalato, GetData restituisce S_OK il che significa che anche la risposta alla query viene restituita in pData. Ad esempio, ecco la sequenza di eventi per restituire il numero di pixel (o campioni quando è abilitato il multicampionamento) disegnato in una sequenza di rendering:

  • Creare la query.
  • Eseguire un evento di inizio.
  • Disegnare qualcosa.
  • Rilasciare un evento finale.

Di seguito è riportata la sequenza di codice corrispondente:

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.

Queste righe di codice eseguono diverse operazioni:

  • Chiamare GetData per restituire il numero di pixel/campioni disegnati.
  • Specificare D3DGETDATA_FLUSH per consentire alla risorsa di eseguire la transizione della query allo stato segnalato.
  • Eseguire il polling della risorsa di query chiamando GetData da un ciclo. Finché GetData restituisce S_FALSE, significa che la risorsa non ha ancora restituito la risposta.

Il valore restituito di GetData indica essenzialmente lo stato della query. I valori possibili sono S_OK, S_FALSE e un errore. Non chiamare GetData su una query che si trova nello stato di compilazione.

  • S_OK indica che la risorsa (GPU o driver o runtime) è stata completata. La query torna allo stato segnalato. La risposta (se presente) viene restituita da GetData.
  • S_FALSE indica che la risorsa (GPU o driver o runtime) non può ancora restituire una risposta. Ciò potrebbe essere dovuto al fatto che la GPU non è terminata o non ha ancora visto il lavoro.
  • Un errore indica che la query ha generato un errore da cui non è possibile eseguire il ripristino. Questo potrebbe essere il caso in cui il dispositivo venga perso durante una query. Dopo che una query ha generato un errore (diverso da S_FALSE), è necessario ricreare la query che riavvia la sequenza di query dallo stato segnalato.

Invece di specificare D3DGETDATA_FLUSH, che fornisce informazioni più aggiornate, è possibile specificare zero, ovvero un controllo più leggero se la query si trova nello stato emesso. Se si specifica zero , GetData non scarica il buffer dei comandi. Per questo motivo, è necessario prestare attenzione a evitare cicli infiniti (vedere GetData per informazioni dettagliate). Poiché le code di runtime funzionano nel buffer dei comandi, D3DGETDATA_FLUSH è un meccanismo per scaricare il buffer dei comandi nel driver (e quindi la GPU; vedere Profilatura accurata delle chiamate API Direct3D (Direct3D 9)). Durante lo scaricamento del buffer dei comandi, una query può passare allo stato segnalato.

Esempio: query di evento

Una query di evento non supporta un evento di inizio.

  • Creare la query.
  • Rilasciare un evento finale.
  • Eseguire il polling fino a quando la GPU non è inattiva.
  • Rilasciare un evento finale.
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 ))
    ;

Questa è la sequenza di comandi usati da una query di eventi per profilare le chiamate API (Application Programming Interface) (vedere Profilatura accurata delle chiamate API Direct3D (Direct3D 9)). Questa sequenza usa marcatori per controllare la quantità di lavoro nel buffer dei comandi.

Si noti che le applicazioni devono prestare particolare attenzione al costo elevato associato allo scaricamento del buffer dei comandi perché in questo modo il sistema operativo passa alla modalità kernel, incorrendo quindi in una riduzione delle prestazioni ridimensionabile. Le applicazioni devono essere consapevoli anche della necessità di sprecare cicli di CPU attendendo il completamento delle query.

Le query sono un'ottimizzazione da usare durante il rendering per migliorare le prestazioni. Pertanto, non è utile dedicare tempo in attesa del completamento di una query. Se viene eseguita una query e se i risultati non sono ancora pronti al momento del controllo dell'applicazione, il tentativo di ottimizzazione non è riuscito e il rendering dovrebbe continuare come di consueto.

L'esempio classico di questo è durante occlusion Culling. Invece del ciclo while precedente, un'applicazione che usa query può implementare il culling di occlusione per verificare se una query è stata completata entro il momento in cui è necessario il risultato. Se la query non è stata completata, continuare (come scenario peggiore) come se l'oggetto sottoposto a test non sia occluso (ad esempio, è visibile) ed eseguirne il rendering. Il codice sarà simile al seguente.

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

Argomenti avanzati