resource_queue.gd 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. extends Node
  2. var thread
  3. var mutex
  4. var semaphore
  5. var exit_thread = false
  6. var time_max = 100 # Milliseconds.
  7. var queue = []
  8. var pending = {}
  9. func _lock(_caller):
  10. mutex.lock()
  11. func _unlock(_caller):
  12. mutex.unlock()
  13. func _post(_caller):
  14. semaphore.post()
  15. func _wait(_caller):
  16. semaphore.wait()
  17. func queue_resource(path, p_in_front = false):
  18. _lock("queue_resource")
  19. if path in pending:
  20. _unlock("queue_resource")
  21. return
  22. elif ResourceLoader.has_cached(path):
  23. var res = ResourceLoader.load(path)
  24. pending[path] = res
  25. _unlock("queue_resource")
  26. return
  27. else:
  28. var res = ResourceLoader.load_interactive(path)
  29. res.set_meta("path", path)
  30. if p_in_front:
  31. queue.insert(0, res)
  32. else:
  33. queue.push_back(res)
  34. pending[path] = res
  35. _post("queue_resource")
  36. _unlock("queue_resource")
  37. return
  38. func cancel_resource(path):
  39. _lock("cancel_resource")
  40. if path in pending:
  41. if pending[path] is ResourceInteractiveLoader:
  42. queue.erase(pending[path])
  43. pending.erase(path)
  44. _unlock("cancel_resource")
  45. func get_progress(path):
  46. _lock("get_progress")
  47. var ret = -1
  48. if path in pending:
  49. if pending[path] is ResourceInteractiveLoader:
  50. ret = float(pending[path].get_stage()) / float(pending[path].get_stage_count())
  51. else:
  52. ret = 1.0
  53. _unlock("get_progress")
  54. return ret
  55. func is_ready(path):
  56. var ret
  57. _lock("is_ready")
  58. if path in pending:
  59. ret = !(pending[path] is ResourceInteractiveLoader)
  60. else:
  61. ret = false
  62. _unlock("is_ready")
  63. return ret
  64. func _wait_for_resource(res, path):
  65. _unlock("wait_for_resource")
  66. while true:
  67. RenderingServer.force_sync()
  68. OS.delay_usec(16000) # Wait approximately 1 frame.
  69. _lock("wait_for_resource")
  70. if queue.size() == 0 || queue[0] != res:
  71. return pending[path]
  72. _unlock("wait_for_resource")
  73. func get_resource(path):
  74. _lock("get_resource")
  75. if path in pending:
  76. if pending[path] is ResourceInteractiveLoader:
  77. var res = pending[path]
  78. if res != queue[0]:
  79. var pos = queue.find(res)
  80. queue.remove(pos)
  81. queue.insert(0, res)
  82. res = _wait_for_resource(res, path)
  83. pending.erase(path)
  84. _unlock("return")
  85. return res
  86. else:
  87. var res = pending[path]
  88. pending.erase(path)
  89. _unlock("return")
  90. return res
  91. else:
  92. _unlock("return")
  93. return ResourceLoader.load(path)
  94. func thread_process():
  95. _wait("thread_process")
  96. _lock("process")
  97. while queue.size() > 0:
  98. var res = queue[0]
  99. _unlock("process_poll")
  100. var ret = res.poll()
  101. _lock("process_check_queue")
  102. if ret == ERR_FILE_EOF || ret != OK:
  103. var path = res.get_meta("path")
  104. if path in pending: # Else, it was already retrieved.
  105. pending[res.get_meta("path")] = res.get_resource()
  106. # Something might have been put at the front of the queue while
  107. # we polled, so use erase instead of remove.
  108. queue.erase(res)
  109. _unlock("process")
  110. func thread_func(_u):
  111. while true:
  112. mutex.lock()
  113. var should_exit = exit_thread # Protect with Mutex.
  114. mutex.unlock()
  115. if should_exit:
  116. break
  117. thread_process()
  118. func start():
  119. mutex = Mutex.new()
  120. semaphore = Semaphore.new()
  121. thread = Thread.new()
  122. thread.start(self, "thread_func", 0)
  123. # Triggered by calling "get_tree().quit()".
  124. func _exit_tree():
  125. mutex.lock()
  126. exit_thread = true # Protect with Mutex.
  127. mutex.unlock()
  128. # Unblock by posting.
  129. semaphore.post()
  130. # Wait until it exits.
  131. thread.wait_to_finish()