Eu sei que no OpenGL eu posso fazer algo assim
glReadBuffer( GL_FRONT );
glReadPixels( 0, 0, _width, _height, GL_RGB, GL_UNSIGNED_BYTE, _buffer );
E é bem rápido, eu recebo o bitmap bruto no _buffer. Quando tento fazer isso no DirectX. Supondo que eu tenha um objeto D3DDevice, posso fazer algo assim
if (SUCCEEDED(D3DDevice->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &pBackbuffer))) {
HResult hr = D3DXSaveSurfaceToFileA(filename, D3DXIFF_BMP, pBackbuffer, NULL, NULL);
Mas o D3DXSaveSurfaceToFile é bem lento, e eu não preciso gravar a captura no disco, então fiquei pensando se havia uma maneira mais rápida de fazer isso.
EDIT: Estou direcionando apenas aplicativos DirectX. Na verdade, aplicativos DX9. Estou fazendo isso através de um gancho para apresentar. (Estou usando Detours, se isso for relevante), e estou fazendo isso para todos os quadros. Não preciso (ou quero) de um formato de bitmap específico, como BMP PNG JPEG etc., e um espaço de cores de RGB24 está OK. Obtê-lo como YUV480p seria melhor, mas definitivamente não é necessário. Obrigado a todos pelas respostas muito úteis
fonte
Respostas:
A maneira como faço isso é a seguinte.
IDirect3DDevice9 :: GetBackBuffer : obtenha acesso ao IDirect3DSurface9 que representa o buffer traseiro, o mesmo que você possui atualmente. Não se esqueça de Liberar esta superfície quando terminar, pois esta chamada aumentará a contagem de referência!
IDirect3DSurface :: GetDesc : obtenha a descrição da superfície do buffer traseiro, que fornecerá largura, altura e formato.
IDirect3DDevice9 :: CreateOffscreenPlainSurface : Crie um novo objeto de superfície em D3DPOOL_SCRATCH; geralmente você deseja usar a mesma largura, altura e formato (mas na verdade não precisa desse método). Novamente, solte quando terminar. Se você estiver executando esta operação em todos os quadros (nesse caso, é melhor procurar alternativas como uma abordagem baseada em shader para o que você está tentando fazer), basta criar a superfície plana fora da tela uma vez na inicialização e reutilizar em vez de criar todos os quadros.
D3DXLoadSurfaceFromSurface : copie da superfície do buffer traseiro para a superfície plana do offsceen. Isso fará um redimensionamento e conversão de formato automaticamente para você. Como alternativa, se você não quiser ou precisar redimensionar ou alterar o formato, poderá usar IDirect3DDevice9 :: GetRenderTargetData , mas, se for o caso, crie a superfície plana fora da tela em D3DPOOL_SYSTEMMEM.
IDirect3DSurface9 :: LockRect : Tenha acesso aos dados na superfície plana fora da tela e faça o que quiser ; UnlockRect quando terminar.
Isso parece muito mais código, mas você descobrirá que é tão rápido quanto o glReadPixels e pode ser ainda mais rápido se você não precisar fazer uma conversão de formato (o que o glReadPixels usando GL_RGB quase certamente faz).
Edite para adicionar: algumas funções auxiliares (prontas para uso) que também tenho, que podem ser úteis para usar este método para capturas de tela:
fonte
Eu acredito que você precisa LockRectangle no pBackBuffer e, em seguida, leia os pixels de D3DLOCKED_RECT.pBits . Isso deve ser bem rápido. Se você precisar ler apenas alguns pixels, considere copiar o backbuffer inteiro para um buffer menor através de algo como DX11 CopySubresourceRegion (tenho certeza de que também há alternativa no DX9), o que é feito na GPU e depois transmite esse pequeno buffer da GPU para CPU via LockRectangle - você salva a transferência do grande backbuffer no barramento.
fonte
Você não pode capturar a tela com GetBackbuffer, esta função obtém apenas um ponteiro do buffer de fundo, não os dados.
A maneira correta de pensar é como abaixo
fonte
Um pouco tarde. Mas espero que isso ajude alguém.
criando
capturando
No buf, você terá dados de imagem brutos. Não se esqueça de liberar tudo.
fonte