https://zhuanlan.zhihu.com/p/550285855
工作中使用到了Qt的qwebengine模块来实现了一个定制化的浏览器,在测试环境下一切正常,但是到了生产上,由于设备软硬件环境五花八门(甚至有10年前设备),出现了例如黑屏,白屏的问题。经过一两个月的痛苦折磨终于找到原因。 黑屏问题现象:软件将QWebengineView全屏显示,屏幕黑屏。该问题只是部分设备出现,且偶然发生,难以复现。后面发现,部分设备会有“wglCreateContextAttribsARB() Failed”相关报错。 问题分析:报错内容wgl和窗体的渲染相关,所以查了一下QT和QWebengine渲染相关的东西,大致如下: Qt的渲染机制 QT程序有三种不同的窗体类型,包括QWidget,QGraphics,QQuick,他们的渲染机制也不相同。 - QWidget是最常用的UI界面类,其实现方式是对各平台对应控件的封装,通过平台自身的绘制工具绘制界面,可以直接使用OpenGL。
- QGraphics是QT为了提升大量简单组件的渲染性能而创造的模块,不一定需要使用OpenGL。
- QQuick是QML(一种声明式的界面编程语言)应用程序使用的QT库,从Qt Quick 2.x 起统一使用OpenGL ES 2.0 或者 OpenGL 2.0来渲染界面。渲染方式更倾向于优先使用显卡,通过硬件来加速,所以现在使用QML需要良好的显卡支持。
同时,Qt中有3中不同的渲染方式,可通过设置环境变量来指定,相关机制可参考官网说明: 翻译一下就是,以下三种 - Qt::AA_UseDesktopOpenGL:使用显卡的openGL库,且要求支持openGL 2.1及以上的版本。因此很多老旧设备是不满足版本要求的(windows默认的驱动版本只支持openGL1.1)。(依赖硬件,即硬件加速渲染)
- Qt::AA_UseOpenGLES:使用Angle库来将 DirectX 11或者DirectX 9的接口转成OpenGL ES2.0的API,从而使得windows上显卡驱动不满足要求的设备也能够正常运行。(通过API封装,使得显卡驱动版本低的设备也可以正常运行)
- Qt::AA_UseSoftwareOpenGL:纯软件实现渲染,不依赖于显卡驱动,当然也没硬件加速。
通过以下代码指定渲染方式,而且需要在Application创建前(创建前还是消息循环前忘记了...反正要早点设) QCoreApplication::setAttribute(Qt::AA_UseSoftwareOpenGL);
在QT5.5之后,默认使用了一种智能的动态加载方式,QT会根据部署的设备环境,自动选择合适的OpenGL实现方式。若机器上显卡驱动不支持OpenGL2.0,QT自动使用第二种通过ANGLE调用Derict3D的方式进行渲染。 QWebEngineView的页面渲染 见QWebEngine的说明: Note:The Qt WebEngine Widgets module uses theQt Quick scene graphto compose the elements of a web page into one view. This means that the UI process requires OpenGL ES 2.0 or OpenGL 2.0 for its rendering. 就是说QWebengine模块使用的是QQucik方式来将一个网页的所有元素压缩到一个网页中,所以QWebengine需要通过OpenGL ES 2.0或者OpenGL 2.0进行渲染。 对于QQuick模块,QT也提供了Quick2DRenderer方案来代替QQuick,使得QQuick应用可以不使用OpenGL,而是使用软件的Qt的栅格化引擎实现替代。相当于给出了软件渲染的形式。通过设置环境变量QMLSCENE_DEVICE=softwarecontext实现。
解决办法 通过问题设备信息收集,并未发现问题设备有什么共性,所以就打算牺牲性能,都用软件渲染,避免对环境依赖。 使用软件渲染,现在就存在两个要设置的环境变量,一个是使用Qt::AA_UseSoftwareOpenGL,一个是使用QMLSCENE_DEVICE=softwarecontext,经过数周大面积验证,解决了黑屏问题。 其他的点: - 指定使用Qt::AA_UseOpenGLES,部分设备会出现弹窗。
- 也尝试仅设置两个变量中的一个,都不行,会出现一些奇怪的显示问题,或者弹窗。
- 性能问题,没做定量比较,肉眼是无法看出区别的。
- 额外收获,之前偶发的程序崩溃问题也随着设置软件渲染消失了。
白屏问题本以为黑屏解决了,结束了天天晚上开会攻坚的生活,没想到,又来个白屏。这个还更诡异,只有一个省的设备会有这个问题,也是偶发的,没法在测试环境复现。
现象 QWebEngineView在加载完网页后,突然间就消失了只剩下背景(软件整了个背景图,并且设置了网页背景透明),日志也啥都没有。
分析 这个也是在组长建议下搜索了一些QWebEngine报告的Bug,偶然发现一个bug,大概是说QWebengine模块会有个专门的进程QWebEngineProcess负责网页的渲染等相关内容,如果在运行过程中这个进程崩了,网页内容就没了,一般情况下就白屏了,在我们的软件上就是显示背景图了。 通过对QWebEngineView::renderProcessTerminated信号进行处理,发现问题设备确实是因为这个进程挂了导致的白屏。示例代码在Qt自带的例子中有(D:\Qt\Qt5.14.2\Examples\Qt-5.14.2\webenginewidgets\simplebrowser项目的webView.cpp开头) connect(this, &QWebEngineView::renderProcessTerminated, [this](QWebEnginePage::RenderProcessTerminationStatus termStatus, int statusCode) { QString status; switch (termStatus) { case QWebEnginePage::NormalTerminationStatus: status = tr("Render process normal exit"); break; case QWebEnginePage::AbnormalTerminationStatus: status = tr("Render process abnormal exit"); break; case QWebEnginePage::CrashedTerminationStatus: status = tr("Render process crashed"); break; case QWebEnginePage::KilledTerminationStatus: status = tr("Render process killed"); break; } QMessageBox::StandardButton btn = QMessageBox::question(window(), status, tr("Render process exited with code: %1\n" "Do you want to reload the page ?").arg(statusCode)); if (btn == QMessageBox::Yes) QTimer::singleShot(0, [this] { reload(); }); });
例子中通过reload()恢复了页面,但这种方式只是重新加载页面,不能恢复网页原来的状态(比如运行了一些js,用户输入了内容)。目前试了几种方法,都没能在重新加载页面的同时恢复网页原来的状态。如果哪位大佬有办法欢迎补充!感激不尽! 从代码可以看到,结束原因存在4种“官方记录的经典”原因,但是问题设备都没有走到这几种case里面,错误码是536870904,查到是chrome里面的错误码,out of memory,内存问题。 同时发现问题设备在重装系统或者加了内存条之后就不会再出现白屏问题了,就通过这种方式解决了。
|