[Spice-devel] [vd-agent-win32 1/2] Detect and use the new WDDM driver

Javier Celaya javier.celaya at flexvdi.com
Thu Feb 4 13:31:47 CET 2016


When setting a new desktop resolution, detect whether the XDDM or the
WDDM driver is loaded, and use the correct API.

This patch is based on the one by Vadim Rozenfeld, with some
improvements:
- Valid for both the XDDM and the WDDM drivers.
- Avoids flickering when the target resolution is the current one.
- Does not define unused constants.
---
 common/vdcommon.h          | 66 ++++++++++++++++++++++++++++++++++
 vdagent/desktop_layout.cpp | 90 +++++++++++++++++++++++++++++++++++++++-------
 2 files changed, 144 insertions(+), 12 deletions(-)

diff --git a/common/vdcommon.h b/common/vdcommon.h
index 93bb673..f40e68e 100644
--- a/common/vdcommon.h
+++ b/common/vdcommon.h
@@ -36,6 +36,72 @@ typedef CRITICAL_SECTION mutex_t;
 #define VD_AGENT_REGISTRY_KEY "SOFTWARE\\Red Hat\\Spice\\vdagent\\"
 #define VD_AGENT_STOP_EVENT   TEXT("Global\\vdagent_stop_event")
 
+#ifndef NT_SUCCESS
+#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
+#endif
+
+// WDDM data types and constants
+#define D3DKMT_HANDLE ULONG
+
+typedef UINT D3DDDI_VIDEO_PRESENT_SOURCE_ID;
+
+struct D3DKMT_OPENADAPTERFROMHDC {
+    HDC                             hDc;            // in:  DC that maps to a single display
+    D3DKMT_HANDLE                   hAdapter;       // out: adapter handle
+    LUID                            AdapterLuid;    // out: adapter LUID
+    D3DDDI_VIDEO_PRESENT_SOURCE_ID  VidPnSourceId;  // out: VidPN source ID for that particular display
+};
+
+enum D3DKMT_ESCAPETYPE {
+    D3DKMT_ESCAPE_DRIVERPRIVATE =  0,
+};
+
+struct D3DDDI_ESCAPEFLAGS {
+    // We do not care about these flags
+    UINT Value;
+};
+
+struct D3DKMT_ESCAPE {
+    D3DKMT_HANDLE       hAdapter;               // in: adapter handle
+    D3DKMT_HANDLE       hDevice;                // in: device handle [Optional]
+    D3DKMT_ESCAPETYPE   Type;                   // in: escape type.
+    D3DDDI_ESCAPEFLAGS  Flags;                  // in: flags
+    VOID*               pPrivateDriverData;     // in/out: escape data
+    UINT                PrivateDriverDataSize;  // in: size of escape data
+    D3DKMT_HANDLE       hContext;               // in: context handle [Optional]
+};
+
+struct D3DKMT_CLOSEADAPTER {
+    D3DKMT_HANDLE hAdapter;
+};
+
+typedef NTSTATUS (APIENTRY *PFND3DKMT_CLOSEADAPTER)(_In_ CONST D3DKMT_CLOSEADAPTER*);
+typedef NTSTATUS (APIENTRY *PFND3DKMT_ESCAPE)(_In_ CONST D3DKMT_ESCAPE*);
+typedef NTSTATUS (APIENTRY *PFND3DKMT_OPENADAPTERFROMHDC)(_Inout_ D3DKMT_OPENADAPTERFROMHDC*);
+
+class D3DKMTLibrary {
+public:
+    ~D3DKMTLibrary();
+    static D3DKMTLibrary& singleton() {
+        static D3DKMTLibrary instance;
+        return instance;
+    }
+    bool found() {
+        return _hGDI && _pD3DKMTCloseAdapter && _pD3DKMTEscape && _pD3DKMTOpenAdapterFromHdc;
+    }
+    D3DKMT_HANDLE open_adapter_from_hdc(HDC hdc);
+    NTSTATUS escape_driver_private(D3DKMT_HANDLE adapter, VOID* priv_data, UINT size);
+    NTSTATUS close_adapter(D3DKMT_HANDLE adapter);
+
+private:
+    D3DKMTLibrary();
+
+    HINSTANCE _hGDI;
+    PFND3DKMT_CLOSEADAPTER _pD3DKMTCloseAdapter;
+    PFND3DKMT_ESCAPE _pD3DKMTEscape;
+    PFND3DKMT_OPENADAPTERFROMHDC _pD3DKMTOpenAdapterFromHdc;
+};
+
 #if defined __GNUC__
 #define ALIGN_GCC __attribute__ ((packed))
 #define ALIGN_VC
diff --git a/vdagent/desktop_layout.cpp b/vdagent/desktop_layout.cpp
index a7666ca..b5e8cfa 100644
--- a/vdagent/desktop_layout.cpp
+++ b/vdagent/desktop_layout.cpp
@@ -265,6 +265,53 @@ bool DesktopLayout::get_qxl_device_id(WCHAR* device_key, DWORD* device_id)
     return key_found;
 }
 
