Améliorations de Direct3D 9Ex

Cette rubrique décrit le support supplémentaire de Windows 7 pour le Flip Mode Present et ses statistiques de présentation associées dans Direct3D 9Ex et Desktop Window Manager. Les applications ciblées incluent les applications de présentation basées sur la vidéo ou le taux de rafraîchissement. Les applications qui utilisent Direct3D 9Ex Flip Mode Present réduisent la charge sur les ressources système lorsque DWM est activé. Les améliorations des statistiques de présentation associées au Flip Mode Present permettent aux applications Direct3D 9Ex de mieux contrôler le taux de présentation en fournissant des mécanismes de retour d’information et de correction en temps réel. Des explications détaillées et des indications vers des ressources d’exemples sont incluses.

Cette rubrique contient les sections suivantes.

Qu’est-ce qui est amélioré dans Direct3D 9Ex pour Windows 7

Le Flip Mode Presentation de Direct3D 9Ex est un mode amélioré de présentation d’images dans Direct3D 9Ex qui transfère efficacement les images rendues au Windows 7 Desktop Window Manager (DWM) pour la composition. À partir de Windows Vista, DWM compose l’ensemble du bureau. Lorsque DWM est activé, les applications en mode fenêtré présentent leur contenu sur le bureau en utilisant une méthode appelée Blt Mode Present to DWM (ou modèle Blt). Avec le modèle Blt, DWM maintient une copie de la surface rendue par Direct3D 9Ex pour la composition du bureau. Lorsque l’application se met à jour, le nouveau contenu est copié sur la surface DWM via un blt. Pour les applications qui contiennent du contenu Direct3D et GDI, les données GDI sont également copiées sur la surface DWM.

Disponible dans Windows 7, le Flip Mode Present to DWM (ou modèle Flip) est une nouvelle méthode de présentation qui permet essentiellement de transmettre des handles des surfaces d’application entre les applications en mode fenêtré et DWM. En plus d’économiser des ressources, le modèle Flip prend en charge des statistiques de présentation améliorées.

Les statistiques de présentation sont des informations de synchronisation des trames que les applications peuvent utiliser pour synchroniser les flux vidéo et audio et récupérer des problèmes de lecture vidéo. Les informations de synchronisation des trames dans les statistiques de présentation permettent aux applications d’ajuster le taux de présentation de leurs trames vidéo pour une présentation plus fluide. Dans Windows Vista, où DWM maintient une copie correspondante de la surface de trame pour la composition du bureau, les applications peuvent utiliser les statistiques de présentation fournies par DWM. Cette méthode d’obtention des statistiques de présentation sera toujours disponible dans Windows 7 pour les applications existantes.

Dans Windows 7, les applications basées sur Direct3D 9Ex qui adoptent le modèle Flip devraient utiliser les API D3D9Ex pour obtenir les statistiques de présentation. Lorsque DWM est activé, les applications Direct3D 9Ex en mode fenêtré et en mode exclusif plein écran peuvent s’attendre à la même information de statistiques de présentation lors de l’utilisation du modèle Flip. Les statistiques de présentation du modèle Flip Direct3D 9Ex permettent aux applications de demander des statistiques de présentation en temps réel, plutôt qu’après que la trame ait été affichée à l’écran. La même information de statistiques de présentation est disponible pour les applications en mode fenêtré avec modèle Flip que pour les applications en plein écran. Un indicateur ajouté dans les API D3D9Ex permet aux applications du modèle Flip de rejeter efficacement les trames tardives au moment de la présentation.

Le modèle Flip Direct3D 9Ex devrait être utilisé par les nouvelles applications de présentation basées sur la vidéo ou le taux de rafraîchissement qui ciblent Windows 7. En raison de la synchronisation entre DWM et le runtime Direct3D 9Ex, les applications qui utilisent le modèle Flip devraient spécifier entre 2 et 4 backbuffers pour assurer une présentation fluide. Les applications qui utilisent l’information des statistiques de présentation bénéficieront des améliorations des statistiques de présentation activées par le modèle Flip.

Présentation du mode Flip Direct3D 9EX

Les améliorations des performances de Direct3D 9Ex Flip Mode Present sont significatives sur le système lorsque DWM est activé et lorsque l’application est en mode fenêtré, plutôt qu’en mode exclusif plein écran. Le tableau et l’illustration suivants montrent une comparaison simplifiée des utilisations de bande passante mémoire et des lectures et écritures système des applications fenêtrées qui choisissent le modèle Flip par rapport à l’utilisation par défaut du modèle Blt.

