NOTE 2015-01-27: On later Raspbian builds this approach may lead to a lock up! It is adviced to use the 'pure fb' approach - code available in this post.
In previous part we tried a simple animation - with not so perfect results...
Obviously with so many different avenues to tackle for the Raspberry Pi driver developers it is not possible for us users to get everything 'for free' and at instant. I sure wish I had the time and/or the drive to attempt extending the fb driver myself ...or maybe the legendary 'Somebody Else' could do it ;) As the thing is, it does not seem that big of a job: there is already support for the 'page flipping' using double size buffer and panning the display in the Raspberry Pi firmware Mailbox interface.
In the meantime, we could take a stab at trying this out. Raspberry Pi forum users hacking on the 'bare metal' (thanks guys) pointed me before to look at the arch/arm/mach-bcm2708/include/mach/vcio.h in the RPi firmware GitHub sources and to the way to talk to the mailbox:
From the "vcio.h" we are using the two defines: DEVICE_FILE_NAME "char_dev" and . This char_dev is a special file for communicating with the mailbox. The file must be created using the command mknod (see man):
Save the code as say fbtestXII.c (full source in GitHub), download the vcio.h file to the same directory, build with:
(as the code uses the clock functions from librt) and run with ./fbtestXII. This should display the same gliding and bouncing rectangle, but this time with no tearing and with minimal flicker.
... #include "vcio.h" ... int mboxfd = 0; ... // helper function to talk to the mailbox interface static int mbox_property(void *buf) { if (mboxfd < -1) return -1; int ret_val = ioctl(mboxfd, IOCTL_MBOX_PROPERTY, buf); if (ret_val < 0) { printf("ioctl_set_msg failed:%d\n", ret_val); } return ret_val; } // helper function to set the framebuffer virtual offset == pan static unsigned set_fb_voffs(unsigned *x, unsigned *y) { int i=0; unsigned p[32]; p[i++] = 0; // size p[i++] = 0x00000000; // process request p[i++] = 0x00048009; // get physical (display) width/height p[i++] = 0x00000008; // buffer size p[i++] = 0x00000000; // request size p[i++] = *x; // value buffer p[i++] = *y; // value buffer 2 p[i++] = 0x00000000; // end tag p[0] = i*sizeof *p; // actual size mbox_property(p); *x = p[5]; *y = p[6]; return p[1]; } ... void draw() { ... // switch page /* vinfo.yoffset = cur_page * vinfo.yres; vinfo.activate = FB_ACTIVATE_VBL; if (ioctl(fbfd, FBIOPAN_DISPLAY, &vinfo)) { printf("Error panning display.\n"); } */ vx = 0; vy = cur_page * vinfo.yres; set_fb_voffs(&vx, &vy); //usleep(1000000 / fps); } ... // main // open a char device file used for communicating with kernel mbox driver mboxfd = open(DEVICE_FILE_NAME, 0); if (mboxfd < 0) { printf("Can't open device file: %s\n", DEVICE_FILE_NAME); printf("Try creating a device file with: mknod %s c %d 0\n", DEVICE_FILE_NAME, MAJOR_NUM); } ...
$ sudo mknod char_dev c 100 0
gcc -lrt -o fbtestXII fbtestXII.c
The program outputs the timing info - the (most likely) 16 seconds (and some 700 ms) comes from the fps = 100 and secs = 10 ...it is quite obvious that since the screen refresh is now tied to the vertical sync, we 'only' get 60 fps and 100 * 10 = 1000 loops takes 1000 / 60 = 16.6 s.
Now if we change the code a bit:
(full source)...and build with (optimisation on):
...we should get 200 colorful, bouncing 'sprites' going all over the screen:
Using the "char_dev" (especially as it has to be created as root) is not the most elegant way, but so far the only solution I know (if we want to stick to the fb) and at least for some uses this may be quite enough.
... #define NUM_ELEMS 200 int xs[NUM_ELEMS]; int ys[NUM_ELEMS]; int dxs[NUM_ELEMS]; int dys[NUM_ELEMS]; ... void draw() { int i, x, y, w, h, dx, dy; struct timespec pt; struct timespec ct; struct timespec df; // rectangle dimensions w = vinfo.yres / 10; h = w; // start position (upper left) x = 0; y = 0; int n; for (n = 0; n < NUM_ELEMS; n++) { int ex = rand() % (vinfo.xres - w); int ey = rand() % (vinfo.yres - h); //printf("%d: %d,%d\n", n, ex, ey); xs[n] = ex; ys[n] = ey; int edx = (rand() % 10) + 1; int edy = (rand() % 10) + 1; dxs[n] = edx; dys[n] = edy; } // move step 'size' dx = 1; dy = 1; int fps = 60; int secs = 10; int vx, vy; clock_gettime(CLOCK_REALTIME, &pt); // loop for a while for (i = 0; i < (fps * secs); i++) { // change page to draw to (between 0 and 1) cur_page = (cur_page + 1) % 2; // clear the previous image (= fill entire screen) clear_screen(0); for (n = 0; n < NUM_ELEMS; n++) { x = xs[n]; y = ys[n]; dx = dxs[n]; dy = dys[n]; // draw the bouncing rectangle fill_rect(x, y, w, h, (n % 15) + 1); // 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; } xs[n] = x; ys[n] = y; dxs[n] = dx; dys[n] = dy; } // switch page vx = 0; vy = cur_page * vinfo.yres; set_fb_voffs(&vx, &vy); } clock_gettime(CLOCK_REALTIME, &ct); df = timediff(pt, ct); printf("done in %ld s %5ld ms\n", df.tv_sec, df.tv_nsec / 1000000); } ...
gcc -O2 -lrt -o fbtestXIII fbtestXIII.c
[Continued in part X+2>
No comments:
Post a Comment
Note: only a member of this blog may post a comment.