Modèle de retournement, rectangles sale, zones de défilement

DXGI 1.2 prend en charge une nouvelle chaîne d’échange de modèle inversé, sale rectangles et zones de défilement. Nous expliquons les avantages de l’utilisation de la nouvelle chaîne d’échange de modèle inversé et de l’optimisation de la présentation en spécifiant sale rectangles et zones de défilement.

Présentation du modèle inverse DXGI

DXGI 1.2 ajoute la prise en charge du modèle de présentation inversée pour les API Direct3D 10 et ultérieures. Dans Windows 7, Direct3D 9EX a adopté la première présentation de modèle inversé pour éviter de copier inutilement la mémoire tampon de la chaîne d’échange. En utilisant le modèle de retournement, les mémoires tampons d’arrière-mémoire sont retournées entre le runtime et desktop Window Manager (DWM), de sorte que DWM compose toujours directement à partir de la mémoire tampon d’arrière-mémoire au lieu de copier le contenu de la mémoire tampon d’arrière-mémoire.

Les API DXGI 1.2 incluent une interface de chaîne d’échange DXGI révisée, IDXGISwapChain1. Vous pouvez utiliser plusieurs méthodes d’interface IDXGIFactory2 pour créer l’objet IDXGISwapChain1 approprié à utiliser avec un handle HWND , un objet CoreWindow , DirectComposition ou l’infrastructure Windows.UI.Xaml .

Vous sélectionnez le modèle de présentation inversée en spécifiant la valeur d’énumération DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL dans le membre SwapEffect de la structure DXGI_SWAP_CHAIN_DESC1 et en affectant au minimum 2 au membre BufferCount de DXGI_SWAP_CHAIN_DESC1 . Pour plus d’informations sur l’utilisation du modèle de retournement DXGI, consultez Modèle de retournement DXGI. En raison de la présentation plus fluide du modèle de présentation à retournement et d’autres nouvelles fonctionnalités, nous vous recommandons d’utiliser le modèle de présentation inversée pour toutes les nouvelles applications que vous écrivez avec les API Direct3D 10 et ultérieures.

Utilisation de sale rectangles et du rectangle de défilement dans la présentation de la chaîne d’échange

En utilisant sale rectangles et le rectangle de défilement dans la présentation de la chaîne d’échange, vous économisez sur l’utilisation de la bande passante mémoire et l’utilisation associée de l’alimentation du système, car la quantité de données de pixels dont le système d’exploitation a besoin pour dessiner le cadre présenté suivant est réduite si le système d’exploitation n’a pas besoin de dessiner l’image entière. Pour les applications qui sont souvent affichées via la connexion Bureau à distance et d’autres technologies d’accès à distance, les économies sont particulièrement notables dans la qualité d’affichage, car ces technologies utilisent des rectangles sale et des métadonnées de défilement.

Vous pouvez utiliser le défilement uniquement avec les chaînes d’échange DXGI qui s’exécutent dans le modèle de présentation à retournement. Vous pouvez utiliser des rectangles sale avec des chaînes d’échange DXGI qui s’exécutent à la fois dans le modèle flip et le modèle bitblt (défini avec DXGI_SWAP_EFFECT_SEQUENTIAL).

Dans ce scénario et cette illustration, nous montrons la fonctionnalité d’utilisation de rectangles sale et de défilement. Ici, une application avec défilement contient du texte et une vidéo d’animation. L’application utilise sale rectangles pour simplement mettre à jour la vidéo d’animation et la nouvelle ligne de la fenêtre, au lieu de mettre à jour la fenêtre entière. Le rectangle de défilement permet au système d’exploitation de copier et de traduire le contenu précédemment rendu sur le nouveau cadre et d’afficher uniquement la nouvelle ligne sur le nouveau cadre.

