Sign in to follow this  
XXChester

XNA Sprite performance problem with Matrix's

Recommended Posts

Hello everyone, I have been working on a shooter game (a lot like Reimer's shooter tutorial) and I have run into a bottle neck. When I draw just a static image as my bomb being fired the performance is fine but as soon as I change it to a sprite (which passes a Rectangle frame to the SpriteBatch.Draw() method instead of null) my game starts to chug.

I am positive the problem is with my Matrix calculation (I am new to Matrices) and I know it has something to do with the Rectangle argument. I am looking for help on this, I have searched the web for the past few hours and cannot seem to find anyone that mentions how to render a sprite opposed to a static image that is being fired.

Here is my Matrix calcuation inside the update loop of the missile;
[code]
Matrix origin = Matrix.CreateTranslation(new Vector3(-this.bomb.Origin, 0f));
Matrix scale = Matrix.CreateScale(new Vector3(this.bomb.Scale, 0f));
Matrix rotation = Matrix.CreateRotationZ(this.bomb.Rotation);
Matrix position = Matrix.CreateTranslation(new Vector3(this.bomb.Position, 0f));
[/code]
I have also tried this;
[code]
Rectangle currentFrame = this.bomb.Frames[this.bomb.AnimationManager.CurrentFrame];
Matrix origin = Matrix.CreateTranslation(new Vector3(-Vector2.Add(this.bomb.Origin, new Vector2(currentFrame.X, currentFrame.Y)), 0f));
Matrix scale = Matrix.CreateScale(new Vector3(this.bomb.Scale, 0f));
Matrix rotation = Matrix.CreateRotationZ(this.bomb.Rotation);
Matrix position = Matrix.CreateTranslation(new Vector3(this.bomb.Position, 0f));
[/code]

My sprite classes draw looks like this;
[code]
public virtual void render(SpriteBatch spriteBatch, int frame) {
spriteBatch.Draw(this.texture, this.position, this.frames[frame], base.lightColour, base.rotation, base.origin, base.scale, base.spriteEffect, base.layer);
}
[/code]

For comparision the static image drawing looks the exact same with the exception that it only has 1 rendering rectangle;
[code]
public override void render(SpriteBatch spriteBatch) {
spriteBatch.Draw(this.texture, base.position, this.renderingRectangle, base.lightColour, base.rotation, base.origin, base.scale, base.spriteEffect, base.layer);
}
[/code]

I know the problem is not with my sprite class because I have used it for many games with way more than the 1 sprite in this game and they all run fine.

If someone can provide some insight to this or provide a link that would be great.
Thanks.

Share this post


Link to post
Share on other sites
Try using struct? I'm not terribly experienced with csharp but I believe this data here, being relatively small, would be bandled better through such a method.

Exampl: (please forgive lack of format, on iPod)

public struct Vector3
{
public float x = 0.0;
public float y = 0.0;
}

//End

Again, only an example of an idea. (Its how I'm handling my positioning)

Also, how big are your sprites exactly? Big ones = slow drawing and loading.

Share this post


Link to post
Share on other sites
I don't know of anything that you could be doing with Matrix calculations that would affect performance so drastically (unless you are doing it 10000+ times a frame). You can try posting some more code, there's probably something else affecting performance here.

Share this post


Link to post
Share on other sites
How big is your sprite on screen, how big is the texture you are using for your sprite. It is most likely that you are hitting a hardware limit if the texture is big and the sprite on screen is big, the GPU's fill rate can't keep up and the card just falls over attempting to execute the command you gave it.

I had this when I tried to render a 500K vertices object in one VB on a 8600 GT card, it drew half the object event tough the card can handle more vertices than that quite comfortable it can't in just one buffer however, this was in immediate mode.

If you hit a fill rate limit you will see your framerate drop and eventually seems to stop updating your frames (frames are taking more than 16 ms to update). The most obbvious way you can hit a fill rate is a particle system with a lot of particles that are textured.

Share this post


Link to post
Share on other sites
Unfortunately my sprite is not big. Each frame of the sprite is 64x64 pixels with several frames. The overall texture width is 256x64. The background texture is 1280x768. I don't think its a powers of 2 problem either because my graphics card has never had a problem with them before. The bottle neck is when I am trying to determine a collision between a player and the nuke using Reimers pixel perfect collision function. The difference is that my "rocket" is a sprite and his is a static image. When I switch to the static image the framerate doesn't drop. I also forgot to include the actual matrix calculation in my first post. this.Matrix = origin * scale * rotation * position; Also note that the XNA Vector objects are structs. I was thinking this morning on my way to work that I must have to adjust the position in regards to the rendering rectangle of the texture in the Position matrix but I don't know. I am at work now so I can't try that out. Keep the suggestions coming guys, I appreciate it. Also note that it is not a scenario where I am reaching a sprite batch limit. My scene consists of 9 static images which is 1 for the background, 2 for each player the truck bed and the missile silo on its back. At the most there is only 1 sprite drawing to the screen in addition to these static images. Thanks.

Share this post


Link to post
Share on other sites
I have no idea what you mean by "Reimers pixel perfect collision function" but for a game i was writing I needed to make check for lists of literally 100's of bullets with 100's of objects.

First trick I used was to only recalculate the matrix and bounding rectangle for each object only when needed.
Basically everything was derived from Sprite implementation of iSpriteinfo. Whenever X,Y, rotation, scale and whatever changed sprite would set a needrecalc flag. The next time I needed a matrix or boundingbox it would recalculate them. Also make sure the values actually changes you would be surprised how often the value set is already stored...

Second trick was to cache the ColorData I needed for collision checking into a static dictionary of spriteNames.
I know this takes up a lot of memory but since I only had a limited amount of sprites that needed to be collision checked and had enough memory I didn't care.

The last trick was to write optimized code to compare lists with lists of objects so that I needed to collect the data for the first list of objects only once.

I leave you the routines to calculate a bounding box and to one I use to do pixel-perfect Collision checks. I forgot where I converted them from they came from a forum much like these ones but I have been using them for ages.

I hope It helps.


[b][size="4"]CalculateBoundingRectangle[/size][/b]
[Code]
/// <summary>
/// Calculates an axis aligned rectangle which fully contains an arbitrarily
/// transformed axis aligned rectangle.
/// </summary>
/// <param name="rectangle">Original bounding rectangle.</param>
/// <param name="transform">World transform of the rectangle.</param>
/// <returns>A new rectangle which contains the trasnformed rectangle.</returns>
public static Rectangle CalculateBoundingRectangle(Rectangle vrectangle,
Matrix transform)
{
Rectangle rectangle = vrectangle;
rectangle.X = 0;
rectangle.Y = 0;

// Get all four corners in local space
Vector2 leftTop = new Vector2(rectangle.Left, rectangle.Top);
Vector2 rightTop = new Vector2(rectangle.Right, rectangle.Top);
Vector2 leftBottom = new Vector2(rectangle.Left, rectangle.Bottom);
Vector2 rightBottom = new Vector2(rectangle.Right, rectangle.Bottom);

// Transform all four corners into work space
Vector2.Transform(ref leftTop, ref transform, out leftTop);
Vector2.Transform(ref rightTop, ref transform, out rightTop);
Vector2.Transform(ref leftBottom, ref transform, out leftBottom);
Vector2.Transform(ref rightBottom, ref transform, out rightBottom);

// Find the minimum and maximum extents of the rectangle in world space
Vector2 min = Vector2.Min(Vector2.Min(leftTop, rightTop),
Vector2.Min(leftBottom, rightBottom));
Vector2 max = Vector2.Max(Vector2.Max(leftTop, rightTop),
Vector2.Max(leftBottom, rightBottom));

// Return that as a rectangle
return new Rectangle((int)min.X, (int)min.Y,
(int)(max.X - min.X), (int)(max.Y - min.Y));
}

[/Code]

[b][size="4"]IntersectPixels[/size][/b]
[code]
/// <summary>
/// Determines if there is overlap of the non-transparent pixels between two
/// sprites.
/// </summary>
/// <param name="transformA">World transform of the first sprite.</param>
/// <param name="widthA">Width of the first sprite's texture.</param>
/// <param name="heightA">Height of the first sprite's texture.</param>
/// <param name="dataA">Pixel color data of the first sprite.</param>
/// <param name="transformB">World transform of the second sprite.</param>
/// <param name="widthB">Width of the second sprite's texture.</param>
/// <param name="heightB">Height of the second sprite's texture.</param>
/// <param name="dataB">Pixel color data of the second sprite.</param>
/// <returns>True if non-transparent pixels overlap; false otherwise</returns>
public static bool IntersectPixels(
Matrix transformA, int widthA, int heightA, Color[] dataA,
Matrix transformB, int widthB, int heightB, Color[] dataB)
{
// Calculate a matrix which transforms from A's local space into
// world space and then into B's local space
Matrix transformAToB = transformA * Matrix.Invert(transformB);

// When a point moves in A's local space, it moves in B's local space with a
// fixed direction and distance proportional to the movement in A.
// This algorithm steps through A one pixel at a time along A's X and Y axes
// Calculate the analogous steps in B:
Vector2 stepX = Vector2.TransformNormal(Vector2.UnitX, transformAToB);
Vector2 stepY = Vector2.TransformNormal(Vector2.UnitY, transformAToB);

// Calculate the top left corner of A in B's local space
// This variable will be reused to keep track of the start of each row
Vector2 yPosInB = Vector2.Transform(Vector2.Zero, transformAToB);

// For each row of pixels in A
for (int yA = 0; yA < heightA; yA++)
{
// Start at the beginning of the row
Vector2 posInB = yPosInB;

// For each pixel in this row
for (int xA = 0; xA < widthA; xA++)
{
// Round to the nearest pixel
int xB = (int)Math.Round(posInB.X);
int yB = (int)Math.Round(posInB.Y);

// If the pixel lies within the bounds of B
if (0 <= xB && xB < widthB &&
0 <= yB && yB < heightB)
{
// Get the colors of the overlapping pixels
Color colorA = dataA[xA + yA * widthA];
Color colorB = dataB[xB + yB * widthB];

// If both pixels are not completely transparent,
if (colorA.A != 0 && colorB.A != 0)
{
// then an intersection has been found
return true;
}
}

// Move to the next pixel in the row
posInB += stepX;
}

// Move to the next row
yPosInB += stepY;
}

// No intersection found
return false;
}
[/code]

Share this post


Link to post
Share on other sites
Thanks for the suggestion. Reimer's is from this website: http://www.riemers.net/ I am already caching the ColorData and recalculating the Matrix of things that move (the rocket), all others are static. I considered changing the collision detection last night to first check two bounding boxes before performing the pixel perfect collision. I just didn't think I would need to do this as the game is quite small. However, I will give that a try tonight and see if that helps. Thanks.

Share this post


Link to post
Share on other sites
[quote name='XXChester' timestamp='1317899941' post='4869741']
Unfortunately my sprite is not big. Each frame of the sprite is 64x64 pixels with several frames. The overall texture width is 256x64. The background texture is 1280x768. I don't think its a powers of 2 problem either because my graphics card has never had a problem with them before. The bottle neck is when I am trying to determine a collision between a player and the nuke using Reimers pixel perfect collision function. The difference is that my "rocket" is a sprite and his is a static image. When I switch to the static image the framerate doesn't drop. I also forgot to include the actual matrix calculation in my first post. this.Matrix = origin * scale * rotation * position; Also note that the XNA Vector objects are structs. I was thinking this morning on my way to work that I must have to adjust the position in regards to the rendering rectangle of the texture in the Position matrix but I don't know. I am at work now so I can't try that out. Keep the suggestions coming guys, I appreciate it. Also note that it is not a scenario where I am reaching a sprite batch limit. My scene consists of 9 static images which is 1 for the background, 2 for each player the truck bed and the missile silo on its back. At the most there is only 1 sprite drawing to the screen in addition to these static images. Thanks.
[/quote]

Are you sure you are checking just against your sprite and not the whole spritesheet? That could slow stuff down a bit.

You said that the pixel perfect collision worked without rotating correct? Are you maybe applying the transform for every pixel you check rather than transforming the sprite then checking against the already transformed sprite? edit: looking at reimers thing it seems to do this, but that shouldn't be a huge issue unless you are accidentally checking against the whole spritesheet. How are you getting your pixel data from the sprite?

Like someone above said, we have no way to tell what's slowing you down because you posted 3 very small variable definitions. I'm sure I could make a program using all the code you've given and get well over 100 fps because we have no idea how you are using any of it other than you have a matrix that is applied to a sprite at least once and that you are trying to do pixel perfect collision.

Share this post


Link to post
Share on other sites
Thanks everyone for the suggestions. Sure enough it was what I was already thinking, I needed to do something specificly related to the rectangle of the sprite from the texture. I changed my 2d texture array to only make the part of the sprite's current frame.

Share this post


Link to post
Share on other sites
I hesitated to send this as there are some problems with the tool right now.

Nprof is a tool that profiles .net code. trouble is it doesn't work for .net 4 and XNA4.
that is not unless you use this workaround.

Download Nprof here [url="http://code.google.com/p/nprof/"]http://code.google.com/p/nprof/[/url] install it.
Then create a batch file NProf.bat and enter

set COMPLUS_ProfAPI_ProfilerCompatibilitySetting=EnableV2Profiler
NProf.exe

If you run your program under it, it will upon exit generate a rapport where you can see what parts of your program take the longest.
[attachment=5699:Nprof.png]

Here you see an example as you can see of the 100% that Test of Nprof takes
BadGameClass takes up 90%
BadGameClass.BadUpdateFunction 89%

so now you know where to look for your problem.

The tool doesn't look like much but for me it's invaluable when optimizing my code as I can find out where to put the work to get the best performance.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this