Abfragen (Direct3D 9)
Es gibt mehrere Arten von Abfragen, die zum Abfragen der status von Ressourcen konzipiert sind. Die status einer bestimmten Ressource umfasst grafikverarbeitungseinheit (GPU) status, Treiber status oder Runtime status. Um den Unterschied zwischen den verschiedenen Abfragetypen zu verstehen, müssen Sie die Abfragezustände verstehen. Im folgenden Zustandsübergangsdiagramm werden die einzelnen Abfragezustände erläutert.
Das Diagramm zeigt drei Zustände, die jeweils durch Kreise definiert sind. Jede der einfarbigen Zeilen sind anwendungsgesteuerte Ereignisse, die einen Zustandsübergang verursachen. Die gestrichelte Linie ist ein ressourcengesteuertes Ereignis, das eine Abfrage vom ausgegebenen Zustand in den signalierten Zustand wechselt. Jeder dieser Zustände hat einen anderen Zweck:
- Der Signalzustand ähnelt einem Leerlaufzustand. Das Abfrageobjekt wurde generiert und wartet darauf, dass die Anwendung die Abfrage ausgibt. Sobald eine Abfrage abgeschlossen und wieder in den signalierten Zustand übergewechselt wurde, kann die Antwort auf die Abfrage abgerufen werden.
- Der Gebäudezustand ähnelt einem Stagingbereich für eine Abfrage. Aus dem Gebäudezustand wurde eine Abfrage ausgegeben (durch Aufrufen D3DISSUE_BEGIN), aber noch nicht in den ausgestellten Zustand übergegangen. Wenn eine Anwendung ein Abfrageende ausgibt (durch Aufrufen von D3DISSUE_END), wechselt die Abfrage in den ausgestellten Zustand.
- Der ausgestellte Zustand bedeutet, dass die abgefragte Ressource die Kontrolle über die Abfrage hat. Sobald die Ressource ihre Arbeit abgeschlossen hat, übergibt die Ressource den Zustandscomputer in den Signalzustand. Während des ausgegebenen Zustands muss die Anwendung abfragen, um den Übergang zum signalierten Zustand zu erkennen. Sobald der Übergang zum Signalzustand erfolgt ist, gibt GetData das Abfrageergebnis (über ein Argument) an die Anwendung zurück.
In der folgenden Tabelle sind die verfügbaren Abfragetypen aufgeführt.
Abfragetyp | Problemereignis | GetData-Puffer | Typ | Impliziter Abfragebeginn |
---|---|---|---|---|
BANDBREITETIMINGS | D3DISSUE_BEGIN, D3DISSUE_END | D3DDEVINFO_D3D9BANDWIDTHTIMINGS | Einzelhandel/Debuggen | – |
CACHEVERWENDUNG | D3DISSUE_BEGIN, D3DISSUE_END | D3DDEVINFO_D3D9CACHEUTILIZATION | Einzelhandel/Debuggen | – |
EREIGNIS | D3DISSUE_END | BOOL | Einzelhandel/Debuggen | CreateDevice |
INTERFACETIMINGS | D3DISSUE_BEGIN, D3DISSUE_END | D3DDEVINFO_D3D9INTERFACETIMINGS | Einzelhandel/Debuggen | – |
OKKLUSION | D3DISSUE_BEGIN, D3DISSUE_END | DWORD | Einzelhandel/Debuggen | – |
PIPELINETIMINGS | D3DISSUE_BEGIN, D3DISSUE_END | D3DDEVINFO_D3D9PIPELINETIMINGS | Einzelhandel/Debuggen | – |
RESOURCEMANAGER | D3DISSUE_END | D3DDEVINFO_ResourceManager | Nur Debuggen | Gegenwart |
timestamp | D3DISSUE_END | UINT64 | Einzelhandel/Debuggen | – |
TIMESTAMPDISJOINT | D3DISSUE_BEGIN, D3DISSUE_END | BOOL | Einzelhandel/Debuggen | – |
TIMESTAMPFREQ | D3DISSUE_END | UINT64 | Einzelhandel/Debuggen | – |
VCACHE | D3DISSUE_END | D3DDEVINFO_VCACHE | Einzelhandel/Debuggen | CreateDevice |
VERTEXSTATS | D3DISSUE_END | D3DDEVINFO_D3DVERTEXSTATS | Nur Debuggen | Gegenwart |
VERTEXTIMINGS | D3DISSUE_BEGIN, D3DISSUE_END | D3DDEVINFO_D3D9STAGETIMINGS | Einzelhandel/Debuggen | – |
Einige der Abfragen erfordern ein Start- und Endereignis, während andere nur ein Endereignis erfordern. Die Abfragen, die nur ein Endereignis erfordern, beginnen, wenn ein anderes implizites Ereignis auftritt (das in der Tabelle aufgeführt ist). Alle Abfragen geben eine Antwort zurück, mit Ausnahme der Ereignisabfrage, deren Antwort immer TRUE ist. Eine Anwendung verwendet entweder den Status der Abfrage oder den Rückgabecode von GetData.
Erstellen einer Abfrage
Bevor Sie eine Abfrage erstellen, können Sie überprüfen, ob die Runtime Abfragen unterstützt, indem Sie CreateQuery mit einem NULL-Zeiger wie dem folgenden aufrufen:
IDirect3DQuery9* pEventQuery;
// Create a device pointer m_pd3dDevice
// Create a query object
HRESULT hr = m_pd3dDevice->CreateQuery(D3DQUERYTYPE_EVENT, NULL);
Diese Methode gibt einen Erfolgscode zurück, wenn eine Abfrage erstellt werden kann. Andernfalls wird ein Fehlercode zurückgegeben. Sobald CreateQuery erfolgreich ist, können Sie ein Abfrageobjekt wie folgt erstellen:
IDirect3DQuery9* pEventQuery;
m_pd3dDevice->CreateQuery(D3DQUERYTYPE_EVENT, &pEventQuery);
Wenn dieser Aufruf erfolgreich ist, wird ein Abfrageobjekt erstellt. Die Abfrage befindet sich im Wesentlichen im Leerlauf des signalisierten Zustands (mit einer nicht initialisierten Antwort), die darauf wartet, ausgegeben zu werden. Wenn Sie mit der Abfrage fertig sind, geben Sie sie wie jede andere Schnittstelle frei.
Ausgeben einer Abfrage
Eine Anwendung ändert einen Abfragestatus, indem sie eine Abfrage ausgibt. Hier sehen Sie ein Beispiel für das Ausgeben einer Abfrage:
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);
Eine Abfrage im signalierten Zustand wird beim Ausgeben wie folgt übergehen:
Problemtyp | Abfrageübergänge in . . . |
---|---|
D3DISSUE_BEGIN | Gebäudezustand. |
D3DISSUE_END | Ausgestellter Status. |
Eine Abfrage im Gebäudezustand wird wie folgt übergehen, wenn sie ausgegeben wird:
Problemtyp | Abfrageübergänge in . . . |
---|---|
D3DISSUE_BEGIN | (Kein Übergang, verbleibt im Gebäudezustand. Startet die Abfrageklammer neu.) |
D3DISSUE_END | Ausgestellter Status. |
Eine Abfrage im Status "Ausgestellt" wird bei der Ausgabe wie folgt übergehen:
Problemtyp | Abfrageübergänge in . . . |
---|---|
D3DISSUE_BEGIN | Erstellt den Zustand und startet die Abfrageklammer neu. |
D3DISSUE_END | Der Status wird ausgegeben, nachdem die vorhandene Abfrage abgebrochen wurde. |
Überprüfen des Abfragestatus und Abrufen der Antwort auf die Abfrage
GetData führt zwei Aufgaben aus:
- Gibt den Abfragezustand im Rückgabecode zurück.
- Gibt die Antwort auf die Abfrage in pData zurück.
Aus jedem der drei Abfragezustände finden Sie die folgenden GetData-Rückgabecodes :
Abfragestatus | GetData-Rückgabecode |
---|---|
Signalisiert | S_OK |
Erstellen | Fehlercode |
Ausgestellt | S_FALSE |
Wenn sich beispielsweise eine Abfrage im Ausgestellten Zustand befindet und die Antwort auf die Abfrage nicht verfügbar ist, gibt GetData S_FALSE zurück. Wenn die Ressource ihre Arbeit beendet und die Anwendung ein Abfrageende ausgegeben hat, übergibt die Ressource die Abfrage in den signalierten Zustand. Aus dem signalierten Zustand gibt GetData S_OK was bedeutet, dass die Antwort auf die Abfrage auch in pData zurückgegeben wird. Für instance sehen Sie hier die Sequenz von Ereignissen, die die Anzahl von Pixeln (oder Stichproben bei Aktiviertem Multisampling) zurückgeben, die in einer Rendersequenz gezeichnet wurden:
- Erstellen der Abfrage
- Geben Sie ein Begin-Ereignis aus.
- Zeichnen Sie etwas.
- Geben Sie ein Endereignis aus.
Im Folgenden ist die entsprechende Codesequenz aufgeführt:
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.
Diese Codezeilen führen mehrere Aufgaben aus:
- Rufen Sie GetData auf, um die Anzahl der gezeichneten Pixel/Stichproben zurückzugeben.
- Geben Sie D3DGETDATA_FLUSH an, damit die Ressource die Abfrage in den signalierten Zustand übergehen kann.
- Rufen Sie die Abfrageressource ab, indem Sie GetData aus einer Schleife aufrufen. Solange GetData S_FALSE zurückgibt, bedeutet dies, dass die Ressource die Antwort noch nicht zurückgegeben hat.
Der Rückgabewert von GetData gibt ihnen im Wesentlichen an, in welchem Zustand sich die Abfrage befindet. Mögliche Werte sind S_OK, S_FALSE und ein Fehler. Rufen Sie GetData nicht für eine Abfrage auf, die sich im Gebäudezustand befindet.
- S_OK bedeutet, dass die Ressource (GPU oder Treiber oder Runtime) abgeschlossen ist. Die Abfrage kehrt in den signalierten Zustand zurück. Die Antwort (falls vorhanden) wird von GetData zurückgegeben.
- S_FALSE bedeutet, dass die Ressource (GPU, Treiber oder Runtime) noch keine Antwort zurückgeben kann. Dies kann daran liegt, dass die GPU noch nicht fertig ist oder die Arbeit noch nicht gesehen hat.
- Ein Fehler bedeutet, dass die Abfrage einen Fehler generiert hat, nach dem sie nicht wiederherstellen kann. Dies kann der Fall sein, wenn das Gerät während einer Abfrage verloren geht. Sobald eine Abfrage einen Fehler (außer S_FALSE) generiert hat, muss die Abfrage neu erstellt werden, wodurch die Abfragesequenz aus dem signalierten Zustand neu gestartet wird.
Anstatt D3DGETDATA_FLUSH anzugeben, der aktuellere Informationen bereitstellt, können Sie null angeben, was eine leichtere Überprüfung ist, wenn sich die Abfrage im ausgestellten Zustand befindet. Die Angabe von 0 (null) führt dazu , dass GetData den Befehlspuffer nicht leert. Aus diesem Grund muss darauf geachtet werden, Endlosschleifen zu vermeiden (weitere Informationen finden Sie unter GetData ). Da die Laufzeit im Befehlspuffer in die Warteschlange eingereiht wird, ist D3DGETDATA_FLUSH ein Mechanismus zum Leeren des Befehlspuffers in den Treiber (und damit die GPU; siehe Genaue Profilerstellung von Direct3D-API-Aufrufen (Direct3D 9)). Während des Leerens des Befehlspuffers kann eine Abfrage in den signalierten Zustand übergehen.
Beispiel: Eine Ereignisabfrage
Eine Ereignisabfrage unterstützt kein start-Ereignis.
- Erstellen der Abfrage
- Geben Sie ein Endereignis aus.
- Rufen Sie ab, bis sich die GPU im Leerlauf befindet.
- Geben Sie ein Endereignis aus.
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 ))
;
Dies ist die Reihenfolge der Befehle, die eine Ereignisabfrage verwendet, um API-Aufrufe (Application Programming Interface) zu profilieren (siehe Genaue Profilerstellung von Direct3D-API-Aufrufen (Direct3D 9)). Diese Sequenz verwendet Marker, um den Arbeitsaufwand im Befehlspuffer zu steuern.
Beachten Sie, dass Anwendungen besonders auf die hohen Kosten achten sollten, die mit dem Leeren des Befehlspuffers verbunden sind, da dies dazu führt, dass das Betriebssystem in den Kernelmodus wechselt, was zu einer erheblichen Leistungseinbuße führt. Anwendungen sollten auch beachten, dass CPU-Zyklen durch Warten auf den Abschluss von Abfragen verschwenden.
Abfragen sind eine Optimierung, die beim Rendern verwendet werden soll, um die Leistung zu steigern. Daher ist es nicht von Vorteil, zeit damit zu verbringen, auf den Abschluss einer Abfrage zu warten. Wenn eine Abfrage ausgegeben wird und die Ergebnisse zum Zeitpunkt der Überprüfung durch die Anwendung noch nicht bereit sind, war der Optimierungsversuch nicht erfolgreich, und das Rendern sollte wie gewohnt fortgesetzt werden.
Das klassische Beispiel hierfür ist die Occlusion Culling. Anstelle der obigen while-Schleife kann eine Anwendung, die Abfragen verwendet, okklusionskulling implementieren, um zu überprüfen, ob eine Abfrage abgeschlossen wurde, bis sie das Ergebnis benötigt. Wenn die Abfrage noch nicht abgeschlossen ist, fahren Sie (im schlimmsten Fall) so fort, als ob das Objekt, an dem getestet wird, nicht verdeckt ist (d. h. es ist sichtbar), und rendern Sie es. Der Code sieht in etwa wie folgt aus.
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();
}
Zugehörige Themen