简体   繁体   中英

How to animate software graphics on XCB?

I'm trying to display software-rendered graphics on XCB, but facing the following issues:

  • It doesn't animate (doesn't call EXPOSE event frequently)
  • If I pull the drawing code out of Expose event handling, it animates, but flickers like there is no Vsync. My desktop environment (GNOME, AMD Radeon) has VSync and other applications don't flicker.

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 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.

If I pull the drawing code out of Expose event handling, it animates, but flickers [...]

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. If I remove the background pixel value, then the flickering disappears here.

[...] like there is no Vsync.

There is nothing like Vsync in X11. X11 is immediate mode drawing and there is no way to tell the X11 server that you finished producing a frame.

However, you are doing the right thing here by having a CopyArea request update the window. 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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