[comp.sys.mac.programmer] Optimizing animation processing speed

vrm@blackwater.cerc.wvu.wvnet.edu (Vasile R. Montan) (05/18/91)

I am in the process of writing a program that does some pretty
complicated animation.  In the version of the program I've got now,
things are just running too slow.  I've got to figure out how to
make things run faster.  (I'm already on an 030 machine with a
math coprocessor, so running this program on, say, a Plus would be
just intolerable.)  I'd like to run my scheme past you; could you
all please suggest places where I could speed things up?

In this animation, I am moving icons around on the screen.  I
have two offscreen bitmaps.  The first offscreen contains the
background art, which is slow to draw; this way, I only have to
draw it once.  I continually CopyBits this whole bitmap into
the second bitmap.  I draw the icons the second bitmap, then
copyBits the second bitmap onto the screen.  This whole process
happens every event loop.  (Slow as this sounds, I have reason
to believe that this is actually not the big time-consumer.)

Each time I plot an icon, I GetIcon it, then draw it using copyBits,
then ReleaseResource it.  Now, if I'm not mistaken, this doesn't
actually purge the icon from memory, right?  If something else needs
the memory it will write over it, but if nobody needs the memory,
the icon will hang around until the next cycle, and GetIcon will
just give me a handle to the icon in memory, right?

In doing the calculations for the animation, I frequently refer
to complex data structures by handles.  Is this a particularly
slow way of doing things?  Could I speed things up appreciably by
locking the handles and using more Pascal WITH statements?

Any suggestions?

--Kurisuto
un020070@vaxa.wvnet.edu

2fmlcalls@kuhub.cc.ukans.edu (05/18/91)

In article <1776@babcock.cerc.wvu.wvnet.edu>, vrm@blackwater.cerc.wvu.wvnet.edu (Vasile R. Montan) writes:
> all please suggest places where I could speed things up?
> 
> In this animation, I am moving icons around on the screen.  I
> have two offscreen bitmaps.  The first offscreen contains the
> background art, which is slow to draw; this way, I only have to
> draw it once.  I continually CopyBits this whole bitmap into
> the second bitmap.  I draw the icons the second bitmap, then
> copyBits the second bitmap onto the screen.  This whole process
> happens every event loop.  (Slow as this sounds, I have reason
> to believe that this is actually not the big time-consumer.)
> --Kurisuto
> un020070@vaxa.wvnet.edu

I'm inclined to believe this is the entire time-consumer (well, not *entire*
:)).
You obviously have defined a rect for your PlotIcon() routine.  Once the icon
appears on the screen (your first 'frame' of animation), then it is the only
portion of your background which needs replacing (as that is the only portion
of the background that was lost from your PlotIcon()).
Therefore, keep track of an oldRect.  Then rather than CopyBits()ing the whole
screen, just replace the background where the icon *was*.
I doubt you want flicker, so try this in your loop:

plotRect --> oldRect
move plotRect to new position
UnionRect(plotRect, oldRect, unionOfTwoRect)
CopyBits(backgroundOffscreen, intermediateOffscreen, unionOfTwoRect,
  unionOfTwoRect, srcCopy, nil)
set your intermediateOffscreen to be the active port
PlotIcon(plotRect, yourIcon)
CopyBits(intermediateScreen, onscreenWindow, unionOfTwoRect, unionOfTwoRect,
  srcCopy, nil);

Hows that for a mish-mash of code and algorithm?  I made some variable-name
assumptions (for clarity I hope).  Also, shouldn't really use nil for
CopyBits() to the screen (use the visRgn).
If you try this out, let me know what speed increase you get.

john calhoun  

lim@iris.ucdavis.edu (Lloyd Lim) (05/19/91)

In article <1776@babcock.cerc.wvu.wvnet.edu> un020070@vaxa.wvnet.edu writes:
>[...]
>
>In this animation, I am moving icons around on the screen.  I
>have two offscreen bitmaps.  The first offscreen contains the
>background art, which is slow to draw; this way, I only have to
>draw it once.  I continually CopyBits this whole bitmap into
>the second bitmap.  I draw the icons the second bitmap, then
>copyBits the second bitmap onto the screen.  This whole process
>happens every event loop.  (Slow as this sounds, I have reason
>to believe that this is actually not the big time-consumer.)

But you can still speed it up.  Instead of copying the whole background
over, keep track of where each icon was and just copy over little squares
of the background.  You could also take all the old squares, make a region
out of them, and use that as the maskRgn in a CopyBits of the whole
background.  I'm not sure which would be faster.

>Each time I plot an icon, I GetIcon it, then draw it using copyBits,
>then ReleaseResource it.  Now, if I'm not mistaken, this doesn't
>actually purge the icon from memory, right?  If something else needs
>the memory it will write over it, but if nobody needs the memory,
>the icon will hang around until the next cycle, and GetIcon will
>just give me a handle to the icon in memory, right?

