Saya tahu bahwa di OpenGL saya dapat melakukan sesuatu seperti ini
glReadBuffer( GL_FRONT );
glReadPixels( 0, 0, _width, _height, GL_RGB, GL_UNSIGNED_BYTE, _buffer );
Dan ini cukup cepat, saya mendapatkan bitmap mentah di _buffer. Ketika saya mencoba melakukan ini di DirectX. Dengan asumsi bahwa saya memiliki objek D3DDevice, saya dapat melakukan sesuatu seperti ini
if (SUCCEEDED(D3DDevice->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &pBackbuffer))) {
HResult hr = D3DXSaveSurfaceToFileA(filename, D3DXIFF_BMP, pBackbuffer, NULL, NULL);
Tapi D3DXSaveSurfaceToFile cukup lambat, dan saya tidak perlu menulis tangkapan ke disk, jadi saya bertanya-tanya apakah ada cara yang lebih cepat untuk melakukan ini.
EDIT: Saya hanya menargetkan aplikasi DirectX. Sebenarnya aplikasi DX9. Saya melakukan ini melalui hook to Present. (Saya menggunakan jalan memutar jika itu relevan), dan saya melakukan ini untuk setiap frame. Saya tidak perlu (atau ingin) format bitmap tertentu seperti BMP PNG JPEG dll, dan ruang warna dari RGB24 adalah OK. Mendapatkannya sebagai YUV480p akan lebih baik tapi jelas tidak perlu. Terima kasih semuanya atas jawaban yang sangat membantu
sumber
Jawaban:
Cara saya melakukan ini adalah sebagai berikut.
IDirect3DDevice9 :: GetBackBuffer : Dapatkan akses ke IDirect3DSurface9 yang mewakili buffer belakang, sama seperti yang Anda miliki saat ini. Jangan lupa untuk melepaskan permukaan ini ketika dilakukan karena panggilan ini akan menambah jumlah referensi!
IDirect3DSurface :: GetDesc : Dapatkan deskripsi permukaan buffer belakang, yang akan memberi Anda lebar, tinggi, dan format.
IDirect3DDevice9 :: CreateOffscreenPlainSurface : Buat objek permukaan baru di D3DPOOL_SCRATCH; Anda biasanya ingin menggunakan lebar, tinggi, dan format yang sama (tetapi Anda sebenarnya tidak harus menggunakan metode ini). Sekali lagi, Lepaskan setelah selesai. Jika Anda melakukan operasi ini setiap frame (dalam hal ini Anda lebih baik melihat alternatif seperti pendekatan berbasis shader untuk apa yang Anda coba lakukan), Anda bisa membuat permukaan polos layar sekali pada saat startup dan menggunakan kembali itu, alih-alih membuatnya setiap frame.
D3DXLoadSurfaceFromSurface : Salin dari permukaan buffer belakang ke permukaan polos offsceen. Ini akan melakukan perubahan ukuran dan memformat konversi secara otomatis untuk Anda. Atau, jika Anda tidak ingin atau perlu mengubah ukuran atau mengubah format, Anda bisa menggunakan IDirect3DDevice9 :: GetRenderTargetData , tetapi jika demikian, buat permukaan polos layar di D3DPOOL_SYSTEMMEM sebagai gantinya.
IDirect3DSurface9 :: LockRect : Dapatkan akses ke data di permukaan polos layar dan miliki cara jahat Anda sendiri dengannya; UnlockRect saat selesai.
Ini terlihat seperti kode yang lebih banyak tetapi Anda akan menemukan bahwa itu secepat glReadPixels, dan bahkan dapat lebih cepat jika Anda tidak perlu melakukan konversi format (yang glReadPixels menggunakan GL_RGB hampir pasti melakukannya).
Sunting untuk menambahkan: beberapa fungsi pembantu yang telah saya siapkan yang mungkin berguna untuk menggunakan metode ini untuk tangkapan layar:
sumber
Saya percaya Anda perlu LockRectangle di pBackBuffer dan kemudian membaca piksel dari D3DLOCKED_RECT.pBits . Ini harusnya cukup cepat. Jika Anda hanya perlu membaca beberapa piksel, pertimbangkan untuk menyalin seluruh backbuffer ke buffer yang lebih kecil melalui sesuatu seperti DX11 CopySubresourceRegion (Saya yakin ada alternatif di DX9 juga), yang dilakukan pada GPU dan kemudian mengalirkan buffer kecil ini dari GPU ke CPU via LockRectangle - Anda menghemat transfer backbuffer besar di bus.
sumber
Anda tidak dapat menangkap layar dengan GetBackbuffer, fungsi ini hanya mendapatkan pointer buffer belakang, bukan data.
Cara yang benar yang bisa saya pikirkan adalah seperti di bawah ini
sumber
Sedikit terlambat. Tapi semoga ini membantu seseorang.
menciptakan
menangkap
Di buf Anda akan memiliki data gambar mentah. Jangan lupa untuk melepaskan semuanya.
sumber