Blt mode present to DWM D3D9Ex Flip Mode Present to DWM
1. L’application met à jour sa trame (Écriture)
1. L’application met à jour sa trame (Écriture)
2. Le runtime Direct3D copie le contenu de la surface vers une surface de redirection DWM (Lecture, Écriture)
2. Le runtime Direct3D transmet la surface de l’application à DWM
3. Après la copie de la surface partagée, DWM rend la surface de l’application à l’écran (Lecture, Écriture)
3. DWM rend la surface de l’application à l’écran (Lecture, Écriture)

illustration d’une comparaison entre le modèle blt et le modèle flip

Flip Mode Present réduit l’utilisation de la mémoire système en réduisant le nombre de lectures et d’écritures par le runtime Direct3D pour la composition de trames fenêtrées par DWM. Cela réduit la consommation d’énergie du système et l’utilisation globale de la mémoire.

Les applications peuvent tirer parti des améliorations des statistiques de présentation Direct3D 9Ex Flip Mode lorsque DWM est activé, que l’application soit en mode fenêtré ou en mode exclusif plein écran.

Modèle de programmation et API

Les nouvelles applications basées sur la vidéo ou le taux de rafraîchissement qui utilisent les API Direct3D 9Ex sur Windows 7 peuvent tirer parti des économies de mémoire et d’énergie et de la présentation améliorée offerte par Flip Mode Present lors de l’exécution sur Windows 7. (Lors de l’exécution sur des versions précédentes de Windows, le runtime Direct3D par défaut de l’application utilise Blt Mode Present).

Flip Mode Present implique que l’application peut tirer parti des statistiques de présentation en temps réel et des mécanismes de correction lorsque DWM est activé. Cependant, les applications qui utilisent Flip Mode Present doivent être conscientes des limitations lorsqu’elles utilisent le rendu API GDI simultané.

Vous pouvez modifier les applications existantes pour tirer parti de Flip Mode Present, avec les mêmes avantages et mises en garde que les applications nouvellement développées.

Comment opter pour le modèle Flip Direct3D 9Ex

Les applications Direct3D 9Ex qui ciblent Windows 7 peuvent opter pour le modèle Flip en créant la chaîne d’échange avec la valeur d’énumération D3DSWAPEFFECT_FLIPEX. Pour opter pour le modèle Flip, les applications spécifient la structure D3DPRESENT_PARAMETERS, puis passent un pointeur vers cette structure lorsqu’elles appellent l’API IDirect3D9Ex::CreateDeviceEx. Cette section décrit comment les applications qui ciblent Windows 7 utilisent IDirect3D9Ex::CreateDeviceEx pour opter pour le modèle Flip. Pour plus d’informations sur l’API IDirect3D9Ex::CreateDeviceEx, veuillez consulter la section IDirect3D9Ex::CreateDeviceEx on MSDN.

Pour plus de commodité, la syntaxe de D3DPRESENT_PARAMETERS et IDirect3D9Ex::CreateDeviceEx est répétée ici.

HRESULT CreateDeviceEx(
  UINT Adapter,
  D3DDEVTYPE DeviceType,
  HWND hFocusWindow,
  DWORD BehaviorFlags,
  D3DPRESENT_PARAMETERS* pPresentationParameters,
  D3DDISPLAYMODEEX *pFullscreenDisplayMode,
  IDirect3DDevice9Ex **ppReturnedDeviceInterface
);
typedef struct D3DPRESENT_PARAMETERS {
    UINT BackBufferWidth, BackBufferHeight;
    D3DFORMAT BackBufferFormat;
    UINT BackBufferCount;
    D3DMULTISAMPLE_TYPE MultiSampleType;
    DWORD MultiSampleQuality;
    D3DSWAPEFFECT SwapEffect;
    HWND hDeviceWindow;
    BOOL Windowed;
    BOOL EnableAutoDepthStencil;
    D3DFORMAT AutoDepthStencilFormat;
    DWORD Flags;
    UINT FullScreen_RefreshRateInHz;
    UINT PresentationInterval;
} D3DPRESENT_PARAMETERS, *LPD3DPRESENT_PARAMETERS;

Lorsque vous modifiez des applications Direct3D 9Ex pour Windows 7 afin d’opter pour le modèle Flip, vous devez prendre en compte les éléments suivants concernant les membres spécifiés de D3DPRESENT_PARAMETERS :

BackBufferCount

(Windows 7 uniquement)

Lorsque SwapEffect est réglé sur le nouveau type d’effet de chaîne d’échange D3DSWAPEFFECT_FLIPEX, le nombre de backbuffers doit être égal ou supérieur à 2, pour éviter une pénalité de performance de l’application en raison de l’attente que le buffer de présentation précédent soit libéré par DWM.

