Speckle-Unity Slow Streaming of medium/big projects

Hello!

I spent some time testing out the latest Speckle - Unity connector. The receiver on Unity side works smooth and fast, with smaller projects (~250 objects / total children count), but with medium/big projects (over 11500 objects) it says Receiving and the little progress bar is stuck at about 10% progress for around 15 minutes. (after 15 mins I stopped the process)

Now my question is, what could we do about this, and is there any work around for this issue? Right now even on the Speckle.xyz server’s web viewer this medium project takes more then 5 minutes to load, but at least it loads.

One thing I recognized is that on the Speckle web viewer, the streaming is done gradually, meaning that it loads and shows objects one by one, while in the Unity it looks like a buffering time and then the models just pops up.

Please correct me if I’m wrong!

1 Like

Hey @dr.P ,

Ouch! 15 mins is clearly unacceptable :sweat_smile:. There’s a few things that could be going on, and first of all we should narrow it down and see if the problem is the download or the conversion.

Are you able to put some breakpoints around this line?

I’m not very experienced with unity, but I noticed some weird and unjustifiable performance issues when dealing with its API. For instance see these lines, I’m assuming you’re facing something similar and that it’s not really an issue with the streaming itself :slight_smile:

If you could also share the stream with me I’d be happy to investigate further next week!

Hey @teocomi ,

I tried to put some breakpoints around the line you suggested (line 148 at Receiver.cs) but I’m not able to reach that breakpoint which I put right before that line, in 15mins, so I guess it’s stuck at the beginning of the downloading probably?

I see the lines you are talking about, regarding the triangles, but I’m not sure how’s that speeding up any processes… It’s indeed interesting :thinking:

It’s stuck at this point

Alright, I’ll dive deep into this next week :slight_smile:
Cc @dimitrie since he has been debugging network issues recently.

1 Like

Ok, sounds good! :slight_smile:
BTW I sent you the stream link in DM, let me know if it works. :slight_smile:

1 Like

Hey @teocomi ! Any news on this? :slight_smile:

Not yet, sorry! Planning to look at it by the end of the week :slight_smile:

It’s ok @teocomi , no rush. Please let me know when you have any news on this. :slight_smile:

Hey @dr.P !

We started looking into this and have some preliminary findings (thanks dim & cristi):

  1. the model is actually huge ~800mb of data
  2. it seems some geometry in the model is extremely heavy, in particular, some chairs having ~300k vertices each!
  3. the Unity connector is much slower at receiving the model compared to other connectors (weird as they use the same code)

I’m still looking into (3), in the meantime, I’d suggest trying to replace/simplify those chairs or at least exclude them from the stream. It should make everything much faster!

2 Likes

@pablothedolphin are you still around these parts? If so would really like to bump heads regarding this slowness issue, I just tried again and exactly the same code takes 25min in unity and 3min in Rhino…

Hey @teocomi and @dr.P,

This slowness is actually a result of the huge overhead involved in creating new instances of objects in Unity. This issue exists in any game engine and is beyond just rendering. This issue is made worse by the fact that Unity’s runtime aims to run at 60fps. Writing async code that plays nice with a game engine’s framerate is the challenge we’re dealing with here.

The solution would be to create the received objects between each batch that’s been downloaded then call await Task.Yield (); to yield to the next frame. From there, you can tweak how many objects you want to request per batch to get the best result.

Hope this helps!

5 Likes

Thanks for chiming in dude, appreciate it!

The slowness we’re experiencing happens before objects are converted and instantiated in Unity, it happens purely when data is being retrieved from the server.

I’ve described the issue more in detail here.

1 Like

swinging by to drop a random thought! @teocomi I’m wondering what the performance difference would be like when using the unity connector in a standalone build… maybe there is some sort of bottleneck during speckle downloads that is being created by unity’s call stack. :chipmunk: I’ll do a quick test on this later today with the profiler :test_tube:

Thanks David, this would surely help!
In my latest tests, I figured the bottleneck being the deserialization, @Jedd has found similar issues when deserializing metadata in editor mode, which might be related…
See Serialized `SpeckleProperties.Data` using `ISerializationCallbackReceiver` and `Speckle.Core.Api.Operations` by JR-Morgan · Pull Request #29 · specklesystems/speckle-unity · GitHub

3 Likes

I checked the profile during a stream download during play + edit mode and it seems like the “editor-loop” is taking up a lot of space but it’s hard to read the profiler stats when we are calling re-paint a bunch of times on the stream manager. I’ll take a pass at breaking down the stream manager functionality, my guess is the editor functionality we have right now is causing a lot of lag.

2 Likes

Hi guys, how are you doing?
Is there any news regarding this issue? :innocent:

Hi Petar,

@cristi has made some big performance improvements to (de)serilisation performance, I’ve just updated the Unity connector with these changes, so please checkout the main branch, you should see a significant speed boost!

I believe Cristi has measured anywhere from a 2-10x speed increase!

Let us know how you get on.

2 Likes

Hi Jedd,

I’ve tested the latest build for couple of hours, but unfortunately none of my streams got streamed. :frowning:
I tried to stream a smaller project (around 241 objects) but the outcome was this:

  1. Receiving stuck at this point, without any errors:

  2. Then I tried to use the “Receive!” button on “EditorReceiver” gameobject, but I got this error:


    The error textually:

