手动控制 GTK 小部件重绘频率
Manually control GTK widget redraw tick rate
出于测试目的,我非常希望能够手动控制我的 GTK3 应用程序的帧刷新率。放慢速度很容易,我只是睡在渲染函数中。但是,我可以达到的最大帧率是我的显示器的 60Hz 刷新率。
我发现我可以使用 gdk_threads_add_idle
在主循环中添加一个无上限的更新槽率。
有什么方法可以根据要求强制重新渲染 "immediate" 吗?我试过手动调用渲染槽,这确实会导致立即调用重新渲染函数,但它根本不会更新屏幕。
void update_auto(gpointer user_data)
{
// ... do some update stuff
// somehow get the appropriate GtkGLArea from user_data
GtkGLArea* area = get_area(user_data);
gtk_gl_area_queue_render(area);
// capped at 60Hz
}
void update_manual(gpointer user_data)
{
// ... do some update stuff
// somehow get the appropriate GtkGLArea from user_data
GtkGLArea* area = get_area(user_data);
gtk_gl_area_make_current(area);
render(area,gtk_gl_area_get_context(area), user_data);
// even though render was called, screen never updates
}
void render(GtkGLArea* area, GdkGLContext* context, gpointer user_data)
{
// ... render
}
void setup_widget()
{
// ... some init stuff
// only one of these is used at a time
#if TEST_FLAG == 0
gdk_threads_add_idle(update_manual, user_data);
#elif TEST_FLAG == 1
gdk_threads_add_idle(update_auto, user_data);
#elif TEST_FLAG == 2
gtk_widget_add_tick_callback((GtkWidget*) area, update_auto, user_data, NULL);
#else
gtk_widget_add_tick_callback((GtkWidget*) area, update_manual, user_data, NULL);
#endif
}
我发现了一个强制立即执行 re-draw:
GtkGLArea
扩展了 GtkWidget
class,因此我可以立即使用绘制函数 re-draw.
// inside update
// get the proper clipping so we don't draw over other widgets
GtkAllocation reg;
gtk_widget_get_allocation((GtkWidget*)area, ®);
cairo_region_t *creg = cairo_region_create_rectangle(®);
cairo_t *cr = gdk_cairo_create(gtk_widget_get_window((GtkWidget*)area));
gdk_cairo_region(cr, creg);
cairo_clip(cr);
gtk_widget_draw((GtkWidget*)area, cr);
cairo_destroy(cr);
cairo_region_destroy(creg);
然后我可以根据需要使用 gdk_threads_add_idle
函数,它会尽快调用更新函数。
即使不使用 gdk_threads_add_idle
,我也注意到即使将此方法与 gtk_widget_add_tick_callback
一起使用,帧速率也明显更加一致。在我注意到帧速率偶尔在 15/30/60 fps 之间跳跃之前,似乎表明它缺少一两个重绘更新周期。这个问题现在已经完全消失了,我得到了一个接近恒定的 N fps,其中对于小场景,N 上限为 60。
我还没有 运行 遇到任何问题,但这样做似乎适用于 windows,其中包含多个 GtkWidget
。我没有尝试重叠的小部件,但我怀疑这里唯一的解决方案是重绘任何重叠的小部件。
出于测试目的,我非常希望能够手动控制我的 GTK3 应用程序的帧刷新率。放慢速度很容易,我只是睡在渲染函数中。但是,我可以达到的最大帧率是我的显示器的 60Hz 刷新率。
我发现我可以使用 gdk_threads_add_idle
在主循环中添加一个无上限的更新槽率。
有什么方法可以根据要求强制重新渲染 "immediate" 吗?我试过手动调用渲染槽,这确实会导致立即调用重新渲染函数,但它根本不会更新屏幕。
void update_auto(gpointer user_data)
{
// ... do some update stuff
// somehow get the appropriate GtkGLArea from user_data
GtkGLArea* area = get_area(user_data);
gtk_gl_area_queue_render(area);
// capped at 60Hz
}
void update_manual(gpointer user_data)
{
// ... do some update stuff
// somehow get the appropriate GtkGLArea from user_data
GtkGLArea* area = get_area(user_data);
gtk_gl_area_make_current(area);
render(area,gtk_gl_area_get_context(area), user_data);
// even though render was called, screen never updates
}
void render(GtkGLArea* area, GdkGLContext* context, gpointer user_data)
{
// ... render
}
void setup_widget()
{
// ... some init stuff
// only one of these is used at a time
#if TEST_FLAG == 0
gdk_threads_add_idle(update_manual, user_data);
#elif TEST_FLAG == 1
gdk_threads_add_idle(update_auto, user_data);
#elif TEST_FLAG == 2
gtk_widget_add_tick_callback((GtkWidget*) area, update_auto, user_data, NULL);
#else
gtk_widget_add_tick_callback((GtkWidget*) area, update_manual, user_data, NULL);
#endif
}
我发现了一个强制立即执行 re-draw:
GtkGLArea
扩展了 GtkWidget
class,因此我可以立即使用绘制函数 re-draw.
// inside update
// get the proper clipping so we don't draw over other widgets
GtkAllocation reg;
gtk_widget_get_allocation((GtkWidget*)area, ®);
cairo_region_t *creg = cairo_region_create_rectangle(®);
cairo_t *cr = gdk_cairo_create(gtk_widget_get_window((GtkWidget*)area));
gdk_cairo_region(cr, creg);
cairo_clip(cr);
gtk_widget_draw((GtkWidget*)area, cr);
cairo_destroy(cr);
cairo_region_destroy(creg);
然后我可以根据需要使用 gdk_threads_add_idle
函数,它会尽快调用更新函数。
即使不使用 gdk_threads_add_idle
,我也注意到即使将此方法与 gtk_widget_add_tick_callback
一起使用,帧速率也明显更加一致。在我注意到帧速率偶尔在 15/30/60 fps 之间跳跃之前,似乎表明它缺少一两个重绘更新周期。这个问题现在已经完全消失了,我得到了一个接近恒定的 N fps,其中对于小场景,N 上限为 60。
我还没有 运行 遇到任何问题,但这样做似乎适用于 windows,其中包含多个 GtkWidget
。我没有尝试重叠的小部件,但我怀疑这里唯一的解决方案是重绘任何重叠的小部件。