Recupero dei dati dal backbuffer D3D11 utilizzando Map [duplicato]

Nov 13 2020

Ho dei problemi nell'utilizzo corretto di ID3D11DeviceContext :: Map ().

Ecco l'intera funzione finora:

 Color Graphics::GetPixel(int x, int y) const
{
    //Temp frame buffer descriptor;
    CD3D11_TEXTURE2D_DESC pFrameDesc;
    pFrameDesc.Width = WindowWidth;
    pFrameDesc.Height = WindowHeight;
    pFrameDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
    pFrameDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
    pFrameDesc.MipLevels = 1u;
    pFrameDesc.ArraySize = 1u;
    pFrameDesc.SampleDesc.Count = 1u;
    pFrameDesc.SampleDesc.Quality = 0u;
    pFrameDesc.Usage = D3D11_USAGE_DEFAULT;
    pFrameDesc.BindFlags = D3D11_BIND_UNORDERED_ACCESS;
    pFrameDesc.MiscFlags = 0u;

    // Create temp frame buffer(2d texture)
    wrl::ComPtr<ID3D11Texture2D>pFrame = nullptr;
    HRESULT hr = pDevice->CreateTexture2D(&pFrameDesc, nullptr, &pFrame);
    GFX_THROW_INFO(hr);

    hr = pSwapChain->GetBuffer(0, __uuidof(pFrame), &pFrame);
    GFX_THROW_INFO(hr);
    
    
    D3D11_MAPPED_SUBRESOURCE map;
    map.RowPitch = WindowWidth * 4;
    map.DepthPitch = WindowHeight * 4;
    
    //Throwing here, CPU access flags problem. 
    hr = pContext->Map(pFrame.Get(), 0u, D3D11_MAP_READ, 0u, &map);
    GFX_THROW_INFO(hr);

    pContext->Unmap(pFrame.Get(), 0u);
    


    return { 0.0f, 0.4f, 0.0f, 0.0f };
}

Ricevo un'eccezione in pContext-> Map ()

"Impossibile chiamare la mappa con l'accesso MAP_READ, perché la risorsa non è stata creata con il flag D3D11_CPU_ACCESS_READ."

Il che è fonte di confusione poiché la risorsa appena creata ha il flag D3D11_CPU_ACCESS_READ abilitato. Questo è vero anche se provo MAP_WRITE e cambio i flag di accesso in modo che corrispondano. Ho anche provato a fare ORing le bandiere insieme senza fortuna.

L'utilizzo finale di questa funzione è quello di essere in grado di creare un array lato CPU di oggetti Color (i colori sono attualmente una struttura di quattro float) che posso quindi inviare alla SDK NewTek NDI. Ma credo che sarebbe utile anche per salvare schermate, creare selettori di colori, ecc.

Tutte le idee sono benvenute!

Risposte

5 ChuckWalbourn Nov 14 2020 at 08:33

Il tuo codice non usa la trama creata con D3D11_CPU_ACCESS_READ.

Crea una texture:

// Create temp frame buffer(2d texture)
wrl::ComPtr<ID3D11Texture2D>pFrame = nullptr;
HRESULT hr = pDevice->CreateTexture2D(&pFrameDesc, nullptr, &pFrame);
GFX_THROW_INFO(hr);

Quindi rilascia quella texture e prende un riferimento a una dalla swapchain (che non è mai impostata per CPU READ):

hr = pSwapChain->GetBuffer(0, __uuidof(pFrame), &pFrame);
GFX_THROW_INFO(hr);

Quindi provi a mappare il buffer swapchain:

hr = pContext->Map(pFrame.Get(), 0u, D3D11_MAP_READ, 0u, &map);
GFX_THROW_INFO(hr);
1 PandaPajama Nov 14 2020 at 15:08

La risposta di Chuck Walbourn è corretta, ma per darti un po 'più di contesto, la trama con cui stai creando pDevice->CreateTexture2Dnon è la stessa di quella che stai ottenendo pSwapChain->GetBuffer.

Con pDevice->CreateTexture2D, stai creando una nuova texture in memoria (che è effettivamente impostata per essere letta dalla CPU). La nuova texture viene memorizzata in pFrame.

Quindi, con pSwapChain->GetBuffer, si ottiene il back buffer esistente dalla catena di scambio (che non è stata creata con l'accesso in lettura della CPU). Questo viene memorizzato pFrame, scartando effettivamente il valore precedente.

Hai due scelte qui (non tocco D3D11 da anni. Questo si basa sulla mia conoscenza di D3D12, Vulkan e console current-gen)

L'opzione A consiste nell'abbandonare la nuova texture in memoria e impostare invece il back buffer nella catena di scambio per l'accesso alla CPU. Non ti consiglio di farlo, poiché le trame destinate alla rilettura della CPU non funzioneranno necessariamente come quelle create senza accesso alla CPU.

Inoltre, mentre hai mappato la memoria, la GPU dovrà svuotare il contenuto della texture e interrompere tutte le operazioni della GPU su di essa finché non rimuovi la mappatura della memoria. A seconda del punto del processo di disegno che stai facendo, questa operazione sarà un enorme collo di bottiglia.

A proposito, non sono nemmeno sicuro che Direct3D ti consentirà di creare una catena di scambio con accesso in lettura alla CPU, quindi probabilmente non sarai nemmeno in grado di provarlo.

L'opzione B consiste nell'eseguire un'operazione di disegno con un quad con texture a schermo intero, dove la texture è il buffer posteriore e la destinazione di rendering è la texture leggibile dalla CPU. Questo è generalmente il metodo preferito, poiché risolve tutti i problemi che l'opzione A aveva, a scapito di un'operazione di disegno a schermo intero.

Tieni presente che è possibile (non ricordo con D3D11) che non sarai in grado di impostare il back buffer come texture per il disegno. In tal caso, invece di effettuare le chiamate di disegno del gioco direttamente al buffer posteriore, potrebbe essere necessario creare ancora un altro target di rendering, disegnare tutto su di esso e quindi fare due copie: una nel buffer di ritorno della catena di scambio e un'altra per la tua texture leggibile dalla CPU.

Questo metodo è un po 'più complesso, ma è garantito che funzioni su D3D12, così come molto probabilmente su ogni altra piattaforma moderna.