Bug on free() present in release but not in-editor

Godot Version

godot-steam 4.2.2 stable

Question

I am trying to create a scene that displays assets to pre-compile shaders. It all works fine i the editor but when I create a release build I get very unspecific “user errors”

USER ERROR: Signal 'caches_cleared' is already connected to given callable '' in that object.
   at: connect (core/object/object.cpp:1358)
USER ERROR: Signal 'animation_list_changed' is already connected to given callable '' in that object.
   at: connect (core/object/object.cpp:1358)
USER ERROR: Attempt to disconnect a nonexistent connection from '<AnimationLibrary#-9223371938708059408>'. Signal: 'animation_added', callable: ''.
   at: _disconnect (core/object/object.cpp:1420)
USER ERROR: Attempt to disconnect a nonexistent connection from '<AnimationLibrary#-9223371938708059408>'. Signal: 'animation_removed', callable: ''.
   at: _disconnect (core/object/object.cpp:1420)
USER ERROR: Signal 'animation_added' is already connected to given callable '' in that object.
   at: connect (core/object/object.cpp:1358)


But here it is running perfectly fine in the editor:

Node structure:

My code here is whack since I have been trying to get this to work for days, I am using timers because I thought it was an issue where Godot had not finished setting up the nodes when I tried to free them but no luck.

@onready var MainMenu: PackedScene = preload("res://scenes/menus/main_menu.tscn")

@onready var cam: Camera3D = $cam
@onready var progress_label: Label = $_/_/progress_label
@onready var master_volume = Settings.get_bus_volume("Master")

@onready var shader_objects: Array[Node] = $shader_registry.get_children()

var _carry: int = 0


func _ready():
	$fade_animation.fade_out()
	AudioServer.set_bus_volume_db(AudioServer.get_bus_index("Master"), -60)


func finish_compile():
	progress_label.text = '100%'
	await $fade_animation.fade_in()
	
	Settings.set_bus_volume("Master", master_volume)
	
	get_parent().call_deferred("add_child", MainMenu.instantiate())
	music_handler.play()
	
	$free_timer.start()


func compile_one():
	if _carry > 0:
		var _prev_obj = shader_objects[_carry - 1]
		if _prev_obj:
			print("SHADER_COMPILE - freeing %s" % _prev_obj.name)
			_prev_obj.free()
		
	if _carry == shader_objects.size():
		finish_compile()
		return
	
	var _obj = shader_objects[_carry]
	if !_obj:
		_carry += 1
		$render_timer.start()
		return
	
	print("SHADER_COMPILE - showing %s" % _obj.name)
	_obj.process_mode = Node.PROCESS_MODE_ALWAYS
	_obj.show()
	cam.global_position = _obj.global_position + Vector3(0, 0, 5)
	cam.look_at(_obj.global_position)
	progress_label.text = str(int(((float(_carry) / float(shader_objects.size())) * 100.0))) + '%'
	_carry += 1
	$render_timer.start()

The $render_timer is an autostart set to .1s, the timeout() is connected to compile_one() - so it’s a convoluted and voluntarily slow loop on all the elements of shader_objects.

The shader_objects are hidden and in PROCESS_MODE_DISABLED to avoid entities interacting with each-other.

This is a terrible way to do this, I know - it’s just where I ended up when trying to debug this.

Also note that the system does what I want it to do, the issue is that in the release this randomly crashes the game :upside_down_face:

So there - if anyone has any clue that’s be great

What happens if you use queue_free() instead?

Same, same …

This said I am merely suspecting that it’s the free-ing that is causing an issue, I can’t check with step-by-step in the editor since the issue arises only outside the editor so it’s only conjecture …

Okay, yeah i dont disagree. I have had release export issues like this too. When in release the binary will run more efficiently potentially running into race conditions.

I was wondering if it was completely necessary to do each step. Other than instantiating the scene and adding it to the scene tree. Does it really require pointing a camera at the material to get it to compile?

Anyway. From what i understand you already have all your objects instanced in a scene and they are in the tree. With processing disabled. You enable show and put a camera on it. 100ms later you free. That should be 7 frames almost.

I wonder if 100ms is enough time. Im also curious on the implementations of the nodes experiencing this issue on signal connections. It could be that the real problem isnt here, but in that specific shader object and maybe just freeing it at the wrong time, that wasn’t apparent in a debug build.

The strange bit is that the error shows empty named callables. Which to me says invalid data somewhere.

2 Likes

As far as i can tell in the source code, once an object is initialized it will call deferred to update its shader which will compile it. I dont think there is a need to point a camera at it. This is only true in 4.3.

I hope I did not come off aggressive, apologies if I did :grimacing:

I am not totally sure, as you say this changes with 4.3 and I might upgrade versions anyway … The code I shared was the Nth attempt at getting this to be stable, so I did a bunch of random things -just short of sacrificing a goat- to get this sorcery to go away

I did try with up to a whole second per object (and yes you perfectly understand what I was trying to do) My understanding was that forcing the render of a shader/particle effect is the way to prevent shader-compile stutters when in-game. I am not an expert in this at all and this is just based on some googling and a video from “Blood Thief” dev Blargis - I still have a lot to learn :sweat_smile:

I think you are right, I associated my stability issues with this “shader compiling” feature because I noticed the crashed after I added it, but I might just be barking at the wrong tree

I will be looking at the sub-scenes and see if I can find a problem somewhere else, and will update this thread if I find anything.

At any rate thank you for your time :pray:

MultiplayerSynchronizers.

It was MultiplayerSynchronizers in some of the sub-scenes that -when freed- tried to disconnect their signals.

I presume some of the connecting must be done by the spawner, which was not present in this scene. Either that or it may be a bug specific to my GodotSteam version.

Anyway @pennyloafers was right, I was just not looking at the right place.

Not at all! I was hoping for an easy fix, but im also a very stoic person anyway. The unchanged outcome just meant i needed to think more critically about the problem.

I tried to follow the 4.2.2 code pathways for shader compiling, but it gets lost in lots of abstraction. The shaders most likely get compiled by the render server at last possible moment. I dont know the server very well so it could be likely that it would require it to be visible in front of a camera just to get them to compile.

im glad i could be of use. :upside_down_face:

1 Like