0 votes

I have a splash screen during which I load my levels in a separate thread.

Basically, during my splash animation, I have a separate thread which runs through a queue of level paths and loads them using an InteractiveLoader.

var queue = ["res://levels/level1.tscn", "res://levels/level2.tscn", "res://levels/level3.tscn"]

The code starts with queue[0], calls loader.poll() until the result is ERR_FILE_EOF, at which point it collects the resource into a dictionary, and then moves on to the next element of the queue. When the queue is exhausted, we close the thread and then move from the splash screen to the main menu.

This was working like a charm for a while. But just yesterday I created a new level and noticed that the process no longer finishes.

After much troubleshooting, I think I've determined that it has something to do with size. If the queue is too large, the process simply stops at a certain point. I checked this by creating copies of some very simple levels. I just copied them again and again until the process failed. I also confirmed it doesn't have to do with a specific level, because I tried this with multiple different simple levels.

For example, here is some debugging output showing that when my queue index is changed to 83, the process just hangs up, rather than moving on to finish the entire queue, which has a size of 91.

enter image description here

I also trimmed my levels down to just 83 and confirmed by removing nodes of this level that a single node could be the difference between the process completing and not completing. So it does seem like the issue has something to do with size...

Does anyone have any idea why my process might hang up like this after loading a certain number of levels?

I am posting my code below to provide some additional context. For debugging purposes, rather than background load during the animation, I simply start the thread after, and wait for it to finish before opening the main menu.

extends CanvasLayer

# background loading
var loader
var queue
var index
var target_level_path
var loading_thread
var is_loading = false
signal background_loading_complete

func _ready():
    # disable gui input -- NOTE: this prevents opening pause during splash screen
    get_tree().get_root().gui_disable_input = true


func _on_AnimationPlayer_animation_finished(anim_name):
    # start loading thread
    loading_thread = Thread.new()
    loading_thread.start(self, "load_data", "nominal_argument")

func load_data(nominal_argument):
    initialize_background_loading()

func initialize_background_loading():

    # track is_loading
    is_loading = true

    # set initial queue
    queue = []

    # fill queue -- NOTE: queue is just a list of level paths to be loaded
    var level_paths = Utility.get_files_in_directory("res://levels")
    for level_path in level_paths:
        queue.append("res://levels/" + level_path)

    # set initial index
    index = 0

    # start background loading
    background_load(index)


func background_load(i):
    print("INDEX: ", index, " QUEUE SIZE: ", queue.size())
    # if done loading, stop
    if i == queue.size():
        # track is_loading
        is_loading = false
        # PRINT - TEST - DEBUG
        print("Done background loading.")
        # emit signal
        emit_signal("background_loading_complete")
        # close thread
        loading_thread.wait_to_finish()

    # otherwise, load next
    else:
        target_level_path = queue[i]
        loader = ResourceLoader.load_interactive(target_level_path)
        load_step()

func load_step():
    # advance the load
    var status = loader.poll()
    print("TARGET LEVEL: ", target_level_path, " POLL STATUS: ", status)

    # check status 
    match status:
        # if done, get resource and attempt to start loading the the next in queue
        ERR_FILE_EOF:
            # get resource
            Globals.preloaded_level_dict[target_level_path] = loader.get_resource()
            # load next in queue
            index += 1
            print("CHANGED QUEUE INDEX TO ", index)
            background_load(index)
        #  otherwise, just advance the load
        OK:
            load_step()
        _:
            print(status)

func _on_SplashScreen_background_loading_complete():
    # open main menu
    Transition.transition([], funcref(Menus.main_menu, "open"), null, true, true, true, true, "fade", "fade")
    queue_free()
in Engine by (1,548 points)
edited by

1 Answer

+1 vote
Best answer

Hi Diet,

You seem to be calling loadingthread.waitto_finish() from loading thread itself, which will deadlock the thread. You should call it from the main thread after you have finished loading. Also, why wouldn't you just use a for loop to go through the queue instead those awkward recursive calls? Something like:

func loading_thread_func(userdata):
    # this is executed on the loading thread
    for path in queue:
        load_data(path)
    call_deferred("on_exiting_thread")

func on_exiting_thread():
    # this is executed on the main thread
    loading_thread.wait_to_finish()
    emit_signal("background_loading_complete")
by (46 points)
selected by

I can't thank you enough for this suggestion! I've got the thread working now using your simplified procedure. Thanks a ton!

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.
Social login is currently unavailable. If you've previously logged in with a Facebook or GitHub account, use the I forgot my password link in the login box to set a password for your account. If you still can't access your account, send an email to webmaster@godotengine.org with your username.