I want to recognize ArUco Marker in Unity3D and attach a GameObject to their position. I know there are packages in the Asset Store, but as other people got it working I was looking for an free existing solution or try it by myself.
ArucoUnity The detection worked like a charm, but Unity crashes when accessing the data of the rvecs, tvecs. The error somewhere occur in the Get(int i) in the Vec3d class, when the c++ method au_cv_Vec3d_get(CppPtr, i, CppPtr); is called
OpenCv plus Unity This implementation seems not to be complete as there exist no function to EstimatePoseSingleMarker or similar to get the rvecs and tvecs. Also the last updated was Jan 2019.
As Unity (C#) provides the possibility to access unmanaged code, I followed this great tutorial and for the begging I was able to forward the cv::VideoCaputre stream to Unity. The only problem that occured was a FPS drop to around 35-40 whereas normally I get around 90-100.
The C# code:
void Update()
{
MatToTexture2D();
image.texture = tex;
}
void MatToTexture2D()
{
OpenCVInterop.GetRawImageBytes(pixelPtr);
//Update the Texture2D with array updated in C++
tex.SetPixels32(pixel32);
tex.Apply();
}
The C++ code:
extern "C" void __declspec(dllexport) __stdcall GetRawImageBytes(unsigned char* data)
{
_capture >> _currentFrame;
cv::Mat resizedMat(camHeight, camWidth, _currentFrame.type());
cv::resize(_currentFrame, resizedMat, resizedMat.size(), cv::INTER_CUBIC);
//Convert from RGB to ARGB
cv::Mat argb_img;
cv::cvtColor(resizedMat, argb_img, cv::COLOR_BGR2BGRA);
std::vector<cv::Mat> bgra;
cv::split(argb_img, bgra);
std::swap(bgra[0], bgra[3]);
std::swap(bgra[1], bgra[2]);
std::memcpy(data, argb_img.data, argb_img.total() * argb_img.elemSize());
}
The cause seems to be the first line _capture >> _currentFrame;, but as the others projects have to do the same (at least I guess so), I wonder if there is another reason.
If I don't manage to fix this issue I have to look for alternative approaches.
Just adding to / building on Mars' answer:
For the threading problem I would actually use a thread-save ConcurrentStack<Color32[]>. A stack is "last-in | first-out" so that the first item returned is always the last image data added by the thread.
The thread uses Push(pixel32) to add a new entry with image data
in Update you only use the latest entry (TryPop) for updating the texture.
The rest you ignore (Clear).
So something like
// the OpenCV thread will add(push) entries
// the Unity main thread will work on the entries
private ConcurrentStack<Color32[]> stack = new ConcurrentStack<Color32[]>();
public RawImage image;
public Texture2D tex;
private Thread thread;
void Start()
{
// Wherever you get your tex from
tex = new Texture2D(...);
// it should be enough to do this only once
// the texture stays the same, you only update its content
image.texture = tex;
}
// do things in OnEnable so everytime the object gets enabled start the thread
void OnEnable()
{
stack.Clear();
if(thread != null)
{
thread.Abort();
}
thread = new Thread(MatToTexture2D);
thread.Start();
}
void Update()
{
// here in the main thread work the stack
if (stack.TryPop(out var pixels32))
{
// Only use SetPixels and Apply when really needed
tex.SetPixels32(pixels32);
tex.Apply();
}
// Erase older data
stack.Clear();
}
// Make sure to terminate the thread everytime this object gets disabled
private void OnDisable()
{
if(thread == null) return;
thread.Abort();
thread = null;
}
// Runs in a thread!
void MatToTexture2D()
{
while(true)
{
try
{
// Do what you already have
OpenCVInterop.GetRawImageBytes(pixelPtr);
// However you convert the pixelPtr into Color32
Color32[] pixel32 = GetColorArrayFromPtr(pixelPtr);
// Now add this data to the stack
stack.Push(pixel32);
}
catch (ThreadAbortException ex)
{
// This exception is thrown when calling Abort on the thread
// -> ignore the exception since it is produced on purpose
}
}
}
If I recall correctly, the C++ call to get an image (_capture >> _currentFrame;) is blocking/synchronous, meaning your code won't continue until it actually retrieves the image. You probably want to run your MatToTexture2D code asynchronously.
※This will mean that your frame rate will be higher than your image retrieval rate.
Have your MatToTexture2D function run continuously as needed, updating tex. Then just continue to set your texture to the latest tex, which may be the same value 2-3 frames in a row.
Edit:
#derHugo's answer is much more solid for the programming side, so I'll hide that part. The basic issue is explained above, and derHugo's work-around is much better than my pseudo-code :)
Related
I have a simple goal for my Unity application running on Android: create a function, which disables my GUI, creates a screenshot, stores the image and enables the GUI again.
This is my current code:
The whole UI is stored in a GameObject which my handler class holds as property:
private bool makeScreenshotFlag;
public GameObject UserInterface;
makeScreenshotFlag above is used in the Update function:
// Update is called once per frame
void Update()
{
if (makeScreenshotFlag)
{
// Working with this flag is necessary,
// since we need to wait for the next frame,
// where the gui is not visible;
MakeScreenShot();
}
}
If the corresponding button gets pressed I fire the following Method:
public void OnCreateScreenshotButtonPressed()
{
UserInterface.SetActive(false);
makeScreenshotFlag = true;
}
And at last the method for the screenshot itself:
private void MakeScreenShot()
{
if (UserInterface.activeSelf) return; // wait for the next frame if the gui is still active
try
{
string filename = "screenshot_" + DateTime.Now.ToString("HH-mm-ss--dd-MM-yyyy") + ".png";
ScreenCapture.CaptureScreenshot(filename);
}
catch (Exception e)
{
DebugHelper.NewLog = "Error capturing screenshot: " + e.Message;
}
finally
{
makeScreenshotFlag = false;
UserInterface.SetActive(true);
}
}
In theory this should work, however the documentation for ScreenCapture.CaptureScreenshot() states the following:
The CaptureScreenshot returns immediately on Android. The screen capture continues in the background. The resulting screen shot is saved in the file system after a few seconds.
In practice this means, when my screenshot is made, the GUI is already enabled again. So far I couldn't find any event or similar to get notified as soon as the screenshot is made. The only thing I can think of is to store the screenshot filename and check if the file exists each frame after the screenshot is started.
Is there any other, more practical way?
Most probably, you are capturing the frame with UI itself. It's better to wait till the end of the frame using Coroutine.
public IEnumerator MakeScreenShot()
{
// Wait till the last possible moment before screen rendering to hide the UI
yield return null;
// Disable UI
UserInterface.SetActive(false);
// Wait for screen rendering to complete
yield return new WaitForEndOfFrame();
// Take screenshot
Application.CaptureScreenshot("screenshot.png");
// Show UI after we're done
UserInterface.SetActive(true);
}
My problem is I try to use Unity socket to implement something. Each time, when I get a new message I need to update it to the updattext (it is a Unity Text). However, When I do the following code, the void update does not calling every time.
The reason for I do not include updatetext.GetComponent<Text>().text = "From server: "+tempMesg;in the void getInformation is this function is in the thread, when I include that in getInformation() it will come with an error:
getcomponentfastpath can only be called from the main thread
I think the problem is I don't know how to run the main thread and the child thread in C# together? Or there maybe other problems.
Here is my code:
using UnityEngine;
using System.Collections;
using System;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using UnityEngine.UI;
public class Client : MonoBehaviour {
System.Net.Sockets.TcpClient clientSocket = new System.Net.Sockets.TcpClient();
private Thread oThread;
// for UI update
public GameObject updatetext;
String tempMesg = "Waiting...";
// Use this for initialization
void Start () {
updatetext.GetComponent<Text>().text = "Waiting...";
clientSocket.Connect("10.132.198.29", 8888);
oThread = new Thread (new ThreadStart (getInformation));
oThread.Start ();
Debug.Log ("Running the client");
}
// Update is called once per frame
void Update () {
updatetext.GetComponent<Text>().text = "From server: "+tempMesg;
Debug.Log (tempMesg);
}
void getInformation(){
while (true) {
try {
NetworkStream networkStream = clientSocket.GetStream ();
byte[] bytesFrom = new byte[10025];
networkStream.Read (bytesFrom, 0, (int)bytesFrom.Length);
string dataFromClient = System.Text.Encoding.ASCII.GetString (bytesFrom);
dataFromClient = dataFromClient.Substring (0, dataFromClient.IndexOf ("$"));
Debug.Log (" >> Data from Server - " + dataFromClient);
tempMesg = dataFromClient;
string serverResponse = "Last Message from Server" + dataFromClient;
Byte[] sendBytes = Encoding.ASCII.GetBytes (serverResponse);
networkStream.Write (sendBytes, 0, sendBytes.Length);
networkStream.Flush ();
Debug.Log (" >> " + serverResponse);
} catch (Exception ex) {
Debug.Log ("Exception error:" + ex.ToString ());
oThread.Abort ();
oThread.Join ();
}
// Thread.Sleep (500);
}
}
}
Unity is not Thread safe, so they decided to make it impossible to call their API from another Thread by adding a mechanism to throw an exception when its API is used from another Thread.
This question has been asked so many times, but there have been no proper solution/answer to any of them. The answers are usually "use a plugin" or do something not thread-safe. Hopefully, this will be the last one.
The solution you will usually see on Stackoverflow or Unity's forum website is to simply use a boolean variable to let the main thread know that you need to execute code in the main Thread. This is not right as it is not thread-safe and does not give you control to provide which function to call. What if you have multiple Threads that need to notify the main thread?
Another solution you will see is to use a coroutine instead of a Thread. This does not work. Using coroutine for sockets will not change anything. You will still end up with your freezing problems. You must stick with your Thread code or use Async.
One of the proper ways to do this is to create a collection such as List. When you need something to be executed in the main Thread, call a function that stores the code to execute in an Action. Copy that List of Action to a local List of Action then execute the code from the local Action in that List then clear that List. This prevents other Threads from having to wait for it to finish executing.
You also need to add a volatile boolean to notify the Update function that there is code waiting in the List to be executed. When copying the List to a local List, that should be wrapped around the lock keyword to prevent another Thread from writing to it.
A script that performs what I mentioned above:
UnityThread Script:
#define ENABLE_UPDATE_FUNCTION_CALLBACK
#define ENABLE_LATEUPDATE_FUNCTION_CALLBACK
#define ENABLE_FIXEDUPDATE_FUNCTION_CALLBACK
using System;
using System.Collections;
using UnityEngine;
using System.Collections.Generic;
public class UnityThread : MonoBehaviour
{
//our (singleton) instance
private static UnityThread instance = null;
////////////////////////////////////////////////UPDATE IMPL////////////////////////////////////////////////////////
//Holds actions received from another Thread. Will be coped to actionCopiedQueueUpdateFunc then executed from there
private static List<System.Action> actionQueuesUpdateFunc = new List<Action>();
//holds Actions copied from actionQueuesUpdateFunc to be executed
List<System.Action> actionCopiedQueueUpdateFunc = new List<System.Action>();
// Used to know if whe have new Action function to execute. This prevents the use of the lock keyword every frame
private volatile static bool noActionQueueToExecuteUpdateFunc = true;
////////////////////////////////////////////////LATEUPDATE IMPL////////////////////////////////////////////////////////
//Holds actions received from another Thread. Will be coped to actionCopiedQueueLateUpdateFunc then executed from there
private static List<System.Action> actionQueuesLateUpdateFunc = new List<Action>();
//holds Actions copied from actionQueuesLateUpdateFunc to be executed
List<System.Action> actionCopiedQueueLateUpdateFunc = new List<System.Action>();
// Used to know if whe have new Action function to execute. This prevents the use of the lock keyword every frame
private volatile static bool noActionQueueToExecuteLateUpdateFunc = true;
////////////////////////////////////////////////FIXEDUPDATE IMPL////////////////////////////////////////////////////////
//Holds actions received from another Thread. Will be coped to actionCopiedQueueFixedUpdateFunc then executed from there
private static List<System.Action> actionQueuesFixedUpdateFunc = new List<Action>();
//holds Actions copied from actionQueuesFixedUpdateFunc to be executed
List<System.Action> actionCopiedQueueFixedUpdateFunc = new List<System.Action>();
// Used to know if whe have new Action function to execute. This prevents the use of the lock keyword every frame
private volatile static bool noActionQueueToExecuteFixedUpdateFunc = true;
//Used to initialize UnityThread. Call once before any function here
public static void initUnityThread(bool visible = false)
{
if (instance != null)
{
return;
}
if (Application.isPlaying)
{
// add an invisible game object to the scene
GameObject obj = new GameObject("MainThreadExecuter");
if (!visible)
{
obj.hideFlags = HideFlags.HideAndDontSave;
}
DontDestroyOnLoad(obj);
instance = obj.AddComponent<UnityThread>();
}
}
public void Awake()
{
DontDestroyOnLoad(gameObject);
}
//////////////////////////////////////////////COROUTINE IMPL//////////////////////////////////////////////////////
#if (ENABLE_UPDATE_FUNCTION_CALLBACK)
public static void executeCoroutine(IEnumerator action)
{
if (instance != null)
{
executeInUpdate(() => instance.StartCoroutine(action));
}
}
////////////////////////////////////////////UPDATE IMPL////////////////////////////////////////////////////
public static void executeInUpdate(System.Action action)
{
if (action == null)
{
throw new ArgumentNullException("action");
}
lock (actionQueuesUpdateFunc)
{
actionQueuesUpdateFunc.Add(action);
noActionQueueToExecuteUpdateFunc = false;
}
}
public void Update()
{
if (noActionQueueToExecuteUpdateFunc)
{
return;
}
//Clear the old actions from the actionCopiedQueueUpdateFunc queue
actionCopiedQueueUpdateFunc.Clear();
lock (actionQueuesUpdateFunc)
{
//Copy actionQueuesUpdateFunc to the actionCopiedQueueUpdateFunc variable
actionCopiedQueueUpdateFunc.AddRange(actionQueuesUpdateFunc);
//Now clear the actionQueuesUpdateFunc since we've done copying it
actionQueuesUpdateFunc.Clear();
noActionQueueToExecuteUpdateFunc = true;
}
// Loop and execute the functions from the actionCopiedQueueUpdateFunc
for (int i = 0; i < actionCopiedQueueUpdateFunc.Count; i++)
{
actionCopiedQueueUpdateFunc[i].Invoke();
}
}
#endif
////////////////////////////////////////////LATEUPDATE IMPL////////////////////////////////////////////////////
#if (ENABLE_LATEUPDATE_FUNCTION_CALLBACK)
public static void executeInLateUpdate(System.Action action)
{
if (action == null)
{
throw new ArgumentNullException("action");
}
lock (actionQueuesLateUpdateFunc)
{
actionQueuesLateUpdateFunc.Add(action);
noActionQueueToExecuteLateUpdateFunc = false;
}
}
public void LateUpdate()
{
if (noActionQueueToExecuteLateUpdateFunc)
{
return;
}
//Clear the old actions from the actionCopiedQueueLateUpdateFunc queue
actionCopiedQueueLateUpdateFunc.Clear();
lock (actionQueuesLateUpdateFunc)
{
//Copy actionQueuesLateUpdateFunc to the actionCopiedQueueLateUpdateFunc variable
actionCopiedQueueLateUpdateFunc.AddRange(actionQueuesLateUpdateFunc);
//Now clear the actionQueuesLateUpdateFunc since we've done copying it
actionQueuesLateUpdateFunc.Clear();
noActionQueueToExecuteLateUpdateFunc = true;
}
// Loop and execute the functions from the actionCopiedQueueLateUpdateFunc
for (int i = 0; i < actionCopiedQueueLateUpdateFunc.Count; i++)
{
actionCopiedQueueLateUpdateFunc[i].Invoke();
}
}
#endif
////////////////////////////////////////////FIXEDUPDATE IMPL//////////////////////////////////////////////////
#if (ENABLE_FIXEDUPDATE_FUNCTION_CALLBACK)
public static void executeInFixedUpdate(System.Action action)
{
if (action == null)
{
throw new ArgumentNullException("action");
}
lock (actionQueuesFixedUpdateFunc)
{
actionQueuesFixedUpdateFunc.Add(action);
noActionQueueToExecuteFixedUpdateFunc = false;
}
}
public void FixedUpdate()
{
if (noActionQueueToExecuteFixedUpdateFunc)
{
return;
}
//Clear the old actions from the actionCopiedQueueFixedUpdateFunc queue
actionCopiedQueueFixedUpdateFunc.Clear();
lock (actionQueuesFixedUpdateFunc)
{
//Copy actionQueuesFixedUpdateFunc to the actionCopiedQueueFixedUpdateFunc variable
actionCopiedQueueFixedUpdateFunc.AddRange(actionQueuesFixedUpdateFunc);
//Now clear the actionQueuesFixedUpdateFunc since we've done copying it
actionQueuesFixedUpdateFunc.Clear();
noActionQueueToExecuteFixedUpdateFunc = true;
}
// Loop and execute the functions from the actionCopiedQueueFixedUpdateFunc
for (int i = 0; i < actionCopiedQueueFixedUpdateFunc.Count; i++)
{
actionCopiedQueueFixedUpdateFunc[i].Invoke();
}
}
#endif
public void OnDisable()
{
if (instance == this)
{
instance = null;
}
}
}
USAGE:
This implementation allows you to call functions in the 3 most used Unity functions: Update, LateUpdate and FixedUpdate functions. This also allows you call run a coroutine function in the main Thread. It can be extended to be able to call functions in other Unity callback functions such as OnPreRender and OnPostRender.
1.First, initialize it from the Awake() function.
void Awake()
{
UnityThread.initUnityThread();
}
2.To execute a code in the main Thread from another Thread:
UnityThread.executeInUpdate(() =>
{
transform.Rotate(new Vector3(0f, 90f, 0f));
});
This will rotate the current Object the scipt is attached to, to 90 deg. You can now use Unity API(transform.Rotate) in another Thread.
3.To call a function in the main Thread from another Thread:
Action rot = Rotate;
UnityThread.executeInUpdate(rot);
void Rotate()
{
transform.Rotate(new Vector3(0f, 90f, 0f));
}
The #2 and #3 samples executes in the Update function.
4.To execute a code in the LateUpdate function from another Thread:
Example of this is a camera tracking code.
UnityThread.executeInLateUpdate(()=>
{
//Your code camera moving code
});
5.To execute a code in the FixedUpdate function from another Thread:
Example of this when doing physics stuff such as adding force to Rigidbody.
UnityThread.executeInFixedUpdate(()=>
{
//Your code physics code
});
6.To Start a coroutine function in the main Thread from another Thread:
UnityThread.executeCoroutine(myCoroutine());
IEnumerator myCoroutine()
{
Debug.Log("Hello");
yield return new WaitForSeconds(2f);
Debug.Log("Test");
}
Finally, if you don't need to execute anything in the LateUpdate and FixedUpdate functions, you should comment both lines of this code below:
//#define ENABLE_LATEUPDATE_FUNCTION_CALLBACK
//#define ENABLE_FIXEDUPDATE_FUNCTION_CALLBACK
This will increase performance.
I have been using this solution to this problem. Create a script with this code and attach it to a Game Object:
using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using UnityEngine;
public class ExecuteOnMainThread : MonoBehaviour {
public static readonly ConcurrentQueue<Action> RunOnMainThread = new ConcurrentQueue<Action>();
void Update()
{
if(!RunOnMainThread.IsEmpty)
{
while(RunOnMainThread.TryDequeue(out var action))
{
action?.Invoke();
}
}
}
}
Then when you need to call something on the main thread and access the Unity API from any other function in your application:
ExecuteOnMainThread.RunOnMainThread.Enqueue(() => {
// Code here will be called in the main thread...
});
Much of the writing about threads in Unity is incorrect.
How so?
Unity is, of course, totally frame-based.
When you work in a frame-based system, threading issues are completely different.
Threading issues on a frame-based system are completely different. (In fact, often much easier to deal with.)
Let's say you have a Unity thermometer display that shows some value
Thermo.cs
So it will have a function which is called in Update, like
func void ShowThermoValue(float fraction) {
display code to show the current thermometer value
}
Recall that the "Update" function in Unity simply means "run this once each frame".
That only runs once per frame, and that's that.
(Naturally, it runs only on the "main thread". There's nothing else in Unity! There's just ... "the Unity thread"!)
Somewhere else, perhaps in "IncomingData.cs", you will have a function which handles the concept "a new value has arrived":
[MonoPInvokeCallback(typeof(ipDel))]
public static void NewValueArrives(float f) {
... ???
}
Note that, of course, that is a class function! What else can it be?
You can't "reach in to" a normal Unity function. (Such as ShowThermoValue.) That would be meaningless - it's just a function which runs once each frame.Footnote 1
Let's say: values arrive very frequently and irregularly.
Image you have some sort of scientific devices (perhaps IR thermometers) connected connected to a rack of PCs
Those electronic devices deliver new "temperature" values very often. Let's say dozens of times per frame.
So, "NewValueArrives" is being called 100s of times a second.
So what do you do with the values?
It could not be simpler.
From the arriving-values thread, all you do is ................. wait for it ............. set a variable in the component!!
WTF? All you do is set a variable? That's it? How can it be that simple?
This is one of those unusual situations:
Much of the writing on threads in Unity is, simply, completely hopeless.
Surprisingly, the actual approach is extremely simple.
It's so simple you may think you are doing something wrong!!
So have the variable ...
[System.Nonserialized] public float latestValue;
Set it from the "arriving thread" ...
[MonoPInvokeCallback(typeof(ipDel))]
public static void NewValueArrives(float f) {
ThisScript.runningInstance.latestValue = f; // done
}
Honestly that's it.
Essentially, to be the world's greatest expert at "threading in Unity" - which is, obviously, frame-based - there's nothing more to do than the above.
And whenever ShowThermoValue is called each frame ...................... simply display that value!
Really, that's it!
[System.Nonserialized] public float latestValue;
func void ShowThermoValue() { // note NO arguments here!
display code, draws a thermometer
thermo height = latestValue
}
You're simply displaying the "latest" value.
latestValue may have been set once, twice, ten times, or a hundred times that frame ............ but, you simply display whatever is the value when ShowThermoValue runs that frame!
What else could you display?
The thermometer is updating at 60fps on-screen so you display the latest value. Footnote 2
It's actually that easy. It's just that easy. Surprising but true.
#(Critical aside - don't forget that vector3, etc, are NOT Atomic in Unity/C#)
As user #dymanoid has pointed out (read the important discussion below) it's critical to remember that while float is atomic in the Unity/C# milieu, anything else (say, Vector3 etc) IS NOT ATOMIC. Typically (as in the example here) you only pass floats around from calculations from, say, native plugins, thermometers etc. But it's essential to be aware that vectors and so on are NOT atomic.
Sometimes experienced threading programmers get in a knot with a frame-based system, because: in a frame based system most of the problems caused by racetrack and locking issues ... do not exist conceptually.
In a frame-based system, any game items should simply be displaying or behaving based on some "current value," which is set somewhere. If you have info arriving from other threads, just set those values - you're done.
You can not meaningfully "talk to the main thread" in Unity because that main thread ............. is frame-based!
Most locking, blocking and racetrack issues are non-existent in the frame-based paradigm because: if you set latestValue ten times, a million times, a billion times, in one particular frame .. what can you do? .. you can only display one value during that frame!
Think of an old-fashioned plastic film. You literally just have ...... a frame, and that's it. If you set latestValue a trillion times in one particular frame, ShowThermoValue will simply display (for that 60th of a second) the one value it grabs when it is run.
All you do is: leave information somewhere, which, the frame-paradigm system will utilize during that frame if it wants to.
That's it in a nutshell.
Thus, most "threading issues" disappear in Unity.
All you can do from
other calculation threads or
from plugin threads,
is just "drop-off values" which the game may use.
That's it!
Let's consider the question title...
How do you "... call a function in the main Thread"
This is completely meaningless. The "functions" in Unity are simply functions which the frame engine runs once per frame.
You can't "call" anything in Unity. The frame engine runs a number of things (many things) once per frame.
Note that indeed threads are totally irrelevant. If Unity ran with a billion threads, or with quantum computing, it would have no bearing on anything.
You can't "call a function" in a frame-based system.
Fortunately, the approach to take is dead simple, you just set values, which the frame-based functions can look at when they want! It's really that easy.
Footnotes
1 How could you? As a thought experiment, forget about the issue that you're on a different thread. ShowThermoValue is run once a frame by the frame engine. You can't "call" it in any meaningful way. Unlike in normal OO software, you cannot, say, instantiate an instance of the class (a Component?? meaningless) and run that function - that is completely meaningless.
In "normal" threaded programming, threads can talk back and fore and so on, and in doing so you have concerns about locking, racetrack and so on. But that is all meaningless in an ECS, frame-based, system. There is nothing to "talk to".
Let's say that Unity was in fact multithreaded!!!! So the Unity guys have all of the engine running in a multithreaded manner. It wouldn't make any difference - you can not get "in to" ShowThermoValue in any meaningful way! It's a Component which the frame engine runs once a frame and that's that.
So NewValueArrives is not anywhere - it's a class function!
Let's answer the question in the headline:
"Use Unity API from another Thread or call a function in the main Thread?"
The concept is >> completely meaningless <<. Unity (like all game engines) is frame-based. There's no concept of "calling" a function on the main thread. To make an analogy: it would be like a cinematographer in the celluloid-film era asking how to "move" something actually on one of the frames.
Of course that is meaningless. All you can do is change something for the next photo, the next frame.
2 I refer to the "arriving-values thread" ... in fact! NewValueArrives may, or may not, run on the main thread!!!! It may run on the thread of the plugin, or on some other thread! It may actually be completely single-threaded by the time you deal with the NewValueArrives call! It just doesn't matter! What you do, and all you can do, in a frame-based paradigm, is, "leave laying around" information which Components such as ShowThermoValue, may use, as, they see fit.
Another solution to run code on the main thread, but without requiring a game object and MonoBehavior, is to use SynchronizationContext:
// On main thread, during initialization:
var syncContext = System.Threading.SynchronizationContext.Current;
// On your worker thread
syncContext.Post(_ =>
{
// This code here will run on the main thread
Debug.Log("Hello from main thread!");
}, null);
Use UniRx's multithreading pattern, UniTask and RxSocket together.
[SerializeField] private Text m_Text;
async UniTaskVoid Connect() {
IPEndPoint endPoint = new IPEndPoint(IPAddress.IPv6Loopback, 12345);
// Create a socket client by connecting to the server at the IPEndPoint.
// See the UniRx Async tooling to use await
IRxSocketClient client = await endPoint.ConnectRxSocketClientAsync();
client.ReceiveObservable
.ToStrings()
.ObserveOnMainThread()
.Subscribe(onNext: message =>
{
m_Text.text = message;
}).AddTo(this);
// Send a message to the server.
client.Send("Hello!".ToByteArray());
}
This scene is common in real time video proccessing. And I need timestamps to synchronize with other devices.
I have tried cv::VideoCapture, but it can not extract the timestamps frome video stream.
So I have two questions here:
Does video stream provided by USB camera indeed contains the timestamp information ?
If it has. What should I do to extract it ? A C# solution is best, while C++ is OK.
Addition:
Using these two properties doesn't work:
secCounter = (long) cap.get(CAP_PROP_POS_MSEC);
frameNumber = (long) cap.get(CAP_PROP_POS_FRAMES);
It always gives the following result:
VIDEOIO ERROR: V4L2: getting property #1 is not supported
msecCounter = 0
frameNumber = -1
OpenCV's VideoCapture class is a very high level interface to retrieve frames from a camera, so it "hides" a lot of the details that are necessary to connect to the camera, retrieve frames from the camera, and decode those frames in to a useful color space like BGR. This is nice because you don't have to worry about the details of grabbing frames, but the downside is that you don't have direct access to other data you might want, like the frame number or frame timestamp. That doesn't mean it's impossible to get the data you want, though!
Here's a sample frame grabbing loop that will get you what you want, loosely based on the example code from here. This is in C++.
#include "opencv2/opencv.hpp"
using namespace cv;
int main(int, char**)
{
VideoCapture cap(0); // open the default camera
if(!cap.isOpened()) // check if we succeeded
return -1;
// TODO: change the width, height, and capture FPS to your desired
// settings.
cap.set(CAP_PROP_FRAME_WIDTH, 1920);
cap.set(CAP_PROP_FRAME_HEIGHT, 1080);
cap.set(CAP_PROP_FPS, 30);
Mat frame;
long msecCounter = 0;
long frameNumber = 0;
for(;;)
{
// Instead of cap >> frame; we'll do something different.
//
// VideoCapture::grab() tells OpenCV to grab a frame from
// the camera, but to not worry about all the color conversion
// and processing to convert that frame into BGR.
//
// This means there's less processing overhead, so the time
// stamp will be more accurate because we are fetching it
// immediately after.
//
// grab() should also wait for the next frame to be available
// based on the capture FPS that is set, so it's okay to loop
// continuously over it.
if(cap.grab())
{
msecCounter = (long) cap.get(CAP_PROP_POS_MSEC);
frameNumber = (long) cap.get(CAP_PROP_POS_FRAMES);
// VideoCapture::retrieve color converts the image and places
// it in the Mat that you provide.
if(cap.retrieve(&frame))
{
// Pass the frame and parameters to your processing
// method.
ProcessFrame(&frame, msecCounter, frameNumber);
}
}
// TODO: Handle your loop termination condition here
}
// the camera will be deinitialized automatically in VideoCapture destructor
return 0;
}
void ProcessFrame(Mat& frame, long msecCounter, long frameNumber)
{
// TODO: Make a copy of frame if you are going to process it
// asynchronously or put it in a buffer or queue and then return
// control from this function. This is because the reference Mat
// being passed in is "owned" by the processing loop, and on each
// iteration it will be destructed, so any references to it will be
// invalid. Hence, if you do any work async, you need to copy frame.
//
// If all your processing happens synchronously in this function,
// you don't need to make a copy first because the loop is waiting
// for this function to return.
// TODO: Your processing logic goes here.
}
If you're using C# and Emgu CV it will look a bit different. I haven't tested this code, but it should work or be very close to the solution.
using System;
using Emgu.CV;
using Emgu.CV.CvEnum;
static class Program
{
[STAThread]
static void Main()
{
VideoCapture cap = new VideoCapture(0);
if(!cap.IsOpened)
{
return;
}
cap.SetCaptureProperty(CapProp.FrameWidth, 1920);
cap.SetCaptureProperty(CapProp.FrameHeight, 1080);
cap.SetCaptureProperty(CapProp.Fps, 30);
Mat frame = new Mat();
long msecCounter = 0;
long frameNumber = 0;
for(;;)
{
if(cap.Grab())
{
msecCounter = (long) cap.GetCaptureProperty(CapProp.PosMsec);
frameNumber = (long) cap.GetCaptureProperty(CapProp.PosFrames);
if(cap.Retrieve(frame))
{
ProcessFrame(frame, msecCounter, frameNumber);
}
}
// TODO: Determine when to quit the processing loop
}
}
private static void ProcessFrame(Mat frame, long msecCounter, long frameNumber)
{
// Again, copy frame here if you're going to queue the frame or
// do any async processing on it.
// TODO: Your processing code goes here.
}
}
Emgu's VideoCapture implementation also allows for asynchronous Grab operations to be done for you, and notifications when a grabbed frame is ready to be used with Retrieve. That looks like this:
using System;
using Emgu.CV;
using Emgu.CV.CvEnum;
static class Program
{
private static Mat s_frame;
private static VideoCapture s_cap;
private static object s_retrieveLock = new object();
[STAThread]
static void Main()
{
s_cap = new VideoCapture(0);
if(!s_cap.IsOpened)
{
return;
}
s_frame = new Mat();
s_cap.SetCaptureProperty(CapProp.FrameWidth, 1920);
s_cap.SetCaptureProperty(CapProp.FrameHeight, 1080);
s_cap.SetCaptureProperty(CapProp.Fps, 30);
s_cap.ImageGrabbed += FrameIsReady;
s_cap.Start();
// TODO: Wait here until you're done with the capture process,
// the same way you'd determine when to exit the for loop in the
// above example.
s_cap.Stop();
s_cap.ImageGrabbed -= FrameIsReady;
}
private static void FrameIsReady(object sender, EventArgs e)
{
// This function is being called from VideoCapture's thread,
// so if you rework this code to run with a UI, be very careful
// about updating Controls here because that needs to be Invoke'd
// back to the UI thread.
// I used a lock here to be extra careful and protect against
// re-entrancy, but this may not be necessary if Emgu's
// VideoCapture thread blocks for completion of this event
// handler.
lock(s_retrieveLock)
{
msecCounter = (long) s_cap.GetCaptureProperty(CapProp.PosMsec);
frameNumber = (long) s_cap.GetCaptureProperty(CapProp.PosFrames);
if(s_cap.Retrieve(s_frame))
{
ProcessFrame(s_frame, msecCounter, frameNumber);
}
}
}
private static void ProcessFrame(Mat frame, long msecCounter, long frameNumber)
{
// Again, copy frame here if you're going to queue the frame or
// do any async processing on it.
// TODO: Your processing code goes here.
}
}
I'm writing a simple optical measurement application using Xamarin for Android and the OpenCV C# bindings library.
In an effort to separate the frame grabber from the processing, I've created some blocking collections to pass around raw, and then processed imagery between different threads. I have an issue where over the period of about 30 seconds, the GUI shows beautifully smooth processed video (15s) down to choppy video (10s), then a crash.
The code below shows the definition of the collections. OnCameraFrame (bottom of the code) shoves each new frame into camframes collection. In OnCreate, I run a task called CamProcessor that takes the frame, does many things, and stuffs it into outframes collection. OnCameraFrame then takes that processed frame and shows it to the GUI. For the purposes of this post and testing, I've completely commented out all my processing, so this issue exists simply by passing raw data through the collections.
One other note is that my collections seem to be running very fast. At no point do I ever have more than 1 frame in there, so it's not an overflow issue (I think).
Can anyone point to why this strategy isn't working well?
BlockingCollection<Mat> camframes = new BlockingCollection<Mat>(10);
BlockingCollection<Mat> outframes = new BlockingCollection<Mat>(10);
public CameraBridgeViewBase mOpenCvCameraView { get; private set; }
protected override void OnCreate(Bundle savedInstanceState)
{
//LayoutStuff
mOpenCvCameraView = FindViewById<CameraBridgeViewBase>(Resource.Id.squish_cam);
Task.Run(() => camProcessor());
}
public void camProcessor()
{
while (!camframes.IsCompleted)
{
Mat frame = new Mat();
try
{
frame = camframes.Take();
}
catch (InvalidOperationException) { }
Mat frameT = frame.T();
Core.Flip(frame.T(), frameT, 1);
Imgproc.Resize(frameT, frameT, frame.Size());
outframes.Add(frameT);
}
}
public Mat OnCameraFrame(CameraBridgeViewBase.ICvCameraViewFrame inputFrame)
{
mRgba = inputFrame.Rgba();
Mat frame = new Mat();
Task.Run(() => camframes.Add(mRgba));
try
{
frame = outframes.Take();
}
catch (InvalidOperationException) { }
return frame;
}
After looking into the Android SDK monitor.bat output, I discovered this was a memory leak. Turns out it's common to the Java openCV wrapper, and is a result of OpenCV's mat heap being much larger than C# expects it to be, so it's not getting garbage collected.
The solution was to append these at every frame grab:
GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
GC.Collect();
GC.WaitForPendingFinalizers();
My problem is I try to use Unity socket to implement something. Each time, when I get a new message I need to update it to the updattext (it is a Unity Text). However, When I do the following code, the void update does not calling every time.
The reason for I do not include updatetext.GetComponent<Text>().text = "From server: "+tempMesg;in the void getInformation is this function is in the thread, when I include that in getInformation() it will come with an error:
getcomponentfastpath can only be called from the main thread
I think the problem is I don't know how to run the main thread and the child thread in C# together? Or there maybe other problems.
Here is my code:
using UnityEngine;
using System.Collections;
using System;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using UnityEngine.UI;
public class Client : MonoBehaviour {
System.Net.Sockets.TcpClient clientSocket = new System.Net.Sockets.TcpClient();
private Thread oThread;
// for UI update
public GameObject updatetext;
String tempMesg = "Waiting...";
// Use this for initialization
void Start () {
updatetext.GetComponent<Text>().text = "Waiting...";
clientSocket.Connect("10.132.198.29", 8888);
oThread = new Thread (new ThreadStart (getInformation));
oThread.Start ();
Debug.Log ("Running the client");
}
// Update is called once per frame
void Update () {
updatetext.GetComponent<Text>().text = "From server: "+tempMesg;
Debug.Log (tempMesg);
}
void getInformation(){
while (true) {
try {
NetworkStream networkStream = clientSocket.GetStream ();
byte[] bytesFrom = new byte[10025];
networkStream.Read (bytesFrom, 0, (int)bytesFrom.Length);
string dataFromClient = System.Text.Encoding.ASCII.GetString (bytesFrom);
dataFromClient = dataFromClient.Substring (0, dataFromClient.IndexOf ("$"));
Debug.Log (" >> Data from Server - " + dataFromClient);
tempMesg = dataFromClient;
string serverResponse = "Last Message from Server" + dataFromClient;
Byte[] sendBytes = Encoding.ASCII.GetBytes (serverResponse);
networkStream.Write (sendBytes, 0, sendBytes.Length);
networkStream.Flush ();
Debug.Log (" >> " + serverResponse);
} catch (Exception ex) {
Debug.Log ("Exception error:" + ex.ToString ());
oThread.Abort ();
oThread.Join ();
}
// Thread.Sleep (500);
}
}
}
Unity is not Thread safe, so they decided to make it impossible to call their API from another Thread by adding a mechanism to throw an exception when its API is used from another Thread.
This question has been asked so many times, but there have been no proper solution/answer to any of them. The answers are usually "use a plugin" or do something not thread-safe. Hopefully, this will be the last one.
The solution you will usually see on Stackoverflow or Unity's forum website is to simply use a boolean variable to let the main thread know that you need to execute code in the main Thread. This is not right as it is not thread-safe and does not give you control to provide which function to call. What if you have multiple Threads that need to notify the main thread?
Another solution you will see is to use a coroutine instead of a Thread. This does not work. Using coroutine for sockets will not change anything. You will still end up with your freezing problems. You must stick with your Thread code or use Async.
One of the proper ways to do this is to create a collection such as List. When you need something to be executed in the main Thread, call a function that stores the code to execute in an Action. Copy that List of Action to a local List of Action then execute the code from the local Action in that List then clear that List. This prevents other Threads from having to wait for it to finish executing.
You also need to add a volatile boolean to notify the Update function that there is code waiting in the List to be executed. When copying the List to a local List, that should be wrapped around the lock keyword to prevent another Thread from writing to it.
A script that performs what I mentioned above:
UnityThread Script:
#define ENABLE_UPDATE_FUNCTION_CALLBACK
#define ENABLE_LATEUPDATE_FUNCTION_CALLBACK
#define ENABLE_FIXEDUPDATE_FUNCTION_CALLBACK
using System;
using System.Collections;
using UnityEngine;
using System.Collections.Generic;
public class UnityThread : MonoBehaviour
{
//our (singleton) instance
private static UnityThread instance = null;
////////////////////////////////////////////////UPDATE IMPL////////////////////////////////////////////////////////
//Holds actions received from another Thread. Will be coped to actionCopiedQueueUpdateFunc then executed from there
private static List<System.Action> actionQueuesUpdateFunc = new List<Action>();
//holds Actions copied from actionQueuesUpdateFunc to be executed
List<System.Action> actionCopiedQueueUpdateFunc = new List<System.Action>();
// Used to know if whe have new Action function to execute. This prevents the use of the lock keyword every frame
private volatile static bool noActionQueueToExecuteUpdateFunc = true;
////////////////////////////////////////////////LATEUPDATE IMPL////////////////////////////////////////////////////////
//Holds actions received from another Thread. Will be coped to actionCopiedQueueLateUpdateFunc then executed from there
private static List<System.Action> actionQueuesLateUpdateFunc = new List<Action>();
//holds Actions copied from actionQueuesLateUpdateFunc to be executed
List<System.Action> actionCopiedQueueLateUpdateFunc = new List<System.Action>();
// Used to know if whe have new Action function to execute. This prevents the use of the lock keyword every frame
private volatile static bool noActionQueueToExecuteLateUpdateFunc = true;
////////////////////////////////////////////////FIXEDUPDATE IMPL////////////////////////////////////////////////////////
//Holds actions received from another Thread. Will be coped to actionCopiedQueueFixedUpdateFunc then executed from there
private static List<System.Action> actionQueuesFixedUpdateFunc = new List<Action>();
//holds Actions copied from actionQueuesFixedUpdateFunc to be executed
List<System.Action> actionCopiedQueueFixedUpdateFunc = new List<System.Action>();
// Used to know if whe have new Action function to execute. This prevents the use of the lock keyword every frame
private volatile static bool noActionQueueToExecuteFixedUpdateFunc = true;
//Used to initialize UnityThread. Call once before any function here
public static void initUnityThread(bool visible = false)
{
if (instance != null)
{
return;
}
if (Application.isPlaying)
{
// add an invisible game object to the scene
GameObject obj = new GameObject("MainThreadExecuter");
if (!visible)
{
obj.hideFlags = HideFlags.HideAndDontSave;
}
DontDestroyOnLoad(obj);
instance = obj.AddComponent<UnityThread>();
}
}
public void Awake()
{
DontDestroyOnLoad(gameObject);
}
//////////////////////////////////////////////COROUTINE IMPL//////////////////////////////////////////////////////
#if (ENABLE_UPDATE_FUNCTION_CALLBACK)
public static void executeCoroutine(IEnumerator action)
{
if (instance != null)
{
executeInUpdate(() => instance.StartCoroutine(action));
}
}
////////////////////////////////////////////UPDATE IMPL////////////////////////////////////////////////////
public static void executeInUpdate(System.Action action)
{
if (action == null)
{
throw new ArgumentNullException("action");
}
lock (actionQueuesUpdateFunc)
{
actionQueuesUpdateFunc.Add(action);
noActionQueueToExecuteUpdateFunc = false;
}
}
public void Update()
{
if (noActionQueueToExecuteUpdateFunc)
{
return;
}
//Clear the old actions from the actionCopiedQueueUpdateFunc queue
actionCopiedQueueUpdateFunc.Clear();
lock (actionQueuesUpdateFunc)
{
//Copy actionQueuesUpdateFunc to the actionCopiedQueueUpdateFunc variable
actionCopiedQueueUpdateFunc.AddRange(actionQueuesUpdateFunc);
//Now clear the actionQueuesUpdateFunc since we've done copying it
actionQueuesUpdateFunc.Clear();
noActionQueueToExecuteUpdateFunc = true;
}
// Loop and execute the functions from the actionCopiedQueueUpdateFunc
for (int i = 0; i < actionCopiedQueueUpdateFunc.Count; i++)
{
actionCopiedQueueUpdateFunc[i].Invoke();
}
}
#endif
////////////////////////////////////////////LATEUPDATE IMPL////////////////////////////////////////////////////
#if (ENABLE_LATEUPDATE_FUNCTION_CALLBACK)
public static void executeInLateUpdate(System.Action action)
{
if (action == null)
{
throw new ArgumentNullException("action");
}
lock (actionQueuesLateUpdateFunc)
{
actionQueuesLateUpdateFunc.Add(action);
noActionQueueToExecuteLateUpdateFunc = false;
}
}
public void LateUpdate()
{
if (noActionQueueToExecuteLateUpdateFunc)
{
return;
}
//Clear the old actions from the actionCopiedQueueLateUpdateFunc queue
actionCopiedQueueLateUpdateFunc.Clear();
lock (actionQueuesLateUpdateFunc)
{
//Copy actionQueuesLateUpdateFunc to the actionCopiedQueueLateUpdateFunc variable
actionCopiedQueueLateUpdateFunc.AddRange(actionQueuesLateUpdateFunc);
//Now clear the actionQueuesLateUpdateFunc since we've done copying it
actionQueuesLateUpdateFunc.Clear();
noActionQueueToExecuteLateUpdateFunc = true;
}
// Loop and execute the functions from the actionCopiedQueueLateUpdateFunc
for (int i = 0; i < actionCopiedQueueLateUpdateFunc.Count; i++)
{
actionCopiedQueueLateUpdateFunc[i].Invoke();
}
}
#endif
////////////////////////////////////////////FIXEDUPDATE IMPL//////////////////////////////////////////////////
#if (ENABLE_FIXEDUPDATE_FUNCTION_CALLBACK)
public static void executeInFixedUpdate(System.Action action)
{
if (action == null)
{
throw new ArgumentNullException("action");
}
lock (actionQueuesFixedUpdateFunc)
{
actionQueuesFixedUpdateFunc.Add(action);
noActionQueueToExecuteFixedUpdateFunc = false;
}
}
public void FixedUpdate()
{
if (noActionQueueToExecuteFixedUpdateFunc)
{
return;
}
//Clear the old actions from the actionCopiedQueueFixedUpdateFunc queue
actionCopiedQueueFixedUpdateFunc.Clear();
lock (actionQueuesFixedUpdateFunc)
{
//Copy actionQueuesFixedUpdateFunc to the actionCopiedQueueFixedUpdateFunc variable
actionCopiedQueueFixedUpdateFunc.AddRange(actionQueuesFixedUpdateFunc);
//Now clear the actionQueuesFixedUpdateFunc since we've done copying it
actionQueuesFixedUpdateFunc.Clear();
noActionQueueToExecuteFixedUpdateFunc = true;
}
// Loop and execute the functions from the actionCopiedQueueFixedUpdateFunc
for (int i = 0; i < actionCopiedQueueFixedUpdateFunc.Count; i++)
{
actionCopiedQueueFixedUpdateFunc[i].Invoke();
}
}
#endif
public void OnDisable()
{
if (instance == this)
{
instance = null;
}
}
}
USAGE:
This implementation allows you to call functions in the 3 most used Unity functions: Update, LateUpdate and FixedUpdate functions. This also allows you call run a coroutine function in the main Thread. It can be extended to be able to call functions in other Unity callback functions such as OnPreRender and OnPostRender.
1.First, initialize it from the Awake() function.
void Awake()
{
UnityThread.initUnityThread();
}
2.To execute a code in the main Thread from another Thread:
UnityThread.executeInUpdate(() =>
{
transform.Rotate(new Vector3(0f, 90f, 0f));
});
This will rotate the current Object the scipt is attached to, to 90 deg. You can now use Unity API(transform.Rotate) in another Thread.
3.To call a function in the main Thread from another Thread:
Action rot = Rotate;
UnityThread.executeInUpdate(rot);
void Rotate()
{
transform.Rotate(new Vector3(0f, 90f, 0f));
}
The #2 and #3 samples executes in the Update function.
4.To execute a code in the LateUpdate function from another Thread:
Example of this is a camera tracking code.
UnityThread.executeInLateUpdate(()=>
{
//Your code camera moving code
});
5.To execute a code in the FixedUpdate function from another Thread:
Example of this when doing physics stuff such as adding force to Rigidbody.
UnityThread.executeInFixedUpdate(()=>
{
//Your code physics code
});
6.To Start a coroutine function in the main Thread from another Thread:
UnityThread.executeCoroutine(myCoroutine());
IEnumerator myCoroutine()
{
Debug.Log("Hello");
yield return new WaitForSeconds(2f);
Debug.Log("Test");
}
Finally, if you don't need to execute anything in the LateUpdate and FixedUpdate functions, you should comment both lines of this code below:
//#define ENABLE_LATEUPDATE_FUNCTION_CALLBACK
//#define ENABLE_FIXEDUPDATE_FUNCTION_CALLBACK
This will increase performance.
I have been using this solution to this problem. Create a script with this code and attach it to a Game Object:
using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using UnityEngine;
public class ExecuteOnMainThread : MonoBehaviour {
public static readonly ConcurrentQueue<Action> RunOnMainThread = new ConcurrentQueue<Action>();
void Update()
{
if(!RunOnMainThread.IsEmpty)
{
while(RunOnMainThread.TryDequeue(out var action))
{
action?.Invoke();
}
}
}
}
Then when you need to call something on the main thread and access the Unity API from any other function in your application:
ExecuteOnMainThread.RunOnMainThread.Enqueue(() => {
// Code here will be called in the main thread...
});
Much of the writing about threads in Unity is incorrect.
How so?
Unity is, of course, totally frame-based.
When you work in a frame-based system, threading issues are completely different.
Threading issues on a frame-based system are completely different. (In fact, often much easier to deal with.)
Let's say you have a Unity thermometer display that shows some value
Thermo.cs
So it will have a function which is called in Update, like
func void ShowThermoValue(float fraction) {
display code to show the current thermometer value
}
Recall that the "Update" function in Unity simply means "run this once each frame".
That only runs once per frame, and that's that.
(Naturally, it runs only on the "main thread". There's nothing else in Unity! There's just ... "the Unity thread"!)
Somewhere else, perhaps in "IncomingData.cs", you will have a function which handles the concept "a new value has arrived":
[MonoPInvokeCallback(typeof(ipDel))]
public static void NewValueArrives(float f) {
... ???
}
Note that, of course, that is a class function! What else can it be?
You can't "reach in to" a normal Unity function. (Such as ShowThermoValue.) That would be meaningless - it's just a function which runs once each frame.Footnote 1
Let's say: values arrive very frequently and irregularly.
Image you have some sort of scientific devices (perhaps IR thermometers) connected connected to a rack of PCs
Those electronic devices deliver new "temperature" values very often. Let's say dozens of times per frame.
So, "NewValueArrives" is being called 100s of times a second.
So what do you do with the values?
It could not be simpler.
From the arriving-values thread, all you do is ................. wait for it ............. set a variable in the component!!
WTF? All you do is set a variable? That's it? How can it be that simple?
This is one of those unusual situations:
Much of the writing on threads in Unity is, simply, completely hopeless.
Surprisingly, the actual approach is extremely simple.
It's so simple you may think you are doing something wrong!!
So have the variable ...
[System.Nonserialized] public float latestValue;
Set it from the "arriving thread" ...
[MonoPInvokeCallback(typeof(ipDel))]
public static void NewValueArrives(float f) {
ThisScript.runningInstance.latestValue = f; // done
}
Honestly that's it.
Essentially, to be the world's greatest expert at "threading in Unity" - which is, obviously, frame-based - there's nothing more to do than the above.
And whenever ShowThermoValue is called each frame ...................... simply display that value!
Really, that's it!
[System.Nonserialized] public float latestValue;
func void ShowThermoValue() { // note NO arguments here!
display code, draws a thermometer
thermo height = latestValue
}
You're simply displaying the "latest" value.
latestValue may have been set once, twice, ten times, or a hundred times that frame ............ but, you simply display whatever is the value when ShowThermoValue runs that frame!
What else could you display?
The thermometer is updating at 60fps on-screen so you display the latest value. Footnote 2
It's actually that easy. It's just that easy. Surprising but true.
#(Critical aside - don't forget that vector3, etc, are NOT Atomic in Unity/C#)
As user #dymanoid has pointed out (read the important discussion below) it's critical to remember that while float is atomic in the Unity/C# milieu, anything else (say, Vector3 etc) IS NOT ATOMIC. Typically (as in the example here) you only pass floats around from calculations from, say, native plugins, thermometers etc. But it's essential to be aware that vectors and so on are NOT atomic.
Sometimes experienced threading programmers get in a knot with a frame-based system, because: in a frame based system most of the problems caused by racetrack and locking issues ... do not exist conceptually.
In a frame-based system, any game items should simply be displaying or behaving based on some "current value," which is set somewhere. If you have info arriving from other threads, just set those values - you're done.
You can not meaningfully "talk to the main thread" in Unity because that main thread ............. is frame-based!
Most locking, blocking and racetrack issues are non-existent in the frame-based paradigm because: if you set latestValue ten times, a million times, a billion times, in one particular frame .. what can you do? .. you can only display one value during that frame!
Think of an old-fashioned plastic film. You literally just have ...... a frame, and that's it. If you set latestValue a trillion times in one particular frame, ShowThermoValue will simply display (for that 60th of a second) the one value it grabs when it is run.
All you do is: leave information somewhere, which, the frame-paradigm system will utilize during that frame if it wants to.
That's it in a nutshell.
Thus, most "threading issues" disappear in Unity.
All you can do from
other calculation threads or
from plugin threads,
is just "drop-off values" which the game may use.
That's it!
Let's consider the question title...
How do you "... call a function in the main Thread"
This is completely meaningless. The "functions" in Unity are simply functions which the frame engine runs once per frame.
You can't "call" anything in Unity. The frame engine runs a number of things (many things) once per frame.
Note that indeed threads are totally irrelevant. If Unity ran with a billion threads, or with quantum computing, it would have no bearing on anything.
You can't "call a function" in a frame-based system.
Fortunately, the approach to take is dead simple, you just set values, which the frame-based functions can look at when they want! It's really that easy.
Footnotes
1 How could you? As a thought experiment, forget about the issue that you're on a different thread. ShowThermoValue is run once a frame by the frame engine. You can't "call" it in any meaningful way. Unlike in normal OO software, you cannot, say, instantiate an instance of the class (a Component?? meaningless) and run that function - that is completely meaningless.
In "normal" threaded programming, threads can talk back and fore and so on, and in doing so you have concerns about locking, racetrack and so on. But that is all meaningless in an ECS, frame-based, system. There is nothing to "talk to".
Let's say that Unity was in fact multithreaded!!!! So the Unity guys have all of the engine running in a multithreaded manner. It wouldn't make any difference - you can not get "in to" ShowThermoValue in any meaningful way! It's a Component which the frame engine runs once a frame and that's that.
So NewValueArrives is not anywhere - it's a class function!
Let's answer the question in the headline:
"Use Unity API from another Thread or call a function in the main Thread?"
The concept is >> completely meaningless <<. Unity (like all game engines) is frame-based. There's no concept of "calling" a function on the main thread. To make an analogy: it would be like a cinematographer in the celluloid-film era asking how to "move" something actually on one of the frames.
Of course that is meaningless. All you can do is change something for the next photo, the next frame.
2 I refer to the "arriving-values thread" ... in fact! NewValueArrives may, or may not, run on the main thread!!!! It may run on the thread of the plugin, or on some other thread! It may actually be completely single-threaded by the time you deal with the NewValueArrives call! It just doesn't matter! What you do, and all you can do, in a frame-based paradigm, is, "leave laying around" information which Components such as ShowThermoValue, may use, as, they see fit.
Another solution to run code on the main thread, but without requiring a game object and MonoBehavior, is to use SynchronizationContext:
// On main thread, during initialization:
var syncContext = System.Threading.SynchronizationContext.Current;
// On your worker thread
syncContext.Post(_ =>
{
// This code here will run on the main thread
Debug.Log("Hello from main thread!");
}, null);
Use UniRx's multithreading pattern, UniTask and RxSocket together.
[SerializeField] private Text m_Text;
async UniTaskVoid Connect() {
IPEndPoint endPoint = new IPEndPoint(IPAddress.IPv6Loopback, 12345);
// Create a socket client by connecting to the server at the IPEndPoint.
// See the UniRx Async tooling to use await
IRxSocketClient client = await endPoint.ConnectRxSocketClientAsync();
client.ReceiveObservable
.ToStrings()
.ObserveOnMainThread()
.Subscribe(onNext: message =>
{
m_Text.text = message;
}).AddTo(this);
// Send a message to the server.
client.Send("Hello!".ToByteArray());
}