대칭 이동 모델, 더티 사각형, 스크롤된 영역

DXGI 1.2는 새로운 대칭 이동 모델 스왑 체인, 더티 사각형 및 스크롤된 영역을 지원합니다. 새 플립 모델 스왑 체인을 사용하고 더티 사각형과 스크롤된 영역을 지정하여 프레젠테이션을 최적화할 때의 이점에 대해 설명합니다.

DXGI 대칭 이동 모델 프레젠테이션

DXGI 1.2는 Direct3D 10 이상 API에 대한 플립 프레젠테이션 모델에 대한 지원을 추가합니다. Windows 7에서 Direct3D 9EX는 스왑 체인 버퍼를 불필요하게 복사하지 않도록 플립 모델 프레젠테이션 을 처음 채택했습니다. 대칭 이동 모델을 사용하면 백 버퍼가 런타임과 DWM(데스크톱 창 관리자) 간에 대칭 이동되므로 DWM은 항상 백 버퍼 콘텐츠를 복사하는 대신 백 버퍼에서 직접 구성합니다.

DXGI 1.2 API에는 수정된 DXGI 스왑 체인 인터페이스 IDXGISwapChain1이 포함됩니다. 여러 IDXGIFactory2 인터페이스 메서드를 사용하여 HWND 핸들, CoreWindow 개체, DirectComposition 또는 Windows.UI.Xaml 프레임워크와 함께 사용할 적절한 IDXGISwapChain1 개체를 만들 수 있습니다.

DXGI_SWAP_CHAIN_DESC1 구조체의 SwapEffect 멤버에 DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL 열거형 값을 지정하고 DXGI_SWAP_CHAIN_DESC1BufferCount 멤버를 최소 2로 설정하여 대칭 이동 프레젠테이션 모델을 선택합니다. DXGI 대칭 이동 모델을 사용하는 방법에 대한 자세한 내용은 DXGI 대칭 이동 모델을 참조하세요. 대칭 이동 프레젠테이션 모델의 부드러운 프레젠테이션 및 기타 새로운 기능으로 인해 Direct3D 10 이상 API를 사용하여 작성하는 모든 새 앱에 대칭 이동 프레젠테이션 모델을 사용하는 것이 좋습니다.

스왑 체인 프레젠테이션에서 더티 사각형 및 스크롤 사각형 사용

스왑 체인 프레젠테이션에서 더티 사각형과 스크롤 사각형을 사용하면 운영 체제가 전체 프레임을 그릴 필요가 없는 경우 운영 체제에서 다음으로 표시되는 프레임을 그리는 데 필요한 픽셀 데이터의 양이 줄어들기 때문에 메모리 대역폭 사용량과 시스템 전원 관련 사용량을 줄일 수 있습니다. 원격 데스크톱 연결 및 기타 원격 액세스 기술을 통해 표시되는 앱의 경우 이러한 기술은 더티 사각형 및 스크롤 메타데이터를 사용하기 때문에 디스플레이 품질에서 특히 두드러집니다.

대칭 이동 프레젠테이션 모델에서 실행되는 DXGI 스왑 체인에서만 스크롤을 사용할 수 있습니다. 대칭 이동 모델과 비트블렛 모델(DXGI_SWAP_EFFECT_SEQUENTIAL 사용하여 설정)에서 실행되는 DXGI 스왑 체인에서 더티 사각형을 사용할 수 있습니다.

이 시나리오 및 그림에서는 더티 사각형 및 스크롤을 사용하는 기능을 보여 줍니다. 여기서 스크롤 가능한 앱에는 텍스트 및 애니메이션 비디오가 포함되어 있습니다. 앱은 더티 사각형을 사용하여 전체 창을 업데이트하는 대신 애니메이션 비디오와 창의 새 줄을 업데이트합니다. 스크롤 사각형을 사용하면 운영 체제가 새 프레임에서 이전에 렌더링된 콘텐츠를 복사 및 변환하고 새 프레임에서 새 줄만 렌더링할 수 있습니다.

