Skip to main content

Architecture Overview

The Virtual Display Driver is a user-mode display driver built on Microsoft’s IddCx framework. It creates virtual displays by implementing the Indirect Display Driver model, integrating with Windows’ graphics stack through Direct3D and the Desktop Window Manager (DWM).

Core Components

Driver Entry Point

The driver implements a standard UMDF driver entry point:
extern "C" DRIVER_INITIALIZE DriverEntry;

// Driver callbacks
EVT_WDF_DRIVER_DEVICE_ADD VirtualDisplayDriverDeviceAdd;
EVT_WDF_DEVICE_D0_ENTRY VirtualDisplayDriverDeviceD0Entry;
The driver runs in User Mode (UMDF), not kernel mode, providing better stability and easier debugging.

IndirectDeviceContext

The IndirectDeviceContext class manages the virtual display adapter and monitors:
namespace Microsoft::IndirectDisp {
    class IndirectDeviceContext {
    public:
        IndirectDeviceContext(_In_ WDFDEVICE WdfDevice);
        virtual ~IndirectDeviceContext();

        void InitAdapter();
        void FinishInit();
        void CreateMonitor(unsigned int index);
        void AssignSwapChain(IDDCX_MONITOR& Monitor, 
                           IDDCX_SWAPCHAIN SwapChain, 
                           LUID RenderAdapter, 
                           HANDLE NewFrameEvent);
        void UnassignSwapChain();

    protected:
        WDFDEVICE m_WdfDevice;           // WDF device object
        IDDCX_ADAPTER m_Adapter;         // Virtual adapter
        IDDCX_MONITOR m_Monitor;         // Primary monitor
        IDDCX_MONITOR m_Monitor2;        // Secondary monitor
        
        std::unique_ptr<SwapChainProcessor> m_ProcessingThread;
    };
}

Adapter Management

Creates and manages the virtual display adapter (IDDCX_ADAPTER) that Windows recognizes

Monitor Lifecycle

Handles creation, configuration, and destruction of virtual monitors

SwapChain Processing

Manages frame buffer exchange between Windows compositor and driver

EDID Handling

Loads and provides EDID data to identify monitor capabilities

Direct3DDevice

Manages Direct3D rendering resources for GPU-accelerated buffer processing:
struct Direct3DDevice {
    Direct3DDevice(LUID AdapterLuid);
    Direct3DDevice();
    HRESULT Init();

    LUID AdapterLuid;                                  // GPU identifier
    Microsoft::WRL::ComPtr<IDXGIFactory5> DxgiFactory; // DXGI factory
    Microsoft::WRL::ComPtr<IDXGIAdapter1> Adapter;     // GPU adapter
    Microsoft::WRL::ComPtr<ID3D11Device> Device;       // D3D11 device
    Microsoft::WRL::ComPtr<ID3D11DeviceContext> DeviceContext; // Context
};

GPU Resource Management

The driver implements an efficient device cache:
class IndirectDeviceContext {
private:
    static std::map<LUID, std::shared_ptr<Direct3DDevice>, 
                    LuidComparator> s_DeviceCache;
    static std::mutex s_DeviceCacheMutex;
    
    static std::shared_ptr<Direct3DDevice> 
        GetOrCreateDevice(LUID RenderAdapter);
    static void CleanupExpiredDevices();
};
The device cache shares D3D11 devices across multiple virtual monitors using the same GPU, reducing memory overhead and improving performance.

SwapChainProcessor

Processes frames from the Windows compositor in a dedicated thread:
class SwapChainProcessor {
public:
    SwapChainProcessor(IDDCX_SWAPCHAIN hSwapChain, 
                      std::shared_ptr<Direct3DDevice> Device, 
                      HANDLE NewFrameEvent);
    ~SwapChainProcessor();

private:
    static DWORD CALLBACK RunThread(LPVOID Argument);
    void Run();
    void RunCore();

public:
    IDDCX_SWAPCHAIN m_hSwapChain;                    // SwapChain handle
    std::shared_ptr<Direct3DDevice> m_Device;        // D3D device
    HANDLE m_hAvailableBufferEvent;                  // Frame ready event
    Microsoft::WRL::Wrappers::Thread m_hThread;      // Processing thread
    Microsoft::WRL::Wrappers::Event m_hTerminateEvent; // Shutdown event
};

Frame Processing Pipeline

1

Wait for Frame

Thread waits on m_hAvailableBufferEvent for new frame from compositor
2

Acquire Buffer

Call IddCxSwapChainReleaseAndAcquireBuffer() to get the latest frame
3

Process Frame

Access the Direct3D surface containing the rendered frame
4

