Voltear modelo, rectángulos sucios, áreas desplazadas

DXGI 1.2 admite una nueva cadena de intercambio de modelo volteo, rectángulos sucios y áreas desplazadas. Explicamos las ventajas de usar la nueva cadena de intercambio de modelo invertido y de optimizar la presentación especificando rectángulos sucios y áreas desplazadas.

Presentación de modelo flip-model de DXGI

DXGI 1.2 agrega compatibilidad con el modelo de presentación de volteo para las API direct3D 10 y posteriores. En Windows 7, Direct3D 9EX adoptó primero la presentación del modelo de volteo para evitar copiar innecesariamente el búfer de la cadena de intercambio. Mediante el modelo de volteo, los búferes de reserva se voltean entre el entorno de ejecución y el Administrador de ventanas de escritorio (DWM), por lo que DWM siempre se compone directamente desde el búfer de reserva en lugar de copiar el contenido del búfer de reserva.

Las API de DXGI 1.2 incluyen una interfaz de cadena de intercambio DXGI revisada, IDXGISwapChain1. Puedes usar varios métodos de interfaz IDXGIFactory2 para crear el objeto IDXGISwapChain1 adecuado para usarlo con un identificador HWND , un objeto CoreWindow , DirectComposition o el marco Windows.UI.Xaml .

Para seleccionar el modelo de presentación invertida, especifique el valor de enumeración DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL en el miembro SwapEffect de la estructura de DXGI_SWAP_CHAIN_DESC1 y estableciendo el miembro BufferCount de DXGI_SWAP_CHAIN_DESC1 en un mínimo de 2. Para obtener más información sobre cómo usar el modelo de volteo DXGI, consulta MODELO de volteo DXGI. Debido a la presentación más fluida del modelo de presentación invertida y a otra nueva funcionalidad, se recomienda usar el modelo de presentación invertida para todas las aplicaciones nuevas que escriba con Direct3D 10 y las API posteriores.

Uso de rectángulos sucios y el rectángulo de desplazamiento en la presentación de la cadena de intercambio

Al usar rectángulos sucios y el rectángulo de desplazamiento en la presentación de la cadena de intercambio, se ahorra en el uso del ancho de banda de memoria y el uso relacionado de la potencia del sistema porque la cantidad de datos de píxeles que el sistema operativo necesita para dibujar el siguiente fotograma presentado se reduce si el sistema operativo no necesita dibujar todo el fotograma. En el caso de las aplicaciones que a menudo se muestran a través de conexión a Escritorio remoto y otras tecnologías de acceso remoto, el ahorro es especialmente notable en la calidad de la pantalla porque estas tecnologías usan rectángulos sucios y metadatos de desplazamiento.

Solo puede usar scroll con cadenas de intercambio DXGI que se ejecutan en el modelo de presentación invertida. Puede usar rectángulos sucios con cadenas de intercambio DXGI que se ejecutan tanto en el modelo de volteo como en el modelo bitblt (establecido con DXGI_SWAP_EFFECT_SEQUENTIAL).

En este escenario e ilustración se muestra la funcionalidad de usar rectángulos y desplazamientos sucios. Aquí, una aplicación desplazable contiene texto y vídeo de animación. La aplicación usa rectángulos sucios para actualizar simplemente el vídeo de animación y la nueva línea de la ventana, en lugar de actualizar toda la ventana. El rectángulo de desplazamiento permite al sistema operativo copiar y traducir el contenido representado previamente en el nuevo marco y representar solo la nueva línea en el nuevo marco.

La aplicación realiza la presentación llamando al método IDXGISwapChain1::P resent1 . En esta llamada, la aplicación pasa un puntero a una estructura de DXGI_PRESENT_PARAMETERS que incluye rectángulos sucios y el número de rectángulos sucios, o el rectángulo de desplazamiento y el desplazamiento asociado, o los rectángulos sucios y el rectángulo de desplazamiento. Nuestra aplicación pasa 2 rectángulos sucios y el rectángulo de desplazamiento. El rectángulo de desplazamiento es el área del marco anterior que el sistema operativo necesita copiar en el marco actual antes de representar el marco actual. La aplicación especifica el vídeo de animación y la nueva línea como rectángulos sucios y el sistema operativo los representa en el fotograma actual.

