FAQ DirectXConsultez toutes les FAQ

Nombre d'auteurs : 4, nombre de questions : 63, dernière mise à jour : 2 septembre 2018 

 
OuvrirSommaireDirectGraphicsProblèmes

Avant de venir exposer votre problème sur les forums, il y a plusieurs étapes à suivre qui vous aideront à identifier votre problème.

Testez le retour de vos fonctions. Les composants DirectX ne lèvent pas d'exception, ainsi vous ne détecterez les erreurs qu'au premier plantage. Mais n'oubliez pas que ceux-ci retournent systématiquement un code d'erreur (ou de réussite), ainsi en les testant vous pourrez mettre le doigt plus précisément sur les comportements anormaux.

Utilisez la version debug. Cela vous permettra de déceler les comportements anormaux, les erreurs, ou encore les fuites mémoire de votre application. Voir http://jeux.developpez.com/faq/directx/?page=dgraphics_problemes#DGRAPHICS_PROBLEMES_debug.

Testez le reference rasterizer. Il vous indiquera si votre bogue provient d'une fonctionnalité non supportée. En effet, il s'agit d'un type de device supportant absolument toutes les fonctionnalités de Direct3D. Attention cependant à ne l'utiliser qu'à des fins de débogage : les fonctionnalités non supportées étant émulées en software, les performances seront très médiocres.
Pour utiliser le reference rasterizer dans votre application, il suffit de spécifier le flag D3DDEVTYPE_REF lors de la création du device.

Cherchez sur le net ! En recherchant sur les forums bien connus (Gamedev, Flipcode, Developpez…) ou de manière plus générale sur Google, on retrouve souvent les comportements anormaux que l'on constate, ainsi que la solution qui va avec. Pensez-y !

Créé le 22 janvier 2006  par Laurent Gomila

DirectX fournit depuis sa version 8 une version de débogage, capable de vous indiquer vos éventuelles erreurs et les comportements anormaux de votre application.

Pour l'activer, il faut se rendre dans le panneau de configuration de Windows, puis cliquer sur l'icône « DirectX ». Plusieurs onglets vous sont proposés, celui qui nous intéresse étant « Direct3D ». Puis, cochez le bouton « Use Debug version of Direct3D ». Le reste du panneau de configuration vous permet de paramétrer selon vos envies le niveau de débogage.

Les informations de débogage seront ensuite affichées dans la sortie debug de votre environnement de programmation. Si vous n'en possédez pas, vous pouvez également utiliser le programme DbMon (debugging monitor), qui récupère ces informations et les affiche dans une boîte de texte.

Dernière remarque, la version debug de DirectX est bien entendu beaucoup plus lente que la version retail, ne vous étonnez donc pas d'une perte de performance !

Créé le 22 janvier 2006  par Laurent Gomila

Dans une application plein écran, vous aurez sans doute remarqué qu'une perte du focus (causée la plupart du temps par un Alt+Tab de l'utilisateur, ou l'ouverture d'une fenêtre pop-up) entraîne un crash de celle-ci. Ainsi dans toute bonne application, il faut mettre sur pied un mécanisme pour gérer ces pertes de focus convenablement et éviter les plantages.

La perte du focus se traduit dans DirectGraphics par une perte du device. Une telle perte empêchera toute opération de rendu d'être effectuée, mais sans cependant retourner d'erreur. La seule exception est IDirect3DDevice9::Present, qui renverra D3DERR_DEVICELOST dans ce cas. On pourrait donc détecter la perte du device en testant le retour de Present, mais cela signifierait attendre d'avoir rendu toute la frame courante avant de détecter la perte du device, mais surtout, le retour de Present ne nous fournit pas suffisamment d'information.

La fonction IDirect3DDevice9::TestCooperativeLevel permet de récupérer l'état du device de manière plus précise :

  • D3D_OK : le device est valide et l'application peut tourner normalement ;
  • D3DERR_DEVICELOST : le device est perdu, mais ne peut pas encore être restauré ; il faut donc attendre ;
  • D3DERR_DEVICENOTRESET : le device est perdu, mais peut cette fois être restauré.

