summaryrefslogtreecommitdiff
path: root/video/IndirectDisplay/IddSampleDriver/Driver.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'video/IndirectDisplay/IddSampleDriver/Driver.cpp')
-rw-r--r--video/IndirectDisplay/IddSampleDriver/Driver.cpp686
1 files changed, 686 insertions, 0 deletions
diff --git a/video/IndirectDisplay/IddSampleDriver/Driver.cpp b/video/IndirectDisplay/IddSampleDriver/Driver.cpp
new file mode 100644
index 00000000..a9bd6108
--- /dev/null
+++ b/video/IndirectDisplay/IddSampleDriver/Driver.cpp
@@ -0,0 +1,686 @@
+/*++
+
+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"
+
+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;
+
+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;
+}
+
+_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;
+
+ IddConfig.EvtIddCxAdapterInitFinished = IddSampleAdapterInitFinished;
+
+ IddConfig.EvtIddCxParseMonitorDescription = IddSampleParseMonitorDescription;
+ IddConfig.EvtIddCxMonitorGetDefaultDescriptionModes = IddSampleMonitorGetDefaultModes;
+ IddConfig.EvtIddCxMonitorQueryTargetModes = IddSampleMonitorQueryModes;
+ IddConfig.EvtIddCxAdapterCommitModes = IddSampleAdapterCommitModes;
+ IddConfig.EvtIddCxMonitorAssignSwapChain = IddSampleMonitorAssignSwapChain;
+ IddConfig.EvtIddCxMonitorUnassignSwapChain = IddSampleMonitorUnassignSwapChain;
+
+ 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()
+{
+ AdapterLuid = LUID{};
+}
+
+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<Direct3DDevice> 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<SwapChainProcessor*>(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 = AvSetMmThreadCharacteristics(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<IDXGIDevice> 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<IDXGIResource> AcquiredBuffer;
+
+ // Ask for the next buffer from the producer
+ IDARG_OUT_RELEASEANDACQUIREBUFFER Buffer = {};
+ hr = IddCxSwapChainReleaseAndAcquireBuffer(m_hSwapChain, &Buffer);
+
+ // 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(Buffer.MetaData.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;
+
+// A list of modes exposed by the sample monitor EDID - FOR SAMPLE PURPOSES ONLY
+const DISPLAYCONFIG_VIDEO_SIGNAL_INFO IndirectDeviceContext::s_KnownMonitorModes[] =
+{
+ // 800 x 600 @ 60Hz
+ {
+ 40 * MHZ, // pixel clock rate [Hz]
+ { 40 * MHZ, 800 + 256 }, // fractional horizontal refresh rate [Hz]
+ { 40 * MHZ, (800 + 256) * (600 + 28) }, // fractional vertical refresh rate [Hz]
+ { 800, 600 }, // (horizontal, vertical) active pixel resolution
+ { 800 + 256, 600 + 28 }, // (horizontal, vertical) total pixel resolution
+ { { 255, 0 }}, // video standard and vsync divider
+ DISPLAYCONFIG_SCANLINE_ORDERING_PROGRESSIVE
+ },
+ // 640 x 480 @ 60Hz
+ {
+ 25175 * KHZ, // pixel clock rate [Hz]
+ { 25175 * KHZ, 640 + 160 }, // fractional horizontal refresh rate [Hz]
+ { 25175 * KHZ, (640 + 160) * (480 + 46) }, // fractional vertical refresh rate [Hz]
+ { 640, 480 }, // (horizontal, vertical) active pixel resolution
+ { 640 + 160, 480 + 46 }, // (horizontal, vertical) blanking pixel resolution
+ { { 255, 0 } }, // video standard and vsync divider
+ DISPLAYCONFIG_SCANLINE_ORDERING_PROGRESSIVE
+ },
+};
+
+// This is a sample monitor EDID - FOR SAMPLE PURPOSES ONLY
+const BYTE IndirectDeviceContext::s_KnownMonitorEdid[] =
+{
+ 0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x79,0x5E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xA6,0x01,0x03,0x80,0x28,
+ 0x1E,0x78,0x0A,0xEE,0x91,0xA3,0x54,0x4C,0x99,0x26,0x0F,0x50,0x54,0x20,0x00,0x00,0x01,0x01,0x01,0x01,0x01,0x01,
+ 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0xA0,0x0F,0x20,0x00,0x31,0x58,0x1C,0x20,0x28,0x80,0x14,0x00,
+ 0x90,0x2C,0x11,0x00,0x00,0x1E,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6E
+};
+
+IndirectDeviceContext::IndirectDeviceContext(_In_ WDFDEVICE WdfDevice) :
+ m_WdfDevice(WdfDevice)
+{
+ m_Adapter = {};
+ m_Monitor = {};
+}
+
+IndirectDeviceContext::~IndirectDeviceContext()
+{
+ m_ProcessingThread.reset();
+}
+
+void IndirectDeviceContext::InitAdapter()
+{
+ // ==============================
+ // 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);
+
+ // Declare basic feature support for the adapter (required)
+ AdapterCaps.MaxMonitorsSupported = 1;
+ 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()
+{
+ // ==============================
+ // 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.
+ // ==============================
+
+ WDF_OBJECT_ATTRIBUTES Attr;
+ WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&Attr, IndirectDeviceContextWrapper);
+
+ IDDCX_MONITOR_INFO MonitorInfo = {};
+ MonitorInfo.Size = sizeof(MonitorInfo);
+ MonitorInfo.MonitorType = DISPLAYCONFIG_OUTPUT_TECHNOLOGY_HDMI;
+ MonitorInfo.ConnectorIndex = 0;
+ MonitorInfo.MonitorDescription.Size = sizeof(MonitorInfo.MonitorDescription);
+ MonitorInfo.MonitorDescription.Type = IDDCX_MONITOR_DESCRIPTION_TYPE_EDID;
+ MonitorInfo.MonitorDescription.DataSize = sizeof(s_KnownMonitorEdid);
+ MonitorInfo.MonitorDescription.pData = const_cast<BYTE*>(s_KnownMonitorEdid);
+
+ // ==============================
+ // 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.
+ // ==============================
+
+ // Create a container ID
+ CoCreateGuid(&MonitorInfo.MonitorContainerId);
+
+ IDARG_IN_MONITORCREATE MonitorCreate = {};
+ MonitorCreate.ObjectAttributes = &Attr;
+ MonitorCreate.pMonitorInfo = &MonitorInfo;
+
+ // Create a monitor object with the specified monitor descriptor
+ IDARG_OUT_MONITORCREATE MonitorCreateOut;
+ NTSTATUS Status = IddCxMonitorCreate(m_Adapter, &MonitorCreate, &MonitorCreateOut);
+ if (NT_SUCCESS(Status))
+ {
+ m_Monitor = MonitorCreateOut.MonitorObject;
+
+ // Associate the monitor with this device context
+ auto* pContext = WdfObjectGet_IndirectDeviceContextWrapper(MonitorCreateOut.MonitorObject);
+ pContext->pContext = this;
+
+ // Tell the OS that the monitor has been plugged in
+ IDARG_OUT_MONITORARRIVAL ArrivalOut;
+ Status = IddCxMonitorArrival(m_Monitor, &ArrivalOut);
+ }
+}
+
+void IndirectDeviceContext::AssignSwapChain(IDDCX_SWAPCHAIN SwapChain, LUID RenderAdapter, HANDLE NewFrameEvent)
+{
+ m_ProcessingThread.reset();
+
+ auto Device = make_shared<Direct3DDevice>(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.
+ // ==============================
+
+ pOutArgs->MonitorModeBufferOutputCount = ARRAYSIZE(IndirectDeviceContext::s_KnownMonitorModes);
+
+ if (pInArgs->MonitorModeBufferInputCount < ARRAYSIZE(IndirectDeviceContext::s_KnownMonitorModes))
+ {
+ // 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 < ARRAYSIZE(IndirectDeviceContext::s_KnownMonitorModes); ModeIndex++)
+ {
+ pInArgs->pMonitorModes[ModeIndex].Size = sizeof(IDDCX_MONITOR_MODE);
+ pInArgs->pMonitorModes[ModeIndex].Origin = IDDCX_MONITOR_MODE_ORIGIN_MONITORDESCRIPTOR;
+ pInArgs->pMonitorModes[ModeIndex].MonitorVideoSignalInfo = IndirectDeviceContext::s_KnownMonitorModes[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;
+}
+
+/// <summary>
+/// Creates a target mode from the fundamental mode attributes.
+/// </summary>
+void CreateTargetMode(DISPLAYCONFIG_VIDEO_SIGNAL_INFO& Mode, UINT Width, UINT Height, UINT VSync)
+{
+ 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 = ((UINT64) VSync) * ((UINT64) Width) * ((UINT64) Height);
+}
+
+void CreateTargetMode(IDDCX_TARGET_MODE& Mode, UINT Width, UINT Height, UINT VSync)
+{
+ Mode.Size = sizeof(Mode);
+ 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<IDDCX_TARGET_MODE> TargetModes(4);
+
+ // 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.
+
+ CreateTargetMode(TargetModes[0], 1920, 1080, 60);
+ CreateTargetMode(TargetModes[1], 1024, 768, 60);
+ CreateTargetMode(TargetModes[2], 800, 600, 60);
+ CreateTargetMode(TargetModes[3], 640, 480, 60);
+
+ 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;
+}
+
+#pragma endregion