Partager via


Requêtes (Direct3D 9)

Il existe plusieurs types de requêtes conçues pour interroger l’status de ressources. Les status d’une ressource donnée incluent les status d’unité de traitement graphique (GPU), les status de pilote ou les status d’exécution. Pour comprendre la différence entre les différents types de requête, vous devez comprendre les états de la requête. Le diagramme de transition d’état suivant explique chacun des états de requête.

diagramme montrant les transitions entre les états de requête

Le diagramme montre trois états, chacun défini par des cercles. Chacune des lignes pleines est des événements pilotés par l’application qui provoquent une transition d’état. La ligne pointillée est un événement piloté par les ressources qui fait passer une requête de l’état émis à l’état signalé. Chacun de ces états a un objectif différent :

  • L’état signalé est semblable à un état inactif. L’objet de requête a été généré et attend que l’application émette la requête. Une fois qu’une requête est terminée et qu’elle est retournée à l’état signalé, la réponse à la requête peut être récupérée.
  • L’état de construction est semblable à une zone de préproduction pour une requête. À partir de l’état de génération, une requête a été émise (en appelant D3DISSUE_BEGIN), mais n’a pas encore été passée à l’état émis. Lorsqu’une application émet une fin de requête (en appelant D3DISSUE_END), la requête passe à l’état émis.
  • L’état émis signifie que la ressource interrogée a le contrôle de la requête. Une fois que la ressource a terminé son travail, la ressource fait passer l’ordinateur d’état à l’état signalé. Pendant l’état émis, l’application doit interroger pour détecter la transition vers l’état signalé. Une fois que la transition vers l’état signalé s’est produite, GetData retourne le résultat de la requête (par le biais d’un argument) à l’application.

Le tableau suivant répertorie les types de requête disponibles.

Type de requête Événement de problème Mémoire tampon GetData Runtime Début implicite de la requête
BANDWIDTHTIMINGS D3DISSUE_BEGIN, D3DISSUE_END D3DDEVINFO_D3D9BANDWIDTHTIMINGS Vente au détail/Débogage N/A
CACHEUTILIZATION D3DISSUE_BEGIN, D3DISSUE_END D3DDEVINFO_D3D9CACHEUTILIZATION Vente au détail/Débogage N/A
ÉVÉNEMENT D3DISSUE_END BOOL Vente au détail/Débogage CreateDevice
INTERFACETIMINGS D3DISSUE_BEGIN, D3DISSUE_END D3DDEVINFO_D3D9INTERFACETIMINGS Vente au détail/Débogage N/A
OCCLUSION D3DISSUE_BEGIN, D3DISSUE_END DWORD Vente au détail/Débogage N/A
PIPELINETIMINGS D3DISSUE_BEGIN, D3DISSUE_END D3DDEVINFO_D3D9PIPELINETIMINGS Vente au détail/Débogage N/A
RESOURCEMANAGER D3DISSUE_END D3DDEVINFO_ResourceManager Déboguer uniquement Présent
timestamp D3DISSUE_END UINT64 Vente au détail/Débogage N/A
TIMESTAMPDISJOINT D3DISSUE_BEGIN, D3DISSUE_END BOOL Vente au détail/Débogage N/A
TIMESTAMPFREQ D3DISSUE_END UINT64 Vente au détail/Débogage N/A
VCACHE D3DISSUE_END D3DDEVINFO_VCACHE Vente au détail/Débogage CreateDevice
VERTEXSTATS D3DISSUE_END D3DDEVINFO_D3DVERTEXSTATS Déboguer uniquement Présent
VERTEXTIMINGS D3DISSUE_BEGIN, D3DISSUE_END D3DDEVINFO_D3D9STAGETIMINGS Vente au détail/Débogage N/A

 

Certaines des requêtes nécessitent un événement de début et de fin, tandis que d’autres nécessitent uniquement un événement de fin. Les requêtes qui nécessitent uniquement un événement de fin commencent lorsqu’un autre événement implicite se produit (qui est répertorié dans le tableau). Toutes les requêtes retournent une réponse, à l’exception de la requête d’événement dont la réponse est toujours TRUE. Une application utilise l’état de la requête ou le code de retour de GetData.

Créer une requête

Avant de créer une requête, vous pouvez case activée pour voir si le runtime prend en charge les requêtes en appelant CreateQuery avec un pointeur NULL comme suit :

IDirect3DQuery9* pEventQuery;

// Create a device pointer m_pd3dDevice

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

Cette méthode retourne un code de réussite si une requête peut être créée ; sinon, il retourne un code d’erreur. Une fois CreateQuery réussi, vous pouvez créer un objet de requête comme suit :

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

Si cet appel réussit, un objet de requête est créé. La requête est essentiellement inactive dans l’état signalé (avec une réponse non initialisée) en attente d’émission. Lorsque vous avez terminé avec la requête, relâchez-la comme n’importe quelle autre interface.

Émettre une requête

Une application modifie l’état d’une requête en émettant une requête. Voici un exemple d’émission d’une requête :

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

Une requête à l’état signalé passe comme suit lorsqu’elle est émise :

Type de problème Transitions de requête vers . . .
D3DISSUE_BEGIN État de construction.
D3DISSUE_END État émis.

 

Une requête à l’état de génération passe comme suit lorsqu’elle est émise :

Type de problème Transitions de requête vers . . .
D3DISSUE_BEGIN (Aucune transition, reste à l’état de construction. Redémarre le crochet de requête.)
D3DISSUE_END État émis.

 