앱은 IDXGISwapChain1::P resent1 메서드를 호출하여 프레젠테이션을 수행합니다. 이 호출에서 앱은 더티 사각형과 더티 사각형 수, 스크롤 사각형 및 연결된 스크롤 오프셋 또는 더티 사각형과 스크롤 사각형을 모두 포함하는 DXGI_PRESENT_PARAMETERS 구조에 포인터를 전달합니다. 앱은 2개의 더티 사각형과 스크롤 사각형을 전달합니다. 스크롤 사각형은 운영 체제가 현재 프레임을 렌더링하기 전에 현재 프레임에 복사해야 하는 이전 프레임의 영역입니다. 앱은 애니메이션 비디오와 새 줄에 애니메이션 효과를 더티 사각형으로 지정하고 운영 체제는 현재 프레임에 애니메이션 효과를 줍니다.

스크롤 및 더티 사각형이 겹치는 그림

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 }

파선 사각형은 현재 프레임의 스크롤 사각형을 표시합니다. 스크롤 사각형은 DXGI_PRESENT_PARAMETERSpScrollRect 멤버에 의해 지정됩니다. 화살표는 스크롤 오프셋을 표시합니다. 스크롤 오프셋은 DXGI_PRESENT_PARAMETERSpScrollOffset 멤버에 의해 지정됩니다. 채워진 사각형은 앱이 새 콘텐츠로 업데이트한 더티 사각형을 표시합니다. 채워진 사각형은 DXGI_PRESENT_PARAMETERSDirtyRectsCountpDirtyRects 멤버에 의해 지정됩니다.

더티 사각형 및 스크롤 사각형이 있는 샘플 2 버퍼 플립 모델 스왑 체인

다음 그림과 시퀀스는 더티 사각형과 스크롤 사각형을 사용하는 DXGI 대칭 이동 모델 프레젠테이션 작업의 예를 보여 줍니다. 이 예제에서는 대칭 이동 모델 프레젠테이션에 최소 버퍼 수를 사용합니다. 이 버퍼 수는 2개, 앱 표시 콘텐츠가 포함된 전면 버퍼 1개, 앱이 렌더링하려는 현재 프레임이 들어 있는 백 버퍼 1개입니다.

  1. 프레임의 시작 부분에 있는 전면 버퍼에 표시된 것처럼 스크롤 가능한 앱은 처음에 일부 텍스트와 애니메이션 비디오가 있는 프레임을 표시합니다.
  2. 다음 프레임을 렌더링하기 위해 앱은 애니메이션 비디오와 창의 새 줄을 업데이트하는 더티 사각형을 백 버퍼에 렌더링합니다.
  3. 앱이 IDXGISwapChain1::P resent1을 호출할 때 더티 사각형과 스크롤 사각형 및 오프셋을 지정합니다. 다음 런타임은 이전 프레임에서 업데이트된 더티 사각형을 뺀 스크롤 사각형을 현재 백 버퍼에 복사합니다.
  4. 런타임은 마지막으로 전면 및 후면 버퍼를 교환합니다.

스크롤 및 더티 사각형이 있는 대칭 이동 모델 스왑 체인의 예

여러 프레임에서 더티 사각형 및 스크롤 사각형 추적

앱에서 더티 사각형을 사용하는 경우 증분 렌더링을 지원하려면 더티 사각형을 추적해야 합니다. 앱이 더티 사각형을 사용하여 IDXGISwapChain1::P resent1을 호출하는 경우 더티 사각형 내의 모든 픽셀이 최신 상태인지 확인해야 합니다. 더티 사각형의 전체 영역을 완전히 다시 렌더링하지 않거나 더러워진 특정 영역을 알 수 없는 경우 렌더링을 시작하기 전에 이전의 완전히 일관된 백 버퍼의 일부 데이터를 현재 부실 백 버퍼로 복사해야 합니다.

런타임은 이전 프레임의 업데이트된 영역과 현재 프레임의 업데이트된 영역 간의 차이점만 현재 백 버퍼에 복사합니다. 이러한 영역이 교차하는 경우 런타임은 둘 사이의 차이만 복사합니다. 다음 다이어그램 및 시퀀스에서 볼 수 있듯이 프레임 1의 더티 사각형과 프레임 2의 더티 사각형 사이의 교차를 프레임 2의 더티 사각형으로 복사해야 합니다.

  1. 프레임 1에 더티 사각형을 표시합니다.
  2. 프레임 1의 더티 사각형과 프레임 2의 더티 사각형 사이의 교차를 프레임 2의 더티 사각형으로 복사합니다.
  3. 프레임 2에 더티 사각형을 표시합니다.

여러 프레임에서 스크롤 및 더티 사각형 추적

