Obteniendo datos del backbuffer D3D11 usando Map [duplicar]
Tengo problemas para usar ID3D11DeviceContext :: Map () correctamente.
Aquí está toda la función hasta ahora:
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 };
}
Recibo una excepción lanzada en pContext-> Map ()
"No se puede llamar al mapa con acceso MAP_READ, porque el recurso no se creó con el indicador D3D11_CPU_ACCESS_READ".
Lo cual es confuso ya que el recurso recién creado tiene habilitada la marca D3D11_CPU_ACCESS_READ. Esto también es cierto si pruebo MAP_WRITE y cambio las banderas de acceso para que coincidan. También probé ORing las banderas juntas sin suerte.
El uso final de esta función es poder crear una matriz lateral de CPU de objetos de color (los colores son actualmente una estructura de cuatro flotantes) que luego puedo enviar al sdk de NewTek NDI. Pero creo que también sería útil para guardar capturas de pantalla, crear selectores de color, etc.
¡Cualquier idea bienvenida!
Respuestas
Tu código no usa tu textura creada con D3D11_CPU_ACCESS_READ.
Crea una textura:
// Create temp frame buffer(2d texture)
wrl::ComPtr<ID3D11Texture2D>pFrame = nullptr;
HRESULT hr = pDevice->CreateTexture2D(&pFrameDesc, nullptr, &pFrame);
GFX_THROW_INFO(hr);
Luego libera esa textura y toma una referencia a una de la cadena de intercambio (que nunca está configurada para CPU READ):
hr = pSwapChain->GetBuffer(0, __uuidof(pFrame), &pFrame);
GFX_THROW_INFO(hr);
Luego intenta mapear el búfer de la cadena de intercambio:
hr = pContext->Map(pFrame.Get(), 0u, D3D11_MAP_READ, 0u, &map);
GFX_THROW_INFO(hr);
La respuesta de Chuck Walbourn es correcta, pero para darle un poco más de contexto, la textura con la que está creando pDevice->CreateTexture2Dno es la misma que con la que está obteniendo pSwapChain->GetBuffer.
Con pDevice->CreateTexture2D, está creando una nueva textura en memoria (que de hecho está configurada para ser leída desde la CPU). La nueva textura se almacena en pFrame.
Luego, con pSwapChain->GetBuffer, está obteniendo el búfer posterior existente de la cadena de intercambio (que no se creó con acceso de lectura de CPU). Esto se almacena en pFrame, descartando efectivamente el valor anterior.
Tienes dos opciones aquí (no he tocado D3D11 en años. Esto se basa en mi conocimiento de la consola D3D12, Vulkan y la generación actual)
La opción A es deshacerse de la nueva textura en memoria y, en su lugar, establecer el búfer trasero en la cadena de intercambio para el acceso a la CPU. No te recomiendo que hagas esto, ya que las texturas destinadas a la lectura de CPU no funcionarán necesariamente tan bien como las que se crean sin acceso a la CPU.
Además, mientras tenga la memoria asignada, la GPU tendrá que vaciar el contenido de la textura y detener todas las operaciones de la GPU hasta que elimine la asignación de la memoria. Dependiendo de en qué parte de su proceso de dibujo esté haciendo esto, esta operación será un gran cuello de botella.
Por cierto, ni siquiera estoy seguro de si Direct3D le permitirá crear una cadena de intercambio con acceso de lectura a la CPU, por lo que probablemente ni siquiera podrá probarlo.
La opción B es realizar una operación de dibujo con un quad texturizado de pantalla completa, donde la textura es el búfer de retroceso y el objetivo de renderizado es su textura legible por CPU. Este es generalmente el método preferido, ya que resuelve todos los problemas que tenía la Opción A, a expensas de una operación de dibujo a pantalla completa.
Tenga en cuenta que es posible (no recuerdo con D3D11) que no pueda configurar el búfer trasero como una textura para dibujar. En ese caso, en lugar de realizar las llamadas de extracción de su juego directamente al búfer trasero, es posible que tenga que crear otro destino de renderizado, dibujar todo en él y luego hacer dos copias: una en el búfer posterior de la cadena de intercambio y otra para su textura legible por CPU.
Este método es un poco más complejo, pero está garantizado que funcionará en D3D12, así como probablemente en cualquier otra plataforma moderna.