Compartilhar via


Inverter modelo, sujo retângulos, áreas roladas

O DXGI 1.2 dá suporte a uma nova cadeia de troca de modelo de inversão, retângulos sujo e áreas roladas. Explicamos os benefícios de usar a nova cadeia de troca de modelo de inversão e de otimizar a apresentação especificando sujo retângulos e áreas roladas.

Apresentação de modelo de inversão DXGI

O DXGI 1.2 adiciona suporte para o modelo de apresentação de inversão para APIs direct3D 10 e posteriores. No Windows 7, o Direct3D 9EX adotou pela primeira vez a apresentação de modelo de flip para evitar copiar desnecessariamente o buffer de cadeia de troca. Usando o modelo de inversão, os buffers traseiros são invertidos entre o runtime e o DWM (Gerenciador de Janelas da Área de Trabalho), portanto, o DWM sempre compõe diretamente do buffer de fundo em vez de copiar o conteúdo do buffer de fundo.

As APIs DXGI 1.2 incluem uma interface de cadeia de troca DXGI revisada, IDXGISwapChain1. Você pode usar vários métodos de interface IDXGIFactory2 para criar o objeto IDXGISwapChain1 apropriado a ser usado com um identificador HWND , um objeto CoreWindow , DirectComposition ou a estrutura Windows.UI.Xaml .

Selecione o modelo de apresentação de inversão especificando o valor de enumeração DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL no membro SwapEffect da estrutura DXGI_SWAP_CHAIN_DESC1 e definindo o membro BufferCount de DXGI_SWAP_CHAIN_DESC1 como um mínimo de 2. Para obter mais informações sobre como usar o modelo de inversão DXGI, consulte Modelo de inversão DXGI. Devido à apresentação mais suave do modelo de apresentação flip e outras novas funcionalidades, recomendamos que você use o modelo de apresentação flip para todos os novos aplicativos que você escreve com APIs direct3D 10 e posteriores.

Usando sujo retângulos e o retângulo de rolagem na apresentação da cadeia de troca

Usando sujo retângulos e o retângulo de rolagem na apresentação da cadeia de troca, você economiza no uso da largura de banda de memória e no uso relacionado da energia do sistema porque a quantidade de dados de pixel que o sistema operacional precisa para desenhar o próximo quadro apresentado será reduzida se o sistema operacional não precisar desenhar todo o quadro. Para aplicativos que geralmente são exibidos por meio da Conexão de Área de Trabalho Remota e outras tecnologias de acesso remoto, a economia é particularmente perceptível na qualidade de exibição porque essas tecnologias usam retângulos sujo e metadados de rolagem.

Você pode usar a rolagem somente com cadeias de troca DXGI que são executadas no modelo de apresentação de inversão. Você pode usar retângulos sujo com cadeias de troca DXGI que são executadas no modelo de inversão e no modelo bitblt (definido com DXGI_SWAP_EFFECT_SEQUENTIAL).

Neste cenário e ilustração, mostramos a funcionalidade de usar sujo retângulos e rolagem. Aqui, um aplicativo rolável contém texto e vídeo animado. O aplicativo usa sujo retângulos para atualizar apenas o vídeo animador e a nova linha da janela, em vez de atualizar a janela inteira. O retângulo de rolagem permite que o sistema operacional copie e traduza o conteúdo renderizado anteriormente no novo quadro e renderize apenas a nova linha no novo quadro.

O aplicativo executa a apresentação chamando o método IDXGISwapChain1::P resent1 . Nessa chamada, o aplicativo passa um ponteiro para uma estrutura DXGI_PRESENT_PARAMETERS que inclui sujo retângulos e o número de retângulos sujo ou o retângulo de rolagem e o deslocamento de rolagem associado, ou ambos os retângulos sujo e o retângulo de rolagem. Nosso aplicativo passa 2 sujo retângulos e o retângulo de rolagem. O retângulo de rolagem é a área do quadro anterior que o sistema operacional precisa copiar para o quadro atual antes de renderizar o quadro atual. O aplicativo especifica o vídeo animador e a nova linha como retângulos sujo e o sistema operacional os renderiza no quadro atual.

ilustração de rolagem e retângulos sujo sobrepostos

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 }

O retângulo tracejado mostra o retângulo de rolagem no quadro atual. O retângulo de rolagem é especificado pelo membro pScrollRect do DXGI_PRESENT_PARAMETERS. A seta mostra o deslocamento de rolagem. O deslocamento de rolagem é especificado pelo membro pScrollOffset do DXGI_PRESENT_PARAMETERS. Retângulos preenchidos mostram sujo retângulos que o aplicativo atualizou com o novo conteúdo. Os retângulos preenchidos são especificados pelos membros DirtyRectsCount e pDirtyRects de DXGI_PRESENT_PARAMETERS.

Cadeia de troca de modelo de inversão de dois buffers de exemplo com retângulos sujo e retângulo de rolagem

A próxima ilustração e sequência mostra um exemplo de uma operação de apresentação de modelo de inversão DXGI que usa sujo retângulos e um retângulo de rolagem. Neste exemplo, usamos o número mínimo de buffers para apresentação de modelo de inversão, que é uma contagem de buffers de dois, um buffer frontal que contém o conteúdo de exibição do aplicativo e um buffer de fundo que contém o quadro atual que o aplicativo deseja renderizar.

  1. Conforme mostrado no buffer frontal no início do quadro, o aplicativo rolável inicialmente mostra um quadro com algum texto e vídeo animado.
  2. Para renderizar o próximo quadro, o aplicativo renderiza no buffer de fundo os retângulos sujo que atualizam o vídeo animador e a nova linha da janela.
  3. Quando o aplicativo chama IDXGISwapChain1::P resent1, ele especifica os retângulos sujo e o retângulo e deslocamento de rolagem. Em seguida, o runtime copia o retângulo de rolagem do quadro anterior menos os retângulos sujo atualizados para o buffer de fundo atual.
  4. O runtime finalmente troca os buffers frontal e traseiro.