Une requête à l’état émis est transférée comme suit lorsqu’elle est émise :

Type de problème Transitions de requête vers . . .
D3DISSUE_BEGIN Génération de l’état et redémarre le crochet de requête.
D3DISSUE_END État émis après avoir abandonné la requête existante.

 

Vérifier l’état de la requête et obtenir la réponse à la requête

GetData effectue deux opérations :

  1. Retourne l’état de la requête dans le code de retour.
  2. Retourne la réponse à la requête dans pData.

À partir de chacun des trois états de requête, voici les codes de retour GetData :

État de requête Code de retour GetData
Signalé S_OK
Génération Code d'erreur
Émis S_FALSE

 

Par exemple, lorsqu’une requête est dans l’état émis et que la réponse à la requête n’est pas disponible, GetData retourne S_FALSE. Lorsque la ressource a terminé son travail et que l’application a émis une fin de requête, la ressource passe la requête à l’état signalé. À partir de l’état signalé, GetData retourne S_OK ce qui signifie que la réponse à la requête est également retournée dans pData. Pour instance, voici la séquence d’événements permettant de retourner le nombre de pixels (ou d’exemples lorsque le multi-échantillonnage est activé) dessinés dans une séquence de rendu :

  • Création de la requête
  • Émettre un événement begin.
  • Dessinez quelque chose.
  • Émettre un événement de fin.

Voici la séquence de code correspondante :

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.

Ces lignes de code effectuent plusieurs opérations :

  • Appelez GetData pour renvoyer le nombre de pixels/échantillons dessinés.
  • Spécifiez D3DGETDATA_FLUSH pour permettre à la ressource de passer la requête à l’état signalé.
  • Interrogez la ressource de requête en appelant GetData à partir d’une boucle. Tant que GetData retourne S_FALSE, cela signifie que la ressource n’a pas encore retourné la réponse.

La valeur de retour de GetData vous indique essentiellement dans quel état se trouve la requête. Les valeurs possibles sont S_OK, S_FALSE et une erreur. N’appelez pas GetData sur une requête à l’état de génération.

  • S_OK signifie que la ressource (GPU, pilote ou runtime) est terminée. La requête revient à l’état signalé. La réponse (le cas échéant) est retournée par GetData.
  • S_FALSE signifie que la ressource (GPU, pilote ou runtime) ne peut pas encore retourner de réponse. Cela peut être dû au fait que le GPU n’est pas terminé ou n’a pas encore vu le travail.
  • Une erreur signifie que la requête a généré une erreur à partir de laquelle elle ne peut pas récupérer. Cela peut être le cas si l’appareil est perdu pendant une requête. Une fois qu’une requête a généré une erreur (autre que S_FALSE), la requête doit être recréée pour redémarrer la séquence de requête à partir de l’état signalé.

Au lieu de spécifier D3DGETDATA_FLUSH, qui fournit des informations plus à jour, vous pouvez fournir zéro, ce qui est un case activée plus léger si la requête est à l’état émis. Si vous fournissez zéro, GetData ne vide pas la mémoire tampon de commandes. Pour cette raison, il faut veiller à éviter les boucles infinies (voir GetData pour plus d’informations). Étant donné que le runtime met en file d’attente le travail dans la mémoire tampon de commandes, D3DGETDATA_FLUSH est un mécanisme de vidage de la mémoire tampon de commandes sur le pilote (et par conséquent le GPU ; consultez Profilage précis des appels d’API Direct3D (Direct3D 9)). Pendant le vidage de la mémoire tampon de commande, une requête peut passer à l’état signalé.

Exemple : une requête d’événement

Une requête d’événement ne prend pas en charge un événement begin.

  • Création de la requête
  • Émettre un événement de fin.
  • Interroger jusqu’à ce que le GPU soit inactif.
  • Émettre un événement de fin.
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 ))
    ;

Il s’agit de la séquence de commandes qu’une requête d’événement utilise pour profiler les appels d’interface de programmation d’application (API) (voir Profilage précis des appels d’API Direct3D (Direct3D 9)). Cette séquence utilise des marqueurs pour contrôler la quantité de travail dans la mémoire tampon de commandes.

Notez que les applications doivent accorder une attention particulière au coût élevé associé au vidage de la mémoire tampon de commande, car cela entraîne le basculement du système d’exploitation en mode noyau, ce qui entraîne une pénalité importante en matière de performances. Les applications doivent également être conscientes de la gaspiller les cycles du processeur en attendant que les requêtes se terminent.

Les requêtes sont une optimisation à utiliser pendant le rendu pour augmenter les performances. Par conséquent, il n’est pas avantageux de passer du temps à attendre la fin d’une requête. Si une requête est émise et si les résultats ne sont pas encore prêts au moment où l’application les recherche, la tentative d’optimisation n’a pas réussi et le rendu doit continuer normalement.

L’exemple classique de ceci est pendant occlusion Culling. Au lieu de la boucle while ci-dessus, une application utilisant des requêtes peut implémenter l’élimination de l’occlusion pour case activée pour voir si une requête s’est terminée au moment où elle a besoin du résultat. Si la requête n’est pas terminée, continuez (dans le pire des cas) comme si l’objet testé n’était pas obstrué (c’est-à-dire qu’il était visible) et le restituez. Le code ressemble à ce qui suit.

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

Rubriques avancées