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

Javier Celaya javier.celaya at flexvdi.com
Fri Feb 12 13:41:59 UTC 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.
---
 Makefile.am                |  2 ++
 common/vdcommon.h          |  4 +++
 vdagent/desktop_layout.cpp | 44 ++++++++++++++++++-------
 vdagent/wddm.cpp           | 63 +++++++++++++++++++++++++++++++++++
 vdagent/wddm.h             | 82 ++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 183 insertions(+), 12 deletions(-)
 create mode 100644 vdagent/wddm.cpp
 create mode 100644 vdagent/wddm.h

diff --git a/Makefile.am b/Makefile.am
index f51ada0..85f52eb 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -33,6 +33,8 @@ vdagent_SOURCES =			\
 	common/vdlog.h			\
 	vdagent/desktop_layout.cpp	\
 	vdagent/desktop_layout.h	\
+	vdagent/wddm.cpp		\
+	vdagent/wddm.h			\
 	vdagent/display_setting.cpp	\
 	vdagent/display_setting.h	\
 	vdagent/file_xfer.cpp		\
diff --git a/common/vdcommon.h b/common/vdcommon.h
index 93bb673..9d86809 100644
--- a/common/vdcommon.h
+++ b/common/vdcommon.h
@@ -36,6 +36,10 @@ 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
+
 #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..64fa649 100644
--- a/vdagent/desktop_layout.cpp
+++ b/vdagent/desktop_layout.cpp
@@ -15,6 +15,7 @@
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
+#include "wddm.h"
 #include <spice/qxl_windows.h>
 #include <spice/qxl_dev.h>
 #include "desktop_layout.h"
@@ -306,25 +307,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 = d3dkmt.open_adapter_from_hdc(hdc);
+    if (adapter) {
+        // 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);
+    } else {
+        // 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);
+    }
+    DeleteDC(hdc);
 
     //Find the closest size which will fit within the monitor
     for (DWORD i = 0; EnumDisplaySettings(dev_name, i, dev_mode); i++) {
diff --git a/vdagent/wddm.cpp b/vdagent/wddm.cpp
new file mode 100644
index 0000000..ae586c5
--- /dev/null
+++ b/vdagent/wddm.cpp
@@ -0,0 +1,63 @@
+/*
+ *   Copyright (C) 2009 Red Hat, Inc.
+ *
+ *   This program is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU General Public License as
+ *   published by the Free Software Foundation; either version 2 of
+ *   the License, or (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "wddm.h"
+#include "common/vdcommon.h"
+
+D3DKMTLibrary::D3DKMTLibrary()
+    : _pD3DKMTCloseAdapter(NULL)
+    , _pD3DKMTEscape(NULL)
+    , _pD3DKMTOpenAdapterFromHdc(NULL)
+{
+    HINSTANCE _hGDI = GetModuleHandle(L"gdi32.dll");
+    if (_hGDI) {
+        _pD3DKMTCloseAdapter = (PFND3DKMT_CLOSEADAPTER)GetProcAddress(_hGDI, "D3DKMTCloseAdapter");
+        _pD3DKMTEscape = (PFND3DKMT_ESCAPE)GetProcAddress(_hGDI, "D3DKMTEscape");
+        _pD3DKMTOpenAdapterFromHdc = (PFND3DKMT_OPENADAPTERFROMHDC)GetProcAddress(_hGDI, "D3DKMTOpenAdapterFromHdc");
+    }
+}
+
+D3DKMT_HANDLE D3DKMTLibrary::open_adapter_from_hdc(HDC hdc)
+{
+    if (!_pD3DKMTOpenAdapterFromHdc) return 0;
+    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)
+{
+    if (!_pD3DKMTEscape) return -1;
+    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)
+{
+    if (!_pD3DKMTCloseAdapter) return -1;
+    D3DKMT_CLOSEADAPTER func_params = {0};
+    func_params.hAdapter = adapter;
+    return _pD3DKMTCloseAdapter(&func_params);
+}
diff --git a/vdagent/wddm.h b/vdagent/wddm.h
new file mode 100644
index 0000000..408c0b6
--- /dev/null
+++ b/vdagent/wddm.h
@@ -0,0 +1,82 @@
+/*
+ *   Copyright (C) 2009 Red Hat, Inc.
+ *
+ *   This program is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU General Public License as
+ *   published by the Free Software Foundation; either version 2 of
+ *   the License, or (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _H_WDDM
+#define _H_WDDM
+
+#include <windows.h>
+
+// 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;
+};
+
+class D3DKMTLibrary {
+public:
+    static D3DKMTLibrary& singleton() {
+        static D3DKMTLibrary instance;
+        return instance;
+    }
+    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:
+    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*);
+
+    D3DKMTLibrary();
+    D3DKMTLibrary(const D3DKMTLibrary &) {}
+    D3DKMTLibrary &operator=(const D3DKMTLibrary &) { return *this; }
+
+    PFND3DKMT_CLOSEADAPTER _pD3DKMTCloseAdapter;
+    PFND3DKMT_ESCAPE _pD3DKMTEscape;
+    PFND3DKMT_OPENADAPTERFROMHDC _pD3DKMTOpenAdapterFromHdc;
+};
+
+#endif
-- 
2.4.3



More information about the Spice-devel mailing list