Capovolgi modello, rettangoli sporchi, aree a scorrimento

DXGI 1.2 supporta una nuova catena di scambio del modello capovolgimento, rettangoli sporchi e aree scorrevoli. Vengono illustrati i vantaggi dell'uso della nuova catena di scambio del modello capovolgimento e dell'ottimizzazione della presentazione specificando rettangoli sporchi e aree scorrevoli.

Presentazione flip-model DXGI

DXGI 1.2 aggiunge il supporto per il modello di presentazione flip per le API Direct3D 10 e successive. In Windows 7, Direct3D 9EX ha adottato per la prima volta la presentazione flip-model per evitare di copiare inutilmente il buffer della catena di scambio. Usando il modello di inversione, i buffer nascosto vengono capovolti tra il runtime e Desktop Window Manager (DWM), quindi DWM compone sempre direttamente dal buffer nascosto anziché copiare il contenuto del buffer nascosto.

Le API DXGI 1.2 includono un'interfaccia rivista della catena di scambio DXGI, IDXGISwapChain1. Puoi usare più metodi di interfaccia IDXGIFactory2 per creare l'oggetto IDXGISwapChain1 appropriato da usare con un handle HWND , un oggetto CoreWindow , DirectComposition o il framework Windows.UI.Xaml .

Per selezionare il modello di presentazione capovolto, specificare il valore di enumerazione DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL nel membro SwapEffect della struttura DXGI_SWAP_CHAIN_DESC1 e impostando il membro BufferCount di DXGI_SWAP_CHAIN_DESC1 su almeno 2. Per altre info su come usare il modello flip DXGI, vedi modello flip DXGI. A causa della presentazione più fluida del modello di presentazione flip e di altre nuove funzionalità, è consigliabile usare il modello di presentazione flip per tutte le nuove app scritte con Direct3D 10 e versioni successive.

Uso di rettangoli sporchi e del rettangolo di scorrimento nella presentazione della catena di scambio

Usando rettangoli dirty e il rettangolo di scorrimento nella presentazione della catena di scambio, si risparmia sull'utilizzo della larghezza di banda di memoria e sull'utilizzo correlato della potenza di sistema perché la quantità di dati pixel che il sistema operativo deve disegnare il frame presentato successivo viene ridotta se il sistema operativo non deve disegnare l'intero frame. Per le app che vengono spesso visualizzate tramite Connessione Desktop remoto e altre tecnologie di accesso remoto, i risparmi sono particolarmente evidenti nella qualità di visualizzazione perché queste tecnologie usano rettangoli sporchi e metadati di scorrimento.

È possibile usare lo scorrimento solo con catene di scambio DXGI eseguite nel modello di presentazione flip. È possibile usare rettangoli dirty con catene di scambio DXGI eseguite sia nel modello flip che nel modello bitblt (impostato con DXGI_SWAP_EFFECT_SEQUENTIAL).

In questo scenario e nella figura vengono illustrate le funzionalità dell'uso di rettangoli dirty e scorrimento. In questo caso, un'app scorrevole contiene testo e animazione video. L'app usa rettangoli dirty per aggiornare semplicemente il video di animazione e la nuova riga per la finestra, invece di aggiornare l'intera finestra. Il rettangolo di scorrimento consente al sistema operativo di copiare e convertire il contenuto di cui è stato eseguito il rendering in precedenza nel nuovo frame e di eseguire il rendering solo della nuova riga nel nuovo frame.

L'app esegue la presentazione chiamando il metodo IDXGISwapChain1::P resent1 . In questa chiamata, l'app passa un puntatore a una struttura di DXGI_PRESENT_PARAMETERS che include rettangoli dirty e il numero di rettangoli dirty o il rettangolo di scorrimento e l'offset di scorrimento associato oppure entrambi i rettangoli dirty e il rettangolo di scorrimento. L'app passa 2 rettangoli dirty e il rettangolo di scorrimento. Il rettangolo di scorrimento è l'area del frame precedente che il sistema operativo deve copiare nel frame corrente prima di eseguire il rendering del frame corrente. L'app specifica il video di animazione e la nuova riga come rettangoli dirty e il sistema operativo ne esegue il rendering nel fotogramma corrente.

