简体   繁体   English

如何在 XCB 上为软件图形制作动画?

[英]How to animate software graphics on XCB?

I'm trying to display software-rendered graphics on XCB, but facing the following issues:我正在尝试在 XCB 上显示软件渲染的图形,但面临以下问题:

  • It doesn't animate (doesn't call EXPOSE event frequently)它没有动画(不经常调用 EXPOSE 事件)
  • If I pull the drawing code out of Expose event handling, it animates, but flickers like there is no Vsync.如果我从 Expose 事件处理中提取绘图代码,它会进行动画处理,但会像没有 Vsync 一样闪烁。 My desktop environment (GNOME, AMD Radeon) has VSync and other applications don't flicker.我的桌面环境(GNOME、AMD Radeon)有 VSync,其他应用程序不闪烁。

Here's my code:这是我的代码:

#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;
}

The code moves a small rectangle vertically.代码垂直移动一个小矩形。

How do I convert this code into a flicker-free animation?如何将此代码转换为无闪烁的动画?

It doesn't animate (doesn't call EXPOSE event frequently)它没有动画(不经常调用 EXPOSE 事件)

EXPOSE events are only send to a window when it has to redraw (for example: becomes visible or is resized to a larger size or a window that was above this window was closed).公开事件仅发送到一个窗口时,它具有重新绘制(例如:变得可见或调整为更大的尺寸,或者是高于该窗口被关闭一个窗口)。

If the content of your window changes, you have to redraw it yourself.如果您的窗口内容发生变化,您必须自己重新绘制它。 For example, you can use select() to wait for X11 events for a while and then you draw a new version of your animation.例如,您可以使用select()等待 X11 事件一段时间,然后绘制新版本的动画。

If I pull the drawing code out of Expose event handling, it animates, but flickers [...]如果我从 Expose 事件处理中提取绘图代码,它会进行动画处理,但会闪烁 [...]

After making your example code work, I see some flickering here where the window switches to black and back.在使您的示例代码工作后,我看到这里有一些闪烁,窗口切换到黑色和背面。 This happens because you set the background color of your window to black.发生这种情况是因为您将窗口的背景颜色设置为黑色。 Thus, before sending an EXPOSE event, the X11 server fills the exposed area with black.因此,在发送 EXPOSE 事件之前,X11 服务器用黑色填充暴露区域。 If I remove the background pixel value, then the flickering disappears here.如果我删除背景像素值,那么这里的闪烁就会消失。

[...] like there is no Vsync. [...] 就像没有 Vsync。

There is nothing like Vsync in X11. X11 中没有像 Vsync 那样的东西。 X11 is immediate mode drawing and there is no way to tell the X11 server that you finished producing a frame. X11 是立即模式绘图,无法告诉 X11 服务器您完成了框架的生成。

However, you are doing the right thing here by having a CopyArea request update the window.但是,通过使用 CopyArea 请求更新窗口,您在这里做的是正确的事情。 This is guaranteed to atomically update the window, but I am not sure if it is synchronised to vertical refresh or not.这保证以原子方式更新窗口,但我不确定它是否与垂直刷新同步。

By the way: Your animation just moves a rectangle down.顺便说一句:您的动画只是向下移动一个矩形。 There cannot be tearing here.这里不能有撕裂。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM