【问题标题】:Any way to speed up/reduce CPU usage when drawing with Cairo?使用 Cairo 绘图时有什么方法可以加快/减少 CPU 使用率?
【发布时间】:2011-07-31 10:46:33
【问题描述】:

我编写了一个使用 Cairo 在屏幕上绘制东西的应用程序(准确地说是在 Gtk::DrawingArea 上)。它需要经常重绘所有内容。事实证明,尽管绘制的图形非常简单,但 X 服务器在重绘时会占用大量 CPU,并且应用程序运行速度非常慢。有什么办法可以加快这个速度吗?或者也许我不应该使用 DrawingArea 和其他一些小部件?

我绘制的是一组矩形,用户可以通过用鼠标拖动它们来移动它们。整个绘图是使用 on_expose_event 完成的,但是随着鼠标指针的移动(按下按钮),我调用 queue_draw() 来刷新绘图。

【问题讨论】:

  • 描述你在画什么以及你是如何画的可能会带来更多的insites。
  • 请添加一些数字。只是在暴露事件中的一个小计数器来测量帧速率。也许我们正在谈论许多刷新而不是缓慢的绘图。高分辨率激光鼠标可以产生很多鼠标运动事件。

标签: c++ gtk cairo gtkmm


【解决方案1】:

只需检查几件事:

你的画在暴露事件中完成了吗?

将您的图像绘制到 Cairo 表面,然后在暴露事件中简单地从该表面复制到小部件的表面。

您是否只裁剪和绘制必要的区域?

expose 事件为您提供需要重绘区域的X, Y, width, height。在开罗,在您的表面上创建一个具有这些尺寸的矩形并调用clip,这样您就不会浪费时间重绘不需要的东西。

【讨论】:

  • 编辑了问题以包含详细信息。
  • 您的第一个建议可能会奏效。我怎样才能做到这一点?我应该使用哪种表面类型,如何获取它的上下文以在其上绘制,最后如何执行快速复制?
【解决方案2】:

绘图成本高昂,尤其是文本绘图已成为 GUI 最耗费 CPU 的任务。

加快速度的唯一方法是减少绘制项目的数量。检查您是否真的只绘制必要的项目。暴露事件给你一个矩形。仅刷新小部件的这一部分。

也许在位图中缓存项目。

例如,对于平滑滚动,它可以帮助将内容绘制到一个比方说大 500 像素的位图中,这样在大多数情况下,您只需要复制图像而不需要绘制任何东西(您通常会得到暴露滚动时只有 5 到 10 像素高的矩形)。

但是您需要向我们提供更多信息,您正在绘制什么以及系统负载是什么,以获得更好的答案。

【讨论】:

  • 编辑了问题以包含详细信息。
  • 尝试使用“queue_draw_area”而不是“queue_draw”,并限制绘制区域。如果你使用 queue_draw,那么整个小部件都会被绘制出来并传输大量数据。
【解决方案3】:

我在 cairo 找到了这篇关于线程绘图的文章,以解决速度问题,也许有帮助:

http://cairographics.org/threaded_animation_with_cairo/

关于高CPU使用率:

您是否为 X 安装了适当的硬件加速驱动程序?

【讨论】:

  • 当然可以!没有任何其他应用程序会导致 X 如此多地使用 CPU。感谢您的文章,我会看看。
  • 哦,虽然这篇文章中的技巧确实很酷,我会利用它,但这并不能解决这个问题,因为它会加快速度,以防绘图复杂。我的不仅仅是几个矩形。
  • 很抱歉没有帮助
  • gdk_threads_enter/gdk_threads_leave 实现不能用于提高性能,它是 GTK 主循环的完整块,因此您永远不会得到比单个 CPU 内核更好的结果。这种线程黑客攻击的主要原因是允许从后台线程与小部件进行更轻松的通信。这种方法在 Windows 上也有很多记录在案的 GTK 问题——这就是为什么最好的建议是不要使用它。 (这就是我投反对票的原因)
  • Cairo 应该可以单独在后台线程中进行纯绘图,不幸的是,当您使用文本绘图时,使用 Pango 并且 Pango 仍然不是线程安全的(有些人在今年 5 月开始研究它 - 是的经过 10 年的无知)。
【解决方案4】:

我最终通过使用锁定标志强制使用最大 25 fps。

bool lock = 0;
bool needs_redraw = 0;

void Redraw(){
    if(lock){
        needs_redraw = 1;
        return;
    }

    //draw image to a surface

    needs_redraw = 0;
    lock = 1;
    Glib::signal_timeout().connect(Unlock, 20);

    queue_draw();
}

bool Unlock(){
    lock = 0;
    if(needs_redraw) Redraw();
    return false;
}

void on_expose_event(something){
    //copy image from surface to widget's context
}

这是一个示例代码,但这就是想法。它将不允许重绘频率高于每 20 毫秒一次。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-16
    • 2013-06-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多