Partilhar via


Modelo flip, retângulos sujos, áreas roladas

O DXGI 1.2 suporta uma nova cadeia de troca de modelo flip, retângulos sujos e áreas roladas. Explicamos os benefícios de usar a nova cadeia de permuta de modelo flip-model e de otimizar a apresentação especificando retângulos sujos e áreas roladas.

Apresentação do modelo flip DXGI

O DXGI 1.2 adiciona suporte para o modelo de apresentação flip para APIs Direct3D 10 e posteriores. No Windows 7, o Direct3D 9EX adotou pela primeira vez de apresentação de modelo de inversão para evitar copiar desnecessariamente o buffer de cadeia de permuta. Usando o modelo flip, os buffers traseiros são alternados entre o tempo de execução e o Desktop Window Manager (DWM), de modo que o DWM sempre compõe diretamente do buffer traseiro em vez de copiar o conteúdo do buffer traseiro.

As APIs do DXGI 1.2 incluem uma interface de cadeia de permuta DXGI revisada IDXGISwapChain1. Você pode usar vários IDXGIFactory2 métodos de interface para criar o objeto IDXGISwapChain1 apropriado para usar com um identificador deHWND, um objeto CoreWindow, DirectComposition ou a estruturaWindows.UI.Xaml.

Você seleciona o modelo de apresentação flip especificando o valor de enumeração DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL no SwapEffect membro da estrutura DXGI_SWAP_CHAIN_DESC1 e definindo o BufferCount membro 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 invertida e a outras novas funcionalidades, recomendamos que você use o modelo de apresentação invertida para todos os novos aplicativos que você escreve com APIs do Direct3D 10 e posteriores.

Usando retângulos sujos e o retângulo de rolagem na apresentação de cadeia de permuta

Usando retângulos sujos e o retângulo de rolagem na apresentação da cadeia de permuta, você economiza no uso da largura de banda da 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 é reduzida se o sistema operacional não precisar desenhar o quadro inteiro. Para aplicativos que geralmente são exibidos por meio da Conexão de Área de Trabalho Remota e outras tecnologias de acesso remoto, as economias são particularmente percetíveis na qualidade de exibição porque essas tecnologias usam retângulos sujos e metadados de rolagem.

Você pode usar a rolagem somente com cadeias de permuta DXGI que são executadas no modelo de apresentação invertida. Você pode usar retângulos sujos com cadeias de permuta DXGI que são executadas tanto no modelo flip quanto no modelo bitblt (definido com DXGI_SWAP_EFFECT_SEQUENTIAL).

Neste cenário e ilustração, mostramos a funcionalidade de usar retângulos sujos e rolar. Aqui, um aplicativo rolável contém texto e vídeo animado. O aplicativo usa retângulos sujos para apenas atualizar o vídeo de animação e nova linha para a 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 IDXGISwapChain1::P resent1 método. Nesta chamada, o aplicativo passa um ponteiro para uma estrutura DXGI_PRESENT_PARAMETERS que inclui retângulos sujos e o número de retângulos sujos, ou o retângulo de rolagem e o deslocamento de rolagem associado, ou ambos retângulos sujos e o retângulo de rolagem. Nosso aplicativo passa 2 retângulos sujos 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 de animação e a nova linha como retângulos sujos, e o sistema operacional os renderiza no quadro atual.

ilustração de rolo e retângulos sujos 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 pScrollRect membro do DXGI_PRESENT_PARAMETERS. A seta mostra o deslocamento de rolagem. O deslocamento de rolagem é especificado pelo pScrollOffset membro do DXGI_PRESENT_PARAMETERS. Retângulos cheios mostram retângulos sujos que o aplicativo atualizou com novo conteúdo. Os retângulos preenchidos são especificados pelo DirtyRectsCount e pDirtyRects membros do DXGI_PRESENT_PARAMETERS.

Exemplo de cadeia de troca de modelo flip-model de 2 buffers com retângulos sujos 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 flip DXGI que usa retângulos sujos e um retângulo de rolagem. Neste exemplo, usamos o número mínimo de buffers para apresentação de modelo invertido, que é uma contagem de buffer de dois, um buffer frontal que contém o conteúdo de exibição do aplicativo e um buffer traseiro que contém o quadro atual que o aplicativo deseja renderizar.

  1. Como 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 traseiro os retângulos sujos que atualizam o vídeo de animação e a nova linha para a janela.
  3. Quando o aplicativo chama IDXGISwapChain1::P resent1, ele especifica os retângulos sujos e o retângulo de rolagem e deslocamento. Em seguida, o tempo de execução copia o retângulo de rolagem do quadro anterior menos os retângulos sujos atualizados para o buffer traseiro atual.
  4. O tempo de execução finalmente troca os buffers frontal e traseiro.

exemplo de cadeia de troca de modelo flip com scroll e retângulos sujos

Rastreamento de retângulos sujos e retângulos de rolagem em vários quadros

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

O tempo de execução copia apenas as diferenças entre as áreas atualizadas do quadro anterior e as áreas atualizadas do quadro atual no buffer traseiro atual. Se essas áreas se cruzarem, o tempo de execução copiará apenas a diferença entre elas. Como você pode ver no diagrama e 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. Apresentar retângulo sujo 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 retângulo sujo no quadro 2.

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

Para generalizar, para uma cadeia de permuta com buffers N, a área que o tempo de execução copia do último quadro para o quadro atual no presente do quadro atual é:

equação para calcular a área que o tempo de execução copia

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

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

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

Rastreamento de uma única interseção entre 2 retângulos sujos

No caso mais simples, quando você atualiza um único retângulo sujo por quadro, os retângulos sujos em dois quadros podem se cruzar. 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 GDI IntersectRect para determinar se dois RECT estruturas que representam os dois retângulos sujos se cruzam.

Neste trecho de código, uma chamada para IntersectRect retorna a interseção de dois retângulos sujos em outro RECT chamado dirtyRectCopy. Depois que o trecho de código determina que os dois retângulos sujos se cruzam, ele chama o ID3D11DeviceContext1::CopySubresourceRegion1 método para copiar a região de 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 trecho 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.

Rastreamento de interseções entre N retângulos sujos

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

Neste trecho 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 regiões retangulares sujas 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 GDI CombineRgn para determinar a interseção entre a região suja do quadro anterior e a região suja do quadro atual. Depois de obter a região de intersecção, chame a função deGDIGetRegionData para obter cada retângulo individual da região de interseção e, em seguida, chame o ID3D11DeviceContext1::CopySubresourceRegion1 método para copiar cada retângulo de interseção para o buffer traseiro atual. O próximo trecho 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;
       }
}

Corrente de troca do modelo Bitblt com retângulos sujos

Você pode usar retângulos sujos com cadeias de permuta DXGI que são executadas no modelo bitblt (definido com DXGI_SWAP_EFFECT_SEQUENTIAL). As cadeias de troca de modelo Bitblt que usam mais de um buffer também devem rastrear retângulos sujos sobrepostos entre quadros da mesma forma descrita em Rastreando retângulos sujos e retângulos de rolagem em vários quadros para cadeias de troca de modelo invertido. As cadeias de troca do modelo Bitblt com apenas um buffer não precisam rastrear retângulos sujos sobrepostos porque todo o buffer é redesenhado a cada quadro.

DXGI 1.2 Melhorias