2009-09-22 5 views
4

Un bug intéressant est venu que je n'ai pas de chance avec. Dans un programme Direct3D9 fenêtré en utilisant le code natif, je gère un appareil perdu en utilisant quelque chose de semblable à ce qui suit:Pourquoi Direct3D ne récupérera-t-il pas après avoir débranché un moniteur sous Windows XP?

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 éteint du PC via un KVM, je ne reçois jamais le D3DERR_DEVICELOST. La seule valeur de retour de 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 faisant ceci:

... 
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 aucun changement. Ce problème semble seulement affecter Windows XP (testé jusqu'à présent 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 a-t-il une idée de la raison pour laquelle je ne peux pas réinitialiser mon appareil?

MISE À JOUR: Je crois que j'ai found a solution, mais je suis toujours perplexe par l'échec du second segment de code énuméré ci-dessus. Après avoir exécuté l'exécution de DirectX Debug pour le débogage à distance, j'ai réalisé que la raison pour laquelle la fonction de réinitialisation continuait à échouer était parce qu'il y avait des ressources inédites. Cependant, le même code de version, lorsqu'il est appliqué comme indiqué dans la réponse, a résolu le problème. J'ai vérifié que le programme ne créait pas d'objets D3DPOOL_DEFAULT entre les appels à la fonction recover. Y a-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?

Répondre

3

J'ai fini par tester un programme différent qui utilise DirectX pour les graphiques, juste pour voir si le problème était juste avec le seul programme. L'autre application récupérée sans problèmes à partir d'un moniteur de débrancher ou de commutation KVM dans Windows XP. La principale différence entre les deux programmes était que celui qui travaillait utilisait DXUT pour gérer le Direct3d, alors que je faisais toute la gestion manuelle dans celui qui ne fonctionnait pas. Après avoir parcouru le code source DXUT, j'ai remarqué qu'ils utilisaient une approche en une seule étape de récupération de périphérique qui ne reposait pas sur un D3DERR_DEVICELOST renvoyé par TestCooperativeLevel avant la valeur de retour 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 le effet secondaire de la remise à zéro plusieurs fois si le moniteur est débranché (ou KVM commuté) pendant une période de temps prolongée.

+0

Je rencontrais une condition similaire où TestCooperativeLevel() renvoyait D3DERR_DEVICENOTRESET, mais l'appel de Reset() échouait. Je libérais les objets D3DPOOL_DEFAULT lorsque TestCooperativeLevel() renvoyait D3DERR_DEVICELOST. La résolution était d'attendre que TestCooperativeLevel() retourne D3DERR_DEVICENOTRESET; – Chris

2

Il y a quelque temps I had similar symptoms, le développement d'une application multi-moniteur. Le débranchement d'un moniteur non se présente 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 et non perdu lors du débranchement du moniteur.

Si, pour une raison quelconque, vous devez détecter le débranchement d'un moniteur lors de l'exécution, même l'API Win32 EnumDisplayMonitors ne suffira pas. Tel qu'expliqué dans the post, cette API interroge un cache de pilote qui est mis à jour par défaut uniquement lors du démarrage, de l'ouverture de session 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é force-cache-mise à jour via NvCplRefreshConnectedDevices (lien vers NvCpl.dll pour l'utiliser). Je ne peux pas dire si ATI expose des fonctionnalités similaires, ou même s'ils en ont besoin.

Et plus important encore - êtes-vous certain qu'un événement umplugging vous force à récupérer vos ressources? Ma conjecture n'est pas.

+0

Vous avez raison, l'événement unplug ne force pas la récupération des ressources dans Vista. Je dois prendre en charge les cartes non-NVIDIA, mais je vais regarder dans le cache du pilote et voir si cela me mène quelque part. – bsruth

Questions connexes