本例代码来源于:DXGIDesktopDuplication;GitHub
Windows8以后微软引入了一套新的接口,叫“Desktop Duplication API”,应用程序,可以通过这套API访问桌面数据。而由于Desktop Duplication API是通过Microsoft DirectX Graphics Infrastructure (DXGI)来提供桌面图像的,速度非常快。由于是通过GPU,所以cpu占用率很低,性能很高。还有一点有意思的是,Duplication API获取到的桌面数据,不管显示模式如何设置,都永远是32位RGBA数据(4*8bit),其实这样方便的多了,不用考虑其他可能的情况,比如24位等。综合来看,各方面秒杀GDI截屏技术,易用性上也比MirrorDriver技术好得多,是Windows8以后平台的截屏技术首选。--摘抄自网络
录屏方法:
- 创建D3DDevice
- 通过一系列接口获取路径,获取到IDXGIOutputDuplication接口
- 调用AcquireNextFrame,获取当前桌面数据,保存在IDXGIResource中
- 把数据从GPU映射到内存中
- 拷贝需要的数据到自己的buffer里
IDXGIDevice --> IDXGIAdapter --> IDXGIOutput --> IDXGIOutput1 --> IDXGIOutputDuplication
demo解析:https://github.com/Tinsau/DXGIDesktopDuplication 代码来源,原始来源于微软官网,但是现在已经找不到了
https://files.cnblogs.com/files/8335IT/DXGIDesktopDuplication-cpp.zip?t=1714206000&download=true
demo分析:
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A // PARTICULAR PURPOSE. // // Copyright (c) Microsoft Corporation. All rights reserved #include <limits.h> #include "DisplayManager.h" #include "DuplicationManager.h" #include "OutputManager.h" #include "ThreadManager.h" #include "TextureToFile.h" // // Globals // OUTPUTMANAGER OutMgr; // Below are lists of errors expect from Dxgi API calls when a transition event like mode change, PnpStop, PnpStart // desktop switch, TDR or session disconnect/reconnect. In all these cases we want the application to clean up the threads that process // the desktop updates and attempt to recreate them. // If we get an error that is not on the appropriate list then we exit the application // These are the errors we expect from general Dxgi API due to a transition HRESULT SystemTransitionsExpectedErrors[] = { DXGI_ERROR_DEVICE_REMOVED, DXGI_ERROR_ACCESS_LOST, static_cast<HRESULT>(WAIT_ABANDONED), S_OK // Terminate list with zero valued HRESULT }; // These are the errors we expect from IDXGIOutput1::DuplicateOutput due to a transition HRESULT CreateDuplicationExpectedErrors[] = { DXGI_ERROR_DEVICE_REMOVED, static_cast<HRESULT>(E_ACCESSDENIED), DXGI_ERROR_UNSUPPORTED, DXGI_ERROR_SESSION_DISCONNECTED, S_OK // Terminate list with zero valued HRESULT }; // These are the errors we expect from IDXGIOutputDuplication methods due to a transition HRESULT FrameInfoExpectedErrors[] = { DXGI_ERROR_DEVICE_REMOVED, DXGI_ERROR_ACCESS_LOST, S_OK // Terminate list with zero valued HRESULT }; // These are the errors we expect from IDXGIAdapter::EnumOutputs methods due to outputs becoming stale during a transition HRESULT EnumOutputsExpectedErrors[] = { DXGI_ERROR_NOT_FOUND, S_OK // Terminate list with zero valued HRESULT }; // // Forward Declarations // DWORD WINAPI DDProc(_In_ void* Param); LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); bool ProcessCmdline(_Out_ INT* Output); void ShowHelp(); // // Class for progressive waits // typedef struct { UINT WaitTime; UINT WaitCount; }WAIT_BAND; #define WAIT_BAND_COUNT 3 #define WAIT_BAND_STOP 0 class DYNAMIC_WAIT { public : DYNAMIC_WAIT(); ~DYNAMIC_WAIT(); void Wait(); private : static const WAIT_BAND m_WaitBands[WAIT_BAND_COUNT]; // Period in seconds that a new wait call is considered part of the same wait sequence static const UINT m_WaitSequenceTimeInSeconds = 2; UINT m_CurrentWaitBandIdx; UINT m_WaitCountInCurrentBand; LARGE_INTEGER m_QPCFrequency; LARGE_INTEGER m_LastWakeUpTime; BOOL m_QPCValid; }; const WAIT_BAND DYNAMIC_WAIT::m_WaitBands[WAIT_BAND_COUNT] = { {250, 20}, {2000, 60}, {5000, WAIT_BAND_STOP} // Never move past this band }; DYNAMIC_WAIT::DYNAMIC_WAIT() : m_CurrentWaitBandIdx(0), m_WaitCountInCurrentBand(0) { m_QPCValid = QueryPerformanceFrequency(&m_QPCFrequency); m_LastWakeUpTime.QuadPart = 0L; } DYNAMIC_WAIT::~DYNAMIC_WAIT() { } void DYNAMIC_WAIT::Wait() { LARGE_INTEGER CurrentQPC = {0}; // Is this wait being called with the period that we consider it to be part of the same wait sequence QueryPerformanceCounter(&CurrentQPC); if (m_QPCValid && (CurrentQPC.QuadPart <= (m_LastWakeUpTime.QuadPart + (m_QPCFrequency.QuadPart * m_WaitSequenceTimeInSeconds)))) { // We are still in the same wait sequence, lets check if we should move to the next band if ((m_WaitBands[m_CurrentWaitBandIdx].WaitCount != WAIT_BAND_STOP) && (m_WaitCountInCurrentBand > m_WaitBands[m_CurrentWaitBandIdx].WaitCount)) { m_CurrentWaitBandIdx++; m_WaitCountInCurrentBand = 0; } } else { // Either we could not get the current time or we are starting a new wait sequence m_WaitCountInCurrentBand = 0; m_CurrentWaitBandIdx = 0; } // Sleep for the required period of time Sleep(m_WaitBands[m_CurrentWaitBandIdx].WaitTime); // Record the time we woke up so we can detect wait sequences QueryPerformanceCounter(&m_LastWakeUpTime); m_WaitCountInCurrentBand++; } /* hInstance 当前实例的句柄 hPrevInstance 之前的实例的句柄,一般为NULL,已废弃 lpCmdLine 指向应用程序命令行的字符串指针, 命令行参数,我们执行程序时可以用命令行的形式传入一些参数 nCmdShow 窗口显示方式 */ // Program entry point //主函数 int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ INT nCmdShow) { //若参数未使用,不会报警告warning,写的好严谨 UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine); // used to start wic /* 这段代码是用于初始化COM库的,使其可以在多线程环境中使用。 CoInitializeEx函数是用来初始化COM库的,第一个参数是一个保留的指针,应该传递为NULL。 第二个参数是初始化选项,COINIT_MULTITHREADED表示COM将被初始化为多线程模型。 这段代码本身没有问题,但是在使用之前应该检查CoInitializeEx的返回值,以确保初始化成功。 如果初始化失败,CoInitializeEx会返回一个HRESULT错误代码,指示具体的错误原因。 */ HRESULT Hr = ::CoInitializeEx(NULL, COINIT_MULTITHREADED); if (FAILED(Hr)) return 0; INT SingleOutput; // Synchronization,void* 类型句柄 HANDLE UnexpectedErrorEvent = nullptr; HANDLE ExpectedErrorEvent = nullptr; HANDLE TerminateThreadsEvent = nullptr; // Window HWND WindowHandle = nullptr; //大概是一个命令行可用性检测?没搜到 bool CmdResult = ProcessCmdline(&SingleOutput); if (!CmdResult) { ShowHelp(); return 0; } // Event used by the threads to signal an unexpected error and we want to quit the app //线程中使用的一个事件,出现未知错误时候 UnexpectedErrorEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr); if (!UnexpectedErrorEvent) { ProcessFailure(nullptr, L"UnexpectedErrorEvent creation failed", L"Error", E_UNEXPECTED); return 0; } // Event for when a thread encounters an expected error //遇到预期错误的时候出现的事件 ExpectedErrorEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr); if (!ExpectedErrorEvent) { ProcessFailure(nullptr, L"ExpectedErrorEvent creation failed", L"Error", E_UNEXPECTED); return 0; } // Event to tell spawned threads to quit //出现了导致线程退出的事件 TerminateThreadsEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr); if (!TerminateThreadsEvent) { ProcessFailure(nullptr, L"TerminateThreadsEvent creation failed", L"Error", E_UNEXPECTED); return 0; } // create and start ansyc save texture thread //创建一个线程。获取gTexDatas中从头开始的数据,逐个保存成BMP格式 StartAnsycSaveTextureThread(); // Load simple cursor //加载光标 HCURSOR Cursor = nullptr; Cursor = LoadCursor(nullptr, IDC_ARROW); if (!Cursor) { ProcessFailure(nullptr, L"Cursor load failed", L"Error", E_UNEXPECTED); return 0; } // Register class WNDCLASSEX 结构用于注册窗口类 WNDCLASSEXW Wc; Wc.cbSize = sizeof(WNDCLASSEXW); Wc.style = CS_HREDRAW | CS_VREDRAW; Wc.lpfnWndProc = WndProc;//窗口处理函数指针,是一个回调,有消息给该窗口,就会触发该函数去处理 Wc.cbClsExtra = 0; Wc.cbWndExtra = 0; Wc.hInstance = hInstance;//模块实例句柄 Wc.hIcon = nullptr; //图标句柄 Wc.hCursor = Cursor; //光标句柄 Wc.hbrBackground = nullptr; //背景画刷的句柄 Wc.lpszMenuName = nullptr; //菜单指针 Wc.lpszClassName = L"ddasample"; Wc.hIconSm = nullptr; //和窗口类关联的小图标。如果该值为NULL。则把hIcon中的图标转换成大小合适的小图标 if (!RegisterClassExW(&Wc)) { ProcessFailure(nullptr, L"Window class registration failed", L"Error", E_UNEXPECTED);//一个抛异常的函数,写的很规范 return 0; } // Create window RECT WindowRect = {0, 0, 800, 600}; //WS_OVERLAPPEDWINDOW可以创建一个拥有各种窗口风格的窗体,包括标题,系统菜单,边框,最小化和最大化按钮等 AdjustWindowRect(&WindowRect, WS_OVERLAPPEDWINDOW, FALSE); // get actual size of desktop RECT actualDesktop; GetWindowRect(GetDesktopWindow(), &actualDesktop); int left; int top; left = ((actualDesktop.right - actualDesktop.left) * 0.75) - (WindowRect.right - WindowRect.left) * 0.5; top = (actualDesktop.bottom - actualDesktop.top) * 0.5 - (WindowRect.bottom - WindowRect.top) * 0.5; WindowHandle = CreateWindowW(L"ddasample", L"DXGI desktop duplication sample", WS_OVERLAPPEDWINDOW, left, top, WindowRect.right - WindowRect.left, WindowRect.bottom - WindowRect.top, nullptr, nullptr, hInstance, nullptr); if (!WindowHandle) { ProcessFailure(nullptr, L"Window creation failed", L"Error", E_FAIL); return 0; } DestroyCursor(Cursor); ShowWindow(WindowHandle, nCmdShow);//展示窗口 UpdateWindow(WindowHandle); //刷新 THREADMANAGER ThreadMgr; RECT DeskBounds; UINT OutputCount; // Message loop (attempts to update screen when no other messages to process) MSG msg = {0}; bool FirstTime = true; bool Occluded = true; DYNAMIC_WAIT DynamicWait; while (WM_QUIT != msg.message)//只要不退出 { DUPL_RETURN Ret = DUPL_RETURN_SUCCESS; //如果消息可得到,返回非零值;如果没有消息可得到,返回值是零。 if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) { if (msg.message == OCCLUSION_STATUS_MSG) { // Present may not be occluded now so try again,遮挡? Occluded = false; } else { // Process window messages TranslateMessage(&msg); DispatchMessage(&msg); } } else if (WaitForSingleObjectEx(UnexpectedErrorEvent, 0, FALSE) == WAIT_OBJECT_0) { // Unexpected error occurred so exit the application break; } else if (FirstTime || WaitForSingleObjectEx(ExpectedErrorEvent, 0, FALSE) == WAIT_OBJECT_0) { if (!FirstTime) { // Terminate other threads SetEvent(TerminateThreadsEvent); ThreadMgr.WaitForThreadTermination(); ResetEvent(TerminateThreadsEvent); ResetEvent(ExpectedErrorEvent); // Clean up ThreadMgr.Clean(); OutMgr.CleanRefs(); // As we have encountered an error due to a system transition we wait before trying again, using this dynamic wait // the wait periods will get progressively long to avoid wasting too much system resource if this state lasts a long time DynamicWait.Wait(); } else { // First time through the loop so nothing to clean up FirstTime = false; } // Re-initialize,设备,适配器,交换链,视图,着色器等创建,绑定 Ret = OutMgr.InitOutput(WindowHandle, SingleOutput, &OutputCount, &DeskBounds); if (Ret == DUPL_RETURN_SUCCESS) { HANDLE SharedHandle = OutMgr.GetSharedHandle(); if (SharedHandle) { //创建多线程(启动DDproc),并指定着色器句柄,处理函数; OutputCount为线程数 Ret = ThreadMgr.Initialize(SingleOutput, OutputCount, UnexpectedErrorEvent, ExpectedErrorEvent, TerminateThreadsEvent, SharedHandle, &DeskBounds); } else { DisplayMsg(L"Failed to get handle of shared surface", L"Error", S_OK); Ret = DUPL_RETURN_ERROR_UNEXPECTED; } } // We start off in occluded state and we should immediate get a occlusion status window message Occluded = true; } else { // Nothing else to do, so try to present to write out to window if not occluded if (!Occluded) { Ret = OutMgr.UpdateApplicationWindow(ThreadMgr.GetPointerInfo(), &Occluded); } } // Check if for errors if (Ret != DUPL_RETURN_SUCCESS) { if (Ret == DUPL_RETURN_ERROR_EXPECTED) { // Some type of system transition is occurring so retry SetEvent(ExpectedErrorEvent); } else { // Unexpected error so exit break; } } } // Make sure all other threads have exited if (SetEvent(TerminateThreadsEvent)) { ThreadMgr.WaitForThreadTermination(); } // close ansyc save texture thread StopAnsycSaveTextureThread(); // Clean up CloseHandle(UnexpectedErrorEvent); CloseHandle(ExpectedErrorEvent); CloseHandle(TerminateThreadsEvent); if (msg.message == WM_QUIT) { // For a WM_QUIT message we should return the wParam value return static_cast<INT>(msg.wParam); } ::CoUninitialize(); return 0; } // // Shows help // void ShowHelp() { DisplayMsg(L"The following optional parameters can be used -\n /output [all | n]\t\tto duplicate all outputs or the nth output\n /?\t\t\tto display this help section", L"Proper usage", S_OK); } // // Process command line parameters // bool ProcessCmdline(_Out_ INT* Output) { *Output = -1; // __argv and __argc are global vars set by system for (UINT i = 1; i < static_cast<UINT>(__argc); ++i) { if ((strcmp(__argv[i], "-output") == 0) || (strcmp(__argv[i], "/output") == 0)) { if (++i >= static_cast<UINT>(__argc)) { return false; } if (strcmp(__argv[i], "all") == 0) { *Output = -1; } else { *Output = atoi(__argv[i]); } continue; } else { return false; } } return true; } // // Window message processor // LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_DESTROY: { PostQuitMessage(0); break; } case WM_SIZE: { // Tell output manager that window size has changed OutMgr.WindowResize(); break; } default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; } // // Entry point for new duplication threads // DWORD WINAPI DDProc(_In_ void* Param) { // Classes DISPLAYMANAGER DispMgr; DUPLICATIONMANAGER DuplMgr; // D3D objects ID3D11Texture2D* SharedSurf = nullptr; IDXGIKeyedMutex* KeyMutex = nullptr; // Data passed in from thread creation THREAD_DATA* TData = reinterpret_cast<THREAD_DATA*>(Param); // Get desktop DUPL_RETURN Ret; HDESK CurrentDesktop = nullptr; CurrentDesktop = OpenInputDesktop(0, FALSE, GENERIC_ALL);//获得当前用户桌面句柄的函数 if (!CurrentDesktop) { // We do not have access to the desktop so request a retry SetEvent(TData->ExpectedErrorEvent); Ret = DUPL_RETURN_ERROR_EXPECTED; goto Exit; } // Attach desktop to this thread 讲桌面句柄附加到当前线程 bool DesktopAttached = SetThreadDesktop(CurrentDesktop) != 0; CloseDesktop(CurrentDesktop); CurrentDesktop = nullptr; if (!DesktopAttached) { // We do not have access to the desktop so request a retry Ret = DUPL_RETURN_ERROR_EXPECTED; goto Exit; } // New display manager DispMgr.InitD3D(&TData->DxRes); // Obtain handle to sync shared Surface 获取同步共享Surface的句柄 HRESULT hr = TData->DxRes.Device->OpenSharedResource(TData->TexSharedHandle, __uuidof(ID3D11Texture2D), reinterpret_cast<void**>(&SharedSurf)); if (FAILED (hr)) { Ret = ProcessFailure(TData->DxRes.Device, L"Opening shared texture failed", L"Error", hr, SystemTransitionsExpectedErrors); goto Exit; } hr = SharedSurf->QueryInterface(__uuidof(IDXGIKeyedMutex), reinterpret_cast<void**>(&KeyMutex)); if (FAILED(hr)) { Ret = ProcessFailure(nullptr, L"Failed to get keyed mutex interface in spawned thread", L"Error", hr); goto Exit; } // Make duplication manager Ret = DuplMgr.InitDupl(TData->DxRes.Device, TData->Output); if (Ret != DUPL_RETURN_SUCCESS) { goto Exit; } // Get output description 描述适配器 (视频卡) 与设备之间的输出或物理连接 DXGI_OUTPUT_DESC DesktopDesc; RtlZeroMemory(&DesktopDesc, sizeof(DXGI_OUTPUT_DESC)); DuplMgr.GetOutputDesc(&DesktopDesc); // Main duplication loop bool WaitToProcessCurrentFrame = false; FRAME_DATA CurrentData; while ((WaitForSingleObjectEx(TData->TerminateThreadsEvent, 0, FALSE) == WAIT_TIMEOUT)) { if (!WaitToProcessCurrentFrame) { // Get new frame from desktop duplication(桌面复制--录屏) bool TimeOut; Ret = DuplMgr.GetFrame(&CurrentData, &TimeOut);//得到一帧桌面数据,里边会设置获取数据的时间间隔和数据信息 if (Ret != DUPL_RETURN_SUCCESS) { // An error occurred getting the next frame drop out of loop which // will check if it was expected or not break; } // Check for timeout if (TimeOut) { // No new frame at the moment continue; } } // We have a new frame so try and process it // Try to acquire keyed mutex in order to access shared surface hr = KeyMutex->AcquireSync(0, 1000); if (hr == static_cast<HRESULT>(WAIT_TIMEOUT)) { // Can't use shared surface right now, try again later WaitToProcessCurrentFrame = true; continue; } else if (FAILED(hr)) { // Generic unknown failure Ret = ProcessFailure(TData->DxRes.Device, L"Unexpected error acquiring KeyMutex", L"Error", hr, SystemTransitionsExpectedErrors); DuplMgr.DoneWithFrame(); break; } // We can now process the current frame WaitToProcessCurrentFrame = false; // Get mouse info 获取鼠标信息(这里表示是否采集鼠标信息) Ret = DuplMgr.GetMouse(TData->PtrInfo, &(CurrentData.FrameInfo), TData->OffsetX, TData->OffsetY); if (Ret != DUPL_RETURN_SUCCESS) { DuplMgr.DoneWithFrame(); KeyMutex->ReleaseSync(1); break; } // Process new frame Ret = DispMgr.ProcessFrame(&CurrentData, SharedSurf, TData->OffsetX, TData->OffsetY, &DesktopDesc); if (Ret != DUPL_RETURN_SUCCESS) { DuplMgr.DoneWithFrame(); KeyMutex->ReleaseSync(1); break; } // Release acquired keyed mutex hr = KeyMutex->ReleaseSync(1); if (FAILED(hr)) { Ret = ProcessFailure(TData->DxRes.Device, L"Unexpected error releasing the keyed mutex", L"Error", hr, SystemTransitionsExpectedErrors); DuplMgr.DoneWithFrame(); break; } // Release frame back to desktop duplication Ret = DuplMgr.DoneWithFrame(); if (Ret != DUPL_RETURN_SUCCESS) { break; } } Exit: if (Ret != DUPL_RETURN_SUCCESS) { if (Ret == DUPL_RETURN_ERROR_EXPECTED) { // The system is in a transition state so request the duplication be restarted SetEvent(TData->ExpectedErrorEvent); } else { // Unexpected error so exit the application SetEvent(TData->UnexpectedErrorEvent); } } if (SharedSurf) { SharedSurf->Release(); SharedSurf = nullptr; } if (KeyMutex) { KeyMutex->Release(); KeyMutex = nullptr; } return 0; } _Post_satisfies_(return != DUPL_RETURN_SUCCESS) DUPL_RETURN ProcessFailure(_In_opt_ ID3D11Device* Device, _In_ LPCWSTR Str, _In_ LPCWSTR Title, HRESULT hr, _In_opt_z_ HRESULT* ExpectedErrors) { HRESULT TranslatedHr; // On an error check if the DX device is lost if (Device) { HRESULT DeviceRemovedReason = Device->GetDeviceRemovedReason(); switch (DeviceRemovedReason) { case DXGI_ERROR_DEVICE_REMOVED : case DXGI_ERROR_DEVICE_RESET : case static_cast<HRESULT>(E_OUTOFMEMORY) : { // Our device has been stopped due to an external event on the GPU so map them all to // device removed and continue processing the condition TranslatedHr = DXGI_ERROR_DEVICE_REMOVED; break; } case S_OK : { // Device is not removed so use original error TranslatedHr = hr; break; } default : { // Device is removed but not a error we want to remap TranslatedHr = DeviceRemovedReason; } } } else { TranslatedHr = hr; } // Check if this error was expected or not if (ExpectedErrors) { HRESULT* CurrentResult = ExpectedErrors; while (*CurrentResult != S_OK) { if (*(CurrentResult++) == TranslatedHr) { return DUPL_RETURN_ERROR_EXPECTED; } } } // Error was not expected so display the message box DisplayMsg(Str, Title, TranslatedHr); return DUPL_RETURN_ERROR_UNEXPECTED; } // // Displays a message // void DisplayMsg(_In_ LPCWSTR Str, _In_ LPCWSTR Title, HRESULT hr) { if (SUCCEEDED(hr)) { MessageBoxW(nullptr, Str, Title, MB_OK); return; } const UINT StringLen = (UINT)(wcslen(Str) + sizeof(" with HRESULT 0x########.")); wchar_t* OutStr = new wchar_t[StringLen]; if (!OutStr) { return; } INT LenWritten = swprintf_s(OutStr, StringLen, L"%s with 0x%X.", Str, hr); if (LenWritten != -1) { MessageBoxW(nullptr, OutStr, Title, MB_OK); } delete [] OutStr; }
获取屏幕数据和鼠标数据;cpp
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A // PARTICULAR PURPOSE. // // Copyright (c) Microsoft Corporation. All rights reserved #include "DuplicationManager.h" // // Constructor sets up references / variables // DUPLICATIONMANAGER::DUPLICATIONMANAGER() : m_DeskDupl(nullptr), m_AcquiredDesktopImage(nullptr), m_MetaDataBuffer(nullptr), m_MetaDataSize(0), m_OutputNumber(0), m_Device(nullptr) { RtlZeroMemory(&m_OutputDesc, sizeof(m_OutputDesc)); } // // Destructor simply calls CleanRefs to destroy everything // DUPLICATIONMANAGER::~DUPLICATIONMANAGER() { if (m_DeskDupl) { m_DeskDupl->Release(); m_DeskDupl = nullptr; } if (m_AcquiredDesktopImage) { m_AcquiredDesktopImage->Release(); m_AcquiredDesktopImage = nullptr; } if (m_MetaDataBuffer) { delete [] m_MetaDataBuffer; m_MetaDataBuffer = nullptr; } if (m_Device) { m_Device->Release(); m_Device = nullptr; } } // // Initialize duplication interfaces // DUPL_RETURN DUPLICATIONMANAGER::InitDupl(_In_ ID3D11Device* Device, UINT Output) { m_OutputNumber = Output; // Take a reference on the device m_Device = Device; m_Device->AddRef(); // Get DXGI device IDXGIDevice* DxgiDevice = nullptr; HRESULT hr = m_Device->QueryInterface(__uuidof(IDXGIDevice), reinterpret_cast<void**>(&DxgiDevice)); if (FAILED(hr)) { return ProcessFailure(nullptr, L"Failed to QI for DXGI Device", L"Error", hr); } // Get DXGI adapter IDXGIAdapter* DxgiAdapter = nullptr; hr = DxgiDevice->GetParent(__uuidof(IDXGIAdapter), reinterpret_cast<void**>(&DxgiAdapter)); DxgiDevice->Release(); DxgiDevice = nullptr; if (FAILED(hr)) { return ProcessFailure(m_Device, L"Failed to get parent DXGI Adapter", L"Error", hr, SystemTransitionsExpectedErrors); } // Get output IDXGIOutput* DxgiOutput = nullptr; hr = DxgiAdapter->EnumOutputs(Output, &DxgiOutput); DxgiAdapter->Release(); DxgiAdapter = nullptr; if (FAILED(hr)) { return ProcessFailure(m_Device, L"Failed to get specified output in DUPLICATIONMANAGER", L"Error", hr, EnumOutputsExpectedErrors); } DxgiOutput->GetDesc(&m_OutputDesc); // QI for Output 1 IDXGIOutput1* DxgiOutput1 = nullptr; hr = DxgiOutput->QueryInterface(__uuidof(DxgiOutput1), reinterpret_cast<void**>(&DxgiOutput1)); DxgiOutput->Release(); DxgiOutput = nullptr; if (FAILED(hr)) { return ProcessFailure(nullptr, L"Failed to QI for DxgiOutput1 in DUPLICATIONMANAGER", L"Error", hr); } // Create desktop duplication hr = DxgiOutput1->DuplicateOutput(m_Device, &m_DeskDupl); DxgiOutput1->Release(); DxgiOutput1 = nullptr; if (FAILED(hr)) { if (hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE) { MessageBoxW(nullptr, L"There is already the maximum number of applications using the Desktop Duplication API running, please close one of those applications and then try again.", L"Error", MB_OK); return DUPL_RETURN_ERROR_UNEXPECTED; } return ProcessFailure(m_Device, L"Failed to get duplicate output in DUPLICATIONMANAGER", L"Error", hr, CreateDuplicationExpectedErrors); } return DUPL_RETURN_SUCCESS; } // // Retrieves mouse info and write it into PtrInfo // DUPL_RETURN DUPLICATIONMANAGER::GetMouse(_Inout_ PTR_INFO* PtrInfo, _In_ DXGI_OUTDUPL_FRAME_INFO* FrameInfo, INT OffsetX, INT OffsetY) { // A non-zero mouse update timestamp indicates that there is a mouse position update and optionally a shape change if (FrameInfo->LastMouseUpdateTime.QuadPart == 0) { return DUPL_RETURN_SUCCESS; } bool UpdatePosition = true; // Make sure we don't update pointer position wrongly // If pointer is invisible, make sure we did not get an update from another output that the last time that said pointer // was visible, if so, don't set it to invisible or update. if (!FrameInfo->PointerPosition.Visible && (PtrInfo->WhoUpdatedPositionLast != m_OutputNumber)) { UpdatePosition = false; } // If two outputs both say they have a visible, only update if new update has newer timestamp if (FrameInfo->PointerPosition.Visible && PtrInfo->Visible && (PtrInfo->WhoUpdatedPositionLast != m_OutputNumber) && (PtrInfo->LastTimeStamp.QuadPart > FrameInfo->LastMouseUpdateTime.QuadPart)) { UpdatePosition = false; } // Update position if (UpdatePosition) { PtrInfo->Position.x = FrameInfo->PointerPosition.Position.x + m_OutputDesc.DesktopCoordinates.left - OffsetX; PtrInfo->Position.y = FrameInfo->PointerPosition.Position.y + m_OutputDesc.DesktopCoordinates.top - OffsetY; PtrInfo->WhoUpdatedPositionLast = m_OutputNumber; PtrInfo->LastTimeStamp = FrameInfo->LastMouseUpdateTime; PtrInfo->Visible = FrameInfo->PointerPosition.Visible != 0; } // No new shape if (FrameInfo->PointerShapeBufferSize == 0) { return DUPL_RETURN_SUCCESS; } // Old buffer too small if (FrameInfo->PointerShapeBufferSize > PtrInfo->BufferSize) { if (PtrInfo->PtrShapeBuffer) { delete [] PtrInfo->PtrShapeBuffer; PtrInfo->PtrShapeBuffer = nullptr; } PtrInfo->PtrShapeBuffer = new (std::nothrow) BYTE[FrameInfo->PointerShapeBufferSize]; if (!PtrInfo->PtrShapeBuffer) { PtrInfo->BufferSize = 0; return ProcessFailure(nullptr, L"Failed to allocate memory for pointer shape in DUPLICATIONMANAGER", L"Error", E_OUTOFMEMORY); } // Update buffer size PtrInfo->BufferSize = FrameInfo->PointerShapeBufferSize; } // Get shape UINT BufferSizeRequired; HRESULT hr = m_DeskDupl->GetFramePointerShape(FrameInfo->PointerShapeBufferSize, reinterpret_cast<VOID*>(PtrInfo->PtrShapeBuffer), &BufferSizeRequired, &(PtrInfo->ShapeInfo)); if (FAILED(hr)) { delete [] PtrInfo->PtrShapeBuffer; PtrInfo->PtrShapeBuffer = nullptr; PtrInfo->BufferSize = 0; return ProcessFailure(m_Device, L"Failed to get frame pointer shape in DUPLICATIONMANAGER", L"Error", hr, FrameInfoExpectedErrors); } return DUPL_RETURN_SUCCESS; } // // Get next frame and write it into Data // _Success_(*Timeout == false && return == DUPL_RETURN_SUCCESS) DUPL_RETURN DUPLICATIONMANAGER::GetFrame(_Out_ FRAME_DATA* Data, _Out_ bool* Timeout) { IDXGIResource* DesktopResource = nullptr; DXGI_OUTDUPL_FRAME_INFO FrameInfo; // Get new frame //1超时间隔(以毫秒为单位)。 此间隔指定此方法在返回给调用方之前等待新帧的时间。 如果间隔已过,并且新的桌面映像不可用,则此方法返回 。 //2指向内存位置的指针,该内存位置接收描述帧的计时和呈现统计信息 的DXGI_OUTDUPL_FRAME_INFO 结构。 //3 指向一个变量的指针,该变量接收包含桌面位图的图面的 IDXGIResource 接口 HRESULT hr = m_DeskDupl->AcquireNextFrame(500, &FrameInfo, &DesktopResource); if (hr == DXGI_ERROR_WAIT_TIMEOUT) { *Timeout = true; return DUPL_RETURN_SUCCESS; } *Timeout = false; if (FAILED(hr)) { return ProcessFailure(m_Device, L"Failed to acquire next frame in DUPLICATIONMANAGER", L"Error", hr, FrameInfoExpectedErrors); } // If still holding old frame, destroy it if (m_AcquiredDesktopImage) { m_AcquiredDesktopImage->Release(); m_AcquiredDesktopImage = nullptr; } // QI for IDXGIResource hr = DesktopResource->QueryInterface(__uuidof(ID3D11Texture2D), reinterpret_cast<void **>(&m_AcquiredDesktopImage)); DesktopResource->Release(); DesktopResource = nullptr; if (FAILED(hr)) { return ProcessFailure(nullptr, L"Failed to QI for ID3D11Texture2D from acquired IDXGIResource in DUPLICATIONMANAGER", L"Error", hr); } // Get metadata if (FrameInfo.TotalMetadataBufferSize) { // Old buffer too small if (FrameInfo.TotalMetadataBufferSize > m_MetaDataSize) { if (m_MetaDataBuffer) { delete [] m_MetaDataBuffer; m_MetaDataBuffer = nullptr; } m_MetaDataBuffer = new (std::nothrow) BYTE[FrameInfo.TotalMetadataBufferSize]; if (!m_MetaDataBuffer) { m_MetaDataSize = 0; Data->MoveCount = 0; Data->DirtyCount = 0; return ProcessFailure(nullptr, L"Failed to allocate memory for metadata in DUPLICATIONMANAGER", L"Error", E_OUTOFMEMORY); } m_MetaDataSize = FrameInfo.TotalMetadataBufferSize; } UINT BufSize = FrameInfo.TotalMetadataBufferSize; // Get move rectangles 获取有关当前桌面框架的移动矩形的信息。 hr = m_DeskDupl->GetFrameMoveRects(BufSize, reinterpret_cast<DXGI_OUTDUPL_MOVE_RECT*>(m_MetaDataBuffer), &BufSize); if (FAILED(hr)) { Data->MoveCount = 0; Data->DirtyCount = 0; return ProcessFailure(nullptr, L"Failed to get frame move rects in DUPLICATIONMANAGER", L"Error", hr, FrameInfoExpectedErrors); } Data->MoveCount = BufSize / sizeof(DXGI_OUTDUPL_MOVE_RECT); BYTE* DirtyRects = m_MetaDataBuffer + BufSize; BufSize = FrameInfo.TotalMetadataBufferSize - BufSize; // Get dirty rectangles hr = m_DeskDupl->GetFrameDirtyRects(BufSize, reinterpret_cast<RECT*>(DirtyRects), &BufSize); if (FAILED(hr)) { Data->MoveCount = 0; Data->DirtyCount = 0; return ProcessFailure(nullptr, L"Failed to get frame dirty rects in DUPLICATIONMANAGER", L"Error", hr, FrameInfoExpectedErrors); } Data->DirtyCount = BufSize / sizeof(RECT); Data->MetaData = m_MetaDataBuffer; } Data->Frame = m_AcquiredDesktopImage; Data->FrameInfo = FrameInfo; return DUPL_RETURN_SUCCESS; } // // Release frame // DUPL_RETURN DUPLICATIONMANAGER::DoneWithFrame() { HRESULT hr = m_DeskDupl->ReleaseFrame(); if (FAILED(hr)) { return ProcessFailure(m_Device, L"Failed to release frame in DUPLICATIONMANAGER", L"Error", hr, FrameInfoExpectedErrors); } if (m_AcquiredDesktopImage) { m_AcquiredDesktopImage->Release(); m_AcquiredDesktopImage = nullptr; } return DUPL_RETURN_SUCCESS; } // // Gets output desc into DescPtr // void DUPLICATIONMANAGER::GetOutputDesc(_Out_ DXGI_OUTPUT_DESC* DescPtr) { *DescPtr = m_OutputDesc; }
输出配置,交换链,着色器等相关配置初始化
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A // PARTICULAR PURPOSE. // // Copyright (c) Microsoft Corporation. All rights reserved #include "OutputManager.h" using namespace DirectX; // // Constructor NULLs out all pointers & sets appropriate var vals // OUTPUTMANAGER::OUTPUTMANAGER() : m_SwapChain(nullptr), m_Device(nullptr), m_Factory(nullptr), m_DeviceContext(nullptr), m_RTV(nullptr), m_SamplerLinear(nullptr), m_BlendState(nullptr), m_VertexShader(nullptr), m_PixelShader(nullptr), m_InputLayout(nullptr), m_SharedSurf(nullptr), m_KeyMutex(nullptr), m_WindowHandle(nullptr), m_NeedsResize(false), m_OcclusionCookie(0) { } // // Destructor which calls CleanRefs to release all references and memory. // OUTPUTMANAGER::~OUTPUTMANAGER() { CleanRefs(); } // // Indicates that window has been resized. // void OUTPUTMANAGER::WindowResize() { m_NeedsResize = true; } // // Initialize all state // DUPL_RETURN OUTPUTMANAGER::InitOutput(HWND Window, INT SingleOutput, _Out_ UINT* OutCount, _Out_ RECT* DeskBounds) { HRESULT hr; // Store window handle m_WindowHandle = Window; // 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 }; UINT NumFeatureLevels = ARRAYSIZE(FeatureLevels); D3D_FEATURE_LEVEL FeatureLevel; // Create device /* HRESULT D3D11CreateDevice( [in, optional] IDXGIAdapter *pAdapter, D3D_DRIVER_TYPE DriverType, HMODULE Software, UINT Flags, [in, optional] const D3D_FEATURE_LEVEL *pFeatureLevels, UINT FeatureLevels, UINT SDKVersion, [out, optional] ID3D11Device **ppDevice, [out, optional] D3D_FEATURE_LEVEL *pFeatureLevel, [out, optional] ID3D11DeviceContext **ppImmediateContext ); */ /* https://learn.microsoft.com/zh-cn/windows/win32/api/d3d11/nf-d3d11-d3d11createdevice?redirectedfrom=MSDN */ /*pAdapter:指向创建 设备时要使用的视频适配器的指针。 传递 NULL 以使用默认适配器,这是 IDXGIFactory1::EnumAdapters 枚举的第一个适配器。*/ /*D3D_DRIVER_TYPE,表示要创建的驱动程序类型。*/ /*Software:实现软件光栅器的 DLL 的句柄。 如果 DriverType是D3D_DRIVER_TYPE_SOFTWARE, 则软件 不得为 NULL。 通过调用 LoadLibrary、 LoadLibraryEx 或 GetModuleHandle 获取句柄。*/ /* ppDevice,返回指向表示所创建设备的 ID3D11Device 对象的指针的地址。 如果此参数为 NULL,则不会返回 ID3D11Device。*/ /* ppImmediateContext,返回指向表示设备上下文的 ID3D11DeviceContext 对象的指针的地址*/ for (UINT DriverTypeIndex = 0; DriverTypeIndex < NumDriverTypes; ++DriverTypeIndex) { hr = D3D11CreateDevice(nullptr, DriverTypes[DriverTypeIndex], nullptr, 0, FeatureLevels, NumFeatureLevels, D3D11_SDK_VERSION, &m_Device, &FeatureLevel, &m_DeviceContext); if (SUCCEEDED(hr)) { // Device creation succeeded, no need to loop anymore break; } } if (FAILED(hr)) { return ProcessFailure(m_Device, L"Device creation in OUTPUTMANAGER failed", L"Error", hr, SystemTransitionsExpectedErrors); } // Get DXGI factory 接口支持性查询,从设备中得到这个结构体,结构体中各种函数 IDXGIDevice* DxgiDevice = nullptr; hr = m_Device->QueryInterface(__uuidof(IDXGIDevice), reinterpret_cast<void**>(&DxgiDevice)); if (FAILED(hr)) { return ProcessFailure(nullptr, L"Failed to QI for DXGI Device", L"Error", hr, nullptr); } //获取适配器 IDXGIAdapter* DxgiAdapter = nullptr; hr = DxgiDevice->GetParent(__uuidof(IDXGIAdapter), reinterpret_cast<void**>(&DxgiAdapter)); DxgiDevice->Release(); DxgiDevice = nullptr; if (FAILED(hr)) { return ProcessFailure(m_Device, L"Failed to get parent DXGI Adapter", L"Error", hr, SystemTransitionsExpectedErrors); } //IDXGIFactory2 接口包括创建具有比 IDXGISwapChain 更多的功能的较新版本交换链的方法,以及监视立体 3D 功能的方法。 hr = DxgiAdapter->GetParent(__uuidof(IDXGIFactory2), reinterpret_cast<void**>(&m_Factory)); DxgiAdapter->Release(); DxgiAdapter = nullptr; if (FAILED(hr)) { return ProcessFailure(m_Device, L"Failed to get parent DXGI Factory", L"Error", hr, SystemTransitionsExpectedErrors); } // Register for occlusion status windows message //注册应用程序窗口,以接收有关遮挡状态更改的通知消息 hr = m_Factory->RegisterOcclusionStatusWindow(Window, OCCLUSION_STATUS_MSG, &m_OcclusionCookie); if (FAILED(hr)) { return ProcessFailure(m_Device, L"Failed to register for occlusion message", L"Error", hr, SystemTransitionsExpectedErrors); } // Get window size RECT WindowRect; GetClientRect(m_WindowHandle, &WindowRect); UINT Width = WindowRect.right - WindowRect.left; UINT Height = WindowRect.bottom - WindowRect.top; // Create swapchain for window 为窗口创建交换链 DXGI_SWAP_CHAIN_DESC1 SwapChainDesc; RtlZeroMemory(&SwapChainDesc, sizeof(SwapChainDesc)); SwapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; SwapChainDesc.BufferCount = 2; SwapChainDesc.Width = Width; SwapChainDesc.Height = Height; SwapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; SwapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;//这表示交换链将是绘图图面,允许将其用作 Direct3D 呈现器目标 SwapChainDesc.SampleDesc.Count = 1; SwapChainDesc.SampleDesc.Quality = 0; //为DXGI与窗口之间创建交换链,引擎和窗口的连接链条 hr = m_Factory->CreateSwapChainForHwnd(m_Device, Window, &SwapChainDesc, nullptr, nullptr, &m_SwapChain); if (FAILED(hr)) { return ProcessFailure(m_Device, L"Failed to create window swapchain", L"Error", hr, SystemTransitionsExpectedErrors); } // Disable the ALT-ENTER shortcut for entering full-screen mode //禁用ALT-ENTER快捷方式以进入全屏模式 hr = m_Factory->MakeWindowAssociation(Window, DXGI_MWA_NO_ALT_ENTER); if (FAILED(hr)) { return ProcessFailure(m_Device, L"Failed to make window association", L"Error", hr, SystemTransitionsExpectedErrors); } // Create shared texture DUPL_RETURN Return = CreateSharedSurf(SingleOutput, OutCount, DeskBounds); if (Return != DUPL_RETURN_SUCCESS) { return Return; } // Make new render target view,创建呈现目标的视图 Return = MakeRTV(); if (Return != DUPL_RETURN_SUCCESS) { return Return; } // Set view port SetViewPort(Width, Height); // Create the sample state 采样器状态的默认值 D3D11_SAMPLER_DESC SampDesc; RtlZeroMemory(&SampDesc, sizeof(SampDesc)); SampDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR; SampDesc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP; SampDesc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP; SampDesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP; SampDesc.ComparisonFunc = D3D11_COMPARISON_NEVER; SampDesc.MinLOD = 0; SampDesc.MaxLOD = D3D11_FLOAT32_MAX; hr = m_Device->CreateSamplerState(&SampDesc, &m_SamplerLinear); if (FAILED(hr)) { return ProcessFailure(m_Device, L"Failed to create sampler state in OUTPUTMANAGER", L"Error", hr, SystemTransitionsExpectedErrors); } // Create the blend state 混合状态的默认值 D3D11_BLEND_DESC BlendStateDesc; BlendStateDesc.AlphaToCoverageEnable = FALSE; BlendStateDesc.IndependentBlendEnable = FALSE; BlendStateDesc.RenderTarget[0].BlendEnable = TRUE; BlendStateDesc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA; BlendStateDesc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA; BlendStateDesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD; BlendStateDesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE; BlendStateDesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO; BlendStateDesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD; BlendStateDesc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; hr = m_Device->CreateBlendState(&BlendStateDesc, &m_BlendState); if (FAILED(hr)) { return ProcessFailure(m_Device, L"Failed to create blend state in OUTPUTMANAGER", L"Error", hr, SystemTransitionsExpectedErrors); } // Initialize shaders,初始化着色器,创建定点,像素着色器,绑定布局 Return = InitShaders(); if (Return != DUPL_RETURN_SUCCESS) { return Return; } GetWindowRect(m_WindowHandle, &WindowRect); //更改指定窗口的位置和尺寸。 对于顶级窗口,位置和尺寸是相对于屏幕左上角的。 对于子窗口,它们相对于父窗口工作区的左上角。 MoveWindow(m_WindowHandle, WindowRect.left, WindowRect.top, (DeskBounds->right - DeskBounds->left) / 2, (DeskBounds->bottom - DeskBounds->top) / 2, TRUE); return Return; } // // Recreate shared texture // DUPL_RETURN OUTPUTMANAGER::CreateSharedSurf(INT SingleOutput, _Out_ UINT* OutCount, _Out_ RECT* DeskBounds) { HRESULT hr; // Get DXGI resources IDXGIDevice* DxgiDevice = nullptr; hr = m_Device->QueryInterface(__uuidof(IDXGIDevice), reinterpret_cast<void**>(&DxgiDevice)); if (FAILED(hr)) { return ProcessFailure(nullptr, L"Failed to QI for DXGI Device", L"Error", hr); } IDXGIAdapter* DxgiAdapter = nullptr; hr = DxgiDevice->GetParent(__uuidof(IDXGIAdapter), reinterpret_cast<void**>(&DxgiAdapter)); DxgiDevice->Release(); DxgiDevice = nullptr; if (FAILED(hr)) { return ProcessFailure(m_Device, L"Failed to get parent DXGI Adapter", L"Error", hr, SystemTransitionsExpectedErrors); } // Set initial values so that we always catch the right coordinates DeskBounds->left = INT_MAX; DeskBounds->right = INT_MIN; DeskBounds->top = INT_MAX; DeskBounds->bottom = INT_MIN; IDXGIOutput* DxgiOutput = nullptr; // Figure out right dimensions for full size desktop texture and # of outputs to duplicate UINT OutputCount; if (SingleOutput < 0) { hr = S_OK; for (OutputCount = 0; SUCCEEDED(hr); ++OutputCount) { if (DxgiOutput) { DxgiOutput->Release(); DxgiOutput = nullptr; } //枚举适配器 (视频卡) 输出 hr = DxgiAdapter->EnumOutputs(OutputCount, &DxgiOutput); if (DxgiOutput && (hr != DXGI_ERROR_NOT_FOUND)) { DXGI_OUTPUT_DESC DesktopDesc; DxgiOutput->GetDesc(&DesktopDesc); DeskBounds->left = min(DesktopDesc.DesktopCoordinates.left, DeskBounds->left); DeskBounds->top = min(DesktopDesc.DesktopCoordinates.top, DeskBounds->top); DeskBounds->right = max(DesktopDesc.DesktopCoordinates.right, DeskBounds->right); DeskBounds->bottom = max(DesktopDesc.DesktopCoordinates.bottom, DeskBounds->bottom); } } --OutputCount; } else { //适配器个人理解应该是电脑连接显示屏的数据线路的数量 hr = DxgiAdapter->EnumOutputs(SingleOutput, &DxgiOutput); if (FAILED(hr)) { DxgiAdapter->Release(); DxgiAdapter = nullptr; return ProcessFailure(m_Device, L"Output specified to be duplicated does not exist", L"Error", hr); } //描述适配器 (视频卡) 与设备之间的输出或物理连接 DXGI_OUTPUT_DESC DesktopDesc; //包含输出设备名称的字符串,桌面坐标中输出边界的 RECT 结构。 桌面坐标取决于桌面的每英寸点数 (DPI) . //AttachedToDesktop如果输出附加到桌面,则为 True;否则为 false //Rotation DXGI_MODE_ROTATION枚举类型的成员,描述输出如何旋转图像。 //monitor 表示显示监视器的 HMONITOR 句柄 DxgiOutput->GetDesc(&DesktopDesc); *DeskBounds = DesktopDesc.DesktopCoordinates; DxgiOutput->Release(); DxgiOutput = nullptr; OutputCount = 1; } DxgiAdapter->Release(); DxgiAdapter = nullptr; // Set passed in output count variable *OutCount = OutputCount; if (OutputCount == 0) { // We could not find any outputs, the system must be in a transition so return expected error // so we will attempt to recreate return DUPL_RETURN_ERROR_EXPECTED; } // Create shared texture for all duplication threads to draw into //2D 纹理描述 D3D11_TEXTURE2D_DESC DeskTexD; RtlZeroMemory(&DeskTexD, sizeof(D3D11_TEXTURE2D_DESC)); DeskTexD.Width = DeskBounds->right - DeskBounds->left; DeskTexD.Height = DeskBounds->bottom - DeskBounds->top; DeskTexD.MipLevels = 1;//纹理中的最大 mipmap 级别数 //纹理数组中的纹理数。 范围为 1 到 D3D11_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION (2048) 。 //对于纹理立方体贴图,此值是 6 (的倍数,即 D3D11_TEXCUBE_ARRAY_SRV) 的 NumCubes 成员中的值的 6 倍,范围为 6 到 2046 DeskTexD.ArraySize = 1; DeskTexD.Format = DXGI_FORMAT_B8G8R8A8_UNORM; DeskTexD.SampleDesc.Count = 1; DeskTexD.Usage = D3D11_USAGE_DEFAULT;//该值标识如何读取和写入纹理 DeskTexD.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE; DeskTexD.CPUAccessFlags = 0;//来指定允许的 CPU 访问类型的标志, 如果不需要 CPU 访问,请使用 0。 这些标志可以与按位 OR 组合使用 DeskTexD.MiscFlags = D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX;//不太常见的资源选项的标识 //1.指向描述 2D 纹理资源的 D3D11_TEXTURE2D_DESC 结构的指针。 若要创建可在运行时解释为不同兼容格式的无类型资源,请在纹理说明中指定无类型格式 //2.指向描述 2D 纹理资源的子资源的 D3D11_SUBRESOURCE_DATA 结构的数组的指针。 创建 IMMUTABLE 资源时,应用程序无法为 pInitialData 指定 NULL //3.指向缓冲区的指针,该缓冲区接收指向所创建纹理的 ID3D11Texture2D 接口的指针 hr = m_Device->CreateTexture2D(&DeskTexD, nullptr, &m_SharedSurf); if (FAILED(hr)) { if (OutputCount != 1) { // If we are duplicating the complete desktop we try to create a single texture to hold the // complete desktop image and blit updates from the per output DDA interface. The GPU can // always support a texture size of the maximum resolution of any single output but there is no // guarantee that it can support a texture size of the desktop. // The sample only use this large texture to display the desktop image in a single window using DX // we could revert back to using GDI to update the window in this failure case. return ProcessFailure(m_Device, L"Failed to create DirectX shared texture - we are attempting to create a texture the size of the complete desktop and this may be larger than the maximum texture size of your GPU. Please try again using the -output command line parameter to duplicate only 1 monitor or configure your computer to a single monitor configuration", L"Error", hr, SystemTransitionsExpectedErrors); } else { return ProcessFailure(m_Device, L"Failed to create shared texture", L"Error", hr, SystemTransitionsExpectedErrors); } } // Get keyed mutex hr = m_SharedSurf->QueryInterface(__uuidof(IDXGIKeyedMutex), reinterpret_cast<void**>(&m_KeyMutex)); if (FAILED(hr)) { return ProcessFailure(m_Device, L"Failed to query for keyed mutex in OUTPUTMANAGER", L"Error", hr); } return DUPL_RETURN_SUCCESS; } // // Present to the application window // DUPL_RETURN OUTPUTMANAGER::UpdateApplicationWindow(_In_ PTR_INFO* PointerInfo, _Inout_ bool* Occluded) { // In a typical desktop duplication application there would be an application running on one system collecting the desktop images // and another application running on a different system that receives the desktop images via a network and display the image. This // sample contains both these aspects into a single application. // This routine is the part of the sample that displays the desktop image onto the display // Try and acquire sync on common display buffer HRESULT hr = m_KeyMutex->AcquireSync(1, 100); if (hr == static_cast<HRESULT>(WAIT_TIMEOUT)) { // Another thread has the keyed mutex so try again later return DUPL_RETURN_SUCCESS; } else if (FAILED(hr)) { return ProcessFailure(m_Device, L"Failed to acquire Keyed mutex in OUTPUTMANAGER", L"Error", hr, SystemTransitionsExpectedErrors); } // Got mutex, so draw DUPL_RETURN Ret = DrawFrame(); if (Ret == DUPL_RETURN_SUCCESS) { // We have keyed mutex so we can access the mouse info if (PointerInfo->Visible) { // Draw mouse into texture Ret = DrawMouse(PointerInfo); } } // Release keyed mutex hr = m_KeyMutex->ReleaseSync(0); if (FAILED(hr)) { return ProcessFailure(m_Device, L"Failed to Release Keyed mutex in OUTPUTMANAGER", L"Error", hr, SystemTransitionsExpectedErrors); } // Present to window if all worked if (Ret == DUPL_RETURN_SUCCESS) { // Present to window hr = m_SwapChain->Present(1, 0); if (FAILED(hr)) { return ProcessFailure(m_Device, L"Failed to present", L"Error", hr, SystemTransitionsExpectedErrors); } else if (hr == DXGI_STATUS_OCCLUDED) { *Occluded = true; } } return Ret; } // // Returns shared handle // HANDLE OUTPUTMANAGER::GetSharedHandle() { HANDLE Hnd = nullptr; // QI IDXGIResource interface to synchronized shared surface. IDXGIResource* DXGIResource = nullptr; HRESULT hr = m_SharedSurf->QueryInterface(__uuidof(IDXGIResource), reinterpret_cast<void**>(&DXGIResource)); if (SUCCEEDED(hr)) { // Obtain handle to IDXGIResource object. DXGIResource->GetSharedHandle(&Hnd); DXGIResource->Release(); DXGIResource = nullptr; } return Hnd; } // // Draw frame into backbuffer // DUPL_RETURN OUTPUTMANAGER::DrawFrame() { HRESULT hr; // If window was resized, resize swapchain if (m_NeedsResize) { DUPL_RETURN Ret = ResizeSwapChain(); if (Ret != DUPL_RETURN_SUCCESS) { return Ret; } m_NeedsResize = false; } // Vertices for drawing whole texture VERTEX Vertices[NUMVERTICES] = { {XMFLOAT3(-1.0f, -1.0f, 0), XMFLOAT2(0.0f, 1.0f)}, {XMFLOAT3(-1.0f, 1.0f, 0), XMFLOAT2(0.0f, 0.0f)}, {XMFLOAT3(1.0f, -1.0f, 0), XMFLOAT2(1.0f, 1.0f)}, {XMFLOAT3(1.0f, -1.0f, 0), XMFLOAT2(1.0f, 1.0f)}, {XMFLOAT3(-1.0f, 1.0f, 0), XMFLOAT2(0.0f, 0.0f)}, {XMFLOAT3(1.0f, 1.0f, 0), XMFLOAT2(1.0f, 0.0f)}, }; D3D11_TEXTURE2D_DESC FrameDesc; m_SharedSurf->GetDesc(&FrameDesc); D3D11_SHADER_RESOURCE_VIEW_DESC ShaderDesc; ShaderDesc.Format = FrameDesc.Format; ShaderDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; ShaderDesc.Texture2D.MostDetailedMip = FrameDesc.MipLevels - 1; ShaderDesc.Texture2D.MipLevels = FrameDesc.MipLevels; // Create new shader resource view ID3D11ShaderResourceView* ShaderResource = nullptr; hr = m_Device->CreateShaderResourceView(m_SharedSurf, &ShaderDesc, &ShaderResource); if (FAILED(hr)) { return ProcessFailure(m_Device, L"Failed to create shader resource when drawing a frame", L"Error", hr, SystemTransitionsExpectedErrors); } // Set resources UINT Stride = sizeof(VERTEX); UINT Offset = 0; FLOAT blendFactor[4] = {0.f, 0.f, 0.f, 0.f}; m_DeviceContext->OMSetBlendState(nullptr, blendFactor, 0xffffffff); m_DeviceContext->OMSetRenderTargets(1, &m_RTV, nullptr); m_DeviceContext->VSSetShader(m_VertexShader, nullptr, 0); m_DeviceContext->PSSetShader(m_PixelShader, nullptr, 0); m_DeviceContext->PSSetShaderResources(0, 1, &ShaderResource); m_DeviceContext->PSSetSamplers(0, 1, &m_SamplerLinear); m_DeviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); D3D11_BUFFER_DESC BufferDesc; RtlZeroMemory(&BufferDesc, sizeof(BufferDesc)); BufferDesc.Usage = D3D11_USAGE_DEFAULT; BufferDesc.ByteWidth = sizeof(VERTEX) * NUMVERTICES; BufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER; BufferDesc.CPUAccessFlags = 0; D3D11_SUBRESOURCE_DATA InitData; RtlZeroMemory(&InitData, sizeof(InitData)); InitData.pSysMem = Vertices; ID3D11Buffer* VertexBuffer = nullptr; // Create vertex buffer hr = m_Device->CreateBuffer(&BufferDesc, &InitData, &VertexBuffer); if (FAILED(hr)) { ShaderResource->Release(); ShaderResource = nullptr; return ProcessFailure(m_Device, L"Failed to create vertex buffer when drawing a frame", L"Error", hr, SystemTransitionsExpectedErrors); } m_DeviceContext->IASetVertexBuffers(0, 1, &VertexBuffer, &Stride, &Offset); // Draw textured quad onto render target m_DeviceContext->Draw(NUMVERTICES, 0); VertexBuffer->Release(); VertexBuffer = nullptr; // Release shader resource ShaderResource->Release(); ShaderResource = nullptr; return DUPL_RETURN_SUCCESS; } // // Process both masked and monochrome pointers // DUPL_RETURN OUTPUTMANAGER::ProcessMonoMask(bool IsMono, _Inout_ PTR_INFO* PtrInfo, _Out_ INT* PtrWidth, _Out_ INT* PtrHeight, _Out_ INT* PtrLeft, _Out_ INT* PtrTop, _Outptr_result_bytebuffer_(*PtrHeight * *PtrWidth * BPP) BYTE** InitBuffer, _Out_ D3D11_BOX* Box) { // Desktop dimensions D3D11_TEXTURE2D_DESC FullDesc; m_SharedSurf->GetDesc(&FullDesc); INT DesktopWidth = FullDesc.Width; INT DesktopHeight = FullDesc.Height; // Pointer position INT GivenLeft = PtrInfo->Position.x; INT GivenTop = PtrInfo->Position.y; // Figure out if any adjustment is needed for out of bound positions if (GivenLeft < 0) { *PtrWidth = GivenLeft + static_cast<INT>(PtrInfo->ShapeInfo.Width); } else if ((GivenLeft + static_cast<INT>(PtrInfo->ShapeInfo.Width)) > DesktopWidth) { *PtrWidth = DesktopWidth - GivenLeft; } else { *PtrWidth = static_cast<INT>(PtrInfo->ShapeInfo.Width); } if (IsMono) { PtrInfo->ShapeInfo.Height = PtrInfo->ShapeInfo.Height / 2; } if (GivenTop < 0) { *PtrHeight = GivenTop + static_cast<INT>(PtrInfo->ShapeInfo.Height); } else if ((GivenTop + static_cast<INT>(PtrInfo->ShapeInfo.Height)) > DesktopHeight) { *PtrHeight = DesktopHeight - GivenTop; } else { *PtrHeight = static_cast<INT>(PtrInfo->ShapeInfo.Height); } if (IsMono) { PtrInfo->ShapeInfo.Height = PtrInfo->ShapeInfo.Height * 2; } *PtrLeft = (GivenLeft < 0) ? 0 : GivenLeft; *PtrTop = (GivenTop < 0) ? 0 : GivenTop; // Staging buffer/texture D3D11_TEXTURE2D_DESC CopyBufferDesc; CopyBufferDesc.Width = *PtrWidth; CopyBufferDesc.Height = *PtrHeight; CopyBufferDesc.MipLevels = 1; CopyBufferDesc.ArraySize = 1; CopyBufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; CopyBufferDesc.SampleDesc.Count = 1; CopyBufferDesc.SampleDesc.Quality = 0; CopyBufferDesc.Usage = D3D11_USAGE_STAGING; CopyBufferDesc.BindFlags = 0; CopyBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; CopyBufferDesc.MiscFlags = 0; ID3D11Texture2D* CopyBuffer = nullptr; HRESULT hr = m_Device->CreateTexture2D(&CopyBufferDesc, nullptr, &CopyBuffer); if (FAILED(hr)) { return ProcessFailure(m_Device, L"Failed creating staging texture for pointer", L"Error", hr, SystemTransitionsExpectedErrors); } // Copy needed part of desktop image Box->left = *PtrLeft; Box->top = *PtrTop; Box->right = *PtrLeft + *PtrWidth; Box->bottom = *PtrTop + *PtrHeight; m_DeviceContext->CopySubresourceRegion(CopyBuffer, 0, 0, 0, 0, m_SharedSurf, 0, Box); // QI for IDXGISurface IDXGISurface* CopySurface = nullptr; hr = CopyBuffer->QueryInterface(__uuidof(IDXGISurface), (void **)&CopySurface); CopyBuffer->Release(); CopyBuffer = nullptr; if (FAILED(hr)) { return ProcessFailure(nullptr, L"Failed to QI staging texture into IDXGISurface for pointer", L"Error", hr, SystemTransitionsExpectedErrors); } // Map pixels DXGI_MAPPED_RECT MappedSurface; hr = CopySurface->Map(&MappedSurface, DXGI_MAP_READ); if (FAILED(hr)) { CopySurface->Release(); CopySurface = nullptr; return ProcessFailure(m_Device, L"Failed to map surface for pointer", L"Error", hr, SystemTransitionsExpectedErrors); } // New mouseshape buffer *InitBuffer = new (std::nothrow) BYTE[*PtrWidth * *PtrHeight * BPP]; if (!(*InitBuffer)) { return ProcessFailure(nullptr, L"Failed to allocate memory for new mouse shape buffer.", L"Error", E_OUTOFMEMORY); } UINT* InitBuffer32 = reinterpret_cast<UINT*>(*InitBuffer); UINT* Desktop32 = reinterpret_cast<UINT*>(MappedSurface.pBits); UINT DesktopPitchInPixels = MappedSurface.Pitch / sizeof(UINT); // What to skip (pixel offset) UINT SkipX = (GivenLeft < 0) ? (-1 * GivenLeft) : (0); UINT SkipY = (GivenTop < 0) ? (-1 * GivenTop) : (0); if (IsMono) { for (INT Row = 0; Row < *PtrHeight; ++Row) { // Set mask BYTE Mask = 0x80; Mask = Mask >> (SkipX % 8); for (INT Col = 0; Col < *PtrWidth; ++Col) { // Get masks using appropriate offsets BYTE AndMask = PtrInfo->PtrShapeBuffer[((Col + SkipX) / 8) + ((Row + SkipY) * (PtrInfo->ShapeInfo.Pitch))] & Mask; BYTE XorMask = PtrInfo->PtrShapeBuffer[((Col + SkipX) / 8) + ((Row + SkipY + (PtrInfo->ShapeInfo.Height / 2)) * (PtrInfo->ShapeInfo.Pitch))] & Mask; UINT AndMask32 = (AndMask) ? 0xFFFFFFFF : 0xFF000000; UINT XorMask32 = (XorMask) ? 0x00FFFFFF : 0x00000000; // Set new pixel InitBuffer32[(Row * *PtrWidth) + Col] = (Desktop32[(Row * DesktopPitchInPixels) + Col] & AndMask32) ^ XorMask32; // Adjust mask if (Mask == 0x01) { Mask = 0x80; } else { Mask = Mask >> 1; } } } } else { UINT* Buffer32 = reinterpret_cast<UINT*>(PtrInfo->PtrShapeBuffer); // Iterate through pixels for (INT Row = 0; Row < *PtrHeight; ++Row) { for (INT Col = 0; Col < *PtrWidth; ++Col) { // Set up mask UINT MaskVal = 0xFF000000 & Buffer32[(Col + SkipX) + ((Row + SkipY) * (PtrInfo->ShapeInfo.Pitch / sizeof(UINT)))]; if (MaskVal) { // Mask was 0xFF InitBuffer32[(Row * *PtrWidth) + Col] = (Desktop32[(Row * DesktopPitchInPixels) + Col] ^ Buffer32[(Col + SkipX) + ((Row + SkipY) * (PtrInfo->ShapeInfo.Pitch / sizeof(UINT)))]) | 0xFF000000; } else { // Mask was 0x00 InitBuffer32[(Row * *PtrWidth) + Col] = Buffer32[(Col + SkipX) + ((Row + SkipY) * (PtrInfo->ShapeInfo.Pitch / sizeof(UINT)))] | 0xFF000000; } } } } // Done with resource hr = CopySurface->Unmap(); CopySurface->Release(); CopySurface = nullptr; if (FAILED(hr)) { return ProcessFailure(m_Device, L"Failed to unmap surface for pointer", L"Error", hr, SystemTransitionsExpectedErrors); } return DUPL_RETURN_SUCCESS; } // // Draw mouse provided in buffer to backbuffer // DUPL_RETURN OUTPUTMANAGER::DrawMouse(_In_ PTR_INFO* PtrInfo) { // Vars to be used ID3D11Texture2D* MouseTex = nullptr; ID3D11ShaderResourceView* ShaderRes = nullptr; ID3D11Buffer* VertexBufferMouse = nullptr; D3D11_SUBRESOURCE_DATA InitData; D3D11_TEXTURE2D_DESC Desc; D3D11_SHADER_RESOURCE_VIEW_DESC SDesc; // Position will be changed based on mouse position VERTEX Vertices[NUMVERTICES] = { {XMFLOAT3(-1.0f, -1.0f, 0), XMFLOAT2(0.0f, 1.0f)}, {XMFLOAT3(-1.0f, 1.0f, 0), XMFLOAT2(0.0f, 0.0f)}, {XMFLOAT3(1.0f, -1.0f, 0), XMFLOAT2(1.0f, 1.0f)}, {XMFLOAT3(1.0f, -1.0f, 0), XMFLOAT2(1.0f, 1.0f)}, {XMFLOAT3(-1.0f, 1.0f, 0), XMFLOAT2(0.0f, 0.0f)}, {XMFLOAT3(1.0f, 1.0f, 0), XMFLOAT2(1.0f, 0.0f)}, }; D3D11_TEXTURE2D_DESC FullDesc; m_SharedSurf->GetDesc(&FullDesc); INT DesktopWidth = FullDesc.Width; INT DesktopHeight = FullDesc.Height; // Center of desktop dimensions INT CenterX = (DesktopWidth / 2); INT CenterY = (DesktopHeight / 2); // Clipping adjusted coordinates / dimensions INT PtrWidth = 0; INT PtrHeight = 0; INT PtrLeft = 0; INT PtrTop = 0; // Buffer used if necessary (in case of monochrome or masked pointer) BYTE* InitBuffer = nullptr; // Used for copying pixels D3D11_BOX Box; Box.front = 0; Box.back = 1; Desc.MipLevels = 1; Desc.ArraySize = 1; Desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; Desc.SampleDesc.Count = 1; Desc.SampleDesc.Quality = 0; Desc.Usage = D3D11_USAGE_DEFAULT; Desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; Desc.CPUAccessFlags = 0; Desc.MiscFlags = 0; // Set shader resource properties SDesc.Format = Desc.Format; SDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; SDesc.Texture2D.MostDetailedMip = Desc.MipLevels - 1; SDesc.Texture2D.MipLevels = Desc.MipLevels; switch (PtrInfo->ShapeInfo.Type) { case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR: { PtrLeft = PtrInfo->Position.x; PtrTop = PtrInfo->Position.y; PtrWidth = static_cast<INT>(PtrInfo->ShapeInfo.Width); PtrHeight = static_cast<INT>(PtrInfo->ShapeInfo.Height); break; } case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME: { ProcessMonoMask(true, PtrInfo, &PtrWidth, &PtrHeight, &PtrLeft, &PtrTop, &InitBuffer, &Box); break; } case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR: { ProcessMonoMask(false, PtrInfo, &PtrWidth, &PtrHeight, &PtrLeft, &PtrTop, &InitBuffer, &Box); break; } default: break; } // VERTEX creation Vertices[0].Pos.x = (PtrLeft - CenterX) / (FLOAT)CenterX; Vertices[0].Pos.y = -1 * ((PtrTop + PtrHeight) - CenterY) / (FLOAT)CenterY; Vertices[1].Pos.x = (PtrLeft - CenterX) / (FLOAT)CenterX; Vertices[1].Pos.y = -1 * (PtrTop - CenterY) / (FLOAT)CenterY; Vertices[2].Pos.x = ((PtrLeft + PtrWidth) - CenterX) / (FLOAT)CenterX; Vertices[2].Pos.y = -1 * ((PtrTop + PtrHeight) - CenterY) / (FLOAT)CenterY; Vertices[3].Pos.x = Vertices[2].Pos.x; Vertices[3].Pos.y = Vertices[2].Pos.y; Vertices[4].Pos.x = Vertices[1].Pos.x; Vertices[4].Pos.y = Vertices[1].Pos.y; Vertices[5].Pos.x = ((PtrLeft + PtrWidth) - CenterX) / (FLOAT)CenterX; Vertices[5].Pos.y = -1 * (PtrTop - CenterY) / (FLOAT)CenterY; // Set texture properties Desc.Width = PtrWidth; Desc.Height = PtrHeight; // Set up init data InitData.pSysMem = (PtrInfo->ShapeInfo.Type == DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR) ? PtrInfo->PtrShapeBuffer : InitBuffer; InitData.SysMemPitch = (PtrInfo->ShapeInfo.Type == DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR) ? PtrInfo->ShapeInfo.Pitch : PtrWidth * BPP; InitData.SysMemSlicePitch = 0; // Create mouseshape as texture HRESULT hr = m_Device->CreateTexture2D(&Desc, &InitData, &MouseTex); if (FAILED(hr)) { return ProcessFailure(m_Device, L"Failed to create mouse pointer texture", L"Error", hr, SystemTransitionsExpectedErrors); } // Create shader resource from texture hr = m_Device->CreateShaderResourceView(MouseTex, &SDesc, &ShaderRes); if (FAILED(hr)) { MouseTex->Release(); MouseTex = nullptr; return ProcessFailure(m_Device, L"Failed to create shader resource from mouse pointer texture", L"Error", hr, SystemTransitionsExpectedErrors); } D3D11_BUFFER_DESC BDesc; ZeroMemory(&BDesc, sizeof(D3D11_BUFFER_DESC)); BDesc.Usage = D3D11_USAGE_DEFAULT; BDesc.ByteWidth = sizeof(VERTEX) * NUMVERTICES; BDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER; BDesc.CPUAccessFlags = 0; ZeroMemory(&InitData, sizeof(D3D11_SUBRESOURCE_DATA)); InitData.pSysMem = Vertices; // Create vertex buffer hr = m_Device->CreateBuffer(&BDesc, &InitData, &VertexBufferMouse); if (FAILED(hr)) { ShaderRes->Release(); ShaderRes = nullptr; MouseTex->Release(); MouseTex = nullptr; return ProcessFailure(m_Device, L"Failed to create mouse pointer vertex buffer in OutputManager", L"Error", hr, SystemTransitionsExpectedErrors); } // Set resources FLOAT BlendFactor[4] = {0.f, 0.f, 0.f, 0.f}; UINT Stride = sizeof(VERTEX); UINT Offset = 0; m_DeviceContext->IASetVertexBuffers(0, 1, &VertexBufferMouse, &Stride, &Offset); m_DeviceContext->OMSetBlendState(m_BlendState, BlendFactor, 0xFFFFFFFF); m_DeviceContext->OMSetRenderTargets(1, &m_RTV, nullptr); m_DeviceContext->VSSetShader(m_VertexShader, nullptr, 0); m_DeviceContext->PSSetShader(m_PixelShader, nullptr, 0); m_DeviceContext->PSSetShaderResources(0, 1, &ShaderRes); m_DeviceContext->PSSetSamplers(0, 1, &m_SamplerLinear); // Draw m_DeviceContext->Draw(NUMVERTICES, 0); // Clean if (VertexBufferMouse) { VertexBufferMouse->Release(); VertexBufferMouse = nullptr; } if (ShaderRes) { ShaderRes->Release(); ShaderRes = nullptr; } if (MouseTex) { MouseTex->Release(); MouseTex = nullptr; } if (InitBuffer) { delete [] InitBuffer; InitBuffer = nullptr; } return DUPL_RETURN_SUCCESS; } // // Initialize shaders for drawing to screen // DUPL_RETURN OUTPUTMANAGER::InitShaders() { HRESULT hr; UINT Size = ARRAYSIZE(g_VS); //创建顶点着色器对象 hr = m_Device->CreateVertexShader(g_VS, Size, nullptr, &m_VertexShader); if (FAILED(hr)) { return ProcessFailure(m_Device, L"Failed to create vertex shader in OUTPUTMANAGER", L"Error", hr, SystemTransitionsExpectedErrors); } D3D11_INPUT_ELEMENT_DESC Layout[] = { {"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0}, {"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0} }; UINT NumElements = ARRAYSIZE(Layout); //创建输入布局对象后,必须在调用绘图 API 之前将其绑定到输入装配器阶段 hr = m_Device->CreateInputLayout(Layout, NumElements, g_VS, Size, &m_InputLayout); if (FAILED(hr)) { return ProcessFailure(m_Device, L"Failed to create input layout in OUTPUTMANAGER", L"Error", hr, SystemTransitionsExpectedErrors); } //将输入布局对象绑定到输入装配器阶段 m_DeviceContext->IASetInputLayout(m_InputLayout); Size = ARRAYSIZE(g_PS); //像素着色器 hr = m_Device->CreatePixelShader(g_PS, Size, nullptr, &m_PixelShader); if (FAILED(hr)) { return ProcessFailure(m_Device, L"Failed to create pixel shader in OUTPUTMANAGER", L"Error", hr, SystemTransitionsExpectedErrors); } return DUPL_RETURN_SUCCESS; } // // Reset render target view // DUPL_RETURN OUTPUTMANAGER::MakeRTV() { // Get backbuffer ID3D11Texture2D* BackBuffer = nullptr; HRESULT hr = m_SwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), reinterpret_cast<void**>(&BackBuffer)); if (FAILED(hr)) { return ProcessFailure(m_Device, L"Failed to get backbuffer for making render target view in OUTPUTMANAGER", L"Error", hr, SystemTransitionsExpectedErrors); } // Create a render target view 创建用于访问资源数据的呈现目标视图 hr = m_Device->CreateRenderTargetView(BackBuffer, nullptr, &m_RTV); BackBuffer->Release(); if (FAILED(hr)) { return ProcessFailure(m_Device, L"Failed to create render target view in OUTPUTMANAGER", L"Error", hr, SystemTransitionsExpectedErrors); } // Set new render target 将呈现目标视图绑定到输出合并阶段 //以原子方式将一个或多个呈现目标以及深度模具缓冲区绑定到 输出合并阶段 //1 要绑定 (范围介于 0 到 D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT) 的呈现目标数 m_DeviceContext->OMSetRenderTargets(1, &m_RTV, nullptr); return DUPL_RETURN_SUCCESS; } // // Set new viewport // void OUTPUTMANAGER::SetViewPort(UINT Width, UINT Height) { D3D11_VIEWPORT VP; VP.Width = static_cast<FLOAT>(Width); VP.Height = static_cast<FLOAT>(Height); VP.MinDepth = 0.0f; VP.MaxDepth = 1.0f; VP.TopLeftX = 0; VP.TopLeftY = 0; //将视区数组绑定到管道的光栅器阶段 m_DeviceContext->RSSetViewports(1, &VP); } // // Resize swapchain // DUPL_RETURN OUTPUTMANAGER::ResizeSwapChain() { if (m_RTV) { m_RTV->Release(); m_RTV = nullptr; } RECT WindowRect; GetClientRect(m_WindowHandle, &WindowRect); UINT Width = WindowRect.right - WindowRect.left; UINT Height = WindowRect.bottom - WindowRect.top; // Resize swapchain DXGI_SWAP_CHAIN_DESC SwapChainDesc; m_SwapChain->GetDesc(&SwapChainDesc); HRESULT hr = m_SwapChain->ResizeBuffers(SwapChainDesc.BufferCount, Width, Height, SwapChainDesc.BufferDesc.Format, SwapChainDesc.Flags); if (FAILED(hr)) { return ProcessFailure(m_Device, L"Failed to resize swapchain buffers in OUTPUTMANAGER", L"Error", hr, SystemTransitionsExpectedErrors); } // Make new render target view DUPL_RETURN Ret = MakeRTV(); if (Ret != DUPL_RETURN_SUCCESS) { return Ret; } // Set new viewport SetViewPort(Width, Height); return Ret; } // // Releases all references // void OUTPUTMANAGER::CleanRefs() { if (m_VertexShader) { m_VertexShader->Release(); m_VertexShader = nullptr; } if (m_PixelShader) { m_PixelShader->Release(); m_PixelShader = nullptr; } if (m_InputLayout) { m_InputLayout->Release(); m_InputLayout = nullptr; } if (m_RTV) { m_RTV->Release(); m_RTV = nullptr; } if (m_SamplerLinear) { m_SamplerLinear->Release(); m_SamplerLinear = nullptr; } if (m_BlendState) { m_BlendState->Release(); m_BlendState = nullptr; } if (m_DeviceContext) { m_DeviceContext->Release(); m_DeviceContext = nullptr; } if (m_Device) { m_Device->Release(); m_Device = nullptr; } if (m_SwapChain) { m_SwapChain->Release(); m_SwapChain = nullptr; } if (m_SharedSurf) { m_SharedSurf->Release(); m_SharedSurf = nullptr; } if (m_KeyMutex) { m_KeyMutex->Release(); m_KeyMutex = nullptr; } if (m_Factory) { if (m_OcclusionCookie) { m_Factory->UnregisterOcclusionStatus(m_OcclusionCookie); m_OcclusionCookie = 0; } m_Factory->Release(); m_Factory = nullptr; } }
数据保存:
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A // PARTICULAR PURPOSE. // // Copyright (c) IQIYI Corporation. All rights reserved #include "TextureToFile.h" #include <wrl.h> #include <wincodec.h> #include <exception> #include <sstream> #include <queue> using namespace Microsoft::WRL; PCSTR StringFromWicFormat(const GUID& FormatGuid) { if (FormatGuid == GUID_WICPixelFormat32bppRGBA) { return "GUID_WICPixelFormat32bppRGBA"; } else if (FormatGuid == GUID_WICPixelFormat32bppBGRA) { return "GUID_WICPixelFormat32bppBGRA"; } return "Unknown pixel format"; } class AutoTextureMap { public: AutoTextureMap(ID3D11Texture2D* Texture) : mTexture(Texture) { // Get the device context Texture->GetDevice(&mD3dDevice); mD3dDevice->GetImmediateContext(&mD3dContext); // map the texture mMapInfo.RowPitch; HRESULT hr = mD3dContext->Map( Texture, 0, // Subresource D3D11_MAP_READ, 0, // MapFlags &mMapInfo); if (FAILED(hr)) { mMapped = false; } else { mMapped = true; } } ~AutoTextureMap() { if (mMapped) { mD3dContext->Unmap(mTexture, 0); } } bool mMapped; ID3D11Texture2D* mTexture; D3D11_MAPPED_SUBRESOURCE mMapInfo; ComPtr<ID3D11Device> mD3dDevice; ComPtr<ID3D11DeviceContext> mD3dContext; }; void D3D11CopyTexture(ID3D11Texture2D** DestTexture, ID3D11Texture2D* SrcSurface, ID3D11Device * device, ID3D11DeviceContext * deviceContext) { D3D11_TEXTURE2D_DESC SrcDesc; SrcSurface->GetDesc(&SrcDesc); // Staging buffer/texture D3D11_TEXTURE2D_DESC DestBufferDesc; DestBufferDesc.Width = SrcDesc.Width; DestBufferDesc.Height = SrcDesc.Height; DestBufferDesc.MipLevels = 1; DestBufferDesc.ArraySize = 1; DestBufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; DestBufferDesc.SampleDesc.Count = 1; DestBufferDesc.SampleDesc.Quality = 0; DestBufferDesc.Usage = D3D11_USAGE_STAGING; DestBufferDesc.BindFlags = 0; DestBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; DestBufferDesc.MiscFlags = 0; *DestTexture = nullptr; ID3D11Texture2D* DestBuffer = nullptr; HRESULT hr = device->CreateTexture2D(&DestBufferDesc, nullptr, &DestBuffer); if (FAILED(hr)) return; deviceContext->CopyResource(DestBuffer, SrcSurface); *DestTexture = DestBuffer; } void SaveTextureToBmp(PCWSTR FileName, ID3D11Texture2D* Texture) { HRESULT hr; // First verify that we can map the texture D3D11_TEXTURE2D_DESC desc; Texture->GetDesc(&desc); // translate texture format to WIC format. We support only BGRA and ARGB. GUID wicFormatGuid; switch (desc.Format) { case DXGI_FORMAT_R8G8B8A8_UNORM: wicFormatGuid = GUID_WICPixelFormat32bppRGBA; break; case DXGI_FORMAT_B8G8R8A8_UNORM: wicFormatGuid = GUID_WICPixelFormat32bppBGRA; break; default: { std::stringstream ss; ss << "Unsupported DXGI_FORMAT: " << desc.Format << ". Only RGBA and BGRA are supported."; throw std::exception(ss.str().c_str()); } } AutoTextureMap texMap(Texture); if (!texMap.mMapped) { return; } ComPtr<IWICImagingFactory> wicFactory; hr = CoCreateInstance( CLSID_WICImagingFactory, nullptr, CLSCTX_INPROC_SERVER, __uuidof(wicFactory), reinterpret_cast<void**>(wicFactory.GetAddressOf())); if (FAILED(hr)) { throw std::exception("Failed to create instance of WICImagingFactory"); } ComPtr<IWICBitmapEncoder> wicEncoder; hr = wicFactory->CreateEncoder( GUID_ContainerFormatBmp, nullptr, &wicEncoder); if (FAILED(hr)) { throw std::exception("Failed to create BMP encoder"); } ComPtr<IWICStream> wicStream; hr = wicFactory->CreateStream(&wicStream); if (FAILED(hr)) { throw std::exception("Failed to create IWICStream"); } hr = wicStream->InitializeFromFilename(FileName, GENERIC_WRITE); if (FAILED(hr)) { throw std::exception("Failed to initialize stream from file name"); } hr = wicEncoder->Initialize(wicStream.Get(), WICBitmapEncoderNoCache); if (FAILED(hr)) { throw std::exception("Failed to initialize bitmap encoder"); } // Encode and commit the frame { ComPtr<IWICBitmapFrameEncode> frameEncode; wicEncoder->CreateNewFrame(&frameEncode, nullptr); if (FAILED(hr)) { throw std::exception("Failed to create IWICBitmapFrameEncode"); } hr = frameEncode->Initialize(nullptr); if (FAILED(hr)) { throw std::exception("Failed to initialize IWICBitmapFrameEncode"); } hr = frameEncode->SetPixelFormat(&wicFormatGuid); if (FAILED(hr)) { std::stringstream ss; ss << "SetPixelFormat(" << StringFromWicFormat(wicFormatGuid) << "%s) failed."; throw std::exception(ss.str().c_str()); } hr = frameEncode->SetSize(desc.Width, desc.Height); if (FAILED(hr)) { throw std::exception("SetSize(...) failed."); } hr = frameEncode->WritePixels( desc.Height, texMap.mMapInfo.RowPitch, desc.Height * texMap.mMapInfo.RowPitch, reinterpret_cast<BYTE*>(texMap.mMapInfo.pData)); if (FAILED(hr)) { throw std::exception("frameEncode->WritePixels(...) failed."); } hr = frameEncode->Commit(); if (FAILED(hr)) { throw std::exception("Failed to commit frameEncode"); } } hr = wicEncoder->Commit(); if (FAILED(hr)) { throw std::exception("Failed to commit encoder"); } } // ansyc save tex to bitmap struct Texture2DData { Texture2DData() { Inited = false; pData = nullptr; } ~Texture2DData() { if (Inited && pData) { free(pData); } } WCHAR FilePath[MAX_PATH]; UINT Width; UINT Height; DXGI_FORMAT Format; void *pData; UINT RowPitch; UINT DepthPitch; bool Inited; }; std::queue<Texture2DData *> gTexDatas; HANDLE gQueueLock; HANDLE gTerminateEvent; HANDLE gTexDataToFileThread; DWORD gTexDataToFileThreadId; void AsyncSaveTextureToBmp(PCWSTR FileName, ID3D11Texture2D* Texture) { // First verify that we can map the texture D3D11_TEXTURE2D_DESC desc; Texture->GetDesc(&desc); // translate texture format to WIC format. We support only BGRA and ARGB. GUID wicFormatGuid; switch (desc.Format) { case DXGI_FORMAT_R8G8B8A8_UNORM: wicFormatGuid = GUID_WICPixelFormat32bppRGBA; break; case DXGI_FORMAT_B8G8R8A8_UNORM: wicFormatGuid = GUID_WICPixelFormat32bppBGRA; break; default: { std::stringstream ss; ss << "Unsupported DXGI_FORMAT: " << desc.Format << ". Only RGBA and BGRA are supported."; throw std::exception(ss.str().c_str()); } } AutoTextureMap texMap(Texture); if (!texMap.mMapped) { return; } Texture2DData *texData = new Texture2DData; if (texData == nullptr) { return; } UINT bufferSize = desc.Height * texMap.mMapInfo.RowPitch; texData->pData = malloc(bufferSize); if (texData->pData == nullptr) { delete texData; return; } texData->Inited = false; texData->Height = desc.Height; texData->Width = desc.Width; texData->Format = desc.Format; texData->RowPitch = texMap.mMapInfo.RowPitch; texData->DepthPitch = texMap.mMapInfo.DepthPitch; wcscpy(texData->FilePath, FileName); memcpy(texData->pData, texMap.mMapInfo.pData, bufferSize); texData->Inited = true; { ::WaitForSingleObject(gQueueLock, INFINITE); gTexDatas.push(texData); ::ReleaseMutex(gQueueLock); } } void SaveTexDataToBmp(Texture2DData* texData) { /* Windows 图像处理组件 (WIC) 提供了一个用于处理图像和图像元数据的可扩展的框架。 WIC 使独立软件供应商 (ISV) 和独立硬件供应商 (IHV) 能够开发自己的图像编解码器, 并获得与标准图像格式(例如 TIFF、JPEG、PNG、GIF、BMP 和 HDPhoto)相同的平台支持。 无论图像采用什么格式,都统一使用一组接口处理所有图像,因此只要安装了编解码器,任何使用 WIC 的应用程序都会自动获得对新图像格式的支持。 可扩展的元数据框架使应用程序可直接读取其自己的专有元数据以及将其写入到图像文件,因此元数据永远不会丢失或与图像分离。 */ HRESULT hr; PCWSTR FileName = texData->FilePath; // translate texture format to WIC format. We support only BGRA and ARGB. GUID wicFormatGuid; switch (texData->Format) { case DXGI_FORMAT_R8G8B8A8_UNORM: wicFormatGuid = GUID_WICPixelFormat32bppRGBA; break; case DXGI_FORMAT_B8G8R8A8_UNORM: wicFormatGuid = GUID_WICPixelFormat32bppBGRA; break; default: { std::stringstream ss; ss << "Unsupported DXGI_FORMAT: " << texData->Format << ". Only RGBA and BGRA are supported."; throw std::exception(ss.str().c_str()); } } ComPtr<IWICImagingFactory> wicFactory; hr = CoCreateInstance( CLSID_WICImagingFactory, nullptr, CLSCTX_INPROC_SERVER, __uuidof(wicFactory), reinterpret_cast<void**>(wicFactory.GetAddressOf())); if (FAILED(hr)) { throw std::exception("Failed to create instance of WICImagingFactory"); } ComPtr<IWICBitmapEncoder> wicEncoder; hr = wicFactory->CreateEncoder( GUID_ContainerFormatBmp, nullptr, &wicEncoder); if (FAILED(hr)) { throw std::exception("Failed to create BMP encoder"); } ComPtr<IWICStream> wicStream; hr = wicFactory->CreateStream(&wicStream); if (FAILED(hr)) { throw std::exception("Failed to create IWICStream"); } hr = wicStream->InitializeFromFilename(FileName, GENERIC_WRITE); if (FAILED(hr)) { throw std::exception("Failed to initialize stream from file name"); } hr = wicEncoder->Initialize(wicStream.Get(), WICBitmapEncoderNoCache); if (FAILED(hr)) { throw std::exception("Failed to initialize bitmap encoder"); } // Encode and commit the frame { ComPtr<IWICBitmapFrameEncode> frameEncode; wicEncoder->CreateNewFrame(&frameEncode, nullptr); if (FAILED(hr)) { throw std::exception("Failed to create IWICBitmapFrameEncode"); } hr = frameEncode->Initialize(nullptr); if (FAILED(hr)) { throw std::exception("Failed to initialize IWICBitmapFrameEncode"); } hr = frameEncode->SetPixelFormat(&wicFormatGuid); if (FAILED(hr)) { std::stringstream ss; ss << "SetPixelFormat(" << StringFromWicFormat(wicFormatGuid) << "%s) failed."; throw std::exception(ss.str().c_str()); } hr = frameEncode->SetSize(texData->Width, texData->Height); if (FAILED(hr)) { throw std::exception("SetSize(...) failed."); } hr = frameEncode->WritePixels( texData->Height, texData->RowPitch, texData->Height * texData->RowPitch, reinterpret_cast<BYTE*>(texData->pData)); if (FAILED(hr)) { throw std::exception("frameEncode->WritePixels(...) failed."); } hr = frameEncode->Commit(); if (FAILED(hr)) { throw std::exception("Failed to commit frameEncode"); } } hr = wicEncoder->Commit(); if (FAILED(hr)) { throw std::exception("Failed to commit encoder"); } } // texture output thread DWORD WINAPI TexDataToFileThreadFunction(LPVOID lpParam) { while (true) { //以16毫秒为间隔,等待终止事件 DWORD waitRet = ::WaitForSingleObject(gTerminateEvent, 16); if (waitRet == WAIT_OBJECT_0) { //获得终止事件所有权 return 0; } else if (waitRet == WAIT_TIMEOUT) { Texture2DData *texData = nullptr; { ::WaitForSingleObject(gQueueLock, INFINITE); if (gTexDatas.size() > 0) { texData = gTexDatas.front(); gTexDatas.pop(); } ::ReleaseMutex(gQueueLock); } if (texData) { SaveTexDataToBmp(texData);//将纹理数据保存成BMP,使用win的wic delete texData; } } } return 0; } void StopAnsycSaveTextureThread() { SetEvent(gTerminateEvent); WaitForSingleObject(gTexDataToFileThread, INFINITE); CloseHandle(gTexDataToFileThread); CloseHandle(gTerminateEvent); CloseHandle(gQueueLock); } void StartAnsycSaveTextureThread() { gQueueLock = CreateMutex(NULL, FALSE, NULL); gTerminateEvent = CreateEvent(NULL, TRUE, FALSE, NULL); gTexDataToFileThread = CreateThread( NULL, // default security attributes 0, // use default stack size TexDataToFileThreadFunction, // thread function name 0, // argument to thread function 0, // use default creation flags &gTexDataToFileThreadId); // returns the thread identifier } std::wstring ExePath() { wchar_t buffer[MAX_PATH]; GetModuleFileName(NULL, buffer, MAX_PATH); std::wstring::size_type pos = std::wstring(buffer).find_last_of(L"\\/"); return std::wstring(buffer).substr(0, pos); } BOOL DirectoryExists(LPCTSTR szPath) { DWORD dwAttrib = GetFileAttributes(szPath); return (dwAttrib != INVALID_FILE_ATTRIBUTES && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY)); }
数据显示,录屏显示到指定窗口
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A // PARTICULAR PURPOSE. // // Copyright (c) Microsoft Corporation. All rights reserved #include "DisplayManager.h" #include "TextureToFile.h" #include <sstream> using namespace DirectX; // // Constructor NULLs out vars // DISPLAYMANAGER::DISPLAYMANAGER() : m_Device(nullptr), m_DeviceContext(nullptr), m_MoveSurf(nullptr), m_VertexShader(nullptr), m_PixelShader(nullptr), m_InputLayout(nullptr), m_RTV(nullptr), m_SamplerLinear(nullptr), m_DirtyVertexBufferAlloc(nullptr), m_DirtyVertexBufferAllocSize(0) { } // // Destructor calls CleanRefs to destroy everything // DISPLAYMANAGER::~DISPLAYMANAGER() { CleanRefs(); if (m_DirtyVertexBufferAlloc) { delete [] m_DirtyVertexBufferAlloc; m_DirtyVertexBufferAlloc = nullptr; } } // // Initialize D3D variables // void DISPLAYMANAGER::InitD3D(DX_RESOURCES* Data) { m_Device = Data->Device; m_DeviceContext = Data->Context; m_VertexShader = Data->VertexShader; m_PixelShader = Data->PixelShader; m_InputLayout = Data->InputLayout; m_SamplerLinear = Data->SamplerLinear; m_Device->AddRef(); m_DeviceContext->AddRef(); m_VertexShader->AddRef(); m_PixelShader->AddRef(); m_InputLayout->AddRef(); m_SamplerLinear->AddRef(); } // // Process a given frame and its metadata // DUPL_RETURN DISPLAYMANAGER::ProcessFrame(_In_ FRAME_DATA* Data, _Inout_ ID3D11Texture2D* SharedSurf, INT OffsetX, INT OffsetY, _In_ DXGI_OUTPUT_DESC* DeskDesc) { DUPL_RETURN Ret = DUPL_RETURN_SUCCESS; // Process dirties and moves if (Data->FrameInfo.TotalMetadataBufferSize) { D3D11_TEXTURE2D_DESC Desc; Data->Frame->GetDesc(&Desc); if (Data->MoveCount) { //拷贝数据到surface Ret = CopyMove(SharedSurf, reinterpret_cast<DXGI_OUTDUPL_MOVE_RECT*>(Data->MetaData), Data->MoveCount, OffsetX, OffsetY, DeskDesc, Desc.Width, Desc.Height); if (Ret != DUPL_RETURN_SUCCESS) { return Ret; } } if (Data->DirtyCount) { Ret = CopyDirty(Data->Frame, SharedSurf, reinterpret_cast<RECT*>(Data->MetaData + (Data->MoveCount * sizeof(DXGI_OUTDUPL_MOVE_RECT))), Data->DirtyCount, OffsetX, OffsetY, DeskDesc); } } return Ret; } // // Returns D3D device being used // ID3D11Device* DISPLAYMANAGER::GetDevice() { return m_Device; } // // Set appropriate source and destination rects for move rects // void DISPLAYMANAGER::SetMoveRect(_Out_ RECT* SrcRect, _Out_ RECT* DestRect, _In_ DXGI_OUTPUT_DESC* DeskDesc, _In_ DXGI_OUTDUPL_MOVE_RECT* MoveRect, INT TexWidth, INT TexHeight) { switch (DeskDesc->Rotation) { case DXGI_MODE_ROTATION_UNSPECIFIED: case DXGI_MODE_ROTATION_IDENTITY: { SrcRect->left = MoveRect->SourcePoint.x; SrcRect->top = MoveRect->SourcePoint.y; SrcRect->right = MoveRect->SourcePoint.x + MoveRect->DestinationRect.right - MoveRect->DestinationRect.left; SrcRect->bottom = MoveRect->SourcePoint.y + MoveRect->DestinationRect.bottom - MoveRect->DestinationRect.top; *DestRect = MoveRect->DestinationRect; break; } case DXGI_MODE_ROTATION_ROTATE90: { SrcRect->left = TexHeight - (MoveRect->SourcePoint.y + MoveRect->DestinationRect.bottom - MoveRect->DestinationRect.top); SrcRect->top = MoveRect->SourcePoint.x; SrcRect->right = TexHeight - MoveRect->SourcePoint.y; SrcRect->bottom = MoveRect->SourcePoint.x + MoveRect->DestinationRect.right - MoveRect->DestinationRect.left; DestRect->left = TexHeight - MoveRect->DestinationRect.bottom; DestRect->top = MoveRect->DestinationRect.left; DestRect->right = TexHeight - MoveRect->DestinationRect.top; DestRect->bottom = MoveRect->DestinationRect.right; break; } case DXGI_MODE_ROTATION_ROTATE180: { SrcRect->left = TexWidth - (MoveRect->SourcePoint.x + MoveRect->DestinationRect.right - MoveRect->DestinationRect.left); SrcRect->top = TexHeight - (MoveRect->SourcePoint.y + MoveRect->DestinationRect.bottom - MoveRect->DestinationRect.top); SrcRect->right = TexWidth - MoveRect->SourcePoint.x; SrcRect->bottom = TexHeight - MoveRect->SourcePoint.y; DestRect->left = TexWidth - MoveRect->DestinationRect.right; DestRect->top = TexHeight - MoveRect->DestinationRect.bottom; DestRect->right = TexWidth - MoveRect->DestinationRect.left; DestRect->bottom = TexHeight - MoveRect->DestinationRect.top; break; } case DXGI_MODE_ROTATION_ROTATE270: { SrcRect->left = MoveRect->SourcePoint.x; SrcRect->top = TexWidth - (MoveRect->SourcePoint.x + MoveRect->DestinationRect.right - MoveRect->DestinationRect.left); SrcRect->right = MoveRect->SourcePoint.y + MoveRect->DestinationRect.bottom - MoveRect->DestinationRect.top; SrcRect->bottom = TexWidth - MoveRect->SourcePoint.x; DestRect->left = MoveRect->DestinationRect.top; DestRect->top = TexWidth - MoveRect->DestinationRect.right; DestRect->right = MoveRect->DestinationRect.bottom; DestRect->bottom = TexWidth - MoveRect->DestinationRect.left; break; } default: { RtlZeroMemory(DestRect, sizeof(RECT)); RtlZeroMemory(SrcRect, sizeof(RECT)); break; } } } // // Copy move rectangles // DUPL_RETURN DISPLAYMANAGER::CopyMove(_Inout_ ID3D11Texture2D* SharedSurf, _In_reads_(MoveCount) DXGI_OUTDUPL_MOVE_RECT* MoveBuffer, UINT MoveCount, INT OffsetX, INT OffsetY, _In_ DXGI_OUTPUT_DESC* DeskDesc, INT TexWidth, INT TexHeight) { D3D11_TEXTURE2D_DESC FullDesc; SharedSurf->GetDesc(&FullDesc); // Make new intermediate surface to copy into for moving if (!m_MoveSurf) { D3D11_TEXTURE2D_DESC MoveDesc; MoveDesc = FullDesc; MoveDesc.Width = DeskDesc->DesktopCoordinates.right - DeskDesc->DesktopCoordinates.left; MoveDesc.Height = DeskDesc->DesktopCoordinates.bottom - DeskDesc->DesktopCoordinates.top; MoveDesc.BindFlags = D3D11_BIND_RENDER_TARGET; MoveDesc.MiscFlags = 0; HRESULT hr = m_Device->CreateTexture2D(&MoveDesc, nullptr, &m_MoveSurf); if (FAILED(hr)) { return ProcessFailure(m_Device, L"Failed to create staging texture for move rects", L"Error", hr, SystemTransitionsExpectedErrors); } } for (UINT i = 0; i < MoveCount; ++i) { RECT SrcRect; RECT DestRect; SetMoveRect(&SrcRect, &DestRect, DeskDesc, &(MoveBuffer[i]), TexWidth, TexHeight); // Copy rect out of shared surface D3D11_BOX Box; Box.left = SrcRect.left + DeskDesc->DesktopCoordinates.left - OffsetX; Box.top = SrcRect.top + DeskDesc->DesktopCoordinates.top - OffsetY; Box.front = 0; Box.right = SrcRect.right + DeskDesc->DesktopCoordinates.left - OffsetX; Box.bottom = SrcRect.bottom + DeskDesc->DesktopCoordinates.top - OffsetY; Box.back = 1; //将区域从源资源复制到目标资源 m_DeviceContext->CopySubresourceRegion(m_MoveSurf, 0, SrcRect.left, SrcRect.top, 0, SharedSurf, 0, &Box); // Copy back to shared surface Box.left = SrcRect.left; Box.top = SrcRect.top; Box.front = 0; Box.right = SrcRect.right; Box.bottom = SrcRect.bottom; Box.back = 1; /*源框必须位于源资源的大小范围内。 目标偏移量( (x、y 和 z) )允许源框在写入目标资源时偏移;但是,源框的尺寸和偏移量必须在资源的大小范围内。 如果尝试在目标资源外部复制或指定大于源资源的源框, 则 CopySubresourceRegion 的行为未定义。 如果创建了支持 调试层的设备, 则调试输出将在此无效 的 CopySubresourceRegion 调用上报告错误。 CopySubresourceRegion 的参数无效会导致未定义的行为, 并可能导致不正确的呈现、剪辑、无副本甚至删除呈现设备。 如果资源是缓冲区,则所有坐标都以字节为单位;如果资源是纹理,则所有坐标都采用纹素。*/ m_DeviceContext->CopySubresourceRegion(SharedSurf, 0, DestRect.left + DeskDesc->DesktopCoordinates.left - OffsetX, DestRect.top + DeskDesc->DesktopCoordinates.top - OffsetY, 0, m_MoveSurf, 0, &Box); } return DUPL_RETURN_SUCCESS; } // // Sets up vertices for dirty rects for rotated desktops // #pragma warning(push) #pragma warning(disable:__WARNING_USING_UNINIT_VAR) // false positives in SetDirtyVert due to tool bug void DISPLAYMANAGER::SetDirtyVert(_Out_writes_(NUMVERTICES) VERTEX* Vertices, _In_ RECT* Dirty, INT OffsetX, INT OffsetY, _In_ DXGI_OUTPUT_DESC* DeskDesc, _In_ D3D11_TEXTURE2D_DESC* FullDesc, _In_ D3D11_TEXTURE2D_DESC* ThisDesc) { INT CenterX = FullDesc->Width / 2; INT CenterY = FullDesc->Height / 2; INT Width = DeskDesc->DesktopCoordinates.right - DeskDesc->DesktopCoordinates.left; INT Height = DeskDesc->DesktopCoordinates.bottom - DeskDesc->DesktopCoordinates.top; // Rotation compensated destination rect RECT DestDirty = *Dirty; // Set appropriate coordinates compensated for rotation switch (DeskDesc->Rotation) { case DXGI_MODE_ROTATION_ROTATE90: { DestDirty.left = Width - Dirty->bottom; DestDirty.top = Dirty->left; DestDirty.right = Width - Dirty->top; DestDirty.bottom = Dirty->right; Vertices[0].TexCoord = XMFLOAT2(Dirty->right / static_cast<FLOAT>(ThisDesc->Width), Dirty->bottom / static_cast<FLOAT>(ThisDesc->Height)); Vertices[1].TexCoord = XMFLOAT2(Dirty->left / static_cast<FLOAT>(ThisDesc->Width), Dirty->bottom / static_cast<FLOAT>(ThisDesc->Height)); Vertices[2].TexCoord = XMFLOAT2(Dirty->right / static_cast<FLOAT>(ThisDesc->Width), Dirty->top / static_cast<FLOAT>(ThisDesc->Height)); Vertices[5].TexCoord = XMFLOAT2(Dirty->left / static_cast<FLOAT>(ThisDesc->Width), Dirty->top / static_cast<FLOAT>(ThisDesc->Height)); break; } case DXGI_MODE_ROTATION_ROTATE180: { DestDirty.left = Width - Dirty->right; DestDirty.top = Height - Dirty->bottom; DestDirty.right = Width - Dirty->left; DestDirty.bottom = Height - Dirty->top; Vertices[0].TexCoord = XMFLOAT2(Dirty->right / static_cast<FLOAT>(ThisDesc->Width), Dirty->top / static_cast<FLOAT>(ThisDesc->Height)); Vertices[1].TexCoord = XMFLOAT2(Dirty->right / static_cast<FLOAT>(ThisDesc->Width), Dirty->bottom / static_cast<FLOAT>(ThisDesc->Height)); Vertices[2].TexCoord = XMFLOAT2(Dirty->left / static_cast<FLOAT>(ThisDesc->Width), Dirty->top / static_cast<FLOAT>(ThisDesc->Height)); Vertices[5].TexCoord = XMFLOAT2(Dirty->left / static_cast<FLOAT>(ThisDesc->Width), Dirty->bottom / static_cast<FLOAT>(ThisDesc->Height)); break; } case DXGI_MODE_ROTATION_ROTATE270: { DestDirty.left = Dirty->top; DestDirty.top = Height - Dirty->right; DestDirty.right = Dirty->bottom; DestDirty.bottom = Height - Dirty->left; Vertices[0].TexCoord = XMFLOAT2(Dirty->left / static_cast<FLOAT>(ThisDesc->Width), Dirty->top / static_cast<FLOAT>(ThisDesc->Height)); Vertices[1].TexCoord = XMFLOAT2(Dirty->right / static_cast<FLOAT>(ThisDesc->Width), Dirty->top / static_cast<FLOAT>(ThisDesc->Height)); Vertices[2].TexCoord = XMFLOAT2(Dirty->left / static_cast<FLOAT>(ThisDesc->Width), Dirty->bottom / static_cast<FLOAT>(ThisDesc->Height)); Vertices[5].TexCoord = XMFLOAT2(Dirty->right / static_cast<FLOAT>(ThisDesc->Width), Dirty->bottom / static_cast<FLOAT>(ThisDesc->Height)); break; } default: assert(false); // drop through case DXGI_MODE_ROTATION_UNSPECIFIED: case DXGI_MODE_ROTATION_IDENTITY: { Vertices[0].TexCoord = XMFLOAT2(Dirty->left / static_cast<FLOAT>(ThisDesc->Width), Dirty->bottom / static_cast<FLOAT>(ThisDesc->Height)); Vertices[1].TexCoord = XMFLOAT2(Dirty->left / static_cast<FLOAT>(ThisDesc->Width), Dirty->top / static_cast<FLOAT>(ThisDesc->Height)); Vertices[2].TexCoord = XMFLOAT2(Dirty->right / static_cast<FLOAT>(ThisDesc->Width), Dirty->bottom / static_cast<FLOAT>(ThisDesc->Height)); Vertices[5].TexCoord = XMFLOAT2(Dirty->right / static_cast<FLOAT>(ThisDesc->Width), Dirty->top / static_cast<FLOAT>(ThisDesc->Height)); break; } } // Set positions Vertices[0].Pos = XMFLOAT3((DestDirty.left + DeskDesc->DesktopCoordinates.left - OffsetX - CenterX) / static_cast<FLOAT>(CenterX), -1 * (DestDirty.bottom + DeskDesc->DesktopCoordinates.top - OffsetY - CenterY) / static_cast<FLOAT>(CenterY), 0.0f); Vertices[1].Pos = XMFLOAT3((DestDirty.left + DeskDesc->DesktopCoordinates.left - OffsetX - CenterX) / static_cast<FLOAT>(CenterX), -1 * (DestDirty.top + DeskDesc->DesktopCoordinates.top - OffsetY - CenterY) / static_cast<FLOAT>(CenterY), 0.0f); Vertices[2].Pos = XMFLOAT3((DestDirty.right + DeskDesc->DesktopCoordinates.left - OffsetX - CenterX) / static_cast<FLOAT>(CenterX), -1 * (DestDirty.bottom + DeskDesc->DesktopCoordinates.top - OffsetY - CenterY) / static_cast<FLOAT>(CenterY), 0.0f); Vertices[3].Pos = Vertices[2].Pos; Vertices[4].Pos = Vertices[1].Pos; Vertices[5].Pos = XMFLOAT3((DestDirty.right + DeskDesc->DesktopCoordinates.left - OffsetX - CenterX) / static_cast<FLOAT>(CenterX), -1 * (DestDirty.top + DeskDesc->DesktopCoordinates.top - OffsetY - CenterY) / static_cast<FLOAT>(CenterY), 0.0f); Vertices[3].TexCoord = Vertices[2].TexCoord; Vertices[4].TexCoord = Vertices[1].TexCoord; } #pragma warning(pop) // re-enable __WARNING_USING_UNINIT_VAR // // Copies dirty rectangles // DUPL_RETURN DISPLAYMANAGER::CopyDirty(_In_ ID3D11Texture2D* SrcSurface, _Inout_ ID3D11Texture2D* SharedSurf, _In_reads_(DirtyCount) RECT* DirtyBuffer, UINT DirtyCount, INT OffsetX, INT OffsetY, _In_ DXGI_OUTPUT_DESC* DeskDesc) { HRESULT hr; D3D11_TEXTURE2D_DESC FullDesc; SharedSurf->GetDesc(&FullDesc); D3D11_TEXTURE2D_DESC ThisDesc; SrcSurface->GetDesc(&ThisDesc); // save src surface to bmp file { ID3D11Texture2D* CpuReadTexture = nullptr; D3D11CopyTexture(&CpuReadTexture, SrcSurface, m_Device, m_DeviceContext); if (CpuReadTexture) { std::wstring filepath; { // static std::wstring CurPath = ExePath(); std::wstringstream wss; static DWORD BmpNumber = 0; BmpNumber++; wss << CurPath; wss << "\\ScreenShot"; if (!DirectoryExists(wss.str().c_str())) { CreateDirectory(wss.str().c_str(), NULL); } wss << "\\DesktopDuplication-" << BmpNumber << L".bmp"; filepath = wss.str(); } #if 0 // sync save may lost render frames SaveTextureToBmp(filepath.c_str(), CpuReadTexture); #else AsyncSaveTextureToBmp(filepath.c_str(), CpuReadTexture); #endif CpuReadTexture->Release(); } } if (!m_RTV) { hr = m_Device->CreateRenderTargetView(SharedSurf, nullptr, &m_RTV); if (FAILED(hr)) { return ProcessFailure(m_Device, L"Failed to create render target view for dirty rects", L"Error", hr, SystemTransitionsExpectedErrors); } } D3D11_SHADER_RESOURCE_VIEW_DESC ShaderDesc; ShaderDesc.Format = ThisDesc.Format; ShaderDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; ShaderDesc.Texture2D.MostDetailedMip = ThisDesc.MipLevels - 1; ShaderDesc.Texture2D.MipLevels = ThisDesc.MipLevels; // Create new shader resource view ID3D11ShaderResourceView* ShaderResource = nullptr; hr = m_Device->CreateShaderResourceView(SrcSurface, &ShaderDesc, &ShaderResource); if (FAILED(hr)) { return ProcessFailure(m_Device, L"Failed to create shader resource view for dirty rects", L"Error", hr, SystemTransitionsExpectedErrors); } FLOAT BlendFactor[4] = {0.f, 0.f, 0.f, 0.f}; m_DeviceContext->OMSetBlendState(nullptr, BlendFactor, 0xFFFFFFFF); m_DeviceContext->OMSetRenderTargets(1, &m_RTV, nullptr); m_DeviceContext->VSSetShader(m_VertexShader, nullptr, 0); m_DeviceContext->PSSetShader(m_PixelShader, nullptr, 0); m_DeviceContext->PSSetShaderResources(0, 1, &ShaderResource); m_DeviceContext->PSSetSamplers(0, 1, &m_SamplerLinear); m_DeviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); // Create space for vertices for the dirty rects if the current space isn't large enough UINT BytesNeeded = sizeof(VERTEX) * NUMVERTICES * DirtyCount; if (BytesNeeded > m_DirtyVertexBufferAllocSize) { if (m_DirtyVertexBufferAlloc) { delete [] m_DirtyVertexBufferAlloc; } m_DirtyVertexBufferAlloc = new (std::nothrow) BYTE[BytesNeeded]; if (!m_DirtyVertexBufferAlloc) { m_DirtyVertexBufferAllocSize = 0; return ProcessFailure(nullptr, L"Failed to allocate memory for dirty vertex buffer.", L"Error", E_OUTOFMEMORY); } m_DirtyVertexBufferAllocSize = BytesNeeded; } // Fill them in VERTEX* DirtyVertex = reinterpret_cast<VERTEX*>(m_DirtyVertexBufferAlloc); for (UINT i = 0; i < DirtyCount; ++i, DirtyVertex += NUMVERTICES) { SetDirtyVert(DirtyVertex, &(DirtyBuffer[i]), OffsetX, OffsetY, DeskDesc, &FullDesc, &ThisDesc); } // Create vertex buffer D3D11_BUFFER_DESC BufferDesc; RtlZeroMemory(&BufferDesc, sizeof(BufferDesc)); BufferDesc.Usage = D3D11_USAGE_DEFAULT; BufferDesc.ByteWidth = BytesNeeded; BufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER; BufferDesc.CPUAccessFlags = 0; D3D11_SUBRESOURCE_DATA InitData; RtlZeroMemory(&InitData, sizeof(InitData)); InitData.pSysMem = m_DirtyVertexBufferAlloc; ID3D11Buffer* VertBuf = nullptr; hr = m_Device->CreateBuffer(&BufferDesc, &InitData, &VertBuf); if (FAILED(hr)) { return ProcessFailure(m_Device, L"Failed to create vertex buffer in dirty rect processing", L"Error", hr, SystemTransitionsExpectedErrors); } UINT Stride = sizeof(VERTEX); UINT Offset = 0; m_DeviceContext->IASetVertexBuffers(0, 1, &VertBuf, &Stride, &Offset); D3D11_VIEWPORT VP; VP.Width = static_cast<FLOAT>(FullDesc.Width); VP.Height = static_cast<FLOAT>(FullDesc.Height); VP.MinDepth = 0.0f; VP.MaxDepth = 1.0f; VP.TopLeftX = 0.0f; VP.TopLeftY = 0.0f; m_DeviceContext->RSSetViewports(1, &VP); m_DeviceContext->Draw(NUMVERTICES * DirtyCount, 0); VertBuf->Release(); VertBuf = nullptr; ShaderResource->Release(); ShaderResource = nullptr; return DUPL_RETURN_SUCCESS; } // // Clean all references // void DISPLAYMANAGER::CleanRefs() { if (m_DeviceContext) { m_DeviceContext->Release(); m_DeviceContext = nullptr; } if (m_Device) { m_Device->Release(); m_Device = nullptr; } if (m_MoveSurf) { m_MoveSurf->Release(); m_MoveSurf = nullptr; } if (m_VertexShader) { m_VertexShader->Release(); m_VertexShader = nullptr; } if (m_PixelShader) { m_PixelShader->Release(); m_PixelShader = nullptr; } if (m_InputLayout) { m_InputLayout->Release(); m_InputLayout = nullptr; } if (m_SamplerLinear) { m_SamplerLinear->Release(); m_SamplerLinear = nullptr; } if (m_RTV) { m_RTV->Release(); m_RTV = nullptr; } }
标签:return,hr,nullptr,cast,录屏,Device,DXGI From: https://www.cnblogs.com/8335IT/p/18158903