Tips about optimising performance

General discussion about LÖVE, Lua, game development, puns, and unicorns.
Post Reply
ddabrahim
Prole
Posts: 9
Joined: Mon May 17, 2021 8:05 pm
Contact:

Tips about optimising performance

Post by ddabrahim »

Hi.

I am a complete beginner with both LOVE and Lua.
When I learn a new framework/engine, the first thing I always do is a raw performance test where I move and rotate around a bunch of sprites on the screen and check distance from an object that follow the mouse, once any of the moving objects get close to the mouse, I remove it from rendering and delete it.

In this test, LOVE did not performed too well compared to others. I was getting only 30FPS with 'just' 10k sprites on the screen NOT moving. Not that I need this many, but I do like to know the limits the framework has that I am using, and other frameworks do perform a lot better in similar scenario. With other frameworks, usually I get 30FPS with 20k-100k sprites MOVING on the screen on same hardware.

I am using only a for loop to iterate through each and every sprite stored in a table to draw them on the screen and compare distance and remove them from the table. It is obviously not optimal but some frameworks even this way perform 3x better than LOVE, so wondering if is there any technique I could use to get better results with LOVE in this simple test?

For one, I was thinking about batch rendering, would that be possible to draw all sprites with a single draw call for example instead of a for loop?
An other thing I was thinking about is to iterate through each instance in update asynchronously and just use a global in the draw callback function, would something like that be possible, useful?
Or any other ideas?

Here is the code to show what I'm doing:
https://gofile.io/d/Ibvlfl

I would appreciate any help.
Thanks.
Last edited by ddabrahim on Mon May 31, 2021 9:09 pm, edited 1 time in total.
grump
Party member
Posts: 749
Joined: Sat Jul 22, 2017 7:43 pm

Re: Tips about optimising performance

Post by grump »

If you posted your test code, then people could tell you why you're doing it wrong.
User avatar
zorg
Party member
Posts: 3096
Joined: Thu Dec 13, 2012 2:55 pm
Location: Absurdistan, Hungary
Contact:

Re: Tips about optimising performance

Post by zorg »

What grump said; some more info: löve already uses auto-batching (unless you do something that breaks said batching), or you can use SpriteBatch objects to do batching and draw the batch itself with one call.
Me and my stuff :3True Neutral Aspirant. Why, yes, i do indeed enjoy sarcastically correcting others when they make the most blatant of spelling mistakes. No bullying or trolling the innocent tho.
ddabrahim
Prole
Posts: 9
Joined: Mon May 17, 2021 8:05 pm
Contact:

Re: Tips about optimising performance

Post by ddabrahim »

grump wrote: Mon May 31, 2021 7:01 pm If you posted your test code, then people could tell you why you're doing it wrong.
Done, I have updated the original post with a link:
https://gofile.io/d/Ibvlfl
ddabrahim
Prole
Posts: 9
Joined: Mon May 17, 2021 8:05 pm
Contact:

Re: Tips about optimising performance

Post by ddabrahim »

zorg wrote: Mon May 31, 2021 7:35 pm What grump said; some more info: löve already uses auto-batching (unless you do something that breaks said batching), or you can use SpriteBatch objects to do batching and draw the batch itself with one call.
Thanks, SpriteBatch sounds like what I am looking for, I'll look in to it.
grump
Party member
Posts: 749
Joined: Sat Jul 22, 2017 7:43 pm

Re: Tips about optimising performance

Post by grump »

Those are not really sprites. The geometry for 20,000 circles drawn in immediate mode has to be constructed, transformed and transmitted to the GPU in every frame. At 30 fps, that's maybe 15,000,000 vertices per second streamed to the GPU, possibly more.

If you draw one pair of circles to a Canvas, which makes it an actual sprite, and use that in a SpriteBatch it should be faster. Or you can make a mesh and use drawInstanced, but that's more complicated.
togFox
Party member
Posts: 207
Joined: Sat Jan 30, 2021 9:46 am

Re: Tips about optimising performance

Post by togFox »