Ainsi il est conseillé de l'appeler à chaque frame, avant de débuter le rendu, et d'effectuer les opérations nécessaires selon l'état du device.

Une fois D3DERR_DEVICENOTRESET renvoyé, on peut donc restaurer le device. Cela se fait via la fonction IDirect3DDevice9::Reset, qui va prendre en paramètre un D3DPRESENT_PARAMETERS de la même manière que lorsque vous avez créé votre device. Mais le reset du device nécessite que certaines ressources soient recréées (et donc détruites auparavant). Toute tentative d'appel à Reset sans avoir libéré toutes les ressources échouera.
Cela concerne :

  • toutes les ressources (textures, surfaces, buffers…) créées dans le pool D3DPOOL_DEFAULT ;
  • les éventuelles swap chains additionnelles ;
  • les renderstates, qui auront repris leur valeur par défaut.

Cela concerne également certaines interfaces D3DX utilisant des ressources en interne :

  • ID3DXFont ;
  • ID3DXLine ;
  • ID3DXRenderToEnvMap ;
  • ID3DXRenderToSurface ;
  • ID3DXSprite ;
  • ID3DXEffect.

Cependant inutile de les détruire/recréer : il suffira d'appeler leur fonction membre OnLostDevice avant reset du device, et OnResetDevice après.
Pour avoir une liste à jour de ces interfaces, nous vous invitons à consulter la documentation du SDK.

Cela ne concerne donc pas les ressources créées dans les pools D3DPOOL_SYSTEMMEM et D3DPOOL_MANAGED, ainsi que les vertex/pixel shaders.

Voici un bout de code C++ résumant tout ce qui vient d'être dit :

 
Sélectionnez
// La boucle de rendu
void MainLoop()
{
    while (AppIsRunning) // Tant que l'application tourne
    {
        // ... Traitement des messages Windows

        if (CheckDevice()) // Teste l'état du device
        {
            RenderScene(); // Si le device est OK on rend la scène, sinon on attend qu'il soit restauré
        }
    }
}

// Teste et renvoie l'état du device
bool CheckDevice()
{
    // On récupère l'état du device
    HRESULT DeviceState = Device->TestCooperativeLevel();

    switch (DeviceState)
    {
        // Device OK
        case D3D_OK :
            return true;
    
        // Device perdu et pas encore récupérable : on attend...
        case D3DERR_DEVICELOST :
            Sleep(100);
            return false;
    
        // Device perdu mais récupérable
        case D3DERR_DEVICENOTRESET :
            ReleaseResources(); // Libération des ressources à libérer
            ResetDevice();      // Recréation du device
            RestoreResources(); // Rechargement des ressources
            return false;
    }
}

Afin de ne pas s'embêter avec ces considérations (et potentiellement mal les gérer), il est cependant recommandé d'utiliser comme base de projet un sample du SDK, par exemple EmptyProject. Celui-ci comporte tout le code nécessaire à la bonne gestion du device, vous n'aurez plus qu'à y insérer votre code de rendu.

Créé le 22 janvier 2006  par Laurent Gomila

On parle de fuite mémoire (memory leak) lorsqu'une ressource n'a pas été libérée proprement à la fin du programme. La mémoire associée étant inaccessible, si de nombreuses fuites interviennent cela peut très vite devenir un problème pour votre application.

DirectX (et de manière générale les objets COM) est particulièrement soumis à ce genre de fuite. Pour rappel, une ressource COM possède un compteur de référence interne qui est incrémenté à chaque fois que l'on obtient un pointeur sur la ressource. Lorsqu'on n'a plus besoin du pointeur, il faut systématiquement appeler la fonction Release, qui effectue une décrémentation du compteur. La ressource est automatiquement libérée lorsque le compteur atteint 0, ainsi si l'on oublie une seule fois un Release(), le compteur n'atteindra jamais 0 et la ressource ne sera jamais libérée.

