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 della query. Il diagramma di transizione dello stato seguente illustra ognuno degli stati della query.
Il diagramma mostra tre stati, ognuno definito dai cerchi. Ognuna delle linee solide è costituita da eventi basati su applicazioni che causano una transizione di stato. La linea tratteggiata è un evento basato sulle risorse che sposta una query dallo stato emesso allo stato segnalato. Ognuno di questi stati ha uno scopo diverso:
- Lo stato segnalato è simile a uno stato di inattività. L'oggetto query è stato generato ed è in attesa che l'applicazione generi la query. Dopo il completamento e la transizione di una query allo stato segnalato, è possibile recuperare la risposta alla query.
- Lo stato dell'edificio è simile a un'area di gestione temporanea per una query. Dallo stato di compilazione, è stata eseguita una query (chiamando D3DISSUE_BEGIN) ma non è ancora stata eseguita la transizione allo stato emesso. Quando un'applicazione rilascia un termine di 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 la macchina a stati allo stato segnalato. Durante lo stato emesso, l'applicazione deve eseguire il polling per rilevare la transizione allo stato segnalato. Quando si verifica 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 issue | Buffer GetData | Runtime | Inizio implicito della query |
---|---|---|---|---|
LARGHEZZA DI BANDATIMINGS | D3DISSUE_BEGIN, D3DISSUE_END | D3DDEVINFO_D3D9BANDWIDTHTIMINGS | Vendita al dettaglio/debug | N/D |
CACHEUTILIZATION | D3DISSUE_BEGIN, D3DISSUE_END | D3DDEVINFO_D3D9CACHEUTILIZATION | Vendita al dettaglio/debug | N/D |
EVENTO | D3DISSUE_END | BOOL | Vendita al dettaglio/debug | CreateDevice |
INTERFACETIMINGS | D3DISSUE_BEGIN, D3DISSUE_END | D3DDEVINFO_D3D9INTERFACETIMINGS | Vendita al dettaglio/debug | N/D |
OCCLUSIONE | D3DISSUE_BEGIN, D3DISSUE_END | DWORD | Vendita al dettaglio/debug | N/D |
PIPELINETIMINGS | D3DISSUE_BEGIN, D3DISSUE_END | D3DDEVINFO_D3D9PIPELINETIMINGS | Vendita al dettaglio/debug | N/D |
RESOURCEMANAGER | D3DISSUE_END | D3DDEVINFO_ResourceManager | Solo debug | presente |
TIMESTAMP | D3DISSUE_END | UINT64 | Vendita al dettaglio/debug | N/D |
TIMESTAMPDISJOINT | D3DISSUE_BEGIN, D3DISSUE_END | BOOL | Vendita al dettaglio/debug | N/D |
TIMESTAMPFREQ | D3DISSUE_END | UINT64 | Vendita al dettaglio/debug | N/D |
VCACHE | D3DISSUE_END | D3DDEVINFO_VCACHE | Vendita al dettaglio/debug | CreateDevice |
VERTEXSTATS | D3DISSUE_END | D3DDEVINFO_D3DVERTEXSTATS | Solo debug | presente |
VERTEXTIMINGS | D3DISSUE_BEGIN, D3DISSUE_END | D3DDEVINFO_D3D9STAGETIMINGS | Vendita al dettaglio/debug | N/D |
Alcune query richiedono un evento di inizio e fine, mentre altre richiedono solo un evento finale. Le query che richiedono un evento finale iniziano solo 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 di esito positivo se è possibile creare una query; in caso contrario restituisce un codice di errore. Quando CreateQuery ha esito positivo, è 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, rilasciarlo 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 dell'edificio. |
D3DISSUE_END | Stato emesso. |
Una query nello stato di compilazione eseguirà una transizione simile alla seguente quando viene eseguita:
Tipo di problema | Esegue la transizione della query a . . . |
---|---|
D3DISSUE_BEGIN | (Nessuna transizione, rimane nello stato dell'edificio. Riavvia la parentesi 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 | Stato di compilazione e riavvia la parentesi di query. |
D3DISSUE_END | Stato rilasciato dopo aver abbandonato la query esistente. |
Controllare lo stato della query e ottenere la risposta alla query
GetData esegue due operazioni:
- Restituisce lo stato della query nel codice restituito.
- Restituisce la risposta alla query in pData.
Da ognuno dei tre stati della query, ecco i codici restituiti GetData:
Stato query | Codice restituito GetData |
---|---|
Segnalato | S_OK |
Edificio | Codice di errore |
Emesso | S_FALSE |
Ad esempio, quando una query si trova nello stato emesso e la risposta alla query non è disponibile, GetData restituisce S_FALSE. Al termine del lavoro della risorsa e l'applicazione ha emesso una fine della query, la risorsa esegue la transizione della query allo stato segnalato. Dallo stato segnalato, getData restituisce S_OK il che significa che la risposta alla query viene restituita anche 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.
- Disegna qualcosa.
- Emettere 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. Se GetData restituisce S_FALSE, significa che la risorsa non ha ancora restituito la risposta.
Il valore restituito di GetData indica essenzialmente in quale stato si trova la query. I valori possibili sono S_OK, S_FALSE e un errore. Non chiamare GetData su una query 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, driver o runtime) non può ancora restituire una risposta. Questo potrebbe essere dovuto al fatto che la GPU non è stata completata 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 riavvierà la sequenza di query dallo stato segnalato.
Invece di specificare D3DGETDATA_FLUSH, che fornisce più informazioni up-to-date, è possibile specificare zero che è un controllo più leggero se la query si trova nello stato emesso. Se si specifica zero, getData non svuotare il buffer dei comandi. Per questo motivo, è necessario prestare attenzione a evitare cicli infiniti (vedere GetData per informazioni dettagliate). Poiché il runtime accoda il lavoro nel buffer dei comandi, D3DGETDATA_FLUSH è un meccanismo per scaricare il buffer dei comandi nel driver (e quindi la GPU; vedere Profiling accurato delle chiamate API Direct3D (Direct3D 9)). Durante lo scaricamento del buffer dei comandi, una query può passare allo stato segnalato.
Esempio: una query di evento
Una query evento non supporta un evento begin.
- Creare la query.
- Emettere un evento finale.
- Eseguire il polling fino a quando la GPU non è inattiva.
- Emettere 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é questo fa sì che il sistema operativo passi alla modalità kernel, causando così una riduzione delle prestazioni ridimensionabile. Le applicazioni devono anche essere consapevoli 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 deve continuare come di consueto.
L'esempio classico di questo è durante Occlusion Culling. Invece del mentre ciclo 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 correlati