先列個標題,爭取這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