IM says:
"Given a handle to a resource, ReleaseResource releases the memory occupied by
the resource data, if any, and replaces the handle to that resource in the
resource map with NIL. The given handle will no longer be recognized as a
handle to a resource; if the Resource Manager is subsequently called to get
the released resource, a new handle will be allocated. Use this procedure
only after you're completely through with a resource."

Don't call ReleaseResource.  If the resource is purgeable, it will stick
around unless the memory is needed for something else.  Icons aren't very
big so if you're using the same ones over and over, you probably will want
to load them once, save the handles, and make them unpurgeable (but not
locked).  Then just refer to them by the handles.

>In doing the calculations for the animation, I frequently refer
>to complex data structures by handles.  Is this a particularly
>slow way of doing things?  Could I speed things up appreciably by
>locking the handles and using more Pascal WITH statements?

There isn't much to a handle dereference.  Generally, using handles
and keeping them unlocked as much as possible is a good thing to do.
If you need to refer to a field with a call that moves memory, make a local
copy of the field and use the copy in the call.  If you need to use
something larger and move memory, HGetState, HLock, and HSetState.
WITHs and/or register variables (does Pascal have those?) might help a
little.

+++
Lloyd Lim     Internet: lim@iris.eecs.ucdavis.edu
              America Online: LimUnltd
              Compuserve: 72647,660
              US Mail: 215 Lysle Leach Hall, U.C. Davis, Davis, CA 95616

keith@Apple.COM (Keith Rollin) (05/19/91)

In article <1776@babcock.cerc.wvu.wvnet.edu> un020070@vaxa.wvnet.edu writes:
>
>In this animation, I am moving icons around on the screen.  I
>have two offscreen bitmaps.  The first offscreen contains the
>background art, which is slow to draw; this way, I only have to
>draw it once.  I continually CopyBits this whole bitmap into
>the second bitmap.  I draw the icons the second bitmap, then
>copyBits the second bitmap onto the screen.  This whole process
>happens every event loop.  (Slow as this sounds, I have reason
>to believe that this is actually not the big time-consumer.)

Depending on how many icons you are moving, I can think of two
things you could do:

- if you have a small number of icons, then don't refresh the
entirety of your second buffer. Just overwrite the few icons
selectively. That way, you aren't refreshing the majority of
your second buffer that doesn't need it.

- if you have a large number of icons, then the overhead of
calling QuickDraw a large number of times my actaully slow
you down enough that a single Copybits call to copy the entire
buffer is faster. However, if your buffers are bitmaps with the
same dimensions, or PixMaps with the same dimensions and color
table information, then try using BlockMove instead of
CopyBits.


-- 
------------------------------------------------------------------------------
Keith Rollin  ---  Apple Computer, Inc. 
INTERNET: keith@apple.com
    UUCP: {decwrl, hoptoad, nsc, sun, amdahl}!apple!keith
"But where the senses fail us, reason must step in."  - Galileo

palmer@nntp-server.caltech.edu (David Palmer) (05/19/91)

lim@iris.ucdavis.edu (Lloyd Lim) writes:

>In article <1776@babcock.cerc.wvu.wvnet.edu> un020070@vaxa.wvnet.edu writes:
>>[...]
>>
>>In this animation, I am moving icons around on the screen.  I
>>have two offscreen bitmaps.  The first offscreen contains the
>>background art, which is slow to draw; this way, I only have to
>>draw it once.  I continually CopyBits this whole bitmap into
>>the second bitmap.  I draw the icons the second bitmap, then
>>copyBits the second bitmap onto the screen.  This whole process
>>happens every event loop.  (Slow as this sounds, I have reason
>>to believe that this is actually not the big time-consumer.)

>But you can still speed it up.  Instead of copying the whole background
>over, keep track of where each icon was and just copy over little squares
>of the background.  You could also take all the old squares, make a region
>out of them, and use that as the maskRgn in a CopyBits of the whole
>background.  I'm not sure which would be faster.

Do an 'InvalRect()' on the place where you remove the icons, and on the
new location of the icon.  Then when you respond to an 'update' event,
just write the background and the icons.  This does what you suggested--
only writing to the invalidated regions.

The Macintosh Way is to not write anything to the screen except during
update events.

-- 
		David Palmer
		palmer@gap.cco.caltech.edu
		...rutgers!cit-vax!gap.cco.caltech.edu!palmer
	"Operator, get me the number for 911"  --Homer Simpson

ed@nsx.Berkeley.EDU (Ed Devinney) (05/21/91)

In article <1991May18.231302.16630@nntp-server.caltech.edu>, palmer@nntp-server.caltech.edu (David Palmer) writes:
|> Do an 'InvalRect()' on the place where you remove the icons, and on the
|> new location of the icon.  Then when you respond to an 'update' event,
|> just write the background and the icons.  This does what you suggested--
|> only writing to the invalidated regions.
|> 
|> The Macintosh Way is to not write anything to the screen except during
|> update events.