+D3DKMTLibrary::D3DKMTLibrary()
+    : _pD3DKMTCloseAdapter(NULL)
+    , _pD3DKMTEscape(NULL)
+    , _pD3DKMTOpenAdapterFromHdc(NULL)
+{
+    _hGDI = LoadLibrary(L"gdi32.dll");
+    if (_hGDI) {
+        _pD3DKMTCloseAdapter = (PFND3DKMT_CLOSEADAPTER)GetProcAddress(_hGDI, "D3DKMTCloseAdapter");
+        _pD3DKMTEscape = (PFND3DKMT_ESCAPE)GetProcAddress(_hGDI, "D3DKMTEscape");
+        _pD3DKMTOpenAdapterFromHdc = (PFND3DKMT_OPENADAPTERFROMHDC)GetProcAddress(_hGDI, "D3DKMTOpenAdapterFromHdc");
+    }
+}
+
+D3DKMTLibrary::~D3DKMTLibrary()
+{
+    if (_hGDI)
+        FreeLibrary(_hGDI);
+}
+
+D3DKMT_HANDLE D3DKMTLibrary::open_adapter_from_hdc(HDC hdc)
+{
+    D3DKMT_OPENADAPTERFROMHDC func_params = {0};
+    func_params.hDc = hdc;
+    if (NT_SUCCESS(_pD3DKMTOpenAdapterFromHdc(&func_params)))
+        return func_params.hAdapter;
+    else
+        return 0;
+}
+
+NTSTATUS D3DKMTLibrary::escape_driver_private(D3DKMT_HANDLE adapter, VOID* priv_data, UINT size)
+{
+    D3DKMT_ESCAPE func_params = {0};
+    func_params.hAdapter = adapter;
+    func_params.Flags.Value = 0;
+    func_params.Type = D3DKMT_ESCAPE_DRIVERPRIVATE;
+    func_params.pPrivateDriverData = priv_data;
+    func_params.PrivateDriverDataSize = size;
+    return _pD3DKMTEscape(&func_params);
+}
+
+NTSTATUS D3DKMTLibrary::close_adapter(D3DKMT_HANDLE adapter)
+{
+    D3DKMT_CLOSEADAPTER func_params = {0};
+    func_params.hAdapter = adapter;
+    return _pD3DKMTCloseAdapter(&func_params);
+}
+
 bool DesktopLayout::init_dev_mode(LPCTSTR dev_name, DEVMODE* dev_mode, DisplayMode* mode,
                                   LONG normal_x, LONG normal_y, bool set_pos)
 {
@@ -306,25 +353,44 @@ bool DesktopLayout::init_dev_mode(LPCTSTR dev_name, DEVMODE* dev_mode, DisplayMo
     if (!hdc) {
         vd_printf("failed to create DC");
         return false;
-    } else {
-        // Update custom resolution
-        custom.xres = mode->_width;
-        custom.yres = mode->_height;
-        custom.bpp = mode->_depth;
+    }
+
+    // Update custom resolution
+    custom.xres = mode->_width;
+    custom.yres = mode->_height;
+    custom.bpp = mode->_depth;
 
+    D3DKMTLibrary& d3dkmt = D3DKMTLibrary::singleton();
+    D3DKMT_HANDLE adapter;
+    if (!d3dkmt.found() || !(adapter = d3dkmt.open_adapter_from_hdc(hdc))) {
+        // Assume XDDM Driver
         int err = ExtEscape(hdc, QXL_ESCAPE_SET_CUSTOM_DISPLAY,
                             sizeof(QXLEscapeSetCustomDisplay), (LPCSTR)&custom, 0, NULL);
         if (err <= 0) {
             vd_printf("can't set custom display, perhaps an old driver");
         }
-        DeleteDC(hdc);
-    }
 
-    // force refresh mode table
-    DEVMODE tempDevMode;
-    ZeroMemory(&tempDevMode, sizeof (tempDevMode));
-    tempDevMode.dmSize = sizeof(DEVMODE);
-    EnumDisplaySettings(dev_name, 0xffffff, &tempDevMode);
+        // force refresh mode table
+        DEVMODE tempDevMode;
+        ZeroMemory(&tempDevMode, sizeof (tempDevMode));
+        tempDevMode.dmSize = sizeof(DEVMODE);
+        EnumDisplaySettings(dev_name, 0xffffff, &tempDevMode);
+    } else {
+        // WDDM Driver
+        if (!EnumDisplaySettings(dev_name, ENUM_CURRENT_SETTINGS, dev_mode) ||
+                (dev_mode->dmPelsWidth & ~0x3) != (mode->_width & ~0x3) ||
+                (dev_mode->dmPelsHeight & ~0x3) != (mode->_height & ~0x3) ||
+                dev_mode->dmBitsPerPel != mode->_depth) {
+            vd_printf("escape xres = %d, yres = %d, bpp = %d", custom.xres, custom.yres, custom.bpp);
+            NTSTATUS status = d3dkmt.escape_driver_private(adapter, &custom,
+                                                           sizeof(QXLEscapeSetCustomDisplay));
+            if (!NT_SUCCESS(status)) {
+                vd_printf("escape failed with error 0x%08X", status);
+            }
+        }
+        d3dkmt.close_adapter(adapter);
+    }
+    DeleteDC(hdc);
 
     //Find the closest size which will fit within the monitor
     for (DWORD i = 0; EnumDisplaySettings(dev_name, i, dev_mode); i++) {
-- 
2.4.3



More information about the Spice-devel mailing list