George Peter Staplin - 2007, Dec 9 - I had a basic rendering algorithm used in Whim2 that I was trying to improve without turning to C, and I came upon an interesting solution, that I thought others might benefit from.
This is the recursive algorithm I started out with. I was getting a peak of about 35 FPS with a 1024x768 display, and much less when there were more windows, or the tree was deeper.
proc render-tree {baseobj win x y} { global bserver_windows foreach child [$win _children] { set scale 1000 set destx [expr {$x + [$child x]}] set desty [expr {$y + [$child y]}] if {[info exists bserver_windows($child.window)]} { set scale [$child.window scale] } if {1000 != $scale} { set b [$child _render_tree_data] if {"" eq $b} { $child _render_tree_data [set b [megaimage-blank]] } $b setdata [[$child obj] getdata] render-tree $b $child 0 0 $b scale $scale $scale lassign [$b getsize] bwidth bheight $child width $bwidth height $bheight $baseobj blendobj $destx $desty $b } else { $baseobj blendobj $destx $desty [$child obj] render-tree $baseobj $child $destx $desty } } }
Now how do we go about turning it into a linear algorithm you may ask, we generate code recursively, that is linear. I found that Tcl is so fast at compiling a proc that it's not a big deal to regenerate it when the tree changes. I also took the time to eliminate as many variable lookups as possible. Improving performance is about removing indirection.
proc generate-render-tree {} { set v 0 set body [generate-render-windows [. obj] . [list] v] proc render-tree {} [subst {set offx0 0; set offy0 0; $body}] } proc generate-render-windows {baseobj win parentlist v_var} { global bserver_windows upvar $v_var v set code "" foreach child [$win _children] { set scale 1000 set oldv $v incr v append code [subst -nocommands -nobackslashes { set offx$v [expr {[set offx$oldv] + [$child x]}] set offy$v [expr {[set offy$oldv] + [$child y]}] }] if {[info exists bserver_windows($child.window)]} { set scale [$child.window scale] } if {1000 == $scale} { append code [subst -nocommands -nobackslashes { $baseobj blendobj [set offx$v] [set offy$v] [$child obj] }] append code [generate-render-windows $baseobj $child \ [concat $parentlist [list $child]] v] } else { set b [$child _render_tree_data] if {$b eq ""} { $child _render_tree_data [set b [megaimage-blank]] } incr v append code [subst -nocommands -nobackslashes { $b setdata [[$child obj] getdata] set offx$v 0 set offy$v 0 }] append code [generate-render-windows $b $child [list] v] incr v -1 append code [subst -nocommands -nobackslashes { $b scale $scale $scale lassign [$b getsize] bwidth bheight $child width [set bwidth] height [set bheight] $baseobj blendobj [set offx$v] [set offy$v] $b }] } incr v -1 } return $code }
So how much did that change my frame rate you may ask? I get a peak of about 74-75 FPS for the same test case as the recursive algorithm. That's more than double the speed. :-) The extra cost of regenerating the proc is minimal. I call generate-render-tree in several key places that alter the tree in Whim2. I have a feeling I wouldn't have been able to get the same performance with a C solution as easily. Tcl has a unique advantage in some respects :-)
Wow, this is really an interesting approach! Redefining a proc being called repeatedly ... hm ... perhaps this can be used to avoid some global variables in e.g. binding scripts, that need some state information ... I have to think about this one. Thanks for sharing!
escargo 10 Dec 2007 - I was thinking of downloading Whim again; is the code going to be updated with this new feature soon?