Linux framebuffer graphics and VSync

2020-03-24 12:43发布

I'm making a game in C that works on the linux framebuffer. So far I have a red 100x100 square that moves in tandem with the mouse, just like a pointer. I have implemented double buffering and it works correctly (the square doesn't flicker). The problem is, there is quite a lot of tearing happening horizontally. What I mean by that is, when the square moves on the x axis, it's like it is divided horizontally, and one part of it advances slithgly more than the other. This "rip" slowly propagates from top to bottom on the square if i keep moving it.

I believe it happens because the hardware reads the framebuffer during the moment when I copy the double buffer into the main buffer.

I have tried to combat the problem with the FBIO_WAITFORVSYNC ioctl, but no success.

Any ideas how I could do VSync on the framebuffer? Please add detail to your explanations as this is the first time I am programming such stuff on a Linux based OS so I might not understand otherwise.

Here is my code: http://pastebin.com/KJ4iaVEL

2条回答
做个烂人
2楼-- · 2020-03-24 13:20

Unfortunately I've learned the hard way that @Steven Bell's answer isn't quite right. Though he is right that a screen refresh could easily happen during the memcpy the proper resolution is not to create a virtual framebuffer of double the size of the screen and pan between them as so many threads suggest. Anyone who attempts this solution will receive an error described here: invalid argument error when setting yres_virtual in fb_var_screeninfo.

Per this thread: https://forum.odroid.com/viewtopic.php?f=55&t=8741 it is not quite possible to truly double buffer the framebuffer (/dev/fb0, although I have heard that the raspberry PI might be an exception to this rule). But that doesn't mean there isn't a way to double buffer in linux using low level graphics.

The real solution here is to use libdrm (/dev/dri/card0) to draw low level graphics to the screen. There is a really nice example of how to do this here: https://github.com/dvdhrm/docs/blob/master/drm-howto/modeset-vsync.c that I used myself when I was trying to solve this problem.

Anyways I hope I've saved someone the amazing headache that I had to go through to figure this out in the future.

查看更多
时光不老,我们不散
3楼-- · 2020-03-24 13:40

This isn't the correct way to do double-buffering. You're right to do all the painting on a back-buffer, but then you do a memcpy to transfer the data to the front. A screen refresh could easily happen during the copy.

To do this properly, you should only have to switch a pointer to the data; not copy the data itself. With the Linux framebuffer device, this is done by having a "virtual" screen that is twice as large as the physical screen, and using an offset variable to set whether you're showing the top or bottom half. You can query the size and set offset using the FBIOGET_VSCREENINFO, FBIOPUT_VSCREENINFO, and FBIOPAN_DISPLAY ioctl calls.

This page briefly gives some details about this: http://www.ummon.eu/Linux/API/Devices/framebuffer.html

All the relevant data structures are in the linux/fb.h header file.

查看更多
登录 后发表回答