L’application effectue une présentation en appelant la méthode IDXGISwapChain1::P resent1 . Dans cet appel, l’application passe un pointeur vers une structure de DXGI_PRESENT_PARAMETERS qui inclut sale rectangles et le nombre de rectangles sale, ou le rectangle de défilement et le décalage de défilement associé, ou les deux sale rectangles et le rectangle de défilement. Notre application passe 2 sale rectangles et le rectangle de défilement. Le rectangle de défilement est la zone de l’image précédente que le système d’exploitation doit copier dans le cadre actuel avant qu’il ne restitue le frame actuel. L’application spécifie la vidéo d’animation et la nouvelle ligne en tant que rectangles sale, et le système d’exploitation les affiche sur l’image actuelle.

illustration de rectangles de défilement et de sale qui se chevauchent

DirtyRectsCount = 2
pDirtyRects[ 0 ] = { 10, 30, 40, 50 } // Video
pDirtyRects[ 1 ] = { 0, 70, 50, 80 } // New line
*pScrollRect = { 0, 0, 50, 70 }
*pScrollOffset = { 0, -10 }

Le rectangle en pointillés affiche le rectangle de défilement dans le cadre actuel. Le rectangle de défilement est spécifié par le membre pScrollRect de DXGI_PRESENT_PARAMETERS. La flèche affiche le décalage de défilement. Le décalage de défilement est spécifié par le membre pScrollOffset de DXGI_PRESENT_PARAMETERS. Les rectangles remplis affichent sale rectangles que l’application a mis à jour avec un nouveau contenu. Les rectangles remplis sont spécifiés par les membres DirtyRectsCount et pDirtyRects de DXGI_PRESENT_PARAMETERS.

Exemple de chaîne d’échange de modèle inversé à 2 mémoires tampons avec des rectangles sale et un rectangle de défilement

L’illustration et la séquence suivantes montrent un exemple d’opération de présentation de modèle inversé DXGI qui utilise sale rectangles et un rectangle de défilement. Dans cet exemple, nous utilisons le nombre minimal de mémoires tampons pour la présentation de modèle inversé, qui est un nombre de mémoires tampons de deux, une mémoire tampon avant qui contient le contenu d’affichage de l’application et une mémoire tampon arrière qui contient le frame actuel que l’application souhaite afficher.

  1. Comme indiqué dans la mémoire tampon avant au début du cadre, l’application avec défilement affiche initialement un cadre avec du texte et une vidéo d’animation.
  2. Pour afficher l’image suivante, l’application restitue sur la mémoire tampon arrière les rectangles sale qui mettent à jour la vidéo d’animation et la nouvelle ligne de la fenêtre.
  3. Lorsque l’application appelle IDXGISwapChain1::P resent1, elle spécifie les rectangles sale, le rectangle de défilement et le décalage. Le runtime copie ensuite le rectangle de défilement de l’image précédente moins les rectangles sale mis à jour sur la mémoire tampon d’arrière-mémoire actuelle.
  4. Le runtime permute finalement les mémoires tampons avant et arrière.

exemple de chaîne d’échange de modèle inversé avec défilement et sale rectangles

Suivi sale rectangles et défilement de rectangles sur plusieurs cadres

Lorsque vous utilisez sale rectangles dans votre application, vous devez suivre les rectangles sale pour prendre en charge le rendu incrémentiel. Lorsque votre application appelle IDXGISwapChain1::P resent1 avec sale rectangles, vous devez vous assurer que chaque pixel dans les rectangles sale est à jour. Si vous n’effectuez pas un rendu complet de toute la zone du rectangle sale ou si vous ne pouvez pas connaître certaines zones qui sont sales, vous devez copier certaines données de la mémoire tampon précédente entièrement cohérente vers la mémoire tampon actuelle et obsolète avant de commencer le rendu.

Le runtime copie uniquement les différences entre les zones mises à jour de l’image précédente et les zones mises à jour de l’image actuelle dans la mémoire tampon d’arrière-mémoire actuelle. Si ces zones se croisent, le runtime copie uniquement la différence entre elles. Comme vous pouvez le voir dans le diagramme et la séquence suivants, vous devez copier l’intersection entre le rectangle sale du cadre 1 et le rectangle sale de l’image 2 dans le rectangle sale du cadre 2.

  1. Présente sale rectangle dans le cadre 1.
  2. Copiez l’intersection entre le rectangle sale du cadre 1 et le rectangle sale du cadre 2 dans le rectangle sale du cadre 2.
  3. Présente sale rectangle dans le cadre 2.

