Récupération des données du backbuffer D3D11 en utilisant Map [duplicate]
J'ai du mal à utiliser correctement ID3D11DeviceContext :: Map ().
Voici toute la fonction jusqu'à présent:
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 };
}
Je reçois une exception lancée à pContext-> Map ()
"La carte ne peut pas être appelée avec l'accès MAP_READ, car la ressource n'a pas été créée avec l'indicateur D3D11_CPU_ACCESS_READ."
Ce qui est déroutant car la ressource nouvellement créée a l'indicateur D3D11_CPU_ACCESS_READ activé. Ceci est également vrai si j'essaye MAP_WRITE et change les indicateurs d'accès pour qu'ils correspondent. J'ai également essayé OU les drapeaux ensemble sans succès.
L'utilisation ultime de cette fonction est de pouvoir créer un tableau côté CPU d'objets Color (les couleurs sont actuellement une structure de quatre flottants) que je peux ensuite envoyer sur le sdk NewTek NDI. Mais je pense que cela serait également utile pour enregistrer des captures d'écran, créer des sélecteurs de couleurs, etc.
Toutes les idées sont les bienvenues!
Réponses
Votre code n'utilise pas votre texture créée avec D3D11_CPU_ACCESS_READ.
Cela crée une texture:
// Create temp frame buffer(2d texture)
wrl::ComPtr<ID3D11Texture2D>pFrame = nullptr;
HRESULT hr = pDevice->CreateTexture2D(&pFrameDesc, nullptr, &pFrame);
GFX_THROW_INFO(hr);
Ensuite, il libère cette texture et prend une référence à une de la swapchain (qui n'est jamais définie pour CPU READ):
hr = pSwapChain->GetBuffer(0, __uuidof(pFrame), &pFrame);
GFX_THROW_INFO(hr);
Ensuite, vous essayez de mapper le tampon de la swapchain:
hr = pContext->Map(pFrame.Get(), 0u, D3D11_MAP_READ, 0u, &map);
GFX_THROW_INFO(hr);
La réponse de Chuck Walbourn est correcte, mais pour vous donner un peu plus de contexte, la texture avec laquelle vous créez pDevice->CreateTexture2Dn'est pas la même que celle que vous obtenez pSwapChain->GetBuffer.
Avec pDevice->CreateTexture2D, vous créez une toute nouvelle texture en mémoire (qui est en effet configurée pour être lue depuis le CPU). La nouvelle texture est stockée dans pFrame.
Ensuite, avec pSwapChain->GetBuffer, vous obtenez le tampon arrière existant de la chaîne de swap (qui n'a pas été créé avec l'accès en lecture du processeur). Ceci est stocké dans pFrame, supprimant effectivement la valeur précédente.
Vous avez deux choix ici (je n'ai pas touché au D3D11 depuis des années. Ceci est basé sur ma connaissance de la console D3D12, Vulkan et la génération actuelle)
L'option A consiste à abandonner la nouvelle texture en mémoire et à définir à la place le tampon arrière dans la chaîne d'échange pour l'accès au processeur. Je ne vous recommande pas de le faire, car les textures destinées à la lecture du processeur ne fonctionneront pas nécessairement aussi bien que celles créées sans accès au processeur.
De plus, tant que vous avez mappé la mémoire, le GPU devra vider le contenu de la texture et cesser toutes les opérations GPU sur celle-ci jusqu'à ce que vous démappiez la mémoire. Selon l'endroit de votre processus de dessin, cette opération sera un énorme goulot d'étranglement.
Au fait, je ne suis même pas sûr que Direct3D vous permette de créer une chaîne d'échange avec accès en lecture au processeur, vous ne pourrez donc probablement même pas l'essayer.
L'option B consiste à effectuer une opération de dessin avec un quad texturé plein écran, où la texture est le tampon arrière et la cible de rendu est votre texture lisible par le processeur. Il s'agit généralement de la méthode préférée, car elle résout tous les problèmes rencontrés par l'option A, au détriment d'une opération de dessin en plein écran.
Gardez à l'esprit qu'il est possible (je ne me souviens pas avec D3D11) que vous ne puissiez pas définir le tampon arrière comme texture pour le dessin. Dans ce cas, au lieu de faire des appels de tirage de votre jeu directement dans le tampon arrière, vous devrez peut-être créer une autre cible de rendu, dessiner tout dessus, puis faire deux copies: une dans le tampon arrière de la chaîne d'échange, et une autre pour votre texture lisible par le processeur.
Cette méthode est un peu plus complexe, mais elle est garantie de fonctionner sur D3D12, ainsi que très probablement sur toutes les autres plates-formes modernes.