TL;DR: How can I query Physics2DDirectSpaceState from a thread a whole bunch of times.
When I first load a new level (as a scene) as part of the initialization of that level I want to make many, many queries to Physics2DDirectSpaceState. To handle doing this I make a thread that belongs to the parent of the level which runs initialization functions. This is the script of the thread that is created and owned by the parent:
var queue = []
var worker_semaphore
var worker_mutex
var worker_thread
var run_worker_thread
func stop():
worker_semaphore.post()
worker_mutex.lock()
run_worker_thread = false
worker_mutex.unlock()
worker_thread.wait_to_finish()
func post_function(node, f_name, args):
worker_mutex.lock()
queue.push_back({
'n' : node,
'f' : f_name,
'a' : args
})
worker_mutex.unlock()
worker_semaphore.post()
func _worker_thread_process(u):
while run_worker_thread:
worker_semaphore.wait()
worker_mutex.lock()
if queue.size() > 0:
var current_function_set = queue[0]
var node = current_function_set['n']
var thread_func = current_function_set['f']
var thread_args = current_function_set['a']
worker_mutex.unlock()
# call given function
# is this thread safe?
node.callv(thread_func, thread_args)
worker_mutex.lock()
queue.erase(current_function_set)
worker_mutex.unlock()
node.emit_signal("init_finished")
worker_mutex.unlock()
func start():
# start up worker thread
worker_semaphore = Semaphore.new()
worker_mutex = Mutex.new()
worker_thread = Thread.new()
run_worker_thread = true
var err = worker_thread.start(self, "_worker_thread_process", null, Thread.PRIORITY_NORMAL)
if err != OK:
print("ERROR WITH STARTING WORKER THREAD")
Then levels that are being initialized post the functions that they want initialized and then wait until the thread finishes with that function and emit the init_finished
signal which tells the parent that the new level is done initializing and is ready to be used:
var space_state
signal init_finished
func _ready():
# get direct space state outside of thread
space_state = get_world_2d().direct_space_state
# tell parent to give this level's function to its thread
get_parent().post_function(self, "_init", [])
# simplified init function
func _init():
# prepare physics query
var shape_query = Physics2DShapeQueryParameters.new()
shape_query.set_collide_with_areas(true)
shape_query.set_collide_with_bodies(true)
shape_query.set_collision_layer(524288)
var shape = CircleShape2D.new()
shape.set_radius(8)
shape_query.set_shape(shape)
# do a whole bunch of physics queries (anywhere from 20,000 to 200,000)
for idx in range(20000):
var result = space_state.intersect_shape(shape_query, 1)
if result:
# do something
pass
The issue is that my game crashes if I run too many queries at once with the message: ERROR: CowData<class RID>::get: FATAL: Index p_index=0 out of size (size()=0)
. My guess is:
The physics thread is simply overwhelmed by my queries during a single frame and blows up.
In this case should I wait for another frame to finish making my queries? Or maybe sink
the user thread to the main thread every once in a while?
I would like to know if the Physics2DDirectSpaceState tries to handle any queries its given during one frame, or if it handles queries some other way.
If you think that I am crashing for another reason or one of my assumptions is wrong, I would consider responses to those issues very greatly as well.