[plt-scheme] FrTime implementation
Hi Greg,
I've had a go at implementing this, and it seems to hang inside
(do-in-manager-after (channel-put frtime-chan #t))
Presumably because the channel-put is being run straight away and
blocking rather than being deferred... I looked at the macro but got a
bit lost :)
My code is still very trivial (mostly copy pasted from your email):
(module frtest mzscheme
(require
(lib "lang-ext.ss" "frtime")
(all-except (lib "frp-core.ss" "frtime") undefined?))
(provide
set-scene
clock)
(define frtime-chan (make-channel))
(define fluxus-pulse (event-receiver))
(define scene-list '())
(define (set-scene s)
(set! scene-list s))
(define clock
(hold
(map-e
(lambda (e)
(current-milliseconds))
fluxus-pulse)))
(define (draw-a-frame lst)
(for-each
(lambda (b)
(display (value-now b))(newline))
lst))
(define (loop)
;; emit the next pulse
(send-event fluxus-pulse #t)
;; have frtime send a message when it's done updating everything
(do-in-manager-after (channel-put frtime-chan #t))
;; wait for the message from frtime
(channel-get frtime-chan)
;; draw the frame from a consistent state
(draw-a-frame scene-list)
(loop))
(thread (loop))
)
(require frtest)
(set-scene (list clock))
On Sun, 2007-12-09 at 06:43 -0500, Gregory Cooper wrote:
> Ah. Now I think I understand the problem a bit better. It sounds
> like what you'll want is a mechanism for synchronizing FrTime and the
> Fluxus render loop. Here's how I'd recommend doing it:
>
> 1. Define an event stream called something like fluxus-pulse:
>
> (define fluxus-pulse (event-receiver))
>
> 2. Define your own versions of the primitive input behaviors you
> need, which only update on these pulses. E.g.:
>
> (define clock (hold (map-e (lambda (e) (current-milliseconds)) fluxus-pulse)))
>
> 3. Each time through the render loop, emit a pulse, then wait for
> FrTime to stabilize, and finally draw. Here's a rough sketch:
>
> (define frtime-chan (make-channel))
>
> (let loop ()
> ;; emit the next pulse
> (send-event fluxus-pulse #t)
> ;; have frtime send a message when it's done updating everything
> (do-in-manager-after (channel-put frtime-chan #t))
> ;; wait for the message from frtime
> (channel-get frtime-chan)
> ;; draw the frame from a consistent state
> (draw-a-frame <reactive scene description data structure>)
> (loop))
>
> 4. Note that with this approach, there is no lifting at all
> (regardless of DrScheme version). Instead, as you traverse the
> reactive structure, you'll just test whether each value you encounter
> is a behavior, and if so, you'll call "value-now" on it before
> proceeding.
>
> Let me know whether this helps or not.
>
> Cheers,
> Greg
>
> On Dec 9, 2007 5:57 AM, Dave Griffiths <dave at pawfal.org> wrote:
> >
> > On Sat, 2007-12-08 at 22:52 -0500, Gregory Cooper wrote:
> > > Hi Dave,
> > >
> > > It looks like you have the right idea. :-)
> > >
> > > Are you running a pre-v371 version of DrScheme / FrTime? (From the
> > > code and description you give, it sounds like you are...) I ask
> > > because around July I changed the way FrTime deals with lists and
> > > other data structures, and this change makes it a bit more complicated
> > > to lift over data structures containing behaviors.
> > >
> > > In pre-v371, all data constructors (including "cons") are lifted, so
> > > any reactivity inside a data structure spreads to the structure
> > > itself. When you lift a procedure over a reactive structure, the
> > > procedure gets called repeatedly with snapshots of the structure
> > > (computed each time anything inside the structure changes). This is
> > > why the second (make-torus ...) gets redrawn every millisecond, even
> > > though it only changes once a second: draw-list processes the entire
> > > list whenever anything inside it changes.
> > >
> > > In v371 and later, data constructors are not lifted, so behaviors can
> > > hide inside structures. In order to lift over such structures, you'll
> > > first need to use "raise-reactivity" to make the reactivity spread to
> > > the top of the structure, or use "compound-lift" (like the new
> > > animation.ss does), which lets your procedure find the reactivity
> > > while traversing the structure. (This avoids the work of constructing
> > > a deep snapshot each time something changes.) If you decide to
> > > upgrade to v371 at some point, let me know and I'll be happy to help
> > > explain this.
> >
> > That makes sense. I'm running 370 - time to upgrade :)
> >
> > > A couple of small comments: I'm surprised that you don't have code to
> > > clear the buffer before rendering the list of shapes, like
> > > animation.ss does.
> >
> > This is the nub of the problem for me really ;)
> >
> > When running in drscheme, the render loop is running in a different
> > (plt) thread.
> >
> > This is due to the way fluxus works - you don't drive the render loop
> > yourself, but you can optionally hook into it to get a function called
> > by the engine every frame to animate/update things. (using frtime even
> > this wont be needed)
> >
> > This is a Good Thing - the framerate can be detached from the script
> > update (you can always interactively move the camera around with the
> > mouse etc)
> >
> > The problem with this naive frtime script is that the primitives strobe
> > as they are drawing once per millisecond, rather than once per frame.
> > This is fixed by using retained mode primitives (rather than the
> > immediate mode (draw-*) ones it uses at the moment), which are
> > maintained inside the render engine and rendered every frame for you,
> > optimised for speed. then frtime can just update them whenever it wants
> > to.
> >
> > This has implications to how I implement the frtime interface - I'll
> > need to do some work to make this stateful (for reasons of speed)
> > situation appear stateless.
> >
> > One option is to require the 'user' to name each object, so the frtime
> > implementation can infer some historical information - first time it
> > appears build it, other times update it, when it doesn't appear in the
> > list delete it.
> >
> > How does animation.ss cope with this? If a shape only updates once a
> > second, how does it get redrawn when other things are updating more
> > quickly?
> >
> > > Also, I'm not sure why you don't just write:
> > >
> > > (define clock milliseconds)
> >
> > Ah, I was just playing with changing it, but left it as * 1 :)
> >
> > cheers,
> >
> > dave
> >
> >