- Home /
How to create a parallel for in Unity
Hello everyone. I know that mono doesn't support the System.Threading package but I would like to try to accellerate a texture creation by using something like:
Color[] dest, source;
// a lot of stuff...
void Update (){
// some other stuff...
Parallel.For (0, texture.height, y => {
for (int x = 0; x < texture.width; x++) {
int sourceIndex = (y * texture.width) + x;
dest[sourceIndex].r = source[sourceIndex].r;
dest[sourceIndex].g = source[sourceIndex].g;
dest[sourceIndex].b = source[sourceIndex].b;
}
});
// some other stuff...
}
Do you think is it possible to do this parallel execution in Unity?
Thanks :)
The Unity API is not thread safe. You will need to either use a different API or create your own to be able to do that
yes... I know. I was looking for some advice about how to do it using the C# threads or if maybe exists some kind of DLL to use to make a parallel calculation. I was thinking of writing my own C++ library to use the C++ parallel_for... but I don't know if this could be the best and fast solution.
Isn't there a way to write C/C++ in unity? It would certainly add some speed up..
Well... if you want to add some C/C++ code to Unity you must have Unity Pro and compile a DLL. Then you can use it by placing your compiled library in the Plugin folder of your Unity project. After that you have to write a C# wrapper that declare the exported symbols of your DLL so you can use in the others C# scripts.
not fun, but if critical what else can you do.. maybe you can get away with processing it on the graphics card, but i'm not very familiar with GL.
Answer by CHPedersen · Jul 08, 2013 at 10:56 AM
Actually, though the general rule is that the Unity API is off-limits in separate threads, there are notable exceptions... And the Color class is one of them. It's also perfectly legal to access the static methods in Mathf, or writing into the console using Debug.Log, even though those are actually members of the UnityEngine namespace.
Study, for example, this wiki post by Eric5h5 which uses the Color class in separate threads while scaling a texture. You can do that just fine because it operates on "off-line" objects, so to speak. The offending part about separate threads and Unity only pertains to things that directly affect the real-time renderstate. That is, instantiating GameObjects, changing the transform of something, replacing a material, etc.
In your case, it looks to me like you're trying to parallelize the deep-copying of one texture's colors into another array, right? You can do that concurrently pretty easily by just instantiating a thread per row in the original texture. You already have the algorithm for accessing rows when flattened to a 1D array, and you don't even need to synchronize the threads' access to the array, because they won't be writing to the same area of the array as they handle one row each.
Make a method that takes object as parameter and start threads using ParameterizedThreadStart, passing the y variable as the parameter. In the method, cast the object back to an int, then use that to calculate the row of indices which are the write targets for that particular thread. Then, in the mainthread, loop over the height (the y's) and start a thread for each. It will look something pretty close to this:
Color[] dest, source;
Texture2D texture;
private void StartThreads()
{
for (int i = 0; i < texture.height; i++)
{
System.Threading.ParameterizedThreadStart pts = new System.Threading.ParameterizedThreadStart(ThreadedColorCopy);
System.Threading.Thread workerForOneRow = new System.Threading.Thread(pts);
workerForOneRow.Start(i);
}
}
private void ThreadedColorCopy(object yVariable)
{
int y = (int)yVariable;
for (int x = 0; x < texture.width; x++)
{
int sourceIndex = (y * texture.width) + x;
dest[sourceIndex].r = source[sourceIndex].r;
dest[sourceIndex].g = source[sourceIndex].g;
dest[sourceIndex].b = source[sourceIndex].b;
}
}
(The above is untested but might work out of the box)
Caveat: It strikes me that one thread per row might be a little too aggressively multithreaded, as it would create 512 separate threads for a 512x512 texture. Instead, it might be an idea to give each thread a region of multiple rows, i.e. one thread handles rows 0-19, thread two handles rows 20-39, etc. You get the idea. It will make the code slightly more complicated, but probably more robust.
Believe it or not, this is exactly what I'm currently writing. as soon as I will finish and test the code I'll post my solution. Thank you very much man !!!!
Currently busy with the same problem. I did an performance test.
for (int j = texture.width - 1; j >= 0; j--)
Is allot faster
Talking about optimizations. You should always try to use a thread pool.
Thread are expensive resources (not so hard but are). If you have more threads than CPU cores, for a pure processing (no waiting threads) you will losing a lot of time with thread switching overhead.
Answer by Eugenio · Jul 08, 2013 at 03:32 PM
Ok guys this is my solution. You can also set how many threads you want to use. For me works like a charm !!! ;) Hope it can be good for anyone else:
public int threadsToUse = 5;
Color[] dest, source;
Texture2D texture;
int threadsStarted, threadsCompleted;
int pixelsPerThread, lostPixels;
// a lot of stuff...
void Start() {
// lots of other stuff...
pixelsPerThread = texture.height / threadsToUse;
lostPixels = texture.height % threadsToUse;
}
void Update() {
if (threadsStarted == 0) {
// some other stuff...
threadsCompleted = 0;
for (int i = 0; i < threadsToUse; i++) {
ParameterizedThreadStart pts = new ParameterizedThreadStart(ThreadedColorCopy);
Thread workerForOneRow = new System.Threading.Thread(pts);
workerForOneRow.Start(i);
threadsStarted++;
}
}
else if (threadsCompleted == threadsToUse) {
threadsStarted = 0;
texture.SetPixels(dest);
texture.Apply();
}
}
private void ThreadedColorCopy(object threadParamsVar) {
int i = (int)threadParamsVar;
int fromY = i * (pixelsPerThread);
int toY = ((i + 1) * pixelsPerThread) - 1;
if (i == threadsToUse - 1) {
toY += lostPixels;
}
Debug.Log("Thread " + threadParams.threadId + " - from " + fromY + " to " + toY);
for (int y = fromY; y <= toY; y++) {
for (int x = textureWidth - 1; x >= 0; x--) {
int sourceIndex = (y * texture.width) + x;
dest[sourceIndex].r = source[sourceIndex].r;
dest[sourceIndex].g = source[sourceIndex].g;
dest[sourceIndex].b = source[sourceIndex].b;
}
}
threadsCompleted++;
}
:)
Just thought I would mention F# in connection to the above solution. I've been incorporating it more and more these days in my Unity projects, especially in regards to parallel and asynchronous tasks.
Not necessarily right for every project, but might be something you are interested in if you are doing a lot of concurrency work. Something similar to the above might go like so:
let copy len src dst =
Async.Parallel [ for i in 0..len..(Array.length src) - len ->
async { do Array.blit src i dst i len } ]
...where len
would be the chunk size of source Color array you wished to copy. Thread count would be deter$$anonymous$$ed by source length / chunk size.
Cool. I will definitely give it a go !!
Thanks for sharing :)
Answer by Sisso · Jul 05, 2013 at 05:24 PM
I dont have 100% sure, but I think that you can.
You can not access any unity3d stuff outside of main thread because unity is not thread safe, but you are free to use many threads do you like.
You can read some data in unity main thread, move it to a indenpendent type like byte[], instantiate a new thread to process the bytes, when it finished, you use the main thread to read the byte[] and update your data.
The project AStarpathfind do some stuff like that.
Thanks man, I'll take a look and eventually I'll post my solution here. :)
Answer by ZJP · May 11, 2017 at 03:15 PM
Have a look here : https://forum.unity3d.com/threads/c-optimized-and-advanced-classic-threads-for-unity-option-with-setthreadaffinitymask.463307/
Your answer
Follow this Question
Related Questions
Alternative for semaphores in Unity? 2 Answers
Jobhandle.IsCompleted == true even if job was not scheduled. 1 Answer
How to warm-up job threads 0 Answers
Mutex on Linux 0 Answers