Solar2D performance... again :)

I’m testing my little game on a Samsung Galaxy J3 v2017.
Sometimes the game is no longer in 60fps without me being able to really know which part is slowing down.
There are a lot of tutorials to optimize Lua, but are there tutorials to optimize Solar2D.

Is there an impact to using too many Groups?
Does a sprite not visible (isvisible=false) take time?
Is it better to create objects when you need them or is it better to create them upstream and make them visible if necessary.
Is it really useful to delete graphical objects that are no longer displayed? (actually, I use mapeditor and I create the blocks as they appear, but I never delete them, does it matter)!

I ended up not finding how precisely the “free” CPU time (so the one that includes the Lua scripts and the Solar2D engine), and surprise:

  • the touchpad alone consumes a lot of time when it is requested
  • moving a group (translation) requires openGL more time to draw than if it does not move!
  • The use of masks is very expensive.

On the other hand, huge sprites are not much more expensive than small ones.

I would like to know if you have any suggestions for improvement.
Stuff where you noticed a significant performance boost that I could implement.

Thank you

I don’t know very much about your project but from experience of optimising our games on Solar2D since many years, we’ve found that the number of display objects at any time can have a significant effect on performance.

Many of our projects use images for water drops and other similar “collections” and the biggest performance gains we have seen were from reducing the number of display objects and minimising the number of physics collisions. I also like to keep a certain number of images spawned off screen ahead of time where there’s a potential need to reuse them and then just bring them up from a “pool” when I need them.

We use many groups but we typically prepare groups before the execution of the game world and don’t create new ones during gameplay. I think if you can minimise the number of display objects, you’ll surely see improvements.

I have an article I wrote at some point about some general Lua and Solar2D performance optimisation. I can see about digging it up and adding it somewhere in readable state, some time early next week.

I’m guessing much of it will be fairly obvious.

One thing that it doesn’t touch on, which is a massive performance hog, is the user of certain blend modes, like screen.

There are some of my experiences:

  • Remove all objects is no longer available (including events), I usually manage to remove events via “finalize”.

  • Stop animations, transitions, particles when they are not displayed on the screen.

  • Limit the use of shader effects (this is one of the important causes of fps drop).

Ok thank you everybody

Remove display objects : actually that’s true that, I add new display objects when needed and I never remove them ! I’m going to do some cleaning…

Conversely, I noticed that using display.newImageRect was a disaster. the speed drops drastically before picking up slightly.

The most obvious was the display of the score
The following method, using an image sheet is a nightmare

local workScore = self.score
local decim
local px = self.xScore
for c = 1, 8 do
	decim	= workScore % 10;
	workScore = (workScore - decim) / 10

	if self.tNumberSprite[c] then self.tNumberSprite[c]:removeSelf() end
	if workScore == 0 and decim == 0 then
		self.tNumberSprite[c] = nil
	else
		self.tNumberSprite[c] = display.newImageRect(self.oScoreGroup, self.tNumberBank, decim + 1, 32, 48)
		self.tNumberSprite[c].x = px
		self.tNumberSprite[c].y = 32
	end
	px = px - 26
end

Instead I created 8 * 10 sprites (each digit at each position) which I made invisible. Then I just make visible the digits that make up my number, and the gain was enormous ! 4% on my old phone !

Maybe display.newImageRect have a catastrophique effect on OpenGL flow :man_shrugging:

Sorry Xedur, my english is not good enough to understand what you mean :sleepy:

You should use bitmap font instead of using image.

@Kan98

Hum. Can you explain ?

@e.sobole
using ponyfont, the font will be an image instead of a .ttf file.
ShoeBox to create bitmap font.

@Kan98

Hum. I don’t like to use things if I don’t understand why I should used them :slightly_smiling_face:
Even if ponywolf seem’s to know what he’s doing :grinning_face_with_smiling_eyes:

I’m using this !

since 1 hour.
I don not have million of display object.

Could the stats that you’re displaying from this script not slow things down further? We see dips in FPS when we have text displays that are constantly being updated.

