Image Layer Performance Idea: Only load tiles in view

We at Raytheon have been working with some overlapping layers, some of which don't have extremely fast servers and are trying to optimize our layer load performance.

We've come up with a method that cuts tile requests/load times (jumping to a low altitude) by 40-60%, but I'm seeing some side-effects.

What we're trying to do is limit our tile requests by only loading tiles that are within our view extent. Pretty much all we've done is to add an overlaps() method to Core/Extent and then tweak queueChildrenAndDetermineIfChildrenAreAllRenderable() (quite a method name)

    function queueChildrenLoadAndDetermineIfChildrenAreAllRenderable(surface, frameState, tile) {
        var allRenderable = true;
        var go = false;

        var children = tile.getChildren();
        
        var extent = new Extent(tile.extent.west, tile.extent.south, tile.extent.east, tile.extent.north);
        /* Queue children if in the view extent */
        if (extent.overlaps( Cesium.lastExtent )) {
            for (var i = 0, len = children.length; i < len; ++i) {
                var child = children[i];
                surface._tileReplacementQueue.markTileRendered(child);
                if (child.state !== TileState.READY) {
                    queueTileLoad(surface, child);
                }
                if (!child.isRenderable) {
                    allRenderable = false;
                }
            }
        } else {
            /* Always return false for tiles that don't overlap with the view extent so we don't try to refine */
            return false;
        }

        return allRenderable;
    }

Pardon my global Cesium.lastExtent.

As I said, this provides some significant performance gains and overall does pretty well. I am seeing a tendency for tiles to not refine as much as they otherwise would (even if they're within the view extent). It's possible this is partially due to some poor view extent calculations when viewing at an angle, but I thought y'all might be able to shed some light on it.

The other thing I've tried that provides still better performance is to actually refrain from queuing any children that aren't in the view extent and instead simply returning "true" even when the out-of-sight tiles aren't renderable.

The impact of this is to cause those tiles that don't get loaded to be black (rather than simply a lower-res version because the parent is replaced but not all the children load to replace it (if my understanding is correct). Obviously this doesn't look great because you see black tiles when you move around and sometimes the tiles will black out momentarily as you're zooming out. I don't know if it's feasible to push things this far, but I thought I'd mention it.

Do y'all already have plans for optimizing? We're on 14b, so a bit behind. Anything we should know about in current or upcoming versions?

Thanks!
-- David

Hi David,

Are you guys using terrain at all? Or just imagery? With terrain out of the picture, some of Cesium’s “rules” are not strictly necessary. For example, we have a rule that we cannot render any of the four tiles in a quad until all four are loaded. This seems silly on the surface, but it’s important. If three of the four tiles in a quad are loaded, but the forth is not, we have two (poor) options for rendering the three tiles we do have: don’t render the fourth tile at all (leaving a black area where it should be), or render the parent tile in addition to the three children. We can kind of sort of maybe get away with the latter in the absence of terrain. The ellipsoid tessellation will not be the same between the parent tile and the three loaded children, but maybe it will be close enough that it won’t look broken. With terrain, however, things will definitely look broken.

Cesium already takes care to avoid rendering tiles that are not in view, taking the above into consideration. If you cull additional tiles, you’re probably violating the rule described above, which is going to break things because the rest of the engine doesn’t expect it.

If you have a mix of slow and fast imagery providers, your biggest benefit will come from allowing the engine to render tiles that do not yet have all of their imagery loaded. With a bit more complexity, this can be done by rendering parent imagery tiles in place of child imagery that is not yet loaded. I’ve wanted to try doing something like this for awhile, so maybe now is a good time. If I do get a chance to try it out, I’ll let you know how it goes.

Also, I’d definitely recommend trying out b17. We made some improvements to the tile load ordering logic that may might a significant difference in your application.

Kevin

David,

I just implemented some changes in the “noWait” branch that I think will make a big difference in the performance you see in your use-case. Can you try it out and let me know how it goes?

Thanks,

Kevin

Hey Kevin, we are using terrain, and using the code above, I can get performance gains without violating the rule above. My understanding of the code was that Cesium uses distance and occluding based on the Earth ellipsoid, but does not take the view extent into account (which is what I'm doing).

We'll try the noWait branch and see how it affects our use cases. Thank you!

-- David

Hey David,

For some reason when I looked at your code before, I thought you were culling individual children rather than the entire quad of children based on the view extent. So yes, you’re not violating that rule - sorry about that. I suspect you’re right that the artifacts you’re seeing at related to imprecision in computing the extent. I haven’t tried it myself, but I can imagine it being a tricky thing to compute for some views, and extremely pessimistic for others. For example, a rectangular extent describing the area the viewer can see in a horizon view is going to be much wider than it needs to be near the viewer.

For what it’s worth, Cesium doesn’t use any distance-based occlusion (although we should consider it… a little fog might go a long way!), but it does do view-frustum culling. In theory that should be equivalent to the extent-based culling. In practice, though, it’s not as accurate as we’d like because we test tile bounding spheres against the view frustum. Bounding spheres tend not to fit terrain tiles very tightly. Years ago, one of my coworkers analyzed the effect of using tighter-fitting bounding spheres for culling terrain and deemed it not worthwhile, but hardware has changed quite a bit since then. We haven’t revisited it for Cesium.

Regarding the noWait branch… since you’re using terrain, you may see some load order weirdness. In particular, the imagery will probably all refine first, and only then will the terrain refine, and probably take longer than you’re used to. I’m still working on ironing this out. Still, if you have a very slow imagery source, the overall effect will hopefully be much better. Let me know how it goes.

Kevin

I meant to say “tighter fitting bounding volumes” not “tighter fitting bounding spheres.”

Hey Kevin,

What happened to the noWait branch? I don’t see it in out there anymore. I guess we shouldn’t have waited to check out the noWait branch :frowning:

Hey Jonah,

The changes in noWait have been merged to master. So you can just try the latest version of master.

Kevin

Thanks Kevin, I thought that may be the case, but it didn’t seem straightforward to tell from the master branch commit history that was for sure what happened to it.

Awesome work, Kevin! The changes you have made greatly improve this use case. The only tradeoff seems to be that terrain refines later than it did before, but that works fine for us! Thanks again for your help.

Cool, glad to hear it’s working for you. I expect to have a solution for the delayed terrain refining at some point, but it may be a little while.