Ich weiß, dass ich in OpenGL so etwas machen kann
glReadBuffer( GL_FRONT );
glReadPixels( 0, 0, _width, _height, GL_RGB, GL_UNSIGNED_BYTE, _buffer );
Und es ist ziemlich schnell, ich bekomme die rohe Bitmap in _buffer. Wenn ich das in DirectX versuche. Angenommen, ich habe ein D3DDevice-Objekt, kann ich so etwas tun
if (SUCCEEDED(D3DDevice->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &pBackbuffer))) {
HResult hr = D3DXSaveSurfaceToFileA(filename, D3DXIFF_BMP, pBackbuffer, NULL, NULL);
Aber D3DXSaveSurfaceToFile ist ziemlich langsam, und ich muss das Capture sowieso nicht auf die Festplatte schreiben, also habe ich mich gefragt, ob es einen schnelleren Weg gibt, dies zu tun.
BEARBEITEN: Ich ziele nur auf DirectX-Anwendungen ab. Eigentlich DX9 Apps. Ich mache das durch einen Haken an Present. (Ich verwende Umwege, wenn dies relevant ist), und ich mache dies für jeden Frame. Ich brauche (oder möchte) kein bestimmtes Bitmap-Format wie BMP PNG JPEG usw., und ein Farbraum von RGB24 ist in Ordnung. Es als YUV480p zu bekommen wäre besser, aber definitiv nicht notwendig. Vielen Dank an alle für die sehr hilfreichen Antworten
quelle
Antworten:
Ich mache das wie folgt.
IDirect3DDevice9 :: GetBackBuffer : Erhalten Sie Zugriff auf das IDirect3DSurface9, das den Backpuffer darstellt, wie Sie es derzeit haben. Vergessen Sie nicht, diese Oberfläche freizugeben, wenn Sie fertig sind, da dieser Aufruf den Referenzzähler erhöht!
IDirect3DSurface :: GetDesc : Ruft die Beschreibung der hinteren Pufferoberfläche ab , die Ihnen die Breite, Höhe und das Format angibt .
IDirect3DDevice9 :: CreateOffscreenPlainSurface : Erstellen Sie ein neues Oberflächenobjekt in D3DPOOL_SCRATCH. Normalerweise möchten Sie dieselbe Breite, Höhe und dasselbe Format verwenden (dies ist jedoch bei dieser Methode nicht erforderlich). Wieder freigeben, wenn Sie fertig sind. Wenn Sie diesen Vorgang in jedem Frame ausführen (in diesem Fall sollten Sie besser nach Alternativen suchen, z. B. nach einem Shader-basierten Ansatz für das, was Sie tun möchten), können Sie die Offscreen-Oberfläche einfach einmal beim Start erstellen und wiederverwenden es, anstatt es jedes Bild zu erstellen.
D3DXLoadSurfaceFromSurface : Kopieren von der hinteren Pufferoberfläche auf die Offsceen-Ebene. Dadurch wird automatisch eine Größen- und Formatkonvertierung für Sie durchgeführt. Wenn Sie das Format nicht ändern möchten oder müssen, können Sie alternativ IDirect3DDevice9 :: GetRenderTargetData verwenden . Wenn dies jedoch der Fall ist , erstellen Sie stattdessen die Offscreen-Oberfläche in D3DPOOL_SYSTEMMEM.
IDirect3DSurface9 :: LockRect : Erhalten Sie Zugriff auf die Daten in der Offscreen-Ebene und haben Sie Ihren eigenen bösen Weg damit; UnlockRect wenn fertig.
Das sieht nach viel mehr Code aus, aber Sie werden feststellen, dass es genauso schnell ist wie glReadPixels und sogar schneller sein kann, wenn Sie keine Formatkonvertierung durchführen müssen (was glReadPixels mit GL_RGB mit ziemlicher Sicherheit tun).
Bearbeiten, um hinzuzufügen: einige (überlegte und fertige) Hilfsfunktionen, die ich auch habe, die für die Verwendung dieser Methode für Screenshots nützlich sein können:
quelle
Ich glaube, Sie müssen LockRectangle auf dem pBackBuffer und dann die Pixel aus D3DLOCKED_RECT.pBits lesen . Das sollte ziemlich schnell gehen. Wenn Sie nur wenige Pixel lesen müssen, sollten Sie den gesamten Backbuffer über DX11 CopySubresourceRegion (ich bin sicher, dass es auch in DX9 eine Alternative gibt) in einen kleineren Puffer kopieren. Dies geschieht auf der GPU und streamen Sie dann diesen kleinen Puffer von der GPU auf CPU über LockRectangle - Sie speichern die Übertragung des großen Backbuffers auf dem Bus.
quelle
Sie können den Bildschirm nicht mit GetBackbuffer erfassen. Diese Funktion ruft nur einen Zeiger auf den Rückpuffer ab, nicht auf die Daten.
Die richtige Art, wie ich denken kann, ist wie folgt
quelle
Ein bisschen spät. Aber hoffe das hilft jemandem.
Erstellen
Erfassen
In buf haben Sie Rohbilddaten. Vergiss nicht, alles freizugeben.
quelle