One script or multiple? Impact on performance


#21

I’d like to share my code for managing bullet like entities, hope it will help someone.

First of all - project structure:

bullet- is just an object which contain only one sprite (representing an enemy or bullet). It is created using a Factory.
Untitled-1

controller - this is a script included in your main game collection. It contains update cycle.

controller.script (5.2 KB)

I’ve included as much comments as i can. If something is not clear, feel free to ask :slight_smile:


#22

Thanks @Sublustris I’ll take a look!

@britzl Hi again! I had to come back here, I was thinking if spawinning 1024 Game Object from the same prototype, reach the limit of 1024 allowed scripts, that means the engine is duplicating the script 1024 times!? So these object are not sharing the same code? :hushed:

( I just tried similar test today with new Godot, 4,000 nodes with update script code, didn’t get any problems, and FPS was fine at 30. )


#23

They are sharing the same code but not the same state, meaning that each game object will have its own self instance/table .


#24

I’m sure you’d get 30 FPS with Defold as well but shouldn’t you aim for 60 FPS?


#25

Is a benchmark test, i set the engines to target 60fps but stress them until I reach 30fps, to see how much can handle.

I already know Defold has great performance, but I’m comparing now the unusual 1024 scripts limitation.
Now for fun tried 40,000 nodes with scripts in Godot! FPS are dead, but didn’t crash, it show all the sprites moving 1 step by second :smiley:

Not comparison-war intentions here, I spend like a week just reading Defold docs and this forum, so I already know how you answer many questions and help people. That’s why get convinced to give a try to Defold even the message system and use of strings to identify properties was pushing me back with my OOP mindset and performance concerns. This benchmark are fun for me and at the same time I start to understand how the engine works, and I believe these discussions at the end help to make a better product.


#26

Has anyone tried ECS in Defold?

@britzl can you expand on 1024 scripts per project? Is it the maximum number of actual LUA script files in a project, or a runtime limitation of only 1024 script-components being executed at runtime? Did a quick search and couldn’t find anything except (max instances count capped at 1025) max_instances is effectively capped at 1025


#27

It’s the runtime limitation of script instances being executed.


#28

@ross.grams so ideal case is having a few execution scripts over a set of objects? Say I have Lua-only objects which are not registered in the engine as script-components and I reference actual active scene objects to them. This Lua-only object count doesn’t count into this 1024 cap?


#29

What do you mean Lua-only objects? Tables? Modules?

But I think the answer is “Yes”. If you need to move around thousands of similar objects, you will want to have one script that iterates over a list and moves them all around. (like in Britzl’s bunnymark)

@pxtracer If you ever do direct performance comparisons between Defold and Godot I am very curious to hear the results. I used Godot for a while, but gave it up because it was just too slow (mostly gdscript performance, but also rendering/overdraw), but that was a while ago.


#30

I’m not very strong with Lua yet, but I’m pretty sure it’s “tables”

Please include a relative build size info on this if you decide to do a comparison. We are drawn to Defold because of its low build size overhead (and less overhead overall) compared to Unity


#31

I try to port bunnymark “update-many-in-single” benchmark to Godot.
But this script gives me 2502 draw calls (according Xcode GPU frame capture):

extends Node2D

export (PackedScene) var Bunny

const dt = 1.0/60.0

func _ready():
	randomize()
	spawn(2500)

func _process(delta):
	var children = get_children()
	for bunny in children:
		bunny.velocity += 1200 * dt
		bunny.position.y += bunny.velocity * dt
		if bunny.position.y > 1086:
			bunny.position.y = 1086
			bunny.velocity = -bunny.velocity

func spawn(amount):
	for i in range(amount):
		var bunny = Bunny.instance()
		bunny.position = Vector2(randi() % 640, randi() % 206 + 106)
		bunny.velocity = rand_range(1, 200)
		add_child(bunny)

Obviously, the line

var bunny = Bunny.instance()

creates new bunny with NEW texture.

Can anyone with Godot knowledge fix this, i.e create new bunny (scene with single “Sprite” node) with SAME texture every time?


#32

Cool! I’m all for evaluating and pushing limits! I hope you’ll enjoy Defold! As an alternative to running benchmarks I can also recommend to either try one of our tutorials or start from scratch and create a simple game of you own. I always find that working on an actual game/project/idea is the best way to learn something new.


#33

What fps did you get from that?
I think with regards to Godot, maybe the only way is to create a var with a texture through code, and add the texture to each bunny as you create them. Try that.

Compare it with the Defold bunny benchmark, and tell us how it goes.
These benchmarks arent almighty truthful, but it gives an indication of the overall reality.


#34

Already tried:

export (Texture) var bunny_texture

also:

var bunny_texture = preload("res://bunnies/rabbitv3.png")

and in _process():

var bunny = Bunny.instance()
bunny.set_texture(bunny_texture)

Nothing changed. One draw call per bunny.
There is no point to compare with Defold until this be fixed.

These benchmarks arent almighty truthful…

Agree.


#35

Well frankly, sprite batching is something that should be built into the renderer, not something the user should even think about. Provided you use the same resource(image, atlas, etc) it should all be 1 draw call and just work.

So this is likely a design flaw with Godot.


#36

Looks like Godot doesn’t support sprite/mesh batching at all, so every object will consume 1 dip. https://github.com/godotengine/godot/issues/1527
Seems like the only way around it is to make a by-hand batching every frame (or simply moving sprite mesh vertex positions) instead of using built-in functionality. But I think it’s far away from the usual approach in that engine.

What we can really use to benchmark engine performance only is to remove renderers completely - just use a bunch of objects with moving logic (without anything on screen) and see how many FPS you’ll get in each case. This will really focus on runtime performance rather on renderers functionality


#37

This seems a bit extreme and very synthetic, but if you wish to do that with Defold then run the test with the headless dmengine version (from d.defold.com).