ilustración de rectángulos de desplazamiento y desfasados superpuestos

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 }

El rectángulo discontinuo muestra el rectángulo de desplazamiento en el marco actual. El rectángulo de desplazamiento se especifica mediante el miembro pScrollRect de DXGI_PRESENT_PARAMETERS. La flecha muestra el desplazamiento de desplazamiento. El desplazamiento de desplazamiento se especifica mediante el miembro pScrollOffset de DXGI_PRESENT_PARAMETERS. Los rectángulos rellenados muestran rectángulos sucios que la aplicación actualizó con nuevo contenido. Los rectángulos rellenados se especifican mediante los miembros DirtyRectsCount y pDirtyRects de DXGI_PRESENT_PARAMETERS.

Cadena de intercambio de 2 búferes flip-model con rectángulos sucios y rectángulo de desplazamiento

En la siguiente ilustración y secuencia se muestra un ejemplo de una operación de presentación de modelo de volteo DXGI que usa rectángulos sucios y un rectángulo de desplazamiento. En este ejemplo, se usa el número mínimo de búferes para la presentación del modelo invertida, que es un recuento de búferes de dos, un búfer frontal que contiene el contenido de presentación de la aplicación y un búfer de reserva que contiene el marco actual que la aplicación quiere representar.

  1. Como se muestra en el búfer frontal al principio del fotograma, la aplicación desplazable muestra inicialmente un fotograma con texto y animación de vídeo.
  2. Para representar el siguiente fotograma, la aplicación se representa en el búfer de reserva de los rectángulos sucios que actualizan el vídeo de animación y la nueva línea de la ventana.
  3. Cuando la aplicación llama a IDXGISwapChain1::P resent1, especifica los rectángulos sucios y el rectángulo de desplazamiento y el desplazamiento. El tiempo de ejecución copia el rectángulo de desplazamiento del marco anterior menos los rectángulos sucios actualizados en el búfer de reserva actual.
  4. El tiempo de ejecución finalmente intercambia los búferes frontal y trasero.

ejemplo de cadena de intercambio de modelo invertida con rectángulos de desplazamiento y desfasados

Seguimiento de rectángulos sucios y rectángulos de desplazamiento en varios marcos

Al usar rectángulos sucios en la aplicación, debes realizar un seguimiento de los rectángulos sucios para admitir la representación incremental. Cuando la aplicación llama a IDXGISwapChain1::P resent1 con rectángulos sucios, debes asegurarte de que todos los píxeles de los rectángulos sucios estén actualizados. Si no vuelve a representar completamente todo el área del rectángulo sucio o si no puede saber para determinadas áreas que están sucias, debe copiar algunos datos del búfer back totalmente coherente anterior al búfer de reserva obsoleto actual antes de empezar a representarse.

El tiempo de ejecución copia solo las diferencias entre las áreas actualizadas del marco anterior y las áreas actualizadas del marco actual en el búfer de reserva actual. Si estas áreas se intersecan, el tiempo de ejecución solo copia la diferencia entre ellas. Como puede ver en el diagrama y la secuencia siguientes, debe copiar la intersección entre el rectángulo sucio del marco 1 y el rectángulo sucio del marco 2 en el rectángulo sucio del marco 2.

  1. Presente rectángulo sucio en el marco 1.
  2. Copie la intersección entre el rectángulo sucio del marco 1 y el rectángulo sucio del marco 2 en el rectángulo sucio del marco 2.
  3. Presente rectángulo sucio en el marco 2.

seguimiento de rectángulos de desplazamiento y desfasados en varios marcos

Para generalizar, para una cadena de intercambio con N búferes, el área que el tiempo de ejecución copia de la última trama en el marco actual es:

ecuación para calcular el área que el tiempo de ejecución copia

donde el búfer indica el índice del búfer en una cadena de intercambio, empezando por el índice de búfer actual en cero.

Puede realizar un seguimiento de cualquier intersección entre el marco anterior y los rectángulos sucios del marco actual manteniendo una copia de los rectángulos sucios del fotograma anterior o re-renderizado los rectángulos sucios del nuevo fotograma con el contenido adecuado del fotograma anterior.

Del mismo modo, en los casos en los que la cadena de intercambio tiene más de 2 búferes de reserva, debe asegurarse de que las áreas superpuestas entre los rectángulos sucios del búfer actual y los rectángulos sucios de todos los fotogramas anteriores se copian o se vuelven a representar.

Seguimiento de una sola intersección entre 2 rectángulos sucios

En el caso más sencillo, al actualizar un único rectángulo sucio por fotograma, los rectángulos sucios en dos fotogramas podrían intersectarse. Para averiguar si el rectángulo sucio del marco anterior y el rectángulo sucio del marco actual se superponen, debe comprobar si el rectángulo sucio del marco anterior se interseca con el rectángulo sucio del marco actual. Puede llamar a la función IntersectRect de GDI para determinar si dos estructuras RECT que representan los dos rectángulos sucios se intersecan.

En este fragmento de código, una llamada a IntersectRect devuelve la intersección de dos rectángulos sucios en otro RECT denominado dirtyRectCopy. Una vez que el fragmento de código determina que los dos rectángulos sucios se intersecan, llama al método ID3D11DeviceContext1::CopySubresourceRegion1 para copiar la región de intersección en el marco actual.

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 usa este fragmento de código en la aplicación, la aplicación estará lista para llamar a IDXGISwapChain1::P resent1 para actualizar el marco actual con el rectángulo sucio actual.

Seguimiento de intersecciones entre N rectángulos sucios

Si especifica varios rectángulos sucios, que pueden incluir un rectángulo sucio para la línea de desplazamiento recién revelada, por fotograma, debe comprobar y realizar un seguimiento de las superposiciones que puedan producirse entre todos los rectángulos sucios del marco anterior y todos los rectángulos sucios del marco actual. Para calcular las intersecciones entre los rectángulos sucios del marco anterior y los rectángulos sucios del marco actual, puede agrupar los rectángulos sucios en regiones.

En este fragmento de código, llamamos a la función SetRectRgn de GDI para convertir cada rectángulo sucio en una región rectangular y, a continuación, llamamos a la función CombineRgn de GDI para combinar todas las regiones rectangulares desfasadas en un grupo.

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.
}

Ahora puede usar la función CombineRgn de GDI para determinar la intersección entre la región sucia del marco anterior y la región desfasada del fotograma actual. Después de obtener la región de intersección, llame a la función GetRegionData de GDI para obtener cada rectángulo individual de la región de intersección y, a continuación, llame al método ID3D11DeviceContext1::CopySubresourceRegion1 para copiar cada rectángulo de intersección en el búfer de reserva actual. El siguiente fragmento de código muestra cómo usar estas funciones GDI y 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;
       }
}

Cadena de intercambio de modelos bitblt con rectángulos sucios

Puede usar rectángulos sucios con cadenas de intercambio DXGI que se ejecutan en el modelo bitblt (establecido con DXGI_SWAP_EFFECT_SEQUENTIAL). Las cadenas de intercambio de modelos bitblt que usan más de un búfer también deben realizar un seguimiento de los rectángulos sucios superpuestos entre marcos de la misma manera que se describe en Seguimiento de rectángulos sucios y rectángulos de desplazamiento en varios marcos para cadenas de intercambio de modelos flip. Las cadenas de intercambio de modelos bitblt con solo un búfer no necesitan realizar un seguimiento de los rectángulos sucios superpuestos porque todo el búfer se vuelve a dibujar cada fotograma.

Mejoras de DXGI 1.2