让远程桌面更好的支持游戏
云游戏为什么要用到远程桌面
对于单进程单窗口的游戏只需要捕捉一个渲染画面,云游戏采用hook+sandboxie的方式基本可以在默认桌面模拟出完整的独立运行环境。但是对于带启动器的游戏,上面的方案并不能很好的工作,因为用户可能随时切换窗口或者游戏存在不确定的弹窗,为此需要给游戏提供独立的操作桌面(类似ecs,相当于一个游戏分配一个虚拟机)。
如果一个虚拟机只跑一个游戏,也会造成成本提升/资源浪费。由于服务器由硬件厂家提供,我们几乎没有diy的空间,同时我们也没有kvm的能力储备,所以调研方向变成了怎么在一个虚拟机上运行多个桌面。windows会给每个单独登录的用户分配独立的桌面,于是远程桌面自然成了首选方案。
远程桌面中运行游戏遇到的典型问题
游戏启动异常
通过逆向游戏进程发现是游戏主动检测远程桌面抛出异常 (通过GetSystemMetrics SM_REMOTESESSION)
解决方案:注入dll hook GetSystemMetrics或者驱动动态修改相关内存
游戏启动弹窗
通过逆向游戏进程发现是系统dxgi.dll返回失败导致游戏以为显卡有问题
dxgi.dll的实现,如果是远程桌面显示器那么直接返回失败
解决方案:patch dxgi.dll然后签名,覆盖原始文件
远程桌面内主动修改分辨率
由于原始桌面的限制,远程桌面内不允许修改分辨率(手动和代码修改都不行)
然而dxgi.dll经过patch后游戏已经能获取到完整的分辨率列表,游戏内用户主动修改分辨率会无效,体验略奇怪
解决方案:远程桌面的客户端选用 https://github.com/FreeRDP/FreeRDP 或者商店版本的mstsc,hook游戏的win32u.dll NtUserChangeDisplaySettings, 当修改分辨率的时候参数透传给freerdp/mstsc, 同步等待操作完成。
rdp对帧数的限制
帧率其实有两个,一个是游戏自身的帧率,一个是dxgi抓屏的帧率
https://learn.microsoft.com/zh-cn/troubleshoot/windows-server/remote/frame-rate-limited-to-30-fps
rdp的fps也是最玄学的部分,微软官方还写了文章教你设置fps。其实文章并不准确,因为windows用户态的Sleep函数精度无法保证。
帧速率映射
15 decimal = 60 帧
10 decimal = 40 帧
5 decimal = 20 帧
1 decimal = 4 帧
通过010暴搜DWMFRAMEINTERVAL,定位到关键的几个文件
逆向发现如果开启远程桌面,dxgkrnl.sys会专门启动内核线程进行vsync模拟, 同时dwm.exe会额外加载rdsdwmdr.dll进行离屏渲染逻辑
v11即注册表配置的值
大概分析逻辑后对相关的文件patch魔改,使得《坦克世界》rdp下双开都能稳定在60fps(目前的业务需求只需要60即可,其实能在60-90之间任意值稳定)