Help with Macintosh programming

macintosh programming, mac programming, mac os programming, mac os x programming, mac programming language, mac programing

Old school (?) drawing philosophy

I finally have a serviceable 2d image drawing engine working under
System 1.0 through OS 9.2. I can draw arbitrary sized multi-frames
sprites, with (pre-generated) white masks, if needed, and with
auto-cleaning (saves background in temp bitmap and draws it back when
moving) during the blit process. (All in C)

However, even with smallish 16×16 pixels^2 bitmaps, I notice a faint
flicker, even if I’m looking at it. My strategy is to impose a delay in
the drawing with an IF condition on the value of TickCount() (do
drawing at every 4 ticks, for example), to blit the sprite, and deal
with movement and boundary/collisions conditions afterwards.

I’ve read in the THINK C reference that while I could install a VBL
task in the queue, the tasks that have to be done there must not move
memory – my sprite blitting functions use several calls of CopyBits at
their core, and that moves memory. The reference also mentions that
Ticks can be tracked and followed by a "quick" drawing to ensure smooth
animation. I’m wondering – is this done inside a VBL task, or is a
simple tracking of TickCount’s return value enough? I’m doing the
latter, to little success.

My main question would be: how do you test the speed of a blitting
function in a more systematic way than to fiddle with it until you
cease to see flickering?

Right now, I reckon that my blit function does several things and may
take more than a tick to resolve (in my target machines – 68k macs):
1) IF to check if the sprite cleans after itself. If yes, A) CopyBits
the old buffer into the old location. B) Change the buffer location to
the new one. C) CopyBits the new location into the buffer. No actual
sprite is drawn yet.
2) IF to check if the sprite uses a mask – if yes, draws the white mask
(a straightforward copybits with srcBic as its transfer mode).
3) CopyBits the sprite proper into its position (srcOr = transfer
mode).
4) If there’s no mask (2nd IF failed), copybits the sprite with
srcCopy.

At the worst (mask + cleaning), there are 2 IFs statement to evaluate,
3 copybits going on, one Rect assigning and 2 pairs of HLock HUnlock
that are needed around the new buffer CopyBits call.

I’m trying to figure out if this can be solved with VBL tasks, or if I
plain and simple need to optimize my BlitSprite function.

.
posted by admin in Uncategorized and have Comments (12)