exemplo de cadeia de troca de modelo de inversão com retângulos de rolagem e sujo

Acompanhamento sujo retângulos e retângulos de rolagem em vários quadros

Ao usar sujo retângulos em seu aplicativo, você deve acompanhar os retângulos sujo para dar suporte à renderização incremental. Quando seu aplicativo chama IDXGISwapChain1::P resent1 com retângulos sujo, você deve garantir que cada pixel dentro dos retângulos sujo esteja atualizado. Se você não estiver renderizando completamente toda a área do retângulo sujo ou se não souber certas áreas que estão sujas, copie alguns dados do buffer de fundo totalmente coerente anterior para o buffer de fundo atual e obsoleto antes de começar a renderizar.

O runtime copia apenas as diferenças entre as áreas atualizadas do quadro anterior e as áreas atualizadas do quadro atual no buffer de fundo atual. Se essas áreas se cruzarem, o runtime copiará apenas a diferença entre elas. Como você pode ver no diagrama e na sequência a seguir, você deve copiar a interseção entre o retângulo sujo do quadro 1 e o retângulo sujo do quadro 2 para o retângulo sujo do quadro 2.

  1. Apresente sujo retângulo no quadro 1.
  2. Copie a interseção entre o retângulo sujo do quadro 1 e o retângulo sujo do quadro 2 para o retângulo sujo do quadro 2.
  3. Apresentar sujo retângulo no quadro 2.

rolagem de rastreamento e retângulos sujo em vários quadros

Para generalizar, para uma cadeia de troca com N buffers, a área que o runtime copia do último quadro para o quadro atual no presente do quadro atual é:

equação para calcular a área que o runtime copia

em que buffer indica o índice de buffer em uma cadeia de troca, começando com o índice de buffer atual em zero.

Você pode controlar quaisquer interseções entre o quadro anterior e os retângulos sujo do quadro atual mantendo uma cópia dos retângulos sujo do quadro anterior ou renderizando novamente os retângulos sujo do novo quadro com o conteúdo apropriado do quadro anterior.

Da mesma forma, nos casos em que a cadeia de troca tem mais de 2 buffers traseiros, você deve garantir que as áreas sobrepostas entre os retângulos sujo do buffer atual e os retângulos sujo de todos os quadros anteriores sejam copiadas ou renderizadas novamente.

Acompanhamento de uma única interseção entre 2 sujo retângulos

No caso mais simples, quando você atualiza um único retângulo sujo por quadro, os retângulos sujo em dois quadros podem se interseccionar. Para descobrir se o retângulo sujo do quadro anterior e o retângulo sujo do quadro atual se sobrepõem, você precisa verificar se o retângulo sujo do quadro anterior se cruza com o retângulo sujo do quadro atual. Você pode chamar a função IntersectRect GDI para determinar se duas estruturas RECT que representam os dois retângulos sujo se cruzam.

Neste snippet de código, uma chamada para IntersectRect retorna a interseção de dois retângulos sujo em outro RECT chamado dirtyRectCopy. Depois que o snippet de código determina que os dois retângulos sujo se cruzam, ele chama o método ID3D11DeviceContext1::CopySubresourceRegion1 para copiar a região da interseção para o quadro atual.

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.

Se você usar esse snippet de código em seu aplicativo, o aplicativo estará pronto para chamar IDXGISwapChain1::P resent1 para atualizar o quadro atual com o retângulo sujo atual.

Acompanhamento de interseções entre N sujo retângulos

Se você especificar vários retângulos sujo, que podem incluir um retângulo sujo para a linha de rolagem recém-revelada, por quadro, será necessário verificar e acompanhar quaisquer sobreposições que possam ocorrer entre todos os retângulos sujo do quadro anterior e todos os retângulos sujo do quadro atual. Para calcular as interseções entre os retângulos sujo do quadro anterior e os retângulos sujo do quadro atual, você pode agrupar os retângulos sujo em regiões.

Neste snippet de código, chamamos a função GDI SetRectRgn para converter cada retângulo sujo em uma região retangular e, em seguida, chamamos a função GDI CombineRgn para combinar todas as sujo regiões retangulares em um 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.
}

Agora você pode usar a função CombineRgn GDI para determinar a interseção entre a região sujo do quadro anterior e a região sujo do quadro atual. Depois de obter a região de interseção, chame a função GDI GetRegionData para obter cada retângulo individual da região de interseção e, em seguida, chame o método ID3D11DeviceContext1::CopySubresourceRegion1 para copiar cada retângulo interseccionário para o buffer de fundo atual. O próximo snippet de código mostra como usar essas funções GDI e 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;
       }
}

Cadeia de troca de modelo bitblt com retângulos sujo

Você pode usar sujo retângulos com cadeias de troca DXGI que são executadas no modelo bitblt (definido com DXGI_SWAP_EFFECT_SEQUENTIAL). Cadeias de troca de modelo bitblt que usam mais de um buffer também devem acompanhar retângulos sujo sobrepostos entre quadros da mesma forma descrita em Acompanhamento sujo retângulos e retângulos de rolagem em vários quadros para cadeias de troca de modelo de inversão. Cadeias de troca de modelo bitblt com apenas um buffer não precisam acompanhar a sobreposição sujo retângulos porque todo o buffer é redesenhado a cada quadro.

Melhorias do DXGI 1.2