DirectX 11 framebuffer capture (C++, no Win32 or D3DX)

前端 未结 3 1848
一个人的身影
一个人的身影 2020-12-08 12:30

I would like to capture the contents of my front or back buffer using DirectX 11 into an array of bytes which I can then use as a texture or as a source for creating a file.

相关标签:
3条回答
  • 2020-12-08 12:48

    To copy the correct size, use the code below.

    ID3D11Texture2D* pSurface;
    HRESULT hr = m_swapChain->GetBuffer( 0, __uuidof( ID3D11Texture2D ), reinterpret_cast< void** >( &pSurface ) );
    if( pSurface )
    {
        const int width = static_cast<int>(m_window->Bounds.Width * m_dpi / 96.0f);
        const int height = static_cast<int>(m_window->Bounds.Height * m_dpi / 96.0f);
        unsigned int size = width * height;
        if( m_captureData )
        {
            freeFramebufferData( m_captureData );
        }
        m_captureData = new unsigned char[ width * height * 4 ];
    
        ID3D11Texture2D* pNewTexture = NULL;
    
        D3D11_TEXTURE2D_DESC description;
        pSurface->GetDesc( &description );
        description.BindFlags = 0;
        description.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE;
        description.Usage = D3D11_USAGE_STAGING;
    
        HRESULT hr = m_d3dDevice->CreateTexture2D( &description, NULL, &pNewTexture );
        if( pNewTexture )
        {
            m_d3dContext->CopyResource( pNewTexture, pSurface );
            D3D11_MAPPED_SUBRESOURCE resource;
            unsigned int subresource = D3D11CalcSubresource( 0, 0, 0 );
            HRESULT hr = m_d3dContext->Map( pNewTexture, subresource, D3D11_MAP_READ_WRITE, 0, &resource );
            //resource.pData; // TEXTURE DATA IS HERE
    
            const int pitch = width << 2;
            const unsigned char* source = static_cast< const unsigned char* >( resource.pData );
            unsigned char* dest = m_captureData;
            for( int i = 0; i < height; ++i )
            {
                memcpy( dest, source, width * 4 );
                source += resource.RowPitch; // <------
                dest += pitch;
            }
    
            m_captureSize = size;
            m_captureWidth = width;
            m_captureHeight = height;
    
            return;
        }
    
        freeFramebufferData( m_captureData );
    }
    
    0 讨论(0)
  • 2020-12-08 12:51

    Swap chain buffers can be easily saved with D3D11 as shown below.

    1. Create a Texture2D as same as the swap chain's back buffer you are trying to save
    2. Call CopyResource on the device context to copy from back buffer to the newly created texture
    3. Call D3DX11SaveTextureToFile(...) with file name

    contrived code fragment:

    ID3D11Texture2D* pBuffer;
    
    swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&pBuffer);
    
    if(texture_to_save == NULL)
    {
        D3D11_TEXTURE2D_DESC td;
        pBuffer->GetDesc(&td);
        device->CreateTexture2D(&td, NULL, &texture_to_save);
    }
    
    deviceContext->CopyResource(texture_to_save, pBuffer);
    
    D3DX11SaveTextureToFile(deviceContext,texture_to_save,D3DX11_IFF_PNG,filename);
    
    0 讨论(0)
  • 2020-12-08 12:59

    So. A little more experimentation revealed the "problem". By getting the description of the framebuffer texture and using that as the basis to create the new texture the problem was resolved...

    ID3D11Texture2D* pSurface;
    HRESULT hr = m_swapChain->GetBuffer( 0, __uuidof( ID3D11Texture2D ), reinterpret_cast< void** >( &pSurface ) );
    if( pSurface )
    {
        const int width = static_cast<int>(m_window->Bounds.Width * m_dpi / 96.0f);
        const int height = static_cast<int>(m_window->Bounds.Height * m_dpi / 96.0f);
        unsigned int size = width * height;
        if( m_captureData )
        {
            freeFramebufferData( m_captureData );
        }
        m_captureData = new unsigned char[ width * height * 4 ];
    
        ID3D11Texture2D* pNewTexture = NULL;
    
        D3D11_TEXTURE2D_DESC description;
        pSurface->GetDesc( &description );
        description.BindFlags = 0;
        description.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE;
        description.Usage = D3D11_USAGE_STAGING;
    
        HRESULT hr = m_d3dDevice->CreateTexture2D( &description, NULL, &pNewTexture );
        if( pNewTexture )
        {
            m_d3dContext->CopyResource( pNewTexture, pSurface );
            D3D11_MAPPED_SUBRESOURCE resource;
            unsigned int subresource = D3D11CalcSubresource( 0, 0, 0 );
            HRESULT hr = m_d3dContext->Map( pNewTexture, subresource, D3D11_MAP_READ_WRITE, 0, &resource );
            //resource.pData; // TEXTURE DATA IS HERE
    
            const int pitch = width << 2;
            const unsigned char* source = static_cast< const unsigned char* >( resource.pData );
            unsigned char* dest = m_captureData;
            for( int i = 0; i < height; ++i )
            {
                memcpy( dest, source, width * 4 );
                source += pitch;
                dest += pitch;
            }
    
            m_captureSize = size;
            m_captureWidth = width;
            m_captureHeight = height;
    
            return;
        }
    
        freeFramebufferData( m_captureData );
    }
    
    0 讨论(0)
提交回复
热议问题