EntityCollection.suspendEvents() equivalent for PointPrimitiveCollection

In the documentation for PointPrimitiveCollection.add(), it states the following:

Calling add is expected constant time. However, the collection's vertex buffer is rewritten - an O(n) operation that also incurs CPU to GPU overhead. For best performance, add as many pointPrimitives as possible before calling update.

As far as I can tell, there is no update method to call. Update seems to be called automatically at some point.

I am trying to update the position of around 1000 points in a collection with around 10,000 points. While everything works well, I'm noticing brief animation stuttering when the update occurs and some investigation has led me to believe that update is being called even before I have completed updating the positions.

If the update method was deprecated at some point, I would love to know what the solution to this problem is!

Hi there,

Could you send me some sample code of what you’re trying to do? You generally shouldn’t have to worry about calling update – that’s called by Cesium before each frame is rendering.

To generate a link to a working code example, follow the “sharing” instructions here: http://cesiumjs.org/tutorials/Sandcastle-Tutorial/

Best,

  • Rachel

Thanks for the response, there may have been a better title for this, but I've created a quick sample here:

What you should see is that the performance of updating all the points on the globe is fairly good, but it causes stuttering every second when the update occurs. This results in a very poor user experience particularly when dragging the globe around.

What I originally wanted to do was prevent Cesium from calling update and instead call it myself, but I'm open to any suggestions you may have to address this.

Thanks!

Once the primitive is in the Scene, update gets automatically called on it every frame, it has to be called in order for anything to draw. Don’t worry about it, that’s just the way it works. It has nothing to do with your performance here. The documentation is a little misleading and probably could use an update.

There are a few things you can do to improve performance in your example:

  1. Don’t use setInterval or setTimeout, these functions happen outside of the browser requestAnimationFrame render loop and can definitely be a source of stuttering. It’s better to do any updates you have in either preRender or clock.tick events. It’s not the end of the world if you don’t, but this is a best practice to follow.

  2. Even more important than 1 is garbage collection and memory pressure. You’re Sandcastle example is creating 100,000 new Cartesian3 instances every frame and then throwing them out. To be honest, I’m surprised this runs well at all, a few years ago the browser’s garbage collector was terrible and this alone would have caused 15 frames per second tops. Cartesian3.fromDegrees takes a result parameter as its parameter that will let you re-use a scratch object.

  3. Finally, don’t actually update all 100,000 points in a frame, it’s too much work to do at once. Instead, cap it at some lime (like 5000) and amortize it over all frames until everything is updated.

With these 3 items in mind, here’s an updated example that runs at a smooth 60fps and updates 5000 points every frame (which means 300,000 points get updated every second with zero stutter!).

http://cesiumjs.org/Cesium/Apps/Sandcastle/?src=Hello%20World.html&label=Showcases&gist=a66a6f436b44c37c65ec6ac7ecf597f6

Hope this helps and that you can easily adapt the example to your use case.

Thanks,

Matt

I just realized that you are also turning your latitude and longitude values into a string and then back to a number. This is a MAJOR performance problem. Here’s another updated example that avoid this and bumps the point updates per-frame up to 10,000, which means it’s doing 600,000 updates per second at a smooth 60 fps:

http://cesiumjs.org/Cesium/Apps/Sandcastle/?src=Hello%20World.html&label=Showcases&gist=1b5ad5d8b3232f5f1fbbf3628e2637f5

Hi Matt,

Thank you for all the helpful resources, it's greatly appreciated. That sample is definitely very impressive, but in a real scenario, the data would be coming from an external source. I've tried to modify your sample to do this, and the stutter comes back again...

In my actual use case, I'm trying to stream the data using Event Source, so I get a message whenever there is new data. I've tried to simulate that with a web worker in the sandcastle sample. Commenting out the line that updates the points stops the stuttering in this case so to me that points to Cesium as being the culprit here.. unless I'm doing something wrong..

Any advice would be greatly appreciated!

Trying to simulate what you want with a worker is not a good apples-to-apples comparison, postMessage is synchronous and will block when the data is being sent to and received from the main thread. Using non-transferable objects also has a high penalty. In fact, used improperly, worker communication can be a major bottleneck in performance. (The chapter I wrote with Kevin Ring for WebGL Insights goes into detail here, it’s actually one of the free chapters available online http://www.webglinsights.com/ (chapter 4) if you’re interested).

In your example, there’s a good chance that the postMessage takes just as long if not longer than doing the work in the main thread, and the code is back to trying to do too much in a single frame. I tweaked your example to use transferable objects and also generate smaller data chunks but send it back to the main thread more often. This probably doesn’t apply in your EventSource usage case, but here’s the link anyway if you’re curious: http://cesiumjs.org/Cesium/Apps/Sandcastle/?src=Hello%20World.html&label=Showcases&gist=c35fcd6cd547a89deb3dd5f321a644f6

I’m sorry to have to say that the bottleneck it is in your EventSource usage and outside the scope of the Cesium side of things (since my previous example shows Cesium can do what you want without breaking a sweat). I would recommend profiling your application in the Chrome JavaScript profiler and see if that can point to any obvious problems (for example if Garbage Collection or other functions show up really high in the flame chart and there is 0% for “idle”).

I’m happy to answer any additional questions you have (Cesium or otherwise), but it’s impossible to give any more detailed advice without having a concrete example that matches your usage exactly (as-is often the case for big-data performance problems).

Hi Matt,

Just wanted to say thank you for the wealth of information as well as the extremely useful code samples. I read the chapter you linked to and it's a wonderful resource. It is a bit difficult to share an example that matches my usage exactly, but I think the web worker example is a fairly good representation.

I'm not entirely convinced yet that the bottleneck is outside the scope of Cesium though. I realize that the code outside the scope of Cesium plays a huge factor in this so I'm not ignoring it, but I'd like to get a good understanding of where the bottlenecks really are.

Working with your most recent sample code, if I only change the data update interval within the web worker from 100 millisecond to 1 second, it causes the Cesium globe to stutter as it spins. I should probably mention here that I'm working on fairly modern hardware (6th gen i7, GTX960M) on the latest Chrome (stable). Any ideas what might be causing this?

Also, as you mentioned it doesn't seem like I can use transferable objects with Event Source. I have been using it because there was an example of using it to stream CZML to the client (which seemed to be the recommended solution). There also isn't a lot of information about streaming CZML to Cesium.. so I might as well as ask you here - what method do you recommend for streaming CZML to Cesium?

Thanks for your time!

I don’t really have much more advice for you other than to profile your applications and see exactly where the stuttering occurs. It should be obvious from the Chrome performance profiler. In this case it’s almost certainly the size of your update causes to much work to be done in any single frame. Copying data across workers, or even just calling JSON.parse on a large string will cause the entire browser to lock up. It’s possible that EventSource is not the right technology for your use case.

As I showed with the posted Sandcastle examples, Cesium can absolutely drive hundreds of thousands of points per-second, but if you can provide an example with data that shows Cesium is a bottleneck, I would be happy to look at it and further improve Cesium.