背景

一个Layered Window,核心是DX实现渲染,通过UpdateLayeredWindowIndirect更新到窗口DC上,窗口表现形式大概是屏幕右侧有一个占屏幕1/3面积的可交互区域,该区域可被用户拖动和缩放,FPS要求是30+

起因

有一天有用户反馈多屏下有问题,东西拖不过去,我看了下,确实,很快就猜到了原因,一开始我们就没打算支持多屏(因为工期关系),所以在创建窗口时用的是

GetSystemMetrics(SM_CXFULLSCREEN)

创建一个全屏窗口,用户拖动可交互区域的物件时,实际上是DX内部的坐标改变,并不是用MoveWindow()来移动窗口。

第一步,支持多屏

找到原因解决方案就很简单了,通过EnumDisplayDevices和EnumDisplaySettings这两个API来枚举所有显示器信息,之后计算出完整屏幕分辨率,创建这么大的一个窗口,然后修改之前假设以[0,0]为屏幕左上角的代码,事情就解决了,拖动也OK了。后面这段我记得不太清了,应该是遭遇了窗口坐标相关的问题,加之它在我的surface4上显示效果太差了,200%的缩放导致画面及其模糊,于是我打算连高DPI一起支持

第二步,支持高DPI

因为限定了Windows7 SP1以上的系统,所以只需要用LoadLibrary的方式依次判断并调用SetProcessDpiAwareness或SetProcessDPIAware即可。由于之前做过的产品支持DPI时遇到了地狱般的问题,因此现在已经做足了心理准备。

开启DPI之后,其实大部分都很好,显示清晰度完美,但是问题很快也来了,我的surface4是27xx * 17xx的分辨率,DPI缩放倍率是200%,在打开DPI之前,系统实际给我创建的窗口应该是13xx * 8**的分辨率,之后拉伸上来,而我的开发机是1920 * 1080,也就是说我一直是在低分辨率上调试,一切都非常流畅。现在来到了3k屏幕,外加一个1080P的外接显示器,那么我一个窗口位图缓存的大小就从1080P的8MB激增到24MB左右,即使不插外接显示器也有18MB左右。

这意味着什么呢,我们的产品需要达到30FPS的渲染速度,也就是说有一个33ms的定时器,每一次定时器来触发刷新时要从显存中Lock一块内存,复制到HBITMAP里,再把这个HBITMAP更新到窗口DC,那么这就是24MB * 2的内存复制,30FPS意味着每秒会发生总共1440MB的显存/内存拷贝,Excuse me?比起之前1080P屏幕的区区几百MB简直是天文数字,加之我的开发机是i7,而surface4是一个低功率版的i5,直接导致产品卡得没法用!

怎么办?

想办法加裁切区域,由于产品的特性,实际上只有一小块区域在更新,虽然这块区域的大小不可以确定(用户可随时修改),但是我可以把它限制到一个屏幕上,除非它被移动到其他屏幕,那么我们就切换到新屏幕渲染,这一下就少了百分之三四十的内存拷贝量,是个大数字,实测下来性能也确实有所提升,没有之前那么卡了,毕竟只渲染一个屏幕(然而为了确保其他逻辑的简洁我们还是必须要把窗口大小设置为所有屏幕大小的总和)。

然后我着重测试了surface4单屏幕的性能,发现在很多时候还是无法即使响应WM_TIMER以外的系统消息(一个消息处理时间内数据没复制完下一个又来了),以及在有弹出窗口时,性能变得更差,因为我们的弹出窗口是cef的,而这个窗口又是一个Layered Window,会遇到和主窗口一样的性能问题,双倍爆炸

优化内存拷贝

这时我首先想到的是memcpy这个东西,很早以前研究过sse版本的,知道这个东西的优化空间很大,那时还是用VS2008和gcc4。于是去找了几套AVX2和SSE2的版本,实测SSE2的性能表现更佳,对比VS2017原生的memcpy,在我的i7上提升不大,但是在surface4 i5这种低端平台上,大概有20%的提升,已经是一个十分喜人的数据了,一次刷新要用两次memcpy,两次都提升20%爽不爽?

