Sé que en OpenGL puedo hacer algo como esto
glReadBuffer( GL_FRONT );
glReadPixels( 0, 0, _width, _height, GL_RGB, GL_UNSIGNED_BYTE, _buffer );
Y es bastante rápido, obtengo el mapa de bits sin procesar en _buffer. Cuando trato de hacer esto en DirectX. Suponiendo que tengo un objeto D3DDevice, puedo hacer algo como esto
if (SUCCEEDED(D3DDevice->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &pBackbuffer))) {
HResult hr = D3DXSaveSurfaceToFileA(filename, D3DXIFF_BMP, pBackbuffer, NULL, NULL);
Pero D3DXSaveSurfaceToFile es bastante lento, y de todos modos no necesito escribir la captura en el disco, así que me preguntaba si había una forma más rápida de hacerlo.
EDITAR: solo apunto a aplicaciones DirectX. En realidad aplicaciones DX9. Estoy haciendo esto a través de un gancho para presentar. (Estoy usando Detours si eso es relevante), y lo estoy haciendo para cada cuadro. No necesito (o quiero) un formato de mapa de bits particular como BMP PNG JPEG, etc., y un espacio de color de RGB24 está bien. Conseguirlo como YUV480p sería mejor, pero definitivamente no es necesario. Gracias a todos por las respuestas muy útiles.
fuente
Respuestas:
La forma en que hago esto es la siguiente.
IDirect3DDevice9 :: GetBackBuffer : Obtenga acceso a IDirect3DSurface9 que representa el búfer posterior, igual que lo que tiene actualmente. ¡No olvide liberar esta superficie cuando termine ya que esta llamada incrementará el recuento de referencias!
IDirect3DSurface :: GetDesc : Obtenga la descripción de la superficie del búfer posterior, que le dará su ancho, alto y formato.
IDirect3DDevice9 :: CreateOffscreenPlainSurface : crea un nuevo objeto de superficie en D3DPOOL_SCRATCH; normalmente desea usar el mismo ancho, alto y formato (pero en realidad no tiene que hacerlo con este método). Nuevamente, suelte cuando haya terminado. Si está realizando esta operación en cada cuadro (en cuyo caso es mejor que busque alternativas como un enfoque basado en sombreador para lo que está tratando de hacer), podría crear la superficie plana fuera de la pantalla una vez al inicio y reutilizarla. en lugar de crearlo cada cuadro.
D3DXLoadSurfaceFromSurface : copie desde la superficie del búfer posterior a la superficie plana descentrada. Esto hará un cambio de tamaño y conversión de formato automáticamente para usted. Alternativamente, si no desea o necesita cambiar el tamaño o cambiar el formato, puede usar IDirect3DDevice9 :: GetRenderTargetData , pero si es así, cree la superficie plana fuera de la pantalla en D3DPOOL_SYSTEMMEM.
IDirect3DSurface9 :: LockRect : Obtenga acceso a los datos en la superficie plana fuera de la pantalla y tenga su propio mal camino; DesbloquearRect cuando haya terminado.
Esto parece mucho más código, pero descubrirá que es tan rápido como glReadPixels, e incluso puede ser más rápido si no necesita hacer una conversión de formato (que glReadPixels usando GL_RGB casi seguramente lo hace).
Editar para agregar: algunas funciones auxiliares (rought 'n' ready) que también tengo que pueden ser útiles para usar este método para capturas de pantalla:
fuente
Creo que necesita LockRectangle en el pBackBuffer y luego leer los píxeles de D3DLOCKED_RECT.pBits . Esto debería ser bastante rápido. Si necesita leer solo unos pocos píxeles, considere copiar todo el backbuffer a un buffer más pequeño a través de algo como DX11 CopySubresourceRegion (estoy seguro de que también hay una alternativa en DX9), que se realiza en GPU y luego transmita este pequeño buffer desde GPU a CPU a través de LockRectangle : guarda la transferencia del gran backbuffer en el bus.
fuente
No puede capturar la pantalla con GetBackbuffer, esta función solo obtiene un puntero del búfer posterior, no los datos.
La forma correcta en que puedo pensar es la siguiente
fuente
Un poco tarde. Pero espero que esto ayude a alguien.
creando
capturar
En buf tendrás datos de imagen en bruto. No te olvides de liberar todo.
fuente