Abrufen von Daten aus dem Backbuffer D3D11 mithilfe von Map [Duplikat]
Ich habe Probleme, ID3D11DeviceContext :: Map () richtig zu verwenden.
Hier ist die ganze Funktion bisher:
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 };
}
Ich erhalte eine Ausnahme bei pContext-> Map ()
"Map kann nicht mit MAP_READ-Zugriff aufgerufen werden, da die Ressource nicht mit dem Flag D3D11_CPU_ACCESS_READ erstellt wurde."
Dies ist verwirrend, da für die neu erstellte Ressource das Flag D3D11_CPU_ACCESS_READ aktiviert ist. Dies gilt auch, wenn ich MAP_WRITE versuche und die Zugriffsflags entsprechend ändere. Ich habe auch versucht, die Flaggen ohne Glück zusammen zu ORing.
Die ultimative Verwendung dieser Funktion besteht darin, ein CPU-seitiges Array von Farbobjekten erstellen zu können (Farben sind derzeit eine Struktur aus vier Floats), die ich dann an das NewTek NDI SDK senden kann. Aber ich denke, es wäre auch nützlich, um Screenshots zu speichern, Farbwähler zu erstellen usw.
Irgendwelche Ideen willkommen!
Antworten
Ihr Code verwendet Ihre erstellte Textur nicht mit D3D11_CPU_ACCESS_READ.
Es entsteht eine Textur:
// Create temp frame buffer(2d texture)
wrl::ComPtr<ID3D11Texture2D>pFrame = nullptr;
HRESULT hr = pDevice->CreateTexture2D(&pFrameDesc, nullptr, &pFrame);
GFX_THROW_INFO(hr);
Dann gibt es diese Textur frei und nimmt einen Verweis auf eine aus der Swapchain (die niemals für CPU READ eingestellt ist):
hr = pSwapChain->GetBuffer(0, __uuidof(pFrame), &pFrame);
GFX_THROW_INFO(hr);
Dann versuchen Sie, den Swapchain-Puffer zuzuordnen:
hr = pContext->Map(pFrame.Get(), 0u, D3D11_MAP_READ, 0u, &map);
GFX_THROW_INFO(hr);
Die Antwort von Chuck Walbourn ist richtig, aber um Ihnen ein bisschen mehr Kontext zu geben, ist die Textur, mit der Sie erstellen, pDevice->CreateTexture2Dnicht die gleiche wie die, mit der Sie arbeiten pSwapChain->GetBuffer.
Mit pDevice->CreateTexture2Derstellen Sie eine brandneue On-Memory-Textur (die tatsächlich so eingestellt ist, dass sie von der CPU gelesen werden kann). Die neue Textur wird in gespeichert pFrame.
Mit erhalten pSwapChain->GetBufferSie dann den vorhandenen Backpuffer aus der Swap-Kette (die nicht mit CPU-Lesezugriff erstellt wurde). Dies wird in gespeichert pFrame, wodurch der vorherige Wert effektiv verworfen wird.
Sie haben hier zwei Möglichkeiten (ich habe D3D11 seit Jahren nicht mehr berührt. Dies basiert auf meinen Kenntnissen über D3D12, Vulkan und Konsolen der aktuellen Generation).
Option A besteht darin, die neue On-Memory-Textur zu löschen und stattdessen den Backpuffer in der Swap-Kette für den CPU-Zugriff festzulegen. Ich empfehle dies nicht, da Texturen, die für das Zurücklesen der CPU vorgesehen sind, nicht unbedingt so gut funktionieren wie Texturen, die ohne CPU-Zugriff erstellt wurden.
Während Sie den Speicher zugeordnet haben, muss die GPU den Inhalt der Textur leeren und alle GPU-Vorgänge darauf einstellen, bis Sie die Zuordnung des Speichers aufheben. Abhängig davon, wo Sie dies in Ihrem Zeichenprozess tun, ist dieser Vorgang ein großer Engpass.
Ich bin mir übrigens nicht einmal sicher, ob Sie mit Direct3D eine Swap-Kette mit CPU-Lesezugriff erstellen können, sodass Sie sie wahrscheinlich nicht einmal ausprobieren können.
Option B besteht darin, eine Zeichenoperation mit einem strukturierten Quad im Vollbildmodus durchzuführen, wobei die Textur der Rückpuffer und das Renderziel Ihre CPU-lesbare Textur ist. Dies ist im Allgemeinen die bevorzugte Methode, da sie alle Probleme löst, die Option A auf Kosten eines Vollbild-Zeichenvorgangs hatte.
Denken Sie daran, dass es möglich ist (ich erinnere mich nicht an D3D11), dass Sie den Rückpuffer nicht als Textur für das Zeichnen festlegen können. In diesem Fall müssen Sie möglicherweise, anstatt die Draw-Aufrufe Ihres Spiels direkt an den Back-Buffer zu senden, ein weiteres Rendering-Ziel erstellen, alles darauf zeichnen und dann zwei Kopien erstellen: eine an den Back-Buffer der Swap-Kette und eine weitere an Ihre CPU-lesbare Textur.
Diese Methode ist etwas komplexer, funktioniert aber garantiert auf D3D12 sowie höchstwahrscheinlich auf jeder anderen modernen Plattform.