Войти
ПрограммированиеФорумГрафика

[WinAPI] BitBlt из d3d окна

Страницы: 1 2 Следующая »
#0
6:47, 11 дек. 2017

Имеется окно, будем считать, что чужое. В него происходит рендеринг через d3d-контекст. Я могу через FindWindow() найти его hWnd. Требуется скопировать содержимое этого окна в HBITMAP в памяти. Есть стандартный код, который это делает:

HWND hWnd = ...; //ищем окно
HDC hWindowDC = GetDC(hWnd);

HBITMAP hBitmap = CreateCompatibleBitmap(hWindowDC, 500, 500);
HDC memoryDC = CreateCompatibleDC(hWindowDC);
SelectObject(memoryDC, hBitmap);
BitBlt(memoryDC, 0, 0, width, height, hWindowDC, 0, 0, SRCCOPY);
Этот код даже работает с обычными окнами — например, я могу FindWindow("Notepad") и взять скриншот блокнота. Но если в окно происходит рендеринг через аппаратное ускорение, то изображение получается чёрным. Если точнее, то не чёрным, а цветом бэграунда окна. Ещё одно наблюдение — если вместо hWnd нужного окна брать скриншот всего экрана через GetDesktopWindow(), то скриншот берётся корректно. Мне не нужен весь десктоп, мне нужно именно окно.

Самое, на мой взгляд, странное, в этой истории — что код работает даже с аппаратно ускоренным окном, если приложение скомпилено в дебаге. К сожалению, я не имею доступа ни к девайсу, ни к сурфейсам d3d, могу пользоваться только WinAPI-вызовами. Чем объясняется такое странное поведение и как корректно скопировать содержимое окна?

Я знаю, что фрапс, например, это делает через хуки. Но мне не нужно ловить каждый кадр, мне нужно только текущее состояние — может, возможно как-то проще?

#1
7:24, 11 дек. 2017

Вот тут https://stackoverflow.com/questions/5069104/fastest-method-of-screen-capturing кивают в сторону опенсоурсной OBS-Studio, конкретно в этот исходник: https://github.com/jp9000/obs-studio/blob/master/plugins/win-capt… /dc-capture.c
Говорят, что она сперва пытается дёрнуть IDXGISurface1, а если не получается - откатывается к твоему коду.

#2
7:32, 11 дек. 2017

Suslik
Проблема скорее всего из-за того, что рендер не на WM_PAINT происходит. Если есть возможность перенести рендер в WM_PAINT, то я бы это первым делом сделал.
На крайняк можно попробовать CAPTUREBLT передать в BitBlt, может поможет. А проблема в том, что BitBlt с десктопа идет после композита, в то время как BitBlt окна скорее всего посылает WM_PAINT.

#3
7:48, 11 дек. 2017

MrShoor
> CAPTUREBLT
пробовал

> Если есть возможность перенести рендер в WM_PAINT, то я бы это первым делом сделал
такой возможности, к сожалению, нет. могу сделать разве что в качестве теста.

=A=L=X=
девайса к моменту вызова этой функции нет. ещё важный момент: если целевое окно — d3d9, то всё работает нормально. если целевое окно — d3d11, то работает только в дебаге.

#4
8:02, 11 дек. 2017

Suslik
Все обращения к экранному контексту окна копируют только видимую область, не перекрытую другими окнами. Чтобы надежно снимать содержимое окна, нужно чтобы оно обрабатывало WM_PAINT, причем само рисовало в переданный в этом сообщении контекст (wParam)

#5
8:15, 11 дек. 2017

попробовал через WM_PAINT. даже через допотопные BeginPaint()/EndPaint() — разумеется, не работает. если честно, оно и не удивительно, потому что я беру hWnd не своего окна, а чужого. то есть я читаю пиксели внутри WM_PAINT своего окна, что не имеет никакого отношения к WM_PAINT окна, из которого я читаю.

#6
8:56, 11 дек. 2017

Suslik
Obs-studio это прога для организации стриминга игр и игрулек в твитч и ютубчик, если в его исходниках не найдешь, то нигде не найдешь.

#7
10:08, 11 дек. 2017

Suslik
> попробовал через WM_PAINT
Попробуй слать чужому окну WM_PAINT, указав в WParam целевой контекст устройства. Есть небольшой шанс, что окно себя туда нарисует

