Skip to main content

Overview

The Direct3DDevice struct encapsulates the creation and lifetime management of a Direct3D render device used for frame processing in the virtual display driver. It manages DXGI factory, adapter, D3D device, and device context objects. Header: Driver.h:52-66 Implementation: Driver.cpp:2992-3076

Structure Declaration

struct Direct3DDevice
{
    Direct3DDevice(LUID AdapterLuid);
    Direct3DDevice();
    HRESULT Init();

    LUID AdapterLuid;
    Microsoft::WRL::ComPtr<IDXGIFactory5> DxgiFactory;
    Microsoft::WRL::ComPtr<IDXGIAdapter1> Adapter;
    Microsoft::WRL::ComPtr<ID3D11Device> Device;
    Microsoft::WRL::ComPtr<ID3D11DeviceContext> DeviceContext;
};

Constructors

Direct3DDevice(LUID AdapterLuid)

Signature:
Direct3DDevice::Direct3DDevice(LUID AdapterLuid);
Parameters:
  • AdapterLuid: Locally Unique Identifier for the target GPU adapter
Source Location: Driver.cpp:2992 Purpose: Initializes the device object with a specific GPU adapter LUID. Usage:
LUID adapterLuid = {0x1234, 0x5678};
Direct3DDevice device(adapterLuid);
HRESULT hr = device.Init();  // Must call Init() after construction

Direct3DDevice()

Signature:
Direct3DDevice::Direct3DDevice();
Source Location: Driver.cpp:2996 Purpose: Initializes the device object with zeroed LUID (default adapter). Usage:
Direct3DDevice device;
HRESULT hr = device.Init();  // Will use default GPU adapter

Initialization

Init

Signature:
HRESULT Direct3DDevice::Init();
Source Location: Driver.cpp:3000 Purpose: Initializes all Direct3D resources (factory, adapter, device, context). Returns:
  • S_OK on success
  • DXGI or D3D error code on failure
Initialization Steps:

1. Create DXGI Factory

hr = CreateDXGIFactory2(0, IID_PPV_ARGS(&DxgiFactory));
if (FAILED(hr)) {
    vddlog("e", "Failed to create DXGI factory");
    return hr;
}
Notes:
  • Factory is not cached to detect new adapters appearing on the system
  • Can check DxgiFactory->IsCurrent() if caching is desired
  • Re-create factory if IsCurrent() returns false

2. Enumerate Adapter by LUID

hr = DxgiFactory->EnumAdapterByLuid(AdapterLuid, IID_PPV_ARGS(&Adapter));
if (FAILED(hr)) {
    vddlog("e", "Failed to enumerate adapter by LUID");
    return hr;
}

// Log adapter information
DXGI_ADAPTER_DESC desc;
Adapter->GetDesc(&desc);
vddlog("i", ("Adapter found: " + desc.Description).c_str());
Failure Scenarios:
  • LUID not found (GPU removed, detached, or invalid LUID)
  • GPU device disabled
  • Driver not loaded

3. Create D3D11 Device

D3D_FEATURE_LEVEL featureLevels[] = {
    D3D_FEATURE_LEVEL_11_1,
    D3D_FEATURE_LEVEL_11_0
};
D3D_FEATURE_LEVEL featureLevel;

hr = D3D11CreateDevice(
    Adapter.Get(),                      // Use specific adapter
    D3D_DRIVER_TYPE_UNKNOWN,            // Must be UNKNOWN when adapter specified
    nullptr,                            // No software rasterizer
    D3D11_CREATE_DEVICE_BGRA_SUPPORT,   // Required by WHQL test suite
    featureLevels,
    ARRAYSIZE(featureLevels),
    D3D11_SDK_VERSION,
    &Device,
    &featureLevel,
    &DeviceContext
);
Feature Levels:
  • D3D_FEATURE_LEVEL_11_1: Preferred, supports advanced features
  • D3D_FEATURE_LEVEL_11_0: Fallback for older GPUs
Creation Flags:
  • D3D11_CREATE_DEVICE_BGRA_SUPPORT: Mandatory for IddCx drivers (WHQL requirement)
Failure Scenarios:
  • GPU lost or removed (e.g., detachable eGPU)
  • System in transient state (driver update, power transition)
  • Out of memory
  • Driver crash recovery
Error Handling:
if (FAILED(hr)) {
    vddlog("e", "Failed to create Direct3D device");
    vddlog("e", "GPU may be lost or system in transient state");
    return hr;
}

Member Variables

AdapterLuid

Type: LUID Purpose: Locally Unique Identifier for the GPU adapter. Structure:
typedef struct _LUID {
    DWORD LowPart;
    LONG  HighPart;
} LUID;
Usage:
LUID luid = device.AdapterLuid;
printf("Adapter LUID: %08X-%08X\n", luid.HighPart, luid.LowPart);

DxgiFactory

Type: Microsoft::WRL::ComPtr<IDXGIFactory5> Purpose: DXGI factory for adapter enumeration and resource creation. Capabilities:
  • Enumerate display adapters
  • Query adapter properties
  • Create swap chains
  • Check feature support
Version Support:
  • IDXGIFactory5: Windows 10 Creators Update (1703) and later
  • Provides HDR and advanced color support

Adapter

Type: Microsoft::WRL::ComPtr<IDXGIAdapter1> Purpose: Represents the physical GPU adapter. Common Operations:
// Get adapter description
DXGI_ADAPTER_DESC1 desc;
device.Adapter->GetDesc1(&desc);

// Query video memory
printf("Dedicated Video Memory: %llu MB\n", 
       desc.DedicatedVideoMemory / (1024 * 1024));

