デスクトップ重複 API
Windows 8 では、標準の Windows 2000 ディスプレイ ドライバー モデル (XDDM) ミラー ドライバーが無効になり、代わりにデスクトップ重複 API が提供されます。 デスクトップ重複 API は、コラボレーション シナリオ用のデスクトップ イメージへのリモート アクセスを提供します。 アプリでは、デスクトップ重複 API を使用して、フレームごとの更新プログラムにデスクトップにアクセスできます。 アプリは DXGI サーフェイスでデスクトップ イメージの更新プログラムを受け取るため、アプリは GPU のフル パワーを使用してイメージの更新を処理できます。
デスクトップ イメージ データの更新
DXGI は、新しい IDXGIOutputDuplication::AcquireNextFrame メソッドを使用して、現在のデスクトップ イメージを含むサーフェスを提供します。 デスクトップ イメージの形式は、現在の表示モードに関係なく常に DXGI_FORMAT_B8G8R8A8_UNORM されます。 このサーフェスと共に、これらの IDXGIOutputDuplication メソッドは、処理する必要があるサーフェス内のピクセルを決定するのに役立つ情報の種類を返します。
- IDXGIOutputDuplication::GetFrameDirtyRects はダーティ領域を返します。これは、前のデスクトップ イメージを処理してからオペレーティング システムが更新したデスクトップ イメージの領域を示す重複しない四角形です。
- IDXGIOutputDuplication::GetFrameMoveRects は移動領域を返します。これは、オペレーティング システムが同じイメージ内の別の場所に移動したデスクトップ イメージ内のピクセルの四角形です。 各移動領域は、移動先の四角形とソース ポイントで構成されます。 ソース ポイントは、オペレーティング システムがリージョンをコピーした場所を指定し、コピー先の四角形はオペレーティング システムがその領域を移動した場所を指定します。 移動領域は常に引き伸ばされていない領域であるため、ソースは常に移動先と同じサイズになります。
デスクトップ イメージがリモート クライアント アプリへの低速接続を介して送信されたとします。 接続経由で送信されるデータの量は、クライアント アプリが実際のピクセル データではなくピクセルの領域を移動する方法に関するデータのみを受信することによって削減されます。 移動を処理するには、クライアント アプリが最後のイメージ全体を格納している必要があります。
オペレーティング システムは未処理のデスクトップ イメージの更新プログラムを蓄積しますが、更新プログラムのリージョンを正確に格納するために領域が不足する可能性があります。 この状況では、オペレーティング システムは、すべての新しい更新プログラムをカバーするために既存の更新リージョンと結合することで、更新プログラムの蓄積を開始します。 その結果、オペレーティング システムは、まだそのフレームで実際に更新されていないピクセルを対象とします。 ただし、この状況では、更新されたピクセルだけでなく、デスクトップ イメージ全体を受け取るため、クライアント アプリで視覚的な問題が発生することはありません。
適切なデスクトップ イメージを再構築するには、クライアント アプリで最初にすべての移動リージョンを処理してから、すべてのダーティ リージョンを処理する必要があります。 ダーティ リージョンと移動リージョンのこれらのリストは、いずれも完全に空にすることができます。 デスクトップ重複サンプル のコード例は、ダーティ リージョンと移動リージョンの両方を 1 つのフレームで処理する方法を示しています。
//
// Get next frame and write it into Data
//
HRESULT DUPLICATIONMANAGER::GetFrame(_Out_ FRAME_DATA* Data)
{
HRESULT hr = S_OK;
IDXGIResource* DesktopResource = NULL;
DXGI_OUTDUPL_FRAME_INFO FrameInfo;
//Get new frame
hr = DeskDupl->AcquireNextFrame(500, &FrameInfo, &DesktopResource);
if (FAILED(hr))
{
if ((hr != DXGI_ERROR_ACCESS_LOST) && (hr != DXGI_ERROR_WAIT_TIMEOUT))
{
DisplayErr(L"Failed to acquire next frame in DUPLICATIONMANAGER", L"Error", hr);
}
return hr;
}
// If still holding old frame, destroy it
if (AcquiredDesktopImage)
{
AcquiredDesktopImage->Release();
AcquiredDesktopImage = NULL;
}
// QI for IDXGIResource
hr = DesktopResource->QueryInterface(__uuidof(ID3D11Texture2D), reinterpret_cast<void **>(&AcquiredDesktopImage));
DesktopResource->Release();
DesktopResource = NULL;
if (FAILED(hr))
{
DisplayErr(L"Failed to QI for ID3D11Texture2D from acquired IDXGIResource in DUPLICATIONMANAGER", L"Error", hr);
return hr;
}
// Get metadata
if (FrameInfo.TotalMetadataBufferSize)
{
// Old buffer too small
if (FrameInfo.TotalMetadataBufferSize > MetaDataSize)
{
if (MetaDataBuffer)
{
delete [] MetaDataBuffer;
MetaDataBuffer = NULL;
}
MetaDataBuffer = new (std::nothrow) BYTE[FrameInfo.TotalMetadataBufferSize];
if (!MetaDataBuffer)
{
DisplayErr(L"Failed to allocate memory for metadata in DUPLICATIONMANAGER", L"Error", E_OUTOFMEMORY);
MetaDataSize = 0;
Data->MoveCount = 0;
Data->DirtyCount = 0;
return E_OUTOFMEMORY;
}
MetaDataSize = FrameInfo.TotalMetadataBufferSize;
}
UINT BufSize = FrameInfo.TotalMetadataBufferSize;
// Get move rectangles
hr = DeskDupl->GetFrameMoveRects(BufSize, reinterpret_cast<DXGI_OUTDUPL_MOVE_RECT*>(MetaDataBuffer), &BufSize);
if (FAILED(hr))
{
if (hr != DXGI_ERROR_ACCESS_LOST)
{
DisplayErr(L"Failed to get frame move rects in DUPLICATIONMANAGER", L"Error", hr);
}
Data->MoveCount = 0;
Data->DirtyCount = 0;
return hr;
}
Data->MoveCount = BufSize / sizeof(DXGI_OUTDUPL_MOVE_RECT);
BYTE* DirtyRects = MetaDataBuffer + BufSize;
BufSize = FrameInfo.TotalMetadataBufferSize - BufSize;
// Get dirty rectangles
hr = DeskDupl->GetFrameDirtyRects(BufSize, reinterpret_cast<RECT*>(DirtyRects), &BufSize);
if (FAILED(hr))
{
if (hr != DXGI_ERROR_ACCESS_LOST)
{
DisplayErr(L"Failed to get frame dirty rects in DUPLICATIONMANAGER", L"Error", hr);
}
Data->MoveCount = 0;
Data->DirtyCount = 0;
return hr;
}
Data->DirtyCount = BufSize / sizeof(RECT);
Data->MetaData = MetaDataBuffer;
}
Data->Frame = AcquiredDesktopImage;
Data->FrameInfo = FrameInfo;
return hr;
}
//
// Release frame
//
HRESULT DUPLICATIONMANAGER::DoneWithFrame()
{
HRESULT hr = S_OK;
hr = DeskDupl->ReleaseFrame();
if (FAILED(hr))
{
DisplayErr(L"Failed to release frame in DUPLICATIONMANAGER", L"Error", hr);
return hr;
}
if (AcquiredDesktopImage)
{
AcquiredDesktopImage->Release();
AcquiredDesktopImage = NULL;
}
return hr;
}
デスクトップ イメージの回転
回転モードをサポートするには、デスクトップ重複クライアント アプリに明示的なコードを追加する必要があります。 回転モードでは、IDXGIOutputDuplication::AcquireNextFrame から受け取るサーフェスは常に回転しない方向になり、デスクトップ イメージはサーフェス内で回転します。 たとえば、デスクトップが 90 度回転で 768 x 1024 に設定されている場合、AcquireNextFrame は、デスクトップ イメージが回転された 1024 x 768 サーフェスを返します。 回転の例をいくつか次に示します。
表示コントロール パネルから設定された表示モード | GDI または DXGI によって返される表示モード | AcquireNextFrameから返される Surface |
---|---|---|
1024 x 768 の風景 | 1024 x 768 0 度回転 | 1024x768[改行] ![]() |
1024 x 768 縦 | 768 x 1024 90 度回転 | 1024 x 768[改行] ![]() |
1024 x 768 横 (反転) | 1024 x 768 180 度回転 | 1024 x 768[改行] ![]() |
1024 x 768 縦 (反転) | 768 x 1024 270 度回転 | 1024 x 768[改行] ![]() |
デスクトップ複製クライアント アプリのコードは、デスクトップ イメージを表示する前に、デスクトップ イメージを適切に回転させる必要があります。
手記
マルチモニターのシナリオでは、モニターごとにデスクトップ イメージを個別に回転させることができます。
デスクトップ ポインターの更新
デスクトップ複製 API を使用して、クライアント アプリでマウス ポインターの図形をデスクトップ イメージに描画する必要があるかどうかを判断する必要があります。 IDXGIOutputDuplication::AcquireNextFrame が提供するデスクトップ イメージにマウス ポインターが既に描画されているか、マウス ポインターがデスクトップ イメージから分離されています。 マウス ポインターがデスクトップ イメージに描画されている場合、AcquireNextFrame (pFrameInfo パラメーターが指す DXGI_OUTDUPL_FRAME_INFO の PointerPosition メンバー内) によって報告されるポインター位置データは、別のポインターが表示されていないことを示します。 グラフィックス アダプターがデスクトップ イメージの上にマウス ポインターをオーバーレイする場合、AcquireNextFrame 別のポインターが表示されていることを報告します。 そのため、クライアント アプリでは、現在のユーザーがモニターに表示する内容を正確に表すために、デスクトップ イメージにマウス ポインターの図形を描画する必要があります。
デスクトップのマウス ポインターを描画するには、AcquireNextFrame の pFrameInfo パラメーターから DXGI_OUTDUPL_FRAME_INFO の PointerPosition メンバーを使用して、デスクトップ イメージ上のマウス ポインターの左上隅を見つける場所を決定します。 最初のフレームを描画するときは、IDXGIOutputDuplication::GetFramePointerShape メソッドを使用して、マウス ポインターの形状に関する情報を取得する必要があります。 次のフレームを取得する AcquireNextFrame 呼び出すたびに、そのフレームの現在のポインター位置も提供されます。 一方、GetFramePointerShape をもう一度使用する必要があるのは、図形が変更された場合のみです。 そのため、マウス ポインターの図形が変更されない限り、最後のポインターイメージのコピーを保持し、デスクトップに描画するために使用します。
手記
GetFramePointerShapeポインター図形イメージと共に、ホット スポットの場所のサイズを提供します。 ホット スポットは情報提供のみを目的として提供されます。 ポインターイメージを描画する場所は、ホットスポットに依存しません。
デスクトップ重複サンプル の次のコード例は、マウス ポインターの図形を取得する方法を示しています。
//
// Retrieves mouse info and write it into PtrInfo
//
HRESULT DUPLICATIONMANAGER::GetMouse(_Out_ PTR_INFO* PtrInfo, _In_ DXGI_OUTDUPL_FRAME_INFO* FrameInfo, INT OffsetX, INT OffsetY)
{
HRESULT hr = S_OK;
// A non-zero mouse update timestamp indicates that there is a mouse position update and optionally a shape change
if (FrameInfo->LastMouseUpdateTime.QuadPart == 0)
{
return hr;
}
bool UpdatePosition = true;
// Make sure we don't update pointer position wrongly
// If pointer is invisible, make sure we did not get an update from another output that the last time that said pointer
// was visible, if so, don't set it to invisible or update.
if (!FrameInfo->PointerPosition.Visible && (PtrInfo->WhoUpdatedPositionLast != OutputNumber))
{
UpdatePosition = false;
}
// If two outputs both say they have a visible, only update if new update has newer timestamp
if (FrameInfo->PointerPosition.Visible && PtrInfo->Visible && (PtrInfo->WhoUpdatedPositionLast != OutputNumber) && (PtrInfo->LastTimeStamp.QuadPart > FrameInfo->LastMouseUpdateTime.QuadPart))
{
UpdatePosition = false;
}
// Update position
if (UpdatePosition)
{
PtrInfo->Position.x = FrameInfo->PointerPosition.Position.x + OutputDesc.DesktopCoordinates.left - OffsetX;
PtrInfo->Position.y = FrameInfo->PointerPosition.Position.y + OutputDesc.DesktopCoordinates.top - OffsetY;
PtrInfo->WhoUpdatedPositionLast = OutputNumber;
PtrInfo->LastTimeStamp = FrameInfo->LastMouseUpdateTime;
PtrInfo->Visible = FrameInfo->PointerPosition.Visible != 0;
}
// No new shape
if (FrameInfo->PointerShapeBufferSize == 0)
{
return hr;
}
// Old buffer too small
if (FrameInfo->PointerShapeBufferSize > PtrInfo->BufferSize)
{
if (PtrInfo->PtrShapeBuffer)
{
delete [] PtrInfo->PtrShapeBuffer;
PtrInfo->PtrShapeBuffer = NULL;
}
PtrInfo->PtrShapeBuffer = new (std::nothrow) BYTE[FrameInfo->PointerShapeBufferSize];
if (!PtrInfo->PtrShapeBuffer)
{
DisplayErr(L"Failed to allocate memory for pointer shape in DUPLICATIONMANAGER", L"Error", E_OUTOFMEMORY);
PtrInfo->BufferSize = 0;
return E_OUTOFMEMORY;
}
// Update buffer size
PtrInfo->BufferSize = FrameInfo->PointerShapeBufferSize;
}
UINT BufferSizeRequired;
// Get shape
hr = DeskDupl->GetFramePointerShape(FrameInfo->PointerShapeBufferSize, reinterpret_cast<VOID*>(PtrInfo->PtrShapeBuffer), &BufferSizeRequired, &(PtrInfo->ShapeInfo));
if (FAILED(hr))
{
if (hr != DXGI_ERROR_ACCESS_LOST)
{
DisplayErr(L"Failed to get frame pointer shape in DUPLICATIONMANAGER", L"Error", hr);
}
delete [] PtrInfo->PtrShapeBuffer;
PtrInfo->PtrShapeBuffer = NULL;
PtrInfo->BufferSize = 0;
return hr;
}
return hr;
}
関連トピック