Desktopduplizierungs-API
Windows 8 deaktiviert das Standardmäßige Windows 2000 Display Driver Model (XDDM) Spiegel Treibern und bietet stattdessen die Desktopduplizierungs-API an. Die Desktopduplizierungs-API bietet Remotezugriff auf ein Desktopimage für Zusammenarbeitsszenarien. Apps können die Desktopduplizierungs-API verwenden, um auf Frame-für-Frame-Updates auf dem Desktop zuzugreifen. Da Apps Updates für das Desktopimage auf einer DXGI-Oberfläche erhalten, können die Apps die volle Leistungsfähigkeit der GPU nutzen, um die Imageupdates zu verarbeiten.
- Aktualisieren der Desktopimagedaten
- Drehen des Desktopimages
- Aktualisieren des Desktopzeigers
- Zugehörige Themen
Aktualisieren der Desktopimagedaten
DXGI stellt eine Oberfläche bereit, die über die neue IDXGIOutputDuplication::AcquireNextFrame-Methode ein aktuelles Desktopimage enthält. Das Format des Desktopbilds ist immer DXGI_FORMAT_B8G8R8A8_UNORM unabhängig vom aktuellen Anzeigemodus. Zusammen mit dieser Oberfläche geben diese IDXGIOutputDuplication-Methoden die angegebenen Arten von Informationen zurück, mit denen Sie ermitteln können, welche Pixel auf der Oberfläche verarbeitet werden müssen:
- IDXGIOutputDuplication::GetFrameDirtyRects gibt modifiziert Regionen zurück, bei denen es sich um nicht überlappende Rechtecke handelt, die die Bereiche des Desktopimages angeben, die das Betriebssystem seit der Verarbeitung des vorherigen Desktopimages aktualisiert hat.
- IDXGIOutputDuplication::GetFrameMoveRects gibt Move-Regionen zurück, bei denen es sich um Rechtecke von Pixeln im Desktopimage handelt, die das Betriebssystem an einen anderen Speicherort innerhalb desselben Images verschoben hat. Jeder Verschiebensbereich besteht aus einem Zielrechteck und einem Quellpunkt. Der Quellpunkt gibt den Speicherort an, von dem das Betriebssystem die Region kopiert hat, und das Zielrechteck gibt an, wohin das Betriebssystem diese Region verschoben hat. Verschieben von Regionen sind immer nicht gestreckte Regionen, sodass die Quelle immer die gleiche Größe wie das Ziel hat.
Angenommen, das Desktopimage wurde über eine langsame Verbindung mit Ihrer Remoteclient-App übertragen. Die Datenmenge, die über die Verbindung gesendet wird, wird reduziert, indem nur Daten darüber empfangen werden, wie Ihre Client-App Pixelbereiche anstelle von tatsächlichen Pixeldaten verschieben muss. Zum Verarbeiten der Verschiebungen muss Ihre Client-App das vollständige letzte Image gespeichert haben.
Während das Betriebssystem nicht verarbeitete Desktopimageupdates ansammelt, ist möglicherweise nicht mehr viel Speicherplatz vorhanden, um die Updateregionen genau zu speichern. In dieser Situation beginnt das Betriebssystem, die Updates zu sammeln, indem es sie mit vorhandenen Updateregionen zusammennimmt, um alle neuen Updates abzudecken. Daher deckt das Betriebssystem Pixel ab, die in diesem Frame noch nicht aktualisiert wurden. Diese Situation führt jedoch nicht zu visuellen Problemen für Ihre Client-App, da Sie das gesamte Desktopimage und nicht nur die aktualisierten Pixel erhalten.
Um das richtige Desktopimage zu rekonstruieren, muss Ihre Client-App zunächst alle Verschiebungsregionen verarbeiten und dann alle modifiziert Regionen verarbeiten. Jede dieser Listen mit modifiziert- und Verschiebungsregionen kann völlig leer sein. Der Beispielcode aus dem Desktopduplizierungsbeispiel zeigt, wie sie sowohl die modifiziert als auch regionen in einem einzelnen Frame verschieben:
//
// 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;
}
Drehen des Desktopimages
Sie müssen Ihrer Desktopduplizierungsclient-App expliziten Code hinzufügen, um rotierte Modi zu unterstützen. In einem gedrehten Modus befindet sich die Oberfläche, die Sie von IDXGIOutputDuplication::AcquireNextFrame erhalten, immer in der nicht gedrehten Ausrichtung, und das Desktopbild wird innerhalb der Oberfläche gedreht. Wenn der Desktop beispielsweise bei einer Drehung um 90 Grad auf 768 x 1024 festgelegt ist, gibt AcquireNextFrame eine Oberfläche von 1024 x 768 zurück, in der das Desktopbild rotiert ist. Im Folgenden finden Sie einige Rotationsbeispiele.
Einstellung des Anzeigemodus über die Systemsteuerung | Anzeigemodus, der von GDI oder DXGI zurückgegeben wird | Von AcquireNextFrame zurückgegebenes Surface |
---|---|---|
1024x768 Landschaft | 1024x768 0 Grad Drehung |
mit 1024x768[newline] |
Hochformat 1024x768 | 768x1024 90 Grad Drehung | 1024x768[newline] |
1024x768 Querformat (gedreht) | 1024x768 180 Grad Drehung | 1024x768[Newline] |
Hochformat 1024x768 (gedreht) | 768x1024 270 Grad Drehung | 1024x768[Newline] |
Der Code in Ihrer Desktopduplizierungsclient-App muss das Desktopimage entsprechend rotieren, bevor Sie das Desktopimage anzeigen.
Hinweis
In Szenarien mit mehreren Monitoren können Sie das Desktopimage für jeden Monitor unabhängig rotieren.
Aktualisieren des Desktopzeigers
Sie müssen die Desktopduplizierungs-API verwenden, um zu ermitteln, ob Ihre Client-App die Mauszeigerform auf das Desktopimage zeichnen muss. Entweder wird der Mauszeiger bereits auf das Desktopbild gezeichnet, das IDXGIOutputDuplication::AcquireNextFrame bereitstellt, oder der Mauszeiger ist vom Desktopimage getrennt. Wenn der Mauszeiger auf das Desktopbild gezeichnet wird, zeigen die Von AcquireNextFrame gemeldeten Zeigerpositionsdaten (im PointerPosition-Element von DXGI_OUTDUPL_FRAME_INFO , auf das der pFrameInfo-Parameter verweist), dass ein separater Zeiger nicht sichtbar ist. Wenn der Grafikkarte den Mauszeiger über das Desktopbild überlagert, meldet AcquireNextFrame , dass ein separater Zeiger sichtbar ist. Daher muss Ihre Client-App die Mauszeigerform auf das Desktopbild zeichnen, um genau darzustellen, was der aktuelle Benutzer auf dem Monitor sehen wird.
Um den Mauszeiger des Desktops zu zeichnen, verwenden Sie den PointerPosition-Member von DXGI_OUTDUPL_FRAME_INFO aus dem pFrameInfo-Parameter von AcquireNextFrame , um zu bestimmen, wo die obere linke Ecke des Mauszeigers auf dem Desktopbild zu finden ist. Wenn Sie den ersten Frame zeichnen, müssen Sie die IDXGIOutputDuplication::GetFramePointerShape-Methode verwenden, um Informationen zur Form des Mauszeigers abzurufen. Jeder Aufruf von AcquireNextFrame zum Abrufen des nächsten Frames stellt auch die aktuelle Zeigerposition für diesen Frame bereit. Andererseits müssen Sie GetFramePointerShape nur dann erneut verwenden, wenn sich das Shape ändert. Behalten Sie also eine Kopie des letzten Zeigersbilds bei, und verwenden Sie es zum Zeichnen auf dem Desktop, es sei denn, die Form des Mauszeigers ändert sich.
Hinweis
Zusammen mit dem Zeigerformbild stellt GetFramePointerShape die Größe des Hot Spot-Standorts bereit. Der Hot Spot dient nur zu Informationszwecken. Die Position, an der das Zeigerbild gezeichnet werden soll, ist unabhängig vom Hotspot.
Dieser Beispielcode aus dem Desktopduplizierungsbeispiel zeigt, wie Sie die Mauszeigerform abrufen:
//
// 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;
}
Zugehörige Themen