illustrazione di rettangoli sporchi e di scorrimento sovrapposti

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 }

Il rettangolo tratteggiato mostra il rettangolo di scorrimento nel frame corrente. Il rettangolo di scorrimento viene specificato dal membro pScrollRect di DXGI_PRESENT_PARAMETERS. La freccia mostra l'offset di scorrimento. L'offset di scorrimento viene specificato dal membro pScrollOffset di DXGI_PRESENT_PARAMETERS. I rettangoli pieni mostrano rettangoli sporchi aggiornati con il nuovo contenuto dell'app. I rettangoli riempiti vengono specificati dai membri DirtyRectsCount e pDirtyRects di DXGI_PRESENT_PARAMETERS.

Catena di scambio del modello capovolgimento a 2 buffer di esempio con rettangoli dirty e rettangolo di scorrimento

L'illustrazione e la sequenza successive illustrano un esempio di un'operazione di presentazione flip-model DXGI che usa rettangoli dirty e un rettangolo di scorrimento. In questo esempio viene usato il numero minimo di buffer per la presentazione flip-model, ovvero un numero di buffer di due, un buffer anteriore che contiene il contenuto di visualizzazione dell'app e un buffer nascosto che contiene il frame corrente che l'app vuole eseguire il rendering.

  1. Come illustrato nel buffer anteriore all'inizio del fotogramma, l'app scorrevole mostra inizialmente un frame con testo e animazione video.
  2. Per eseguire il rendering del fotogramma successivo, l'app esegue il rendering nel buffer nascosto i rettangoli dirty che aggiornano il video di animazione e la nuova riga per la finestra.
  3. Quando l'app chiama IDXGISwapChain1::P resent1, specifica i rettangoli dirty e il rettangolo di scorrimento e l'offset. Il runtime copia quindi il rettangolo di scorrimento dal frame precedente meno i rettangoli dirty aggiornati nel buffer nascosto corrente.
  4. Il runtime scambia infine i buffer front-end e back.

esempio di catena di scambio del modello capovolgimento con rettangoli di scorrimento e dirty

Rilevamento di rettangoli sporchi e rettangoli di scorrimento tra più fotogrammi

Quando usi rettangoli dirty nell'app, devi tenere traccia dei rettangoli dirty per supportare il rendering incrementale. Quando l'app chiama IDXGISwapChain1::P resent1 con rettangoli dirty, devi assicurarti che ogni pixel all'interno dei rettangoli dirty sia aggiornato. Se non si esegue completamente il rendering dell'intera area del rettangolo dirty o se non si è in grado di conoscere determinate aree che sono inattive, è necessario copiare alcuni dati dal buffer back completamente coerente precedente al buffer nascosto corrente e non aggiornato prima di iniziare il rendering.

Il runtime copia solo le differenze tra le aree aggiornate del frame precedente e le aree aggiornate del frame corrente nel buffer nascosto corrente. Se queste aree si intersecano, il runtime copia solo la differenza tra di esse. Come si può vedere nel diagramma e nella sequenza seguenti, è necessario copiare l'intersezione tra il rettangolo dirty dal frame 1 e il rettangolo dirty dal frame 2 al rettangolo dirty di frame 2.

  1. Presenta rettangolo sporco nel frame 1.
  2. Copiare l'intersezione tra il rettangolo dirty dal frame 1 e il rettangolo dirty dal frame 2 al rettangolo dirty del frame 2.
  3. Presenta rettangolo sporco nel frame 2.

scorrimento di rilevamento e rettangoli sporchi su più fotogrammi

Per generalizzare, per una catena di scambio con N buffer, l'area copiata dal runtime dall'ultimo fotogramma al frame corrente nel frame corrente è:

equazione per calcolare l'area copiata dal runtime

