Abfragen (Direct3D 9)
Es gibt mehrere Arten von Abfragen, die für die Abfrage des Status von Ressourcen konzipiert sind. Der Status einer bestimmten Ressource umfasst den GPU-Status (Graphics Processing Unit), den Treiberstatus oder den Laufzeitstatus. 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 Linien sind anwendungsgesteuerte Ereignisse, die einen Zustandsübergang verursachen. Die gestrichelte Linie ist ein ressourcengesteuertes Ereignis, das eine Abfrage vom ausgegebenen Zustand in den signalgesteuerten Zustand wechselt. Jeder dieser Zustände hat einen anderen Zweck:
- Der signalisierte Zustand ist wie ein Leerlaufzustand. Das Abfrageobjekt wurde generiert und wartet darauf, dass die Anwendung die Abfrage ausstellen kann. Sobald eine Abfrage abgeschlossen und wieder in den signalisierten Zustand umgestellt wurde, kann die Antwort auf die Abfrage abgerufen werden.
- Der Gebäudezustand ist wie ein Stagingbereich für eine Abfrage. Aus dem Gebäudezustand wurde eine Abfrage (durch Aufrufen von D3DISSUE_BEGIN) ausgestellt, aber noch nicht in den ausgestellten Zustand übergegangen. Wenn eine Anwendung ein Abfrageende (durch Aufrufen von D3DISSUE_END) ausgibt, wechselt die Abfrage in den ausgegebenen Zustand.
- Der ausgegebene Zustand bedeutet, dass die abgefragte Ressource die Kontrolle über die Abfrage hat. Sobald die Ressource ihre Arbeit beendet hat, übergibt die Ressource den Zustandsautomaten in den signalierten Zustand. Während des ausgestellten Zustands muss die Anwendung abfragen, um den Übergang zum signalisierten Zustand zu erkennen. Sobald der Übergang zum signalisierten Zustand erfolgt, 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 | Laufzeit | Impliziter Anfang der Abfrage |
---|---|---|---|---|
BANDBREITENIMINGS | D3DISSUE_BEGIN, D3DISSUE_END | D3DDEVINFO_D3D9BANDWIDTHTIMINGS | Einzelhandel/Debug | N/A |
CACHEUTILIZATION | D3DISSUE_BEGIN, D3DISSUE_END | D3DDEVINFO_D3D9CACHEUTILIZATION | Einzelhandel/Debug | N/A |
EREIGNIS | D3DISSUE_END | BOOL | Einzelhandel/Debug | CreateDevice- |
INTERFACETIMINGS | D3DISSUE_BEGIN, D3DISSUE_END | D3DDEVINFO_D3D9INTERFACETIMINGS | Einzelhandel/Debug | N/A |
VERSTOPFUNG | D3DISSUE_BEGIN, D3DISSUE_END | DWORD | Einzelhandel/Debug | N/A |
PIPELINETIMINGS | D3DISSUE_BEGIN, D3DISSUE_END | D3DDEVINFO_D3D9PIPELINETIMINGS | Einzelhandel/Debug | N/A |
RESOURCEMANAGER | D3DISSUE_END | D3DDEVINFO_ResourceManager | Nur Debuggen | präsentieren |
ZEITSTEMPEL | D3DISSUE_END | UINT64 | Einzelhandel/Debug | N/A |
TIMESTAMPDISJOINT | D3DISSUE_BEGIN, D3DISSUE_END | BOOL | Einzelhandel/Debug | N/A |
TIMESTAMPFREQ | D3DISSUE_END | UINT64 | Einzelhandel/Debug | N/A |
VCACHE | D3DISSUE_END | D3DDEVINFO_VCACHE | Einzelhandel/Debug | CreateDevice- |
VERTEXSTATS | D3DISSUE_END | D3DDEVINFO_D3DVERTEXSTATS | Nur Debuggen | präsentieren |
VERTEXTIMINGS | D3DISSUE_BEGIN, D3DISSUE_END | D3DDEVINFO_D3D9STAGETIMINGS | Einzelhandel/Debug | N/A |
Einige der Abfragen erfordern ein Anfangs- und Endereignis, während andere nur ein Endereignis erfordern. Die Abfragen, für die nur ein Endereignis erforderlich ist, 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 TRUEist. 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 Laufzeit Abfragen unterstützt, indem Sie CreateQuery- mit einem NULL- Zeiger wie folgt 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 im signalisierten Zustand (mit einer nicht initialisierten Antwort), die auf die Ausstellung wartet. Wenn Sie mit der Abfrage fertig sind, geben Sie sie wie jede andere Schnittstelle frei.
Abfrage ausstellen
Eine Anwendung ändert einen Abfragestatus, indem eine Abfrage ausgestellt wird. Hier ist 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 wechselt wie folgt, wenn sie ausgegeben wird:
Problemtyp | Abfrageübergänge zur . . . |
---|---|
D3DISSUE_BEGIN | Gebäudezustand. |
D3DISSUE_END | Ausgestellter Zustand. |
Eine Abfrage im Gebäudezustand wechselt wie folgt, wenn sie ausgegeben wird:
Problemtyp | Abfrageübergänge zur . . . |
---|---|
D3DISSUE_BEGIN | (Kein Übergang, bleibt im Gebäudezustand. Startet die Abfrageklammer neu.) |
D3DISSUE_END | Ausgestellter Zustand. |
Eine Abfrage im ausgestellten Zustand wechselt wie folgt, wenn sie ausgegeben wird:
Problemtyp | Abfrageübergänge zur . . . |
---|---|
D3DISSUE_BEGIN | Erstellen des Zustands und Neustarts der Abfrageklammer. |
D3DISSUE_END | Ausgegebener Zustand nach dem Verlassen der vorhandenen Abfrage. |
Überprüfen des Abfragestatus und Abrufen der Antwort auf die Abfrage
GetData- führt zwei Aktionen aus:
- Gibt den Abfragestatus im Rückgabecode zurück.
- Gibt die Antwort auf die Abfrage in pData-zurück.
Aus den drei Abfragezuständen finden Sie hier die GetData- Rückgabecodes:
Abfragestatus | GetData-Rückgabecode |
---|---|
Signalisierte | S_OK |
Gebäude | Fehlercode |
Ausgestellt | S_FALSE |
Wenn sich beispielsweise eine Abfrage im ausgegebenen Zustand befindet und die Antwort auf die Abfrage nicht verfügbar ist, gibt GetData- S_FALSE zurück. Wenn die Ressource ihre Arbeit beendet hat und die Anwendung ein Abfrageende ausgegeben hat, übergibt die Ressource die Abfrage in den signalierten Zustand. Aus dem signalisierten Zustand gibt GetData- S_OK zurück, was bedeutet, dass die Antwort an die Abfrage auch in pData-zurückgegeben wird. Hier ist beispielsweise die Abfolge von Ereignissen, um die Anzahl von Pixeln (oder Beispielen, wenn Multisampling aktiviert ist) in einer Rendersequenz zurückzugeben:
- Erstellen Sie die Abfrage.
- Ausgeben eines Anfangsereignisses.
- Zeichnen Sie etwas.
- Problem eines Endereignisses.
Es folgt die entsprechende Codesequenz:
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 verschiedene Aktionen aus:
- Rufen Sie GetData- auf, um die Anzahl der gezeichneten Pixel/Beispiele 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- teilt Ihnen im Wesentlichen mit, welcher Status die Abfrage ist. 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 Laufzeit) abgeschlossen ist. Die Abfrage wird an den signalisierten Zustand zurückgegeben. Die Antwort (falls vorhanden) wird von GetData-zurückgegeben.
- S_FALSE bedeutet, dass die Ressource (GPU oder Treiber oder Laufzeit) noch keine Antwort zurückgeben kann. Dies könnte darauf zurückzuführen sein, dass die GPU noch nicht abgeschlossen ist oder die Arbeit noch nicht gesehen hat.
- Ein Fehler bedeutet, dass die Abfrage einen Fehler generiert hat, von dem sie nicht wiederhergestellt werden 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_FLUSHanzugeben, das mehr up-to-Datumsinformationen bereitstellt, könnten Sie null angeben, bei der es sich um eine leichtere Prüfung handelt, wenn sich die Abfrage im ausgestellten Zustand befindet. Das Bereitstellen von Null führt dazu, dass GetData- den Befehlspuffer nicht geleert. Aus diesem Grund müssen Sie darauf achten, unendliche Schleifen zu vermeiden (weitere Informationen finden Sie unter GetData-). Da die Laufzeitwarteschlangen im Befehlspuffer funktionieren, ist D3DGETDATA_FLUSH ein Mechanismus zum Leeren des Befehlspuffers auf den Treiber (und damit die GPU; siehe Genaue Profilierung von Direct3D-API-Aufrufen (Direct3D 9)). Während des Leerens des Befehlspuffers kann eine Abfrage zum signalisierten Zustand wechseln.
Beispiel: Eine Ereignisabfrage
Eine Ereignisabfrage unterstützt kein Startereignis.
- Erstellen Sie die Abfrage.
- Problem eines Endereignisses.
- Abrufen, bis die GPU im Leerlauf ist.
- Problem eines Endereignisses.
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 Abfolge von Befehlen, die von einer Ereignisabfrage verwendet werden, um API-Aufrufe (Profile Application Programming Interface, API) zu profilieren (siehe genaue Profilerstellung von Direct3D-API-Aufrufen (Direct3D 9)). Diese Sequenz verwendet Markierungen, um die Menge der Arbeit im Befehlspuffer zu steuern.
Beachten Sie, dass Anwendungen besonders auf die großen Kosten achten sollten, die mit dem Leeren des Befehlspuffers verbunden sind, da dadurch das Betriebssystem in den Kernelmodus wechselt, was zu einer erheblichen Leistungseinbuße führt. Anwendungen sollten auch darauf achten, CPU-Zyklen zu verschwenden, indem sie auf den Abschluss von Abfragen warten.
Abfragen sind eine Optimierung, die beim Rendern verwendet werden soll, um die Leistung zu erhöhen. Daher ist es nicht von Vorteil, zeitlang zu verbringen, bis eine Abfrage abgeschlossen ist. Wenn eine Abfrage ausgegeben wird und die Ergebnisse zum Zeitpunkt der Überprüfung der Anwendung noch nicht bereit sind, ist der Versuch zur Optimierung nicht erfolgreich, und das Rendern sollte wie gewohnt fortgesetzt werden.
Das klassische Beispiel hierfür ist während occlusion Culling. Anstelle der , während obige Schleife ausgeführt wird, kann eine Anwendung, die Abfragen verwendet, okklusions-Culling implementieren, um zu überprüfen, ob eine Abfrage nach dem Zeitpunkt, zu dem das Ergebnis benötigt wurde, abgeschlossen war. Wenn die Abfrage noch nicht abgeschlossen ist, fahren Sie fort (als Szenario im ungünstigsten Fall), als ob das getestete Objekt nicht verdeckt ist (d. h. es ist sichtbar), und rendern Sie sie. Der Code sieht ähnlich 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();
}
Verwandte Themen