Performance drop with scale()

Hello all,

I found that changing the scale of everything inside the draw loop at each draw cycle reduces fps to a crawl (10/20 fps) while using a constant scale factor keeps fps to 60.

For instance: scale((90+math.sin(os.clock()/2)*30)/100) drops fps to 15 fps while scale(1.1) doesn’t affect performance.

Why is this? Is there an alternative to changing the scale of a whole drawn scene without losing performance?


@Rodolphe One of them does a lot of calculations while the other doesn’t do any calculations. That will account for a difference. I tried you’re 2 scale statements in the draw function on my iPad Air and they both run at 59 to 60 FPS.

Of course, computing “90+math.sin(os.clock()/2)*30)/100” once every 60th of a sec has to account for a cpu load, but not enough to drop fps, I don’t think.

But if you tried and didn’t find a difference, then it has to be something in my draw cycle… Thanks for trying it out!

Ok, now I notice that the frame-rate seems to be correlated to the scale factor difference between two frames.

When I use a sin function to change the scale factor through time (as my above example shows), the drop seems to follow the derivative of the function. That is, I lose more fps when the slope of “90+math.sin(os.clock()/2)*30)/100” is steeper, and less when it reaches a maximum or a minimum (where the slope gets closer to zero, hence giving scale() almost the same values from one frame to another, for a short time).

So… for:

function draw()
-- lots of drawing and calculations

Here’s the evolution of fps through time for scale_factor = 90+math.sin(os.clock()/2)*30)/100:


Here’s the evolution of fps through time for scale_factor = math.random(90,110)/100:


Here’s the evolution of fps through time for scale_factor = 1.1:


Is giving highly different scale factor between two frames tougher on the GPU?

@Rodolphe It’s possible, but I can’t say for sure, is that the scaled image is buffered. In the first example, the scaled image is constantly changing so a buffered image isn’t helping. In the second example, there are only 20 different possibilities, so a buffered image might exist already and doesn’t need to be re-created. In the bottom example, the scaled image is constant and doesn’t need to be created each draw cycle. But like I said, it just a guess.

I can’t see why changing scale has that effect, because all it involves is multiplying the top left 3x3 part of modelMatrix by the scale factor, which is trivial.

And I don’t believe the image is buffered, because it would be very rare that it didn’t change, and even if it didn’t, it could be sprited easily at different sizes, something we do frequently without speed issues.

What happens if you do this?

local s=(90+math.sin(os.clock()/2)*30)/100

What exactly are you doing in your draw function?

If you make things bigger they take longer to draw. Apologies for stating the extremely obvious. I’ve only really noticed this in fairly extreme mesh profiling tests though (thousands of rects)

That’s certainly true. The more of the screen you cover with drawing, the longer it takes.

@Rodolphe I tried your scale calculation while drawing 3000 sprites. When the scale value was at the highest value, I was running at 5 FPS. When the scale value was at the lowest value, I was running at 19 FPS. If I comment out the calculation and scale command and run draw normally with 3000 sprites, I run at 7 FPS. So there is a wide range of FPS depending on the scale value.

@Rodolphe what isn’t clear from your posts is, are you scaling bigger or smaller? I’m guessing that as you’re scaling bigger, you’re getting a lower fps. But it’s not the actual scale command itself, it’s the draw that follows (ie you’d see a similar slowdown if you weren’t using scale, but you were just making the geometry bigger, eg by increasing the width and height of a setRect for instance). It’s something I’ve only really noticed with thousands of objects flying around (ie particle systems, mesh tests etc). Tell us about your draw calls, there could be a lot of room for optimisation there.

@yojimbo2000 From the calculation in the scale command he gave above, the value cycles from .6 to 1.2 .

@Ignatz, local s=(90+math.sin(os.clock()/2)*30)/100 has the same effect.
Thanks for the test @dave1707, that gives us a clue on what’s going on I guess…
@yojimbo2000 my draw calls are plenty and not very optimized I’m sure… Lots of translates, A few large sprites, lot’s (40/50) of meshes, iterations through physics objects, etc. It’s messy and (over?) complicated, but it runs a smooth 60 fps on my old-ish ipad retina, so I was surprised a “simple” scaling on the whole scene would break the performance.

Generally speaking, use meshes (rather than sprites, primitive shapes etc), and try to get as much geometry onto one mesh as is practical (ie, if it’s 2D, using addRect setRect with texture atlases) in order to cut down the number of draw calls.

Indeed, my project (in churning seas) could use a deep refactoring/optimizing. It’s my first project and I have no clue beyond intuition as to what makes fast rendered graphics… Your tips are noted @yojimbo2000 :wink: