Partager via


API de duplication de bureau

Windows 8 désactive le modèle XDDM (Display Driver Model) Windows 2000 standard miroir et propose à la place l’API de duplication de bureau. L’API de duplication de bureau fournit un accès à distance à une image de bureau pour les scénarios de collaboration. Les applications peuvent utiliser l’API de duplication de bureau pour accéder aux mises à jour image par image sur le bureau. Étant donné que les applications reçoivent des mises à jour de l’image de bureau dans une surface DXGI, les applications peuvent utiliser toute la puissance du GPU pour traiter les mises à jour d’image.

Mise à jour des données d’image de bureau

DXGI fournit une surface qui contient une image de bureau actuelle via la nouvelle méthode IDXGIOutputDuplication::AcquireNextFrame . Le format de l’image de bureau est toujours DXGI_FORMAT_B8G8R8A8_UNORM quel que soit le mode d’affichage actuel. Avec cette surface, ces méthodes IDXGIOutputDuplication retournent les types d’informations indiqués qui vous aident à déterminer les pixels de la surface que vous devez traiter :

  • IDXGIOutputDuplication::GetFrameDirtyRects retourne sale régions, qui sont des rectangles qui ne se chevauchent pas et qui indiquent les zones de l’image de bureau que le système d’exploitation a mise à jour depuis que vous avez traité l’image de bureau précédente.
  • IDXGIOutputDuplication::GetFrameMoveRects retourne les régions de déplacement, qui sont des rectangles de pixels dans l’image de bureau que le système d’exploitation a déplacées vers un autre emplacement dans la même image. Chaque région de déplacement se compose d’un rectangle de destination et d’un point source. Le point source spécifie l’emplacement à partir duquel le système d’exploitation a copié la région et le rectangle de destination spécifie l’emplacement où le système d’exploitation a déplacé cette région. Les régions de déplacement étant toujours des régions non étirées, la source a toujours la même taille que la destination.

Supposons que l’image de bureau ait été transmise via une connexion lente à votre application cliente distante. La quantité de données envoyées via la connexion est réduite en recevant uniquement des données sur la façon dont votre application cliente doit déplacer des régions de pixels plutôt que des données de pixels réelles. Pour traiter les déplacements, votre application cliente doit avoir stocké la dernière image complète.

Bien que le système d’exploitation accumule les mises à jour d’images de bureau non traitées, il peut manquer d’espace pour stocker avec précision les régions de mise à jour. Dans ce cas, le système d’exploitation commence à accumuler les mises à jour en les fusionnant avec les régions de mise à jour existantes pour couvrir toutes les nouvelles mises à jour. Par conséquent, le système d’exploitation couvre les pixels qu’il n’a pas encore mis à jour dans ce cadre. Toutefois, cette situation ne génère pas de problèmes visuels sur votre application cliente, car vous recevez l’ensemble de l’image de bureau et pas seulement les pixels mis à jour.

Pour reconstruire l’image de bureau appropriée, votre application cliente doit d’abord traiter toutes les régions de déplacement, puis traiter toutes les régions sale. L’une de ces listes de régions de sale et de déplacement peut être complètement vide. L’exemple de code de l’exemple de duplication du bureau montre comment traiter les régions sale et déplacer dans une seule image :

//
// 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;
}

Rotation de l’image de bureau

Vous devez ajouter du code explicite à votre application cliente de duplication de bureau pour prendre en charge les modes pivotés. En mode pivotant, la surface que vous recevez d’IDXGIOutputDuplication::AcquireNextFrame est toujours dans l’orientation non pivotée, et l’image de bureau est pivotée à l’intérieur de la surface. Par exemple, si le bureau a la valeur 768x1024 à une rotation de 90 degrés, AcquireNextFrame retourne une surface de 1024 x 768 avec l’image de bureau pivotée à l’intérieur. Voici quelques exemples de rotation.

Mode d’affichage défini à partir du panneau de configuration d’affichage Mode d’affichage retourné par GDI ou DXGI Surface retournée à partir d’AcquireNextFrame
Paysage 1024x768 Rotation de 0 degré 1024x768 Bureau à distance non 1024x768[nouvelle ligne]
Portrait 1024x768 Rotation de 768x1024 90 degrés Bureau à distance 1024x768[nouvelle ligne] pivoté à 90 degrés
Paysage 1024x768 (retourné) Rotation 1024x768 180 degrés Bureau à distance 1024x768[nouvelle ligne] pivoté à 180 degrés
Portrait 1024x768 (retourné) Rotation 768x1024 270 degrés Bureau à distance 1024x768[nouvelle ligne] pivoté à 270 degrés

 

Le code de votre application cliente de duplication de bureau doit faire pivoter l’image de bureau de manière appropriée avant d’afficher l’image de bureau.

Notes

Dans les scénarios multi-moniteurs, vous pouvez faire pivoter l’image de bureau pour chaque moniteur indépendamment.

 

Mise à jour du pointeur de bureau

Vous devez utiliser l’API de duplication de bureau pour déterminer si votre application cliente doit dessiner la forme du pointeur de la souris sur l’image de bureau. Soit le pointeur de la souris est déjà dessiné sur l’image de bureau que fournit IDXGIOutputDuplication::AcquireNextFrame , soit le pointeur de la souris est distinct de l’image de bureau. Si le pointeur de la souris est dessiné sur l’image du bureau, les données de position du pointeur signalées par AcquireNextFrame (dans le membre PointerPosition de DXGI_OUTDUPL_FRAME_INFO vers lequel pointe le paramètre pFrameInfo ) indiquent qu’un pointeur distinct n’est pas visible. Si la carte graphique superpose le pointeur de la souris au-dessus de l’image de bureau, AcquireNextFrame signale qu’un pointeur distinct est visible. Par conséquent, votre application cliente doit dessiner la forme du pointeur de la souris sur l’image de bureau pour représenter avec précision ce que l’utilisateur actuel verra sur son moniteur.

Pour dessiner le pointeur de la souris du bureau, utilisez le membre PointerPosition de DXGI_OUTDUPL_FRAME_INFO à partir du paramètre pFrameInfo de AcquireNextFrame pour déterminer où localiser le coin supérieur gauche du pointeur de la souris sur l’image de bureau. Lorsque vous dessinez la première image, vous devez utiliser la méthode IDXGIOutputDuplication::GetFramePointerShape pour obtenir des informations sur la forme du pointeur de la souris. Chaque appel à AcquireNextFrame pour obtenir l’image suivante fournit également la position actuelle du pointeur pour cette image. En revanche, vous devez à nouveau utiliser GetFramePointerShape uniquement si la forme change. Par conséquent, conservez une copie de la dernière image de pointeur et utilisez-la pour dessiner sur le bureau, sauf si la forme du pointeur de la souris change.

Notes

Avec l’image de forme de pointeur, GetFramePointerShape fournit la taille de l’emplacement du point chaud. Le point chaud est donné à titre d’information uniquement. L’emplacement où dessiner l’image de pointeur est indépendant du point d’accès.

 

Cet exemple de code de l’exemple de duplication du bureau montre comment obtenir la forme du pointeur de souris :

//
// 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;
}

Améliorations apportées à DXGI 1.2