Which is all very nice, but not for animation.  Like the commercial says, sometimes you
gotta break the rules, and smooth, consistent animation requires taking control of the
drawing.  (Kind of hard to guarantee a frame rate in a purely user-event-driven 
environment).

|> 		David Palmer
|> 		palmer@gap.cco.caltech.edu

-- 
ed devinney						ed@visix.com
Visix Software Inc., Reston, VA				...!uunet!visix!ed
	
		"She turned me into a newt!"
		"A _newt_?"
		"I got better..."- MP&THG

trebor@lkbreth.foretune.co.jp (Robert J Woodhead) (05/21/91)

ed@nsx.Berkeley.EDU (Ed Devinney) writes:
>Which is all very nice, but not for animation.  Like the commercial says, sometimes you
>gotta break the rules, and smooth, consistent animation requires taking control of the
>drawing.  (Kind of hard to guarantee a frame rate in a purely user-event-driven 
>environment).

Well said!  I've been doing a little hacking with a colorspace II board
recently and worked out a couple of simple techniques that can be
useful.

My problem was to be able to do an instantaneous (eg: during the VBL
time) change from 1 image to another.  Unfortunately, a Mac II can't
repaint all 250k odd pixels in <1/60th of a second!

	(BTW, if anyone has any tricks to speed up block
	transfers to NuBus device memory, I'd be interested!)

Since I often needed to change most of the pixels on the screen, but
DIDN'T need to do this each and every VBL interrupt, the following
technique was useful.

The idea is to reduce the number of simultaneous colors you can have
on the screen and trade this for having multiple images in the screen
buffer.  You then simply change the color table in order to make the
desired image appear.

A simple example:  Lets say you only need 8 colors per image (3
bits per "pixel".  You reserve a block of color table entries that
start on an 64 entry boundary.  You need 64 of them (6 bits) do
be able to put 2 8 color images into the buffer at the same time.

These colors will all be in the format of, say, 01AAABBB, where AAA
is one set of 3 pixels and BBB is another set.  Next, paint image
#1 into the AAA pixels and image #2 into the BBB pixels.

Now set up 2 color tables for the graphics device.  In one, the AAA
bits are "don't care", so that the color setting for any value in
the table depends only on the value of BBB.  The second is the inverse,
where the AAA bits are don't care bits.  In each table, each color
is repeated 8 times.

Now, by flipping from color table to color table in a slotVBL routine,
the images will appear alternately.

If the object you are animating is small enough, you have enough time
to repaint the invisible object before the next VBL time, and thus
do totally seamless animation (albeit in reduced colors).  Note that
you can change the invisible object at any time, because changes to
those bits don't affect the visible image on the screen.

In my own application (a video titler) I only needed 4 colors, so I
have 3 different objects on the screen, and a pixel format of
01AABBCC.  Thus my program has 1 image visible, one waiting to be
displayed, and one being created.  You can also, by use of multiple
color tables, do fades and dissolves between images.

Not a general technique, but for some things it is incredibly useful.


-- 
+--------------------------------------------------------------------------+
| Robert J. Woodhead, Biar Games / AnimEigo, Incs.   trebor@foretune.co.jp |
| "The Force. It surrounds us; It enfolds us; It gets us dates on Saturday |
| Nights." -- Obi Wan Kenobi, Famous Jedi Knight and Party Animal.         |

jimc@isc-br.ISC-BR.COM (Jim Cathey) (05/22/91)

In article <1991May18.231302.16630@nntp-server.caltech.edu> palmer@nntp-server.caltech.edu (David Palmer) writes:
>Do an 'InvalRect()' on the place where you remove the icons, and on the
>new location of the icon.  Then when you respond to an 'update' event,
>just write the background and the icons.  This does what you suggested--
>only writing to the invalidated regions.

Yuck.  Sort of like writing yourself a note to buy eggs on Friday, and then
putting a stamp on it and your own address and dropping it into the mailbox
instead of leaving it on the refrigerator!

If you know you need to draw the icons, then just draw the icons.  Why
invoke a whole bunch of region math when you don't need to, especially
when it's just going to tell you what you already knew?  It will
certainly be slower.

>The Macintosh Way is to not write anything to the screen except during
>update events.

I disagree.  The Way is to write on the screen whenever necessary
(though it's not necessary more often than once per VSYNC time per
item) _and_ to write (everything dirty) on the screen during update
events --- not 'instead of'.

The same goes for a lot of GUI systems.

+----------------+
! II      CCCCCC !  Jim Cathey
! II  SSSSCC     !  ISC-Bunker Ramo
! II      CC     !  TAF-C8;  Spokane, WA  99220
! IISSSS  CC     !  UUCP: uunet!isc-br!jimc (jimc@isc-br.isc-br.com)
! II      CCCCCC !  (509) 927-5757
+----------------+
			"With excitement like this, who is needing enemas?"