Skip to content

ScreenGrab

Chuck Walbourn edited this page Nov 5, 2022 · 28 revisions
DirectXTK

A Direct3D 12 2D texture save routine for generating a "screenshot" from a render target texture. There is a function that will dump the 2D texture to a .DDS file, and another that will write using WIC (BMP, JPEG, PNG, TIFF, GIF, JPEG-XR / HD Photo, or other WIC supported file container).

These writers do not support array textures, 1D textures, 3D volume textures, cubemaps, or cubemap arrays. Mipmaps are also ignored. For those scenarios, use the DirectXTex library functionality. The ScreenGrab module is really designed to make a quick and light-weight solution for capturing screenshots at runtime.

MSAA textures are resolved before being written.

A standalone version is included in DirectXTex for Direct3D 9, Direct3D 11, and Direct3D 12. Be sure to add both the h and cpp file to your project.

Header

#include <wincodec.h> // Optional

#include <ScreenGrab.h>

You only need to directly include the <wincodec.h> header if you are providing an explicit guidContainerFormat or targetFormat. Note you should link with uuid.lib as well.

Initialization

The library assumes that the client code will have already called CoInitialize, CoInitializeEx, or Windows::Foundation::Initialize as needed by the application before calling any Windows Imaging Component functionality.

For a Universal Windows Platform (UWP) app using /ZW, the Windows Runtime and COM is initialized by the C/C++ Run-Time. For C++/WinRT applications, this is done by calling winrt::init_apartment();.

For a classic Windows desktop application you have to do this explicitly:

#if (_WIN32_WINNT >= 0x0A00 /*_WIN32_WINNT_WIN10*/)
    Microsoft::WRL::Wrappers::RoInitializeWrapper initialize(RO_INIT_MULTITHREADED);
    if (FAILED(initialize))
        // error
#else
    HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
    if (FAILED(hr))
        // error
#endif

Functions

SaveDDSTextureToFile

Saves a texture to a DDS file on disk. It performs no format conversions, but will try to favor writing legacy .DDS files when possible for improved tool support.

HRESULT SaveDDSTextureToFile(
    ID3D12CommandQueue* pCommandQueue,
    ID3D12Resource* pSource,
    const wchar_t* fileName,
    D3D12_RESOURCE_STATES beforeState = D3D12_RESOURCE_STATE_RENDER_TARGET,
    D3D12_RESOURCE_STATES afterState = D3D12_RESOURCE_STATE_RENDER_TARGET);

SaveWICTextureToFile

Saves a texture to a WIC-supported bitmap file on disk. The caller provides the desired WIC container format via guidContainerFormat and can optionally specify a desired WIC pixel format via targetFormat (which will result in E_FAIL if the requested pixel format is not supported by the WIC codec). If no WIC pixel format GUID is provided as the targetFormat parameter, it will default to a non-alpha format since 'screenshots' usually ignore the alpha channel in render targets.

HRESULT SaveWICTextureToFile(
    ID3D12CommandQueue* pCommandQ,
    ID3D12Resource* pSource,
    REFGUID guidContainerFormat,
    const wchar_t* fileName,
    D3D12_RESOURCE_STATES beforeState = D3D12_RESOURCE_STATE_RENDER_TARGET,
    D3D12_RESOURCE_STATES afterState = D3D12_RESOURCE_STATE_RENDER_TARGET,
    const GUID* targetFormat = nullptr,
    std::function<void(IPropertyBag2*)> setCustomProps = nullptr,
    bool forceSRGB = false);

This function can only write images using the subset of DXGI_FORMAT values that map directly to a WIC native pixel format.

DXGI_FORMAT WIC Format
DXGI_FORMAT_R32G32B32A32_FLOAT GUID_WICPixelFormat128bppRGBAFloat
DXGI_FORMAT_R16G16B16A16_FLOAT GUID_WICPixelFormat64bppRGBAHalf
DXGI_FORMAT_R16G16B16A16_UNORM GUID_WICPixelFormat64bppRGBA
DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM GUID_WICPixelFormat32bppRGBA1010102XR
DXGI_FORMAT_R10G10B10A2_UNORM GUID_WICPixelFormat32bppRGBA1010102
DXGI_FORMAT_B5G5R5A1_UNORM GUID_WICPixelFormat16bppBGRA5551
DXGI_FORMAT_B5G6R5_UNORM GUID_WICPixelFormat16bppBGR565
DXGI_FORMAT_R32_FLOAT GUID_WICPixelFormat32bppGrayFloat
DXGI_FORMAT_R16_FLOAT GUID_WICPixelFormat16bppGrayHalf
DXGI_FORMAT_R16_UNORM GUID_WICPixelFormat16bppGray
DXGI_FORMAT_R8_UNORM GUID_WICPixelFormat8bppGray
DXGI_FORMAT_A8_UNORM GUID_WICPixelFormat8bppAlpha
DXGI_FORMAT_R8G8B8A8_UNORM
DXGI_FORMAT_R8G8B8A8_UNORM_SRGB
GUID_WICPixelFormat32bppRGBA
DXGI_FORMAT_B8G8R8A8_UNORM
DXGI_FORMAT_B8G8R8A8_UNORM_SRGB
GUID_WICPixelFormat32bppBGRA
DXGI_FORMAT_B8G8R8X8_UNORM
DXGI_FORMAT_B8G8R8X8_UNORM_SRGB
GUID_WICPixelFormat32bppBGR

