Performance Tweaks: Skip Tile Loads?

Hey guys, we've been looking for ways to optimize performance in Cesium, particularly in loading imagery. We're not counting on our users always having a strong connection to a really fast imagery server. We would like to find a way to skip loading parent tiles when we're zoomed far in so we don't have to incur the extra cost to load tiles that aren't high enough resolution to improve the view and which are mostly outside of our view anyway.

The simplest solution seems to be to skip the load process and simply render blank tile images for parents until getting down to a level that's actually helpful. The problem with this is that A) it still incurs some rendering costs and B) it would be pretty ugly in the meantime since you'd be looking at blank tiles until the right level came in.

Do any other problems with this come to mind? Is there a better way to do this that doesn't involve a major re-design?

Thanks!
-- David

Hi David,

I have some ideas, but no quick solutions. One thing to keep in mind is that downloading imagery might not even be the bulk of the time. You’ll want to skip generation of lower-detail ellipsoid geometry as well, which is a disappointingly slow process even though it’s done in a web worker. In the terrain branch, I’ve started to decouple terrain (or ellipsoid) and imagery refinement. That may help some. An extension of that is to make the tile selection algorithm smarter about the order in which it loads tiles. In general, downloading less detailed tiles before more detailed ones is a good way to go. But in applications where the camera starts zoomed in very close, I agree that it’s silly to download a low-detail tile when only a tiny fraction of it is even visible.

Kevin

Thanks for the information Kevin! For a start, I've been looking into refining which tiles actually get loaded since it seems that all the children of each tile in the traversal to the high-detail tile get loaded (though not rendered) whether they're visible or not. Looks like we could potentially cut the low-detail tile load effort by 50-75% that way.

Thanks again,
-- David

Hey Kevin, I have noticed some additional problems with multiple imagery layers that I would like to bring up and see if we can help out with. From taking a few hours getting familiar with your code, this is how I am perceiving things to work…

The Scene.Tile object contains a collection of imagery for each layer you have added as well as the image used to compute terrain?

This tile object will not be considered renderable until all imagery resources for each layer associated with the tile have been loaded by the browser.

This corresponds to what I am perceiving in our application in that the highest level of imagery/data detail you get is hindered by your weakest link.

I have a wms layer added for the whole earth extent with only a couple of low resolution lods. Then, I have a layer on top of that with 15 lods of a small city. The small area only loads if I remove the large area low res wms layer.

It seems like we could call a tile ready for rendering if it’s top imagery layer has been downloaded and re-render it once sub layers have been loaded or this may not even be necessary if the top level is opaque. Also, it seems as though we could have a priority queue for processing the tile imagery and prioritize loading resources by what is in view and also let the top layer and terrain get highest priority over sub layers.

I may be over simplifying this, but I would really like to get you guy’s thoughts or plan to tackle these things and let us know how we can help.

Thanks!

Hi Jonah,

Thanks for taking the time to understand how the terrain and imagery systems work. You’re asking some good questions, so let me see if I can address them.

