hi Havoc, First I want to note that GPeriodic was only an attempt to get the timing right. It's just a clock. It is not in any way meant to provide policy about how a paint cycle is actually run.
That said, I did make a Gtk branch with some crackful experimentation (currently shoving GPeriodic into gdkthreads in a global way). This is not meant to be "the way" -- it was just a convenient place to stick it for now so that we could experiment with getting some widgets animating using it. Of course, we're discovering that resize handling and stuff is quite difficult (another mail for all that stuff). It's worth noting, though, that Emmanuele was able to get Clutter's paint cycle working on top of it without modification, so there is something here... Anyway. GPeriodic is just a clock, so let's talk timing. On Thu, 2010-10-21 at 03:09 -0400, Havoc Pennington wrote: > Another thought, in the patch > periodic->last_run = now; > I think this will look a little rocky - the frames are going to > display at actual-screen-hz intervals, no matter what time it is when > you record last_run and no matter what time it is when you call your > drawing APIs. So things look better if you keep the "tween timestamp" > on hz intervals. The last_run time probably has very little to do with > when frames hit the display. Animations should go ahead and paint > assuming they are hitting the display at a fixed fps. This is something that gave me a great deal of pause, and is a rather interesting question. I'll attach the full code fragment in its current form, for context: /* Update the last_run. * * In the normal case we want to add exactly 1 tick to it. This * ensures that the clock runs at the proper rate in the normal case * (ie: the dispatch overhead time is not lost). * * If more than one tick has elapsed, we set it equal to the current * time. This has two purposes: * * - sets last_run to something reasonable if the clock is running * for the first time or after a long period of inactivity * * - resets our stride if we missed a frame */ now = g_periodic_get_microticks (periodic); elapsed_ticks = (now - periodic->last_run) / 1000000; g_assert (elapsed_ticks > 0); if G_LIKELY (elapsed_ticks == 1) periodic->last_run += 1000000; else periodic->last_run = now; [[and yes, I'm using G_LIKELY strictly for documentation purposes]] In the usual case, it's true that the ticker (which is expressed in microframes, by the way) is advanced exactly one frame when dispatching from the main context (ie: free-running clock with no external synch information from vblank). The only place I disagree with you is on what to do when we want to skip a frame. > In the litl shell fwiw the pseudocode for the tween time on each frame is: > > int frame_time = 1000 / fps; > int actual_time = <time since start of animation> - current_ticker_time; > int frames_late = (actual_time / frame_time) - 1; > current_ticker_time += frame_time; > if (frames_late > 0) { > current_ticker_time += (frame_time * (frames_late + 1)); > } > > The idea of this is: decide to drop frames based on floor(frames_late) > and then skip ahead by ceil(frames_late). The point of that is to bias > against dropping a frame until we're a full frame behind, but then be > sure we drop enough frames to get ahead a bit when we do drop them, > and always stay on a multiple of the refresh rate. This is an interesting proposal. The problem when the clock is free-running is that we don't know exactly what side of the vblank we're on. That's the point of resetting the stride (ie: assuming that the new top-of-the-frame time is now). That might end up being right, or it might be wrong. In the event that we are late for only one frame, it's a cointoss. On the other hand, if we are consistently dropping frames -and- are unaware of the vblank, I think I could mount a statistical argument that my approach is more likely to result in a more smooth/accurate animation (say, RMS of correct position vs. actual position for each frame that actually hits the monitor). Far more interesting, I think is in unblock(): periodic->last_run = g_periodic_get_microticks (periodic); In the event that we *do* have vblank information, we set the counter to exactly the wallclock time at some semi-random interval (namely: whenever our process bothered to notice the notification from the X server). I'm actively unhappy with that. I was talking with Emmanuele about the information that's in the vblank notification we get from the server. There is timestamp information there. I'd be quite a lot happier if we had a method to inject that information into GPeriodic (ie: a timestamp parameter on the unblock API). > Due to this and also the desire to not explode when the computer's > clock is set, I would define the ticker to be a monotonic value that > is in time units but is not a wall clock time. i.e. if I change my > computer's clock back an hour, the ticker should keep marching > forward, and the ticker is allowed to be fudged to make animations > pretty. I think we have some agreement that we should move to using the kernel monotonic time instead of gettimeofday(). The fact that GMainContext already mixes these two quite a lot (ie: using gettimeofday() to make calculations of how many milliseconds to give to poll()) has sort of pre-muddied the water there, so I'm currently just wading into that existing mess. No point in making it worse, however. Cheers _______________________________________________ gtk-devel-list mailing list gtk-devel-list@gnome.org http://mail.gnome.org/mailman/listinfo/gtk-devel-list