#8
11:03, 11 дек. 2017

=A=L=X=
> Говорят, что она сперва пытается дёрнуть IDXGISurface1, а если не получается -
> откатывается к твоему коду.
Очень маленькая вероятность что  IDXGISwapChain был создан флагом DXGI_SWAP_CHAIN_FLAG_GDI_COMPATIBLE, не помню будет ли коррекетно получен IDXGISurface1 без этого флага.

#9
11:26, 11 дек. 2017

При захвате рабочего стола через bitblt возникает один неприятный момент - весь UI в винде начинает лагать. Я, например, снимаю раз в секунду и раз в секунду перетаскиваение окон и ресайз обнаруживают лаг. Возможно, эффект присутствует только на семерках, выше не пробовал.

#10
11:43, 11 дек. 2017

Andrey
> Очень маленькая вероятность что  IDXGISwapChain был создан флагом
> DXGI_SWAP_CHAIN_FLAG_GDI_COMPATIBLE, не помню будет ли коррекетно получен
> IDXGISurface1 без этого флага.
>
на самом деле я сам создаю свопчейн, просто это — независимый процесс, то есть я не имею доступа ни к его девайсу, ни к контексту. можно тогда переформулировать вопрос так: какие флаги при создании окна, контекста или свопчейна для d3d11 указать, чтобы скриншот с него можно было взять через bitblt? одного только DXGI_SWAP_CHAIN_FLAG_GDI_COMPATIBLE не хватает — по-прежнему чёрный экран. ещё куда копать? напомню, что d3d9 работает просто без всяких танцев с бубном, не работает именно d3d11 и именно в релизе (код инициализации в явном виде от конфигурации не зависит).

#11
11:50, 11 дек. 2017

Suslik
> если целевое окно — d3d9, то всё работает нормально
Даже с включенным MSAA?

#12
11:52, 11 дек. 2017

Mikle
msaa на d3d9 у меня не поддерживается. вообще слышал, что с этим проблемы есть, да.

#13
12:36, 11 дек. 2017

Suslik
> а самом деле я сам создаю свопчейн, просто это — независимый процесс, то есть я
> не имею доступа ни к его девайсу, ни к контексту. можно тогда переформулировать
> вопрос так: какие флаги при создании окна, контекста или свопчейна для d3d11
> указать, чтобы скриншот с него можно было взять через bitblt? одного только
> DXGI_SWAP_CHAIN_FLAG_GDI_COMPATIBLE не хватает — по-прежнему чёрный экран. ещё
> куда копать?

Я получал HBITMAP c Direct3D11 во так:

1) 

DXGI_SWAP_CHAIN_DESC::Flags |= DXGI_SWAP_CHAIN_FLAG_GDI_COMPATIBLE // без MSAA:
  DXGI_SWAP_CHAIN_DESC.SampleDesc.Count = 1;
  DXGI_SWAP_CHAIN_DESC.SampleDesc.Quality = 0;
DXGI_SWAP_CHAIN_DESC::BufferDesc::Format = DXGI_FORMAT_B8G8R8A8_UNORM
3) дернуть
swapChain->GetBuffer(0, __uuidof(IDXGISurface1), reinterpret_cast<void **>(&pDXGISurface));
4) Получить HDC:
pDXGISurface->GetDC(FALSE, &dxgiHDC);
5) Наверное теперь должен работать bitblt, по крайней мерне у меня отлично с окна Direct3D11 получается HBITMAP через Bitblt
6) Освободить HDC:
pDXGISurface->ReleaseDC(0)
Suslik
> msaa на d3d9 у меня не поддерживается. вообще слышал, что с этим проблемы есть, да.
странно

#14
13:34, 11 дек. 2017

Andrey
я не могу swapChain->чё-то_там, потому что хоть я его и создаю сам, но он вообще в другом процессе, можно сказать. к нему у меня доступа нет. я его могу создать, как захочу, но обращаться к нему из участка кода, который копирует изображение, не могу.

PS предупреждая набег иннуэндо, напомню, что гей-пропаганда на форуме запрещена

Страницы: 1 2 Следующая »
ПрограммированиеФорумГрафика