First, you should take a look at the “terrainRefactoring” (https://github.com/AnalyticalGraphicsInc/cesium/tree/terrainRefactoring) branch in GitHub. I’ve made substantial changes to how the terrain and imagery load state machines work, and I think it’s much more understandable, less insane, and will make it substantially easier to write TerrainProviders. I just need to hook the water mask support back up and then I’ll merge it back into the terrain branch (and hopefully master soon after that).

I believe it now correctly does something the previous code had intended to do, but failed at: it will refine imagery without necessarily downloading terrain data. This works by upsampling the terrain in parent tiles for use in child tiles, which often (but not always) is just a matter of dropping 3/4 of the posts. Subjectively, the terrain and imagery refinement seems much more responsive, now.

About your suggestion that tiles render as soon as the base layer is ready: that would be an easy change, but I avoided doing it for fear that it would create some very objectionable popping artifacts. With a slow non-base layer, every time you moved the camera, you would see the base layer first, and then the slower layer would pop in over it. Perhaps we could make it a per layer option?

Better than that would be to use parent imagery tiles if child tiles aren’t available, on a per-layer basis. So your fast base layer would refine immediately, and the slower layer on top of that would initially be a low-res (parent) version until the slow child tile actually loaded.

With such a system in place, we could allow “refinement” of both terrain and imagery without regard for what data has actually been downloaded yet. That puts us in a much better position to select which tiles to download first, rather than the current strategy which is basically depth first. The tile load queue is actually already a priority queue, but it is tuned to give the highest priority to tiles with a lower level in the tree. I think this is appropriate for many use-cases (such as when zooming in from space at a reasonable rate) but gives us a lousy experience when the camera starts zoomed in or the user zooms in very quickly.

Another idea is to allow imagery providers (especially the WMS one) to apply to multiple extents instead of just one. You should see a big performance improvement from that in your use-case, because the imagery system is currently rendering fully-transparent images over the entire globe.

It’s going to take me a little while to get to all of this, and a lot of it might happen after the merge to master. If you can help out, though, I’d definitely be grateful. The multiple-extents-in-WMS would be a great place to start if you’re up for it. Please do start with the terrainRefactoring branch, though.

Kevin

I appreciate the quick response Kevin. I will start looking at your recent changes to the terrain branch. It seems like we may still have a disconnect on the use case I am trying to describe, but I think what you said here would definitely get rid of the problem.

Better than that would be to use parent imagery tiles if child tiles aren’t available, on a per-layer basis. So your fast base layer would refine immediately, and the slower layer on top of that would initially be a low-res (parent) version until the slow child tile actually loaded.

It’s basically the backwards of that, where my layer on top of the base layer is fast, cached imagery of a small city and the base layer is slow because it is being dynamically generated by the imagery service, but I think what you said sounds like it could work for that case as well.

I realize there is a Tile loading queue, but I was thinking that maybe the imagery collections in those tiles should be added to a resource loading queue where the top-most, opaque layer imagery has priority.

I need to investigate the code more to follow you about imagery providers and multiple extents. I know the imagery provider has a tiling scheme which an extent can be given to, but I’m not sure how all the tiling schemes work together and how supporting multiple extents on the provider could help.

Sorry, I read that wrong. So yes, in your case, the base layer is less important than the layers on top. That’s not universally true, though, right? Imagine a setup where the base layer is imagery but the layer on top is, say, roads or weather. In that scenario, we could potentially get away with rendering a tile without the layers on top, but the base layer is pretty important. In any case, I’m pretty sure as well that allowing imagery layers to refine independently of all others, and showing parent tiles if child tiles are not yet available, would address both scenarios nicely.

Regarding prioritizing imagery layers, that’s a good idea, although it might not help as much as you think. In most cases, all imagery requests happen in parallel and then complete asynchronously. So the only time it would make much difference to load some layers before others is if the tile load timeslice expires before all layers are requested/reprojected/etc. Actually, I’m not totally sure how often that happens, so it may happen more than I’m admitting. We should look into it.

Tiling schemes have extents, but so do imagery providers. I’m not sure what it would mean for a tiling scheme to have multiple extents (how would you number the tiles?) but it’s reasonably straightforward for an imagery provider to have multiple. Take a look at ImageryLayer.js, _createTileImagerySkeletons method. It uses the extent of the provider to determine which imagery tiles overlap the current terrain tile. If the imagery provider has multiple extents, I believe you’d just have to loop over each of them and do the same for each.

Kevin

Sorry if it seemed like I flaked out on this, but I was pulled away onto some other tasks last week. We have to do testing and bug fixes this week, but hopefully by the end of the week or next week we can come back to this.

This is really interesting to me as it greatly affects usability of our app. I have implemented a “wait” screen with an activity indicator instead of showing a terrain during its initial tile loading and rendering, but even with really fast connections the load time can be as long as 10 seconds or more. Web users today don’t have that kind of patience.

I’d be happy to grab early experimental code to solve this problem.

Thanks,

Peter

There’s no code yet (experimental or otherwise), but now that terrain has been merged to master I may be able to find time to work on this. I’ll keep you all in the loop as that develops.