Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 13 Next capture
2021 2022 2023
1 capture
13 Jun 22 - 13 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
0
Question by rageingnonsense · Feb 06, 2015 at 11:31 PM · lagthreads

Worker thread blocking main thread?

I am writing my own custom terrain engine, as I want to have spherical terrain with LOD support. I have a (relatively) low poly sphere that I subdivide and deform based off of noise generated on the fly, and desired LOD.

Deforming the highest LOD chunks was causing significant lag spikes (a little over a second per chunk being processed), so I decided to put the slowest part of it (noise generation) in it's own thread (using code found here: http://answers.unity3d.com/questions/357033/unity3d-and-c-coroutines-vs-threading.html). I expected this to solve the problem, but it didn't. For some reason, even though the processing is now on it;s own thread, the main thread still has a lag spike. Here is the relevant code:

 public class ThreadedJob {
     private bool m_IsDone = false;
     private object m_Handle = new object();
     private System.Threading.Thread m_Thread = null;
     public bool IsDone {
         get {
             bool tmp;
             lock (m_Handle) {
                 tmp = m_IsDone;
             }
             return tmp;
         }
         set {
             lock (m_Handle) {
                 m_IsDone = value;
             }
         }
     }
     public virtual void Start() {
         m_Thread = new System.Threading.Thread(Run);
         m_Thread.Start();
     }
     public virtual void Abort() {
         m_Thread.Abort();
     }
     protected virtual void ThreadFunction() { }
     protected virtual void OnFinished() { }
     public virtual bool Update() {
         if (IsDone) {
             OnFinished();
             return true;
         }
         return false;
     }
     private void Run() {
         ThreadFunction();
         IsDone = true;
     }
 }
 
 
 
 public class ChunkDeform : ThreadedJob {
     public int chunkID;
     public int LOD;
     public HeightMap heightMap;
     public GameObject chunk;
     public Mesh tapeMesh;
     public int[] manifoldIndices;
 
     private Mesh chunkMesh;
     private Vector3[] chunkVerts;
     private Vector3[] tapeVerts;
     private Vector2[] chunkUVs;
     private Vector2[] tapeUVs;
 
     // for profiling
     private float timeSpent;
     private int threadID;
 
     public override void Start() {
         chunkMesh = chunk.GetComponent<MeshFilter>().mesh;
         chunkVerts = chunkMesh.vertices;
         tapeVerts = tapeMesh.vertices;
         chunkUVs = chunkMesh.uv;
         tapeUVs = tapeMesh.uv;
 
         heightMap = new HeightMap();
         heightMap.mapHeight = 16384;
         heightMap.mapWidth = 16384;
         heightMap.seed = 42;
 
         timeSpent = Time.realtimeSinceStartup;
                 
         base.Start();
     }
 
     protected override void ThreadFunction() {
         // Do your threaded task. DON'T use the Unity API here    
         threadID = System.Threading.Thread.CurrentThread.ManagedThreadId;
         for (int v = 0; v < chunkVerts.Length; v++) {
             chunkVerts[v] = chunkVerts[v] * ((heightMap.GetValue(chunkUVs[v].x, chunkUVs[v].y) * 0.05f) + 1);
         }
 
         for (int v = 0; v < tapeVerts.Length; v++) {            
             tapeVerts[v] = tapeVerts[v] * ((heightMap.GetValue(tapeUVs[v].x, tapeUVs[v].y) * 0.05f) + 1);
         }
     }
 
     protected override void OnFinished() {
         // This is executed by the Unity main thread when the job is finished
         chunkMesh.vertices = chunkVerts;
         chunkMesh.RecalculateNormals();
 
         tapeMesh.vertices = tapeVerts;
         tapeMesh.RecalculateNormals();
 
         Vector3[] chunkNormals = chunkMesh.normals;
         Vector3[] tapeNormals = tapeMesh.normals;
                
 
         // fix the normals
         for (int tv = 0; tv < tapeVerts.Length; tv++) {
             for (int i = 0; i < manifoldIndices.Length; i++) {
                 Vector3 sample = chunkVerts[manifoldIndices[i]];
                 float distance = Vector3.Distance(sample, tapeVerts[tv]);
                 if (distance < 0.1) {
                     // these match, use tape vertex normal
                     chunkNormals[manifoldIndices[i]] = tapeNormals[tv];
                 }
             }
         }
 
         chunkMesh.normals = chunkNormals;
         chunkMesh.RecalculateBounds();
 
         chunk.GetComponent<MeshCollider>().sharedMesh = chunkMesh;
 
         timeSpent = Time.realtimeSinceStartup - timeSpent;
         Debug.Log("ChunkDeform tid "+threadID+": deformed in " + timeSpent.ToString() + " seconds");
     }
 }