Exception: Cannot deserialize System.Collections.Generic.List`1[[System.Object, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]] to Speckle.Core.Models.Base
Speckle.Core.Serialisation.BaseObjectDeserializerV2.Dict2Base (System.Collections.Generic.Dictionary`2[TKey,TValue] dictObj) (at <7f5015df359e46e0a95d5555b36cecaa>:0)
Speckle.Core.Serialisation.BaseObjectDeserializerV2.ConvertJsonElement (Speckle.Newtonsoft.Json.Linq.JToken doc) (at <7f5015df359e46e0a95d5555b36cecaa>:0)
Speckle.Core.Serialisation.BaseObjectDeserializerV2.ConvertJsonElement (Speckle.Newtonsoft.Json.Linq.JToken doc) (at <7f5015df359e46e0a95d5555b36cecaa>:0)
Speckle.Core.Serialisation.BaseObjectDeserializerV2.DeserializeTransportObject (System.String objectJson) (at <7f5015df359e46e0a95d5555b36cecaa>:0)
Speckle.Core.Serialisation.DeserializationWorkerThreads.ThreadMain () (at <7f5015df359e46e0a95d5555b36cecaa>:0)
Rethrow as AggregateException: One or more errors occurred.
System.Threading.Tasks.Task.ThrowIfExceptional (System.Boolean includeTaskCanceledExceptions) (at <695d1cc93cca45069c528c15c9fdd749>:0)
System.Threading.Tasks.Task`1[TResult].GetResultCore (System.Boolean waitCompletionNotification) (at <695d1cc93cca45069c528c15c9fdd749>:0)
System.Threading.Tasks.Task`1[TResult].get_Result () (at <695d1cc93cca45069c528c15c9fdd749>:0)
Speckle.Core.Serialisation.BaseObjectDeserializerV2.ConvertJsonElement (Speckle.Newtonsoft.Json.Linq.JToken doc) (at <7f5015df359e46e0a95d5555b36cecaa>:0)
Speckle.Core.Serialisation.BaseObjectDeserializerV2.ConvertJsonElement (Speckle.Newtonsoft.Json.Linq.JToken doc) (at <7f5015df359e46e0a95d5555b36cecaa>:0)
Speckle.Core.Serialisation.BaseObjectDeserializerV2.ConvertJsonElement (Speckle.Newtonsoft.Json.Linq.JToken doc) (at <7f5015df359e46e0a95d5555b36cecaa>:0)
Speckle.Core.Serialisation.BaseObjectDeserializerV2.DeserializeTransportObject (System.String objectJson) (at <7f5015df359e46e0a95d5555b36cecaa>:0)
Speckle.Core.Serialisation.BaseObjectDeserializerV2.Deserialize (System.String rootObjectJson) (at <7f5015df359e46e0a95d5555b36cecaa>:0)
Speckle.Core.Api.Operations+<Receive>d__3.MoveNext () (at <7f5015df359e46e0a95d5555b36cecaa>:0)
--- End of stack trace from previous location where exception was thrown ---
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () (at <695d1cc93cca45069c528c15c9fdd749>:0)
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess (System.Threading.Tasks.Task task) (at <695d1cc93cca45069c528c15c9fdd749>:0)
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (System.Threading.Tasks.Task task) (at <695d1cc93cca45069c528c15c9fdd749>:0)
System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd (System.Threading.Tasks.Task task) (at <695d1cc93cca45069c528c15c9fdd749>:0)
System.Runtime.CompilerServices.TaskAwaiter`1[TResult].GetResult () (at <695d1cc93cca45069c528c15c9fdd749>:0)
Speckle.ConnectorUnity.StreamManagerEditor+<Receive>d__43.MoveNext () (at Assets/Speckle Connector/Editor/StreamManagerEditor.cs:162)
Rethrow as SpeckleException: One or more errors occurred.
Speckle.ConnectorUnity.StreamManagerEditor+<Receive>d__43.MoveNext () (at Assets/Speckle Connector/Editor/StreamManagerEditor.cs:183)
--- End of stack trace from previous location where exception was thrown ---
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () (at <695d1cc93cca45069c528c15c9fdd749>:0)
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess (System.Threading.Tasks.Task task) (at <695d1cc93cca45069c528c15c9fdd749>:0)
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (System.Threading.Tasks.Task task) (at <695d1cc93cca45069c528c15c9fdd749>:0)
System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd (System.Threading.Tasks.Task task) (at <695d1cc93cca45069c528c15c9fdd749>:0)
System.Runtime.CompilerServices.TaskAwaiter.GetResult () (at <695d1cc93cca45069c528c15c9fdd749>:0)
Speckle.ConnectorUnity.StreamManagerEditor+<OnInspectorGUI>d__44.MoveNext () (at Assets/Speckle Connector/Editor/StreamManagerEditor.cs:316)
--- End of stack trace from previous location where exception was thrown ---
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () (at <695d1cc93cca45069c528c15c9fdd749>:0)
System.Runtime.CompilerServices.AsyncMethodBuilderCore+<>c.<ThrowAsync>b__6_0 (System.Object state) (at <695d1cc93cca45069c528c15c9fdd749>:0)
UnityEngine.UnitySynchronizationContext+WorkRequest.Invoke () (at <07c89f7520694139991332d3cf930d48>:0)
UnityEngine.UnitySynchronizationContext:ExecuteTasks()

Any idea what went wrong here? :thinking:

Hi Petar,

Sorry you are having problems :worried:

Is it possible for you to invite me to a stream that’s failing?

Could you also tell me which program you are sending geometry from? and what version of that connector you are using.

I’ve done some testing my end, and I can only reproduce your issue with stream send from an old version of the Revit connector.
If this issue is reproduceable with the any latest versions of a connector then that’s concerning!

1 Like

Hi Petar,
Any updates?, I hope you still aren’t having issues.
Let me know and we can work things out.

2 Likes