FAQ DirectX
FAQ DirectXConsultez toutes les FAQ
Nombre d'auteurs : 4, nombre de questions : 63, dernière mise à jour : 14 juin 2021
L'optimisation des vertex et index buffers passe par plusieurs étapes.
Optimisation de leur création
Lors de la création de buffers, il est important de correctement choisir le pool dans lequel ils seront mis. Il est conseillé de toujours utiliser D3DPOOL_DEFAULT, ce qui permettra au driver de placer le buffer au meilleur endroit possible. D3DPOOL_MANAGED est déconseillé : en effet le driver maintiendra deux copies du buffer, une en mémoire système et une autre en mémoire vidéo ; ainsi à chaque modification, vous gaspillerez du CPU et de la mémoire inutilement.
Il est également important de spécifier les options de création appropriées : par exemple si vous ne comptez jamais lire depuis votre buffer, indiquez le flag D3DUSAGE_WRITEONLY. Cela permettra encore une fois au driver d'optimiser le placement et l'accès au buffer.
Si vous souhaitez pouvoir récupérer des informations depuis votre buffer, il est alors conseillé de ne pas utiliser D3DPOOL_MANAGED et de créer votre propre copie en mémoire système (D3DPOOL_SYSTEMMEM).
Optimisation de leur taille
Il ne faudra pas négliger la taille de vos buffers. En effet, des buffers trop petits et trop nombreux vont surcharger la mémoire (le driver stocke des informations supplémentaires dans chaque buffer), et vont réduire les performances à cause des trop nombreux changements de buffers.
D'un autre côté, des buffers trop gros vont affecter le swapping des ressources, et causer une importante fragmentation de la mémoire qui sera du coup gaspillée.
La taille optimale dépend bien sûr de la mémoire disponible et doit être testée régulièrement, mais un bon point de départ est généralement entre 1 et 4 Mo pour un buffer statique, et entre 256 Ko et 1 Mo pour un buffer dynamique.
Optimisation des mises à jour
Lorsque vous verrouillez un buffer, pensez toujours à indiquer les flags appropriés.
Pour un buffer dynamique, utilisez D3DLOCK_DISCARD et D3DLOCK_NOOVERWRITE. D3DLOCK_NOOVERWRITE garantit que l'application ne va pas modifier ce qui se trouve déjà dans le buffer, et fournit donc un verrouillage très léger. D3DLOCK_DISCARD indique que l'on n'a plus besoin du buffer précédent, le driver peut ainsi allouer une nouvelle zone mémoire à remplir pendant qu'il traite l'ancien buffer. Pour plus de précisions, ceci est décrit en détail dans la documentation du SDK (page « Performance Optimizations » section « Using Dynamic Vertex and Index Buffers »).
Utiliser des index buffers en toute circonstance !
Cela permet de ne pas dupliquer les sommets utilisés par plusieurs polygones, ce qui a deux conséquences :
- d'une part, seuls les index buffers permettent d'utiliser le vertex cache de votre carte graphique, c'est-à-dire que si un même sommet apparaît plusieurs fois à la suite, il ne sera transformé et éclairé qu'une seule fois ;
- d'autre part, cela permet également de réduire la taille des données envoyées au hardware. En effet, un indice (qui n'est qu'un entier, généralement sur 16 bits) sera toujours plus petit qu'un vertex.
La première chose à faire, la plus banale et simple, est d'intégrer à votre application un compteur de FPS (frames par seconde). Le principe est de mesurer à l'aide de compteur très précis (QueryPerformanceCounter sous Windows) la durée du rendu d'une frame.
Pour connaître le framerate de votre application en temps réel, vous pouvez également utiliser le freeware FRAPS (http://www.fraps.com).
Il existe également des outils plus évolués :
- NvPerfHUD, outil gratuit fourni par nVidia permettant un débogage avancé et une analyse poussée des performances
(https://developer.nvidia.com/nvidia-perfhud); - PIX (Performance Investigator for DirectX), outil gratuit fourni avec le SDK DirectX pour analyser vos applications
(https://msdn.microsoft.com/en-us/library/windows/desktop/jj585574%28v=vs.85%29.aspx).
Au cœur de toute application Windows, se trouve ce que l'on appelle la boucle principale (main loop). C'est elle qui va récupérer les messages Windows, les traduire et les renvoyer à qui devra les traiter. Voici la boucle principale d'une application Windows classique :
MSG msg;
while
(GetMessage(&
msg, NULL
, 0
, 0
))
{
TranslateMessage(&
msg);
DispatchMessage(&
msg);
}
GetMessage est utilisé pour récupérer le premier message de la file de messages. Si aucun message n'est en attente, alors GetMessage endort le processus jusqu'à ce qu'un nouveau message arrive. Si le message est WM_QUIT, alors la fonction renvoie 0 stoppant ainsi la boucle principale et du même coup l'application.
TranslateMessage traduit les messages clavier, et doit obligatoirement être appelée avant de dispatcher le message.
DispatchMessage renvoie le message au gestionnaire ou à la fenêtre appropriés, qui devra à son tour le traiter correctement.
On remarque tout de suite que cette boucle est inadaptée au rendu temps réel : en effet, si aucun message Windows n'est reçu l'application sera endormie ! Pour pallier ce problème, il existe la fonction PeekMessage, équivalente à GetMessage, mais ne bloquant pas l'exécution si aucun message n'est dans la file d'attente. Sa valeur de retour est également différente : PeekMessage renvoie un booléen indiquant si un message a été récupéré, et non plus la fin de l'exécution.
Voici donc à quoi doit ressembler la boucle principale d'une application utilisant DirectGraphics :
// On initialise une première fois notre message
MSG msg;
PeekMessage(&
msg, NULL
, 0
, 0
, PM_NOREMOVE);
// On boucle tant qu'on n'a pas reçu le message WM_QUIT
while
(msg.message !=
WM_QUIT)
{
// Y a t-il un message à traiter ?
if
(PeekMessage(&
msg, NULL
, 0
, 0
, PM_REMOVE))
{
// Oui : on le traduit et on le dispatche
TranslateMessage(&
msg);
DispatchMessage(&
msg);
}
else
{
// Non : on peut effectuer notre rendu
RenderScene();
}
}
Enfin, pour une gestion optimale, on peut également utiliser les deux fonctions conjointement : PeekMessage lorsque votre application est active, et GetMessage lorsqu'elle est inactive (récupérer le message WM_ACTIVATEAPP). Ainsi tant que votre application est inactive, vous ne gaspillerez pas de temps CPU inutilement puisque GetMessage endormira le processus.