Capturar pantalla con DirectX

Sé cómo usar GDI para capturar pantalla, sin embargo, es muy lento (apenas capta 10 fps)

He leído que DirectX ofrece la mejor velocidad. Pero antes de comenzar a aprender DirectX, quería probar una muestra para ver si realmente es tan rápido.

He encontrado esta pregunta que ofrece un código de muestra para hacer eso:

void dump_buffer() { IDirect3DSurface9* pRenderTarget=NULL; IDirect3DSurface9* pDestTarget=NULL; const char file[] = "Pickture.bmp"; // sanity checks. if (Device == NULL) return; // get the render target surface. HRESULT hr = Device->GetRenderTarget(0, &pRenderTarget); // get the current adapter display mode. //hr = pDirect3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT,&d3ddisplaymode); // create a destination surface. hr = Device->CreateOffscreenPlainSurface(DisplayMde.Width, DisplayMde.Height, DisplayMde.Format, D3DPOOL_SYSTEMMEM, &pDestTarget, NULL); //copy the render target to the destination surface. hr = Device->GetRenderTargetData(pRenderTarget, pDestTarget); //save its contents to a bitmap file. hr = D3DXSaveSurfaceToFile(file, D3DXIFF_BMP, pDestTarget, NULL, NULL); // clean up. pRenderTarget->Release(); pDestTarget->Release(); } 

Intenté incluir los archivos necesarios. Sin embargo, no todos pueden incluirse (por ejemplo, #include ).

¿Alguien puede proporcionar un ejemplo de trabajo que tenga todas las inclusiones requeridas o señalarme qué bibliotecas debo instalar?

Estoy usando Visual C ++ 2010 Express en Windows 7 Ultimate (x64).


Editar:

Además, este código no está completo, por ejemplo, ¿cuál es el identificador del Device ?

Aquí hay algunos ejemplos de código para capturar la pantalla con DirectX 9. No debería tener que instalar ningún SDK (excepto los archivos estándar que vienen con Visual Studio, aunque no probé VS 2010).

Simplemente cree una aplicación simple de consola Win32, agregue lo siguiente en el archivo stdafx.h:

  #include  // we use WIC for saving images #include  // DirectX 9 header #pragma comment(lib, "d3d9.lib") // link to DirectX 9 library 

Aquí está la implementación principal de la muestra

  int _tmain(int argc, _TCHAR* argv[]) { HRESULT hr = Direct3D9TakeScreenshots(D3DADAPTER_DEFAULT, 10); return 0; } 

Lo que hará es capturar 10 veces la pantalla y guardar imágenes “cap% i.png” en el disco. También mostrará el tiempo necesario para esto (guardar las imágenes no se cuenta en ese momento, solo capturas de pantalla). En mi (computadora de escritorio Windows 8 – Dell Precision M2800 / i7-4810MQ-2.80GHz / Intel HD 4600 que es una máquina bastante horrible …), toma 100 capturas de 1920×1080 en ~ 4sec, así que alrededor de 20/25 fps.

  HRESULT Direct3D9TakeScreenshots(UINT adapter, UINT count) { HRESULT hr = S_OK; IDirect3D9 *d3d = nullptr; IDirect3DDevice9 *device = nullptr; IDirect3DSurface9 *surface = nullptr; D3DPRESENT_PARAMETERS parameters = { 0 }; D3DDISPLAYMODE mode; D3DLOCKED_RECT rc; UINT pitch; SYSTEMTIME st; LPBYTE *shots = nullptr; // init D3D and get screen size d3d = Direct3DCreate9(D3D_SDK_VERSION); HRCHECK(d3d->GetAdapterDisplayMode(adapter, &mode)); parameters.Windowed = TRUE; parameters.BackBufferCount = 1; parameters.BackBufferHeight = mode.Height; parameters.BackBufferWidth = mode.Width; parameters.SwapEffect = D3DSWAPEFFECT_DISCARD; parameters.hDeviceWindow = NULL; // create device & capture surface HRCHECK(d3d->CreateDevice(adapter, D3DDEVTYPE_HAL, NULL, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &parameters, &device)); HRCHECK(device->CreateOffscreenPlainSurface(mode.Width, mode.Height, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, &surface, nullptr)); // compute the required buffer size HRCHECK(surface->LockRect(&rc, NULL, 0)); pitch = rc.Pitch; HRCHECK(surface->UnlockRect()); // allocate screenshots buffers shots = new LPBYTE[count]; for (UINT i = 0; i < count; i++) { shots[i] = new BYTE[pitch * mode.Height]; } GetSystemTime(&st); // measure the time we spend doing  captures wprintf(L"%i:%i:%i.%i\n", st.wHour, st.wMinute, st.wSecond, st.wMilliseconds); for (UINT i = 0; i < count; i++) { // get the data HRCHECK(device->GetFrontBufferData(0, surface)); // copy it into our buffers HRCHECK(surface->LockRect(&rc, NULL, 0)); CopyMemory(shots[i], rc.pBits, rc.Pitch * mode.Height); HRCHECK(surface->UnlockRect()); } GetSystemTime(&st); wprintf(L"%i:%i:%i.%i\n", st.wHour, st.wMinute, st.wSecond, st.wMilliseconds); // save all screenshots for (UINT i = 0; i < count; i++) { WCHAR file[100]; wsprintf(file, L"cap%i.png", i); HRCHECK(SavePixelsToFile32bppPBGRA(mode.Width, mode.Height, pitch, shots[i], file, GUID_ContainerFormatPng)); } cleanup: if (shots != nullptr) { for (UINT i = 0; i < count; i++) { delete shots[i]; } delete[] shots; } RELEASE(surface); RELEASE(device); RELEASE(d3d); return hr; } 

Tenga en cuenta que este código enlaza implícitamente con WIC (una biblioteca de imágenes incluida con Windows desde hace bastante tiempo) para guardar los archivos de imagen (por lo que no necesita el famoso D3DXSaveSurfaceToFile que requiere que se instalen SDK DirectX antiguos):

  HRESULT SavePixelsToFile32bppPBGRA(UINT width, UINT height, UINT stride, LPBYTE pixels, LPWSTR filePath, const GUID &format) { if (!filePath || !pixels) return E_INVALIDARG; HRESULT hr = S_OK; IWICImagingFactory *factory = nullptr; IWICBitmapEncoder *encoder = nullptr; IWICBitmapFrameEncode *frame = nullptr; IWICStream *stream = nullptr; GUID pf = GUID_WICPixelFormat32bppPBGRA; BOOL coInit = CoInitialize(nullptr); HRCHECK(CoCreateInstance(CLSID_WICImagingFactory, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&factory))); HRCHECK(factory->CreateStream(&stream)); HRCHECK(stream->InitializeFromFilename(filePath, GENERIC_WRITE)); HRCHECK(factory->CreateEncoder(format, nullptr, &encoder)); HRCHECK(encoder->Initialize(stream, WICBitmapEncoderNoCache)); HRCHECK(encoder->CreateNewFrame(&frame, nullptr)); // we don't use options here HRCHECK(frame->Initialize(nullptr)); // we dont' use any options here HRCHECK(frame->SetSize(width, height)); HRCHECK(frame->SetPixelFormat(&pf)); HRCHECK(frame->WritePixels(height, stride, stride * height, pixels)); HRCHECK(frame->Commit()); HRCHECK(encoder->Commit()); cleanup: RELEASE(stream); RELEASE(frame); RELEASE(encoder); RELEASE(factory); if (coInit) CoUninitialize(); return hr; } 

Y algunas macros que utilicé:

  #define WIDEN2(x) L ## x #define WIDEN(x) WIDEN2(x) #define __WFILE__ WIDEN(__FILE__) #define HRCHECK(__expr) {hr=(__expr);if(FAILED(hr)){wprintf(L"FAILURE 0x%08X (%i)\n\tline: %u file: '%s'\n\texpr: '" WIDEN(#__expr) L"'\n",hr, hr, __LINE__,__WFILE__);goto cleanup;}} #define RELEASE(__p) {if(__p!=nullptr){__p->Release();__p=nullptr;}} 

Nota: para los clientes de Windows 8+, todos estos (excepto WIC) deben descartarse a favor de la API de Duplicación de Escritorio .

No ha indicado los requisitos para las versiones de Windows de destino. Si no necesita compatibilidad con Windows 7, Windows 8 incluye una nueva interfaz DXGI IDXGIOutputDuplication que permite crear un objeto COM que duplica la salida de un adaptador de video y proporciona acceso a la CPU a la memoria de video a través de IDXGIOutputDuplication :: MapDesktopSurface . MSDN tiene una buena muestra que captura el escritorio a través de esto y lo dibuja dentro de un formulario y funciona de forma agradable y sin problemas. Entonces, si Windows 7 no es obligatorio, le sugiero que mire esto.

Puede obtener el DirectX SDK de microsoft.com/en-ca/download/details.aspx?id=6812 (publicado por @yms). Este SDK es compatible con todas las versiones de Windows, incluido XP. Consulte su documentación sobre cómo incluir / vincular con D3D9.

En su ejemplo, Device es un IDirect3DDevice9 . Cada aplicación D3D9 debe crear uno de estos. Es muy fácil encontrar un código de ejemplo sobre cómo crear uno (por ejemplo, https://msdn.microsoft.com/en-us/library/windows/desktop/bb204867%28v=vs.85%29.aspx ).

En su código de ejemplo, solo se están capturando los contenidos que se procesan en DirectX, lo que supongo que no es su intención. Para capturar toda la pantalla (que supongo que es el objective), en lugar de usar IDirect3DDevice9::GetRenderTarget , debe usar IDirect3DDevice9::GetFrontBufferData , como en este tutorial ( http://dim-i.net/2008/01 / 29 / taking-screenshots-with-directx-and-dev-cpp / ). Si está buscando velocidad, no debe recrear la superficie fuera de pantalla en cada cuadro, como en su ejemplo y este tutorial. El grupo de memoria debe ser D3DPOOL_SYSTEMMEM en este caso, no D3DPOOL_SCRATCH . Dependiendo del tamaño de la pantalla, el cuello de botella probable será escribir las imágenes en el disco.

También tenga en cuenta que la pantalla capturada de esto será para el adaptador utilizado para crear el IDirect3DDevice9 . Este es el primer parámetro para IDirect3D9::CreateDevice . Esto solo es una preocupación si la captura de monitores múltiples es una posibilidad.