在 QML 中旋转图像时减少 CPU 使用率

Posted

技术标签:

【中文标题】在 QML 中旋转图像时减少 CPU 使用率【英文标题】:Decrease CPU usage while rotating image in QML 【发布时间】:2014-02-03 15:53:30 【问题描述】:

我创建了一个基于Qt 5.2 的 QML 应用程序,并将其部署在 Mac OS X 和 Windows(从 XP 到 8)上。我的应用程序有一个主动画,可以在用户直播时无限期地旋转图像。此动画是应用程序的核心部分,我无法删除它。

旋转图像会占用过多的 CPU 资源,我正在寻找解决此问题的方法。其背后的原因是 很多 用户不支持 OpenGL 2(在 Windows 上),我将不得不依靠 MESA 的 DLL 在软件中进行渲染,这使得应用程序在这些机器上无法使用,而动画正在运行。

这是我当前在 QML 中的动画实现:

Image 
  id: imgBroadcastState
  source: "images/broadcast_button.png"
  anchors.horizontalCenter: parent.horizontalCenter

  NumberAnimation on rotation 
    from: 0
    to: 360
    running: rootWindow.isBroadcasting
    loops: Animation.Infinite
    duration: 7000

    onRunningChanged:
      if(!running) 
        imgBroadcastState.rotation = 0;
      
    
  

我尝试的第一个解决方案是创建一个巨大的精灵,其中包含我的动画的所有帧,然后我使用AnimatedSprite 加载该精灵。这减少了 CPU,但显然不够,而且 RAM 消耗增加了三倍多,高达 300MB,这对于旧的 Windows XP 机器来说不是一个好的解决方案。

我还尝试对QQuickPaintedItem 进行子类化,并每 30 毫秒手动调用一次paint()(使用QTimer)来旋转图像。这也会减少 CPU,但也不够。这是我使用的代码:

void MXPaintedItem::paint(QPainter *painter)

  QTransform rot;
  rot.rotate(m_angle);
  painter->setRenderHint(QPainter::Antialiasing);
  painter->setRenderHint(QPainter::SmoothPixmapTransform);
  painter->translate(width() / 2, height() / 2);
  painter->rotate(m_angle);
  // Use preloaded QImage
  painter->drawImage(QPoint(-width() / 2, -height() / 2), m_image);


  m_angle += 2.5;

有什么方法可以改善这一点并减少旋转动画的 CPU 使用率?

编辑

当然,我可以更改动画或不使用动画,但这不是一个长期的解决方案。最后,必须动态绘制/更新此图像以反映 VUMeter 并表示声级。所以我需要找到一个合适的解决方案,让我能够实时更新 QML 视图的一部分,而不必重新绘制整个 UI 并占用大量 CPU。

EDIT2: 我发现主要的 CPU 使用率不是旋转本身,而是它每次都必须重绘整个 UI。您可以通过在paint() 函数中调用return 而不是旋转任何东西来确保这一点。与动画图像本身相比,这样做的 CPU 使用率是相同的,这表明问题来自于每次更新场景中的 QML 组件之一时更新整个 UI

【问题讨论】:

你为什么使用 Mesa?你有lookedANGLE吗? 是的,我目前正在使用 ANGLE,但它在 XP 上不受支持,然后只能在我的一半客户端机器上运行,即使使用 ANGLE,在其他 Windows 上启动应用程序时,我也会得到一些用户的空白帧机器 你需要一个自定义的QML 组件。它将提前生成所有需要的旋转,将它们存储在内存中,您只需在需要时播放它们,也许可以利用对称性...... 哇,那一定是一些旧机器。好吧,QtQuick 2.0 似乎不适合你。不知道你是否尝试使用 QtQuick 1.0(它可以作为插件使用),如果它仍然像在 Qt4.8 中一样工作,它应该在 CPU 上运行得比 Mesa 上的 OpenGL 更快。 Arpegius > 在 QtQuick 2.0/QtQuick.Controls 开发 6 个月后回到 QtQuick 1.0 是我想避免的。 【参考方案1】:

如果您有内存空间并且旋转次数很少,则每次旋转使用一个位图。自计算机诞生以来,这就是“微调器”的编写方式。

理论上,bitblit 比执行旋转变换,然后对结果图像进行 blitting 更快。

【讨论】:

这与创建一个精灵并在我已经尝试过的AnimatedSprite 中加载它不一样吗?部分问题是完整的旋转需要 7 秒,我需要至少 30 fps(在你真的看到它滞后)这导致同一个 sprite 中有 210 帧,因此使用至少 250MB 的额外内存但仍然过多的 CPU。部分问题是每次帧发生变化时都需要重新绘制整个场景,这会花费太多。 @koopajah 你不可能需要那么多帧。让我们谈谈一个简单的例子,比如“进度轮”(google it)。您应该能够创建一个具有少量帧数的类似动画,可能是 12-ish,也可能会更多,具体取决于复杂性。 @koopajah 或者为什么不探索其他选项来解决您的问题?一个亮红色的大按钮在播出吗?这在广播室外显示是相当标准的,为什么不在你的 UI 上显示呢?或者可能只是一个闪烁的红色动画,类似于录音机的显示方式?这两者都会为您大大简化事情。 因为动画是我们应用程序/品牌的核心部分,仅仅因为它在计算机上占用太多 CPU 就将其删除似乎有点疯狂。对于帧数,我看不到如何旋转只有 12 帧的图像而不会滞后,除非它在一秒钟内旋转超过一次,当然这不是我的情况。此外,这不是我们计划在应用程序中添加的唯一动画,因此我们正在寻找一个长期的解决方案,而不仅仅是针对这个的 hack。 @koopajah,你是对的。对于建议(用于每秒动画某些帧,例如 12fps),您可以在 onTrigger 事件处理程序中使用 Timer 并将图像的 rotation 属性增加 30(将 360 除以 12)。它将显着降低 CPU 使用率。【参考方案2】:

接下来我会尝试使用您的动画并制作动画 GIF。无论您使用什么工具来生成 gif,都应该能够配置压缩/优化以获得您满意的质量/大小。

然后尝试使用 QML AnimatedImage Element 显示 GIF,也许会提供更好的结果。

【讨论】:

我尝试过的第一个解决方案之一,但这需要与 NumberAnimation 一样多的 CPU【参考方案3】:

在进一步调查之后,我得出的结论是,没有简单的方法可以优化它。通过 QML 本身可以获得最佳性能,因为它针对缓存元素和改进场景重绘进行了优化。

主要的解决方案是仅重新绘制场景的一部分。这在 QML 和新的 SceneGraph 渲染中尚无法实现,并且在不久的将来也没有计划。这个答案是Qt中负责该功能开发的人给我的。

CPU 使用率本身高度依赖于硬件和显卡驱动程序,因为现在一切都基于 OpenGL 2.0,并且依赖于您的硬件/驱动程序正确支持某些 OpenGL 操作这一事实。

【讨论】:

以上是关于在 QML 中旋转图像时减少 CPU 使用率的主要内容,如果未能解决你的问题,请参考以下文章

我可以阻止静止的 SVG 图像在 QML 中使用过多的 CPU 资源吗?

画布使用大量 CPU

如何使用qml qt3d(qt)将对象旋转一个角度?

如何在运行时生成的 QML 中加载图像(jpg)

如何使用 QML 创建全局图像?

如何从 QML 旋转框中获取数据