Lorsque l’application utilise également des statistiques de présentation associées à D3DSWAPEFFECT_FLIPEX, nous recommandons de régler le nombre de backbuffers entre 2 et 4.

L’utilisation de D3DSWAPEFFECT_FLIPEX sur Windows Vista ou des versions précédentes du système d’exploitation renverra un échec de CreateDeviceEx.

SwapEffect

(Windows 7 uniquement)

Le nouveau type d’effet de chaîne d’échange D3DSWAPEFFECT_FLIPEX désigne quand une application adopte Flip Mode Present vers DWM. Il permet à l’application d’utiliser la mémoire et l’énergie plus efficacement, et permet également à l’application de tirer parti des statistiques de présentation plein écran en mode fenêtré. Le comportement de l’application en plein écran n’est pas affecté. Si Windowed est réglé sur TRUE et SwapEffect est réglé sur D3DSWAPEFFECT_FLIPEX, le runtime crée un buffer arrière supplémentaire et fait tourner le handle qui appartient au buffer qui devient le buffer avant au moment de la présentation.

Indicateurs

(Windows 7 uniquement)

L’indicateur D3DPRESENTFLAG_LOCKABLE_BACKBUFFER ne peut pas être réglé si SwapEffect est réglé sur le nouveau type d’effet de chaîne d’échange D3DSWAPEFFECT_FLIPEX.

Lignes directrices pour la conception d’applications du modèle Flip Direct3D 9Ex

Utilisez les lignes directrices des sections suivantes pour concevoir vos applications du modèle Flip Direct3D 9Ex.

Utilisez Flip Mode Present dans un HWND séparé de Blt Mode Present

Les applications doivent utiliser Direct3D 9Ex Flip Mode Present dans un HWND qui n’est pas également ciblé par d’autres API, y compris Blt Mode Present Direct3D 9Ex, d’autres versions de Direct3D, ou GDI. Flip Mode Present peut être utilisé pour présenter à des fenêtres enfants ; c’est-à-dire que les applications peuvent utiliser le modèle Flip lorsqu’il n’est pas mélangé avec le modèle Blt dans le même HWND, comme illustré dans les illustrations suivantes.

illustration d’une fenêtre parent direct3d et d’une fenêtre enfant gdi, chacune avec son propre hwnd

illustration d’une fenêtre parent gdi et d’une fenêtre enfant direct3d, chacune avec son propre hwnd

Étant donné que le modèle Blt maintient une copie supplémentaire de la surface, le contenu GDI et autres Direct3D peuvent être ajoutés au même HWND via des mises à jour progressives de Direct3D et GDI. En utilisant le modèle Flip, seul le contenu Direct3D 9Ex dans les chaînes d’échange D3DSWAPEFFECT_FLIPEX qui sont passées à DWM sera visible. Toutes les autres mises à jour de contenu Blt Model Direct3D ou GDI seront ignorées, comme illustré dans les illustrations suivantes.

illustration de texte gdi qui pourrait ne pas être affiché si le modèle flip est utilisé et que le contenu direct3d et gdi est dans le même hwnd

illustration de contenu direct3d et gdi dans lequel dwm est activé et l’application est en mode fenêtré

Par conséquent, le modèle Flip doit être activé pour les surfaces des buffers de la chaîne d’échange où le modèle Flip Direct3D 9Ex seul rend l’ensemble du HWND.

Ne pas utiliser le modèle Flip avec les fonctions ScrollWindow ou ScrollWindowEx de GDI

Certaines applications Direct3D 9Ex utilisent les fonctions ScrollWindow ou ScrollWindowEx de GDI pour mettre à jour le contenu de la fenêtre lorsqu’un événement de défilement utilisateur est déclenché. ScrollWindow et ScrollWindowEx effectuent des blts du contenu de la fenêtre à l’écran lorsqu’une fenêtre est défilée. Ces fonctions nécessitent également des mises à jour du modèle Blt pour le contenu GDI et Direct3D 9Ex. Les applications qui utilisent l’une ou l’autre fonction n’afficheront pas nécessairement le défilement du contenu visible de la fenêtre à l’écran lorsque l’application est en mode fenêtré et que DWM est activé. Nous vous recommandons de ne pas utiliser les API ScrollWindow et ScrollWindowEx de GDI dans vos applications et de redessiner leur contenu à l’écran en réponse au défilement.

Utilisez une chaîne d’échange D3DSWAPEFFECT_FLIPEX par HWND

Les applications qui utilisent le modèle Flip ne doivent pas utiliser plusieurs chaînes d’échange du modèle Flip ciblant le même HWND.