爽!但是性能跑下来还是不满意,虽然不至于老是占满一整个i5的核心,但是也是无法即使响应一些操作,并且依然会导致消息队列里塞着不少未及时处理的WM_TIMER,cef窗口弹出后也一样会小卡,这个结果依然无法接受的!

再次优化

在技术选型时我们选择了创建全屏窗口,这是因为不确定交互对象会出现在哪,并且交互对象会在DX的shader被修改多边形数据,因此其包围盒也是不准确的,如何做到最有效率的刷新?最后我还是妥协了,用户体验和性能,总有一方会妥协。我们临时采用一个折中方案,为交互对象的包围盒加上一个经验间距。只刷新这个大包围盒内部的数据,那么内存拷贝又被砍掉一大半,是不是很爽,这个方案当然也不完美,因为还是有可能出现交互区域的多边形出现在假设包围盒之外的情况,这里需要依靠后期的编辑器改版,让创作人员手动设定一个自己作品的最大可视范围,这样才是更完美的方案。

收尾和完结

至此,整个产品对比最初,显示效果提升了,并且在显示器分辨率变大的情况下,性能反而还比之前的代码提升了,具体数字这里就不方便附上了。期间当然还有一些别的问题,比如SetProcessDpiAwareness有一个PROCESS_PER_MONITOR_DPI_AWARE参数,是windows10新增的,这里我也遇到了问题,surface4屏幕是200%缩放倍率,外接显示器是100%,当我的鼠标在外接屏幕时,使用GetCursorPos会得到一个翻倍的坐标,后来查阅MSDN发现他们还提供了一套诸如GetPhysicalCursorPos的新API,我没有去用,但是我觉得也能曲线解决问题。而我是直接把之前的参数从PROCESS_SYSTEM_DPI_AWARE改成PROCESS_PER_MONITOR_DPI_AWARE,GetCursorPos的坐标就正确了。

对于Windows C++程序来说,支持DPI其实是很麻烦的事情,当然具体复杂程度取决于你的框架和交互逻辑,我用Qt来做的几个程序倒是一步直接迁移,其框架设计功不可没,而我经历的第一个支持高DPI的程序则是地狱般的体验,当年的框架完全没有考虑过这个问题,这次的产品,其实已经很顺利了,毕竟大部分性能问题是别人不会遇到的。相比起来,web程序员真的是轻松很多

