【问题标题】:GTK3 and cairo too slowGTK3和cairo太慢了
【发布时间】:2018-07-21 08:45:03
【问题描述】:

我用 GTK3 和 cairo 制作了一个非常简单的动画,但对于这个简单的图形来说它太慢了。我不明白为什么它这么慢。我尝试使用 gtk_widget_queue_draw_area,但结果是一样的,没有任何变化。 谁能解释一下,为什么这么慢,我该如何解决?

这是程序:

#include <gtk/gtk.h>
#include <cairo.h>

void draw(GtkWidget* widget, cairo_t* cr)
{
    static int width, height,
               posX = 0,
               vX = 1;

    GtkWidget* window = gtk_widget_get_toplevel(widget);
    gtk_window_get_size(GTK_WINDOW(window), &width, &height);

    cairo_set_source_rgb(cr, 0, 0, 0);
    cairo_set_line_width(cr, 1);

    cairo_rectangle(cr, posX, height/2, 1, 1);
    cairo_stroke(cr);

    if(posX + vX >= width || posX + vX == 0)
        vX = -vX;
    posX += vX;

    gtk_widget_queue_draw(window);
}

int main(int argc, char** argv)
{
    GtkWidget* window;
    GtkWidget* darea;

    gtk_init(&argc, &argv);

    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    darea = gtk_drawing_area_new();

    gtk_container_add(GTK_CONTAINER(window), darea);

    gtk_window_set_default_size(GTK_WINDOW(window), 500, 400);

    g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), NULL);
    g_signal_connect(G_OBJECT(darea), "draw", G_CALLBACK(draw), NULL);

    gtk_widget_show_all(window);
    gtk_main();
}

【问题讨论】:

  • 也许gtk_queue_drawdraw 信号回调中被有效地忽略了。尝试从超时运行它,在调用之间有一小段时间。我怀疑队列绘制被精确地忽略以避免错误导致意外的“动画”。 draw 回调应该实际进行绘图,而不是推迟到以后,所以在它中间调用 gtk_queue_draw 听起来像是一个不正确的实现。
  • 您的代码没有问题。我使用秒表测量了在窗口上移动的点为 8.3 秒。使用 60 Hz 的刷新率,每帧移动 1 个像素的点和 500 个像素的窗口宽度计算出需要 8.3333 秒。所以,一切正常。要提高速度,您需要增加每帧移动的距离。在您的代码中,这将增加 vX 的值。

标签: c gtk3 cairo


【解决方案1】:

您应该使用计时器,例如g_timeout_add。您可以向g_timeout_add 注册您的新函数redraw,每0.05 秒(例如每50 毫秒)调用一次,并且redraw 不仅应该绘制东西,还应该返回TRUE 以在50 毫秒后重新启动。你会发现这样 50 毫秒的延迟可能太小了,你肯定想增加它。

(请注意,您的程序仅占用 3% 的 CPU 时间,在 Intel i5-4690S 上的 Linux/Debian/Sid/x86-64 上使用 time(1)... 测量,所以它不会太慢;计算机花费 97% 的时间等待!还请记住,人眼在大多数情况下看的速度不会超过 30 到 60 Hz)

您的程序中没有动画代码(因为任何动画都应该定期运行)。 event loop(在gtk_main 中)仅在需要时调用。您可能只想部分重绘您的窗口。

你的方法是错误的。您不想用gtk_widget_queue_draw 重复绘图,而是希望它定期再次发生(例如,从用g_timeout_add 注册的新redraw 例程调用gtk_widget_queue_draw)。您将通过实验调整该周期。

您也可以使用gtk_widget_add_tick_callback(由 c-smile 评论)。我认为您不需要它(因为我猜您的动画可能会运行得太快)。

研究一些GTK examples 的源代码(特别是clock 示例)。另见thisthat。查看custom drawing 示例,并查看aclock 的源代码。

(您的程序并不算太慢,但是您的draw 调用频率可能不够高;没有代码可以重复它;您应该use the gdb debugger 并使用传递给@ 的-Wall -g 编译您的代码987654344@ - 除了pkg-config提供的其他标志)

【讨论】:

  • "你的程序中没有动画代码",查看gtk_widget_queue_draw(window);
  • 但是没有任何东西重复绘制。你需要使用计时器,如果你想要一些动画,你不能避免使用它们。电脑很快!
  • gtk_widget_queue_draw() -> gdk_window_invalidate_region() -> 通过在小部件的窗口及其所有子窗口上调用 gdk_window_invalidate_region() 使区域定义的小部件区域无效。一旦主循环空闲(大致处理完当前批次的事件之后),窗口将接收所有已失效区域联合的公开事件。
  • 不,窗口不会收到很多暴露事件。这些由您的 X11 或 Wayland 服务器提供,如果您没有更多 other 窗口(在您的上方和周围),您将不会获得公开事件。如果你想要一些动画,你应该有一个超时。在调试器中运行您的代码,在您的 draw 例程上放置一个断点:您会惊讶于它是多么很少被调用。
  • 但是您不在 Windows 上,但可能在 Linux 上。同样,使用调试器
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-09-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-10
  • 2014-06-07
  • 2016-05-31
相关资源
最近更新 更多