Or use actual sprites maybe (I didn't see the code but assuming code uses love.draw.circle)
ddabrahim
Prole
Posts: 9
Joined: Mon May 17, 2021 8:05 pm
Contact:

Re: Tips about optimising performance

Post by ddabrahim »

grump wrote: Mon May 31, 2021 9:52 pm draw one pair of circles to a Canvas, which makes it an actual sprite, and use that in a SpriteBatch it should be faster. Or you can make a mesh and use drawInstanced, but that's more complicated.
Thank you. I'm going to look in to that.
togFox wrote: Tue Jun 01, 2021 6:07 am Or use actual sprites maybe (I didn't see the code but assuming code uses love.draw.circle)
I also tried to draw images using love.graphics.draw() in case that is what you mean, but I was getting similar performance, did not make much difference.
User avatar
Nikki
Prole
Posts: 41
Joined: Wed Jan 25, 2017 5:42 pm

Re: Tips about optimising performance

Post by Nikki »

You could use a Canvas (aka render to Texture)

the existing code on my low end machine starts at 30 fps, the canvas code below 200 fps

Code: Select all


local player = {}
local coins = {}

function love.load(arg)
   love.window.setVSync( false )
   player.x = 100
   player.y = 100
   player.radius = 30
   player.draw = function()
      player.x = love.mouse.getX()
      player.y = love.mouse.getY()
      love.graphics.setColor(1,1,1)
      love.graphics.circle('fill',player.x, player.y, player.radius)
      love.graphics.setColor(1,0,0)
      love.graphics.circle('line',player.x, player.y, player.radius)
   end
   player.distanceFrom = function(_other)
      local horizontal_distance = player.x - _other.x
      local vertical_distance = player.y - _other.y
      
      local a = horizontal_distance * horizontal_distance
      local b = vertical_distance ^2

      local c = a + b
      local distance = math.sqrt(c)
      
      return distance
   end
   
   coinRadius = 20
   canvas = love.graphics.newCanvas(coinRadius*2, coinRadius*2)
   

   love.graphics.setCanvas(canvas)
   love.graphics.clear()
   love.graphics.setBlendMode("alpha")
   love.graphics.setColor(0.7,0.5,0)
   love.graphics.circle('fill',coinRadius, coinRadius, coinRadius)
   love.graphics.setColor(1,1,1)
   love.graphics.circle('line',coinRadius, coinRadius, coinRadius)
   love.graphics.setCanvas()
   
   for i =1,10000,1 do
      table.insert(coins,addCoin())
   end
   
end

function love.update(dt)
   
   for i,coin in ipairs(coins) do
      if player.distanceFrom(coin) <= player.radius + coinRadius then
         table.remove(coins,i)
      end
   end
   
end

function love.draw()
   
   player.draw()
   love.graphics.setColor(1,1,1)
   love.graphics.setBlendMode("alpha", "premultiplied")
   for i,coin in ipairs(coins) do
      coin.draw()
   end
   love.graphics.setBlendMode("alpha")
   
   love.graphics.setColor(1,0,0)
   love.graphics.print(love.timer.getFPS())
end

function addCoin()
   local coin = {}
   coin.x = math.random(0,1000)
   coin.y = math.random(0,700)
   
   coin.draw = function()
      love.graphics.draw(canvas, coin.x, coin.y)
   end
   
   return coin
end

edit:
and removing the coin.draw() function
to just do that render code inline gives me another 40fps

Code: Select all

for i,coin in ipairs(coins) do
      love.graphics.draw(canvas, coin.x, coin.y)
end
   
ddabrahim
Prole
Posts: 9
Joined: Mon May 17, 2021 8:05 pm
Contact:

Re: Tips about optimising performance

Post by ddabrahim »

Thanks a lot @Nikki, it was very useful, was not sure what is the best way to use canvas to reduce the number of draw calls.
An other thing I also did after is to draw only what is actually on the screen and even after I implement a camera and move around I get decent performance now.
Post Reply

Who is online

Users browsing this forum: Google [Bot] and 16 guests