Jump to content
  • entries
    657
  • comments
    2,692
  • views
    898,796

a couple optimizations


SpiceWare

1,297 views

The sprite collision routine is called a lot, and I do mean a lot. it's used to check if a robot can safely move (Wall Avoidance in Berzerk and Wall + Robot Avoidance in Frenzy), figure out if a robot collided with something, figure out if the humanoid collided with something, and so on. So my first optimization was to look it over and see what I could do to make it faster. I ended up replacing a number of things with pre-calculated values that are stored in the space freed up by dropping the digital sample buffer. For example:

if (image2_id >= ROBOT_RIGHT_ID && image2_id <= ROBOT_RIGHT_ID + 7)
{
left2--;
right2--;
}

right2 = left2 + 8 + double2;
 

 

became

if (gSpriteState[i] & 0x100)
{
left2--;
right2--;
}

right2 = gSpriteXr[i];
 

 

The if was used to compensate for the right-movement robot's change of the center pixel of the robot. To keep the robot centered, right-moving robots (but not tanks) are displayed 1 pixel to the left of their actual X location, so the collision routine must also compensate. The right2 is used figure out if sprites overlap horizontally. Double2 would have had the value of 8 for 2X sprites (like big otto). The pre-calculated values like the bits in gSpriteState and gSpriteXr are only updated whenever a sprite moves.

 

 

The other thing I did was revamp the robot movement routines. I did a couple things there:

 

1) added a "sleep" timer. When the robot fires, it goes to sleep for 15 movement-frames1. This prevents the robot from rapid-firing at you and saves processing time on it's "sleeping frames". The number of sleep frames will be revised as the game is developed.

 

2) when a robot moves, it used to do up to 3 Wall/Robot Avoidance checks per movement-frame. The check compares the potentially new location against all other sprites. If it failed it would check the alt1 direction, and finally alt2 direction. The revised routines only do 1 Avoidance check per movement-frame. If it succeeded the robot would move, otherwise a flag is set to try alt1 direction and then processing for that robot ends. If alt1 failed on the next movement frame, the flag would be set for alt-2. On the following movement-frame, if alt2 direction failed the robot would go to sleep for 15 movement-frames. This sleep duration can be a different duration from the one used when the robot fires.

 

 

So - the big question is what did these changes buy us? In the prior release I noticed screen jitter starting in room 9 of the Frenzy variation. In today's release I noticed it starting in room 1a (26).

 

Besides code optimization, some other things I can do to make the game perform better is to 1) reduce the maximum number of sprites the game can handle (it's currently 24 [the 4 shots are not part of the 24]) and 2) decrease the maximum robots that can move per frame.

 

 

1 only a limited number of robots move on each frame. As the levels increase, the number of robots per frame also increases. Frames that a particular robot moves are considered its "movement-frame".

 

ROMs

frantic_harmony_20111003.bin

frantic_stella_20111003.bin

 

Source

Frantic20111003.zip

10 Comments


Recommended Comments

This code in the PlayfieldSpriteCheck function :-

 

for(i=7;i>=0;i--)
{
	temp = x >> 2;
	if (gRoomLayout[y5 + x_offset[temp]] & x_bit[temp])
		result = result | (1 << i);
	if (gRoomLayoutAlt[y5 + x_offset[temp]] & x_bit[temp])
		resultalt = resultalt | (1 << i);
	x++;
}

 

Could probably be replaced with the following (assuming I understand what its doing correctly).

 

static const int PlayfieldCheck1[4]={0xF0,0x70,0x30,0x10};
static const int PlayfieldCheck2[4]={0x0F,0x07,0x03,0x01};

 

temp=x>>2;
       x&=3;   // Common
if (gRoomLayout[y5+x_offset[temp]]&x_bit[temp])
    result|=PlayFieldCheck1[x];
if (gRoomLayoutAlt[y5+x_offset[temp]]&x_bit[temp])
    resultalt|=PlayFieldCheck1[x];
temp++;
if (gRoomLayout[y5+x_offset[temp]]&x_bit[temp])
    result|=PlayFieldCheck2[x];
if (gRoomLayoutAlt[y5+x_offset[temp]]&x_bit[temp])
    resultalt|=PlayFieldCheck2[x];

 

Which is approximately the same as 2 passes through the loop instead of 8.

 

Edit: Further optimised.

Link to comment

It's building an 8x1 sprite pattern that matches the playfield at position X,Y (in sprite coordinates).

 

I think I see what your doing, but the 8x1 pattern can cover 2 or 3 PF pixels and it looks like your routine only deals with 2 PF pixels.

 

Maybe this will do it...

 

static const int PlayfieldCheck1[4]={0xF0,0xE0,0xC0,0x80};

static const int PlayfieldCheck2[4]={0x0F,0x1E,0x3C,0x78};

static const int PlayfieldCheck3[4]={0x00,0x01,0x03,0x07};

 

 

temp=x>>2;

x&=3; // Common

if (gRoomLayout[y5+x_offset[temp]]&x_bit[temp])

result|=PlayFieldCheck1[x];

if (gRoomLayoutAlt[y5+x_offset[temp]]&x_bit[temp])

resultalt|=PlayFieldCheck1[x];

temp++;

if (gRoomLayout[y5+x_offset[temp]]&x_bit[temp])

result|=PlayFieldCheck2[x];

if (gRoomLayoutAlt[y5+x_offset[temp]]&x_bit[temp])

resultalt|=PlayFieldCheck2[x];

temp++;

if (gRoomLayout[y5+x_offset[temp]]&x_bit[temp])

result|=PlayFieldCheck3[x];

if (gRoomLayoutAlt[y5+x_offset[temp]]&x_bit[temp])

resultalt|=PlayFieldCheck3[x];

Link to comment

Yep - that worked. I modified the routine to run both ways and to set the score to 111111 to flag if the results didn't match.

 

I haven't yet hooked my systems back up after NNO so I'm not able to test the performance increase at the moment, but I'm sure that saved some time - thanks!

Link to comment

It's called a lot, every time something moves it's called at least once, sometimes twice (due to the robot's wall-avoidance logic). A lot more time is spent in Collision(), but PlayfieldSpriteCheck() would be easier to convert.

Link to comment

What room can you get up to now before the game runs into problems? I have thought of a neat trick for THUMB coding because "y5+x_offset[temp]" only increases by 37 or is unchanged depending on the value of temp. This extra information could be encoded into a table and extracted. However, it wouldn't be very efficient in "C".

Link to comment

I don't know - Frantic is now crashing before I can get that far. I'm also noticing shots that go away as though they hit something, even though they didn't so something with the collision optimizations messed something up :(

 

It doesn't crash when I test in Stella (the ARM exception screen shows the PC which I can use with the listing to figure out the function it crashed in), so I'm going to have to revert to the October 1 version and do lots of testing between small changes.

 

Between this and the Chun-Li problems (it's now crashing on the 7800, even with the MAM1 setting), I think I'm going to take a break from Frantic for a bit as I've gotten too frustrated.

Link to comment

If MAM1 isn't working you have to slow the ARM core's CPU clock down too. Sounds like you are at the limit with MAM1 enabled in Frantic. To take a hit on the CPU clock you'd probably have to move over to pure THUMB code or reconsider what happens every frame (or both).

Link to comment

Based on the shots disappearing - I suspect it's not the MAM1 issue. Most likely there's a pre-calculated value that's not getting updated correctly and the program's pointing to the wrong bit of memory when it tries to point to the image bitmap for the collision test.

Link to comment
Guest
Add a comment...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...