先列个标题,争取这2天写完。。。
标题写错了居然,
我们一般要实现某个功能,首先希望能找到对应的DEMO,比如我们做屏幕共享,在WIN10下,首先想到的就是DXGI技术,帧率和效率是非常不错的,这里不讲性能,讲下怎么扩展某些功能:
- 鼠标功能:
- 数据获取
- 多显示器需求(多屏,副显,扩展屏说法很多)
先说第一个:
本人下载的DEMO不含鼠标功能,好的,我们加上,直接上代码就是这么简单粗暴:
D3D11_MAPPED_SUBRESOURCE mapdesc;
HRESULT hRes = m_hContext->Map((ID3D11Texture2D *)hNewDesktopImage, m_Subresource, D3D11_MAP_READ_WRITE, 0, &mapdesc);
if (FAILED(hRes))
{
return m_pBuf;
}
if (m_bHaveCursor)//画鼠标
hRes = DrawCursor2(mapdesc, frameDescriptor, FrameInfo);
m_hContext->Unmap(hAcquiredDesktopImage, m_Subresource);
上面这段代码可以控制是否画鼠标,下面是DrawCursor2的实现函数:
HRESULT VideoDXGICaptor::DrawCursor2(D3D11_MAPPED_SUBRESOURCE mapdesc, D3D11_TEXTURE2D_DESC desc, DXGI_OUTDUPL_FRAME_INFO frameInfo)
{
HRESULT hRes = S_OK;
bool bShowCursor = true;
if (mapdesc.pData)
{
CURSORINFO ci;
memset(&ci, 0, sizeof(ci));
ci.cbSize = sizeof(ci);
if (GetCursorInfo(&ci))
{
memcpy(&m_CursorPos, &ci.ptScreenPos, sizeof(m_CursorPos));
if (ci.flags & CURSOR_SHOWING)
{
if (ci.hCursor != m_hCurrentCursor) // re-get cursor data
{
HICON hIcon = CopyIcon(ci.hCursor);
m_hCurrentCursor = ci.hCursor;
free(m_CursorData);
m_CursorData = NULL;
if (hIcon)
{
ICONINFO ii;
if (GetIconInfo(hIcon, &ii))
{
xHotspot = int(ii.xHotspot);
yHotspot = int(ii.yHotspot);
m_CursorData = GetCursorData(hIcon, ii, m_CursorWidth, m_CursorHeight, m_CursorPitch);
DeleteObject(ii.hbmColor);
DeleteObject(ii.hbmMask);
}
DestroyIcon(hIcon);
}
}
}
else
{
bShowCursor = false;
}
}
if (m_CursorData && bShowCursor)
{
// Not supporting mono and masked pointers at the moment
//printf("Drawing pointer at %d %d\n", data->PointerPosition.Position.x, data->PointerPosition.Position.y);
const int ptrx = m_CursorPos.x - xHotspot;
const int ptry = m_CursorPos.y - yHotspot;
uint8_t* ptr = m_CursorData;
uint8_t* dst;
// ### Should really do the blending on the GPU (Using DirectX) rather than using SSE2 on the CPU
const int ptrw = min(m_CursorWidth, desc.Width - ptrx);
for (unsigned int y = 0; y < m_CursorHeight; ++y)
{
if (y + ptry >= desc.Height)
break;
dst = static_cast<uint8_t*>(mapdesc.pData) + (((y + ptry) * mapdesc.RowPitch) + (ptrx * 4));
//memcpy(dst, ptr, data->PointerShape.Width * 4);
ARGBBlendRow_SSE2(ptr, dst, dst, ptrw);
ptr += m_CursorPitch;
}
}
}
return hRes;
}
完成!收工!完美!
第二个问题:数据的获取,我们要从GPU获取数据到CPU内存:
原DEMO的例子是直接将数据大小默认为宽X高X4,这样对于大多数分辨率可能没问题,但是对于一些特殊分辨率比如1366X768,这样是有问题的,得到的数据是错位的。这牵涉到数据对齐问题,一定要注意。
原始:memcpy((BYTE*)m_pBuf, mappedRect.pBits, m_dxgiOutDesc.DesktopCoordinates.right * m_dxgiOutDesc.DesktopCoordinates.bottom * 4);
修改为:
int nImagePitch = m_iWidth * 4;
for (int i = 0; i < m_iHeight; i++)
{
memcpy(m_pBuf + i * nImagePitch, mappedRect.pBits + i * mapdesc.RowPitch, mapdesc.RowPitch);
}
OK!最后一个问题,多显示器需求,共享主屏幕都可以的,但是也有接副显的需求啊,这方面的资料比较难找。遇到问题我一般会先思考,能自己解决最好,比如第二个问题,因为接触类似问题多了,闭着眼睛也知道什么原因。第一个问题原来做GDI的时候也遇到过,所以稍微查下资料也可以解决。第3个比较难找,自己也不是很熟悉,但是问题始终还是要解决的,具体解决过程就不说了,反正现在知道了,呵呵。
先贴代码:
//定义一个数据结构,这里面是每个DXGI的输出和对应的适配器(显示器),没理解错的话
struct DxgiData
{
IDXGIAdapter* pAdapter;
IDXGIOutput *pOutput;
};
std::vector<DxgiData> m_vOutputs;
int Enum()
{
IDXGIFactory1* pFactory1;
HRESULT hr = CreateDXGIFactory1(__uuidof(IDXGIFactory1), (void**)(&pFactory1));
if (FAILED(hr))
{
return 0;
}
for (UINT i = 0;; i++)
{
IDXGIAdapter1* pAdapter1 = nullptr;
hr = pFactory1->EnumAdapters1(i, &pAdapter1);
if (hr == DXGI_ERROR_NOT_FOUND)
{
// no more adapters
break;
}
if (FAILED(hr))
{
return 0;
}
DXGI_ADAPTER_DESC1 desc;
hr = pAdapter1->GetDesc1(&desc);
if (FAILED(hr))
{
return 0;
}
desc.Description;
for (UINT j = 0;; j++)
{
IDXGIOutput *pOutput = nullptr;
HRESULT hr = pAdapter1->EnumOutputs(j, &pOutput);
if (hr == DXGI_ERROR_NOT_FOUND)
{
// no more outputs
break;
}
if (FAILED(hr))
{
return 0;
}
DXGI_OUTPUT_DESC desc;
hr = pOutput->GetDesc(&desc);
if (FAILED(hr))
{
return 0;
}
desc.DeviceName;
desc.DesktopCoordinates.left;
desc.DesktopCoordinates.top;
int width=desc.DesktopCoordinates.right - desc.DesktopCoordinates.left;
int height=desc.DesktopCoordinates.bottom - desc.DesktopCoordinates.top;
DxgiData data;
data.pAdapter = pAdapter1;
data.pOutput = pOutput;
m_vOutputs.push_back(data);
}
}
return m_vOutputs.size();
}
上面是枚举显示器适配器信息:
下面贴初始化调用代码:
HRESULT hr = S_OK;
if (m_bInit)
{
return FALSE;
}
int nCount=Enum();
if (nCount <= 0)
return FALSE;
m_iWidth = m_iHeight = 0;
// Driver types supported
D3D_DRIVER_TYPE DriverTypes[] =
{
D3D_DRIVER_TYPE_HARDWARE,
D3D_DRIVER_TYPE_WARP,
D3D_DRIVER_TYPE_REFERENCE,
};
UINT NumDriverTypes = ARRAYSIZE(DriverTypes);
// Feature levels supported
D3D_FEATURE_LEVEL FeatureLevels[] =
{
D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_10_1,
D3D_FEATURE_LEVEL_10_0,
D3D_FEATURE_LEVEL_9_1
};
//
// Get output
//
INT nOutput = 0;
DxgiData data;
IDXGIAdapter *pAdapter = NULL;
IDXGIOutput *hDxgiOutput = NULL;
int nSize = m_vOutputs.size();
if (m_nDisPlay<0||m_nDisPlay >= nSize)
m_nDisPlay = 0;
data = m_vOutputs.at(m_nDisPlay);
hDxgiOutput = data.pOutput;
pAdapter = data.pAdapter;
hDxgiOutput->GetDesc(&m_dxgiOutDesc);
UINT NumFeatureLevels = ARRAYSIZE(FeatureLevels);
for (int i = 0; i < NumFeatureLevels; i++)
{
D3D_FEATURE_LEVEL FeatureLevel = FeatureLevels[i];
hr = D3D11CreateDevice(pAdapter, D3D_DRIVER_TYPE_UNKNOWN, NULL, 0, 0, 0, D3D11_SDK_VERSION, &m_hDevice, &FeatureLevel, &m_hContext);
if (SUCCEEDED(hr))
{
break;
}
}
//
// Get DXGI device
//
IDXGIDevice *hDxgiDevice = NULL;
hr = m_hDevice->QueryInterface(__uuidof(IDXGIDevice), reinterpret_cast<void**>(&hDxgiDevice));
if (FAILED(hr))
{
return FALSE;
}
//
// Get DXGI adapter
//
IDXGIAdapter *hDxgiAdapter = NULL;
hr = hDxgiDevice->GetParent(__uuidof(IDXGIAdapter), reinterpret_cast<void**>(&hDxgiAdapter));
RESET_OBJECT(hDxgiDevice);
if (FAILED(hr))
{
return FALSE;
}
IDXGIOutput1 *hDxgiOutput1 = NULL;
hr = hDxgiOutput->QueryInterface(__uuidof(hDxgiOutput1), reinterpret_cast<void**>(&hDxgiOutput1));
RESET_OBJECT(hDxgiOutput);
if (FAILED(hr))
{
return FALSE;
}
//
// Create desktop duplication
//
hr = hDxgiOutput1->DuplicateOutput(m_hDevice, &m_hDeskDupl);
RESET_OBJECT(hDxgiOutput1);
if (FAILED(hr))
{
return FALSE;
}
m_Subresource = D3D11CalcSubresource(0, 0, 0);
AttatchToThread();
// 初始化成功
m_bInit = TRUE;
return TRUE;
好了,到此为止,我也无话可说,简直就是完美,没人比我更懂DXGI了(听上去好熟悉,逗逼川普的名言)。。。
m_nDisPlay随意指定,看看效果吧
本人QQ35744025,寻求合作随时骚扰即可
最后附上一个GDI多屏的下载链接:
https://download.csdn.net/download/xjb2006/12570425
这dxgi的抓屏demo(包含捕获数据,实时显示,鼠标,副屏)
https://download.csdn.net/download/xjb2006/16126779