1.问题描述
在某些机器上偶尔看到进程ukui-kwin-x11占用cpu使用率100%的情况。
2.期望
在任何机器上不允许看到进程ukui-kwin-x11占用cpu使用率100%的情况。
3.问题分析定位
Ukui-kwin最大的一个特点就是支持窗口模糊等等特效,而也就是这个模糊特效要求使得cpu占用率会比不支持窗口模糊特效的情况(或者关闭模特时的情况)明显增高,因为它在维持这种毛玻璃窗口效果时,会持续进行噪点和模糊度绘制。
Ukui-kwin首先得开启混成器,否则任何特效都不支持,开启混成时有两种后端选择方式,分别是OpenGL和XRender,XRender后端支持绝大部分普通特效,但一些高端特效不支持;OpenGL后端支持所有特效,包括窗口模糊、圆角等高端特效。
但并不是所有机器的性能及显卡支持OpenGL作为渲染后端,Ukui-kwin针对机器的显卡以及CPU性能,在开机启动时会做自我检测工作,如果显卡以及CPU性能不够,则不会启动OpenGL后端。
4.问题解决方案
为了对性能进行优化,近期我们进行了以下工作。最开始,Ukui-kwin针对机器的显卡以及CPU性能,在开机启动时会做自我检测工作,如果显卡以及CPU性能达不到要求,则直接关闭混成器,这样所有特效都没有了,尤其是无边框窗体的边框自我绘制功能失效,出现黑框的情况,这是不允许的。
3.1 ukui-kwin开机自检不通过,不关闭混成,渲染后端走XRender,代码如下:
bool X11StandalonePlatform::compositingPossible() const
{
// first off, check whether we figured that we'll crash on detection because of a buggy driver
KConfigGroup gl_workaround_group(kwinApp()->config(), "Compositing");
const QString unsafeKey(QLatin1String("OpenGLIsUnsafe") + (kwinApp()->isX11MultiHead() ? QString::number(kwinApp()->x11ScreenNumber()) : QString()));
if (gl_workaround_group.readEntry("Backend", "OpenGL") == QLatin1String("OpenGL") && gl_workaround_group.readEntry(unsafeKey, false))
{
//对于从配置文件中读取的参数OpenGLIsUnsafe为true直接忽略
fputs("X11StandalonePlatform::compositingPossible, 从配置文件中读取的参数OpenGLIsUnsafe为true直接忽略\n", stderr);
//return false;
}
if (!Xcb::Extensions::self()->isCompositeAvailable()) {
qCDebug(KWIN_CORE) << "No composite extension available";
return false;
}
if (!Xcb::Extensions::self()->isDamageAvailable()) {
qCDebug(KWIN_CORE) << "No damage extension available";
return false;
}
if (hasGlx())
return true;
#ifdef KWIN_HAVE_XRENDER_COMPOSITING
if (Xcb::Extensions::self()->isRenderAvailable() && Xcb::Extensions::self()->isFixesAvailable())
return true;
#endif
if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES) {
return true;
} else if (qstrcmp(qgetenv("KWIN_COMPOSE"), "O2ES") == 0) {
return true;
}
qCDebug(KWIN_CORE) << "No OpenGL or XRender/XFixes support";
return false;
}
3.2 3D性能优化
之前,我们在对开启毛玻璃特效后,进行过3D性能测试,开启时比不开启时,3D性能跑分减少约三分之一。
而我们之前开启毛玻璃的开关有两个,一个在Ukui-kwin侧,另一个在主题框架侧,必须2个开关都开启才能使毛玻璃特效生效,后来经过定位分析,主题框架在开启的过程中是针对系统所有应用,经过改进后,我们将主题框架的开关去除,对只有需要支持模糊特效窗口的自研桌面应用采用独自开启的方案方式,使用新方案后3D性能提升46%。
3.3 由于自检测机制并不是万能的,对于个别机器,Ukui-kwin自检测期间,其他桌面应用启动时,在进入QApplication的构造函数时会出不来,出现任务栏和桌面无法完全启动而黑屏的情况,而尤其是不支持OpenGL作为后端的机器居多。
解决方案:将不支持OpenGL作为后端的机器,将其CPU型号和显卡类型进入黑名单记录适配,代码如下:
bool X11StandalonePlatform::adaptCPUPerformance() const
{
//从系统文件中读取CPU信息
QFile file;
QString strCPUInfoList;
file.setFileName(CPU_INFO);
bool bRet = file.open(QIODevice::ReadOnly | QIODevice::Text);
if(false == bRet)
{
file.close();
return false;
}
strCPUInfoList = QString(file.readAll());
file.close();
//解析CPU信息,提取model name
QStringList lines = strCPUInfoList.split(QRegExp("[\r\n]"), QString::SkipEmptyParts);
QString strLocalCPUInfo;
foreach (QString line, lines) {
if (line.startsWith("model name")) {
strLocalCPUInfo = line.split(":").at(1).trimmed();
break;
}
}
if("" == strLocalCPUInfo)
{
return false;
}
QFile fileConfig;
fileConfig.setFileName(LOW_PERFORMANCE_CPU_LIST);
bRet = fileConfig.open(QIODevice::ReadOnly | QIODevice::Text);
if(false == bRet)
{
fileConfig.close();
return false;
}
QString strCPUConfig = QString(fileConfig.readAll());
fileConfig.close();
if("" == strCPUConfig)
{
return false;
}
QStringList lines2 = strCPUConfig.split(QRegExp("[\r\n]"), QString::SkipEmptyParts);
bool bXRender = false;
bool bBlurDisable = false;
foreach (QString line, lines2) {
if("[XRender]" == line)
{
bXRender = true;
bBlurDisable = false;
}
if("[blurDisable]" == line)
{
bXRender = false;
bBlurDisable = true;
}
if (true == bXRender && strLocalCPUInfo.contains(line, Qt::CaseSensitive)) {
printf("X11StandalonePlatform::adaptCPUPerformance=======匹配\n");
//低性能CPU型号判断,如果是低性能CPU则将渲染后端设为XRender
KConfigGroup kwinConfig(KSharedConfig::openConfig("ukui-kwinrc"), "Compositing");
kwinConfig.writeEntry("Backend", "XRender");
kwinConfig.sync();
return true;
}
if (true == bBlurDisable && strLocalCPUInfo.contains(line, Qt::CaseSensitive)) {
//显卡差,开多了文件管理器就会卡,需去除毛玻璃
KConfigGroup kwinConfig(KSharedConfig::openConfig("ukui-kwinrc"), "Plugins");
kwinConfig.writeEntry("blurEnabled", "false");
kwinConfig.sync();
}
}
return false;
}
bool X11StandalonePlatform::adaptVga() const
{
QFile file;
QString strPreDefinePcieInfo;
file.setFileName(LOW_VGA_PCI_LIST);
bool bRet = file.open(QIODevice::ReadOnly | QIODevice::Text);
if(false == bRet)
{
file.close();
return false;
}
strPreDefinePcieInfo = QString(file.readAll());
file.close();
//获取预定义pcie信息
QStringList lines = strPreDefinePcieInfo.split(QRegExp("[\r\n]"), QString::SkipEmptyParts);
QMap qMapPreDefinePcieInfo;
bool ok;
foreach (QString line, lines) {
QStringList stringList = line.split(":");
qMapPreDefinePcieInfo.insert(stringList[0].toInt(&ok, 16), stringList[1].toInt(&ok, 16));
printf("X11StandalonePlatform::adaptVga===vid:%s pid:%s\n", stringList[0].toStdString().c_str(), stringList[1].toStdString().c_str());
}
//根据本机pcie信息遍历匹配预定义信息
QDir dir(PCIE_DEVICE_PATH);
if (!dir.exists())
{
return false;
}
// 遍历PCIE_DEVICE_PATH目录,获取当前PCIE设备列表
dir.setFilter(QDir::Dirs);
QStringList busList = dir.entryList();
busList.removeOne(".");
busList.removeOne("..");
foreach(QString bus, busList) {
QString path;
QFile file;
QByteArray charArray;
bool ok;
int vid;
int pid;
// 读取设备vid
path = dir.absoluteFilePath(bus + "/" + "vendor");
file.setFileName(path);
file.open(QIODevice::ReadOnly | QIODevice::Text);
charArray = file.readAll();
file.close();
vid = QString(charArray).toInt(&ok, 16);
// 读取设备pid
path = dir.absoluteFilePath(bus + "/" + "device");
file.setFileName(path);
file.open(QIODevice::ReadOnly | QIODevice::Text);
charArray = file.readAll();
file.close();
pid = QString(charArray).toInt(&ok, 16);
QMap::const_iterator iter1 = qMapPreDefinePcieInfo.find(vid);
if(iter1 != qMapPreDefinePcieInfo.end() && pid == iter1.value()) //找到,并且vid和pid完全匹配
{
printf("X11StandalonePlatform::adaptVga=======匹配\n");
//显卡vid和pid完全匹配,属于低性能显卡
KConfigGroup kwinConfig(KSharedConfig::openConfig("ukui-kwinrc"), "Compositing");
kwinConfig.writeEntry("Backend", "XRender");
kwinConfig.sync();
return true;
}
}
return false;
}
3.4 经过可以对一些CPU型号和显卡类型记录黑名单,但是无法将所有CPU型号和显卡不适配的型号记录入案。为了彻底解决上述问题,我们修改了系统启动机制,那就是在ukui-kwin在自检测的过程中,不允许其他桌面应用启动,只有在ukui-kwin自检测完以后,才允许其他桌面应用启动,我们通过跟ukui-session-manager进行交互,进行了总体的控制,代码如下:
void X11Compositor::start()
{
QDBusMessage message = QDBusMessage::createMethodCall("org.gnome.SessionManager",
"/org/gnome/SessionManager",
"org.gnome.SessionManager",
"startupfinished");
QList args;
args.append("ukui-kwin");
args.append("startupfinished");
message.setArguments(args);
if (m_suspended) {
QStringList reasons;
if (m_suspended & UserSuspend) {
reasons << QStringLiteral("Disabled by User");
}
if (m_suspended & BlockRuleSuspend) {
reasons << QStringLiteral("Disabled by Window");
}
if (m_suspended & ScriptSuspend) {
reasons << QStringLiteral("Disabled by Script");
}
qCDebug(KWIN_CORE) << "Compositing is suspended, reason:" << reasons;
qCCritical(KWIN_CORE) << "Compositing is suspended, reason:" << reasons;
QDBusConnection::sessionBus().send(message);
return;
} else if (!kwinApp()->platform()->compositingPossible()) {
qCCritical(KWIN_CORE) << "Compositing is not possible";
QDBusConnection::sessionBus().send(message);
return;
}
if (!Compositor::setupStart()) {
// Internal setup failed, abort.
QDBusConnection::sessionBus().send(message);
return;
}
QDBusConnection::sessionBus().send(message);
m_xrrRefreshRate = KWin::currentRefreshRate();
startupWithWorkspace();
}
经过不断的对ukui-kwin性能进行优化后,目前,ukui-kwin版本号>=1.0.1kylin21版本的cpu使用率比较稳定且不高。