/*++ Copyright (c) Microsoft Corporation Abstract: This module contains a sample implementation of an indirect display driver. See the included README.md file and the various TODO blocks throughout this file and all accompanying files for information on building a production driver. MSDN documentation on indirect displays can be found at https://msdn.microsoft.com/en-us/library/windows/hardware/mt761968(v=vs.85).aspx. Environment: User Mode, UMDF --*/ #include "Driver.h" //#include "Driver.tmh" #include #include #include #include #include // jocke added for comming dynamic edid loading #include using namespace std; using namespace Microsoft::IndirectDisp; using namespace Microsoft::WRL; extern "C" DRIVER_INITIALIZE DriverEntry; EVT_WDF_DRIVER_DEVICE_ADD IddSampleDeviceAdd; EVT_WDF_DEVICE_D0_ENTRY IddSampleDeviceD0Entry; EVT_IDD_CX_ADAPTER_INIT_FINISHED IddSampleAdapterInitFinished; EVT_IDD_CX_ADAPTER_COMMIT_MODES IddSampleAdapterCommitModes; EVT_IDD_CX_PARSE_MONITOR_DESCRIPTION IddSampleParseMonitorDescription; EVT_IDD_CX_MONITOR_GET_DEFAULT_DESCRIPTION_MODES IddSampleMonitorGetDefaultModes; EVT_IDD_CX_MONITOR_QUERY_TARGET_MODES IddSampleMonitorQueryModes; EVT_IDD_CX_MONITOR_ASSIGN_SWAPCHAIN IddSampleMonitorAssignSwapChain; EVT_IDD_CX_MONITOR_UNASSIGN_SWAPCHAIN IddSampleMonitorUnassignSwapChain; EVT_IDD_CX_ADAPTER_QUERY_TARGET_INFO IddSampleEvtIddCxAdapterQueryTargetInfo; EVT_IDD_CX_MONITOR_SET_DEFAULT_HDR_METADATA IddSampleEvtIddCxMonitorSetDefaultHdrMetadata; EVT_IDD_CX_PARSE_MONITOR_DESCRIPTION2 IddSampleEvtIddCxParseMonitorDescription2; EVT_IDD_CX_MONITOR_QUERY_TARGET_MODES2 IddSampleEvtIddCxMonitorQueryTargetModes2; EVT_IDD_CX_ADAPTER_COMMIT_MODES2 IddSampleEvtIddCxAdapterCommitModes2; EVT_IDD_CX_MONITOR_SET_GAMMA_RAMP IddSampleEvtIddCxMonitorSetGammaRamp; vector> monitorModes; vector< DISPLAYCONFIG_VIDEO_SIGNAL_INFO> s_KnownMonitorModes2; UINT numVirtualDisplays; std::vector Microsoft::IndirectDisp::IndirectDeviceContext::s_knownMonitorEdidData; struct IndirectDeviceContextWrapper { IndirectDeviceContext* pContext; void Cleanup() { delete pContext; pContext = nullptr; } }; // This macro creates the methods for accessing an IndirectDeviceContextWrapper as a context for a WDF object WDF_DECLARE_CONTEXT_TYPE(IndirectDeviceContextWrapper); extern "C" BOOL WINAPI DllMain( _In_ HINSTANCE hInstance, _In_ UINT dwReason, _In_opt_ LPVOID lpReserved) { UNREFERENCED_PARAMETER(hInstance); UNREFERENCED_PARAMETER(lpReserved); UNREFERENCED_PARAMETER(dwReason); return TRUE; } _Use_decl_annotations_ extern "C" NTSTATUS DriverEntry( PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegistryPath ) { WDF_DRIVER_CONFIG Config; NTSTATUS Status; WDF_OBJECT_ATTRIBUTES Attributes; WDF_OBJECT_ATTRIBUTES_INIT(&Attributes); WDF_DRIVER_CONFIG_INIT(&Config, IddSampleDeviceAdd ); Status = WdfDriverCreate(pDriverObject, pRegistryPath, &Attributes, &Config, WDF_NO_HANDLE); if (!NT_SUCCESS(Status)) { return Status; } return Status; } vector split(string& input, char delimiter) { istringstream stream(input); string field; vector result; while (getline(stream, field, delimiter)) { result.push_back(field); } return result; } void loadOptions(string filepath) { ifstream ifs(filepath); string line; vector> res; getline(ifs, line);//num of displays numVirtualDisplays = stoi(line); while (getline(ifs, line)) { vector strvec = split(line, ','); if (strvec.size() == 3 && strvec[0].substr(0, 1) != "#") { res.push_back({ stoi(strvec[0]),stoi(strvec[1]),stoi(strvec[2]) }); } } monitorModes = res; return; } _Use_decl_annotations_ NTSTATUS IddSampleDeviceAdd(WDFDRIVER Driver, PWDFDEVICE_INIT pDeviceInit) { NTSTATUS Status = STATUS_SUCCESS; WDF_PNPPOWER_EVENT_CALLBACKS PnpPowerCallbacks; UNREFERENCED_PARAMETER(Driver); // Register for power callbacks - in this sample only power-on is needed WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&PnpPowerCallbacks); PnpPowerCallbacks.EvtDeviceD0Entry = IddSampleDeviceD0Entry; WdfDeviceInitSetPnpPowerEventCallbacks(pDeviceInit, &PnpPowerCallbacks); IDD_CX_CLIENT_CONFIG IddConfig; IDD_CX_CLIENT_CONFIG_INIT(&IddConfig); // If the driver wishes to handle custom IoDeviceControl requests, it's necessary to use this callback since IddCx // redirects IoDeviceControl requests to an internal queue. This sample does not need this. // IddConfig.EvtIddCxDeviceIoControl = IddSampleIoDeviceControl; loadOptions("C:\\IddSampleDriver\\option.txt"); IddConfig.EvtIddCxAdapterInitFinished = IddSampleAdapterInitFinished; IddConfig.EvtIddCxMonitorGetDefaultDescriptionModes = IddSampleMonitorGetDefaultModes; IddConfig.EvtIddCxMonitorAssignSwapChain = IddSampleMonitorAssignSwapChain; IddConfig.EvtIddCxMonitorUnassignSwapChain = IddSampleMonitorUnassignSwapChain; if (IDD_IS_FIELD_AVAILABLE(IDD_CX_CLIENT_CONFIG, EvtIddCxAdapterQueryTargetInfo)) { IddConfig.EvtIddCxAdapterQueryTargetInfo = IddSampleEvtIddCxAdapterQueryTargetInfo; IddConfig.EvtIddCxMonitorSetDefaultHdrMetaData = IddSampleEvtIddCxMonitorSetDefaultHdrMetadata; IddConfig.EvtIddCxParseMonitorDescription2 = IddSampleEvtIddCxParseMonitorDescription2; IddConfig.EvtIddCxMonitorQueryTargetModes2 = IddSampleEvtIddCxMonitorQueryTargetModes2; IddConfig.EvtIddCxAdapterCommitModes2 = IddSampleEvtIddCxAdapterCommitModes2; IddConfig.EvtIddCxMonitorSetGammaRamp = IddSampleEvtIddCxMonitorSetGammaRamp; } else { IddConfig.EvtIddCxParseMonitorDescription = IddSampleParseMonitorDescription; IddConfig.EvtIddCxMonitorQueryTargetModes = IddSampleMonitorQueryModes; IddConfig.EvtIddCxAdapterCommitModes = IddSampleAdapterCommitModes; } Status = IddCxDeviceInitConfig(pDeviceInit, &IddConfig); if (!NT_SUCCESS(Status)) { return Status; } WDF_OBJECT_ATTRIBUTES Attr; WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&Attr, IndirectDeviceContextWrapper); Attr.EvtCleanupCallback = [](WDFOBJECT Object) { // Automatically cleanup the context when the WDF object is about to be deleted auto* pContext = WdfObjectGet_IndirectDeviceContextWrapper(Object); if (pContext) { pContext->Cleanup(); } }; WDFDEVICE Device = nullptr; Status = WdfDeviceCreate(&pDeviceInit, &Attr, &Device); if (!NT_SUCCESS(Status)) { return Status; } Status = IddCxDeviceInitialize(Device); // Create a new device context object and attach it to the WDF device object auto* pContext = WdfObjectGet_IndirectDeviceContextWrapper(Device); pContext->pContext = new IndirectDeviceContext(Device); return Status; } _Use_decl_annotations_ NTSTATUS IddSampleDeviceD0Entry(WDFDEVICE Device, WDF_POWER_DEVICE_STATE PreviousState) { UNREFERENCED_PARAMETER(PreviousState); // This function is called by WDF to start the device in the fully-on power state. auto* pContext = WdfObjectGet_IndirectDeviceContextWrapper(Device); pContext->pContext->InitAdapter(); return STATUS_SUCCESS; } #pragma region Direct3DDevice Direct3DDevice::Direct3DDevice(LUID AdapterLuid) : AdapterLuid(AdapterLuid) { } Direct3DDevice::Direct3DDevice() { } HRESULT Direct3DDevice::Init() { // The DXGI factory could be cached, but if a new render adapter appears on the system, a new factory needs to be // created. If caching is desired, check DxgiFactory->IsCurrent() each time and recreate the factory if !IsCurrent. HRESULT hr = CreateDXGIFactory2(0, IID_PPV_ARGS(&DxgiFactory)); if (FAILED(hr)) { return hr; } // Find the specified render adapter hr = DxgiFactory->EnumAdapterByLuid(AdapterLuid, IID_PPV_ARGS(&Adapter)); if (FAILED(hr)) { return hr; } // Create a D3D device using the render adapter. BGRA support is required by the WHQL test suite. hr = D3D11CreateDevice(Adapter.Get(), D3D_DRIVER_TYPE_UNKNOWN, nullptr, D3D11_CREATE_DEVICE_BGRA_SUPPORT, nullptr, 0, D3D11_SDK_VERSION, &Device, nullptr, &DeviceContext); if (FAILED(hr)) { // If creating the D3D device failed, it's possible the render GPU was lost (e.g. detachable GPU) or else the // system is in a transient state. return hr; } return S_OK; } #pragma endregion #pragma region SwapChainProcessor SwapChainProcessor::SwapChainProcessor(IDDCX_SWAPCHAIN hSwapChain, shared_ptr Device, HANDLE NewFrameEvent) : m_hSwapChain(hSwapChain), m_Device(Device), m_hAvailableBufferEvent(NewFrameEvent) { m_hTerminateEvent.Attach(CreateEvent(nullptr, FALSE, FALSE, nullptr)); // Immediately create and run the swap-chain processing thread, passing 'this' as the thread parameter m_hThread.Attach(CreateThread(nullptr, 0, RunThread, this, 0, nullptr)); } SwapChainProcessor::~SwapChainProcessor() { // Alert the swap-chain processing thread to terminate SetEvent(m_hTerminateEvent.Get()); if (m_hThread.Get()) { // Wait for the thread to terminate WaitForSingleObject(m_hThread.Get(), INFINITE); } } DWORD CALLBACK SwapChainProcessor::RunThread(LPVOID Argument) { reinterpret_cast(Argument)->Run(); return 0; } void SwapChainProcessor::Run() { // For improved performance, make use of the Multimedia Class Scheduler Service, which will intelligently // prioritize this thread for improved throughput in high CPU-load scenarios. DWORD AvTask = 0; HANDLE AvTaskHandle = AvSetMmThreadCharacteristicsW(L"Distribution", &AvTask); RunCore(); // Always delete the swap-chain object when swap-chain processing loop terminates in order to kick the system to // provide a new swap-chain if necessary. WdfObjectDelete((WDFOBJECT)m_hSwapChain); m_hSwapChain = nullptr; AvRevertMmThreadCharacteristics(AvTaskHandle); } void SwapChainProcessor::RunCore() { // Get the DXGI device interface ComPtr DxgiDevice; HRESULT hr = m_Device->Device.As(&DxgiDevice); if (FAILED(hr)) { return; } IDARG_IN_SWAPCHAINSETDEVICE SetDevice = {}; SetDevice.pDevice = DxgiDevice.Get(); hr = IddCxSwapChainSetDevice(m_hSwapChain, &SetDevice); if (FAILED(hr)) { return; } // Acquire and release buffers in a loop for (;;) { ComPtr AcquiredBuffer; // Ask for the next buffer from the producer IDARG_IN_RELEASEANDACQUIREBUFFER2 BufferInArgs = {}; BufferInArgs.Size = sizeof(BufferInArgs); IDXGIResource* pSurface; if (IDD_IS_FUNCTION_AVAILABLE(IddCxSwapChainReleaseAndAcquireBuffer2)) { IDARG_OUT_RELEASEANDACQUIREBUFFER2 Buffer = {}; hr = IddCxSwapChainReleaseAndAcquireBuffer2(m_hSwapChain, &BufferInArgs, &Buffer); pSurface = Buffer.MetaData.pSurface; } else { IDARG_OUT_RELEASEANDACQUIREBUFFER Buffer = {}; hr = IddCxSwapChainReleaseAndAcquireBuffer(m_hSwapChain, &Buffer); pSurface = Buffer.MetaData.pSurface; } // AcquireBuffer immediately returns STATUS_PENDING if no buffer is yet available if (hr == E_PENDING) { // We must wait for a new buffer HANDLE WaitHandles[] = { m_hAvailableBufferEvent, m_hTerminateEvent.Get() }; DWORD WaitResult = WaitForMultipleObjects(ARRAYSIZE(WaitHandles), WaitHandles, FALSE, 16); if (WaitResult == WAIT_OBJECT_0 || WaitResult == WAIT_TIMEOUT) { // We have a new buffer, so try the AcquireBuffer again continue; } else if (WaitResult == WAIT_OBJECT_0 + 1) { // We need to terminate break; } else { // The wait was cancelled or something unexpected happened hr = HRESULT_FROM_WIN32(WaitResult); break; } } else if (SUCCEEDED(hr)) { AcquiredBuffer.Attach(pSurface); // ============================== // TODO: Process the frame here // // This is the most performance-critical section of code in an IddCx driver. It's important that whatever // is done with the acquired surface be finished as quickly as possible. This operation could be: // * a GPU copy to another buffer surface for later processing (such as a staging surface for mapping to CPU memory) // * a GPU encode operation // * a GPU VPBlt to another surface // * a GPU custom compute shader encode operation // ============================== AcquiredBuffer.Reset(); hr = IddCxSwapChainFinishedProcessingFrame(m_hSwapChain); if (FAILED(hr)) { break; } // ============================== // TODO: Report frame statistics once the asynchronous encode/send work is completed // // Drivers should report information about sub-frame timings, like encode time, send time, etc. // ============================== // IddCxSwapChainReportFrameStatistics(m_hSwapChain, ...); } else { // The swap-chain was likely abandoned (e.g. DXGI_ERROR_ACCESS_LOST), so exit the processing loop break; } } } #pragma endregion #pragma region IndirectDeviceContext const UINT64 MHZ = 1000000; const UINT64 KHZ = 1000; constexpr DISPLAYCONFIG_VIDEO_SIGNAL_INFO dispinfo(UINT32 h, UINT32 v, UINT32 r) { const UINT32 clock_rate = r * (v + 4) * (v + 4) + 1000; return { clock_rate, // pixel clock rate [Hz] { clock_rate, v + 4 }, // fractional horizontal refresh rate [Hz] { clock_rate, (v + 4) * (v + 4) }, // fractional vertical refresh rate [Hz] { h, v }, // (horizontal, vertical) active pixel resolution { h + 4, v + 4 }, // (horizontal, vertical) total pixel resolution { { 255, 0 }}, // video standard and vsync divider DISPLAYCONFIG_SCANLINE_ORDERING_PROGRESSIVE }; } // Jocke, createing a 512 byte array filled with 0 values (largest edid bin without breaking standards) std::vector s_knownMonitorEdidData; static void edidLoad(const std::string& filename) { std::wofstream logFile("C:\\IddSampleDriver\\lddlog.txt", std::ios::app); logFile << L"Entered EDIDLoad" << std::endl; std::ifstream file(filename, std::ios::binary); if (file.is_open()) { logFile << L"Filename found for custom edid" << std::endl; // Read file into vector s_knownMonitorEdidData = std::vector(std::istreambuf_iterator(file), {}); logFile << L"Replacing edid data in custom bin" << std::endl; // Modify bytes at positions 8, 9, 10, and 11 s_knownMonitorEdidData[8] = 0x36; s_knownMonitorEdidData[9] = 0x94; s_knownMonitorEdidData[10] = 0x37; s_knownMonitorEdidData[11] = 0x13; logFile << L"calculating new checksum" << std::endl; // Calculate checksum int checksum = 0; for (int i = 0; i < 128; ++i) { checksum += s_knownMonitorEdidData[i]; } checksum %= 256; uint8_t bytesum = static_cast(checksum); s_knownMonitorEdidData[127] = bytesum; // Store checksum as a byte, all byte number might be off by 1 (don't knwo how c++ handles array/vector position 0, if it's resereved or filed with data logFile << L"Checksum calculated and placed on last byte of edid data" << std::endl; } else { logFile << L"CustomEdid not found, checking default" << std::endl; std::ifstream filedef("default.bin", std::ios::binary); if (filedef.is_open()) { // Read file into vector this is our pre patched default edid s_knownMonitorEdidData = std::vector(std::istreambuf_iterator(filedef), {}); logFile << L"Default.bin loaded into edid data" << std::endl; } else { // if all files are gone from install folder then use our minimal hardcoded edid logFile << L"Defaulting to hardcoded edid" << std::endl; string incode = " {\ 0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x36,0x94,0x37,0x13,0x00,0x00,0x00,0x00,\ 0x05,0x16,0x01,0x03,0x6D,0x32,0x1C,0x78,0xEA,0x5E,0xC0,0xA4,0x59,0x4A,0x98,0x25,\ 0x20,0x50,0x54,0x00,0x00,0x00,0xD1,0xC0,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,\ 0x01,0x01,0x01,0x01,0x01,0x01,0x02,0x3A,0x80,0x18,0x71,0x38,0x2D,0x40,0x58,0x2C,\ 0x45,0x00,0xF4,0x19,0x11,0x00,0x00,0x1E,0x00,0x00,0x00,0xFF,0x00,0x4D,0x54,0x54,\ 0x31,0x33,0x33,0x37,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x00,0x00,0xFD,0x00,0x3B,\ 0x3D,0x42,0x44,0x0F,0x00,0x0A,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x00,0x00,0xFC,\ 0x00,0x4D,0x54,0x54,0x31,0x33,0x33,0x37,0x0A,0x20,0x20,0x20,0x20,0x20,0x00,0x83}"; s_knownMonitorEdidData = std::vector(incode.begin(), incode.end()); } } logFile << L"Exiting EDID Load and closing log" << std::endl; logFile.close(); }; // This is a sample monitor EDID - FOR SAMPLE PURPOSES ONLY /* const BYTE IndirectDeviceContext::s_KnownMonitorEdid[] = { 0x00,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x36,0x94,0x37,0x13,0x37,0x13,0x37,0x13, 0x0d,0x22,0x01,0x03,0x80,0x32,0x1f,0x78,0x07,0xee,0x95,0xa3,0x54,0x4c,0x99,0x26, 0x0f,0x50,0x54,0x00,0x00,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, 0x01,0x01,0x01,0x01,0x01,0x01,0x02,0x3a,0x80,0x18,0x71,0x38,0x2d,0x40,0x58,0x2c, 0x45,0x00,0x63,0xc8,0x10,0x00,0x00,0x06,0x00,0x00,0x00,0xfd,0x00,0x17,0xf0,0x0f, 0xff,0x37,0x00,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x00,0x00,0xfc,0x00,0x4d, 0x54,0x54,0x31,0x33,0x33,0x37,0x0a,0x20,0x20,0x20,0x20,0x20,0x00,0x00,0x00,0x10, 0x00,0x13,0x37,0x13,0x37,0x13,0x37,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x33, 0x02,0x03,0x23,0x40,0x23,0x09,0x00,0x00,0xe6,0x06,0x0d,0x01,0xb4,0x78,0x01,0x67, 0x03,0x0c,0x00,0x00,0x00,0x10,0x6e,0xe3,0x05,0x80,0x00,0x67,0xd8,0x5d,0xc4,0x01, 0x6e,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x9a, 0x70,0x13,0x79,0x00,0x00,0x00,0x00,0x1e,0x4d,0x54,0x54,0x0d,0x00,0x39,0x05,0x00, 0x00,0x0e,0x18,0x12,0x56,0x44,0x44,0x20,0x62,0x79,0x20,0x4d,0x69,0x6b,0x65,0x54, 0x68,0x65,0x54,0x65,0x63,0x68,0x03,0x01,0x14,0x72,0xd6,0x00,0x84,0xff,0x1d,0x9f, 0x00,0x2f,0x80,0x1f,0x00,0xdf,0x10,0x18,0x02,0x02,0x00,0x04,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3e,0x90 }; */ IndirectDeviceContext::IndirectDeviceContext(_In_ WDFDEVICE WdfDevice) : m_WdfDevice(WdfDevice) { m_Adapter = {}; } IndirectDeviceContext::~IndirectDeviceContext() { m_ProcessingThread.reset(); } #define NUM_VIRTUAL_DISPLAYS 1 void IndirectDeviceContext::InitAdapter() { edidLoad("C:\\IddSampleDriver\\user_edid.bin"); // ============================== // TODO: Update the below diagnostic information in accordance with the target hardware. The strings and version // numbers are used for telemetry and may be displayed to the user in some situations. // // This is also where static per-adapter capabilities are determined. // ============================== IDDCX_ADAPTER_CAPS AdapterCaps = {}; AdapterCaps.Size = sizeof(AdapterCaps); if (IDD_IS_FUNCTION_AVAILABLE(IddCxSwapChainReleaseAndAcquireBuffer2)) { AdapterCaps.Flags = IDDCX_ADAPTER_FLAGS_CAN_PROCESS_FP16; } // Declare basic feature support for the adapter (required) AdapterCaps.MaxMonitorsSupported = numVirtualDisplays; AdapterCaps.EndPointDiagnostics.Size = sizeof(AdapterCaps.EndPointDiagnostics); AdapterCaps.EndPointDiagnostics.GammaSupport = IDDCX_FEATURE_IMPLEMENTATION_NONE; AdapterCaps.EndPointDiagnostics.TransmissionType = IDDCX_TRANSMISSION_TYPE_WIRED_OTHER; // Declare your device strings for telemetry (required) AdapterCaps.EndPointDiagnostics.pEndPointFriendlyName = L"IddSample Device"; AdapterCaps.EndPointDiagnostics.pEndPointManufacturerName = L"Microsoft"; AdapterCaps.EndPointDiagnostics.pEndPointModelName = L"IddSample Model"; // Declare your hardware and firmware versions (required) IDDCX_ENDPOINT_VERSION Version = {}; Version.Size = sizeof(Version); Version.MajorVer = 1; AdapterCaps.EndPointDiagnostics.pFirmwareVersion = &Version; AdapterCaps.EndPointDiagnostics.pHardwareVersion = &Version; // Initialize a WDF context that can store a pointer to the device context object WDF_OBJECT_ATTRIBUTES Attr; WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&Attr, IndirectDeviceContextWrapper); IDARG_IN_ADAPTER_INIT AdapterInit = {}; AdapterInit.WdfDevice = m_WdfDevice; AdapterInit.pCaps = &AdapterCaps; AdapterInit.ObjectAttributes = &Attr; // Start the initialization of the adapter, which will trigger the AdapterFinishInit callback later IDARG_OUT_ADAPTER_INIT AdapterInitOut; NTSTATUS Status = IddCxAdapterInitAsync(&AdapterInit, &AdapterInitOut); if (NT_SUCCESS(Status)) { // Store a reference to the WDF adapter handle m_Adapter = AdapterInitOut.AdapterObject; // Store the device context object into the WDF object context auto* pContext = WdfObjectGet_IndirectDeviceContextWrapper(AdapterInitOut.AdapterObject); pContext->pContext = this; } } void IndirectDeviceContext::FinishInit() { for (unsigned int i = 0; i < numVirtualDisplays; i++) { CreateMonitor(i); } } void IndirectDeviceContext::CreateMonitor(unsigned int index) { // ============================== // TODO: In a real driver, the EDID should be retrieved dynamically from a connected physical monitor. The EDID // provided here is purely for demonstration, as it describes only 640x480 @ 60 Hz and 800x600 @ 60 Hz. Monitor // manufacturers are required to correctly fill in physical monitor attributes in order to allow the OS to optimize // settings like viewing distance and scale factor. Manufacturers should also use a unique serial number every // single device to ensure the OS can tell the monitors apart. // ============================== std::wofstream logFile("C:\\IddSampleDriver\\lddlog.txt", std::ios::app); logFile << L"Creating a monitor start at " << std::endl; WDF_OBJECT_ATTRIBUTES Attr; WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&Attr, IndirectDeviceContextWrapper); logFile << L"Initialized WDF object attributes at " << std::endl; IDDCX_MONITOR_INFO MonitorInfo = {}; MonitorInfo.Size = sizeof(MonitorInfo); MonitorInfo.MonitorType = DISPLAYCONFIG_OUTPUT_TECHNOLOGY_HDMI; MonitorInfo.ConnectorIndex = index; MonitorInfo.MonitorDescription.Size = sizeof(MonitorInfo.MonitorDescription); MonitorInfo.MonitorDescription.Type = IDDCX_MONITOR_DESCRIPTION_TYPE_EDID; MonitorInfo.MonitorDescription.DataSize = sizeof(s_knownMonitorEdidData); MonitorInfo.MonitorDescription.pData = s_knownMonitorEdidData.data(); // ============================== // TODO: The monitor's container ID should be distinct from "this" device's container ID if the monitor is not // permanently attached to the display adapter device object. The container ID is typically made unique for each // monitor and can be used to associate the monitor with other devices, like audio or input devices. In this // sample we generate a random container ID GUID, but it's best practice to choose a stable container ID for a // unique monitor or to use "this" device's container ID for a permanent/integrated monitor. // ============================== logFile << L"Initialized monitor info at " << std::endl; CoCreateGuid(&MonitorInfo.MonitorContainerId); logFile << L"Created container ID at " << std::endl; IDARG_IN_MONITORCREATE MonitorCreate = {}; MonitorCreate.ObjectAttributes = &Attr; MonitorCreate.pMonitorInfo = &MonitorInfo; IDARG_OUT_MONITORCREATE MonitorCreateOut; NTSTATUS Status = IddCxMonitorCreate(m_Adapter, &MonitorCreate, &MonitorCreateOut); if (!NT_SUCCESS(Status)) { logFile << L"Failed to create monitor object. Status code: " << Status << std::endl; } if (NT_SUCCESS(Status)) { m_Monitor = MonitorCreateOut.MonitorObject; auto* pContext = WdfObjectGet_IndirectDeviceContextWrapper(MonitorCreateOut.MonitorObject); pContext->pContext = this; logFile << L"Created monitor object and associated it with this device context at " << std::endl; IDARG_OUT_MONITORARRIVAL ArrivalOut; Status = IddCxMonitorArrival(m_Monitor, &ArrivalOut); if (!NT_SUCCESS(Status)) { logFile << L"Failed to plug in monitor. Status code: " << Status << std::endl; } else { logFile << L"Monitor has been plugged in at " << std::endl; } } else { logFile << L"Weird " << std::endl; } logFile << L"Creating a monitor end at " << std::endl; logFile.close(); } void IndirectDeviceContext::AssignSwapChain(IDDCX_SWAPCHAIN SwapChain, LUID RenderAdapter, HANDLE NewFrameEvent) // Jocke, is it here we could assign rendering gpu? { m_ProcessingThread.reset(); auto Device = make_shared(RenderAdapter); if (FAILED(Device->Init())) { // It's important to delete the swap-chain if D3D initialization fails, so that the OS knows to generate a new // swap-chain and try again. WdfObjectDelete(SwapChain); } else { // Create a new swap-chain processing thread m_ProcessingThread.reset(new SwapChainProcessor(SwapChain, Device, NewFrameEvent)); } } void IndirectDeviceContext::UnassignSwapChain() { // Stop processing the last swap-chain m_ProcessingThread.reset(); } #pragma endregion #pragma region DDI Callbacks _Use_decl_annotations_ NTSTATUS IddSampleAdapterInitFinished(IDDCX_ADAPTER AdapterObject, const IDARG_IN_ADAPTER_INIT_FINISHED* pInArgs) { // This is called when the OS has finished setting up the adapter for use by the IddCx driver. It's now possible // to report attached monitors. auto* pContext = WdfObjectGet_IndirectDeviceContextWrapper(AdapterObject); if (NT_SUCCESS(pInArgs->AdapterInitStatus)) { pContext->pContext->FinishInit(); } return STATUS_SUCCESS; } _Use_decl_annotations_ NTSTATUS IddSampleAdapterCommitModes(IDDCX_ADAPTER AdapterObject, const IDARG_IN_COMMITMODES* pInArgs) { UNREFERENCED_PARAMETER(AdapterObject); UNREFERENCED_PARAMETER(pInArgs); // For the sample, do nothing when modes are picked - the swap-chain is taken care of by IddCx // ============================== // TODO: In a real driver, this function would be used to reconfigure the device to commit the new modes. Loop // through pInArgs->pPaths and look for IDDCX_PATH_FLAGS_ACTIVE. Any path not active is inactive (e.g. the monitor // should be turned off). // ============================== return STATUS_SUCCESS; } _Use_decl_annotations_ NTSTATUS IddSampleParseMonitorDescription(const IDARG_IN_PARSEMONITORDESCRIPTION* pInArgs, IDARG_OUT_PARSEMONITORDESCRIPTION* pOutArgs) { // ============================== // TODO: In a real driver, this function would be called to generate monitor modes for an EDID by parsing it. In // this sample driver, we hard-code the EDID, so this function can generate known modes. // ============================== for (int i = 0; i < monitorModes.size(); i++) { s_KnownMonitorModes2.push_back(dispinfo(std::get<0>(monitorModes[i]), std::get<1>(monitorModes[i]), std::get<2>(monitorModes[i]))); } pOutArgs->MonitorModeBufferOutputCount = (UINT)monitorModes.size(); if (pInArgs->MonitorModeBufferInputCount < monitorModes.size()) { // Return success if there was no buffer, since the caller was only asking for a count of modes return (pInArgs->MonitorModeBufferInputCount > 0) ? STATUS_BUFFER_TOO_SMALL : STATUS_SUCCESS; } else { // Copy the known modes to the output buffer for (DWORD ModeIndex = 0; ModeIndex < monitorModes.size(); ModeIndex++) { pInArgs->pMonitorModes[ModeIndex].Size = sizeof(IDDCX_MONITOR_MODE); pInArgs->pMonitorModes[ModeIndex].Origin = IDDCX_MONITOR_MODE_ORIGIN_MONITORDESCRIPTOR; pInArgs->pMonitorModes[ModeIndex].MonitorVideoSignalInfo = s_KnownMonitorModes2[ModeIndex]; } // Set the preferred mode as represented in the EDID pOutArgs->PreferredMonitorModeIdx = 0; return STATUS_SUCCESS; } } _Use_decl_annotations_ NTSTATUS IddSampleMonitorGetDefaultModes(IDDCX_MONITOR MonitorObject, const IDARG_IN_GETDEFAULTDESCRIPTIONMODES* pInArgs, IDARG_OUT_GETDEFAULTDESCRIPTIONMODES* pOutArgs) { UNREFERENCED_PARAMETER(MonitorObject); UNREFERENCED_PARAMETER(pInArgs); UNREFERENCED_PARAMETER(pOutArgs); // Should never be called since we create a single monitor with a known EDID in this sample driver. // ============================== // TODO: In a real driver, this function would be called to generate monitor modes for a monitor with no EDID. // Drivers should report modes that are guaranteed to be supported by the transport protocol and by nearly all // monitors (such 640x480, 800x600, or 1024x768). If the driver has access to monitor modes from a descriptor other // than an EDID, those modes would also be reported here. // ============================== return STATUS_NOT_IMPLEMENTED; } /// /// Creates a target mode from the fundamental mode attributes. /// void CreateTargetMode(DISPLAYCONFIG_VIDEO_SIGNAL_INFO& Mode, UINT Width, UINT Height, UINT VSync) // Jocke, is it her we have the issue with 640*480 init when multiple vdds are chosen { Mode.totalSize.cx = Mode.activeSize.cx = Width; Mode.totalSize.cy = Mode.activeSize.cy = Height; Mode.AdditionalSignalInfo.vSyncFreqDivider = 1; Mode.AdditionalSignalInfo.videoStandard = 255; Mode.vSyncFreq.Numerator = VSync; Mode.vSyncFreq.Denominator = Mode.hSyncFreq.Denominator = 1; Mode.hSyncFreq.Numerator = VSync * Height; Mode.scanLineOrdering = DISPLAYCONFIG_SCANLINE_ORDERING_PROGRESSIVE; Mode.pixelRate = VSync * Width * Height; } void CreateTargetMode(IDDCX_TARGET_MODE& Mode, UINT Width, UINT Height, UINT VSync) { Mode.Size = sizeof(Mode); CreateTargetMode(Mode.TargetVideoSignalInfo.targetVideoSignalInfo, Width, Height, VSync); } void CreateTargetMode2(IDDCX_TARGET_MODE2& Mode, UINT Width, UINT Height, UINT VSync) { Mode.Size = sizeof(Mode); Mode.BitsPerComponent.Rgb = IDDCX_BITS_PER_COMPONENT_8 | IDDCX_BITS_PER_COMPONENT_10; CreateTargetMode(Mode.TargetVideoSignalInfo.targetVideoSignalInfo, Width, Height, VSync); } _Use_decl_annotations_ NTSTATUS IddSampleMonitorQueryModes(IDDCX_MONITOR MonitorObject, const IDARG_IN_QUERYTARGETMODES* pInArgs, IDARG_OUT_QUERYTARGETMODES* pOutArgs)//////////////////////////////////////////////////////////////////////////////// { UNREFERENCED_PARAMETER(MonitorObject); vector TargetModes(monitorModes.size()); // Create a set of modes supported for frame processing and scan-out. These are typically not based on the // monitor's descriptor and instead are based on the static processing capability of the device. The OS will // report the available set of modes for a given output as the intersection of monitor modes with target modes. for (int i = 0; i < monitorModes.size(); i++) { CreateTargetMode(TargetModes[i], std::get<0>(monitorModes[i]), std::get<1>(monitorModes[i]), std::get<2>(monitorModes[i])); } pOutArgs->TargetModeBufferOutputCount = (UINT)TargetModes.size(); if (pInArgs->TargetModeBufferInputCount >= TargetModes.size()) { copy(TargetModes.begin(), TargetModes.end(), pInArgs->pTargetModes); } return STATUS_SUCCESS; } _Use_decl_annotations_ NTSTATUS IddSampleMonitorAssignSwapChain(IDDCX_MONITOR MonitorObject, const IDARG_IN_SETSWAPCHAIN* pInArgs) { auto* pContext = WdfObjectGet_IndirectDeviceContextWrapper(MonitorObject); pContext->pContext->AssignSwapChain(pInArgs->hSwapChain, pInArgs->RenderAdapterLuid, pInArgs->hNextSurfaceAvailable); return STATUS_SUCCESS; } _Use_decl_annotations_ NTSTATUS IddSampleMonitorUnassignSwapChain(IDDCX_MONITOR MonitorObject) { auto* pContext = WdfObjectGet_IndirectDeviceContextWrapper(MonitorObject); pContext->pContext->UnassignSwapChain(); return STATUS_SUCCESS; } _Use_decl_annotations_ NTSTATUS IddSampleEvtIddCxAdapterQueryTargetInfo( IDDCX_ADAPTER AdapterObject, IDARG_IN_QUERYTARGET_INFO* pInArgs, IDARG_OUT_QUERYTARGET_INFO* pOutArgs ) { UNREFERENCED_PARAMETER(AdapterObject); UNREFERENCED_PARAMETER(pInArgs); pOutArgs->TargetCaps = IDDCX_TARGET_CAPS_HIGH_COLOR_SPACE; pOutArgs->DitheringSupport.Rgb = IDDCX_BITS_PER_COMPONENT_10; return STATUS_SUCCESS; } _Use_decl_annotations_ NTSTATUS IddSampleEvtIddCxMonitorSetDefaultHdrMetadata( IDDCX_MONITOR MonitorObject, const IDARG_IN_MONITOR_SET_DEFAULT_HDR_METADATA* pInArgs ) { UNREFERENCED_PARAMETER(MonitorObject); UNREFERENCED_PARAMETER(pInArgs); return STATUS_SUCCESS; } _Use_decl_annotations_ NTSTATUS IddSampleEvtIddCxParseMonitorDescription2( const IDARG_IN_PARSEMONITORDESCRIPTION2* pInArgs, IDARG_OUT_PARSEMONITORDESCRIPTION* pOutArgs ) { // ============================== // TODO: In a real driver, this function would be called to generate monitor modes for an EDID by parsing it. In // this sample driver, we hard-code the EDID, so this function can generate known modes. // ============================== for (int i = 0; i < monitorModes.size(); i++) { s_KnownMonitorModes2.push_back(dispinfo(std::get<0>(monitorModes[i]), std::get<1>(monitorModes[i]), std::get<2>(monitorModes[i]))); } pOutArgs->MonitorModeBufferOutputCount = (UINT)monitorModes.size(); if (pInArgs->MonitorModeBufferInputCount < monitorModes.size()) { // Return success if there was no buffer, since the caller was only asking for a count of modes return (pInArgs->MonitorModeBufferInputCount > 0) ? STATUS_BUFFER_TOO_SMALL : STATUS_SUCCESS; } else { // Copy the known modes to the output buffer for (DWORD ModeIndex = 0; ModeIndex < monitorModes.size(); ModeIndex++) { pInArgs->pMonitorModes[ModeIndex].Size = sizeof(IDDCX_MONITOR_MODE2); pInArgs->pMonitorModes[ModeIndex].Origin = IDDCX_MONITOR_MODE_ORIGIN_MONITORDESCRIPTOR; pInArgs->pMonitorModes[ModeIndex].MonitorVideoSignalInfo = s_KnownMonitorModes2[ModeIndex]; pInArgs->pMonitorModes[ModeIndex].BitsPerComponent.Rgb = IDDCX_BITS_PER_COMPONENT_8 | IDDCX_BITS_PER_COMPONENT_10; } // Set the preferred mode as represented in the EDID pOutArgs->PreferredMonitorModeIdx = 0; return STATUS_SUCCESS; } } _Use_decl_annotations_ NTSTATUS IddSampleEvtIddCxMonitorQueryTargetModes2( IDDCX_MONITOR MonitorObject, const IDARG_IN_QUERYTARGETMODES2* pInArgs, IDARG_OUT_QUERYTARGETMODES* pOutArgs ) { UNREFERENCED_PARAMETER(MonitorObject); vector TargetModes(monitorModes.size()); // Create a set of modes supported for frame processing and scan-out. These are typically not based on the // monitor's descriptor and instead are based on the static processing capability of the device. The OS will // report the available set of modes for a given output as the intersection of monitor modes with target modes. for (int i = 0; i < monitorModes.size(); i++) { CreateTargetMode2(TargetModes[i], std::get<0>(monitorModes[i]), std::get<1>(monitorModes[i]), std::get<2>(monitorModes[i])); } pOutArgs->TargetModeBufferOutputCount = (UINT)TargetModes.size(); if (pInArgs->TargetModeBufferInputCount >= TargetModes.size()) { copy(TargetModes.begin(), TargetModes.end(), pInArgs->pTargetModes); } return STATUS_SUCCESS; } _Use_decl_annotations_ NTSTATUS IddSampleEvtIddCxAdapterCommitModes2( IDDCX_ADAPTER AdapterObject, const IDARG_IN_COMMITMODES2* pInArgs ) { UNREFERENCED_PARAMETER(AdapterObject); UNREFERENCED_PARAMETER(pInArgs); return STATUS_SUCCESS; } _Use_decl_annotations_ NTSTATUS IddSampleEvtIddCxMonitorSetGammaRamp( IDDCX_MONITOR MonitorObject, const IDARG_IN_SET_GAMMARAMP* pInArgs ) { UNREFERENCED_PARAMETER(MonitorObject); UNREFERENCED_PARAMETER(pInArgs); return STATUS_SUCCESS; } #pragma endregion