dove buffer indica l'indice del buffer in una catena di scambio, a partire dall'indice del buffer corrente a zero.

È possibile tenere traccia di eventuali intersezioni tra il frame precedente e i rettangoli dirty del frame corrente mantenendo una copia dei rettangoli dirty del frame precedente o eseguendo nuovamente il rendering dei rettangoli dirty del nuovo frame con il contenuto appropriato del frame precedente.

Analogamente, nei casi in cui la catena di scambio ha più di 2 buffer back, è necessario assicurarsi che le aree sovrapposte tra i rettangoli dirty del buffer corrente e i rettangoli dirty di tutti i frame precedenti vengano copiati o sottoposti nuovamente a rendering.

Rilevamento di una singola intersezione tra 2 rettangoli dirty

Nel caso più semplice, quando si aggiorna un singolo rettangolo dirty per fotogramma, i rettangoli dirty su due fotogrammi potrebbero intersecarsi. Per verificare se il rettangolo dirty del frame precedente e il rettangolo dirty del frame corrente si sovrappongono, è necessario verificare se il rettangolo dirty del frame precedente interseca con il rettangolo dirty del frame corrente. È possibile chiamare la funzione GDI IntersectRect per determinare se due strutture RECT che rappresentano i due rettangoli dirty intersecano.

In questo frammento di codice, una chiamata a IntersectRect restituisce l'intersezione di due rettangoli dirty in un altro RECT denominato dirtyRectCopy. Dopo che il frammento di codice determina che i due rettangoli dirty si intersecano, chiama il metodo ID3D11DeviceContext1::CopySubresourceRegion1 per copiare l'area di intersezione nel frame corrente.

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 usi questo frammento di codice nell'applicazione, l'app sarà pronta per chiamare IDXGISwapChain1::P resent1 per aggiornare il frame corrente con il rettangolo dirty corrente.

Rilevamento delle intersezioni tra rettangoli sporchi N

Se si specificano più rettangoli dirty, che possono includere un rettangolo dirty per la riga di scorrimento appena visualizzata, è necessario verificare e tenere traccia di eventuali sovrapposizioni che potrebbero verificarsi tra tutti i rettangoli dirty del frame precedente e tutti i rettangoli dirty del frame corrente. Per calcolare le intersezioni tra i rettangoli dirty del frame precedente e i rettangoli dirty del frame corrente, è possibile raggruppare i rettangoli dirty in aree.

In questo frammento di codice chiamiamo la funzione GDI SetRectRgn per convertire ogni rettangolo dirty in un'area rettangolare e quindi chiamiamo la funzione GDI CombineRgn per combinare tutte le aree rettangolari dirty in un gruppo.

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

È ora possibile usare la funzione GDI CombineRgn per determinare l'intersezione tra l'area dirty del frame precedente e l'area dirty del frame corrente. Dopo aver ottenuto l'area di intersezione, chiamare la funzione GDI GetRegionData per ottenere ogni singolo rettangolo dall'area di intersezione e quindi chiamare il metodo ID3D11DeviceContext1::CopySubresourceRegion1 per copiare ogni rettangolo intersecante nel buffer nascosto corrente. Il frammento di codice successivo illustra come usare queste funzioni 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;
       }
}

Catena di scambio del modello bitblt con rettangoli dirty

È possibile usare rettangoli dirty con catene di scambio DXGI eseguite nel modello bitblt (impostato con DXGI_SWAP_EFFECT_SEQUENTIAL). Le catene di scambio dei modelli bitblt che usano più buffer devono tenere traccia anche di rettangoli dirty sovrapposti tra fotogrammi nello stesso modo descritto in Rilevamento di rettangoli dirty e rettangoli di scorrimento tra più fotogrammi per catene di scambio del modello capovolto. Le catene di scambio dei modelli bitblt con un solo buffer non devono tenere traccia dei rettangoli dirty sovrapposti perché l'intero buffer viene ridisegnato ogni fotogramma.

Miglioramenti di DXGI 1.2