Examples

This example saves a DDS screenshot and a JPEG screenshot given a swapchain backbuffer.

m_screenshot = m_deviceResources->GetRenderTarget();

m_deviceResources->Present();

if (m_screenshot)
{
    DX::ThrowIfFailed(
        SaveDDSTextureToFile(m_deviceResources->GetCommandQueue(), m_screenshot.Get(),
            L"screenshot.dds",
            D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_PRESENT)
        );

    DX::ThrowIfFailed(
        SaveWICTextureToFile(m_deviceResources->GetCommandQueue(), m_screenshot.Get(),
            GUID_ContainerFormatJpeg, L"screenshot.jpg",
            D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_PRESENT)
        );
}

Here is an example of explicitly writing a screenshot as a 16-bit (5:6:5) BMP.

...
DX::ThrowIfFailed(
    SaveWICTextureToFile(m_deviceResources->GetCommandQueue(), m_screenshot.Get(),
        GUID_ContainerFormatBmp, L"screenshot.bmp",
        D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_PRESENT,
        &GUID_WICPixelFormat16bppBGR565)
    );

When writing WIC files, you can also provide a callback for setting specific encoding options.

...
DX::ThrowIfFailed(
    SaveWICTextureToFile(m_deviceResources->GetCommandQueue(), m_screenshot.Get(),
        GUID_ContainerFormatTiff, L"screenshot.tif",
        D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_PRESENT,
        nullptr,
        [&](IPropertyBag2* props)
        {
            PROPBAG2 options[2] = { 0, 0 };
            options[0].pstrName = const_cast<wchar_t*>(L"CompressionQuality");
            options[1].pstrName = const_cast<wchar_t*>(L"TiffCompressionMethod");

            VARIANT varValues[2];
            varValues[0].vt = VT_R4;
            varValues[0].fltVal = 0.75f;
            varValues[1].vt = VT_UI1;
            varValues[1].bVal = WICTiffCompressionNone;

            std::ignore = props->Write( 2, options, varValues );
        })
    );
}

sRGB vs. Linear Color Space

With the modern presentation flip models, you don't actually use DXGI_FORMAT_*_SRGB with the backbuffer format. You only use it with the render target view if doing gamma-correct rendering. Therefore, when writing PNG screenshots directly from the swapchain backbuffer you can end up with a washed-out image. You solve this by using the optional parameter forceSRGB to influence the image metadata:

DX::ThrowIfFailed(
    SaveWICTextureToFile(m_deviceResources->GetCommandQueue(), m_screenshot.Get(),
    GUID_ContainerFormatJpeg, L"screenshot.png",
    D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_PRESENT,
    nullptr, nullptr, true)
    );

Remarks

The pSource resource must be put into the COPY_SOURCE state before calling these functions, which is why it takes a before and after state.

This function is blocking as it waits for a fence for the copy to finish. Be sure that you've submitted the command list for drawing the render target to the same command-queue that you use for the screengrab.

Does not support capture of planar formats such as video textures or depth/stencil formats.

Release Notes

  • JPEG-XR / HD Photo supports nearly all WIC pixel formats including floating-point for both encoding and decoding.

  • TIFF can contain floating-point data (128bpp or 96bpp), but the WIC built-in codec can only decode such images. It always converts floating-point data to unorm when encoding.

  • Paletted WIC formats are not supported for writing by the SaveWICTextureToFile function.

  • When writing PNG files, if the input image format is DXGI_FORMAT_*_SRGB, then the resulting file will have the sRGB rendering intent chunk. Otherwise, it will not have the sRGB chunk and instead will have an explicit gAMA chunk of 1.0.

WIC2

Since WIC2 is available on Windows 10, it is always used by DirectX Tool Kit for DirectX 12.

See Windows Imaging Component and Windows 8

Windows Store apps

For information on using Save*TextureToFile from a Windows Store app or a Universal Windows Platform (UWP) app, see here

For Use

  • Universal Windows Platform apps
  • Windows desktop apps
  • Windows 11
  • Windows 10
  • Xbox One
  • Xbox Series X|S

Architecture

  • x86
  • x64
  • ARM64

For Development

  • Visual Studio 2022
  • Visual Studio 2019 (16.11)
  • clang/LLVM v12 - v18
  • MinGW 12.2, 13.2
  • CMake 3.20

Related Projects

DirectX Tool Kit for DirectX 11

DirectXMesh

DirectXTex

DirectXMath

Tools

Test Suite

Model Viewer

Content Exporter

DxCapsViewer

See also

DirectX Landing Page

Clone this wiki locally