12 Responses to “Old school (?) drawing philosophy”

  1. admin says:

    In article <1102756408.738018.263…@z14g2000cwz.googlegroups.com>,

     micjun…@gmail.com wrote:
    > I’ve read in the THINK C reference that while I could install a VBL
    > task in the queue, the tasks that have to be done there must not move
    > memory – my sprite blitting functions use several calls of CopyBits at
    > their core, and that moves memory. The reference also mentions that
    > Ticks can be tracked and followed by a "quick" drawing to ensure smooth
    > animation. I’m wondering – is this done inside a VBL task, or is a
    > simple tracking of TickCount’s return value enough? I’m doing the
    > latter, to little success.

    You can’t just wait for TickCount to change: what if the use has
    multiple video cards, all set to different refresh rates? TickCount is
    in no way synchronized to anything. That is what SlotVBLs are for: You
    register for an interrupt that occurs when the card that controls a
    specific monitor sends its vertical blanking signal.

    But: not all video cards send a VBL. Some LCD displays don’t need one.

    Also: you can’t do any drawing with Quickdraw (including CopyBits) at
    VBL time, since all Quickdraw drawing calls look at the visRgn and
    clipRgn of the current GrafPort, and since these are RgnHandles, which
    are Handles, they are not guaranteed to be valid at interrupt time,
    depending on whatever heap shuffling the memory manager happens otbe
    doing.

    I asked Apple: what if you create your windows in a private heap, then
    switch back to the main heap for everything else, so you know the memory
    manager isn’t going to touch your RgnHandles. I was told that even then
    quickdraw at interrupt time wasn’t guaranteed to be safe. (Besides,
    someone might have installed unsafe trap patches.)

    > My main question would be: how do you test the speed of a blitting
    > function in a more systematic way than to fiddle with it until you
    > cease to see flickering?

    See how long it takes to execute it 10000 times in a tight loop,
    compared to executing an empty 10000 times loop?

    - Hide quoted text — Show quoted text -

    > Right now, I reckon that my blit function does several things and may
    > take more than a tick to resolve (in my target machines – 68k macs):
    > 1) IF to check if the sprite cleans after itself. If yes, A) CopyBits
    > the old buffer into the old location. B) Change the buffer location to
    > the new one. C) CopyBits the new location into the buffer. No actual
    > sprite is drawn yet.
    > 2) IF to check if the sprite uses a mask – if yes, draws the white mask
    > (a straightforward copybits with srcBic as its transfer mode).
    > 3) CopyBits the sprite proper into its position (srcOr = transfer
    > mode).
    > 4) If there’s no mask (2nd IF failed), copybits the sprite with
    > srcCopy.

    > At the worst (mask + cleaning), there are 2 IFs statement to evaluate,
    > 3 copybits going on, one Rect assigning and 2 pairs of HLock HUnlock
    > that are needed around the new buffer CopyBits call.

    > I’m trying to figure out if this can be solved with VBL tasks, or if I
    > plain and simple need to optimize my BlitSprite function.

    Use a SlotVBL to set a bit on entry to VBL time, and have your main
    thread poll for that bit and do the drawing.

    None of the above applies to OS X. Under OS X, you’d just use OpenGL,
    which is hardware double buffered: while the user is looking at one
    frame, you are drawing the next frame to an offscreen buffer. Call
    SwapBuffers() and OpenGL busy-waits until that monitor’s VBL time, when
    it swaps the back and fore buffers by writing to single register on the
    video card, and now you have an entire frame time to draw. See:

    <http://www.ati.com/developer/demos/macss2/&gt; the animusic demo, in
    particular, is simply eye-popping incredible.


    David Phillip Oster

  2. admin says:


    See how long it takes to execute it 10000 times in a tight loop,
    compared to executing an empty 10000 times loop?

    Empty 5000 times loops yields a 0 tick difference between the a
    tickcount made before the loop, and one after it. I guess the compiler
    knows better.
    However, I’ve tried my Blit routine (with all the mask+cleaning
    features) vs a raw one (which is essentially only a direct CopyBits
    use, with the transfer mode of my choice) and the difference is
    staggering.
    First loop strategy: BlitSprite use, and a pointer is incremented to
    point to the correct frame. Ticks = 745
    Second loop strategy: DrawSprite use only (equivalent to CopyBits,
    using srcCopy, the fastest mode). Ticks = 25.

    I’ve just stumbled (an hour ago) unto a sprite graphics library that
    hopefully, will be easily adaptable for b&w macs (it claims it is).
    It’s called SAT (sprite animation toolkit) and I just downloaded
    version 2.6.0. There are many similar features, judging from its header
    file. However, I can’t compare it to my stuff because it’s compiled as
    a library and the source code isn’t available. I’ll take what I can
    from it.
    Thanks for your help on VBL tasks, you’ve been helpful, as always.

  3. admin says:

    In article <1103315059.983705.200…@f14g2000cwb.googlegroups.com>,

     micjun…@gmail.com wrote:
    > Empty 5000 times loops yields a 0 tick difference between the a
    > tickcount made before the loop, and one after it. I guess the compiler
    > knows better.

    That may be the case, but I guess that you do not realize how fast
    modern processors are.

    An idle loop of say four instructions times 5000 loops takes 20000
    instructions. At 1 GHz, that’s 2 * 10^4 / 10^9 = 2 * 10^-5 seconds.

    At 10 Mhz, it would take 2 ms. A Tick is about 16 ms.

    => The compiler does not have to optimize your loop away for it to take
    ‘zero ticks’

    You should use a faster counter, such as ‘MicroSeconds’ for your timing,
    or use many more loops.

    Reinder

  4. admin says:


    That may be the case, but I guess that you do not realize how fast
    modern processors are.

    An idle loop of say four instructions times 5000 loops takes 20000
    instructions. At 1 GHz, that’s 2 * 10^4 / 10^9 = 2 * 10^-5 seconds.

    At 10 Mhz, it would take 2 ms. A Tick is about 16 ms.

    Thanks for the approximative educated guesses. I just had a complete
    lack of knowledge in that area before.


    You should use a faster counter, such as ‘MicroSeconds’ for your
    timing,
    or use many more loops.

    Here’s the thing. I’m using Symantec C++ 6.0 (THINK C) and intend to
    program for 68000 macs. There is no MicroSeconds in that generation of
    C compiler. If you know of any other way to using timing for that
    period, please tell me.

  5. admin says:

    In article <1103349139.862498.184…@f14g2000cwb.googlegroups.com>,

    - Hide quoted text — Show quoted text -

     micjun…@gmail.com wrote:
    > —
    > That may be the case, but I guess that you do not realize how fast
    > modern processors are.

    > An idle loop of say four instructions times 5000 loops takes 20000
    > instructions. At 1 GHz, that’s 2 * 10^4 / 10^9 = 2 * 10^-5 seconds.

    > At 10 Mhz, it would take 2 ms. A Tick is about 16 ms.
    > —

    > Thanks for the approximative educated guesses. I just had a complete
    > lack of knowledge in that area before.

    > —
    > You should use a faster counter, such as ‘MicroSeconds’ for your
    > timing,
    > or use many more loops.
    > —

    > Here’s the thing. I’m using Symantec C++ 6.0 (THINK C) and intend to
    > program for 68000 macs. There is no MicroSeconds in that generation of
    > C compiler.

    I think that you can get MicroSeconds there by installing QuickTime
    version <whatever> or higher. So, this might be a problem with your
    header files.

    > If you know of any other way to using timing for that
    > period, please tell me.

    Use the extended Time Manager

    Reinder

  6. admin says:

    <micjun…@gmail.com> wrote:
    > I’ve just stumbled (an hour ago) unto a sprite graphics library that
    > hopefully, will be easily adaptable for b&w macs (it claims it is).
    > It’s called SAT (sprite animation toolkit) and I just downloaded
    > version 2.6.0. There are many similar features, judging from its header
    > file. However, I can’t compare it to my stuff because it’s compiled as
    > a library and the source code isn’t available. I’ll take what I can
    > from it.

    You might want to check out SpriteWorld:

    <http://www.spriteworld.org/&gt;

    The older version 2.3.1 should work fine on toaster Macs, and includes
    all source code.  Pascal is also available.

  7. admin says:

    I found it too, shortly after my last message. It claims it requires
    68020 macs and up, but I don’t know if it’s a requirement to compile it
    (meaning, it’d be usable only on compilers which run on these
    machines), or if the target environment for the compiled applications
    needs to be on 68020 macs.

    I’d rather continue my work and develop all my functions myself,
    perfectly tailored to my needs, but looking at the header file for the
    SAT engine, I have a lot of wheels to reinvent.

    Right now, I’m working on trying to use InsTime and PrimeTime to
    install tasks into a queue, that will give me control over
    milliseconds. I’m still trying to understand how I can avoid the
    flicker that comes when the background is restored under a sprite. I
    might have to devise a function that looks at which pixels must be
    changed and acts only upon those.

  8. admin says:

    In article <1103315059.983705.200…@f14g2000cwb.googlegroups.com>,

     micjun…@gmail.com wrote:
    > I’ve just stumbled (an hour ago) unto a sprite graphics library that
    > hopefully, will be easily adaptable for b&w macs (it claims it is).

    It is.

    > It’s called SAT (sprite animation toolkit) and I just downloaded
    > version 2.6.0. There are many similar features, judging from its header
    > file. However, I can’t compare it to my stuff because it’s compiled as
    > a library and the source code isn’t available. I’ll take what I can
    > from it.
    > Thanks for your help on VBL tasks, you’ve been helpful, as always.

    Mr. Ragnemalm made some fairly interesting architectural choices in that
    library that aren’t obvious but give some useful side-effects, as I
    recall. It’s been a long time since I did anything with it and that was
    mostly puttering. I ended up creating my own based on SpriteWorld 1.0b4
    at the time and took it in my own direction after that.

    G


    Change account to gw when responding by mail.

  9. admin says:

    —–
    I think that you can get MicroSeconds there by installing QuickTime
    version <whatever> or higher. So, this might be a problem with your
    header files.

    > If you know of any other way to using timing for that
    > period, please tell me.

    Use the extended Time Manager
    —–

    I’ve gathered some notes from IM and from websites about the Time
    Manager:

    original  - below System 6.0.3 – gives control of milliseconds (about 1
    millisecond is the best), can drift over time
    revised – between System 6.0.3 and 6.0.8L – gives control of
    microseconds (20 ms is the best), can drift over time
    Extended – From System 7.0 and up – gives control of nanoseconds,
    drifting is corrected

    The differences are well explained in the graphs in this IM page:
    http://developer.apple.com/documentation/mac/Processes/Processes-57.h

    As for the time delays I need – as I’ve told earlier, 500 Blits for a
    small image required 745 ticks, which comes to about 1,49 ticks per
    Blit (which, I remind you, uses the full gamut I’ve given it –
    background cleaning + mask + sprite itself).

    Note again that yes, I intend to write code for Classic Macs first, and
    not to mess with SlotVBL interrupts. So my screen refresh frequency is
    60.15 Hz. Checking the first edition of IM, here are the time delays at
    hand:
    Each of the 342 horizontal scan lines take 32.68 microsecs to draw.
    Each horizontal blanking (going from right edge to left edge between
    each line) take 12.25 microsecs.
    So the drawing process takes a total of 15367.65 microsecs = 15.37 ms.
    The vertical retrace takes 1.26 ms, and the grand cycle takes 16.63 ms.

    This means that when I’m waiting for a VBL interrupt before I start
    drawing, I only have 1.26 ms at my disposal before there’s a risk that
    there’ll be blinking (especially near the top of the screen). Barring
    optimizing my blit routines in assembly (which I don’t want to touch
    with a ten foot pole) to lower that estimate -count of 1,5 ms or so,
    I’ll have to use the revised Time Manager (to get System 6
    compatibility, but not prior to that) in order to draw my sprites with
    a strategy. I could roughly separate it into 2 drawing passes per
    screen refresh, one for the top half of the screen, and one for the
    bottom half, seeing as how 2 drawings of 1.5 ms each can fit fine into
    this 15.37 ms total time.

    I’d use the VBL interrupt ONCE to turn on a flag, which would be a cue
    to start implementing 2 revised Time Manager interrupts that would
    control the start of both drawing passes.

    Any comments, suggestions or error pointing you see with this strategy?
    I’d rather keep the original Time Manager to ensure compatibility for
    earlier-than-6.0.3 Systems. I’ll give it a try first, I think. Then on
    to revised if it’s not satisfactory.

  10. admin says:

    Sorry about the errors with the milliseconds (ms), and microseconds (us
    – mu seconds).
    original – milli
    revised – micro
    extended – nano
    screen refresh delays – total in milli, individual lines in micro

  11. admin says:

    Just a note on the program I had on hand at the start of this thread –
    I clearly see that the flickering starts to vanish partially between
    the middle of the screen and about 2/3rds of the screen down. There is
    no flicker between 2/3rds and the bottom of the screen. Flickering
    being an optical remanance effect, it’s of course lessened if the VBL
    delay is set to numbers higher than 1 (ie, drawing cued at every x
    screen refresh).

    However, I was deliberately not helping things by forcing a 17-pixel
    wide sprite unto my code. I’ll repeat other tests with widths that are
    multiples of 4, if not 8.

  12. admin says:

    Sorry for the barrage of replies, but I just made some more tests and
    the results are widely different. Somehow, my last results are thrice
    as long as what I get now.

    Blit, 16pixel wide, 10 000 times
    293 ticks = 1/60.15 * 293 = 4871.16 ms
    per blit: 0.4871 ms

    Blit + frame reeling, 16pixel wide, 10 000 times
    297 ticks = 1/60.15 * 297 = 5087.28 ms
    per frame blit: 0.5087 ms

    Blit, 16pixel wide, 10 000 times
    301 ticks = 1/60.15 * 301 = 5004.16 ms
    per blit: 0.5004 ms

    Blit + frame reeling, 17pixel wide, 10 000 times
    306 ticks = 1/60.15 * 306 = 4937.66 ms
    per frame blit: 0.4938 ms

    This means that in the best case scenario, with careful planning in the
    drawing of different regions on the screen, I can get about 28 smallish
    16 pixel wide sprites on screen at any given time. Of course, the
    number will end up being lower since I need to perform other tasks
    (collision detection, AI, etc) and I’ll probably need to use larger
    sprite (but the blit time is not directly proportional to width, thank
    god), but at least I’m relieved that it’s not as stark as I thought.