Release Buffer

Call IddCxSwapChainFinishedProcessingFrame() to return buffer
5

Repeat

Loop continues until monitor is disconnected

Windows Integration

IddCx Callback Events

The driver implements required IddCx callbacks:
// Adapter initialization complete
EVT_IDD_CX_ADAPTER_INIT_FINISHED 
    VirtualDisplayDriverAdapterInitFinished;

// Commit display mode changes
EVT_IDD_CX_ADAPTER_COMMIT_MODES 
    VirtualDisplayDriverAdapterCommitModes;
EVT_IDD_CX_ADAPTER_COMMIT_MODES2 
    VirtualDisplayDriverEvtIddCxAdapterCommitModes2;

// Query adapter capabilities
EVT_IDD_CX_ADAPTER_QUERY_TARGET_INFO 
    VirtualDisplayDriverEvtIddCxAdapterQueryTargetInfo;
// Parse monitor EDID
EVT_IDD_CX_PARSE_MONITOR_DESCRIPTION 
    VirtualDisplayDriverParseMonitorDescription;
EVT_IDD_CX_PARSE_MONITOR_DESCRIPTION2 
    VirtualDisplayDriverEvtIddCxParseMonitorDescription2;

// Enumerate supported modes
EVT_IDD_CX_MONITOR_GET_DEFAULT_DESCRIPTION_MODES 
    VirtualDisplayDriverMonitorGetDefaultModes;
EVT_IDD_CX_MONITOR_QUERY_TARGET_MODES 
    VirtualDisplayDriverMonitorQueryModes;
EVT_IDD_CX_MONITOR_QUERY_TARGET_MODES2 
    VirtualDisplayDriverEvtIddCxMonitorQueryTargetModes2;
// Assign swapchain to monitor
EVT_IDD_CX_MONITOR_ASSIGN_SWAPCHAIN 
    VirtualDisplayDriverMonitorAssignSwapChain;

// Remove swapchain from monitor
EVT_IDD_CX_MONITOR_UNASSIGN_SWAPCHAIN 
    VirtualDisplayDriverMonitorUnassignSwapChain;
// Set HDR metadata (IddCx 1.10)
EVT_IDD_CX_MONITOR_SET_DEFAULT_HDR_METADATA 
    VirtualDisplayDriverEvtIddCxMonitorSetDefaultHdrMetadata;

// Set gamma ramp (IddCx 1.10)
EVT_IDD_CX_MONITOR_SET_GAMMA_RAMP 
    VirtualDisplayDriverEvtIddCxMonitorSetGammaRamp;

Configuration System

Multi-Source Configuration

The driver supports configuration from multiple sources with priority:

Settings Query System

Implements a unified settings query interface:
std::map<std::wstring, std::pair<std::wstring, std::wstring>> 
    SettingsQueryMap = {
    {L"LoggingEnabled", {L"LOGS", L"logging"}},
    {L"HDRPlusEnabled", {L"HDRPLUS", L"HDRPlus"}},
    {L"CustomEdidEnabled", {L"CUSTOMEDID", L"CustomEdid"}},
    // ... 50+ settings
};

// Query functions
bool EnabledQuery(const std::wstring& settingKey);
int GetIntegerSetting(const std::wstring& settingKey);
std::wstring GetStringSetting(const std::wstring& settingKey);
double GetDoubleSetting(const std::wstring& settingKey);
Settings are queried first from registry (HKLM\SOFTWARE\MikeTheTech\VirtualDisplayDriver), then from C:\VirtualDisplayDriver\vdd_settings.xml if not found.

Configuration Categories

UINT numVirtualDisplays;           // Number of monitors (1-5)
vector<tuple<int, int, int, int>> monitorModes; // Resolutions
bool customEdid;                   // Use custom EDID

EDID Integration

EDID Profile Structure

The driver supports loading EDID profiles from XML:
struct EdidProfileData {
    vector<tuple<int, int, int, int>> modes;  // Resolutions
    bool hdr10Supported;
    bool dolbyVisionSupported;
    bool hdr10PlusSupported;
    double maxLuminance;
    double minLuminance;
    wstring primaryColorSpace;  // sRGB, DCI-P3, Rec.2020
    double gamma;               // Gamma correction
    double redX, redY;          // Color primaries
    double greenX, greenY;
    double blueX, blueY;
    double whiteX, whiteY;
    int preferredWidth, preferredHeight;
    double preferredRefresh;
};

EDID Loading Pipeline

1

Load XML Profile

Parse EDID profile from C:\VirtualDisplayDriver\EDID\monitor_profile.xml
2

Extract Capabilities