Synchronisation de trame des applications du modèle Flip Direct3D 9Ex

Les statistiques de présentation sont des informations de synchronisation de trame que les applications multimédias utilisent pour synchroniser les flux vidéo et audio et récupérer des problèmes de lecture vidéo. Pour activer la disponibilité des statistiques de présentation, l’application Direct3D 9Ex doit s’assurer que le paramètre BehaviorFlags que l’application passe à IDirect3D9Ex::CreateDeviceEx contient l’indicateur de comportement de périphérique D3DCREATE_ENABLE_PRESENTSTATS.

Pour plus de commodité, la syntaxe de IDirect3D9Ex::CreateDeviceEx est répétée ici.

HRESULT CreateDeviceEx(
  UINT Adapter,
  D3DDEVTYPE DeviceType,
  HWND hFocusWindow,
  DWORD BehaviorFlags,
  D3DPRESENT_PARAMETERS* pPresentationParameters,
  D3DDISPLAYMODEEX *pFullscreenDisplayMode,
  IDirect3DDevice9Ex **ppReturnedDeviceInterface
);

Le modèle Flip Direct3D 9Ex ajoute l’indicateur de présentation D3DPRESENT_FORCEIMMEDIATE qui impose le comportement de l’indicateur de présentation D3DPRESENT_INTERVAL_IMMEDIATE. L’application Direct3D 9Ex spécifie ces indicateurs de présentation dans le paramètre dwFlags que l’application passe à IDirect3DDevice9Ex::PresentEx, comme illustré ici.

HRESULT PresentEx(
  CONST RECT *pSourceRect,
  CONST RECT *pDestRect,
  HWND hDestWindowOverride,
  CONST RGNDATA *pDirtyRegion,
  DWORD dwFlags
);

Lorsque vous modifiez votre application Direct3D 9Ex pour Windows 7, vous devez prendre en compte les informations suivantes concernant les indicateurs de présentation D3DPRESENT spécifiés :

D3DPRESENT_DONOTFLIP

Cet indicateur est disponible uniquement en mode plein écran ou

(Windows 7 uniquement)

lorsque l’application définit le membre SwapEffect de D3DPRESENT_PARAMETERS sur D3DSWAPEFFECT_FLIPEX dans un appel à CreateDeviceEx.

D3DPRESENT_FORCEIMMEDIATE

(Windows 7 uniquement)

Cet indicateur ne peut être spécifié que si l’application définit le membre SwapEffect de D3DPRESENT_PARAMETERS sur D3DSWAPEFFECT_FLIPEX dans un appel à CreateDeviceEx. L’application peut utiliser cet indicateur pour mettre à jour immédiatement une surface avec plusieurs trames plus tard dans la file d’attente de présentation DWM, sautant essentiellement des trames intermédiaires.

Les applications fenêtrées activées pour FlipEx peuvent utiliser cet indicateur pour mettre à jour immédiatement une surface avec une trame qui est plus tard dans la file d’attente de présentation DWM, sautant les trames intermédiaires. Cela est particulièrement utile pour les applications multimédias qui souhaitent éliminer les trames qui ont été détectées comme tardives et présenter les trames suivantes au moment de la composition. IDirect3DDevice9Ex::PresentEx retourne une erreur de paramètre invalide si cet indicateur est mal spécifié.

Pour obtenir des informations sur les statistiques de présentation, l’application obtient la structure D3DPRESENTSTATS en appelant l’API IDirect3DSwapChain9Ex::GetPresentStatistics.

La structure D3DPRESENTSTATS contient des statistiques sur les appels IDirect3DDevice9Ex::PresentEx. Le périphérique doit être créé en utilisant un appel IDirect3D9Ex::CreateDeviceEx avec l’indicateur D3DCREATE_ENABLE_PRESENTSTATS. Sinon, les données renvoyées par GetPresentStatistics sont indéfinies. Une chaîne d’échange activée pour le modèle Flip Direct3D 9Ex fournit des informations de statistiques de présentation en modes fenêtré et plein écran.

Pour les chaînes d’échange activées pour le modèle Blt Direct3D 9Ex en mode fenêtré, toutes les valeurs de la structure D3DPRESENTSTATS seront des zéros.

Pour les statistiques de présentation FlipEx, GetPresentStatistics renvoie D3DERR_PRESENT_STATISTICS_DISJOINT dans les situations suivantes :

  • Premier appel à GetPresentStatistics jamais fait, ce qui indique le début d’une séquence
  • Transition DWM de activé à désactivé
  • Changement de mode : soit passage du mode fenêtré au mode plein écran ou transition de plein écran à plein écran

Pour plus de commodité, la syntaxe de GetPresentStatistics est répétée ici.

