+9 votes

I had a problem earlier in Godot, wherein it was taking me several ms (almost 10!) to instance a simple scene. I eventually discovered the root cause: instancing nodes from a thread seemed to incur a serious performance penalty. Using my network client as an example:

const MESSAGE_RECEIVED = "message_received"
var thread = Thread.new()

func start():
  thread.start(self, '_run', null)

func _run():
 while true:
   var msg = _block_while_waiting_for_tcp_message()
   emit_signal(MESSAGE_RECEIVED, msg)

Code that connected to MESSAGE_RECEIVED and instanced a scene based on the data in the message took anywhere from 10-30ms to instance an object. Instancing 500 scenes like this took about 11 seconds.

However, changing this line:
emit_signal(MESSAGE_RECEIVED, msg)
to this:
call_deferred("emit_signal", MESSAGE_RECEIVED, msg)
gave me the expected result, and was able to render all 500 scenes within 100ms.

My assumption is that calling PackedScene.instance() inside of a thread incurs a context switch (background thread -> main thread) before the scene is instanced.

call_deferred on the other hand might simply perform that context switch once, then process all of its messages, which would explain the great improvement performance-wise. Docs are a little scare on the semantics of all the function calls involved, though.

Does anyone know if my understanding of call_deferred is correct?

asked Aug 24, 2016 in Engine by Arron Washington (35 points)

1 Answer

+8 votes
Best answer

I believe call_deferred pushes the message and its data in a queue that is dequeued in the main thread, involving only one mutex lock.
Using emit_signal directly from the thread has the risk of messing with the main thread, and I think Godot is locking too much concurrent mutexes to instance the scene in that case, involving a huge performance loss.

However I can't find such explanation in the doc, so I can be wrong.

Another usage of call_deferred is to postpone the call of a function instead of executing it immediately, so for example you can use it in _ready to create more child nodes (because at this point the engine is locking the tree).

answered Aug 24, 2016 by Zylann (25,829 points)
edited Feb 13, 2018 by Zylann

After browsing through the source for quite a bit, looks like this is more or less the situation.

Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read How to use this Q&A? before posting your first questions.