[Spice-devel] [vd-agent-win32 2/2] Use CCD API to update the WDDM display modes.

Frediano Ziglio fziglio at redhat.com
Wed Feb 10 16:33:19 CET 2016


> 
> When a new custom display mode is added, the current WDDM driver notifies
> a disconnection and reconnection of the virtual monitor to force Windows
> to update the display modes. This produces an ugly effect, keeping the
> screen black for up to some seconds and usually not repainting it
> afterwards.
> 
> This patch uses the CCD API to update the display modes, and produces
> just a quick flash followed by a whole screen repaint. For best results,
> it should be used with a driver that does not update the display modes
> by itself, but it is still compatible with the current implementation.
> ---
>  common/vdcommon.h          | 40 +++++++++++++++++++++++++++
>  vdagent/desktop_layout.cpp | 68
>  +++++++++++++++++++++++++++++++++++++++++++++-
>  2 files changed, 107 insertions(+), 1 deletion(-)
> 
> diff --git a/common/vdcommon.h b/common/vdcommon.h
> index f40e68e..29437c3 100644
> --- a/common/vdcommon.h
> +++ b/common/vdcommon.h
> @@ -102,6 +102,46 @@ private:
>      PFND3DKMT_OPENADAPTERFROMHDC _pD3DKMTOpenAdapterFromHdc;
>  };
>  
> +// CCD API
> +#define SDC_APPLY 0x00000080
> +#define SDC_USE_SUPPLIED_DISPLAY_CONFIG 0x00000020
> +#define SDC_FORCE_MODE_ENUMERATION 0x00001000
> +#define QDC_ALL_PATHS 1
> +struct DISPLAYCONFIG_PATH_INFO;
> +struct DISPLAYCONFIG_MODE_INFO;
> +struct DISPLAYCONFIG_TOPOLOGY_ID;
> +typedef LONG (*PFNGETDISPLAYCONFIGBUFFERSIZES)(UINT32, UINT32*, UINT32*);
> +typedef LONG (*PFNQUERYDISPLAYCONFIG)(UINT32, UINT32*,
> DISPLAYCONFIG_PATH_INFO*, UINT32*,
> +                                      DISPLAYCONFIG_MODE_INFO*,
> DISPLAYCONFIG_TOPOLOGY_ID*);
> +typedef LONG (*PFNSETDISPLAYCONFIG)(UINT32, DISPLAYCONFIG_PATH_INFO*,
> UINT32,
> +                                    DISPLAYCONFIG_MODE_INFO*, UINT32);
> +class CCDLibrary {
> +public:
> +    ~CCDLibrary();
> +    static CCDLibrary& singleton() {
> +        static CCDLibrary instance;
> +        return instance;
> +    }
> +    bool found() {
> +        return _hUser32 && _pGetDisplayConfigBufferSizes &&
> +               _pQuieryDisplayConfig && _pSetDisplayConfig;
> +    }
> +    LONG get_display_config_buffer_sizes(UINT32* num_paths, UINT32*
> num_modes);
> +    LONG query_display_config(UINT32* num_paths, DISPLAYCONFIG_PATH_INFO*
> paths,
> +                              UINT32* num_modes, DISPLAYCONFIG_MODE_INFO*
> modes);
> +    LONG set_display_config(UINT32 num_paths, DISPLAYCONFIG_PATH_INFO*
> paths,
> +                            UINT32 num_modes, DISPLAYCONFIG_MODE_INFO*
> modes,
> +                            UINT32 flags);
> +
> +private:
> +    CCDLibrary();
> +
> +    HINSTANCE _hUser32;
> +    PFNGETDISPLAYCONFIGBUFFERSIZES _pGetDisplayConfigBufferSizes;
> +    PFNQUERYDISPLAYCONFIG _pQuieryDisplayConfig;
> +    PFNSETDISPLAYCONFIG _pSetDisplayConfig;
> +};
> +
>  #if defined __GNUC__
>  #define ALIGN_GCC __attribute__ ((packed))
>  #define ALIGN_VC
> diff --git a/vdagent/desktop_layout.cpp b/vdagent/desktop_layout.cpp
> index b5e8cfa..92493f3 100644
> --- a/vdagent/desktop_layout.cpp
> +++ b/vdagent/desktop_layout.cpp
> @@ -312,6 +312,70 @@ NTSTATUS D3DKMTLibrary::close_adapter(D3DKMT_HANDLE
> adapter)
>      return _pD3DKMTCloseAdapter(&func_params);
>  }
>  
> +CCDLibrary::CCDLibrary()
> +    : _pGetDisplayConfigBufferSizes(NULL)
> +    , _pQuieryDisplayConfig(NULL)
> +    , _pSetDisplayConfig(NULL)
> +{
> +    _hUser32 = LoadLibrary(L"User32.dll");

probably you want a GetModuleHandle, user32 have to be already available.

> +    if (_hUser32) {
> +        _pGetDisplayConfigBufferSizes =
> (PFNGETDISPLAYCONFIGBUFFERSIZES)GetProcAddress(_hUser32,
> "GetDisplayConfigBufferSizes");
> +        _pQuieryDisplayConfig =
> (PFNQUERYDISPLAYCONFIG)GetProcAddress(_hUser32, "QueryDisplayConfig");
> +        _pSetDisplayConfig = (PFNSETDISPLAYCONFIG)GetProcAddress(_hUser32,
> "SetDisplayConfig");
> +    }
> +}
> +
> +
> +CCDLibrary::~CCDLibrary()
> +{
> +    if (_hUser32)
> +        FreeLibrary(_hUser32);

If you use GetModuleHandle this is not necessary

> +}
> +
> +LONG CCDLibrary::get_display_config_buffer_sizes(UINT32* num_paths, UINT32*
> num_modes)
> +{
> +    return _pGetDisplayConfigBufferSizes(QDC_ALL_PATHS, num_paths,
> num_modes);
> +}
> +
> +LONG CCDLibrary::query_display_config(UINT32* num_paths,
> DISPLAYCONFIG_PATH_INFO* paths,
> +                                      UINT32* num_modes,
> DISPLAYCONFIG_MODE_INFO* modes)
> +{
> +    return _pQuieryDisplayConfig(QDC_ALL_PATHS, num_paths, paths, num_modes,
> modes, NULL);
> +}
> +
> +LONG CCDLibrary::set_display_config(UINT32 num_paths,
> DISPLAYCONFIG_PATH_INFO* paths,
> +                                    UINT32 num_modes,
> DISPLAYCONFIG_MODE_INFO* modes,
> +                                    UINT32 flags)
> +{
> +    return _pSetDisplayConfig(num_paths, paths, num_modes, modes, flags);
> +}
> +
> +static void update_display_modes()
> +{
> +    // The trick here is to call SetDisplayConfig with the current config
> and
> +    // the SDC_FORCE_MODE_ENUMERATION flag
> +    CCDLibrary& ccd = CCDLibrary::singleton();
> +    if (!ccd.found()) return;
> +    UINT32 num_paths = 0, num_modes = 0;
> +    DISPLAYCONFIG_PATH_INFO * paths;
> +    DISPLAYCONFIG_MODE_INFO * modes;
> +    LONG status = ccd.get_display_config_buffer_sizes(&num_paths,
> &num_modes);
> +    if (NT_SUCCESS(status)) {
> +        // Being conservative, struct sizes are under 100 bytes
> +        paths = reinterpret_cast<DISPLAYCONFIG_PATH_INFO *>(new
> char[num_paths * 100]);
> +        modes = reinterpret_cast<DISPLAYCONFIG_MODE_INFO *>(new
> char[num_modes * 100]);

why not

paths = new DISPLAYCONFIG_PATH_INFO[num_paths];
modes = new DISPLAYCONFIG_MODE_INFO[num_modes];

> +        status = ccd.query_display_config(&num_paths, paths, &num_modes,
> modes);
> +        if (NT_SUCCESS(status)) {
> +            ccd.set_display_config(num_paths, paths, num_modes, modes,
> +                                   SDC_APPLY |
> +                                   SDC_USE_SUPPLIED_DISPLAY_CONFIG |
> +                                   SDC_FORCE_MODE_ENUMERATION);
> +        }
> +        delete[] reinterpret_cast<char *>(paths);
> +        delete[] reinterpret_cast<char *>(modes);

and here

delete[] paths;
delete[] modes;

Frediano

> +    }
> +}
> +
>  bool DesktopLayout::init_dev_mode(LPCTSTR dev_name, DEVMODE* dev_mode,
>  DisplayMode* mode,
>                                    LONG normal_x, LONG normal_y, bool
>                                    set_pos)
>  {
> @@ -384,7 +448,9 @@ bool DesktopLayout::init_dev_mode(LPCTSTR dev_name,
> DEVMODE* dev_mode, DisplayMo
>              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)) {
> +            if (NT_SUCCESS(status)) {
> +                update_display_modes();
> +            } else {
>                  vd_printf("escape failed with error 0x%08X", status);
>              }
>          }



More information about the Spice-devel mailing list