HRESULT GetPresentStatistics(
  D3DPRESENTSTATS * pPresentationStatistics
);

La méthode IDirect3DSwapChain9Ex::GetLastPresentCount retourne le dernier PresentCount, c’est-à-dire, l’ID de présentation du dernier appel de présentation réussi effectué par un périphérique d’affichage associé à la chaîne d’échange. Cet ID de présentation est la valeur du membre PresentCount de la structure D3DPRESENTSTATS. Pour les chaînes d’échange activées pour le modèle Blt Direct3D 9Ex, en mode fenêtré, toutes les valeurs de la structure D3DPRESENTSTATS seront des zéros.

Pour plus de commodité, la syntaxe de IDirect3DSwapChain9Ex::GetLastPresentCount est répétée ici.

HRESULT GetLastPresentCount(
  UINT * pLastPresentCount
);

Lorsque vous modifiez votre application Direct3D 9Ex pour Windows 7, vous devez prendre en compte les informations suivantes concernant la structure D3DPRESENTSTATS :

  • La valeur PresentCount que GetLastPresentCount retourne ne se met pas à jour lorsqu’un appel PresentEx avec D3DPRESENT_DONOTWAIT spécifié dans le paramètre dwFlags retourne un échec.
  • Lorsque PresentEx est appelé avec D3DPRESENT_DONOTFLIP, un appel GetPresentStatistics réussit mais ne renvoie pas une structure D3DPRESENTSTATS mise à jour lorsque l’application est en mode fenêtré.
  • PresentRefreshCount par rapport à SyncRefreshCount dans D3DPRESENTSTATS :
    • PresentRefreshCount est égal à SyncRefreshCount lorsque l’application se présente à chaque vsync.
    • SyncRefreshCount est obtenu sur l’intervalle vsync lorsque la présentation a été soumise, SyncQPCTime est approximativement le temps associé à l’intervalle vsync.
typedef struct _D3DPRESENTSTATS {
    UINT PresentCount;
    UINT PresentRefreshCount;
    UINT SyncRefreshCount;
    LARGE_INTEGER SyncQPCTime;
    LARGE_INTEGER SyncGPUTime;
} D3DPRESENTSTATS;

Synchronisation de trame pour les applications fenêtrées lorsque DWM est désactivé

Lorsque DWM est désactivé, les applications fenêtrées s’affichent directement sur l’écran du moniteur sans passer par une chaîne de retournement. Dans Windows Vista, il n’y a pas de support pour obtenir des informations sur les statistiques de trame pour les applications fenêtrées lorsque DWM est désactivé. Pour maintenir une API où les applications n’ont pas besoin d’être conscientes de DWM, Windows 7 renverra des informations sur les statistiques de trame pour les applications fenêtrées lorsque DWM est désactivé. Les statistiques de trame renvoyées lorsque DWM est désactivé sont uniquement des estimations.

Étude approfondie d’un exemple de modèle Flip Direct3D 9Ex et de statistiques de présentation

Pour opter pour la présentation FlipEx pour l’exemple Direct3D 9Ex

  1. Assurez-vous que l’application exemple fonctionne sur Windows 7 ou une version ultérieure du système d’exploitation.
  2. Définissez le membre SwapEffect de D3DPRESENT_PARAMETERS sur D3DSWAPEFFECT_FLIPEX dans un appel à CreateDeviceEx.
    OSVERSIONINFO version;
    ZeroMemory(&version, sizeof(version));
    version.dwOSVersionInfoSize = sizeof(version);
    GetVersionEx(&version);
    
    // Sample would run only on Win7 or higher
    // Flip Model present and its associated present statistics behavior are only available on Windows 7 or higher operating system
    bool bIsWin7 = (version.dwMajorVersion > 6) || 
        ((version.dwMajorVersion == 6) && (version.dwMinorVersion >= 1));

    if (!bIsWin7)
    {
        MessageBox(NULL, L"This sample requires Windows 7 or higher", NULL, MB_OK);
        return 0;
    }

Pour également opter pour les statistiques de présentation associées à FlipEx pour l’exemple Direct3D 9Ex

    // Set up the structure used to create the D3DDevice
    D3DPRESENT_PARAMETERS d3dpp;
    ZeroMemory(&d3dpp, sizeof(d3dpp));

    d3dpp.Windowed = TRUE;
    d3dpp.SwapEffect = D3DSWAPEFFECT_FLIPEX;        // Opts into Flip Model present for D3D9Ex swapchain
    d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;
    d3dpp.BackBufferWidth = 256;                
    d3dpp.BackBufferHeight = 256;
    d3dpp.BackBufferCount = QUEUE_SIZE;
    d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_ONE;

    g_iWidth = d3dpp.BackBufferWidth;
    g_iHeight = d3dpp.BackBufferHeight;

    // Create the D3DDevice with present statistics enabled - set D3DCREATE_ENABLE_PRESENTSTATS for behaviorFlags parameter
    if(FAILED(g_pD3D->CreateDeviceEx(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
                                      D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_ENABLE_PRESENTSTATS,
                                      &d3dpp, NULL, &g_pd3dDevice)))
    {
        return E_FAIL;
    }

