Unity Community


Page 1 of 2 12 LastLast
Results 1 to 20 of 28

  1. Posts
    44

    Why no multithreading with Unity objects?

    Anybody know why Unity made their classes so as to prevent any sort of threading/multicore code? This would open up so many possibilities yet Unity keeps it locked into one thread. Yes, I know you can do some calculations in other threads, but let's be real here. Why restrict it?


    Anyone who's tried has probably received:

    "Internal <MethodNameHere> can only be called from the main thread."

    EDIT: Not asking for true multithreaded-safeness here, just ease up on all the restrictions for people that know how to correctly multithread.
    Last edited by Sequence; 08-06-2013 at 07:44 PM.

  2. Code Maker

    Location
    Canberra
    Posts
    849
    Internally Unity does a lot of multi threading and scheduling, but game level code can not be multi threaded for a variety of reasons. The primary one being that when the engine is ready for gameworld updates it is in a known and valid state. If for example you attempted to change a particle system during a threaded particle system update what would you expect to happen?

    That being said threading is supported in game code so long as the calls you make are not into unity API. What you can do is use a producer / consumer based architecture to generate calls you would like to make and then in a game update execute them. This means you can utilise more CPU, and issue calls into unity at the 'correct' time.
    Have you tried the Strumpy Shader Editor?


  3. Location
    Germany
    Posts
    337
    Hey Tim, my guess is that Sequence is aware of that. But why dont we have access to Methods that are not changing any unity object?
    For Example: I have an Level of Detail system currently for sale on the Asset Store wich is Multi Threaded. To optimise my calculation Thread I would like to do Frustrum culling but I didnt have access to
    Code:  
    1. static Plane[] CalculateFrustumPlanes(Matrix4x4 worldToProjectionMatrix);
    and
    Code:  
    1. static bool TestPlanesAABB(Plane[] planes, Bounds bounds);

    these methods dont interuppt any Unity Object (at least not from our point of view), so why dont we have access to these?


  4. Posts
    44
    Quote Originally Posted by Tim C View Post
    Internally Unity does a lot of multi threading and scheduling, but game level code can not be multi threaded for a variety of reasons. The primary one being that when the engine is ready for gameworld updates it is in a known and valid state. If for example you attempted to change a particle system during a threaded particle system update what would you expect to happen?

    That being said threading is supported in game code so long as the calls you make are not into unity API. What you can do is use a producer / consumer based architecture to generate calls you would like to make and then in a game update execute them. This means you can utilise more CPU, and issue calls into unity at the 'correct' time.
    Oh, I see. I'm realizing more and more how everything won't work multithreaded. Even creating a gameObject in another thread seems fine until you realize that it needs to get added to the global gameObject list. (Which is on the main thread). I see it's a lot harder than you'd expect.



    Quote Originally Posted by element_wsc View Post
    Hey Tim, my guess is that Sequence is aware of that. But why dont we have access to Methods that are not changing any unity object?
    For Example: I have an Level of Detail system currently for sale on the Asset Store wich is Multi Threaded. To optimise my calculation Thread I would like to do Frustrum culling but I didnt have access to
    Code:  
    1. static Plane[] CalculateFrustumPlanes(Matrix4x4 worldToProjectionMatrix);
    and
    Code:  
    1. static bool TestPlanesAABB(Plane[] planes, Bounds bounds);

    these methods dont interuppt any Unity Object (at least not from our point of view), so why dont we have access to these?
    This makes sense (to me at least) and should probably be opened up. I don't know if static functions are thread safe or not I guess.

    But man, if Unity was multi-thread capable, that would be the ultimate.


  5. Location
    Auckland, New Zealand
    Posts
    3,441
    Quote Originally Posted by element_wsc View Post
    Hey Tim, my guess is that Sequence is aware of that. But why dont we have access to Methods that are not changing any unity object?
    For Example: I have an Level of Detail system currently for sale on the Asset Store wich is Multi Threaded. To optimise my calculation Thread I would like to do Frustrum culling but I didnt have access to
    Code:  
    1. static Plane[] CalculateFrustumPlanes(Matrix4x4 worldToProjectionMatrix);
    and
    Code:  
    1. static bool TestPlanesAABB(Plane[] planes, Bounds bounds);

    these methods dont interuppt any Unity Object (at least not from our point of view), so why dont we have access to these?

    Frustum culling is done internally and automatically by Unity. I would think trying to do it yourself when Unity has already done it internally would cause problems, as in Tim's particle system example above?


  6. Posts
    44
    Quote Originally Posted by Meltdown View Post
    Frustum culling is done internally and automatically by Unity. I would think trying to do it yourself when Unity has already done it internally would cause problems, as in Tim's particle system example above?
    I don't think element_wsc wants to calculate the frustum planes for a camera, but just for an arbitrary matrix4x4 and get an array of planes. I don't think this should cause any issues internally right?


  7. Location
    Dallas, Tx
    Posts
    1,251
    I don't think you can even use Quaternions or Vectors, which does not make sense to me. These would be handy when trying to do calculations in other threads, instead you have to create your own.
    “A year from now you will wish you had started today.” -Karen Lamb
    “Even if you stumble, you’re still moving forward.”
    “Use what talents you possess, the woods will be very silent if no birds sang there except those that sang best.” -Henry van Dyke


  8. Location
    Portland, OR
    Posts
    1,819
    You can do alot with multithreading in Unity actually...

    one thing to keep in mind is that simple types in C# are atomic

    This means you can safely declare a bunch of Global ints or floats, then have an alternate thread update and make changes to them, while the main thread reads them in. With really no extra code at all, as long as your using simple types, your main Update loop will easily stay at 60 fps because it is not having to lock any variables. It may have to pause for like a fraction of a millisecond if it tries to read an int at the same time another thread is reading the int, but the reading of an int is so fast that I found it makes no performance hit at all. 60 fps can easily be maintained even with like an array of 10,000 ints being read and written to and from by multiple threads, while the Update loop is trying to access that same array.

    If you start thinking in terms of that, you can actually put alot of game logic into alternate threads. Make it so all serious game logic executes at a certain rate in alternate thread, then the Update loop on the main thread just polls all the global variables that were updated in alternate thread, then makes the calls to Unity's internal system to update whats on screen.

    This is how the game 'enrgy' in my profile works. Over 70% of the CPU load is actually happening on alternate threads in that game. It simulates a bunch of particles moving around, which all the particle movement is done on a seperate thread. The Update loop just reads what has been done on the alternate thread, then loads thats data into the particle system.
    Last edited by techmage; 08-07-2013 at 04:18 PM.
    enrgy liquid wars clone for iOS - free version available
    psi.kart weapons-based techie racer for iOS - free


  9. Posts
    3,768
    Quote Originally Posted by techmage View Post
    If you start thinking in terms of that, you can actually put alot of game logic into alternate threads. Make it so all serious game logic executes at a certain rate in alternate thread, then the Update loop on the main thread just polls all the global variables that were updated in alternate thread, then makes the calls to Unity's internal system to update whats on screen.
    Precisely. It's only MonoBehaviours which need to run in Unity's main thread, and there's no reason that you have to implement your logic directly inside them. You can simply use them as adapters to apply logic calculated outside of the scene (in whatever threading structure you want) to objects inside the scene.

    Having said that, I've so far never needed to do any such thing.
    Hobby project: Master Thief


  10. Location
    Ryazan, Russia
    Posts
    779
    Quote Originally Posted by jc_lvngstn View Post
    I don't think you can even use Quaternions or Vectors, which does not make sense to me. These would be handy when trying to do calculations in other threads, instead you have to create your own.
    Actually, you can use them, and matrices too. I'm not sure about all the methods, but no problems so far.


  11. Location
    Virginia, USA
    Posts
    1,372
    Quote Originally Posted by techmage View Post
    one thing to keep in mind is that simple types in C# are atomic

    This means you can safely declare a bunch of Global ints or floats, then have an alternate thread update and make changes to them, while the main thread reads them in. With really no extra code at all, as long as your using simple types, your main Update loop will easily stay at 60 fps because it is not having to lock any variables. It may have to pause for like a fraction of a millisecond if it tries to read an int at the same time another thread is reading the int, but the reading of an int is so fast that I found it makes no performance hit at all. 60 fps can easily be maintained even with like an array of 10,000 ints being read and written to and from by multiple threads, while the Update loop is trying to access that same array.

    This is how the game 'enrgy' in my profile works. Over 70% of the CPU load is actually happening on alternate threads in that game. It simulates a bunch of particles moving around, which all the particle movement is done on a seperate thread. The Update loop just reads what has been done on the alternate thread, then loads thats data into the particle system.
    How does this work in code? I'm completely unfamiliar with how to code threading into C#/Unity. What you're doing sounds cool and I'd like to know how.

    Gigi
    What Went Well - a Meditation on Gratitude and Happiness
    Good Sex, Great Marriage - Still The #1 Marriage App on iOS *** Also on Android!
    The Gratitude Habit - 1000 5-star reviews

    The Secret of Success? Try; Improve; Repeat- 'till too good to ignore.


  12. Posts
    106
    Quote Originally Posted by Gigiwoo2 View Post
    How does this work in code? I'm completely unfamiliar with how to code threading into C#/Unity. What you're doing sounds cool and I'd like to know how.

    Gigi
    Yes, please! Share with us


  13. Location
    Portland, OR
    Posts
    1,819
    There isn't a whole lot to explain really. Well there is actually a TON to explain, but without specific cases it's hard to explain that. The pattern of multithreading I describe is really specific to a specific way the game internally functions.

    Basically this is all you do at the base.

    Code:  
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.Threading;
    4.  
    5. public class ThreadedGameLogicObject : MonoBehaviour
    6. {
    7.     private Thread thread;
    8.     private Transform thisTransform;
    9.     private Vector3 currentPos;
    10.    
    11.     private void Start()
    12.     {
    13.         thisTransform = this.transform;
    14.        
    15.         thread = new Thread(ThreadUpdate)
    16.         thread.Start();
    17.     }
    18.    
    19.     private void Update()
    20.     {
    21.         thisTransform.position = currentPos;
    22.     }
    23.    
    24.     //I used FixedUpdate function to modulate the speed at which the threads execute
    25.     //there is potentially like a dozen different ways to do this, and this one might not be ideal
    26.     //my game used no physics, so I could change the fixed timestep to whatever I needed to make this work
    27.     private void FixedUpdate()
    28.     {
    29.         updateThread = true;
    30.     }
    31.    
    32.     private bool runThread = true;
    33.     private bool updateThread;
    34.     private void ThreadUpdate()
    35.     {
    36.         while (runThread)
    37.         {
    38.             if (updateThread)
    39.             {
    40.                 updateThread = false;
    41.            
    42.                 currentPos += 1f; //some crazy ass function that takes forever to do here
    43.             }
    44.         }   
    45.     }
    46.    
    47.     private void OnApplicationQuit()
    48.     {
    49.         EndThreads();   
    50.     }
    51.    
    52.     //This must be called from OnApplicationQuit AND before the loading of a new level.
    53.     //Threads spawned from this class must be ended when this class is destroyed in level changes.
    54.     public void EndThreads()
    55.     {
    56.         runThread = false;
    57.         //you could use thread.abort() but that has issues on iOS
    58.        
    59.         while (thread.IsAlive())
    60.         {
    61.             //simply have main loop wait till thread ends
    62.         }
    63.     }
    64.  
    65. }

    Then you just have to really think about how your laying out your functions.

    simple types are Atomic, but processes are not atomic

    So for example, lets assume globalFloatArray is something accessed by many different threads:

    This simple line IS atomic:
    float myVar = globalFloatArray[10];

    However this is not:
    if ( globalFloatArray[10] != null )
    float localFloat = globalFloatArray[10];
    //use localFloat in operation

    Because between doing the null check on floatArray[10] and actually attempting to read out the variable, it is possible that floatArray[10] could have been set to null by another thread.

    The standard C# way you to deal with this, if you search google, you will come up with something like this:

    lock(lockObject)
    {
    if ( globalFloatArray[10] != null )
    float localFloat = globalFloatArray[10];
    //do your stuff with local float
    }

    Which is atomic. However I found you must absolutely avoid locking variables accessed from the main thread. If your alternate thread locks a variable, then your main thread tries to access it during the lock, your main thread will wait until it is unlocked. This will cause frame stutter. There is serious overhead to locking a variable, it does not happen quickly. It should be avoided.

    What can be done is you can program in a way that you never need to do anything but simple reads and writes to simple types.

    So in the example above you could do something like this

    float localFloat = globalFloatArray[10]
    if (localFloat != null)
    //do your stuff with localFloat
    globalFloatArray[10] = localFloat;

    This is atomic, accomplishes the same thing essentially, and doesn't use locks. Multiple threads can all be reading in and out of globalFloatArray at a full 60 fps in this manner. But you have to think about how to set that up so that it is atomic without locks.

    The game in my profile, enrgy, uses this base design pattern. It seems to run without any issues on Windows, OSX and iOS.

    However when you get into it, you may find that not everything can be adapted to this type of pattern very easily. Some things may be impossible. It also must be kept in mind that the thread updating your transform position is out of sync with your update thread. So ideally you alternate thread is executing faster than your Update loop, and for each cycle of the Update loop, your alternate thread will have done something to make things move. But it could be possible depending on how much code you put in your alternate thread that your alternate thread executes slower than your main update loop. At which point your alternate thread may only be outputting a new position only 15 times a second, while your main update loop is updating 60 times a second. You will have to interpolate the output from your alternate thread.

    enrgy has two alternate threads running. One which goes faster than the update loop, and one that goes about 1/4 the speed of the update loop. The thread that goes 1/4 the speed is basically the path finding algorithm, it is a very intense calculation, it uses up the entire second CPU on the iPad, and it runs at full speed with no time limits or pauses, constantly updating the path finding grid based on user input. The second alternate thread moves all the particles, this one can run twice the speed of Update loop, and this one is time constrained to go at a certain rate. This thread smoothly moves the particles along the grid that the path finding algorithm laid out, and also does all the game logic of having one particle attack another particle, or test if a particle is hitting anything.

    The main thread and Update loop then reads all these particle positions out of a huge global array, clumps together nearby particles to be represented by a single larger particle, then uploads them into a ParticleSystem for display.

    Runs reliably as far as I can tell.
    Last edited by techmage; 08-08-2013 at 02:43 PM.
    enrgy liquid wars clone for iOS - free version available
    psi.kart weapons-based techie racer for iOS - free


  14. Posts
    154
    @techmage
    Your hack/solution to the multithreading problem blew my mind. I had thoughts on such things before but never could figure out how to work against the if(null == object) problems. Your solutions is genius

  15. Super Moderator
    Location
    Great Britain
    Posts
    9,647
    Techmage, is there any reason to use FixedUpdate at all when you can use InvokeRepeating ? just curious!
    Currently working with Sony on our new
    PS4 and Vita game in Unity!

    This post is not representative of Simian Squared Ltd


  16. Location
    Portland, OR
    Posts
    1,819
    Quote Originally Posted by hippocoder View Post
    Techmage, is there any reason to use FixedUpdate at all when you can use InvokeRepeating ? just curious!
    Nope.

    I just used FixedUpdate cause it was there and I wasn't using it. InvokeRepeating would probably better.

    Even better than that is figuring out some timing mechanism that doesn't rely on Unity's main loop at all. I am sure it is possible and not too diffucult, I just haven't needed it so I haven't gone through the process of figuring it out. You would need to use some kind of built in .NET timer that is thread safe, because obviously you can't call Time.time from a seperate thread.
    enrgy liquid wars clone for iOS - free version available
    psi.kart weapons-based techie racer for iOS - free


  17. Posts
    1,321
    Quote Originally Posted by techmage View Post
    So in the example above you could do something like this

    float localFloat = globalFloatArray[10]
    if (localFloat != null)
    //do your stuff with localFloat
    globalFloatArray[10] = localFloat;

    This is atomic, accomplishes the same thing essentially, and doesn't use locks. Multiple threads can all be reading in and out of globalFloatArray at a full 60 fps in this manner. But you have to think about how to set that up so that it is atomic without locks.
    That's not atomic and definitely doesn't accomplish the same thing. Each individual line is atomic itself but the block as a whole, and the globalArray, is not atomic. You would need to use a lock if access to the array was supposed to be atomic. For instance, if you had two threads running versions of that code with different things in the "do stuff" section like the first moves a position by an inch and the second thread moves it by a mile, you could get thread A grabbing a local value of 1, then B grabbing a local value of 1, and B making a very important change to 5000 and exiting, and then A ignoring that important change and overwriting it with 1.1. And your important change that was supposed to teleport the enemy 5000 feet away gets lost and it only moves an inch.

    If you don't actually need to make sure that each thread is accessing the same values, then it doesn't actually need to be atomic, and you can use the code you wrote. But I wouldn't call the code you wrote atomic just because it doesn't have locks, as that's not what the word means.


  18. Location
    Portland, OR
    Posts
    1,819
    I guess calling it atomic is too much. It does require a very specific use to work properly.

    What I describe is not very generic or open, it does require alot of very specific assumptions.
    enrgy liquid wars clone for iOS - free version available
    psi.kart weapons-based techie racer for iOS - free

  19. Volunteer Moderator
    Location
    Up there, in the sky!
    Posts
    1,725
    WRT the fixedupdate thing: Can't you just use .NET threadpool threads? ThreadPool.QueueUserWorkItem and so on.


  20. Posts
    174
    Wow, such an interesting thread.. It's over my head for now but, interesting! So essentially, this is how to access more cores, on muticore CPU's?

Page 1 of 2 12 LastLast

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •