Skip to content

Commit

Permalink
fix: moves resource caching into _process method and out of a separat…
Browse files Browse the repository at this point in the history
…e thread to avoid odd resource/RID loading issues, as noted here: godotengine/godot#63493
  • Loading branch information
BHSDuncan authored and StraToN committed Apr 22, 2023
1 parent 7e8c2ef commit 508e6f9
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 83 deletions.
113 changes: 31 additions & 82 deletions addons/escoria-core/game/core-scripts/esc_resource_cache.gd
Original file line number Diff line number Diff line change
@@ -1,47 +1,24 @@
# A cache for resources
extends Reference
extends Node
class_name ESCResourceCache

var thread: Thread
var mutex: Mutex
var sem: Semaphore

var queue: Array = []
var pending: Dictionary = {}

signal resource_loading_progress(path, progress)
signal resource_loading_done(path)
signal resource_queue_progress(queue_size)


#warning-ignore:unused_argument
func _lock(caller):
mutex.lock()

#warning-ignore:unused_argument
func _unlock(caller):
mutex.unlock()

#warning-ignore:unused_argument
func _post(caller):
sem.post()

#warning-ignore:unused_argument
func _wait(caller):
sem.wait()
var queue: Array = []
var pending: Dictionary = {}


func queue_resource(path: String, p_in_front: bool = false, p_permanent: bool = false):
_lock("queue_resource")
if path in pending:
_unlock("queue_resource")
return

elif ResourceLoader.has(path):
var res = ResourceLoader.load(path)
pending[path] = ESCResourceDescriptor.new(res, p_permanent)
_unlock("queue_resource")
return
else:
var res = ResourceLoader.load_interactive(path)
res.set_meta("path", path)
Expand All @@ -50,33 +27,25 @@ func queue_resource(path: String, p_in_front: bool = false, p_permanent: bool =
else:
queue.push_back(res)
pending[path] = ESCResourceDescriptor.new(res, p_permanent)
_post("queue_resource")
_unlock("queue_resource")
return


func cancel_resource(path):
_lock("cancel_resource")
if path in pending:
if pending[path].res is ResourceInteractiveLoader:
queue.erase(pending[path].res)
pending.erase(path)
_unlock("cancel_resource")

func clear():
_lock("clear")

func clear():
for p in pending.keys():
if pending[p].permanent:
continue
cancel_resource(p)
#queue = []
#pending = {}

_unlock("clear")


func get_progress(path):
_lock("get_progress")
var ret = -1
if path in pending:
if pending[path].res is ResourceInteractiveLoader:
Expand All @@ -85,36 +54,32 @@ func get_progress(path):
ret = 1.0
emit_signal("resource_loading_done", path)
emit_signal("resource_loading_progress", path, ret)
_unlock("get_progress")

return ret


func is_ready(path):
var ret
_lock("is_ready")

if path in pending:
ret = !(pending[path].res is ResourceInteractiveLoader)
else:
ret = false

_unlock("is_ready")

return ret


func _wait_for_resource(res, path):
_unlock("wait_for_resource")
while true:
#VisualServer.call("sync") # workaround because sync is a keyword
VisualServer.force_sync()
OS.delay_usec(16000) # wait 1 frame
_lock("wait_for_resource")

if queue.size() == 0 || queue[0] != res:
return pending[path].res
_unlock("wait_for_resource")


func get_resource(path):
_lock("get_resource")
if path in pending:
if pending[path].res is ResourceInteractiveLoader:
var res = pending[path].res
Expand All @@ -127,17 +92,16 @@ func get_resource(path):

if !pending[path].permanent:
pending.erase(path)
_unlock("return")

return res

else:
var res = pending[path].res
if !pending[path].permanent:
pending.erase(path)
_unlock("return")

return res
else:
_unlock("return")
# We can't use ESCProjectSettingsManager here since this method
# can be called from escoria._init()
if not ProjectSettings.get_setting("escoria/platform/skip_cache"):
Expand All @@ -146,53 +110,38 @@ func get_resource(path):
return res
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")

var path = res.get_meta("path")
if ret == ERR_FILE_EOF || ret != OK:
printt("finished loading ", path)
if path in pending: # else it was already retrieved
pending[res.get_meta("path")].res = res.get_resource()

queue.erase(res) # something might have been put at the front of the queue while we polled, so use erase instead of remove
emit_signal("resource_queue_progress", queue.size())

get_progress(path)

_unlock("process")

#warning-ignore:unused_argument
func thread_func(u):
while true:
thread_process()

func print_progress(p_path, p_progress):
printt(p_path, "loading", round(p_progress * 100), "%")


func res_loaded(p_path):
printt("loaded resource", p_path)


func print_queue_progress(p_queue_size):
printt("queue size:", p_queue_size)

func start():
mutex = Mutex.new()
sem = Semaphore.new()
thread = Thread.new()
thread.start(self, "thread_func", 0)


func start():
pass
## Uncomment these for debug, or wait for someone to implement log levels
# connect("resource_loading_progress", self, "print_progress")
# connect("resource_loading_done", self, "res_loaded")
# connect("resource_queue_progress", self, "print_queue_progress")


func _process(_delta) -> void:
while queue.size() > 0:
var res = queue[0]

var ret = res.poll()

var path = res.get_meta("path")
if ret == ERR_FILE_EOF || ret != OK:
printt("finished loading ", path)
if path in pending: # else it was already retrieved
pending[res.get_meta("path")].res = res.get_resource()

queue.erase(res) # something might have been put at the front of the queue while we polled, so use erase instead of remove
emit_signal("resource_queue_progress", queue.size())
3 changes: 2 additions & 1 deletion addons/escoria-core/game/escoria.gd
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ func _init():
escoria.object_manager = ESCObjectManager.new()
escoria.command_registry = ESCCommandRegistry.new()
escoria.resource_cache = ESCResourceCache.new()
escoria.resource_cache.start()
escoria.save_manager = ESCSaveManager.new()
escoria.inputs_manager = ESCInputsManager.new()
escoria.settings_manager = ESCSettingsManager.new()
Expand All @@ -45,6 +44,8 @@ func _init():

# Load settings
func _ready():
add_child(escoria.resource_cache)

_handle_direct_scene_run()

escoria.settings_manager.load_settings()
Expand Down

0 comments on commit 508e6f9

Please sign in to comment.