如何在 XCB 上为软件图形制作动画?
How to animate software graphics on XCB?
我正在尝试在 XCB 上显示软件渲染的图形,但面临以下问题:
- 它没有动画(不经常调用 EXPOSE 事件)
- 如果我将绘图代码从 Expose 事件处理中拉出来,它会动画化,但会闪烁,就像没有 Vsync 一样。我的桌面环境(GNOME、AMD Radeon)有 VSync,其他应用程序不闪烁。
这是我的代码:
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <xcb/xcb.h>
#include <xcb/shm.h>
#include <xcb/xcb_image.h>
int main()
{
xcb_gcontext_t gcontext;
xcb_generic_event_t *e;
uint32_t value_mask = 0;
uint32_t value_list[ 2 ];
const int width = 1200;
const int height = 675;
xcb_connection_t* c = xcb_connect( NULL, NULL );
xcb_screen_t* screen = xcb_setup_roots_iterator( xcb_get_setup( c ) ).data;
value_mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
value_list[ 0 ] = screen->black_pixel;
value_list[ 1 ] = XCB_EVENT_MASK_EXPOSURE;
xcb_window_t win = xcb_generate_id( c );
xcb_create_window( c,
XCB_COPY_FROM_PARENT,
win,
screen->root,
0, 0,
width, height,
10,
XCB_WINDOW_CLASS_INPUT_OUTPUT,
screen->root_visual,
value_mask, value_list );
value_mask = XCB_GC_FOREGROUND | XCB_GC_GRAPHICS_EXPOSURES;
value_list[ 0 ] = screen->black_pixel;
value_list[ 1 ] = 0;
gcontext = xcb_generate_id( c );
xcb_create_gc( c, gcontext, win, value_mask, value_list );
xcb_map_window( c, win );
xcb_flush( c );
xcb_shm_query_version_reply_t* reply = xcb_shm_query_version_reply(
c,
xcb_shm_query_version( c ),
NULL
);
xcb_shm_segment_info_t info;
info.shmid = shmget( IPC_PRIVATE, width * height * 4, IPC_CREAT | 0777 );
info.shmaddr = shmat( info.shmid, 0, 0 );
info.shmseg = xcb_generate_id( c );
xcb_shm_attach( c, info.shmseg, info.shmid, 0 );
shmctl( info.shmid, IPC_RMID, 0 );
uint32_t* data = info.shmaddr;
xcb_pixmap_t pix = xcb_generate_id( c );
xcb_shm_create_pixmap(
c,
pix,
win,
width, height,
screen->root_depth,
info.shmseg,
0
);
int ry = 0;
while ((e = xcb_wait_for_event( c )))
{
switch (e->response_type & ~0x80)
{
case XCB_EXPOSE:
{
for (int y = 0; y < height; ++y)
{
for (int x = 0; x < width; ++x)
{
data[ y * width + x ] = 0x0000FF00;
}
}
++ry;
if (ry > 400) ry = 0;
for (int y = ry; y < ry + 50; ++y)
{
for (int x = 0; x < 50; ++x)
{
data[ y * width + x ] = 0x00FF0000;
}
}
xcb_copy_area(
c,
pix,
win,
gcontext,
0, 0, 0, 0,
width, height
);
xcb_flush( c );
}
break;
}
default: break;
}
free( e );
}
return 0;
}
代码垂直移动一个小矩形。
如何将此代码转换为无闪烁动画?
It doesn't animate (doesn't call EXPOSE event frequently)
EXPOSE 事件仅在 必须 重绘时发送到 window(例如:变得可见或调整为更大尺寸或 window 上面 window 已关闭)。
如果你的window的内容改变了,你就得自己重绘了。例如,您可以使用 select()
等待 X11 事件一段时间,然后绘制新版本的动画。
If I pull the drawing code out of Expose event handling, it animates, but flickers [...]
在使您的示例代码运行后,我看到此处 window 切换为黑色并返回时有些闪烁。发生这种情况是因为您将 window 的背景颜色设置为黑色。因此,在发送 EXPOSE 事件之前,X11 服务器用黑色填充暴露区域。如果我删除背景像素值,那么这里的闪烁就会消失。
[...] like there is no Vsync.
X11 中没有 Vsync。 X11 是即时模式绘图,无法告诉 X11 服务器您已完成帧生成。
但是,您在这里通过让 CopyArea 请求更新 window 来做正确的事情。这保证自动更新 window,但我不确定它是否同步到垂直刷新。
顺便说一句:您的动画只是向下移动了一个矩形。这里不能有撕裂。