Dropping it into the for loop is much worse when there is rotation and causes a noticeable hit since the sin cos only needs to be precalculated i pulled it out of the loop. I did however wrap it in the pre calculation with a Vector2 q = Vector2.Zero if(..) which has very little impact it seems under rotation and should get skipped when there is none now. A long time ago i used look up tables for sin cos but i doubt that would be faster nowdays.
I like to use r as shorthand for result, So it's sort of how i keep it in mind r = p * q + o (result position rotation origin) t for translation s for scale. Ya q can also represent quaternions. Plus it looks nice... so it's all the easier to remember.
-Too many temp variables and memory moving around. Memory access are the
bottleneck on modern CPU's. Cache misses can be as expensive or worst
Im thinking this is whats going on. Ill try to implement that range of advice.
I tend to pre-calculate all my divisions into coefficients before dropping them into any loops. The same would apply for the texture uv's here i think.
Also MichaelDePiazzi pointed out here1 that we should precalculate Glyph's texture UV,
On load or when a windows resize occured i thought the same thing (though i didn't implement it here) to make my own glyph class that has a extra set of 4 uv members. take the coefficient of the screen width and height and multiply it by the bounds and create the uv's so the glyph holds the uv directly.
Which is pretty much what cu cv are doing (inefficiently) in the function
uvLT.X = bounds.X * (1f / image.Width);
cu cv in my code is just that 1f / image.Width and 1f / image.Height
so basically if you just did that on load or when the window resized in the glyph its just sitting in the glyph to be taken out. glyph.uvLT;
I don't really see the point of requiring performance while the user is resizing the window a slight hiccup at that time, is not only passable, its typically expected. It's the perfect and correct time to recalculate the coefficient for the texture and the uv's to the glyphs. Then you just pass them straight in from the glyph its not like the uv changes for the glyph at any other time, since each spritefont has its own texture.
Though note you still need the actual pixel value of the bounds here for rotation. So it needs a extra set of fields i don't think you can just replace bounds.
Vector2 localDrawXY = new Vector2(currentGlyph.Cropping.X, currentGlyph.Cropping.Y) * scale + offset;
Vector2 localDrawRB = localDrawXY + new Vector2(currentGlyph.BoundsInTexture.Width * scale.X, currentGlyph.BoundsInTexture.Height * scale.Y);
offset.X = (currentGlyph.Width + currentGlyph.RightSideBearing) * scale.X + offset.X;
// prep vertices to directly draw
Vector2 lt = new Vector2(localDrawXY.X, localDrawXY.Y);
Vector2 lb = new Vector2(localDrawXY.X, localDrawRB.Y);
Vector2 rt = new Vector2(localDrawRB.X, localDrawXY.Y);
Vector2 rb = new Vector2(localDrawRB.X, localDrawRB.Y);
if (rotation != 0)
lt = new Vector2(lt.X * q.Y - lt.Y * q.X, lt.X * q.X + lt.Y * q.Y);
lb = new Vector2(lb.X * q.Y - lb.Y * q.X, lb.X * q.X + lb.Y * q.Y);
rt = new Vector2(rt.X * q.Y - rt.Y * q.X, rt.X * q.X + rt.Y * q.Y);
rb = new Vector2(rb.X * q.Y - rb.Y * q.X, rb.X * q.X + rb.Y * q.Y);
Also origin is nothing though xna used it against the uv to alter the texel position, you can easily just use it against the position directly. which is what i've done ideologically here. Which turns it into simple addition subtraction.
Vector2 localDrawXY = new Vector2(currentGlyph.Cropping.X, currentGlyph.Cropping.Y) * scale + offset - origin;
... rotations code ...
lt += position + origin;
lb += position + origin;
rt += position + origin;
rb += position + origin;
However this has 'no meaning' in the context of drawstring without spriteffects around. So i actually totally removed it which is perfectly fine. As the position then IS the origin in all cases, in the context of a drawstring. (its only in there because i was lazy and didn't yank it out yet in fact i just noticed i forgot to)
SpriteEffects however are horrible as the context of the origin becomes unknowable without a pre-determination of the size, which of course must be fully calculated in sequence.
Have you taken a look at charsource at all ?
Does this actually do anything to improve speed or anything at all. Or is it just a redundant and useless attempt to hack around c# value to char garbage generation. ? I still see no reason for why its in there.