Partager via


Requêtes (Direct3D 9)

Plusieurs types de requêtes sont conçus pour interroger l’état des ressources. L’état d’une ressource donnée inclut l’état de l’unité de traitement graphique (GPU), l’état du pilote ou l’état du runtime. Pour comprendre la différence entre les différents types de requêtes, vous devez comprendre les états de requête. Le diagramme de transition d’état suivant décrit 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 solides est des événements pilotés par l’application qui provoquent une transition d’état. La ligne en pointillés est un événement piloté par les ressources qui bascule une requête de l’état émis vers 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 a terminé et revient à l’état signalé, la réponse à la requête peut être récupérée.
  • L’état de génération est semblable à une zone intermédiaire 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 passé à 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 passe 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 la transition vers l’état signalé, GetData retourne le résultat de la requête (via un argument) à l’application.

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

Type de requête Événement de problème Mémoire tampon GetData Duree Début implicite de la requête
BANDE PASSANTETIMINGS 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
HORODATAGE 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 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 la table). 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 vérifier 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, elle retourne un code d’erreur. Une fois CreateQuery réussit, 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. Une fois la requête terminée, 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 dans l’état signalé passe comme suit lors de l’émission :

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

 

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

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

 

Une requête dans l’état émis passe comme suit lors de l’émission :

Type de problème Transitions de requête vers le . . .
D3DISSUE_BEGIN État de génération et redémarrage du crochet de requête.
D3DISSUE_END État émis après l’abandon de 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
Bâtiment Code d’erreur
Délivré 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 termine 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 . Par exemple, voici la séquence d’événements pour retourner le nombre de pixels (ou d’exemples lorsque l’échantillonnage multiple est activé) dessiné dans une séquence de rendu :

  • Créez la requête.
  • Émettre un événement de début.
  • 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 retourner le nombre de pixels/exemples dessinés.
  • Spécifiez D3DGETDATA_FLUSH pour permettre à la ressource de passer de 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 l’état de la requête. Les valeurs possibles sont S_OK, S_FALSE et une erreur. N’appelez pas GetData sur une requête qui est dans l’état de génération.

  • S_OK signifie que la ressource (GPU ou pilote ou runtime) est terminée. La requête retourne à l’état signalé. La réponse (le cas échéant) est retournée par GetData.
  • S_FALSE signifie que la ressource (GPU ou pilote, ou runtime) ne peut pas encore retourner une 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êtes à partir de l’état signalé.

Au lieu de spécifier D3DGETDATA_FLUSH, qui fournit plus d’informations up-to-date, vous pouvez fournir zéro qui est une vérification plus légère si la requête est dans l’état émis. L’approvisionnement zéro entraîne GetData ne vide pas la mémoire tampon de commande. Pour cette raison, vous devez 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 commande, D3DGETDATA_FLUSH est un mécanisme permettant de vider la mémoire tampon de commande sur le pilote (et donc le GPU ; voir 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 : requête d’événement

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

  • Créez la requête.
  • Émettre un événement de fin.
  • Interrogez 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 commande.

Notez que les applications doivent porter une attention particulière au coût important 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é de performances volumineuse. Les applications doivent également être conscientes de l’interruption des 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 utile 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 l'alors que boucle ci-dessus, une application utilisant des requêtes peut implémenter un culclusion occlusion pour vérifier si une requête a été terminée au moment où elle a besoin du résultat. Si la requête n’a pas terminé, continuez (comme un scénario pire) comme si l’objet testé n’est pas obstrué (c’est-à-dire visible) et affichez-le. 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