Le debug de DirectX permet heureusement de détecter ces fuites et nous permet de les éradiquer facilement. Pour cela, il suffit d'activer le debug runtime (voir http://jeux.developpez.com/faq/directx/?page=dgraphics_problemes#DGRAPHICS_PROBLEMES_debug), et de regarder dans la fenêtre de sortie les identifiants des ressources qui n'ont pas été correctement libérées. Il est ensuite possible de cocher dans le panneau de configuration de Direct3D l'option « Break on alloc ID » afin que le débogueur s'arrête automatiquement sur l'allocation correspondant à l'identifiant spécifié.
Les identifiants proches de zéro correspondant généralement au device ou à l'objet d3d, qui ne peuvent être libérés correctement tant que d'autres ressources ne sont pas elles-mêmes libérées. Une bonne pratique est donc de commencer par régler le cas des identifiants les plus élevés.

Un autre procédé beaucoup plus simple et recommandé quoi qu'il arrive est d'utiliser (en C++) des pointeurs intelligents pour encapsuler vos ressources DirectX. Il existe par exemple la classe CComPtr, livrée avec Visual Studio (voir https://msdn.microsoft.com/en-us/library/aa266806%28v=vs.60%29.aspx ou encore http://www.codeproject.com/Articles/1458/A-COM-Smart-Pointer).

Créé le 22 janvier 2006  par Laurent Gomila

Ils n'existent tout simplement plus, depuis la version summer 2004 de DirectX 9. Les concepteurs ont jugé qu'il était trop difficile de maintenir ceux-ci à jour à chaque update, ainsi ils recommandent maintenant de simplement utiliser comme base le sample « Empty Project » fourni avec le SDK.

Créé le 22 janvier 2006  par Laurent Gomila

Il est possible que lors d'une update ou après lecture d'un tutoriel pas tout neuf, vous rencontriez des erreurs de compilation sur certaines fonctions de DirectGraphics, vous indiquant que les paramètres fournis ne sont pas ceux qu'attend la fonction. Ce n'est malheureusement pas un bogue : en effet, certains prototypes de fonctions sont modifiés lors d'une mise à jour du SDK, « cassant » ainsi votre vieux code.

C'est par exemple le cas de la fonction D3DXCreateFont, dont le prototype a été modifié entre DirectX 8 et DirectX 9.

 
Sélectionnez
// Version DirectX 8
HRESULT WINAPI D3DXCreateFont
(
    LPDIRECT3DDEVICE9 pDevice,
    HFONT             hFont,
    LPD3DXFONT*       ppFont
);

// Version DirectX 9   
HRESULT WINAPI D3DXCreateFont
(
    LPDIRECT3DDEVICE9 pDevice,
    INT               Height,
    UINT              Width,
    UINT              Weight,
    UINT              MipLevels,
    BOOL              Italic,
    DWORD             CharSet,
    DWORD             OutputPrecision,
    DWORD             Quality,
    DWORD             PitchAndFamily,
    LPCTSTR           pFacename,
    LPD3DXFONT*       ppFont
);

Si une telle erreur se produit, n'hésitez donc pas à consulter la documentation afin d'avoir le prototype correct pour votre version de DirectX.

Créé le 22 janvier 2006  par Laurent Gomila

Si tous vos appels aux fonctions Getxxx() du device échouent, c'est probablement que vous utilisez un pure device (créé avec le flag D3DCREATE_PUREDEVICE). En effet, comme stipulé dans la documentation ce flag permet d'effectuer des optimisations au niveau du stockage et de l'envoi des paramètres, et ceux-ci ne peuvent donc plus être récupérés via les fonctions Getxxx() du device. Cette restriction ne concerne que les états pouvant être stockés dans un bloc d'états (state block).

Créé le 22 janvier 2006  par Laurent Gomila

Pour désactiver la synchronisation verticale dans votre code, il suffit de modifier les paramètres du device de cette manière :

 
Sélectionnez
D3DPRESENT_PARAMETERS PresentParameters;
...
PresentParameters.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;

Pour plus de détails, vous pouvez consulter la page D3DPRESENT de la documentation du SDK.

À noter que la synchronisation verticale est également réglable dans les options de vos drivers, même si ceux-ci laissent généralement par défaut l'application la contrôler.

Créé le 10 mai 2006  par Laurent Gomila
  

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2005-2012 Developpez Developpez LLC. Tous droits réservés Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de son contenu : textes, documents et images sans l'autorisation expresse de Developpez LLC. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.