Dela via


API för skrivbordsduplicering

Windows 8 inaktiverar XDDM-speglingsdrivrutiner (Standard Windows 2000 Display Driver Model) och erbjuder skrivbordsduplicerings-API:et i stället. API:et för skrivbordsduplicering ger fjärråtkomst till en skrivbordsavbildning för samarbetsscenarier. Appar kan använda skrivbordsduplicerings-API:et för att få åtkomst till uppdateringar från bildruta till skrivbordet. Eftersom appar tar emot uppdateringar av skrivbordsavbildningen på en DXGI-yta kan apparna använda GPU:ns fulla kraft för att bearbeta avbildningsuppdateringarna.

Uppdatera skrivbordsavbildningsdata

DXGI tillhandahåller en yta som innehåller en aktuell skrivbordsavbildning via den nya metoden IDXGIOutputDuplication::AcquireNextFrame. Formatet på skrivbordsbilden är alltid DXGI_FORMAT_B8G8R8A8_UNORM oavsett vad det aktuella visningsläget är. Tillsammans med den här ytan returnerar dessa IDXGIOutputDuplication- metoder de angivna typerna av information som hjälper dig att avgöra vilka pixlar i ytan du behöver bearbeta:

  • IDXGIOutputDuplication::GetFrameDirtyRects returnerar smutsiga regioner, som inte är överlappande rektanglar som anger de områden i skrivbordsavbildningen som operativsystemet uppdaterade sedan du bearbetade den föregående skrivbordsavbildningen.
  • IDXGIOutputDuplication::GetFrameMoveRects returnerar flyttregioner, som är rektanglar med bildpunkter i skrivbordsavbildningen som operativsystemet flyttade till en annan plats i samma bild. Varje flyttregion består av en målrektangel och en källpunkt. Källpunkten anger platsen där operativsystemet kopierade regionen och målrektangeln anger var operativsystemet flyttade regionen. Flyttregioner är alltid icke-utsträckta regioner, så källan har alltid samma storlek som målet.

Anta att skrivbordsavbildningen överfördes via en långsam anslutning till fjärrklientappen. Mängden data som skickas via anslutningen minskas genom att endast ta emot data om hur klientappen måste flytta områden med bildpunkter i stället för faktiska pixeldata. För att bearbeta flyttningarna måste klientappen ha lagrat den fullständiga sista avbildningen.

Även om operativsystemet ackumulerar obearbetade skrivbordsavbildningsuppdateringar kan det ta slut på utrymme för att lagra uppdateringsregionerna korrekt. I den här situationen börjar operativsystemet ackumulera uppdateringarna genom att sammansönja dem med befintliga uppdateringsregioner för att täcka alla nya uppdateringar. Operativsystemet täcker därför pixlar som inte har uppdaterats i ramen ännu. Men den här situationen ger inte visuella problem i klientappen eftersom du får hela skrivbordsbilden och inte bara de uppdaterade bildpunkterna.

Om du vill rekonstruera rätt skrivbordsavbildning måste klientappen först bearbeta alla flyttregioner och sedan bearbeta alla smutsiga regioner. Någon av dessa listor över smutsiga och flytta regioner kan vara helt tom. Exempelkoden från Desktop Duplication Sample visar hur du bearbetar både de smutsiga och flytta regionerna i en enda ram:

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

Rotera skrivbordsavbildningen

Du måste lägga till explicit kod i klientappen för skrivbordsduplicering för att stödja roterade lägen. I ett roterat läge är den yta som du får från IDXGIOutputDuplication::AcquireNextFrame alltid i oroterad orientering och skrivbordsbilden roteras inom ytan. Om skrivbordet till exempel är inställt på 768x1024 vid 90 graders rotation returnerar AcquireNextFrame en 1024x768-yta med skrivbordsbilden roterad i den. Här är några rotationsexempel.

Visningsläge inställt från kontrollpanelen Visningsläge som returneras av GDI eller DXGI Surface som returneras från AcquireNextFrame
1024x768 landskap 1024x768 0 graders rotation 1024x768[newline] icke-roterad fjärrskrivbord
1024x768 stående 768x1024 90 graders rotation 1024x768[newline] roterat 90 grader fjärrskrivbord
1024x768 landskap (vänt) 1024x768 180 graders rotation 1024x768[newline] roterat 180 grader fjärrskrivbord
1024x768 stående (vänt) 768x1024 270 graders rotation 1024x768[newline] roterade 270 grader fjärrskrivbord

 

Koden i skrivbordsdupliceringsklientappen måste rotera skrivbordsavbildningen på rätt sätt innan du visar skrivbordsavbildningen.

Not

I scenarier med flera övervakare kan du rotera skrivbordsavbildningen för varje bildskärm oberoende av varandra.

 

Uppdatera skrivbordspekaren

Du måste använda API:et för skrivbordsduplicering för att avgöra om klientappen måste rita muspekarformen på skrivbordsbilden. Antingen ritas muspekaren redan på skrivbordsbilden som IDXGIOutputDuplication::AcquireNextFrame tillhandahåller eller så är muspekaren separat från skrivbordsbilden. Om muspekaren ritas till skrivbordsbilden anger pekarens placeringsdata som rapporteras av AcquireNextFrame (i PointerPosition medlem i DXGI_OUTDUPL_FRAME_INFO att pFrameInfo parameter pekar på) att en separat pekare inte visas. Om grafikkortet lägger över muspekaren ovanpå skrivbordsbilden rapporterar AcquireNextFrame att en separat pekare är synlig. Klientappen måste därför rita muspekarformen på skrivbordsbilden för att korrekt representera vad den aktuella användaren ser på sin bildskärm.

Om du vill rita skrivbordets muspekare använder du PointerPosition medlem i DXGI_OUTDUPL_FRAME_INFO från parametern pFrameInfo i AcquireNextFrame för att ta reda på var du hittar det övre vänstra hörnet av muspekaren på skrivbordsbilden. När du ritar den första ramen måste du använda metoden IDXGIOutputDuplication::GetFramePointerShape för att få information om muspekarens form. Varje anrop till AcquireNextFrame för att hämta nästa bildruta ger också den aktuella pekarpositionen för ramen. Å andra sidan behöver du bara använda GetFramePointerShape igen om formen ändras. Behåll därför en kopia av den sista pekarbilden och använd den för att rita på skrivbordet om inte muspekarens form ändras.

Not

Tillsammans med pekarformbilden ger GetFramePointerShape storleken på platsen för frekventa platser. Hot spot ges endast i informationssyfte. Platsen där pekarbilden ska ritas är oberoende av hotspoten.

 

Den här exempelkoden från Desktop Duplication Sample visar hur du hämtar muspekarformen:

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

DXGI 1.2 Förbättringar