Я собираюсь построить клон ампулы для моего компьютера. Для этого мне нужен способ рассчитать средний цвет нескольких областей экрана.
Самый быстрый способ, который я нашел до сих пор, заключается в следующем:
pd3dDevice->CreateOffscreenPlainSurface(ddm.Width, ddm.Height, D3DFMT_A8R8G8B8, D3DPOOL_SCRATCH/*D3DPOOL_SYSTEMMEM*/, &pSurface, nullptr)
pd3dDevice->GetFrontBufferData(0, pSurface);
D3DLOCKED_RECT lockedRect;
pSurface->LockRect(&lockedRect, nullptr, D3DLOCK_NO_DIRTY_UPDATE|D3DLOCK_NOSYSLOCK|D3DLOCK_READONLY);
memcpy(pBits, (unsigned char*) lockedRect.pBits, dataLength);
pSurface->UnlockRect();
//calculate average over of pBits
Однако он вынужден копировать весь передний буфер обратно в системную память, которая в среднем занимает 33 мс. Obviuosly 33ms никак не приближается к скорости, необходимой мне для приличной скорости обновления, поэтому я ищу способ вычисления среднего значения по области переднего буфера непосредственно на gpu, не копируя передний буфер обратно в системную память.
edit: узким местом в фрагменте кода является pd3dDevice->GetFrontBufferData(0, pSurface);
.
Memcpy не оказывает заметного влияния на производительность.
изменить
На основе user3125280 ответ я приготовил кусочек кода, который должен взять верхний левый угол экрана и усреднить его. Однако результат всегда равен 0. Что мне не хватает?
Также обратите внимание, что pSurface
теперь находится в видеопамяти, и, таким образом, GetFrontBufferData
- это только memcpy в видеокамере, который очень быстрый.
pd3dDevice->CreateOffscreenPlainSurface(1, 1, D3DFMT_A8R8G8B8, D3DPOOL_SCRATCH, &pAvgSurface, nullptr);
pd3dDevice->CreateOffscreenPlainSurface(ddm.Width, ddm.Height, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &pSurface, nullptr);
pd3dDevice->GetFrontBufferData(0, pSurface);
RECT r;
r.right = 100;
r.bottom = 100;
r.left = 0;
r.top = 0;
pd3dDevice->StretchRect(pSurface, &r, pAvgSurface, nullptr, D3DTEXF_LINEAR);
D3DLOCKED_RECT lockedRect;
pAvgSurface->LockRect(&lockedRect, nullptr, D3DLOCK_NO_DIRTY_UPDATE|D3DLOCK_NOSYSLOCK|D3DLOCK_READONLY);
unsigned int color = -1;
memcpy((unsigned char*) &color, (unsigned char*) lockedRect.pBits, 4); //FIXME there has to be a better way than memcopy
pAvgSurface->UnlockRect();
edit2:
Очевидно, GetFrontBufferData
требует, чтобы цель находилась в системной памяти. Поэтому я вернусь к квадрату.
Edit3: Согласно this в DX11.1 должно быть возможно следующее:
- Создайте устройство Direct3D 11.1. (Возможно, раньше работает тоже - я не пробовал. Я не уверен, что есть причина использовать устройство D3D10/10.1/11 в любом случае.)
- Найдите IDXGIOutput, который вы хотите дублировать, и вызовите DuplicateOutput(), чтобы получить интерфейс IDXGIOutputDuplication.
- Вызовите AcquireNextFrame(), чтобы ждать появления нового фрейма.
- Обработать полученную текстуру.
- Вызов ReleaseFrame().
- Повтор.
Однако из-за моих незнакомых знаний о DirectX мне трудно выполнять его.
edit4: DuplicateOutput не поддерживается в операционных системах старше Windows 8: (
edit5:
Я провел несколько экспериментов с классическим API GetPixel
, думая, что он может быть достаточно быстрым для случайной выборки. К сожалению, это не так. GetPixel
занимает такое же количество времени, что и GetFrontBufferData
. Я предполагаю, что он внутренне вызывает GetFrontBufferData
.
Итак, теперь я вижу два решения:
* Отключите Aero и используйте GetFrontBufferData
* Переключиться на windows 8
Оба они не очень хороши: (