// Check for software adapter
if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) {
    printf("Software adapter\n");
}

Device

Type: Microsoft::WRL::ComPtr<ID3D11Device> Purpose: Main D3D11 device for resource creation and rendering. Common Operations:
// Create resources
ID3D11Texture2D* texture;
device.Device->CreateTexture2D(&desc, nullptr, &texture);

// Create shader
ID3D11VertexShader* shader;
device.Device->CreateVertexShader(bytecode, size, nullptr, &shader);

// Check feature support
D3D11_FEATURE_DATA_D3D11_OPTIONS options;
device.Device->CheckFeatureSupport(
    D3D11_FEATURE_D3D11_OPTIONS,
    &options,
    sizeof(options)
);

DeviceContext

Type: Microsoft::WRL::ComPtr<ID3D11DeviceContext> Purpose: Immediate context for issuing rendering commands. Common Operations:
// Copy resource
device.DeviceContext->CopyResource(pDest, pSrc);

// Draw
device.DeviceContext->Draw(vertexCount, 0);

// Map resource for CPU access
D3D11_MAPPED_SUBRESOURCE mapped;
device.DeviceContext->Map(resource, 0, D3D11_MAP_READ, 0, &mapped);
// ... access mapped.pData ...
device.DeviceContext->Unmap(resource, 0);

Device Caching

The driver implements device caching to reuse D3D devices across multiple monitors on the same GPU: Cache Declaration:
static std::map<LUID, std::shared_ptr<Direct3DDevice>, LuidComparator> s_DeviceCache;
static std::mutex s_DeviceCacheMutex;
LUID Comparator:
struct LuidComparator {
    bool operator()(const LUID& a, const LUID& b) const {
        if (a.HighPart != b.HighPart)
            return a.HighPart < b.HighPart;
        return a.LowPart < b.LowPart;
    }
};
Cache Access (Driver.h:137-138):
static std::shared_ptr<Direct3DDevice> GetOrCreateDevice(LUID RenderAdapter);
static void CleanupExpiredDevices();

GetOrCreateDevice

Purpose: Retrieves cached device or creates new one if not cached. Thread Safety: Protected by s_DeviceCacheMutex. Usage:
std::shared_ptr<Direct3DDevice> device = 
    IndirectDeviceContext::GetOrCreateDevice(renderAdapterLuid);
Benefits:
  • Reduces device creation overhead
  • Shares GPU resources across monitors
  • Automatic reference counting

Error Recovery

Device Lost Handling

When GPU device is lost (removed, driver reset, power state change):
// Validate device before use
if (!m_Device || !m_Device->Device) {
    vddlog("e", "Direct3DDevice became invalid");
    return STATUS_UNSUCCESSFUL;
}

// Check for device removed
HRESULT hr = m_Device->Device->GetDeviceRemovedReason();
if (FAILED(hr)) {
    vddlog("e", "Device removed");
    // OS will trigger new swap chain with new device
    return hr;
}

Transient State Recovery

For transient failures during initialization:
HRESULT hr = device.Init();
if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) {
    // Retry after delay
    Sleep(100);
    hr = device.Init();
}

Integration with SwapChainProcessor

The Direct3DDevice is used by SwapChainProcessor for frame processing:
class SwapChainProcessor {
    std::shared_ptr<Direct3DDevice> m_Device;
    
public:
    SwapChainProcessor(IDDCX_SWAPCHAIN hSwapChain,
                       std::shared_ptr<Direct3DDevice> Device,
                       HANDLE NewFrameEvent)
        : m_Device(Device) {
        // Set device to swap chain
        ComPtr<IDXGIDevice> DxgiDevice;
        m_Device->Device.As(&DxgiDevice);
        
        IDARG_IN_SWAPCHAINSETDEVICE SetDevice = {};
        SetDevice.pDevice = DxgiDevice.Get();
        IddCxSwapChainSetDevice(hSwapChain, &SetDevice);
    }
};

Performance Considerations

Factory Recreation

Current Implementation:
hr = CreateDXGIFactory2(0, IID_PPV_ARGS(&DxgiFactory));
Optimization Opportunity:
// Cache factory and check if current
if (cachedFactory && cachedFactory->IsCurrent()) {
    DxgiFactory = cachedFactory;
} else {
    hr = CreateDXGIFactory2(0, IID_PPV_ARGS(&DxgiFactory));
    cachedFactory = DxgiFactory;
}
Trade-offs:
  • Current: Always detects new adapters, slight overhead
  • Cached: Better performance, may miss hot-plugged GPUs

BGRA Support Flag

The D3D11_CREATE_DEVICE_BGRA_SUPPORT flag is mandatory for IddCx drivers:
hr = D3D11CreateDevice(
    Adapter.Get(),
    D3D_DRIVER_TYPE_UNKNOWN,
    nullptr,
    D3D11_CREATE_DEVICE_BGRA_SUPPORT,  // Required!
    // ...
);
Reason: WHQL test suite requires BGRA surface support for compatibility.

Debugging

Debug Device Creation

For development builds, enable D3D debug layer:
UINT createDeviceFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
#ifdef _DEBUG
    createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
#endif

hr = D3D11CreateDevice(
    Adapter.Get(),
    D3D_DRIVER_TYPE_UNKNOWN,
    nullptr,
    createDeviceFlags,
    // ...
);

Logging

The implementation logs all major steps:
vddlog("d", "Initializing Direct3DDevice...");
vddlog("d", "DXGI factory created successfully");
vddlog("i", "Adapter found: [name]");
vddlog("i", "Direct3D device created successfully");
vddlog("e", "Failed to create Direct3D device");

References

Build docs developers (and LLMs) love