翻轉模型、已變更矩形、捲動區域
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.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 和 pDirtyRects 成員所指定 DXGI_PRESENT_PARAMETERS。
範例 2 緩衝區翻轉模型交換鏈結與已變更的矩形和捲動矩形
下一個圖例和順序顯示使用已變更矩形和滾動矩形的 DXGI 翻轉模型呈現作業範例。 在此範例中,我們使用翻轉模型簡報的緩衝區數目下限,這是兩個緩衝區計數,一個前端緩衝區包含應用程式顯示內容,另一個後端緩衝區,其中包含應用程式想要轉譯的目前畫面格。
- 如畫面開頭的前端緩衝區所示,可捲動的應用程式一開始會顯示含有一些文字和動畫影片的框架。
- 為了轉譯下一個畫面格,應用程式會將轉譯到背景緩衝區,該矩形會更新動畫視訊和視窗的新線條。
- 當應用程式呼叫 IDXGISwapChain1::P resent1時,它會指定已變更的矩形和滾動矩形和位移。 執行時間接下來會從上一個框架複製滾動矩形減去更新的已變更矩形到目前的背景緩衝區。
- 執行時間最後會交換前端和後端緩衝區。
追蹤已變更的矩形,並在多個框架之間捲動矩形
當您在應用程式中使用已變更的矩形時,您必須追蹤已變更的矩形以支援累加轉譯。 當您的應用程式呼叫 IDXGISwapChain1::P resent1 時,您必須確定已變更矩形中的每個圖元都是最新的。 如果您未完全重新轉譯已變更矩形的整個區域,或如果您不知道某些已變更的區域,則必須在開始轉譯之前,先將先前完全一致的後端緩衝區的某些資料複製到目前的過時背景緩衝區。
執行時間只會將上一個畫面格的更新區域與目前框架更新區域之間的差異複製到目前背景緩衝區。 如果這些區域相交,執行時間只會複製它們之間的差異。 如下圖和順序所示,您必須從畫面格 1 複製中途矩形之間的交集,以及從畫面格 2 到框架 2 的中途矩形之間的交集。
- 在畫面 1 中呈現已變更的矩形。
- 從圖文框 1 和畫面格 2 中變更矩形之間的交集複製到框架 2 的中途矩形。
- 在畫面 2 中呈現已變更的矩形。
若要一般化,針對具有 N 個緩衝區的交換鏈結,執行時間從最後一個框架複製到目前框架目前框架的區域為:
其中 buffer 表示交換鏈結中的緩衝區索引,從目前緩衝區索引開始為零。
您可以保留前一個框架與目前框架的變更矩形之間的任何交集,方法是保留前一個框架的已變更矩形複本,或使用上一個框架的適當內容重新轉譯新框架的已變更矩形。
同樣地,在交換鏈結有超過 2 個背景緩衝區的情況下,您必須確定目前緩衝區的已變更矩形與所有先前框架的已變更矩形之間的重迭區域會複製或重新轉譯。
追蹤 2 個中途矩形之間的單一交集
在最簡單的情況下,當您針對每個框架更新單一已變更矩形時,兩個畫面之間的已變更矩形可能會相交。 若要瞭解上一個框架的已變更矩形和目前框架的已變更矩形是否重迭,您必須確認先前框架的已變更矩形是否與目前框架的已變更矩形交集。 您可以呼叫 GDI IntersectRect 函式,以判斷兩個代表兩個已變更矩形的 RECT 結構是否交集。
在此程式碼片段中, 對 IntersectRect 的呼叫會傳回另一個 RECT 中兩個已變更矩形的交集,稱為 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 模型交換鏈結與已變更的矩形
您可以搭配在 bitblt 模型中執行的 DXGI 交換鏈結使用已變更矩形, (設定為 DXGI_SWAP_EFFECT_SEQUENTIAL) 。 使用多個緩衝區的 Bitblt 模型交換鏈結也必須追蹤跨畫面格重迭的本機矩形,如同 追蹤變更矩形,以及跨多個畫面 捲動矩形捲動模型交換鏈結中所述。 只有一個緩衝區的 Bitblt 模型交換鏈結不需要追蹤重迭的變更矩形,因為整個緩衝區都會重新繪製每個框架。