一个Layered Window从支持多屏到高DPI开始,遭遇的性能雪崩及其优化历程相关推荐

  1. duilib适配高分屏(高DPI适配)

    duilib适配高分屏 高分屏(高DPI)适配 适配原理(duilib) 适配例子 高分屏(高DPI)适配 随着PC设备高分屏的越来越普及,PC端客户端软件也需要适配一下高分屏幕.在没有专门适配高分屏 ...

  2. qt 获取dpi_Qt 对高 DPI 显示器的支持简介

    高 DPI 显示器 高 DPI 显示器是与标准 DPI 显示器相比具有增加的像素密度的显示器. 该像素密度以每英寸点数(DPI)或每英寸像素数(PPI)测量,并且由显示像素的数量和物理尺寸确定.这意味 ...

  3. 让MFC(c++)编译的程序支持高DPI

    让MFC(c++)编译的程序支持高DPI 问题描述: 写的程序A.exe,在笔记本开着盖子重启电脑,打开程序的时候是有界面的:但是如果点击重启之后把盖子合上之后,等重启完毕再打开A的时候,程序就打不开 ...

  4. android视频播放器ui,ArtVideoPlayer 一个灵活的Android视频播放器,支持全屏,小屏播放...

    ArtPlayer 简介 Kotlin实现的视频播放器,将MediaPlayer与VideoView解耦合,支持切换播放器内核(如ExoPlayer和ijkPlayer),支持自定义控制视图,提供Me ...

  5. 使用AVPlayer自定义支持全屏的播放器(一)

    前言 最近在项目中,遇到了视频播放的需求,直接使用系统封装的播放器太过于简单,不能很好的满足项目要求,于是花时间研究了一下,使用AVPlayer来自定义播放器.     本视频播放器主要自定义了带缓冲 ...

  6. boostrap 鼠标滚轮滑动图片_Bootstrap幻灯片轮播图支持触屏左右手势滑动的实现方法...

    最近ytkah在学习用bootstrap搭建网站,Bootstrap能自适应pc端和手机端,并且移动设备优先,适合现如今移动营销.bootstrap是封装好的框架,需要某些功能只需调用相应的组件就可以 ...

  7. 用JS轻松实现一个录音、录像、录屏工具库

    大家好,我是若川.持续组织了6个月源码共读活动,感兴趣的可以点此加我微信 ruochuan12 参与,每周大家一起学习200行左右的源码,共同进步.同时极力推荐订阅我写的<学习源码整体架构系列& ...

  8. Photoshop CS6 MAC 中文版破解版 支持Retina屏

    Photoshop CS6 MAC 中文版破解版 支持Retina屏 目前世界上"最好的化妆品"是一款叫做PhotoShop的产品,它可以帮你去除所有你不满意的地方.上周末,这款最 ...

  9. Android 屏幕适配 - 支持刘海屏

    刘海屏是指某些设备显示屏上的一个区域延伸到显示面,这样既能为用户提供全面屏体验,又能为设备正面的重要传感器留出空间.Android 在搭载 Android 9(API 级别 28)及更高版本的设备上正 ...

  10. android listview中播放视频,支持全屏

    最近在做项目的时候有需求要在 listView 中播放视频,并且支持横竖屏无缝切换,在网上搜索了一下,关于这种 demo真的很少, 有的也只是实现简单的功能,无法满足项目中的需求,想着修改一下凑合用, ...

最新文章

  1. 图像拼接--Seam Carving for Content-Aware Image Resizing
  2. zendopcache代替APC效果不错
  3. erlang精要(2)-数制
  4. mybatis @sqlprovider
  5. HDU 5308 I Wanna Become A 24-Point Master
  6. Spectral Bounds for Sparse PCA: Exact and Greedy Algorithms[贪婪算法选特征]
  7. python语言网课答案_Python语言答案
  8. 修复IE下相对容器中绝对定位Bug
  9. 数据库课程设计基础需求
  10. python3调用arcpy地理加权回归_分析地理加权回归分析结果的操作方法
  11. HC05蓝牙点亮LED
  12. 黎曼猜想用计算机验证,黎曼猜想的一个“有趣”证明,当今数学界最重要的数学难题之一...
  13. Android 9 隐藏下拉状态栏中的快捷开关
  14. python画狗的代码_简单的Python代码能狗实现哪些丧心病狂的功能
  15. 微信java转盘抽奖代码_jquery手机端抽奖代码_微信jquery实现大转盘抽奖代码特效...
  16. 经济专业需要学c语言吗,学c语言要什么基础?
  17. java音频剪辑_Java混剪音频
  18. getParentFile()方法的一些使用技巧
  19. input和textarea设置placeholder属性的颜色、字体大小
  20. 安迪·鲁宾支持的猫头鹰实验室刚推出了一款机器人视频会议摄像机

热门文章

  1. 苹果系列产品如何保修-Apple官方回复
  2. 阿里云域名搭建DNSLOG
  3. php怎么把图片设置为背景,ppt怎么把图片设为背景
  4. FYI| Free online events
  5. ps的cc版本安装包以及破解包
  6. 通俗理解路由器和交换机的区别?
  7. EAUML日拱一卒-活动图::活动分区
  8. pwm控制直流电机转速流程图_直流电机PWM控制原理
  9. Flutter调用摄像头录像及获取视频信息
  10. php计算1000000以内的质数,1000000以内质数表