c++directx-11direct3ddxgidesktop-duplication

AcquireNextFrame not working (Desktop Duplication API & D3D11)


I've put together this code that takes a screenshot of the desktop and maps it for raw pixel data access, but the output is all zeros. I have no idea what i've done wrong. After looking at many examples of the Desktop Duplication Api online I don't see any differences between them and mine.

This is my method to initialize everything, and no errors are raised.

BOOL init()
{
    CHECKHR(D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, 0, gFeatureLevels, gNumFeatureLevels, D3D11_SDK_VERSION, &lDevice, &FeatureLevel, &lImmediateContext))
    IDXGIDevice* lDxgiDevice;
    CHECKHR(lDevice->QueryInterface(IID_PPV_ARGS(&lDxgiDevice)))
    IDXGIAdapter* lDxgiAdapter;
    CHECKHR(lDxgiDevice->GetParent(__uuidof(IDXGIAdapter), (void**)&lDxgiAdapter))
    lDxgiDevice->Release();
    IDXGIOutput* lDxgiOutput;
    CHECKHR(lDxgiAdapter->EnumOutputs(0, &lDxgiOutput))
    lDxgiAdapter->Release();
    CHECKHR(lDxgiOutput->GetDesc(&OutputDesc))
    IDXGIOutput1* lDxgiOutput1;
    CHECKHR(lDxgiOutput->QueryInterface(IID_PPV_ARGS(&lDxgiOutput1)))
    lDxgiOutput->Release();
    CHECKHR(lDxgiOutput1->DuplicateOutput(lDevice, &lDeskDupl))
    lDxgiOutput1->Release();
    lDeskDupl->GetDesc(&OutputDuplDesc);
    D3D11_TEXTURE2D_DESC desc;
    desc.Width = OutputDuplDesc.ModeDesc.Width;
    desc.Height = OutputDuplDesc.ModeDesc.Height;
    desc.Format = OutputDuplDesc.ModeDesc.Format;
    desc.ArraySize = 1;
    desc.BindFlags = 0;
    desc.MiscFlags = 0;
    desc.SampleDesc.Count = 1;
    desc.SampleDesc.Quality = 0;
    desc.MipLevels = 1;
    desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE;
    desc.Usage = D3D11_USAGE_STAGING;
    CHECKHR(lDevice->CreateTexture2D(&desc, NULL, &lDestImage))
    return TRUE;
}

the CHECKHR macro i'm using was tested by me and works, just to clarify that it is not the problem.

This is the code that I use to actually grab the frame:

int main()
{
    init();

    HRESULT hr;
    IDXGIResource* lDesktopResource;
    ID3D11Texture2D* lAcquiredDesktopImage;
    DXGI_OUTDUPL_FRAME_INFO FrameInfo;
    while (true)
    {
        if (SUCCEEDED(lDeskDupl->AcquireNextFrame(INFINITE, &FrameInfo, &lDesktopResource)))
        {
            break;
        }
    }
    hr = lDesktopResource->QueryInterface(IID_PPV_ARGS(&lAcquiredDesktopImage));
    if (FAILED(hr))
    {
        cout << "QueryInterface failed!" << endl;
        system("pause");
    }
    lDesktopResource->Release();
    lImmediateContext->CopyResource(lDestImage, lAcquiredDesktopImage);
    lAcquiredDesktopImage->Release();
    lDeskDupl->ReleaseFrame();
    D3D11_MAPPED_SUBRESOURCE resource;
    UINT subresource = D3D11CalcSubresource(0, 0, 0);
    hr = lImmediateContext->Map(lDestImage, subresource, D3D11_MAP_READ_WRITE, 0, &resource);
    if (FAILED(hr))
    {
        cout << "Map failed!" << endl;
        system("pause");
    }
    BYTE* pb = (BYTE*)(resource.pData);
    for (int i = 0; i < 2000000; i++)
    {
        cout << (int)pb[i] << endl;
    }
    system("pause");
    return 0;
}

All that happens is 2000000 zeros get printed to the console. Is there something i'm missing or that I cant see?


Solution

  • First, call D3D11CreateDevice against specific adapter which output you are about to duplicate:

    BOOL init()
    {
        IDXGIFactory* lDxgiFactory;
        CHECKHR(CreateDXGIFactory1(__uuidof(IDXGIFactory), (void**)&lDxgiFactory))
        IDXGIAdapter* lDxgiAdapter;
        CHECKHR(lDxgiFactory->EnumAdapters(0, &lDxgiAdapter))
        lDxgiFactory->Release();
        CHECKHR(D3D11CreateDevice(lDxgiAdapter, D3D_DRIVER_TYPE_UNKNOWN, nullptr, 0, gFeatureLevels, gNumFeatureLevels, D3D11_SDK_VERSION, &lDevice, &FeatureLevel, &lImmediateContext))
        IDXGIDevice* lDxgiDevice;
        CHECKHR(lDevice->QueryInterface(IID_PPV_ARGS(&lDxgiDevice)))
        //IDXGIAdapter* lDxgiAdapter;
        //CHECKHR(lDxgiDevice->GetParent(__uuidof(IDXGIAdapter), (void**)&lDxgiAdapter))
        lDxgiDevice->Release();
        IDXGIOutput* lDxgiOutput;
        CHECKHR(lDxgiAdapter->EnumOutputs(0, &lDxgiOutput))
        lDxgiAdapter->Release();
        CHECKHR(lDxgiOutput->GetDesc(&OutputDesc))
        IDXGIOutput1* lDxgiOutput1;
        CHECKHR(lDxgiOutput->QueryInterface(IID_PPV_ARGS(&lDxgiOutput1)))
        lDxgiOutput->Release();
        CHECKHR(lDxgiOutput1->DuplicateOutput(lDevice, &lDeskDupl))
        // ...
    

    Then, your code is not quite accurate around AcquireNextFrame. You need to wait for actual frame and do ReleaseFrame in the loop while you are skipping.

    // ...
    while (true)
    {
        if (SUCCEEDED(lDeskDupl->AcquireNextFrame(INFINITE, &FrameInfo, &lDesktopResource)) && FrameInfo.LastPresentTime.QuadPart)
        {
            break;
        }
        lDeskDupl->ReleaseFrame();
    }
    // ...