home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1992 #20 / NN_1992_20.iso / spool / comp / sys / mac / programm / 15157 < prev    next >
Encoding:
Text File  |  1992-09-08  |  6.6 KB  |  169 lines

  1. Newsgroups: comp.sys.mac.programmer
  2. Path: sparky!uunet!sun-barr!decwrl!adobe!jholt@adobe.com
  3. From: jholt@adobe.com (joe holt)
  4. Subject: Re: Game Techniques (was: NON-QUICKDRAW GAMES)
  5. Message-ID: <1992Sep8.191342.15509@adobe.com>
  6. Sender: usenet@adobe.com (USENET NEWS)
  7. Organization: Adobe Systems Inc.
  8. References: <1992Sep8.004821.11323@adobe.com> <Bu8oyn.LD6@acsu.buffalo.edu>
  9. Date: Tue, 8 Sep 1992 19:13:42 GMT
  10. Lines: 157
  11.  
  12. Nick asks some good questions. The biz about 1K per row of 640 bytes is a real shocker,
  13. isn't it? *All* of that left over memory! But that's the way it's done--it's easier in
  14. hardware to divide by 1K. You'll notice other such wastes as you go to other screen
  15. depths/configurations. Most cards do this. RAM is cheap, right?
  16.  
  17. There are three things that slow down your small screen blasting loop:
  18.  
  19. while ( !Button() )
  20.     *myScrnPtr++ = 0xAA;
  21.  
  22. #1 is that the Button() call here is a real cycle sink--altho' it's quite snappy
  23. compared to, say, CopyDeepMask() with a maskRgn. If you were to profile this code
  24. you'd see about 95% going to the Button() call. Here's how it compiles for me:
  25.  
  26.   mouse_down
  27.      +003E  00EFECF4  *BRA.S      mouse_down+0044            ; 00EFECFA   | 6004
  28.      +0040  00EFECF6   MOVE.B     #$AA,(A3)+                              | 16FC 00AA
  29.      +0044  00EFECFA   CLR.B      -(A7)                                   | 4227
  30.      +0046  00EFECFC   _Button                               ; A974       | A974
  31.      +0048  00EFECFE   TST.B      (A7)+                                   | 4A1F
  32.      +004A  00EFED00   BEQ.S      mouse_down+0040            ; 00EFECF6   | 67F4
  33.  
  34. The branch at the start is only done once to get into the while() loop, so it
  35. doesn't count. The button testing code, including the trap dispatch overhead and
  36. the actual Button() code in ROM, comes to about 300 cycles. In contrast, the *one*
  37. instruction which actually writes to the screen and increments myScrnPtr:
  38.  
  39.      +0040  00EFECF6   MOVE.B     #$AA,(A3)+                              | 16FC 00AA
  40.  
  41. Is all of 12 cycles, or just 4% of the loop. Wow. So get rid of the Button()! You
  42. could test the low-mem global MBState yourself, or make it a for loop. Do this and
  43. you'll see a blinding increase:
  44.  
  45. while ( *((char *)0x172) < 0 )  // MBState, if you didn't know...
  46.     *myScrnPtr++ = 0xAA;
  47.  
  48. Get ready to hit that mouse button!
  49.  
  50.  
  51. #2 is that you're only stuffing bytes at a time. The 680x0 is non-intuitive in that
  52. it does *not* take twice as long to write a word into memory, or four times as long
  53. to write a long. Words are just as fast as bytes. Let me repeat: whenever you store
  54. a byte, you can store a word just as fast. Gee, that makes sense, doesn't it?
  55. Whatever the reason for this quantum leap, we game programmers must take advantage
  56. of it. But wait. Long words are faster than two words, so that's what you've got
  57. to use. Change the loop above to:
  58.  
  59.     long *longPtr = (long *) myScrnPtr;
  60.  
  61.     while ( *((char *)0x172) < 0 )
  62.         *longPtr++ = 0xAAAAAAAA;
  63.  
  64. Now you're talking fast. We're also getting into that gray area where it really would
  65. be better just to code the dang thing in assembly. For example, the compiler is likely
  66. to code the 0xAAAAAAAA as an immediate operand, but if it's put into a data register
  67. the speed almost doubles. This might do it:
  68.  
  69.     long *longPtr = (long *) myScrnPtr;
  70.     long color = 0xAAAAAAAA;
  71.  
  72.     while ( *((char *)0x172) < 0 )
  73.         *longPtr++ = color;
  74.  
  75. Which in assembly looks like this:
  76.  
  77.   mouse_down
  78.      +0042  00ECA5F8  *MOVEA.L    A2,A4                                   | 284A
  79.      +0044  00ECA5FA   MOVE.L     #$AAAAAAAA,D7                           | 2E3C AAAA AAAA
  80.      +004A  00ECA600   BRA.S      mouse_down+004E            ; 00ECA604   | 6002
  81.      +004C  00ECA602   MOVE.L     D7,(A4)+                                | 28C7
  82.      +004E  00ECA604   TST.B      MBState                                 | 4A38 0172
  83.      +0052  00ECA608   BLT.S      mouse_down+004C            ; 00ECA602   | 66F8
  84.  
  85. The first three instructions are only done once, so they don't count. The loop itself--
  86. the last three instructions--is just 34 cycles. 40x faster than the original version!
  87.  
  88.  
  89. #3 is nit-picking, but it will really speed things up, too. It's pretty silly to test
  90. for a mouse down between *every* long. Do it between every ten or so:
  91.  
  92.     while ( *((char *)0x172) < 0 ) {
  93.         *longPtr++ = color;
  94.         *longPtr++ = color;
  95.         *longPtr++ = color;
  96.         *longPtr++ = color;
  97.         *longPtr++ = color;
  98.         *longPtr++ = color;
  99.         *longPtr++ = color;
  100.         *longPtr++ = color;
  101.         *longPtr++ = color;
  102.         *longPtr++ = color;
  103.     }
  104.  
  105. The previous version required 34 cycles per long (12 for the write, 22 for the button
  106. test), or 340 cycles for ten longs. Here, however, the time for ten longs has been cut
  107. down to 142 cycles (120 for the writes, 22 for the button test). Let's go to the big
  108. board and see what our totals are so far: 90x (90x!) faster than the original Button()/
  109. single-byte loop. Jeez, you'd think we could just keep going and eventually fill the
  110. entire screen in just a few cycles, right?
  111.  
  112. Well, no. We've sorta reached the end of the speed-up line. These three tricks are
  113. at the heart of every fast memory operation:
  114.  
  115.   1. find the fastest instruction that moves the most bytes,
  116.   2. keep everything in registers, and 
  117.   3. unroll the loops.
  118.  
  119. Oops. There is a #4.
  120.  
  121.  
  122. #4. Only write to what you need to write to. This goes back to the observation at the
  123. beginning of the message: there're 384 bytes per scan line (in Nick's configuration)
  124. that are unseen (does this remind anyone of the old Apple II text screen holes?). So
  125. why write to them?! Change the loops to for loops: one for 640/4 = 160 longs per scan
  126. line, and one for 480 scan lines. See how fast you can fill the screen. Change the
  127. color on each fill and let it rip. You'll be amazed!
  128.  
  129. Here's my final version of the thing:
  130.  
  131.     register int x, y;
  132.     register long color = 0;
  133.     char mmuMode;
  134.  
  135.     mmuMode = true32b;
  136.     SwapMMUMode( &mmuMode );
  137.  
  138.     while ( *((char *)0x172) < 0 ) {
  139.         register long *rowBase = (long *) baseAddr;
  140.     
  141.         for ( y = 480; y; --y ) {
  142.             register long *longPtr = rowBase;
  143.  
  144.             for ( x = 640/4/10; x; --x ) {
  145.                 *longPtr++ = color;
  146.                 *longPtr++ = color;
  147.                 *longPtr++ = color;
  148.                 *longPtr++ = color;
  149.                 *longPtr++ = color;
  150.                 *longPtr++ = color;
  151.                 *longPtr++ = color;
  152.                 *longPtr++ = color;
  153.                 *longPtr++ = color;
  154.                 *longPtr++ = color;
  155.             }
  156.             rowBase = (long *) ((char *)rowBase + rowBytes);
  157.  
  158.         }
  159.         color += 0x01010101;  // next color
  160.  
  161.     }
  162.  
  163.     SwapMMUMode( &mmuMode );
  164.  
  165.  
  166. Have fun!
  167.  
  168. /joe
  169.