class_name ResourceQueue var thread var mutex var sem var threaded = true var time_max = 100 # Milliseconds. var queue = [] var pending = {} func _lock(_caller): mutex.lock() func _unlock(_caller): mutex.unlock() func _post(_caller): sem.post() func _wait(_caller): sem.wait() func queue_resource(path, p_in_front = false): _lock("queue_resource") if path in pending: _unlock("queue_resource") return elif ResourceLoader.has_cached(path): var res = ResourceLoader.load(path) pending[path] = res _unlock("queue_resource") return else: var res = ResourceLoader.load_interactive(path) res.set_meta("path", path) if p_in_front: queue.insert(0, res) else: queue.push_back(res) pending[path] = res _post("queue_resource") _unlock("queue_resource") return func cancel_resource(path): _lock("cancel_resource") if path in pending: if pending[path] is ResourceInteractiveLoader: queue.erase(pending[path]) pending.erase(path) _unlock("cancel_resource") func get_progress(path): if not threaded: return 1.0 _lock("get_progress") var ret = -1 if path in pending: if pending[path] is ResourceInteractiveLoader: ret = float(pending[path].get_stage()) / float(pending[path].get_stage_count()) else: ret = 1.0 _unlock("get_progress") return ret func is_ready(path): if not threaded: return true var ret _lock("is_ready") if path in pending: ret = !(pending[path] is ResourceInteractiveLoader) else: ret = false _unlock("is_ready") return ret func _wait_for_resource(res, path): if not threaded: return get_resource(path) _unlock("wait_for_resource") while true: VisualServer.sync() OS.delay_usec(16000) # Wait approximately 1 frame. _lock("wait_for_resource") if queue.size() == 0 || queue[0] != res: return pending[path] _unlock("wait_for_resource") func get_resource(path): if not threaded: return load(path) _lock("get_resource") if path in pending: if pending[path] is ResourceInteractiveLoader: var res = pending[path] if res != queue[0]: var pos = queue.find(res) queue.remove(pos) queue.insert(0, res) res = _wait_for_resource(res, path) pending.erase(path) _unlock("return") return res else: var res = pending[path] pending.erase(path) _unlock("return") return res else: _unlock("return") return ResourceLoader.load(path) func thread_process(): _wait("thread_process") _lock("process") while queue.size() > 0: var res = queue[0] _unlock("process_poll") var ret = res.poll() _lock("process_check_queue") if ret == ERR_FILE_EOF || ret != OK: var path = res.get_meta("path") if path in pending: # Else, it was already retrieved. pending[res.get_meta("path")] = res.get_resource() # Something might have been put at the front of the queue while # we polled, so use erase instead of remove. queue.erase(res) _unlock("process") func thread_func(_u): while true: thread_process() func start(use_threads: bool): threaded = use_threads if not threaded: return mutex = Mutex.new() sem = Semaphore.new() thread = Thread.new() thread.start(self, "thread_func", 0)