일반화하기 위해 N 버퍼가 있는 스왑 체인의 경우 런타임이 마지막 프레임에서 현재 프레임의 현재 프레임으로 복사하는 영역은 다음과 같습니다.

런타임에서 복사하는 영역을 계산하는 수식

여기서 버퍼는 스왑 체인의 버퍼 인덱스(현재 버퍼 인덱스 0부터 시작)를 나타냅니다.

이전 프레임의 더티 사각형 복사본을 유지하거나 새 프레임의 더티 사각형을 이전 프레임의 적절한 콘텐츠로 다시 렌더링하여 이전 프레임과 현재 프레임의 더티 사각형 간의 교차를 추적할 수 있습니다.

마찬가지로 스왑 체인에 2개 이상의 백 버퍼가 있는 경우 현재 버퍼의 더티 사각형과 이전 프레임의 더티 사각형 간에 겹치는 영역이 복사되거나 다시 렌더링되는지 확인해야 합니다.

2개의 더티 사각형 간의 단일 교차 추적

가장 간단한 경우 프레임당 단일 더티 사각형을 업데이트하면 두 프레임의 더티 사각형이 교차할 수 있습니다. 이전 프레임의 더티 사각형과 현재 프레임의 더티 사각형이 겹치는지 확인하려면 이전 프레임의 더티 사각형이 현재 프레임의 더티 사각형과 교차하는지 확인해야 합니다. GDI IntersectRect 함수를 호출하여 두 개의 더티 사각형을 나타내는 두 RECT 구조체가 교차하는지 여부를 확인할 수 있습니다.

이 코드 조각에서 IntersectRect에 대한 호출은 dirtyRectCopy라는 다른 RECT에 있는 두 개의 더티 사각형의 교집합을 반환합니다. 코드 조각에서 두 더티 사각형이 교차하는지 확인한 후 ID3D11DeviceContext1::CopySubresourceRegion1 메서드를 호출하여 교차 영역을 현재 프레임으로 복사합니다.

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.

애플리케이션에서 이 코드 조각을 사용하는 경우 앱은 IDXGISwapChain1::P resent1을 호출하여 현재 프레임을 현재 더티 사각형으로 업데이트할 준비가 됩니다.

N 더티 사각형 간의 교차 추적

프레임당 새로 표시된 스크롤 선에 대한 더티 사각형을 포함할 수 있는 여러 더티 사각형을 지정하는 경우 이전 프레임의 모든 더티 사각형과 현재 프레임의 모든 더티 사각형 간에 발생할 수 있는 겹침을 확인하고 추적해야 합니다. 이전 프레임의 더티 사각형과 현재 프레임의 더티 사각형 간의 교차를 계산하려면 더티 사각형을 영역으로 그룹화할 수 있습니다.

이 코드 조각에서는 GDI SetRectRgn 함수를 호출하여 각 더티 사각형을 사각형 영역으로 변환한 다음 GDI CombineRgn 함수를 호출하여 모든 더티 사각형 영역을 그룹으로 결합합니다.

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

이제 GDI CombineRgn 함수를 사용하여 이전 프레임의 더티 영역과 현재 프레임의 더티 영역 간의 교집합을 확인할 수 있습니다. 교차 영역을 가져온 후 GDI GetRegionData 함수를 호출하여 교차하는 지역에서 각 개별 사각형을 가져온 다음 ID3D11DeviceContext1::CopySubresourceRegion1 메서드를 호출하여 교차하는 각 사각형을 현재 백 버퍼에 복사합니다. 다음 코드 조각은 이러한 GDI 및 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;
       }
}

더티 사각형이 있는 비트블렛 모델 스왑 체인

비트blt 모델에서 실행되는 DXGI 스왑 체인에서 더티 사각형을 사용할 수 있습니다(DXGI_SWAP_EFFECT_SEQUENTIAL 사용하여 설정). 둘 이상의 버퍼를 사용하는 비트블렛 모델 스왑 체인은 대칭 이동 모델 스왑 체인을 위해 여러 프레임에서 더티 사각형 및 스크롤 사각형 추적에 설명된 것과 동일한 방식으로 프레임 간에 겹치는 더티 사각형을 추적해야 합니다. 전체 버퍼가 프레임마다 다시 그려지므로 버퍼가 하나만 있는 Bitblt 모델 스왑 체인은 겹치는 더티 사각형을 추적할 필요가 없습니다.

DXGI 1.2 개선 사항