Pour éviter, détecter et récupérer des anomalies

  1. Mettez en file d’attente les appels Present : le nombre recommandé de backbuffers est de 2 à 4.

  2. L’échantillon Direct3D 9Ex ajoute un backbuffer implicite, la longueur réelle de la file d’attente Present est donc le nombre de backbuffers + 1.

  3. Créez une structure de file d’attente Present auxiliaire pour stocker tous les ID Present soumis avec succès (PresentCount) et le PresentRefreshCount associé, calculé/attendu.

  4. Pour détecter la survenue d’une anomalie :

    • Appelez GetPresentStatistics.
    • Obtenez l’ID Present (PresentCount) et le nombre de vsync où la trame est affichée (PresentRefreshCount) de la trame dont les statistiques de présentation sont obtenues.
    • Récupérez le PresentRefreshCount attendu (TargetRefresh dans le code d’exemple) associé à l’ID Present.
    • Si le PresentRefreshCount réel est ultérieur à celui attendu, une anomalie s’est produite.
  5. Pour récupérer d’une anomalie :

    • Calculez combien de trames sauter (variable g_iImmediates dans le code d’exemple).
    • Présentez les trames sautées avec l’intervalle D3DPRESENT_FORCEIMMEDIATE.

Considérations pour la détection et la récupération des anomalies

  1. La récupération des anomalies prend N (variable g_iQueueDelay dans le code d’exemple) appels Present où N (g_iQueueDelay) est égal à g_iImmediates plus la longueur de la file d’attente Present, soit :

    • Saut de trames avec l’intervalle Present D3DPRESENT_FORCEIMMEDIATE, plus
    • Les Presents en file d’attente qui doivent être traités
  2. Définissez une limite à la longueur de l’anomalie (GLITCH_RECOVERY_LIMIT dans l’exemple). Si l’application exemple ne peut pas récupérer d’une anomalie qui est trop longue (c’est-à-dire, disons, 1 seconde, ou 60 vsyncs sur un moniteur à 60 Hz), passez l’animation intermittente et réinitialisez la file d’attente auxiliaire Present.

