Samstag, Juni 6, 2020

DirectX-11-framebuffer-capture (C++, keine Win32-oder D3DX)

Ich würde gerne erfassen den Inhalt meiner front-oder back-buffer Verwendung von DirectX 11 in ein array von bytes, die ich dann als textur oder als Quelle für eine Datei erstellen. Ich habe eine swap-Kette-setup, viel Rendern geschieht und den folgenden code so weit – die ich stellen Sie sicher, rufen Sie nach dem Aufruf zu Präsentieren.

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 =
    {
        width, height, 1, 1, DXGI_FORMAT_R8G8B8A8_UNORM,
        { 1, 0 }, //DXGI_SAMPLE_DESC
        D3D11_USAGE_STAGING,
        0, D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE, 0
    };

    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, 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 );
}

Es gibt mir immer schwarz, mit null alphas.

Ich würde normalerweise haben die Möglichkeit, GDI-interop verwenden BitBlt kopieren einer bitmap aus dem swap-Kette – jedoch habe ich Einschränkungen was bedeutet, dies ist nicht eine gültige Lösung.

Auch die D3DX-Bibliothek enthält Funktionen für die tun, bits dies ist auch aus der Frage.

InformationsquelleAutor jheriko | 2012-05-16

3 Kommentare

  1. 9

    So. Ein wenig mehr Experimentierfreude offenbart sich das „problem“. Durch getting die Beschreibung der framebuffer textur und verwenden, die als Grundlage zum erstellen der neuen textur das problem war gelöst…

    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 );
    }
    • Dieser code funktioniert nicht gut für mich. Die erfassten Bild-Daten scheint nicht ausgerichtet(ich bin nicht beenden sicher), so dass das Bild ist nicht das, was es werden sollte. Wenn jemand will nur zu erfassen, einen Rahmen, und speichern Sie es als Datei, mit DirectXTK oder DirectXTex wäre eine bessere Wahl. github.com/microsoft/DirectXTK github.com/Microsoft/DirectXTex
  2. 2

    Swap-Kette-Puffer können einfach gespeichert werden mit D3D11 wie unten gezeigt.

    1. Erstellen Sie eine Texture2D genauso wie die swap-Kette back-buffer, die Sie versuchen, zu speichern
    2. Nennen CopyResource auf den device context zu kopieren vom Puffer zurück, um die neu erstellte textur
    3. Nennen D3DX11SaveTextureToFile(…) mit dem Datei-Namen

    gekünstelt 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);
    • das problem ist, dass d3dx11 ist/war nicht für winrt-apps an der Zeit zu schreiben – zitiere mich selbst: „Auch die D3DX-Bibliothek enthält Funktionen für die tun, bits dies ist auch aus der Frage.“
  3. 2

    Kopieren Sie die richtige Größe, verwenden Sie den folgenden code ein.

    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 );
    }
    • Bitte Bearbeiten mit mehr Informationen. Code-und nur „versuchen, diese“ Antworten sind entmutigt, denn Sie enthalten keine durchsuchbaren Inhalten, und nicht erklären, warum jemand sollte „try this“. Wir bemühen uns, hier zu sein, eine Ressource für wissen.

Kostenlose Online-Tests