suivi du défilement et des rectangles sale sur plusieurs images

Pour généraliser, pour une chaîne d’échange avec N mémoires tampons, la zone que le runtime copie à partir de la dernière image vers l’image actuelle sur la trame actuelle est la suivante :

équation pour calculer la zone que le runtime copie

où buffer indique l’index de mémoire tampon dans une chaîne d’échange, en commençant par l’index de mémoire tampon actuel à zéro.

Vous pouvez effectuer le suivi des intersections entre le cadre précédent et les rectangles sale du cadre actuel en conservant une copie des rectangles sale du cadre précédent ou en revoyant les rectangles sale du nouveau cadre avec le contenu approprié du cadre précédent.

De même, dans les cas où la chaîne d’échange comporte plus de 2 mémoires tampons d’arrière-mémoire, vous devez vous assurer que les zones qui se chevauchent entre les rectangles sale de la mémoire tampon actuelle et les rectangles sale de tous les frame précédents sont copiés ou restitués.

Suivi d’une intersection unique entre 2 rectangles sale

Dans le cas le plus simple, lorsque vous mettez à jour un seul rectangle sale par image, les sale rectangles sur deux images peuvent se croiser. Pour savoir si le rectangle sale du cadre précédent et le rectangle sale du cadre actuel se chevauchent, vous devez vérifier si le rectangle sale du cadre précédent croise le rectangle sale du cadre actuel. Vous pouvez appeler la fonction GDI IntersectRect pour déterminer si deux structures RECT qui représentent les deux rectangles sale se croisent.

Dans cet extrait de code, un appel à IntersectRect renvoie l’intersection de deux rectangles sale dans un autre RECT appelé dirtyRectCopy. Une fois que l’extrait de code a déterminé que les deux rectangles sale se croisent, il appelle la méthode ID3D11DeviceContext1::CopySubresourceRegion1 pour copier la région d’intersection dans le cadre actuel.

RECT dirtyRectPrev, dirtyRectCurrent, dirtyRectCopy;
 
if (IntersectRect( &dirtyRectCopy, &dirtyRectPrev, &dirtyRectCurrent ))
{
       D3D11_BOX intersectBox;
       intersectBox.left    = dirtyRectCopy.left;
       intersectBox.top     = dirtyRectCopy.top;
       intersectBox.front   = 0;
       intersectBox.right   = dirtyRectCopy.right;
       intersectBox.bottom  = dirtyRectCopy.bottom;
       intersectBox.back    = 1;
 
       d3dContext->CopySubresourceRegion1(pBackbuffer,
                                    0,
                                    0,
                                    0,
                                    0,
                                    pPrevBackbuffer,
                                    0,
                                    &intersectBox,
                                    0
                                    );
}

// Render additional content to the current pBackbuffer and call Present1.

Si vous utilisez cet extrait de code dans votre application, l’application sera alors prête à appeler IDXGISwapChain1::P resent1 pour mettre à jour le frame actuel avec le rectangle de sale actuel.

Suivi des intersections entre N sale rectangles

Si vous spécifiez plusieurs rectangles sale, qui peuvent inclure un rectangle sale pour la ligne de défilement nouvellement révélée, par image, vous devez vérifier et suivre les chevauchements qui peuvent se produire entre tous les rectangles sale du cadre précédent et tous les rectangles sale du frame actuel. Pour calculer les intersections entre les rectangles sale de l’image précédente et les rectangles sale du cadre actuel, vous pouvez regrouper les rectangles sale en régions.

Dans cet extrait de code, nous appelons la fonction GDI SetRectRgn pour convertir chaque rectangle sale en une région rectangulaire, puis nous appelons la fonction GDI CombineRgn pour combiner toutes les régions rectangulaires sale en un groupe.