VOID Render()
{
    g_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);

    g_pd3dDevice->BeginScene();

    // Compute new animation parameters for time and frame based animations

    // Time-based is a difference between base and current SyncRefreshCount
    g_aTimeBasedHistory[g_iBlurHistoryCounter] = g_iStartFrame + g_LastSyncRefreshCount - g_SyncRefreshCount;
    // Frame-based is incrementing frame value
    g_aFrameBasedHistory[g_iBlurHistoryCounter] = g_iStartFrame + g_iFrameNumber;

    RenderBlurredMesh(TRUE);    // Time-based
    RenderBlurredMesh(FALSE);   // Frame-based

    g_iBlurHistoryCounter = (g_iBlurHistoryCounter + 1) % BLUR_FRAMES;

    DrawText();

    g_pd3dDevice->EndScene();

    // Performs glitch recovery if glitch was detected
    if (g_bGlitchRecovery && (g_iImmediates > 0))
    {
        // If we have present immediates queued as a result of glitch detected, issue forceimmediate Presents for glitch recovery 
        g_pd3dDevice->PresentEx(NULL, NULL, NULL, NULL, D3DPRESENT_FORCEIMMEDIATE);
        g_iImmediates--;
        g_iShowingGlitchRecovery = MESSAGE_SHOW;
    }
    // Otherwise, Present normally
    else
    {
        g_pd3dDevice->PresentEx(NULL, NULL, NULL, NULL, 0);
    }

    // Add to helper Present queue: PresentID + expected present refresh count of last submitted Present
    UINT PresentCount;
    g_pd3dSwapChain->GetLastPresentCount(&PresentCount);
    g_Queue.QueueFrame(PresentCount, g_TargetRefreshCount);
    
    // QueueDelay specifies # Present calls to be processed before another glitch recovery attempt
    if (g_iQueueDelay > 0)
    {
        g_iQueueDelay--;
    }

    if (g_bGlitchRecovery)
    {
        // Additional DONOTFLIP presents for frame conversions, which basically follows the same logic, but without rendering
        for (DWORD i = 0; i < g_iDoNotFlipNum; i++)
        {
            if (g_TargetRefreshCount != -1)
            {
                g_TargetRefreshCount++;
                g_iFrameNumber++;
                g_aTimeBasedHistory[g_iBlurHistoryCounter] = g_iStartFrame + g_LastSyncRefreshCount - g_SyncRefreshCount;
                g_aFrameBasedHistory[g_iBlurHistoryCounter] = g_iStartFrame + g_iFrameNumber;
                g_iBlurHistoryCounter = (g_iBlurHistoryCounter + 1) % BLUR_FRAMES;
            }
            
            if (g_iImmediates > 0)
            {
                g_pd3dDevice->PresentEx(NULL, NULL, NULL, NULL, D3DPRESENT_FORCEIMMEDIATE | D3DPRESENT_DONOTFLIP);
                g_iImmediates--;
            }
            else
            {
                g_pd3dDevice->PresentEx(NULL, NULL, NULL, NULL, D3DPRESENT_DONOTFLIP);
            }
            UINT PresentCount;
            g_pd3dSwapChain->GetLastPresentCount(&PresentCount);
            g_Queue.QueueFrame(PresentCount, g_TargetRefreshCount);

            if (g_iQueueDelay > 0)
            {
                g_iQueueDelay--;
            }
        }
    }

    // Check Present Stats info for glitch detection 
    D3DPRESENTSTATS PresentStats;

    // Obtain present statistics information for successfully displayed presents
    HRESULT hr = g_pd3dSwapChain->GetPresentStats(&PresentStats);

    if (SUCCEEDED(hr))
    {
        // Time-based update
        g_LastSyncRefreshCount = PresentStats.SyncRefreshCount;
        if ((g_SyncRefreshCount == -1) && (PresentStats.PresentCount != 0))
        {
            // First time SyncRefreshCount is reported, use it as base
            g_SyncRefreshCount = PresentStats.SyncRefreshCount;
        }

        // Fetch frame from the queue...
        UINT TargetRefresh = g_Queue.DequeueFrame(PresentStats.PresentCount);

        // If PresentStats returned a really old frame that we no longer have in the queue, just don't do any glitch detection
        if (TargetRefresh == FRAME_NOT_FOUND)
            return;

        if (g_TargetRefreshCount == -1)
        {
            // This is first time issued frame is confirmed by present stats, so fill target refresh count for all frames in the queue
            g_TargetRefreshCount = g_Queue.FillRefreshCounts(PresentStats.PresentCount, g_SyncRefreshCount);
        } 
        else
        {
            g_TargetRefreshCount++;
            g_iFrameNumber++;

            // To determine whether we're glitching, see if our estimated refresh count is confirmed
            // if the frame is displayed later than the expected vsync count
            if (TargetRefresh < PresentStats.PresentRefreshCount)
            {
                // then, glitch is detected!

                // If glitch is too big, don't bother recovering from it, just jump animation
                if ((PresentStats.PresentRefreshCount - TargetRefresh) > GLITCH_RECOVERY_LIMIT)
                {
                    g_iStartFrame += PresentStats.SyncRefreshCount - g_SyncRefreshCount;
                    ResetAnimation();
                    if (g_bGlitchRecovery)
                        g_iGlitchesInaRow++;    
                } 
                // Otherwise, compute number of immediate presents to recover from it -- if we?re not still trying to recover from another glitch
                else if (g_iQueueDelay == 0)
                {
                      // skip frames to catch up to expected refresh count
                    g_iImmediates = PresentStats.PresentRefreshCount - TargetRefresh;
                    // QueueDelay specifies # Present calls before another glitch recovery 
                    g_iQueueDelay = g_iImmediates + QUEUE_SIZE;
                    if (g_bGlitchRecovery)
                        g_iGlitchesInaRow++;
                }
            }
            else
            {
                // No glitch, reset glitch count
                g_iGlitchesInaRow = 0;
            }
        }
    }
    else if (hr == D3DERR_PRESENT_STATISTICS_DISJOINT)
    {
        // D3DERR_PRESENT_STATISTICS_DISJOINT means measurements should be started from the scratch (could be caused by mode change or DWM on/off transition)
        ResetAnimation();
    }

    // If we got too many glitches in a row, reduce framerate conversion factor (that is, render less frames)
    if (g_iGlitchesInaRow == FRAMECONVERSION_GLITCH_LIMIT)
    {
        if (g_iDoNotFlipNum < FRAMECONVERSION_LIMIT)
        {
            g_iDoNotFlipNum++;
        }
        g_iGlitchesInaRow = 0;
        g_iShowingDoNotFlipBump = MESSAGE_SHOW;
    }
}

