Я знаю, что в OpenGL я могу сделать что-то вроде этого
glReadBuffer( GL_FRONT );
glReadPixels( 0, 0, _width, _height, GL_RGB, GL_UNSIGNED_BYTE, _buffer );
И это довольно быстро, я получаю необработанное растровое изображение в _buffer. Когда я пытаюсь сделать это в DirectX. Предполагая, что у меня есть объект D3DDevice, я могу сделать что-то вроде этого
if (SUCCEEDED(D3DDevice->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &pBackbuffer))) {
HResult hr = D3DXSaveSurfaceToFileA(filename, D3DXIFF_BMP, pBackbuffer, NULL, NULL);
Но D3DXSaveSurfaceToFile работает довольно медленно, и мне все равно не нужно записывать запись на диск, поэтому мне было интересно, есть ли более быстрый способ сделать это.
РЕДАКТИРОВАТЬ: я ориентируюсь только на приложения DirectX. На самом деле приложения DX9. Я делаю это через крючок для подарка. (Я использую Detours, если это уместно), и я делаю это для каждого кадра. Мне не нужен (или не нужен) конкретный растровый формат, такой как BMP PNG JPEG и т. Д., И цветовое пространство RGB24 - это нормально. Получить его как YUV480p было бы лучше, но определенно не обязательно. Спасибо всем за очень полезные ответы
источник
Ответы:
Я делаю это следующим образом.
IDirect3DDevice9 :: GetBackBuffer : Получить доступ к IDirect3DSurface9, представляющему задний буфер, так же, как вы в настоящее время получили. Не забудьте освободить эту поверхность после завершения, так как этот вызов увеличит счетчик ссылок!
IDirect3DSurface :: GetDesc : Получить описание задней поверхности буфера, которая даст вам ее ширину, высоту и формат.
IDirect3DDevice9 :: CreateOffscreenPlainSurface : создать новый объект поверхности в D3DPOOL_SCRATCH; Вы обычно хотите использовать одинаковую ширину, высоту и формат (но на самом деле это не обязательно). Снова отпустите, когда закончите. Если вы выполняете эту операцию каждый кадр (в этом случае вам лучше смотреть на альтернативы, такие как шейдерный подход к тому, что вы пытаетесь сделать), вы можете просто создать закадровую плоскую поверхность один раз при запуске и повторно использовать это, вместо того, чтобы создавать его каждый кадр.
D3DXLoadSurfaceFromSurface : Копировать с задней поверхности буфера на невидимую плоскую поверхность. Это сделает изменение размера и форматирование автоматически для вас. В качестве альтернативы, если вы не хотите или не хотите изменять размер или изменять формат, вы можете использовать IDirect3DDevice9 :: GetRenderTargetData , но если это так, то вместо этого создайте плоскую поверхность вне экрана в D3DPOOL_SYSTEMMEM.
IDirect3DSurface9 :: LockRect : получите доступ к данным на простой экранной поверхности и используйте свой собственный злой путь; UnlockRect, когда закончите.
Это выглядит как гораздо больше кода, но вы обнаружите, что он такой же быстрый, как glReadPixels, и может быть даже быстрее, если вам не нужно выполнять преобразование формата (что почти наверняка делает glReadPixels, использующий GL_RGB).
Редактировать, чтобы добавить: некоторые (готовые и готовые) вспомогательные функции, которые у меня также есть, которые могут быть полезны для использования этого метода для снимков экрана:
источник
Я считаю, что вам нужно LockRectangle на pBackBuffer, а затем прочитать пиксели из D3DLOCKED_RECT.pBits . Это должно быть довольно быстро. Если вам нужно прочитать только несколько пикселей, рассмотрите возможность копирования всего буфера в меньший буфер через что-то вроде DX11 CopySubresourceRegion (я уверен, что есть альтернатива и в DX9), что делается на GPU, а затем передайте этот небольшой буфер из GPU в CPU через LockRectangle - вы сохраняете передачу большого буфера на шину.
источник
Вы не можете захватывать экран с помощью GetBackbuffer, эта функция получает только указатель на задний буфер, а не данные.
Правильный способ, которым я могу думать, как ниже
источник
Немного поздно. Но надеюсь, что это кому-то поможет.
создание
отлов
В buf у вас будут необработанные данные изображения. Не забудьте выпустить все.
источник