Read supported modes, color primaries, HDR capabilities, and preferred mode
3

Convert Metadata

Transform EDID data to SMPTE ST.2086 format for HDR
4

Generate EDID Block

Create binary EDID structure for monitor identification
5

Apply to Monitor

Provide EDID to IddCxMonitorCreate() during monitor creation

HDR Metadata Conversion

The driver converts EDID color data to SMPTE ST.2086 format:
// Convert EDID chromaticity (0.0-1.0) to SMPTE (0-50000)
UINT16 ConvertChromaticityToSmpte(double edidValue) {
    if (edidValue < 0.0) edidValue = 0.0;
    if (edidValue > 1.0) edidValue = 1.0;
    return static_cast<UINT16>(edidValue * 50000.0);
}

// Convert luminance (nits) to SMPTE (0.0001 cd/m² units)
UINT32 ConvertLuminanceToSmpte(double nits) {
    if (nits < 0.0001) nits = 0.0001;
    if (nits > 10000.0) nits = 10000.0;
    return static_cast<UINT32>(nits * 10000.0);
}

VddHdrMetadata ConvertEdidToSmpteMetadata(const EdidProfileData& profile);

Multi-GPU Support

GPU Selection

The driver supports targeting specific GPUs via LUID (Locally Unique Identifier):
struct Direct3DDevice {
    LUID AdapterLuid;  // Identifies the target GPU
    
    Direct3DDevice(LUID AdapterLuid);
    HRESULT Init();
};

// Select GPU by LUID
std::shared_ptr<Direct3DDevice> 
    GetOrCreateDevice(LUID RenderAdapter);
The driver can assign different virtual monitors to different GPUs, enabling advanced multi-GPU configurations.

LUID-based Adapter Selection

struct AdapterOption {
    wstring gpuName;           // Friendly name
    LUID adapterLuid;          // PCI bus-based identifier
    int pciBusNumber;          // PCI slot location
};

Logging and Diagnostics

Logging System

The driver implements a flexible logging system:
void vddlog(const char* type, const char* message);

// Log levels
vddlog("i", "Information message");
vddlog("w", "Warning message");
vddlog("e", "Error message");
vddlog("d", "Debug message");

Named Pipe Communication

Logs can be sent through a named pipe for external monitoring:
#define PIPE_NAME L"\\\\.\\pipe\\MTTVirtualDisplayPipe"

HANDLE g_pipeHandle = INVALID_HANDLE_VALUE;
bool sendLogsThroughPipe = true;
Enable pipe logging in vdd_settings.xml to monitor driver activity in real-time from external applications.

Performance Optimizations

Device Caching

Shares D3D devices across monitors using the same GPU:
static std::map<LUID, std::shared_ptr<Direct3DDevice>, 
                LuidComparator> s_DeviceCache;
static std::mutex s_DeviceCacheMutex;

static void CleanupExpiredDevices();

Thread Management

Dedicated swapchain processing threads:
class SwapChainProcessor {
    Microsoft::WRL::Wrappers::Thread m_hThread;
    Microsoft::WRL::Wrappers::Event m_hTerminateEvent;
    
    static DWORD CALLBACK RunThread(LPVOID Argument);
};

Memory Management

  • Smart pointers (std::unique_ptr, std::shared_ptr) for automatic cleanup
  • COM smart pointers (Microsoft::WRL::ComPtr) for D3D resources
  • Static caches with mutex protection for thread safety

Data Flow

Security Considerations

User-Mode Execution

Runs in Session 0 user mode, not kernel mode, limiting potential damage from crashes

Driver Signing

Signed by SignPath Foundation for Windows driver signature enforcement

Limited Privileges

UMDF drivers have restricted access compared to kernel drivers

Isolated Crashes

Driver failures won’t cause system Blue Screen of Death (BSOD)

File Structure

Virtual Display Driver (HDR)/
├── MttVDD/
│   ├── Driver.cpp          # Main driver implementation (170KB)
│   ├── Driver.h            # Driver headers and declarations
│   ├── MttVDD.inf          # Driver installation file
│   ├── Trace.h             # Tracing and diagnostics
│   └── MttVDD.vcxproj      # Visual Studio project
├── GetIddCx/
│   └── IddCxVersionQuery.cpp  # IddCx version detection utility
├── EDID/
│   └── [EDID profile files]
└── Common/
    └── Include/
        └── AdapterOption.h  # GPU adapter definitions

Next Steps

Virtual Displays

Learn about virtual display capabilities and use cases

IddCx Framework

Understand the IddCx framework versions and features

Configuration

Configure driver settings and parameters

Development

Build the driver from source code

Build docs developers (and LLMs) love