Exemple de scénario

  • L’illustration suivante montre une application avec un nombre de backbuffers de 4. La longueur réelle de la file d’attente Present est donc de 5.

    illustration des trames rendues et de la file d’attente present d’une application

    La trame A est censée être affichée à l’écran au compte d’intervalle de synchronisation de 1, mais il a été détecté qu’elle était affichée au compte d’intervalle de synchronisation de 4. Une anomalie s’est donc produite. Les 3 trames suivantes sont présentées avec D3DPRESENT_INTERVAL_FORCEIMMEDIATE. L’anomalie devrait prendre un total de 8 appels Present avant d’être récupérée - la trame suivante sera affichée selon son compte d’intervalle de synchronisation cible.

Résumé des recommandations de programmation pour la synchronisation de trame

  • Créez une liste de sauvegarde de tous les IDs LastPresentCount (obtenus via GetLastPresentCount) et du PresentRefreshCount estimé associé de tous les Presents soumis.

    Remarque

    Lorsque l’application appelle PresentEx avec D3DPRESENT_DONOTFLIP, l’appel GetPresentStatistics réussit mais ne renvoie pas une structure D3DPRESENTSTATS mise à jour lorsque l’application est en mode fenêtré.

  • Appelez GetPresentStatistics pour obtenir le PresentRefreshCount réel associé à chaque ID Present des trames affichées, pour vous assurer que l’application gère les échecs de retour de l’appel.

  • Si le PresentRefreshCount réel est ultérieur au PresentRefreshCount estimé, une anomalie est détectée. Compensez en soumettant les trames en retard Present avec D3DPRESENT_FORCEIMMEDIATE.

  • Lorsqu’une trame est présentée en retard dans la file d’attente Present, toutes les trames en file d’attente suivantes seront présentées en retard. D3DPRESENT_FORCEIMMEDIATE corrigera uniquement la prochaine trame à être présentée après toutes les trames en file d’attente. Par conséquent, la file d’attente Present ou le nombre de backbuffers ne devrait pas être trop long -- de sorte qu’il y ait moins d’anomalies de trame à rattraper. Le nombre optimal de backbuffers est de 2 à 4.

  • Si le PresentRefreshCount estimé est ultérieur au PresentRefreshCount réel, une limitation DWM pourrait s’être produite. Les solutions suivantes sont possibles :

    • réduction de la longueur de la file d’attente Present
    • réduction des exigences de mémoire GPU par tout autre moyen que la réduction de la longueur de la file d’attente Present (c’est-à-dire, diminution de la qualité, suppression d’effets, etc)
    • spécification de DwmEnableMMCSS pour prévenir la limitation DWM en général
  • Vérifiez la fonctionnalité d’affichage de l’application et les performances des statistiques de trame dans les scénarios suivants :

    • avec DWM activé et désactivé
    • modes exclusif plein écran et fenêtré
    • matériel de capacité inférieure
  • Lorsque les applications ne peuvent pas récupérer d’un grand nombre de trames en anomalie avec Present D3DPRESENT_FORCEIMMEDIATE, elles peuvent potentiellement effectuer les opérations suivantes :

    • réduire l’utilisation du CPU et du GPU en rendant avec moins de charge de travail.
    • dans le cas du décodage vidéo, décoder plus rapidement en réduisant la qualité et, par conséquent, l’utilisation du CPU et du GPU.

Conclusion sur les améliorations de Direct3D 9Ex

Sur Windows 7, les applications qui affichent des vidéos ou évaluent le taux de rafraîchissement des trames pendant la présentation peuvent opter pour le modèle Flip. Les améliorations des statistiques de présentation qui sont associées au modèle Flip Direct3D 9Ex peuvent bénéficier aux applications qui synchronisent la présentation par taux de rafraîchissement des trames, avec un retour en temps réel pour la détection et la récupération des anomalies. Les développeurs qui adoptent le modèle Flip Direct3D 9Ex devraient prendre en compte le ciblage d’un HWND séparé du contenu GDI et la synchronisation du taux de rafraîchissement des trames. Consultez les détails dans cette rubrique. Pour une documentation supplémentaire, veuillez consulter la section Centre de développement DirectX sur MSDN.

À vous d’agir !

Nous vous encourageons à utiliser le modèle Flip Direct3D 9Ex et ses statistiques de présentation sur Windows 7 lorsque vous créez des applications qui tentent de synchroniser le taux de rafraîchissement des trames de présentation ou de récupérer des anomalies d’affichage.

Centre de développement DirectX sur MSDN