├── .gitattributes ├── .gitignore ├── CppProperties.json ├── IddSampleDriver.sln ├── IddSampleDriver ├── Driver.cpp ├── Driver.h ├── IddSampleDriver.inf ├── IddSampleDriver.sln ├── IddSampleDriver.vcxproj ├── IddSampleDriver.vcxproj.filters ├── Trace.h └── readme.md ├── README.md └── option.txt /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | [Xx]64/ 19 | [Xx]86/ 20 | [Bb]uild/ 21 | bld/ 22 | [Bb]in/ 23 | [Oo]bj/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | artifacts/ 46 | 47 | *_i.c 48 | *_p.c 49 | *_i.h 50 | *.ilk 51 | *.meta 52 | *.obj 53 | *.pch 54 | *.pdb 55 | *.pgc 56 | *.pgd 57 | *.rsp 58 | *.sbr 59 | *.tlb 60 | *.tli 61 | *.tlh 62 | *.tmp 63 | *.tmp_proj 64 | *.log 65 | *.vspscc 66 | *.vssscc 67 | .builds 68 | *.pidb 69 | *.svclog 70 | *.scc 71 | 72 | # Chutzpah Test files 73 | _Chutzpah* 74 | 75 | # Visual C++ cache files 76 | ipch/ 77 | *.aps 78 | *.ncb 79 | *.opendb 80 | *.opensdf 81 | *.sdf 82 | *.cachefile 83 | *.VC.db 84 | 85 | # Visual Studio profiler 86 | *.psess 87 | *.vsp 88 | *.vspx 89 | *.sap 90 | 91 | # TFS 2012 Local Workspace 92 | $tf/ 93 | 94 | # Guidance Automation Toolkit 95 | *.gpState 96 | 97 | # ReSharper is a .NET coding add-in 98 | _ReSharper*/ 99 | *.[Rr]e[Ss]harper 100 | *.DotSettings.user 101 | 102 | # JustCode is a .NET coding add-in 103 | .JustCode 104 | 105 | # TeamCity is a build add-in 106 | _TeamCity* 107 | 108 | # DotCover is a Code Coverage Tool 109 | *.dotCover 110 | 111 | # NCrunch 112 | _NCrunch_* 113 | .*crunch*.local.xml 114 | nCrunchTemp_* 115 | 116 | # MightyMoose 117 | *.mm.* 118 | AutoTest.Net/ 119 | 120 | # Web workbench (sass) 121 | .sass-cache/ 122 | 123 | # Installshield output folder 124 | [Ee]xpress/ 125 | 126 | # DocProject is a documentation generator add-in 127 | DocProject/buildhelp/ 128 | DocProject/Help/*.HxT 129 | DocProject/Help/*.HxC 130 | DocProject/Help/*.hhc 131 | DocProject/Help/*.hhk 132 | DocProject/Help/*.hhp 133 | DocProject/Help/Html2 134 | DocProject/Help/html 135 | 136 | # Click-Once directory 137 | publish/ 138 | 139 | # Publish Web Output 140 | *.[Pp]ublish.xml 141 | *.azurePubxml 142 | 143 | # TODO: Un-comment the next line if you do not want to checkin 144 | # your web deploy settings because they may include unencrypted 145 | # passwords 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # NuGet Packages 150 | *.nupkg 151 | # The packages folder can be ignored because of Package Restore 152 | **/packages/* 153 | # except build/, which is used as an MSBuild target. 154 | !**/packages/build/ 155 | # Uncomment if necessary however generally it will be regenerated when needed 156 | #!**/packages/repositories.config 157 | # NuGet v3's project.json files produces more ignoreable files 158 | *.nuget.props 159 | *.nuget.targets 160 | 161 | # Microsoft Azure Build Output 162 | csx/ 163 | *.build.csdef 164 | 165 | # Microsoft Azure Emulator 166 | ecf/ 167 | rcf/ 168 | 169 | # Windows Store app package directory 170 | AppPackages/ 171 | BundleArtifacts/ 172 | 173 | # Visual Studio cache files 174 | # files ending in .cache can be ignored 175 | *.[Cc]ache 176 | # but keep track of directories ending in .cache 177 | !*.[Cc]ache/ 178 | 179 | # Others 180 | ClientBin/ 181 | [Ss]tyle[Cc]op.* 182 | ~$* 183 | *~ 184 | *.dbmdl 185 | *.dbproj.schemaview 186 | *.pfx 187 | *.publishsettings 188 | node_modules/ 189 | orleans.codegen.cs 190 | 191 | # RIA/Silverlight projects 192 | Generated_Code/ 193 | 194 | # Backup & report files from converting an old project file 195 | # to a newer Visual Studio version. Backup files are not needed, 196 | # because we have git ;-) 197 | _UpgradeReport_Files/ 198 | Backup*/ 199 | UpgradeLog*.XML 200 | UpgradeLog*.htm 201 | 202 | # SQL Server files 203 | *.mdf 204 | *.ldf 205 | 206 | # Business Intelligence projects 207 | *.rdl.data 208 | *.bim.layout 209 | *.bim_*.settings 210 | 211 | # Microsoft Fakes 212 | FakesAssemblies/ 213 | 214 | # GhostDoc plugin setting file 215 | *.GhostDoc.xml 216 | 217 | # Node.js Tools for Visual Studio 218 | .ntvs_analysis.dat 219 | 220 | # Visual Studio 6 build log 221 | *.plg 222 | 223 | # Visual Studio 6 workspace options file 224 | *.opt 225 | 226 | # Visual Studio LightSwitch build output 227 | **/*.HTMLClient/GeneratedArtifacts 228 | **/*.DesktopClient/GeneratedArtifacts 229 | **/*.DesktopClient/ModelManifest.xml 230 | **/*.Server/GeneratedArtifacts 231 | **/*.Server/ModelManifest.xml 232 | _Pvt_Extensions 233 | 234 | # LightSwitch generated files 235 | GeneratedArtifacts/ 236 | ModelManifest.xml 237 | 238 | # Paket dependency manager 239 | .paket/paket.exe 240 | 241 | # FAKE - F# Make 242 | .fake/ 243 | -------------------------------------------------------------------------------- /CppProperties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "inheritEnvironments": [ 5 | "msvc_x64" 6 | ], 7 | "name": "x64-Debug", 8 | "includePath": [ 9 | "${env.INCLUDE}", 10 | "${workspaceRoot}\\**" 11 | ], 12 | "defines": [ 13 | "WIN32", 14 | "_DEBUG", 15 | "UNICODE", 16 | "_UNICODE" 17 | ], 18 | "intelliSenseMode": "windows-msvc-x64" 19 | } 20 | ] 21 | } -------------------------------------------------------------------------------- /IddSampleDriver.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.31129.286 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "IddSampleDriver", "IddSampleDriver\IddSampleDriver.vcxproj", "{2D54CB75-8B17-4F11-97DC-847B0244CD46}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|ARM = Debug|ARM 11 | Debug|ARM64 = Debug|ARM64 12 | Debug|x64 = Debug|x64 13 | Debug|x86 = Debug|x86 14 | Release|ARM = Release|ARM 15 | Release|ARM64 = Release|ARM64 16 | Release|x64 = Release|x64 17 | Release|x86 = Release|x86 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Debug|ARM.ActiveCfg = Debug|x64 21 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Debug|ARM.Build.0 = Debug|x64 22 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Debug|ARM.Deploy.0 = Debug|x64 23 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Debug|ARM64.ActiveCfg = Debug|ARM64 24 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Debug|ARM64.Build.0 = Debug|ARM64 25 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Debug|ARM64.Deploy.0 = Debug|ARM64 26 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Debug|x64.ActiveCfg = Debug|x64 27 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Debug|x64.Build.0 = Debug|x64 28 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Debug|x64.Deploy.0 = Debug|x64 29 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Debug|x86.ActiveCfg = Debug|Win32 30 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Debug|x86.Build.0 = Debug|Win32 31 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Debug|x86.Deploy.0 = Debug|Win32 32 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Release|ARM.ActiveCfg = Release|ARM 33 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Release|ARM.Build.0 = Release|ARM 34 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Release|ARM.Deploy.0 = Release|ARM 35 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Release|ARM64.ActiveCfg = Release|ARM64 36 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Release|ARM64.Build.0 = Release|ARM64 37 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Release|ARM64.Deploy.0 = Release|ARM64 38 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Release|x64.ActiveCfg = Release|x64 39 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Release|x64.Build.0 = Release|x64 40 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Release|x64.Deploy.0 = Release|x64 41 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Release|x86.ActiveCfg = Release|Win32 42 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Release|x86.Build.0 = Release|Win32 43 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Release|x86.Deploy.0 = Release|Win32 44 | EndGlobalSection 45 | GlobalSection(SolutionProperties) = preSolution 46 | HideSolutionNode = FALSE 47 | EndGlobalSection 48 | GlobalSection(ExtensibilityGlobals) = postSolution 49 | SolutionGuid = {DE75497E-BE4F-4215-8345-84C5F8358E3B} 50 | EndGlobalSection 51 | EndGlobal 52 | -------------------------------------------------------------------------------- /IddSampleDriver/Driver.cpp: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) Microsoft Corporation 4 | 5 | Abstract: 6 | 7 | This module contains a sample implementation of an indirect display driver. See the included README.md file and the 8 | various TODO blocks throughout this file and all accompanying files for information on building a production driver. 9 | 10 | MSDN documentation on indirect displays can be found at https://msdn.microsoft.com/en-us/library/windows/hardware/mt761968(v=vs.85).aspx. 11 | 12 | Environment: 13 | 14 | User Mode, UMDF 15 | 16 | --*/ 17 | 18 | #include "Driver.h" 19 | #include "Driver.tmh" 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | using namespace std; 27 | using namespace Microsoft::IndirectDisp; 28 | using namespace Microsoft::WRL; 29 | 30 | extern "C" DRIVER_INITIALIZE DriverEntry; 31 | 32 | EVT_WDF_DRIVER_DEVICE_ADD IddSampleDeviceAdd; 33 | EVT_WDF_DEVICE_D0_ENTRY IddSampleDeviceD0Entry; 34 | 35 | EVT_IDD_CX_ADAPTER_INIT_FINISHED IddSampleAdapterInitFinished; 36 | EVT_IDD_CX_ADAPTER_COMMIT_MODES IddSampleAdapterCommitModes; 37 | 38 | EVT_IDD_CX_PARSE_MONITOR_DESCRIPTION IddSampleParseMonitorDescription; 39 | EVT_IDD_CX_MONITOR_GET_DEFAULT_DESCRIPTION_MODES IddSampleMonitorGetDefaultModes; 40 | EVT_IDD_CX_MONITOR_QUERY_TARGET_MODES IddSampleMonitorQueryModes; 41 | 42 | EVT_IDD_CX_MONITOR_ASSIGN_SWAPCHAIN IddSampleMonitorAssignSwapChain; 43 | EVT_IDD_CX_MONITOR_UNASSIGN_SWAPCHAIN IddSampleMonitorUnassignSwapChain; 44 | 45 | vector> monitorModes; 46 | vector< DISPLAYCONFIG_VIDEO_SIGNAL_INFO> s_KnownMonitorModes2; 47 | UINT numVirtualDisplays; 48 | 49 | struct IndirectDeviceContextWrapper 50 | { 51 | IndirectDeviceContext* pContext; 52 | 53 | void Cleanup() 54 | { 55 | delete pContext; 56 | pContext = nullptr; 57 | } 58 | }; 59 | 60 | // This macro creates the methods for accessing an IndirectDeviceContextWrapper as a context for a WDF object 61 | WDF_DECLARE_CONTEXT_TYPE(IndirectDeviceContextWrapper); 62 | 63 | extern "C" BOOL WINAPI DllMain( 64 | _In_ HINSTANCE hInstance, 65 | _In_ UINT dwReason, 66 | _In_opt_ LPVOID lpReserved) 67 | { 68 | UNREFERENCED_PARAMETER(hInstance); 69 | UNREFERENCED_PARAMETER(lpReserved); 70 | UNREFERENCED_PARAMETER(dwReason); 71 | 72 | return TRUE; 73 | } 74 | 75 | _Use_decl_annotations_ 76 | extern "C" NTSTATUS DriverEntry( 77 | PDRIVER_OBJECT pDriverObject, 78 | PUNICODE_STRING pRegistryPath 79 | ) 80 | { 81 | WDF_DRIVER_CONFIG Config; 82 | NTSTATUS Status; 83 | 84 | WDF_OBJECT_ATTRIBUTES Attributes; 85 | WDF_OBJECT_ATTRIBUTES_INIT(&Attributes); 86 | 87 | WDF_DRIVER_CONFIG_INIT(&Config, 88 | IddSampleDeviceAdd 89 | ); 90 | 91 | Status = WdfDriverCreate(pDriverObject, pRegistryPath, &Attributes, &Config, WDF_NO_HANDLE); 92 | if (!NT_SUCCESS(Status)) 93 | { 94 | return Status; 95 | } 96 | 97 | return Status; 98 | } 99 | vector split(string& input, char delimiter) 100 | { 101 | istringstream stream(input); 102 | string field; 103 | vector result; 104 | while (getline(stream, field, delimiter)) { 105 | result.push_back(field); 106 | } 107 | return result; 108 | } 109 | 110 | void loadOptions(string filepath) { 111 | ifstream ifs(filepath); 112 | 113 | string line; 114 | vector> res; 115 | getline(ifs, line);//num of displays 116 | numVirtualDisplays = stoi(line); 117 | while (getline(ifs, line)) { 118 | vector strvec = split(line, ','); 119 | if (strvec.size() == 3 && strvec[0].substr(0, 1) != "#") { 120 | res.push_back({ stoi(strvec[0]),stoi(strvec[1]),stoi(strvec[2]) }); 121 | } 122 | } 123 | monitorModes = res; return; 124 | } 125 | _Use_decl_annotations_ 126 | NTSTATUS IddSampleDeviceAdd(WDFDRIVER Driver, PWDFDEVICE_INIT pDeviceInit) 127 | { 128 | NTSTATUS Status = STATUS_SUCCESS; 129 | WDF_PNPPOWER_EVENT_CALLBACKS PnpPowerCallbacks; 130 | 131 | UNREFERENCED_PARAMETER(Driver); 132 | 133 | // Register for power callbacks - in this sample only power-on is needed 134 | WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&PnpPowerCallbacks); 135 | PnpPowerCallbacks.EvtDeviceD0Entry = IddSampleDeviceD0Entry; 136 | WdfDeviceInitSetPnpPowerEventCallbacks(pDeviceInit, &PnpPowerCallbacks); 137 | 138 | IDD_CX_CLIENT_CONFIG IddConfig; 139 | IDD_CX_CLIENT_CONFIG_INIT(&IddConfig); 140 | 141 | // If the driver wishes to handle custom IoDeviceControl requests, it's necessary to use this callback since IddCx 142 | // redirects IoDeviceControl requests to an internal queue. This sample does not need this. 143 | // IddConfig.EvtIddCxDeviceIoControl = IddSampleIoDeviceControl; 144 | loadOptions("C:\\IddSampleDriver\\option.txt"); 145 | IddConfig.EvtIddCxAdapterInitFinished = IddSampleAdapterInitFinished; 146 | 147 | IddConfig.EvtIddCxParseMonitorDescription = IddSampleParseMonitorDescription; 148 | IddConfig.EvtIddCxMonitorGetDefaultDescriptionModes = IddSampleMonitorGetDefaultModes; 149 | IddConfig.EvtIddCxMonitorQueryTargetModes = IddSampleMonitorQueryModes; 150 | IddConfig.EvtIddCxAdapterCommitModes = IddSampleAdapterCommitModes; 151 | IddConfig.EvtIddCxMonitorAssignSwapChain = IddSampleMonitorAssignSwapChain; 152 | IddConfig.EvtIddCxMonitorUnassignSwapChain = IddSampleMonitorUnassignSwapChain; 153 | 154 | Status = IddCxDeviceInitConfig(pDeviceInit, &IddConfig); 155 | if (!NT_SUCCESS(Status)) 156 | { 157 | return Status; 158 | } 159 | 160 | WDF_OBJECT_ATTRIBUTES Attr; 161 | WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&Attr, IndirectDeviceContextWrapper); 162 | Attr.EvtCleanupCallback = [](WDFOBJECT Object) 163 | { 164 | // Automatically cleanup the context when the WDF object is about to be deleted 165 | auto* pContext = WdfObjectGet_IndirectDeviceContextWrapper(Object); 166 | if (pContext) 167 | { 168 | pContext->Cleanup(); 169 | } 170 | }; 171 | 172 | WDFDEVICE Device = nullptr; 173 | Status = WdfDeviceCreate(&pDeviceInit, &Attr, &Device); 174 | if (!NT_SUCCESS(Status)) 175 | { 176 | return Status; 177 | } 178 | 179 | Status = IddCxDeviceInitialize(Device); 180 | 181 | // Create a new device context object and attach it to the WDF device object 182 | auto* pContext = WdfObjectGet_IndirectDeviceContextWrapper(Device); 183 | pContext->pContext = new IndirectDeviceContext(Device); 184 | 185 | return Status; 186 | } 187 | 188 | _Use_decl_annotations_ 189 | NTSTATUS IddSampleDeviceD0Entry(WDFDEVICE Device, WDF_POWER_DEVICE_STATE PreviousState) 190 | { 191 | UNREFERENCED_PARAMETER(PreviousState); 192 | 193 | // This function is called by WDF to start the device in the fully-on power state. 194 | 195 | auto* pContext = WdfObjectGet_IndirectDeviceContextWrapper(Device); 196 | pContext->pContext->InitAdapter(); 197 | 198 | return STATUS_SUCCESS; 199 | } 200 | 201 | #pragma region Direct3DDevice 202 | 203 | Direct3DDevice::Direct3DDevice(LUID AdapterLuid) : AdapterLuid(AdapterLuid) 204 | { 205 | 206 | } 207 | 208 | Direct3DDevice::Direct3DDevice() 209 | { 210 | 211 | } 212 | 213 | HRESULT Direct3DDevice::Init() 214 | { 215 | // The DXGI factory could be cached, but if a new render adapter appears on the system, a new factory needs to be 216 | // created. If caching is desired, check DxgiFactory->IsCurrent() each time and recreate the factory if !IsCurrent. 217 | HRESULT hr = CreateDXGIFactory2(0, IID_PPV_ARGS(&DxgiFactory)); 218 | if (FAILED(hr)) 219 | { 220 | return hr; 221 | } 222 | 223 | // Find the specified render adapter 224 | hr = DxgiFactory->EnumAdapterByLuid(AdapterLuid, IID_PPV_ARGS(&Adapter)); 225 | if (FAILED(hr)) 226 | { 227 | return hr; 228 | } 229 | 230 | // Create a D3D device using the render adapter. BGRA support is required by the WHQL test suite. 231 | hr = D3D11CreateDevice(Adapter.Get(), D3D_DRIVER_TYPE_UNKNOWN, nullptr, D3D11_CREATE_DEVICE_BGRA_SUPPORT, nullptr, 0, D3D11_SDK_VERSION, &Device, nullptr, &DeviceContext); 232 | if (FAILED(hr)) 233 | { 234 | // If creating the D3D device failed, it's possible the render GPU was lost (e.g. detachable GPU) or else the 235 | // system is in a transient state. 236 | return hr; 237 | } 238 | 239 | return S_OK; 240 | } 241 | 242 | #pragma endregion 243 | 244 | #pragma region SwapChainProcessor 245 | 246 | SwapChainProcessor::SwapChainProcessor(IDDCX_SWAPCHAIN hSwapChain, shared_ptr Device, HANDLE NewFrameEvent) 247 | : m_hSwapChain(hSwapChain), m_Device(Device), m_hAvailableBufferEvent(NewFrameEvent) 248 | { 249 | m_hTerminateEvent.Attach(CreateEvent(nullptr, FALSE, FALSE, nullptr)); 250 | 251 | // Immediately create and run the swap-chain processing thread, passing 'this' as the thread parameter 252 | m_hThread.Attach(CreateThread(nullptr, 0, RunThread, this, 0, nullptr)); 253 | } 254 | 255 | SwapChainProcessor::~SwapChainProcessor() 256 | { 257 | // Alert the swap-chain processing thread to terminate 258 | SetEvent(m_hTerminateEvent.Get()); 259 | 260 | if (m_hThread.Get()) 261 | { 262 | // Wait for the thread to terminate 263 | WaitForSingleObject(m_hThread.Get(), INFINITE); 264 | } 265 | } 266 | 267 | DWORD CALLBACK SwapChainProcessor::RunThread(LPVOID Argument) 268 | { 269 | reinterpret_cast(Argument)->Run(); 270 | return 0; 271 | } 272 | 273 | void SwapChainProcessor::Run() 274 | { 275 | // For improved performance, make use of the Multimedia Class Scheduler Service, which will intelligently 276 | // prioritize this thread for improved throughput in high CPU-load scenarios. 277 | DWORD AvTask = 0; 278 | HANDLE AvTaskHandle = AvSetMmThreadCharacteristicsW(L"Distribution", &AvTask); 279 | 280 | RunCore(); 281 | 282 | // Always delete the swap-chain object when swap-chain processing loop terminates in order to kick the system to 283 | // provide a new swap-chain if necessary. 284 | WdfObjectDelete((WDFOBJECT)m_hSwapChain); 285 | m_hSwapChain = nullptr; 286 | 287 | AvRevertMmThreadCharacteristics(AvTaskHandle); 288 | } 289 | 290 | void SwapChainProcessor::RunCore() 291 | { 292 | // Get the DXGI device interface 293 | ComPtr DxgiDevice; 294 | HRESULT hr = m_Device->Device.As(&DxgiDevice); 295 | if (FAILED(hr)) 296 | { 297 | return; 298 | } 299 | 300 | IDARG_IN_SWAPCHAINSETDEVICE SetDevice = {}; 301 | SetDevice.pDevice = DxgiDevice.Get(); 302 | 303 | hr = IddCxSwapChainSetDevice(m_hSwapChain, &SetDevice); 304 | if (FAILED(hr)) 305 | { 306 | return; 307 | } 308 | 309 | // Acquire and release buffers in a loop 310 | for (;;) 311 | { 312 | ComPtr AcquiredBuffer; 313 | 314 | // Ask for the next buffer from the producer 315 | IDARG_OUT_RELEASEANDACQUIREBUFFER Buffer = {}; 316 | hr = IddCxSwapChainReleaseAndAcquireBuffer(m_hSwapChain, &Buffer); 317 | 318 | // AcquireBuffer immediately returns STATUS_PENDING if no buffer is yet available 319 | if (hr == E_PENDING) 320 | { 321 | // We must wait for a new buffer 322 | HANDLE WaitHandles[] = 323 | { 324 | m_hAvailableBufferEvent, 325 | m_hTerminateEvent.Get() 326 | }; 327 | DWORD WaitResult = WaitForMultipleObjects(ARRAYSIZE(WaitHandles), WaitHandles, FALSE, 16); 328 | if (WaitResult == WAIT_OBJECT_0 || WaitResult == WAIT_TIMEOUT) 329 | { 330 | // We have a new buffer, so try the AcquireBuffer again 331 | continue; 332 | } 333 | else if (WaitResult == WAIT_OBJECT_0 + 1) 334 | { 335 | // We need to terminate 336 | break; 337 | } 338 | else 339 | { 340 | // The wait was cancelled or something unexpected happened 341 | hr = HRESULT_FROM_WIN32(WaitResult); 342 | break; 343 | } 344 | } 345 | else if (SUCCEEDED(hr)) 346 | { 347 | AcquiredBuffer.Attach(Buffer.MetaData.pSurface); 348 | 349 | // ============================== 350 | // TODO: Process the frame here 351 | // 352 | // This is the most performance-critical section of code in an IddCx driver. It's important that whatever 353 | // is done with the acquired surface be finished as quickly as possible. This operation could be: 354 | // * a GPU copy to another buffer surface for later processing (such as a staging surface for mapping to CPU memory) 355 | // * a GPU encode operation 356 | // * a GPU VPBlt to another surface 357 | // * a GPU custom compute shader encode operation 358 | // ============================== 359 | 360 | AcquiredBuffer.Reset(); 361 | hr = IddCxSwapChainFinishedProcessingFrame(m_hSwapChain); 362 | if (FAILED(hr)) 363 | { 364 | break; 365 | } 366 | 367 | // ============================== 368 | // TODO: Report frame statistics once the asynchronous encode/send work is completed 369 | // 370 | // Drivers should report information about sub-frame timings, like encode time, send time, etc. 371 | // ============================== 372 | // IddCxSwapChainReportFrameStatistics(m_hSwapChain, ...); 373 | } 374 | else 375 | { 376 | // The swap-chain was likely abandoned (e.g. DXGI_ERROR_ACCESS_LOST), so exit the processing loop 377 | break; 378 | } 379 | } 380 | } 381 | 382 | #pragma endregion 383 | 384 | #pragma region IndirectDeviceContext 385 | 386 | const UINT64 MHZ = 1000000; 387 | const UINT64 KHZ = 1000; 388 | 389 | constexpr DISPLAYCONFIG_VIDEO_SIGNAL_INFO dispinfo(UINT32 h, UINT32 v, UINT32 r) { 390 | const UINT32 clock_rate = r * (v + 4) * (v + 4) + 1000; 391 | return { 392 | clock_rate, // pixel clock rate [Hz] 393 | { clock_rate, v + 4 }, // fractional horizontal refresh rate [Hz] 394 | { clock_rate, (v + 4) * (v + 4) }, // fractional vertical refresh rate [Hz] 395 | { h, v }, // (horizontal, vertical) active pixel resolution 396 | { h + 4, v + 4 }, // (horizontal, vertical) total pixel resolution 397 | { { 255, 0 }}, // video standard and vsync divider 398 | DISPLAYCONFIG_SCANLINE_ORDERING_PROGRESSIVE 399 | }; 400 | } 401 | 402 | // This is a sample monitor EDID - FOR SAMPLE PURPOSES ONLY 403 | const BYTE IndirectDeviceContext::s_KnownMonitorEdid[] = 404 | { 405 | /* 0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x79,0x5E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xA6,0x01,0x03,0x80,0x28, 406 | 0x1E,0x78,0x0A,0xEE,0x91,0xA3,0x54,0x4C,0x99,0x26,0x0F,0x50,0x54,0x20,0x00,0x00,0x01,0x01,0x01,0x01,0x01,0x01, 407 | 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0xA0,0x0F,0x20,0x00,0x31,0x58,0x1C,0x20,0x28,0x80,0x14,0x00, 408 | 0x90,0x2C,0x11,0x00,0x00,0x1E,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 409 | 0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 410 | 0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6E */ 411 | 412 | 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x31, 0xD8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 413 | 0x05, 0x16, 0x01, 0x03, 0x6D, 0x32, 0x1C, 0x78, 0xEA, 0x5E, 0xC0, 0xA4, 0x59, 0x4A, 0x98, 0x25, 414 | 0x20, 0x50, 0x54, 0x00, 0x00, 0x00, 0xD1, 0xC0, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 415 | 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3A, 0x80, 0x18, 0x71, 0x38, 0x2D, 0x40, 0x58, 0x2C, 416 | 0x45, 0x00, 0xF4, 0x19, 0x11, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x4C, 0x69, 0x6E, 417 | 0x75, 0x78, 0x20, 0x23, 0x30, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x3B, 418 | 0x3D, 0x42, 0x44, 0x0F, 0x00, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xFC, 419 | 0x00, 0x4C, 0x69, 0x6E, 0x75, 0x78, 0x20, 0x46, 0x48, 0x44, 0x0A, 0x20, 0x20, 0x20, 0x00, 0x05 420 | 421 | }; 422 | 423 | IndirectDeviceContext::IndirectDeviceContext(_In_ WDFDEVICE WdfDevice) : 424 | m_WdfDevice(WdfDevice) 425 | { 426 | } 427 | 428 | IndirectDeviceContext::~IndirectDeviceContext() 429 | { 430 | m_ProcessingThread.reset(); 431 | } 432 | 433 | #define NUM_VIRTUAL_DISPLAYS 1 434 | 435 | void IndirectDeviceContext::InitAdapter() 436 | { 437 | // ============================== 438 | // TODO: Update the below diagnostic information in accordance with the target hardware. The strings and version 439 | // numbers are used for telemetry and may be displayed to the user in some situations. 440 | // 441 | // This is also where static per-adapter capabilities are determined. 442 | // ============================== 443 | 444 | IDDCX_ADAPTER_CAPS AdapterCaps = {}; 445 | AdapterCaps.Size = sizeof(AdapterCaps); 446 | 447 | // Declare basic feature support for the adapter (required) 448 | AdapterCaps.MaxMonitorsSupported = numVirtualDisplays; 449 | AdapterCaps.EndPointDiagnostics.Size = sizeof(AdapterCaps.EndPointDiagnostics); 450 | AdapterCaps.EndPointDiagnostics.GammaSupport = IDDCX_FEATURE_IMPLEMENTATION_NONE; 451 | AdapterCaps.EndPointDiagnostics.TransmissionType = IDDCX_TRANSMISSION_TYPE_WIRED_OTHER; 452 | 453 | // Declare your device strings for telemetry (required) 454 | AdapterCaps.EndPointDiagnostics.pEndPointFriendlyName = L"IddSample Device"; 455 | AdapterCaps.EndPointDiagnostics.pEndPointManufacturerName = L"Microsoft"; 456 | AdapterCaps.EndPointDiagnostics.pEndPointModelName = L"IddSample Model"; 457 | 458 | // Declare your hardware and firmware versions (required) 459 | IDDCX_ENDPOINT_VERSION Version = {}; 460 | Version.Size = sizeof(Version); 461 | Version.MajorVer = 1; 462 | AdapterCaps.EndPointDiagnostics.pFirmwareVersion = &Version; 463 | AdapterCaps.EndPointDiagnostics.pHardwareVersion = &Version; 464 | 465 | // Initialize a WDF context that can store a pointer to the device context object 466 | WDF_OBJECT_ATTRIBUTES Attr; 467 | WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&Attr, IndirectDeviceContextWrapper); 468 | 469 | IDARG_IN_ADAPTER_INIT AdapterInit = {}; 470 | AdapterInit.WdfDevice = m_WdfDevice; 471 | AdapterInit.pCaps = &AdapterCaps; 472 | AdapterInit.ObjectAttributes = &Attr; 473 | 474 | // Start the initialization of the adapter, which will trigger the AdapterFinishInit callback later 475 | IDARG_OUT_ADAPTER_INIT AdapterInitOut; 476 | NTSTATUS Status = IddCxAdapterInitAsync(&AdapterInit, &AdapterInitOut); 477 | 478 | if (NT_SUCCESS(Status)) 479 | { 480 | // Store a reference to the WDF adapter handle 481 | m_Adapter = AdapterInitOut.AdapterObject; 482 | 483 | // Store the device context object into the WDF object context 484 | auto* pContext = WdfObjectGet_IndirectDeviceContextWrapper(AdapterInitOut.AdapterObject); 485 | pContext->pContext = this; 486 | } 487 | } 488 | 489 | void IndirectDeviceContext::FinishInit() 490 | { 491 | for (unsigned int i = 0; i < numVirtualDisplays; i++) { 492 | CreateMonitor(i); 493 | } 494 | } 495 | 496 | void IndirectDeviceContext::CreateMonitor(unsigned int index) { 497 | // ============================== 498 | // TODO: In a real driver, the EDID should be retrieved dynamically from a connected physical monitor. The EDID 499 | // provided here is purely for demonstration, as it describes only 640x480 @ 60 Hz and 800x600 @ 60 Hz. Monitor 500 | // manufacturers are required to correctly fill in physical monitor attributes in order to allow the OS to optimize 501 | // settings like viewing distance and scale factor. Manufacturers should also use a unique serial number every 502 | // single device to ensure the OS can tell the monitors apart. 503 | // ============================== 504 | 505 | WDF_OBJECT_ATTRIBUTES Attr; 506 | WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&Attr, IndirectDeviceContextWrapper); 507 | 508 | IDDCX_MONITOR_INFO MonitorInfo = {}; 509 | MonitorInfo.Size = sizeof(MonitorInfo); 510 | MonitorInfo.MonitorType = DISPLAYCONFIG_OUTPUT_TECHNOLOGY_HDMI; 511 | MonitorInfo.ConnectorIndex = index; 512 | MonitorInfo.MonitorDescription.Size = sizeof(MonitorInfo.MonitorDescription); 513 | MonitorInfo.MonitorDescription.Type = IDDCX_MONITOR_DESCRIPTION_TYPE_EDID; 514 | MonitorInfo.MonitorDescription.DataSize = sizeof(s_KnownMonitorEdid); 515 | MonitorInfo.MonitorDescription.pData = const_cast(s_KnownMonitorEdid); 516 | 517 | // ============================== 518 | // TODO: The monitor's container ID should be distinct from "this" device's container ID if the monitor is not 519 | // permanently attached to the display adapter device object. The container ID is typically made unique for each 520 | // monitor and can be used to associate the monitor with other devices, like audio or input devices. In this 521 | // sample we generate a random container ID GUID, but it's best practice to choose a stable container ID for a 522 | // unique monitor or to use "this" device's container ID for a permanent/integrated monitor. 523 | // ============================== 524 | 525 | // Create a container ID 526 | CoCreateGuid(&MonitorInfo.MonitorContainerId); 527 | 528 | IDARG_IN_MONITORCREATE MonitorCreate = {}; 529 | MonitorCreate.ObjectAttributes = &Attr; 530 | MonitorCreate.pMonitorInfo = &MonitorInfo; 531 | 532 | // Create a monitor object with the specified monitor descriptor 533 | IDARG_OUT_MONITORCREATE MonitorCreateOut; 534 | NTSTATUS Status = IddCxMonitorCreate(m_Adapter, &MonitorCreate, &MonitorCreateOut); 535 | if (NT_SUCCESS(Status)) 536 | { 537 | m_Monitor = MonitorCreateOut.MonitorObject; 538 | 539 | // Associate the monitor with this device context 540 | auto* pContext = WdfObjectGet_IndirectDeviceContextWrapper(MonitorCreateOut.MonitorObject); 541 | pContext->pContext = this; 542 | 543 | // Tell the OS that the monitor has been plugged in 544 | IDARG_OUT_MONITORARRIVAL ArrivalOut; 545 | Status = IddCxMonitorArrival(m_Monitor, &ArrivalOut); 546 | } 547 | } 548 | 549 | void IndirectDeviceContext::AssignSwapChain(IDDCX_SWAPCHAIN SwapChain, LUID RenderAdapter, HANDLE NewFrameEvent) 550 | { 551 | m_ProcessingThread.reset(); 552 | 553 | auto Device = make_shared(RenderAdapter); 554 | if (FAILED(Device->Init())) 555 | { 556 | // It's important to delete the swap-chain if D3D initialization fails, so that the OS knows to generate a new 557 | // swap-chain and try again. 558 | WdfObjectDelete(SwapChain); 559 | } 560 | else 561 | { 562 | // Create a new swap-chain processing thread 563 | m_ProcessingThread.reset(new SwapChainProcessor(SwapChain, Device, NewFrameEvent)); 564 | } 565 | } 566 | 567 | void IndirectDeviceContext::UnassignSwapChain() 568 | { 569 | // Stop processing the last swap-chain 570 | m_ProcessingThread.reset(); 571 | } 572 | 573 | #pragma endregion 574 | 575 | #pragma region DDI Callbacks 576 | 577 | _Use_decl_annotations_ 578 | NTSTATUS IddSampleAdapterInitFinished(IDDCX_ADAPTER AdapterObject, const IDARG_IN_ADAPTER_INIT_FINISHED* pInArgs) 579 | { 580 | // This is called when the OS has finished setting up the adapter for use by the IddCx driver. It's now possible 581 | // to report attached monitors. 582 | 583 | auto* pContext = WdfObjectGet_IndirectDeviceContextWrapper(AdapterObject); 584 | if (NT_SUCCESS(pInArgs->AdapterInitStatus)) 585 | { 586 | pContext->pContext->FinishInit(); 587 | } 588 | 589 | return STATUS_SUCCESS; 590 | } 591 | 592 | _Use_decl_annotations_ 593 | NTSTATUS IddSampleAdapterCommitModes(IDDCX_ADAPTER AdapterObject, const IDARG_IN_COMMITMODES* pInArgs) 594 | { 595 | UNREFERENCED_PARAMETER(AdapterObject); 596 | UNREFERENCED_PARAMETER(pInArgs); 597 | 598 | // For the sample, do nothing when modes are picked - the swap-chain is taken care of by IddCx 599 | 600 | // ============================== 601 | // TODO: In a real driver, this function would be used to reconfigure the device to commit the new modes. Loop 602 | // through pInArgs->pPaths and look for IDDCX_PATH_FLAGS_ACTIVE. Any path not active is inactive (e.g. the monitor 603 | // should be turned off). 604 | // ============================== 605 | 606 | return STATUS_SUCCESS; 607 | } 608 | _Use_decl_annotations_ 609 | NTSTATUS IddSampleParseMonitorDescription(const IDARG_IN_PARSEMONITORDESCRIPTION* pInArgs, IDARG_OUT_PARSEMONITORDESCRIPTION* pOutArgs) 610 | { 611 | // ============================== 612 | // TODO: In a real driver, this function would be called to generate monitor modes for an EDID by parsing it. In 613 | // this sample driver, we hard-code the EDID, so this function can generate known modes. 614 | // ============================== 615 | 616 | for (int i = 0; i < monitorModes.size(); i++) { 617 | s_KnownMonitorModes2.push_back(dispinfo(std::get<0>(monitorModes[i]), std::get<1>(monitorModes[i]), std::get<2>(monitorModes[i]))); 618 | } 619 | pOutArgs->MonitorModeBufferOutputCount = (UINT)monitorModes.size(); 620 | 621 | if (pInArgs->MonitorModeBufferInputCount < monitorModes.size()) 622 | { 623 | // Return success if there was no buffer, since the caller was only asking for a count of modes 624 | return (pInArgs->MonitorModeBufferInputCount > 0) ? STATUS_BUFFER_TOO_SMALL : STATUS_SUCCESS; 625 | } 626 | else 627 | { 628 | // Copy the known modes to the output buffer 629 | for (DWORD ModeIndex = 0; ModeIndex < monitorModes.size(); ModeIndex++) 630 | { 631 | pInArgs->pMonitorModes[ModeIndex].Size = sizeof(IDDCX_MONITOR_MODE); 632 | pInArgs->pMonitorModes[ModeIndex].Origin = IDDCX_MONITOR_MODE_ORIGIN_MONITORDESCRIPTOR; 633 | pInArgs->pMonitorModes[ModeIndex].MonitorVideoSignalInfo = s_KnownMonitorModes2[ModeIndex]; 634 | } 635 | 636 | // Set the preferred mode as represented in the EDID 637 | pOutArgs->PreferredMonitorModeIdx = 0; 638 | 639 | return STATUS_SUCCESS; 640 | } 641 | } 642 | 643 | _Use_decl_annotations_ 644 | NTSTATUS IddSampleMonitorGetDefaultModes(IDDCX_MONITOR MonitorObject, const IDARG_IN_GETDEFAULTDESCRIPTIONMODES* pInArgs, IDARG_OUT_GETDEFAULTDESCRIPTIONMODES* pOutArgs) 645 | { 646 | UNREFERENCED_PARAMETER(MonitorObject); 647 | UNREFERENCED_PARAMETER(pInArgs); 648 | UNREFERENCED_PARAMETER(pOutArgs); 649 | 650 | // Should never be called since we create a single monitor with a known EDID in this sample driver. 651 | 652 | // ============================== 653 | // TODO: In a real driver, this function would be called to generate monitor modes for a monitor with no EDID. 654 | // Drivers should report modes that are guaranteed to be supported by the transport protocol and by nearly all 655 | // monitors (such 640x480, 800x600, or 1024x768). If the driver has access to monitor modes from a descriptor other 656 | // than an EDID, those modes would also be reported here. 657 | // ============================== 658 | 659 | return STATUS_NOT_IMPLEMENTED; 660 | } 661 | 662 | /// 663 | /// Creates a target mode from the fundamental mode attributes. 664 | /// 665 | void CreateTargetMode(DISPLAYCONFIG_VIDEO_SIGNAL_INFO& Mode, UINT Width, UINT Height, UINT VSync) 666 | { 667 | Mode.totalSize.cx = Mode.activeSize.cx = Width; 668 | Mode.totalSize.cy = Mode.activeSize.cy = Height; 669 | Mode.AdditionalSignalInfo.vSyncFreqDivider = 1; 670 | Mode.AdditionalSignalInfo.videoStandard = 255; 671 | Mode.vSyncFreq.Numerator = VSync; 672 | Mode.vSyncFreq.Denominator = Mode.hSyncFreq.Denominator = 1; 673 | Mode.hSyncFreq.Numerator = VSync * Height; 674 | Mode.scanLineOrdering = DISPLAYCONFIG_SCANLINE_ORDERING_PROGRESSIVE; 675 | Mode.pixelRate = VSync * Width * Height; 676 | } 677 | 678 | void CreateTargetMode(IDDCX_TARGET_MODE& Mode, UINT Width, UINT Height, UINT VSync) 679 | { 680 | Mode.Size = sizeof(Mode); 681 | CreateTargetMode(Mode.TargetVideoSignalInfo.targetVideoSignalInfo, Width, Height, VSync); 682 | } 683 | 684 | _Use_decl_annotations_ 685 | NTSTATUS IddSampleMonitorQueryModes(IDDCX_MONITOR MonitorObject, const IDARG_IN_QUERYTARGETMODES* pInArgs, IDARG_OUT_QUERYTARGETMODES* pOutArgs)//////////////////////////////////////////////////////////////////////////////// 686 | { 687 | UNREFERENCED_PARAMETER(MonitorObject); 688 | 689 | vector TargetModes(monitorModes.size()); 690 | 691 | // Create a set of modes supported for frame processing and scan-out. These are typically not based on the 692 | // monitor's descriptor and instead are based on the static processing capability of the device. The OS will 693 | // report the available set of modes for a given output as the intersection of monitor modes with target modes. 694 | 695 | for (int i = 0; i < monitorModes.size(); i++) { 696 | CreateTargetMode(TargetModes[i], std::get<0>(monitorModes[i]), std::get<1>(monitorModes[i]), std::get<2>(monitorModes[i])); 697 | } 698 | 699 | pOutArgs->TargetModeBufferOutputCount = (UINT)TargetModes.size(); 700 | 701 | if (pInArgs->TargetModeBufferInputCount >= TargetModes.size()) 702 | { 703 | copy(TargetModes.begin(), TargetModes.end(), pInArgs->pTargetModes); 704 | } 705 | 706 | return STATUS_SUCCESS; 707 | } 708 | 709 | _Use_decl_annotations_ 710 | NTSTATUS IddSampleMonitorAssignSwapChain(IDDCX_MONITOR MonitorObject, const IDARG_IN_SETSWAPCHAIN* pInArgs) 711 | { 712 | auto* pContext = WdfObjectGet_IndirectDeviceContextWrapper(MonitorObject); 713 | pContext->pContext->AssignSwapChain(pInArgs->hSwapChain, pInArgs->RenderAdapterLuid, pInArgs->hNextSurfaceAvailable); 714 | return STATUS_SUCCESS; 715 | } 716 | 717 | _Use_decl_annotations_ 718 | NTSTATUS IddSampleMonitorUnassignSwapChain(IDDCX_MONITOR MonitorObject) 719 | { 720 | auto* pContext = WdfObjectGet_IndirectDeviceContextWrapper(MonitorObject); 721 | pContext->pContext->UnassignSwapChain(); 722 | return STATUS_SUCCESS; 723 | } 724 | 725 | #pragma endregion 726 | -------------------------------------------------------------------------------- /IddSampleDriver/Driver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define NOMINMAX 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | 18 | #include "Trace.h" 19 | 20 | namespace Microsoft 21 | { 22 | namespace WRL 23 | { 24 | namespace Wrappers 25 | { 26 | // Adds a wrapper for thread handles to the existing set of WRL handle wrapper classes 27 | typedef HandleT Thread; 28 | } 29 | } 30 | } 31 | 32 | namespace Microsoft 33 | { 34 | namespace IndirectDisp 35 | { 36 | /// 37 | /// Manages the creation and lifetime of a Direct3D render device. 38 | /// 39 | struct Direct3DDevice 40 | { 41 | Direct3DDevice(LUID AdapterLuid); 42 | Direct3DDevice(); 43 | HRESULT Init(); 44 | 45 | LUID AdapterLuid; 46 | Microsoft::WRL::ComPtr DxgiFactory; 47 | Microsoft::WRL::ComPtr Adapter; 48 | Microsoft::WRL::ComPtr Device; 49 | Microsoft::WRL::ComPtr DeviceContext; 50 | }; 51 | 52 | /// 53 | /// Manages a thread that consumes buffers from an indirect display swap-chain object. 54 | /// 55 | class SwapChainProcessor 56 | { 57 | public: 58 | SwapChainProcessor(IDDCX_SWAPCHAIN hSwapChain, std::shared_ptr Device, HANDLE NewFrameEvent); 59 | ~SwapChainProcessor(); 60 | 61 | private: 62 | static DWORD CALLBACK RunThread(LPVOID Argument); 63 | 64 | void Run(); 65 | void RunCore(); 66 | 67 | public: 68 | IDDCX_SWAPCHAIN m_hSwapChain; 69 | std::shared_ptr m_Device; 70 | HANDLE m_hAvailableBufferEvent; 71 | Microsoft::WRL::Wrappers::Thread m_hThread; 72 | Microsoft::WRL::Wrappers::Event m_hTerminateEvent; 73 | }; 74 | 75 | /// 76 | /// Provides a sample implementation of an indirect display driver. 77 | /// 78 | class IndirectDeviceContext 79 | { 80 | public: 81 | IndirectDeviceContext(_In_ WDFDEVICE WdfDevice); 82 | virtual ~IndirectDeviceContext(); 83 | 84 | void InitAdapter(); 85 | void FinishInit(); 86 | 87 | void CreateMonitor(unsigned int index); 88 | 89 | void AssignSwapChain(IDDCX_SWAPCHAIN SwapChain, LUID RenderAdapter, HANDLE NewFrameEvent); 90 | void UnassignSwapChain(); 91 | 92 | protected: 93 | 94 | WDFDEVICE m_WdfDevice; 95 | IDDCX_ADAPTER m_Adapter; 96 | IDDCX_MONITOR m_Monitor; 97 | IDDCX_MONITOR m_Monitor2; 98 | 99 | std::unique_ptr m_ProcessingThread; 100 | 101 | public: 102 | static const DISPLAYCONFIG_VIDEO_SIGNAL_INFO s_KnownMonitorModes[]; 103 | static const BYTE s_KnownMonitorEdid[]; 104 | }; 105 | } 106 | } -------------------------------------------------------------------------------- /IddSampleDriver/IddSampleDriver.inf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ge9/IddSampleDriver/2737d7414f6692a17aa39914d7f0346b52176b96/IddSampleDriver/IddSampleDriver.inf -------------------------------------------------------------------------------- /IddSampleDriver/IddSampleDriver.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.31129.286 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "IddSampleDriver", "IddSampleDriver.vcxproj", "{2D54CB75-8B17-4F11-97DC-847B0244CD46}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|ARM = Debug|ARM 11 | Debug|ARM64 = Debug|ARM64 12 | Debug|x64 = Debug|x64 13 | Debug|x86 = Debug|x86 14 | Release|ARM = Release|ARM 15 | Release|ARM64 = Release|ARM64 16 | Release|x64 = Release|x64 17 | Release|x86 = Release|x86 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Debug|ARM.ActiveCfg = Debug|ARM 21 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Debug|ARM.Build.0 = Debug|ARM 22 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Debug|ARM.Deploy.0 = Debug|ARM 23 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Debug|ARM64.ActiveCfg = Debug|ARM64 24 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Debug|ARM64.Build.0 = Debug|ARM64 25 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Debug|ARM64.Deploy.0 = Debug|ARM64 26 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Debug|x64.ActiveCfg = Debug|x64 27 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Debug|x64.Build.0 = Debug|x64 28 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Debug|x64.Deploy.0 = Debug|x64 29 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Debug|x86.ActiveCfg = Debug|Win32 30 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Debug|x86.Build.0 = Debug|Win32 31 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Debug|x86.Deploy.0 = Debug|Win32 32 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Release|ARM.ActiveCfg = Release|ARM 33 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Release|ARM.Build.0 = Release|ARM 34 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Release|ARM.Deploy.0 = Release|ARM 35 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Release|ARM64.ActiveCfg = Release|ARM64 36 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Release|ARM64.Build.0 = Release|ARM64 37 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Release|ARM64.Deploy.0 = Release|ARM64 38 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Release|x64.ActiveCfg = Release|x64 39 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Release|x64.Build.0 = Release|x64 40 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Release|x64.Deploy.0 = Release|x64 41 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Release|x86.ActiveCfg = Release|Win32 42 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Release|x86.Build.0 = Release|Win32 43 | {2D54CB75-8B17-4F11-97DC-847B0244CD46}.Release|x86.Deploy.0 = Release|Win32 44 | EndGlobalSection 45 | GlobalSection(SolutionProperties) = preSolution 46 | HideSolutionNode = FALSE 47 | EndGlobalSection 48 | GlobalSection(ExtensibilityGlobals) = postSolution 49 | SolutionGuid = {9A8BE7D9-9F0F-40A2-BA2D-8AE9E35890DB} 50 | EndGlobalSection 51 | EndGlobal 52 | -------------------------------------------------------------------------------- /IddSampleDriver/IddSampleDriver.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | Debug 22 | ARM 23 | 24 | 25 | Release 26 | ARM 27 | 28 | 29 | Debug 30 | ARM64 31 | 32 | 33 | Release 34 | ARM64 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | {2D54CB75-8B17-4F11-97DC-847B0244CD46} 49 | {32909489-7be5-497b-aafa-db6669d9b44b} 50 | v4.5 51 | 12.0 52 | Debug 53 | Win32 54 | IddSampleDriver 55 | 56 | 57 | WindowsUserModeDriver10.0 58 | DynamicLibrary 59 | Universal 60 | 61 | 62 | WindowsUserModeDriver10.0 63 | DynamicLibrary 64 | Universal 65 | 66 | 67 | WindowsUserModeDriver10.0 68 | DynamicLibrary 69 | Universal 70 | 71 | 72 | WindowsUserModeDriver10.0 73 | DynamicLibrary 74 | Universal 75 | 76 | 77 | WindowsUserModeDriver10.0 78 | DynamicLibrary 79 | Universal 80 | 81 | 82 | WindowsUserModeDriver10.0 83 | DynamicLibrary 84 | Universal 85 | 86 | 87 | WindowsUserModeDriver10.0 88 | DynamicLibrary 89 | Universal 90 | 91 | 92 | WindowsUserModeDriver10.0 93 | DynamicLibrary 94 | Universal 95 | 96 | 97 | 98 | Windows10 99 | true 100 | 2 101 | true 102 | 1 103 | 0 104 | 105 | 106 | Windows10 107 | false 108 | 2 109 | true 110 | 1 111 | 0 112 | 113 | 114 | Windows10 115 | true 116 | 2 117 | true 118 | 1 119 | 0 120 | Spectre 121 | 122 | 123 | Windows10 124 | false 125 | 2 126 | true 127 | 1 128 | 0 129 | 130 | 131 | Windows10 132 | true 133 | 2 134 | true 135 | 1 136 | 0 137 | 138 | 139 | Windows10 140 | false 141 | 2 142 | true 143 | 1 144 | 0 145 | 146 | 147 | Windows10 148 | true 149 | 2 150 | true 151 | 1 152 | 0 153 | 154 | 155 | Windows10 156 | false 157 | 2 158 | true 159 | 1 160 | 0 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | DbgengRemoteDebugger 171 | true 172 | 173 | 174 | DbgengRemoteDebugger 175 | true 176 | 177 | 178 | DbgengRemoteDebugger 179 | true 180 | true 181 | 182 | 183 | DbgengRemoteDebugger 184 | true 185 | true 186 | 187 | 188 | DbgengRemoteDebugger 189 | true 190 | 191 | 192 | DbgengRemoteDebugger 193 | true 194 | 195 | 196 | DbgengRemoteDebugger 197 | true 198 | 199 | 200 | DbgengRemoteDebugger 201 | true 202 | 203 | 204 | 205 | true 206 | true 207 | trace.h 208 | Async 209 | true 210 | 211 | 212 | %(AdditionalDependencies);OneCoreUAP.lib;avrt.lib 213 | 214 | 215 | 216 | 217 | true 218 | true 219 | trace.h 220 | Async 221 | true 222 | 223 | 224 | %(AdditionalDependencies);OneCoreUAP.lib;avrt.lib 225 | 226 | 227 | 228 | 229 | true 230 | true 231 | trace.h 232 | Async 233 | true 234 | 235 | 236 | %(AdditionalDependencies);OneCoreUAP.lib;avrt.lib 237 | 238 | 239 | 240 | 241 | true 242 | true 243 | trace.h 244 | Async 245 | true 246 | 247 | 248 | %(AdditionalDependencies);OneCoreUAP.lib;avrt.lib 249 | 250 | 251 | SHA256 252 | 253 | 254 | 255 | 256 | true 257 | true 258 | trace.h 259 | Async 260 | true 261 | 262 | 263 | %(AdditionalDependencies);OneCoreUAP.lib;avrt.lib 264 | 265 | 266 | 267 | 268 | true 269 | true 270 | trace.h 271 | Async 272 | true 273 | 274 | 275 | %(AdditionalDependencies);OneCoreUAP.lib;avrt.lib 276 | 277 | 278 | 279 | 280 | true 281 | true 282 | trace.h 283 | Async 284 | true 285 | 286 | 287 | %(AdditionalDependencies);OneCoreUAP.lib;avrt.lib 288 | 289 | 290 | 291 | 292 | true 293 | true 294 | trace.h 295 | Async 296 | true 297 | 298 | 299 | %(AdditionalDependencies);OneCoreUAP.lib;avrt.lib 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | -------------------------------------------------------------------------------- /IddSampleDriver/IddSampleDriver.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | {8E41214B-6785-4CFE-B992-037D68949A14} 18 | inf;inv;inx;mof;mc; 19 | 20 | 21 | 22 | 23 | Driver Files 24 | 25 | 26 | 27 | 28 | Header Files 29 | 30 | 31 | Header Files 32 | 33 | 34 | 35 | 36 | Source Files 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /IddSampleDriver/Trace.h: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Module Name: 4 | 5 | Internal.h 6 | 7 | Abstract: 8 | 9 | This module contains the local type definitions for the 10 | driver. 11 | 12 | Environment: 13 | 14 | Windows User-Mode Driver Framework 2 15 | 16 | --*/ 17 | 18 | // 19 | // Define the tracing flags. 20 | // 21 | // Tracing GUID - b254994f-46e6-4718-80a0-0a3aa50d6ce4 22 | // 23 | 24 | #define WPP_CONTROL_GUIDS \ 25 | WPP_DEFINE_CONTROL_GUID( \ 26 | MyDriver1TraceGuid, (b254994f,46e6,4718,80a0,0a3aa50d6ce4), \ 27 | \ 28 | WPP_DEFINE_BIT(MYDRIVER_ALL_INFO) \ 29 | WPP_DEFINE_BIT(TRACE_DRIVER) \ 30 | WPP_DEFINE_BIT(TRACE_DEVICE) \ 31 | WPP_DEFINE_BIT(TRACE_QUEUE) \ 32 | ) 33 | 34 | #define WPP_FLAG_LEVEL_LOGGER(flag, level) \ 35 | WPP_LEVEL_LOGGER(flag) 36 | 37 | #define WPP_FLAG_LEVEL_ENABLED(flag, level) \ 38 | (WPP_LEVEL_ENABLED(flag) && \ 39 | WPP_CONTROL(WPP_BIT_ ## flag).Level >= level) 40 | 41 | #define WPP_LEVEL_FLAGS_LOGGER(lvl,flags) \ 42 | WPP_LEVEL_LOGGER(flags) 43 | 44 | #define WPP_LEVEL_FLAGS_ENABLED(lvl, flags) \ 45 | (WPP_LEVEL_ENABLED(flags) && WPP_CONTROL(WPP_BIT_ ## flags).Level >= lvl) 46 | 47 | // 48 | // This comment block is scanned by the trace preprocessor to define our 49 | // Trace function. 50 | // 51 | // begin_wpp config 52 | // FUNC Trace{FLAG=MYDRIVER_ALL_INFO}(LEVEL, MSG, ...); 53 | // FUNC TraceEvents(LEVEL, FLAGS, MSG, ...); 54 | // end_wpp 55 | 56 | // 57 | // 58 | // Driver specific #defines 59 | // 60 | #if UMDF_VERSION_MAJOR == 2 && UMDF_VERSION_MINOR == 0 61 | // TODO: Update the name of the tracing provider 62 | #define MYDRIVER_TRACING_ID L"Microsoft\\UMDF2.0\\IddSampleDriver V1.0" 63 | #endif -------------------------------------------------------------------------------- /IddSampleDriver/readme.md: -------------------------------------------------------------------------------- 1 | # Get 5 virtual monitors for the price of zero! 2 | 3 | # Steps (for experienced pros only!!!) 4 | 1. Download the latest release as a zip file. 5 | 6 | 2. As an administrator, Run the *.bat file to add the driver certificate as a trusted root certificate. 7 | 8 | 3. Don't install the inf. Open device manager, click on any device, then click on the "Action" menu and click "Add Legacy Hardware" 9 | 10 | 4. Select "Add hardware from a list (Advanced)" and then select Display adapters 11 | 12 | 5. Click "Have Disk..." and click the "Browse..." button. Navigate to the extracted files and select the inf file. 13 | 14 | 6. You are done! Go to display settings to customize the resolution of the additional displays. These displays show up in Oculus and should be able to be streamed from. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | License MIT and CC0 or Public Domain (for changes I made, check with Microsoft for their license), whichever is least restrictive -- Use it 2 | 3 | AS IS - NO IMPLICIT OR EXPLICIT warranty This may break your computer, it didn't break mine. It runs in User Mode which means it's less likely to cause system instability like the Blue Screen of Death. 4 | Check out the latest release to download, or find other versions below: 5 | # Newer Versions 6 | # Fork that is easy to install 7 | https://github.com/ge9/IddSampleDriver 8 | 9 | ## Fork with HDR 10 | https://github.com/itsmikethetech/Virtual-Display-Driver 11 | 12 | If you want me to build on this donate eth or similar here: 0xB01b6328F8Be53c852a54432bbEe630cE0Bd559a 13 | I now have a NEAR address: moopaloo.near 14 | 15 | Thanks to https://github.com/akatrevorjay/edid-generator for the hi-res EDID. 16 | 17 | # Indirect Display Driver Sample # 18 | 19 | This is a sample driver that shows how to create a Windows Indirect Display Driver using the IddCx class extension driver. 20 | 21 | ## Installation 22 | 23 | ### Scoop (recommended) 24 | If you have [Scoop](https://scoop.sh/), you can easily install this driver in one go. In an elevated prompt, run: 25 | ```powershell 26 | scoop bucket add extras 27 | scoop bucket add nonportable 28 | scoop install iddsampledriver-ge9-np -g 29 | ``` 30 | The driver should be automatically installed and should be working out of the box. 31 | 32 | ### Manually 33 | 34 | 1. Download the latest version from the [releases](https://github.com/ge9/IddSampleDriver/releases/latest) page, and extract the contents to a folder. 35 | 2. Copy `option.txt` to `C:\IddSampleDriver\option.txt` before installing the driver **(important!)**. 36 | 3. See the [guide](https://github.com/roshkins/IddSampleDriver/releases) in [roshkins repo](https://github.com/roshkins/IddSampleDriver) for the rest of the installation steps. 37 | 38 | ## Configuration 39 | Configure `C:\IddSampleDriver\option.txt` to set the number of monitors and resolutions. 40 | See `option.txt` 41 | 42 | 43 | ## Background reading ## 44 | 45 | Start at the [Indirect Display Driver Model Overview](https://msdn.microsoft.com/en-us/library/windows/hardware/mt761968(v=vs.85).aspx) on MSDN. 46 | 47 | ## Customizing the sample ## 48 | 49 | The sample driver code is very simplistic and does nothing more than enumerate a single monitor when its device enters the D0/started power state. Throughout the code, there are `TODO` blocks with important information on implementing functionality in a production driver. 50 | 51 | ### Code structure ### 52 | 53 | * `Direct3DDevice` class 54 | * Contains logic for enumerating the correct render GPU from DXGI and creating a D3D device. 55 | * Manages the lifetime of a DXGI factory and a D3D device created for the render GPU the system is using to render frames for your indirect display device's swap-chain. 56 | * `SwapChainProcessor` class 57 | * Processes frames for a swap-chain assigned to the monitor object on a dedicated thread. 58 | * The sample code does nothing with the frames, but demonstrates a correct processing loop with error handling and notifying the OS of frame completion. 59 | * `IndirectDeviceContext` class 60 | * Processes device callbacks from IddCx. 61 | * Manages the creation and arrival of the sample monitor. 62 | * Handles swap-chain arrival and departure by creating a `Direct3DDevice` and handing it off to a `SwapChainProcessor`. 63 | 64 | ### First steps ### 65 | 66 | Consider the capabilities of your device. If the device supports multiple monitors being hotplugged and removed at runtime, you may want to abstract the monitors further from the `IndirectDeviceContext` class. 67 | 68 | The INF file included in the sample needs updating for production use. One field, `DeviceGroupId`, controls how the UMDF driver gets pooled with other UMDF drivers in the same process. Since indirect display drivers tend to be more complicated than other driver classes, it's highly recommended that you pick a unique string for this field which will cause instances of your device driver to pool in a dedicated process. This will improve system reliability in case your driver encounters a problem since other drivers will not be affected. 69 | 70 | Ensure the device information reported to `IddCxAdapterInitAsync` is accurate. This information determines how the device is reported to the OS and what static features (like support for gamma tables) the device will have available. If some information cannot be known immediately in the `EvtDeviceD0Entry` callback, IddCx allows the driver to call `IddCxAdapterInitAsync` at any point after D0 entry, before D0 exit. 71 | 72 | Careful attention should be paid to the frame processing loop. This will directly impact the performance of the user's system, so making use of the [Multimedia Class Scheduler Service](https://msdn.microsoft.com/en-us/library/windows/desktop/ms684247(v=vs.85).aspx) and DXGI's support for [GPU prioritization](https://msdn.microsoft.com/en-us/library/windows/desktop/bb174534(v=vs.85).aspx) should be considered. Any significant work should be performed outside the main processing loop, such as by queuing work in a thread pool. See `SwapChainProcessor::RunCore` for more information. 73 | 74 | ## License 75 | 76 | License MIT and CC0 or Public Domain (for changes I made, check with Microsoft for their license), whichever is least restrictive -- Use it 77 | 78 | AS IS - NO IMPLICIT OR EXPLICIT warranty This may break your computer, it didn't break mine. It runs in User Mode which means it's less likely to cause system instability like the Blue Screen of Death. 79 | 80 | ## Acknowledgements 81 | 82 | See the original repo below: 83 | https://github.com/roshkins/IddSampleDriver 84 | 85 | Thanks to https://github.com/akatrevorjay/edid-generator for the hi-res EDID. 86 | -------------------------------------------------------------------------------- /option.txt: -------------------------------------------------------------------------------- 1 | 1 2 | #lines beginning with "#" are ignored (comment) 3 | #the first line must be a positive integer (small number (<5) is recommended)), NOT comment 4 | #(currently) the location of this file must be "C:\IddSampleDriver\option.txt" (hard-coded) 5 | #numbers should be separated by comma 6 | #spaces before number are allowed 7 | 640, 480, 60 8 | 800, 600, 60 9 | 1024, 768, 60 10 | 1152, 864, 60 11 | 1280, 600, 60 12 | 1280, 720, 60 13 | 1280, 768, 60 14 | 1280, 800, 60 15 | 1280, 960, 60 16 | 1280, 1024, 60 17 | 1360, 768, 60 18 | 1366, 768, 60 19 | 1400, 1050, 60 20 | 1440, 900, 60 21 | 1600, 900, 60 22 | 1680, 1050, 60 23 | 1600, 1024, 60 24 | 1920, 1080, 60 25 | 1920, 1200, 60 26 | 1920, 1440, 60 27 | 2560, 1440, 60 28 | 2560, 1600, 60 29 | 2880, 1620, 60 30 | 2880, 1800, 60 31 | 3008, 1692, 60 32 | 3200, 1800, 60 33 | 3200, 2400, 60 34 | 3840, 2160, 60 35 | 3840, 2400, 60 36 | 4096, 2304, 60 37 | 4096, 2560, 60 38 | 5120, 2880, 60 39 | 6016, 3384, 60 40 | 7680, 4320, 60 41 | --------------------------------------------------------------------------------