Pourquoi Direct3D ne récupère-t-il pas après avoir débranché un moniteur sous Windows XP?

Un bug intéressant est apparu et je n’ai pas de chance. Dans un programme Direct3D9 fenêtré utilisant du code natif, je gère un périphérique perdu à l’aide de quelque chose de similaire au suivant:

void MyClass::RecoverFromDeviceLost(LPDIRECT3DDEVICE9 deviceToRecover, D3DPRESENT_PARAMETERS devicePresentParams ) { HRESULT hr = deviceToRecover->TestCooperativeLevel(); if(hr == D3DERR_DEVICELOST ) { //Code to shutdown all D3DPOOL_DEFAULT allocated objects }else if(hr == D3DERR_DEVICENOTRESET){ hr = deviceToRecover->Reset(&devicePresentParams); if(SUCCEEDED(hr)) { //Code to rebuild all D3DPOOL_DEFAULT objects } } } 

Cela fonctionne bien sur Vista, mais semble avoir des problèmes majeurs sur XP. Si le moniteur est débranché ou déconnecté du PC via un KVM, je ne reçois jamais le D3DERR_DEVICELOST . La seule valeur renvoyée par TestCooperativeLevel que je reçois est D3DERR_DEVICENOTRESET . Et chaque appel à Reset donne un D3DERR_INVALIDCALL. J’ai essayé de forcer le programme à utiliser le code d’arrêt en procédant comme suit:

 ... else if(hr == D3DERR_DEVICENOTRESET){ hr = deviceToRecover->Reset(&devicePresentParams); if(SUCCEEDED(hr)) { //Code to rebuild all D3DPOOL_DEFAULT objects }else { //Duplicate of code to shutdown all D3DPOOL_DEFAULT objects } } ... 

Mais il n’y avait pas de changement. Ce problème semble n’affecter que Windows XP (jusqu’à présent testé sur SP2, SP3). J’utilise le DXSDK d’août 2007 et je ne peux pas mettre à jour pour le moment. Quelqu’un a-t-il déjà vu ce problème ou sait-il pourquoi je ne parviens pas à réinitialiser mon appareil?

UPDATE: Je pense avoir trouvé une solution , mais je suis toujours perplexe devant l’échec du second segment de code indiqué ci-dessus. Une fois que le runtime de débogage de DirectX fonctionnait via le débogage distant, j’ai compris que la fonction de réinitialisation continuait à échouer, car il y avait des ressources non libérées. Cependant, le même code de version, appliqué comme indiqué dans la réponse, résolvait le problème. J’ai vérifié que le programme ne créait pas d’objects D3DPOOL_DEFAULT entre les appels à la fonction de récupération. Existe-t-il quelque chose dans la structure de Direct3D qui pourrait causer un problème si vous effectuez une réinitialisation comme indiqué dans les segments de code de cette question?

J’ai fini par tester un programme différent qui utilise DirectX pour les graphismes, juste pour voir si le problème venait uniquement du programme en question. L’autre application a récupéré sans problème à partir d’un moniteur déconnecté ou d’un basculement KVM sous Windows XP. La principale différence entre les deux programmes était que celui qui travaillait utilisait DXUT pour gérer Direct3d, alors que je faisais toute la gestion manuelle dans celui qui ne fonctionnait pas. Après avoir analysé le code source DXUT, j’ai constaté qu’ils utilisaient une approche en une étape pour la récupération de périphérique qui ne D3DERR_DEVICELOST pas sur le D3DERR_DEVICELOST un D3DERR_DEVICELOST de TestCooperativeLevel avant la valeur D3DERR_DEVICENOTRESET . Le code suivant semble avoir résolu le problème:

 void MyClass::RecoverFromDeviceLost(LPDIRECT3DDEVICE9 deviceToRecover, D3DPRESENT_PARAMETERS devicePresentParams ) { HRESULT hr = deviceToRecover->TestCooperativeLevel(); if(hr == D3DERR_DEVICELOST ) { //Device is lost and cannot be reset yet return; } //Code to shutdown all D3DPOOL_DEFAULT allocated objects hr=deviceToRecover->Reset(&devicePresentParams); if(SUCCEEDED(hr)){ //Code to rebuild all D3DPOOL_DEFAULT objects } } 

Ce code a pour effet secondaire de réinitialiser plusieurs fois si le moniteur est débranché (ou commuté par KVM) pendant une période prolongée.

Il y a quelque temps, j’avais des symptômes similaires , en développant une application multi-moniteur. Le détwigment d’un moniteur ne se présente pas comme un périphérique DX perdu – le “périphérique” est une abstraction logicielle au niveau du système d’exploitation, et il n’est pas perdu lors du détwigment du moniteur.

Si, pour une raison quelconque, vous devez détecter le détwigment d’un moniteur au moment de l’exécution, même l’API Win32 EnumDisplayMonitors ne suffira pas. Comme cela a été expliqué dans l’article , cette API interroge le cache du pilote mis à jour par défaut uniquement lors du “démarrage, de la connexion ou de l’ouverture du panneau de configuration des propriétés d’affichage”. Maintenant, nous travaillons uniquement avec nVidia et ils exposent une fonctionnalité de mise à jour forcée en cache via NvCplRefreshConnectedDevices (lien vers NvCpl.dll pour l’utiliser). Je ne peux pas dire si ATI expose des fonctionnalités similaires, ou même si elles en ont besoin.

Et plus important encore – êtes-vous certain qu’un événement umplugging vous oblige à récupérer vos ressources? Je suppose que non.