API di duplicazione desktop

Windows 8 disabilita i driver mirror standard di Windows 2000 (XDDM) 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-by-frame nel desktop. Poiché le app ricevono aggiornamenti all'immagine desktop in un'area DXGI, le app possono usare la potenza completa della GPU per elaborare gli aggiornamenti dell'immagine.

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. Oltre a questa superficie, questi metodi IDXGIOutputDuplicazione restituiscono i tipi indicati di informazioni che consentono di determinare quali pixel all'interno della superficie è necessario elaborare:

  • IDXGIOutputDuplica::GetFrameDirtyRects restituisce aree sporche, che sono rettangoli non sovrapposti che indicano le aree dell'immagine desktop aggiornata dal sistema operativo dopo aver elaborato l'immagine desktop precedente.
  • IDXGIOutputDuplica::GetFrameMoveRects restituisce aree di spostamento, ovvero rettangoli di pixel nell'immagine desktop spostata nel 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 aree di pixel anziché dati pixel effettivi. Per elaborare i movimenti, l'app client deve avere archiviato l'ultima immagine completa.

Mentre il sistema operativo accumula aggiornamenti dell'immagine desktop non elaborati, potrebbe essere esaurito spazio per archiviare in modo accurato le aree di aggiornamento. In questa situazione, 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 è ancora stato aggiornato in quel frame. Ma questa situazione non produce problemi visivi nell'app client perché si riceve 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 sporche. Uno di questi elenchi di aree sporche e di spostamento può essere completamente vuoto. Il codice di esempio dell'esempio di duplicazione desktop illustra come elaborare sia le aree sporche che di spostare in un singolo frame:

//
// 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 modalità ruotata, la superficie ricevuta da IDXGIOutputDuplica::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 rotazione a 90 gradi, AcquireNextFrame restituisce una superficie di 1024x768 con l'immagine desktop ruotata all'interno di esso. Ecco alcuni esempi di rotazione.

Set di modalità di visualizzazione dal pannello di controllo visualizzato Modalità di visualizzazione restituita da GDI o DXGI Surface restituito da AcquireNextFrame
Orizzontale 1024x768 Rotazione di 1024x768 0 gradi Desktop remoto nonrotato 1024x768[newline]
1024x768 verticale Rotazione di 768x1024 90 gradi Desktop remoto a 1024x768[newline] ruotato a 90 gradi
Orizzontale 1024x768 (capovolto) Rotazione di 1024x768 180 gradi Desktop remoto 1024x768[newline] ruotato a 180 gradi desktop remoto
1024x768 verticale (capovolto) Rotazione di 768x1024 270 gradi Desktop remoto a 1024x768[newline] ruotato a 270 gradi

 

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 multi-monitor è possibile ruotare l'immagine desktop per ogni monitoraggio 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 sull'immagine desktop. Il puntatore del mouse è già disegnato nell'immagine desktop che IDXGIOutputDuplica::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 membro PointerPosition 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 nella parte superiore dell'immagine desktop, AcquireNextFrame segnala che un puntatore separato è visibile. Quindi, l'app client deve disegnare la forma del puntatore del mouse sull'immagine desktop per rappresentare in modo accurato ciò che l'utente corrente visualizzerà sul proprio monitor.

Per disegnare il puntatore del mouse del desktop, usare il membro PointerPosition di DXGI_OUTDUPL_FRAME_INFO dal parametro pFrameInfo di AcquireNextFrame per determinare dove individuare l'angolo superiore sinistro del puntatore del mouse sull'immagine desktop. Quando si disegna il primo frame, è necessario usare il metodo IDXGIOutputDuplica::GetFramePointerShape per ottenere informazioni sulla forma del puntatore del mouse. Ogni chiamata a AcquireNextFrame per ottenere il frame successivo fornisce anche la posizione corrente del puntatore per tale frame. D'altra parte, è necessario usare di nuovo GetFramePointerShape solo se la forma cambia. Quindi, mantenere 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 del punto di accesso frequente. Il punto di accesso frequente viene fornito solo per scopi informativi. La posizione di dove 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;
}

Miglioramenti di DXGI 1.2