1

When I want to use DXGI Duplication API to take screenshots, the deviceContext->map() always failed. It returns E_INVALIDAGR (one or more arguments are invalid). However, I don't think there are any arguments invalid. I don't know how to solve this problem. Here's my code. (all the initialization are successes, including device, deviceContext, dxgiOutputDuplication).

/*
this function take a screenshot and save the bmp image of the screenshot to the data pointer.
@param BYTE* data: (out) The pointer to a BYTE array where the bmp image will be saved to.
@return ULARGE_INTEGER: the size of bmp image.
*/
ULARGE_INTEGER GetFrame(BYTE* data) {

    IDXGIResource* desktopResource = nullptr;
    DXGI_OUTDUPL_FRAME_INFO FrameInfo{};
    ID3D11Texture2D* acquiredDesktopImage = nullptr;
    
    //Get new frame
    HRESULT hr = 0;
    do {
        hr = dxgiOutputDuplication->AcquireNextFrame(1000, &FrameInfo, &desktopResource);
    } while (hr != S_OK);
    

    hr = desktopResource->QueryInterface(__uuidof(ID3D11Texture2D), reinterpret_cast<void**>(&acquiredDesktopImage));
    if (hr != S_OK) {
    printf("failed to query interface\n");
    }
    D3D11_MAPPED_SUBRESOURCE* resource = new D3D11_MAPPED_SUBRESOURCE();

    ID3D11Texture2D* copiedImage = nullptr;

    D3D11_TEXTURE2D_DESC desc{};

    acquiredDesktopImage->GetDesc(&desc);

    desc.Usage = D3D11_USAGE_DEFAULT;
    desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
    desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;

    hr = device->CreateTexture2D(&desc, NULL, &copiedImage);
    if (hr != S_OK) {
    printf("failed to create texture\n");
    }
    deviceContext->CopyResource(copiedImage, acquiredDesktopImage);
    
    hr = deviceContext->Map(copiedImage, 0, D3D11_MAP_READ, NULL, resource);  
    // always failed in the upper line.
    
    if (hr != S_OK) {
    printf("failed to map\n");
    }

    
    ULARGE_INTEGER length = encodeData(dxgiOutDuplDesc.ModeDesc.Width, dxgiOutDuplDesc.ModeDesc.Height, resource->RowPitch, resource->pData, data);
    // This function is to encode the data from rgb matrix to bmp format. 
    // Here is the description of this function.
    /*
    encode RGB matrix to bmp format and save it into data
    @param int width: (in) matrix width
    @param int height: (in) matrix height
    @param UINT stride: (in) the size of a line of matrix
    @param void* pixels: (in) data source pointer
    @param BYTE* data: (out) data result pointer
    @return ULARGE_INTEGER: data result length
    */

    desktopResource->Release();
    acquiredDesktopImage->Release();
    delete resource;
    return length;
}

here is the initialization function.


BOOL initDevice() {
    IDXGIDevice*            dxgiDevice = nullptr;
    IDXGIAdapter*           dxgiAdapter = nullptr;
    IDXGIOutput*            dxgiOutput = nullptr;
    IDXGIOutput1*           dxgiOutput1 = nullptr;

    D3D_FEATURE_LEVEL feature_levels[] = {
        D3D_FEATURE_LEVEL_11_0
    };

    CHECK_RESULT(D3D11CreateDevice(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, D3D11_CREATE_DEVICE_DEBUG, feature_levels, ARRAYSIZE(feature_levels), D3D11_SDK_VERSION, &device, NULL, &deviceContext));
    printf("successful init create d3d11 device\n");

    // Get DXGI device
    CHECK_RESULT(device->QueryInterface(__uuidof(IDXGIDevice), reinterpret_cast<void**>(&dxgiDevice)));

    // Get DXGI adapter
    CHECK_RESULT(dxgiDevice->GetParent(__uuidof(IDXGIAdapter), reinterpret_cast<void**>(&dxgiAdapter)));


    // Get output
    CHECK_RESULT(dxgiAdapter->EnumOutputs(0, &dxgiOutput));


    CHECK_RESULT(dxgiOutput->GetDesc(&dxgiOutputDesc));

    // QI for Output 1
    CHECK_RESULT(dxgiOutput->QueryInterface(__uuidof(dxgiOutput1), reinterpret_cast<void**>(&dxgiOutput1)));
    

    // Create desktop duplication
    CHECK_RESULT(dxgiOutput1->DuplicateOutput(device, &dxgiOutputDuplication));

    dxgiOutputDuplication->GetDesc(&dxgiOutDuplDesc);

    dxgiDevice->Release();
    dxgiDevice = nullptr;
    dxgiAdapter->Release();
    dxgiAdapter = nullptr;
    dxgiOutput->Release();
    dxgiOutput = nullptr;
    dxgiOutput1->Release();
    dxgiOutput1 = nullptr;
    return true;
}

This is the macro that I used.

#define CHECK_RESULT(func) if ((func) != S_OK) return false

I tried to change almost all of the parameters of desc(D3D11_TEXTURE2D_DESC), but none of them can succeed. Also, I have searched about this question, and the code on their articles are as the same as mine, so I am really confused about what happened. Could anyone help me with that? I'll appreciate very much for your helping.

Mgetz
  • 5,108
  • 2
  • 33
  • 51
ton197
  • 11
  • 2
  • Note: you really shouldn't use raw `new` and `delete` here, they aren't necessary. C++ isn't Java. Also consider using either `wrl::com_ptr` or [`wil::com_ptr`](https://github.com/microsoft/wil) to have RAII management of the interfaces so you're not manually calling `Release` – Mgetz Mar 23 '22 at 12:29
  • 3
    Have you looked at the [DXGI duplication sample](https://github.com/microsoft/Windows-classic-samples/tree/main/Samples/DXGIDesktopDuplication)? – Mgetz Mar 23 '22 at 12:33
  • 1
    D3D11_BIND_SHADER_RESOURCE is for binding with a shader (CreateShaderResourceView). You can't read this from CPU. What are you trying to do? – Simon Mourier Mar 23 '22 at 13:35
  • 1
    FYI - Don't use ``!= S_OK`` for ``HRESULT`` success or failure. Use ``SUCCEEDED`` and ``FAILED`` macros. Also, ``NULL`` doesn't make sense in your ``Map`` line. You want `0` since it's not a pointer--this is a good reason to use C++11's ``nullptr`` instead of legacy ``NULL`` since the compiler can tell you it's not a pointer. – Chuck Walbourn Mar 23 '22 at 20:18

1 Answers1

1

Your 'copiedImage' texture is not valid for mapping:

desc.Usage = D3D11_USAGE_DEFAULT;
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;

should be something like:

desc.Usage = D3D11_USAGE_STAGING;
desc.BindFlags = 0;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;

Take a look at this ScreenGrab code on GitHub.

You also didn't release your copiedImage texture. You should consider using a COM smart-pointer like Microsoft::WRL::ComPtr to avoid resource leaks.

Note that with DirectX 11.1 or better, there is an optional hardware feature that supports calling Map on D3D11_USAGE_DEFAULT resources, but it only works if D3D11_FEATURE_DATA_D3D11_OPTIONS1.MapOnDefaultBuffers is reported as TRUE by the driver -and- you are using a ID3D11Buffer. This never works for textures.

Chuck Walbourn
  • 38,259
  • 2
  • 58
  • 81