Search code examples
c++directxdirectx-11

How do I take a screenshot with DirectX 11?


I'm writing a plugin for a DX11 game and I need to take a screenshot and save it to a file. I found some code snippets that use the function D3DX11SaveTextureToFile but I just couldn't get it to work. Here's what I have so far: (error checking and unrelated code removed for simplicity)

void screenshot(IDXGISwapChain *pSwapChain)
{
    ID3D11Device *pDevice;
    pSwapChain->GetDevice(__uuidof(ID3D11Device), reinterpret_cast<void **>(&pDevice));

    ID3D11DeviceContext *pContext;
    pDevice->GetImmediateContext(&pContext);

    ID3D11Texture2D *pBackBuffer;
    pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), reinterpret_cast<void **>(&pBackBuffer));

    D3D11_TEXTURE2D_DESC txtDesc;
    pBackBuffer->GetDesc(&txtDesc);
    txtDesc.Usage = D3D11_USAGE_STAGING;
    txtDesc.BindFlags = 0;
    txtDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
    // txtDesc.Format == DXGI_FORMAT_B8G8R8A8_UNORM

    ID3D11Texture2D *pBackBufferStaging;
    pDevice->CreateTexture2D(&txtDesc, nullptr, &pBackBufferStaging);
    pContext->CopyResource(pBackBufferStaging, pBackBuffer);

    HRESULT hr = D3DX11SaveTextureToFileA(pContext, pBackBufferStaging, D3DX11_IFF_PNG, "E:\\screenshot.png");
    // hr == E_FAIL

    pBackBufferStaging->Release();
    pBackBuffer->Release();
}

I got a E_FAIL error and after some research it seems that D3DX11SaveTextureToFile doesn't support the texture format DXGI_FORMAT_B8G8R8A8_UNORM, which is used by the game I assume. If I manually set the format to something like DXGI_FORMAT_R8G8B8A8_UNORM the function will succeed but I will just get a black image. I think CopyResource requires that the source and destination have the same format.

I'm aware that there's the DirectXTK library but I'm stuck with VS2013 and DirectXTK doesn't seem to support it if I read its readme correctly.

EDIT
DirectXTK/DirectXTex worked flawlessly.


Solution

  • Using the ScreenGrab module from the DirectX Tool Kit is your best bet. It can write either DDS files or various formats supported by WIC, JPG included:

    using namespace DirectX;
    using namespace Microsoft::WRL;
    
    ComPtr<ID3D11Texture2D> backBuffer;
    HRESULT hr = swapChain->GetBuffer( 0,
        __uuidof( ID3D11Texture2D ),
       reinterpret_cast<LPVOID*>( backBuffer.GetAddressOf() ) );
    if ( SUCCEEDED(hr) )
    {
        hr = SaveWICTextureToFile( immContext.Get(), backBuffer.Get(),
                    GUID_ContainerFormatJpeg, L"SCREENSHOT.JPG" );
    }
    DX::ThrowIfFailed(hr);
    

    I recently dropped support for Visual Studio 2013 from my GitHub projects--see this blog post for some details. If you can't upgrade, you can use a slightly older April 2018 release which does support VS 2013.

    D3DX9, D3DX10, and D3DX11 are all deprecated along with the DirectX SDK generally per MSDN.