API di duplicazione desktop
Windows 8 disabilita i driver mirror Windows 2000 Display Driver Model (XDDM) standard e offre invece l'API di duplicazione desktop. L'API di duplicazione desktop fornisce l'accesso remoto a un'immagine desktop per scenari di collaborazione. Le app possono usare l'API di duplicazione desktop per accedere agli aggiornamenti frame per frame sul desktop. Poiché le app ricevono aggiornamenti per l'immagine desktop in una superficie DXGI, le app possono usare la potenza completa della GPU per elaborare gli aggiornamenti delle immagini.
- Aggiornamento dei dati dell'immagine desktop
- rotazione dell'immagine desktop
- Aggiornamento del puntatore desktop
- argomenti correlati
Aggiornamento dei dati dell'immagine desktop
DXGI fornisce una superficie che contiene un'immagine desktop corrente tramite il nuovo metodo IDXGIOutputDuplication::AcquireNextFrame. Il formato dell'immagine desktop è sempre DXGI_FORMAT_B8G8R8A8_UNORM indipendentemente dalla modalità di visualizzazione corrente. Insieme a questa superficie, questi metodi IDXGIOutputDuplication restituiscono i tipi di informazioni indicati che consentono di determinare quali pixel all'interno della superficie è necessario elaborare:
- IDXGIOutputDuplication::GetFrameDirtyRects restituisce aree dirty, che sono rettangoli non sovrapposti che indicano le aree dell'immagine desktop aggiornate dal sistema operativo dopo l'elaborazione dell'immagine desktop precedente.
- IDXGIOutputDuplication::GetFrameMoveRects restituisce aree di spostamento, ovvero rettangoli di pixel nell'immagine desktop spostata dal sistema operativo in un'altra posizione all'interno della stessa immagine. Ogni area di spostamento è costituita da un rettangolo di destinazione e da un punto di origine. Il punto di origine specifica il percorso da cui il sistema operativo ha copiato l'area e il rettangolo di destinazione specifica dove il sistema operativo ha spostato tale area. Le aree di spostamento sono sempre aree non estese, quindi l'origine è sempre la stessa dimensione della destinazione.
Si supponga che l'immagine desktop sia stata trasmessa tramite una connessione lenta all'app client remota. La quantità di dati inviati tramite la connessione viene ridotta ricevendo solo dati su come l'app client deve spostare le aree di pixel anziché i dati pixel effettivi. Per elaborare gli spostamenti, l'app client deve avere archiviato l'ultima immagine completa.
Mentre il sistema operativo accumula aggiornamenti delle immagini desktop non elaborati, potrebbe esaurire spazio per archiviare accuratamente le aree di aggiornamento. In questo caso, il sistema operativo inizia ad accumulare gli aggiornamenti combinandoli con le aree di aggiornamento esistenti per coprire tutti i nuovi aggiornamenti. Di conseguenza, il sistema operativo copre i pixel che non è stato effettivamente aggiornato in quel frame. Ma questa situazione non genera problemi visivi nell'app client perché ricevi l'intera immagine desktop e non solo i pixel aggiornati.
Per ricostruire l'immagine desktop corretta, l'app client deve prima elaborare tutte le aree di spostamento e quindi elaborare tutte le aree dirty. Uno di questi elenchi di aree dirty e move può essere completamente vuoto. Il codice di esempio dell'esempio di duplicazione desktop illustra come elaborare le aree dirty e spostarle in un singolo fotogramma:
//
// 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;
}
Rotazione dell'immagine desktop
È necessario aggiungere codice esplicito all'app client di duplicazione desktop per supportare le modalità ruotate. In una modalità ruotata, la superficie ricevuta da IDXGIOutputDuplication::AcquireNextFrame è sempre nell'orientamento non ruotato e l'immagine desktop viene ruotata all'interno della superficie. Ad esempio, se il desktop è impostato su 768x1024 a una rotazione di 90 gradi, AcquireNextFrame restituisce una superficie di 1024x768 con l'immagine desktop ruotata al suo interno. Ecco alcuni esempi di rotazione.
Modalità di visualizzazione impostata dal pannello di controllo dello schermo | Modalità di visualizzazione restituita da GDI o DXGI | Surface restituito da AcquireNextFrame |
---|---|---|
Paesaggio 1024x768 | Rotazione di 1024x768 0 gradi | 1024x768[newline] ![]() |
Ritratto 1024x768 | Rotazione di 768x1024 a 90 gradi | 1024x768[nuova riga] ![]() |
Orizzontale 1024x768 (capovolto) | Rotazione di 1024x768 a 180 gradi | 1024x768[nuova riga] ![]() |
1024x768 verticale (capovolto) | Rotazione di 768x1024 a 270 gradi | 1024x768[newline] ![]() |
Il codice nell'app client di duplicazione desktop deve ruotare l'immagine desktop in modo appropriato prima di visualizzare l'immagine desktop.
Nota
Negli scenari con più monitor è possibile ruotare l'immagine desktop per ogni monitor in modo indipendente.
Aggiornamento del puntatore desktop
È necessario usare l'API di duplicazione desktop per determinare se l'app client deve disegnare la forma del puntatore del mouse nell'immagine desktop. Il puntatore del mouse è già disegnato nell'immagine desktop che IDXGIOutputDuplication::AcquireNextFrame fornisce o il puntatore del mouse è separato dall'immagine desktop. Se il puntatore del mouse viene disegnato nell'immagine desktop, i dati di posizione del puntatore segnalati da AcquireNextFrame (nel PointerPosition membro di DXGI_OUTDUPL_FRAME_INFO a cui punta il parametro pFrameInfo) indica che un puntatore separato non è visibile. Se la scheda grafica sovrappone il puntatore del mouse sopra l'immagine desktop, AcquireNextFrame segnala che un puntatore separato è visibile. L'app client deve quindi disegnare la forma del puntatore del mouse nell'immagine desktop per rappresentare in modo accurato ciò che l'utente corrente visualizzerà sul monitor.
Per disegnare il puntatore del mouse del desktop, usare il PointerPosition membro di DXGI_OUTDUPL_FRAME_INFO dal parametro pFrameInfo di AcquireNextFrame per determinare dove individuare l'angolo superiore sinistro del puntatore del mouse nell'immagine desktop. Quando si disegna il primo fotogramma, è necessario utilizzare il metodo IDXGIOutputDuplication::GetFramePointerShape per ottenere informazioni sulla forma del puntatore del mouse. Ogni chiamata a AcquireNextFrame per ottenere il fotogramma successivo fornisce anche la posizione corrente del puntatore per tale fotogramma. D'altra parte, è necessario usare GetFramePointerShape di nuovo solo se la forma cambia. Mantenere quindi una copia dell'ultima immagine del puntatore e usarla per disegnare sul desktop, a meno che la forma del puntatore del mouse non cambi.
Nota
Insieme all'immagine della forma del puntatore, GetFramePointerShape fornisce le dimensioni della posizione dell'area ad accesso frequente. Il punto critico viene fornito solo a scopo informativo. La posizione in cui disegnare l'immagine del puntatore è indipendente dall'hotspot.
Questo codice di esempio dell'esempio di duplicazione desktop illustra come ottenere la forma del puntatore del mouse:
//
// 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;
}
Argomenti correlati