HRGN hDirtyRgnPrev, hDirtyRgnCurrent, hRectRgn; // Handles to regions 
// Save all the dirty rectangles from the previous frame.
 
RECT dirtyRect[N]; // N is the number of dirty rectangles in current frame, which includes newly scrolled area.
 
int iReturn;
SetRectRgn(hDirtyRgnCurrent, 
       dirtyRect[0].left, 
       dirtyRect[0].top, 
       dirtyRect[0].right, 
       dirtyRect[0].bottom 
       );

for (int i = 1; i<N; i++)
{
   SetRectRgn(hRectRgn, 
          dirtyRect[0].left, 
          dirtyRect[0].top, 
          dirtyRect[0].right, 
          dirtyRect[0].bottom 
          );

   iReturn = CombineRgn(hDirtyRgnCurrent,
                        hDirtyRgnCurrent,
                        hRectRgn,
                        RGN_OR
                        );
   // Handle the error that CombineRgn returns for iReturn.
}

Vous pouvez maintenant utiliser la fonction GDI CombineRgn pour déterminer l’intersection entre la région sale de l’image précédente et la région sale de l’image actuelle. Après avoir obtenu la région d’intersection, appelez la fonction GDI GetRegionData pour obtenir chaque rectangle individuel à partir de la région d’intersection, puis appelez la méthode ID3D11DeviceContext1::CopySubresourceRegion1 pour copier chaque rectangle croisé dans la mémoire tampon d’arrière-plan actuelle. L’extrait de code suivant montre comment utiliser ces fonctions GDI et Direct3D.

HRGN hIntersectRgn;
bool bRegionsIntersect;
iReturn = CombineRgn(hIntersectRgn, hDirtyRgnCurrent, hDirtyRgnPrev, RGN_AND);
if (iReturn == ERROR)
{
       // Handle error.
}
else if(iReturn == NULLREGION)
{
       bRegionsIntersect = false;
}
else
{
       bRegionsIntersect = true;
}
 
if (bRegionsIntersect)
{
       int rgnDataSize = GetRegionData(hIntersectRgn, 0, NULL);
       if (rgnDataSize)
       {
              char pMem[] = new char[size];
              RGNDATA* pRgnData = reinterpret_cast<RGNDATA*>(pMem);
              iReturn = GetRegionData(hIntersectRgn, rgnDataSize, pRgnData);
              // Handle iReturn failure.
 
              for (int rectcount = 0; rectcount < pRgnData->rdh.nCount; ++r)
              {
                     const RECT* pIntersectRect = reinterpret_cast<RECT*>(pRgnData->Buffer) +                                            
                                                  rectcount;                
                     D3D11_BOX intersectBox;
                     intersectBox.left    = pIntersectRect->left;
                     intersectBox.top     = pIntersectRect->top;
                     intersectBox.front   = 0;
                     intersectBox.right   = pIntersectRect->right;
                     intersectBox.bottom  = pIntersectRect->bottom;
                     intersectBox.back    = 1;
 
                     d3dContext->CopySubresourceRegion1(pBackbuffer,
                                                      0,
                                                      0,
                                                      0,
                                                      0,
                                                      pPrevBackbuffer,
                                                      0,
                                                      &intersectBox,
                                                      0
                                                      );
              }

              delete [] pMem;
       }
}

Chaîne d’échange de modèle bitblt avec sale rectangles

Vous pouvez utiliser des rectangles sale avec des chaînes d’échange DXGI qui s’exécutent dans le modèle bitblt (défini avec DXGI_SWAP_EFFECT_SEQUENTIAL). Les chaînes d’échange de modèle bitblt qui utilisent plusieurs mémoires tampons doivent également suivre les sale rectangles qui se chevauchent entre les cadres de la même façon que décrit dans Suivi sale rectangles et rectangles de défilement sur plusieurs trames pour les chaînes d’échange de modèle à retournement. Les chaînes d’échange de modèle bitblt avec une seule mémoire tampon n’ont pas besoin de suivre les sale rectangles qui se chevauchent, car la mémoire tampon entière est redessinée à chaque image.

Améliorations apportées à DXGI 1.2