翻转模型,脏矩形,滚动区域

DXGI 1.2 支持新的翻转模型交换链、脏矩形和滚动区域。 我们介绍了使用新的翻转模型交换链和优化演示的好处,即指定脏矩形和滚动区域。

DXGI 翻转模型演示

DXGI 1.2 为 Direct3D 10 和更高版本的 Api 增加了对翻转表示模型的支持。 在 Windows 7 中,Direct3D 9EX 首先采用翻转模型演示,以避免不必要地复制交换链缓冲区。 通过使用翻转模型,可在运行时与桌面窗口管理器 (DWM) 之间翻转后台缓冲区,因此,DWM 始终直接从后台缓冲区组合,而不是复制后台缓冲区内容。

DXGI 1.2 Api 包含修改后的 DXGI 交换链接口, IDXGISwapChain1。 可以使用多个 IDXGIFactory2接口方法创建相应的 IDXGISwapChain1 对象,以便与 HWND句柄、 CoreWindow对象、 DirectComposition或 Windows 一起使用 。无.Xaml框架。

通过在 dxgi 交换 _ _ 链 _ DESC1结构的 SwapEffect 成员中指定 " dxgi _ 交换 _ 效果" _ 反向 _ 顺序枚举值,并将 dxgi _ 交换 _ 链 _ DESC1BufferCount 成员设置为最小值2,可以选择翻转演示模型。 有关如何使用 DXGI 翻转模型的详细信息,请参阅 dxgi 翻转模型。 由于翻转表示模型的平滑演示和其他新功能,我们建议你为使用 Direct3D 10 和更高版本 Api 编写的所有新应用使用 "翻转表示模型"。

使用脏矩形和交换链演示中的滚动矩形

通过使用脏矩形和交换链演示中的滚动矩形,你可以节省内存带宽和相关的系统电源使用情况,因为操作系统无需绘制整个帧就会减少操作系统需要绘制下一个所提供的帧的像素数据量。 对于通常通过远程桌面连接和其他远程访问技术显示的应用,节省的质量在显示质量上特别突出,因为这些技术使用脏矩形并滚动元数据。

只能对在 "翻转演示模型" 中运行的 DXGI 交换链使用 "滚动"。 您可以使用脏矩形,其中包含使用 dxgi _ 交换 _ 效果 _ 顺序) 在翻转模型和 BITBLT 模型中运行的 DXGI 交换链 (设置。

在此方案中,我们将演示如何使用脏矩形和滚动功能。 此处,可滚动应用包含文本和动画动画。 应用使用脏矩形只更新窗口的动画视频和新行,而不是更新整个窗口。 滚动矩形允许操作系统复制和转换新帧上先前呈现的内容,并只在新帧上呈现新行。

此应用通过调用 IDXGISwapChain1::P resent1 方法来执行演示。 在此调用中,应用程序将一个指针传递到一个 DXGI _ 现有的 _ 参数 结构,其中包括脏矩形和脏矩形的数量、滚动矩形和关联的滚动偏移量,或者两个脏矩形和滚动矩形。 应用程序通过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 _ 现有 _ 参数pScrollRect 成员指定。 箭头显示滚动偏移量。 滚动偏移由 DXGI _ 现有 _ 参数pScrollOffset 成员指定。 实心矩形显示应用已更新为新内容的脏矩形。 填充的矩形由 DXGI _ 现有 _ 参数DirtyRectsCountpDirtyRects 成员指定。

示例 2-缓冲区翻转模型交换链,包含脏矩形和滚动矩形

下图和序列显示了一个使用脏矩形和一个滚动矩形的 DXGI 翻转模型演示操作的示例。 在此示例中,我们使用了翻转模型表示形式的最小缓冲区数,这是两个,一个前端缓冲区包含应用显示内容,另一个后台缓冲区包含应用想要呈现的当前帧。

  1. 如帧开头的前台缓冲区所示,可滚动应用最初显示带有一些文本和动画动画的帧。
  2. 若要呈现下一帧,应用会将更新动画视频的脏矩形和窗口的新行呈现到后台缓冲区。
  3. 当应用调用 IDXGISwapChain1::P resent1时,它将指定脏矩形和滚动矩形以及偏移量。 接下来,下一帧将滚动矩形从上一帧复制到当前的后台缓冲区。
  4. 运行时最终调换前台和后台缓冲区。

滚动和脏矩形翻转模型交换链的示例

跟踪脏矩形并跨多个帧滚动矩形

在应用中使用脏矩形时,必须跟踪脏矩形以支持增量渲染。 当应用程序调用 IDXGISwapChain1: 带脏矩形的:P resent1 时,必须确保已更新矩形中的每个像素都是最新的。 如果不是完全重新渲染脏矩形的整个区域,或者不知道某些区域更新,则必须在开始呈现之前,将一些数据从以前的完全连贯后台缓冲区复制到当前的陈旧后台缓冲区。

运行时仅复制上一帧的更新区域与当前帧的已更新区域之间的差异到当前的后台缓冲区。 如果这些区域相交,则运行时仅复制它们之间的差异。 如以下关系图和序列中所示,您必须将脏矩形与帧1之间的交集以及从帧2到帧2的脏矩形之间的交集。

  1. 在帧1中显示脏矩形。
  2. 从帧1中的脏矩形和从帧2到帧2的有脏矩形之间的复制交集。
  3. 在帧2中显示脏矩形。

跨多个帧跟踪滚动和脏矩形

若要通用化,对于包含 N 个缓冲区的交换链,运行时从最后一个帧复制到当前帧上的当前帧的区域是:

用于计算运行时副本的区域的公式

其中 buffer 指示交换链中的缓冲区索引,从零开始的当前缓冲区索引开始。

您可以通过保留上一帧的脏矩形的副本或使用上一帧中的相应内容重新呈现新帧的脏矩形,来跟踪前一帧和当前帧的脏矩形之间的任何交集。

同样,在交换链具有2个以上的后台缓冲区的情况下,必须确保复制或重新呈现当前缓冲区的脏矩形与所有前一帧的脏矩形之间的重叠区域。

跟踪两个脏矩形之间的单个交集

在最简单的情况下,每帧更新一个脏矩形时,两个帧间的脏矩形可能会相交。 若要确定上一帧的脏矩形和当前帧的脏矩形是否重叠,需要验证上一帧的脏矩形是否与当前帧的脏矩形相交。 可以调用 GDI IntersectRect 函数来确定两个表示两个脏矩形的 矩形 结构是否相交。

在此代码片段中,对 IntersectRect 的调用返回另一个名为 dirtyRectCopy 的矩形中 的两个脏矩形的交集。 在代码段确定两个脏矩形相交后,它将调用 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;
       }
}

带脏矩形的 Bitblt 模型交换链

可以将脏矩形与 DXGI 交换链一起使用,这些交换链在 bitblt 模型中运行 (使用 dxgi _ 交换 _ 效果 _ 顺序) 设置。 使用多个缓冲区的 Bitblt 模型交换链还必须按以下方式跟踪帧中的重叠脏帧: 跟踪脏矩形并跨多个帧滚动多个帧 以切换模型交换链。 仅有一个缓冲区的 Bitblt 模型交换链无需跟踪重叠的已更新矩形,因为整个缓冲区将被每帧重绘。

DXGI 1.2 改进