In working on my lyrics viewer project, I have come across a slight problem which I'd like to submit for consideration by the geekdom of this BBS. The problem is similar to what Richard Kirby was seeing while writing empacman, and is documented in this post. A gander at the empacman source shows that he didn't find a solution, or at least didn't implement constant FPS in v1 of empacman.

Richard's goal was to have a fixed frames per second in empacman, by having a timing loop which nanosleeps for a certain time (total frame time minus total time spent doing useful stuff). He was seeing that in reality, each iteration of the loop was taking more than he expected it to. For the lyrics scroller, it's not "frames per second" per se that I'm worried about, it's more a question of "I have one second to get this piece of text to this part of the screen before the next line draws." In my logic, no matter where the current line is, when the timestamp of the next line comes, it's going to be drawn. If the current line is behind for any reason, the next line is going to be drawn "on top" of the last one as it tries to scroll to the left. But, fundamentally, the loop algorithm Richard and I have is the same, my "frames per second" just changes with each line of lyrics.

Back when he originally asked this question, I suggested he try to use realtime scheduling. This turns out not to help out very much. It's important to realize that the sleep times we're talking about are rather small. Richard was expecting each loop iteration to take 1 second / 30 frames, or 33 milliseconds. The intervals I need for smooth (moving one pixel per frame) scrolling of the lyrics are sometimes smaller, sometimes down around 15ms. With sleep times this small, they seem to take longer than I want them to, just as Richard was seeing.

I think I may have found the reason why this is happening... Since Linux timeslices are 10ms (that's true on ARM as well, right, Mark/Hugo/Peter?) it seems logical that if I ask for a sleep time of, say, 15 ms, it's going to say "ok, fine, while you're sleeping, I'm going to schedule you and do some useful work for someone else." If that happens, it seems that the "sleep time" wouldn't necessarily be accurate. Lo and behold, the nanosleep man page documents this bug:


BUGS
The current implementation of nanosleep is based on the normal ker-
nel timer mechanism, which has a resolution of 1/HZ s (i.e, 10 ms
on Linux/i386 and 1 ms on Linux/Alpha). Therefore, nanosleep
pauses always for at least the specified time, however it can take
up to 10 ms longer than specified until the process becomes
runnable again. For the same reason, the value returned in case of
a delivered signal in *rem is usually rounded to the next larger
multiple of 1/HZ s.

As some applications require much more precise pauses (e.g., in
order to control some time-critical hardware), nanosleep is also
capable of short high-precision pauses. If the process is scheduled
under a real-time policy like SCHED_FIFO or SCHED_RR, then pauses
of up to 2 ms will be performed as busy waits with microsecond pre-
cision.

.

So that sounds like the culprit, right? Great. Now what to do about it? Going to SCHED_FIFO or SCHED_RR, as the man page suggests, is only going to help me if my sleep times are sub 2ms, where the kernel will just busy-wait instead of scheduling me. My sleep times are rarely that small, though, so even with one of those policies, I'm going to get scheduled, and the resolution of the nanosleep is far too coarse, resulting in loops which take too long, and, in my case, lyrics which are printed on top of each other on the screen.

Basically, if my analysis is right, empacman and emphatic are both falling prey to the fact that our sleep times are too big for Linux to bother giving us an accurate return, but sufficiently small that the error factor is a large enough percentage of the total loop time that it causes visible problems.

So what do I do to get around this bug, and still get smooth scrolling? The workaround is, of course, to skip two pixels instead of just one when drawing, and double the sleep time, effectively halving the frame rate. That works fine. But I want *smooth* scrolling if I can find a way to do it.

Could I just write a busy-wait loop which keeps calling gettimeofday() until the specified time has elapsed? That seems like it might work, but could starve other processes. Would a busy-wait loop with a sched_yield() inside it be any better than a nanosleep? I want this thing to be efficient, but I really need a way to delay my loop that's going to give me better than 10ms resolution to get really smooth scrolling of very fast lyrics. Anyone have any ideas?
_________________________
- Tony C
my empeg stuff