翻轉模型、髒矩形、捲動區域
DXGI 1.2 支援新的翻轉模型交換鏈結、髒矩形和捲動區域。 我們會藉由指定髒矩形和卷動區域,來說明使用新的翻轉模型交換鏈結和優化簡報的優點。
DXGI 翻轉模型簡報
DXGI 1.2 新增 Direct3D 10 和更新版本的 API 翻轉簡報模型支援。 在 Windows 7 中,Direct3D 9EX 會先採用 翻轉模型簡報,以避免不必要的複製交換鏈結緩衝區。 使用翻轉模型時,後台緩衝區會在運行時間和桌面視窗管理員之間翻轉,因此 DWM 一律會直接從後台緩衝區撰寫,而不是複製後台緩衝區內容。
DXGI 1.2 API 包含修訂后的 DXGI 交換鏈結介面,IDXGISwapChain1。 您可以使用多個 IDXGIFactory2 介面方法來建立適當的 IDXGISwapChain1 物件,以搭配 HWND 句柄、CoreWindow 物件、DirectComposition或 Windows.UI.Xaml 架構使用。
您可以在 DXGI_SWAP_CHAIN_DESC1 結構的 SwapEffect 成員中指定 DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL 列舉值,以及將 DXGI_SWAP_CHAIN_DESC1 的 BufferCount 成員設定為至少 2,以選取翻轉簡報模型。 如需如何使用 DXGI 翻轉模型的詳細資訊,請參閱 DXGI 翻轉模型。 由於翻轉簡報模型的更流暢的簡報和其他新功能,我們建議您針對使用 Direct3D 10 和更新版本 API 撰寫的所有新應用程式使用翻轉簡報模型。
在交換鏈結簡報中使用髒矩形和卷動矩形
藉由在交換鏈結簡報中使用髒矩形和滾動矩形,您可以節省記憶體頻寬的使用方式,以及系統電源的相關使用方式,因為如果作系統不需要繪製整個框架,則作系統需要繪製下一個呈現框架的圖元數據量會減少。 對於經常透過遠端桌面連線和其他遠端訪問技術顯示的應用程式,由於這些技術使用骯髒的矩形和卷動元數據,因此在顯示品質中會特別明顯節省成本。
您只能搭配在翻轉簡報模型中執行的 DXGI 交換鏈結使用卷動。 您可以搭配在翻轉模型和 bitblt 模型中執行的 DXGI 交換鏈結使用髒矩形(使用 DXGI_SWAP_EFFECT_SEQUENTIAL設定)。
在此案例和圖例中,我們會示範使用髒矩形和捲動的功能。 在這裡,可捲動的應用程式包含文字和動畫影片。 應用程式會使用骯髒的矩形來更新視窗的動畫影片和新行,而不是更新整個視窗。 滾動矩形可讓作系統複製和轉譯新框架上先前轉譯的內容,並只轉譯新框架上的新行。
應用程式會呼叫 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_PARAMETERS的 pScrollRect 成員指定。 箭號會顯示滾動位移。 捲動位移是由 DXGI_PRESENT_PARAMETERS成員 pScrollOffset 所指定。 填滿矩形會顯示應用程式以新內容更新的骯髒矩形。 填滿的矩形是由 DirtyRectsCount 和 pDirtyRectsDXGI_PRESENT_PARAMETERS的成員所指定。
範例 2 緩衝區翻轉模型交換鏈結與髒矩形和卷動矩形
下一個圖例和序列顯示使用髒矩形和滾動矩形的 DXGI 翻轉模型呈現作業範例。 在此範例中,我們使用翻轉模型簡報的緩衝區數目下限,這是兩個緩衝區計數,一個前端緩衝區,其中包含應用程式顯示內容,另一個後台緩衝區,其中包含應用程式想要轉譯的目前畫面。
- 如畫面開頭的前端緩衝區所示,可捲動的應用程式一開始會顯示具有一些文字和動畫影片的畫面。
- 若要轉譯下一個畫面格,應用程式會將轉譯到後台緩衝區,以更新動畫視訊和視窗的新線條。
- 當應用程式呼叫 IDXGISwapChain1::P resent1時,它會指定髒矩形和滾動矩形和位移。 運行時間接下來會從上一個框架複製滾動矩形,減去目前後台緩衝區的已更新的髒矩形。
- 運行時間最後會交換前端和後端緩衝區。
追蹤髒矩形,並在多個框架之間捲動矩形
當您在應用程式中使用髒矩形時,您必須追蹤髒矩形以支援累加轉譯。 當您的應用程式呼叫 IDXGISwapChain1::P resent1 含髒矩形時,您必須確保髒矩形中的每個圖元都是最新的。 如果您未完全重新轉譯髒矩形的整個區域,或如果您無法知道某些已變更的區域,您必須先從先前完全一致的後台緩衝區將某些數據複製到目前過時的緩衝區,再開始轉譯。
運行時間只會將上一個框架的更新區域與目前框架更新區域之間的差異複製到目前的後台緩衝區。 如果這些區域相交,運行時間只會複製它們之間的差異。 如下圖和順序所示,您必須從框架 1 將髒矩形之間的交集從框架 1 複製到框架 2 的髒矩形到框架 2 的髒矩形。
- 在框架 1 中呈現骯髒的矩形。
- 從框架 1 複製髒矩形之間的交集,以及從框架 2 到框架 2 的髒矩形。
- 在畫面 2 中呈現髒矩形。
若要一般化,針對具有 N 緩衝區的交換鏈結,運行時間從最後一個框架複製到目前畫面上目前框架的區域為:
的區域
其中buffer表示交換鏈結中的緩衝區索引,從目前緩衝區索引開始為零。
您可以藉由保留上一個框架的骯髒矩形的複本,或使用上一個框架的適當內容重新轉譯新框架的骯髒矩形,來追蹤上一個框架與目前框架的髒矩形之間的任何交集。
同樣地,在交換鏈結有超過 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;
}
}
Bitblt 模型交換鏈結與髒矩形
您可以使用骯髒的矩形搭配以 bitblt 模型執行的 DXGI 交換鏈結(使用 DXGI_SWAP_EFFECT_SEQUENTIAL設定)。 使用多個緩衝區的 Bitblt 模型交換鏈結也必須追蹤跨框架的重疊的髒矩形,就像 追蹤髒矩形,並在多個框架之間捲動矩形, 來翻轉模型交換鏈結。 只有一個緩衝區的 Bitblt 模型交換鏈結不需要追蹤重疊的髒矩形,因為整個緩衝區會重新繪製每個畫面。