I’m currently trying to move some computing to a separate thread, but I get an exception, EXCEPTION_ACCESS_VIOLATION reading address 0x0000014a00000152, and the line where the error occurs in code, but nothing really useful for debuging.
It occurs when I’m doing a RayCast by calling the LineTraceSingleByChannel method, with a pointer to an Actor present in the scene from a separate thread. The pointer is not null.
Here is the global flow :
My Actor calls a method on its component
Its component creates a Thread by creating a new Object from a class inheriting from FRunnable. It gives to the constructor several attributes, including a Pointer to the initial Actor. Then calls the static method Create of FRunnableThread.
The created thread executes its Init method, in which it creates X pure C++ objects (the class isn’t inheriting from anything, just C++).
Each created C++ Object get the Pointer to the initial Actor, and uses it to call the LineTraceSingleByChannel method, (Pointer->GetWorld()->LineTraceSingleByChannel(…)). Since it’s not an object placed in World, it can’t RayCast by itself (and the LineTrace method inside UKismetSystemLibrary needs a WorldReference anyway), hence the pointer to the initial Actor.
Crashes when calling the LineTrace, with the above error.
Is there any read/write conflict occuring because I’m trying to do things with a pointer to an Object in the scene inside a separate thread ? Or am I missing something else ?
When I’m doing the exact same thing, but without creating a thread to handle it, it works perfectly.
I wouldn’t assume a-priori you can safely raycast on worker threads, have you seen docs or other sources saying you can? If you want to spend less game thread time on raycasts, use AsyncLineTrace, which gets the result ready for the start of the next frame.
I didn’t find anything saying I can or I can’t, so I tried. But I would say that’s indeed not possible. I read this page Traces in Unreal Engine - Overview | Unreal Engine 5.0 Documentation but nothing about it.
Regarding AsyncLineTrace, it seems a good approach but I don’t know if I can do it in a for loop. I have several instances of my C++ objects, with different coordinates and rotations, and each of them need to perform a RayCast. I can try to give each of them a different delegate to be called afterward, for example.
Looks like the async call returns an FTraceHandle which will let you uniquely identify each trace. This also gets passed in the callback, so you can check which trace you’re getting a result for. But I think what I’d do for your case is store the trace handles in an array, and ignore the delegate (you don’t have to provide one). You can use UWorld::QueryTraceData in a loop to get last frame’s trace results, then clear the array and start the traces for the next frame, storing the trace handles.
I managed to make something with the FTraceHandle returned by the Async call and UWorld::QueryTraceData to retrieve and use the result, and it works surprisingly well !
Do you think there can be cases where the result can’t be ready for the next frame (due to numerous/complex RayCast for instance), and will be ready for the next frame(s) after ? Or it forces to be ready, and can induce framerate drop ?
I wouldn’t stake my life on it but I believe it’ll be the latter - it’ll do every trace you ask for, and you’ll induce a framerate drop if you make extremely heavy use of the async tracing. I say that because the handles are only valid for the next frame, if you try to use them in later frames it won’t work.