【发布时间】:2014-01-14 01:59:31
【问题描述】:
我正准备为我的电脑构建流光溢彩的克隆。 为此,我需要一种方法来计算屏幕几个区域的平均颜色。
目前我发现最快的方法如下:
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 毫秒。显然 33 毫秒远不及获得良好更新率所需的速度,因此我正在寻找一种方法来直接在 gpu 上计算前缓冲区区域的平均值,而无需将前缓冲区复制回系统内存。
编辑:代码 sn-p 中的瓶颈是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: Windows 8 之前的操作系统不支持 DuplicateOutput :(
edit5:
我用经典的GetPixel API 做了一些实验,认为它对于随机采样可能足够快。可悲的是,事实并非如此。 GetPixel 花费的时间与 GetFrontBufferData 花费的时间相同。我猜它内部调用GetFrontBufferData。
所以现在我看到了两种解决方案:
* 禁用 Aero 并使用 GetFrontBufferData
* 切换到 Windows 8
他们两个都不是很好:(
【问题讨论】:
-
听起来不错,你用什么做灯光?你确定你可以用后台进程抓取整个屏幕吗?喜欢这个amblone.com?
-
你能把他的缓冲区绑定为纹理,然后在像素着色器中进行计算吗?
-
@◙user3125280 我打算使用由 Arduino 驱动的this led rgb 灯条。是的,就像安布隆一样。但我想自己编写代码:D 实际上,我发布的代码 sn-p 几乎与 ambone 使用的代码相同。
-
一个更快的选择是随机采样点,并保持移动平均
标签: c++ c graphics directx gpu