That was just a reference to my article. How I don’t discuss blend modes or such in it.

Some of these blend modes, like the aforementioned screen blend more, as well as certain shaders, may use up a lot of resources.

For instance, some years ago I was working on a project that included a “spinning spotlight graphic” that was set to use the blend mode, “add”. It worked great on my computer, as it does have a dedicated GPU, but when I ran it on my ancient Android 5 device, then having the blend mode active dropped the FPS from 60 to about 20-25.

@famousdoggstudios

Yes, that’s true.
display.newText take a lot of time!

I have removed display object not used !
I wonder if it is not worst :grinning_face_with_smiling_eyes:

Maybe I should try to handle a pool of objects and reused them instead of create and remove them !

Is there anyway to change the frameIndex of a newImageRect ?
For example to change the frameIndex of a sprite from 2 to 3, actually I have to do:

tTable[id] = display.newImageRect(group, tTileBank, 2, 80, 80)
tTable[id]:removeSelf()
tTable[id] = display.newImageRect(group, tTileBank, 3, 80, 80)

If there any solution to not remove the object but tell it that the texture positon has changed ?

@XeduR

Ok :+1:

I didn’t know about blend modes.

Some basic points on performance…

  1. Masking images is VERY expensive.
  2. Invisible objects will slow things down (remove them if not required).
  3. Time is taken to navigate group hierarchies.
  4. Object pooling is a great boost to performance.
  5. Blend modes (not run on a GPU) will be slow.
  6. Shaders (not run exclusively on a GPU) will be slow. Avoid conditional statements like if where possible.
  7. Moving groups requires solar to update the geometry for all children.
  8. Text objects take time to render to bitmaps. Don’t update per frame - only when a value changes.
  9. Pause emitters/animations if off screen.
  10. Measuring performance (especially counting display objects is very slow) will slow things down.
  11. Inefficient code will slow things down.

Oh and masking many images is VERY expensive!

I could go far more low level but I doubt you need that unless you are managing 10k+ objects.

2 Likes

@anon63346430

It all seems very relevant :+1:

What do you mean by “Object pooling is a great boost to performance.”
you mean object that do not change appearance!
For example particles ?

Let suppose you have 100 objects in your pool !
What are you doing to inactivate an object without remove it ?
You set it as isVisible = false ?

Now, I have made all you said, and Masking is very expensive that’s true !
A have less than 1600 displays objects :muscle:

I had a particles système in a masked zone. That was that conjunction of events that make the Samsung J3 slow Down !

the only optimization I think I can still do is to group the 80*80 tiles into larger sprites maybe with snapshot, I don’t know yet !

Object pooling is where you don’t destroy frequently used objects right away but save them for using later.

Let’s say you are working on a shooter game where you constantly shoot bullets to the sky. Number of bullets that is visible on the screen at a given time is finite, let’s say 10. Instead of creating and destroying the bullet every single time, you create 10 bullets at max and reuse those same bullets everytime you need.

By the way, 1600 display objects? What kind of game are you working on?

@bgmadclown

Ok for pooling ! That’s what I thaugh !
Did you see ma question about changing indexFrame of a sprite ? Do you think this is possible ?

I’m making a platformer like Super Mario Bros U :slightly_smiling_face:

I just looked it up after you asked so I don’t know if it works like the way I understand it. According to the docs, you probably don’t need to remove and reload every time you need to specify a different frame. Here:

Gotchas - All loaded images are cached. To save texture memory, the image in the cache memory is used when it detects an image with the same file name is displayed. This means that loading the same image multiple times doesn’t increase the amount of texture memory used on the device. A potential side-effect to the image caching is that the comparison is based on the file name and not the file content. This means if an image file is displayed and then deleted from the directory, any file loaded after that with the same file name will still display the previous cached image. To avoid this problem, use a different file name.

@e.sobole I’m planning on creating a repo with a bunch of performance test, this would help us to understand how Lua and Solar2d performs better under different situations.

If you are interested, we can plan some of these tests.