Jump to content







Photo

a couple optimizations

Posted by SpiceWare, in Frantic 03 October 2011 · 262 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[i] and gSpriteXr[i] 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
Attached File  frantic_harmony_20111003.bin (32K)
downloads: 24
Attached File  frantic_stella_20111003.bin (32K)
downloads: 30

Source
Attached File  Frantic20111003.zip (925.38K)
downloads: 19




Optimizations? Did someone mention my name? :)
  • Report
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.
  • Report
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];
  • Report
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!
  • Report
No probs! Glad to help. The whole routine could be a good candidate for THUMB coding if it gets called a load of times.
  • Report
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.
  • Report
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".
  • Report
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.
  • Report
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).
  • Report
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.
  • Report

May 2012

S M T W T F S
  12345
6789101112
13141516171819
2021 22 23242526
2728293031  

Recent Entries

Recent Comments

1 user(s) viewing

0 members, 1 guests, 0 anonymous users

Search My Blog

Latest Visitors