In another class that runs on the main thread, the following code is run for any chunk that has yet to be deformed:

 // it has not, so set up a chunk deform job
                 // for this LOD              
                 GameObject chunk = transform.FindChild("Planet_LOD" + currentLOD + "." + chunkID.ToString("D3")).gameObject;
                 int[] mvIndices = MeshHelper.BuildManifoldVertices(chunk.GetComponent<MeshFilter>().mesh);
                 // store these manifold indicies for later use
                 chunkTree.manifoldIndices[chunkID][currentLOD] = mvIndices;
 
                 ChunkDeform deformJob = new ChunkDeform();
                 deformJob.chunkID = chunkID;
                 deformJob.LOD = currentLOD;
                 //deformJob.heightMap = (HeightMap)heightMap.Clone();
                 deformJob.chunk = chunk;
                 deformJob.tapeMesh = chunkTree.tape[chunkID, currentLOD].GetComponent<MeshFilter>().mesh;                
                 deformJob.manifoldIndices = chunkTree.manifoldIndices[chunkID][currentLOD];
 
                 deformJobs[currentLOD] = deformJob;
                 deformJobs[currentLOD].Start();



Finally, in the Update() function of that class, I run this to check for completion:

 // check on our threaded jobs
         foreach (ChunkDeform cd in deformJobs) {
             if (cd != null) {
                 if (cd.Update()) {                 
                     deformed[cd.LOD] = true;
                     deformJobs[cd.LOD] = null;                    
                 }
             }
         }


You can see I added code to check on the thread ID to ensure it is really in it's own thread, which it is. So, I am confused about why this would still cause lag even though the heavy processing is on its own thread now. Is there something I am missing? I feel like maybe I have a misunderstanding

Comment
Add comment · Show 7
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Glurth · Feb 07, 2015 at 02:57 AM 0
Share

I assume you have tested to confirm that the delay is not cause by some function you call that is NOT in the thread, like .. $$anonymous$$eshHelper.Build$$anonymous$$anifoldVertices

avatar image rageingnonsense · Feb 07, 2015 at 06:22 PM 0
Share

@Glurth: I'm glad you brought that up. While I had did some testing of speed outside of the thread, I did not test it well enough it seems!

The slow points are the processing of the manifold vertices (either gathering them in the first place, or looping through them; unsure yet); and, surprisingly enough, assignment of the meshcollider.

I can easily fix the issue with the manifold vertex lookup by altering it to be thread safe. How in the hell do I fix the issue of assigning the meshcollider though, considering Unity API is not thread safe!?

avatar image Glurth · Feb 07, 2015 at 06:34 PM 0
Share

you mean here?

  chunk$$anonymous$$esh.RecalculateBounds();
  
  chunk.GetComponent<$$anonymous$$eshCollider>().shared$$anonymous$$esh = chunk$$anonymous$$esh;

I would suspect it's the recalculatebounds function, rather than the assignment of the collider. Or have you eli$$anonymous$$ated that?

If you construct the mesh properly, I don't think you will need to call recalculateBounds. That's been my experience with procedural meshes, at least DRAWING them. I have not had to implement colliders on them though, so that may be different.

avatar image rageingnonsense · Feb 07, 2015 at 06:44 PM 0
Share

@Glurth: I have eli$$anonymous$$ated RecalculateBounds() as the problem. The biggest slowdown comes from : chunk.GetComponent<$$anonymous$$eshCollider>().shared$$anonymous$$esh = chunk$$anonymous$$esh;

avatar image Glurth · Feb 07, 2015 at 07:00 PM 0
Share

Ok, that IS odd. perhaps it has to do some processing internally when you make the assignment? $$anonymous$$gest sanity check: breaking it up into two lines, to further narrow it down: One line for the getcomponent call, a second line just for the assignment.

If it DOES do lots of processing during the assignment, you may need to cache your unassigned meshes. Then in a co-routine (which should have access to the API), assign everything in the cache to the appropriate collider. (I have only used threads, but not co-routines in unity so far; so take this suggestion with a grain of salt.)

Edit: another thought- have your meshes created and attached, before you even launch your thread to recompute the mesh. rather than create a new mesh that you will need to assign- modify the mesh that is ALREADY assigned.

Show more comments

0 Replies

· Add your reply
  • Sort: 

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

20 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

ThreadPooled loops cause lag 0 Answers

Is it possible to split Texture2D.LoadImage into multiple parts? 1 Answer

CombineMeshes in thread 1 Answer

Loading animation while Application.LoadLevel in UnityFree 0 Answers

multithread constructor call create gameObject 2 Answers


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges