Friday, 14 March 2014

Low-level Graphics on Raspberry Pi (part X)

Now that we have been gradually building the example program to allow us to do something interesting - how about trying a bit of animation?
...
// helper function to draw a rectangle in given color
void fill_rect(int x, int y, int w, int h, int c) {
    int cx, cy;
    for (cy = 0; cy < h; cy++) {
        for (cx = 0; cx < w; cx++) {
            put_pixel(x + cx, y + cy, c);
        }
    }
}

// helper function to clear the screen - fill whole 
// screen with given color
void clear_screen(int c) {
    memset(fbp, c, vinfo.xres * vinfo.yres);
}

void draw() {

    int i, x, y, w, h, dx, dy;

    // start position (upper left)
    x = 0;
    y = 0;
    // rectangle dimensions
    w = vinfo.yres / 10;
    h = w;
    // move step 'size'
    dx = 1;
    dy = 1;

    int fps = 100;
    int secs = 10;
    
    // loop for a while
    for (i = 0; i < (fps * secs); i++) {

        // clear the previous image (= fill entire screen)
        clear_screen(8);
        
        // draw the bouncing rectangle
        fill_rect(x, y, w, h, 15);

        // move the rectangle
        x = x + dx;
        y = y + dy;

        // check for display sides
        if ((x < 0) || (x > (vinfo.xres - w))) {
            dx = -dx; // reverse direction
            x = x + 2 * dx; // counteract the move already done above
        }
        // same for vertical dir
        if ((y < 0) || (y > (vinfo.yres - h))) {
            dy = -dy;
            y = y + 2 * dy;
        }
        
        usleep(1000000 / fps);
        // to be exact, would need to time the above and subtract...
    }

}
...

Save as fbtestX.c (complete code in GitHub) - build with make fbtestX. This should give us a moving white rectangle that bounces off the screen sides... Unfortunately the updates are not smooth (at least on most displays) - there is a quite prominent tearing effect.

Linux framebuffer interface does define some methods to overcome this - we could make the framebuffer virtual size double the height of the (smaller) physical one using the FBIOPUT_VSCREENINFO call:
  // Set variable info
  vinfo.xres = 640; // try a smaller resolution
  vinfo.yres = 480;
  vinfo.xres_virtual = 640;
  vinfo.yres_virtual = 960; // double the physical
  vinfo.bits_per_pixel = 8;
  if (ioctl(fbfd, FBIOPUT_VSCREENINFO, &vinfo)) {
    printf("Error setting variable information.\n");
  }

  //long int screensize = vinfo.xres * vinfo.yres;
  // have to use the virtual size for the mmap...
  long int screensize = vinfo.xres_virtual * vinfo.yres_virtual;

And change our drawing loop to use the two halves of the virtual buffer using a call to FBIOPAN_DISPLAY for page-flipping tied to a vertical sync using FB_ACTIVATE_VBL:
    int vs = 0;
    // initially show upper half (0) - draw to lower half (1)
    int cur_half = 1;
    for (i = 0; i < 1000; i++) {

        fill_rect(x, y, 40, 40, 4);

        x = x + dx;
        y = y + dy;

        if ((x < 0) || (x > (vinfo.xres - 40)) {
            dx = -dx;
            x = x + 2 * dx;
        }
        if ((y < 0) || (y > (vinfo.yres - 40)) {
            dy = -dy;
            y = y + 2 * dy;
        }

        // switch page
        vinfo.yoffset = cur_page * vinfo.yres;
        vinfo.activate = FB_ACTIVATE_VBL;
        if (ioctl(fbfd, FBIOPAN_DISPLAY, &vinfo)) {
            printf("Error panning display.\n");
        }
    }

Unfortunately these calls have not been implemented in the RPi framebuffer driver (does not seem to be in later versions either yet, see also http://www.raspberrypi.org/phpBB3/viewtopic.php?f=67&t=19073). So running the above code (full code) results in repeated output of Error panning display. and the rectangle flashing even worse (as we miss every second screen update by drawing outside of the visible area).

[Continued in next part]

[UPDATE on changes in the fb driver here]

1 comment:

  1. Switching the ioctl(fbfd, FBIOPAN_DISPLAY, &vinfo) to ioctl(fbfd, FBIOPUT_VSCREENINFO, &vinfo) seems to work slightly better - it does flip the page to the yoffset, but unfortunately it also redraws the console buffer, so one may end up with flashing directory listing or whatever was on display when starting the tester app...

    ReplyDelete

Note: only a member of this blog may post a comment.