Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor Tileset Traversal #5254

Merged
merged 23 commits into from
May 8, 2017
Merged

Conversation

austinEng
Copy link
Contributor

  • Traversal and functions relevant to traversal have been moved into a new file Cesium3DTilesetTraversal.js
  • Tileset has an added property baseScreenSpaceError which defines the screen space error which must be met before LOD skipping starts
  • Traversal has been generalized as a pipeline of tree traversals. The base traversal is a DFS, the leaves of that are used to initialize a BFS for the skipping traversal which internally uses a DFS. The traversal has been refactored so each traversal method has functions execute, visit, getChildren, shouldVisit, and leafHandler.
  • To prevent tiles from being processed or requested twice (which would happen when the leaf of one traversal becomes the root of the following, _requestedFrame and _lastVisitedFrame have been added to Cesium3DTile
  • Some loops after the old traversal have been removed and simplified by keeping track of _ancestorWithContent and _ancestorWithLoadedContent during the top-down traversal. It is no longer necessary to do a bottom-up traversal.
  • _sse has been renamed to _screenSpaceError
  • SettingskipLODs = false now just sets baseScreenSpaceError to _maximumScreenSpaceError and runs a base traversal.
  • The refactored traversal better manages the _hasMixedContent flag so that the stencil test is only applied when necessary. Previously, it would always set _hasMixedContent to true if we had to go up the tree to select a tile. It is more correct to do this only when _selectionDepth > 0 which implies a selected tile has a selected ancestor. Tests have been updated to account for this.

@@ -338,6 +339,10 @@ define([
this._selectionDepth = 0;
this._lastFinalResolution = undefined;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No longer used.

if (b.distanceToCamera === 0 && a.distanceToCamera === 0) {
return b._centerZDepth - a._centerZDepth;
function sortForLoad(a, b) {
var diff = a.distanceToCamera - b.distanceToCamera;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rename to distanceDifference and screenSpaceErrorDifference

@@ -124,6 +126,7 @@ define([
* @param {Boolean} [options.debugShowGeometricError=false] For debugging only. When true, draws labels to indicate the geometric error of each tile
* @param {ShadowMode} [options.shadows=ShadowMode.ENABLED] Determines whether the tileset casts or receives shadows from each light source.
* @param {Boolean} [options.skipLODs=true] Determines if level-of-detail skipping optimization should be used.
* @param {Number} [options.baseScreenSpaceError=1024] The screen-space error that must be reached before skipping LODs
* @param {Number} [options.skipSSEFactor=10] Multiplier defining the minimum screen space error to skip when loading tiles. Used in conjuction with skipLevels to determine which tiles to load.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While we're doing the other renamings, this could be skipScreenSpaceErrorFactor.


//>>includeStart('debug', pragmas.debug);
Check.typeOf.number.greaterThanOrEquals('baseScreenSpaceError', baseScreenSpaceError, this.tileset._maximumScreenSpaceError);
//>>includeEnd('debug');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be checked in Cesium3DTileset somewhere, or always just clamped.


if (defined(stack.trim)) {
stack.trim(maxLength);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When would stack.trim be undefined?

Also this comment still applies right?

// on the next frame, we will likely need an array about the same size

Could be worth adding back.

child.selected = true;
child._finalResolution = true;
child._selectedFrame = frameState.frameNumber;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment here.

if (tile.hasTilesetContent) {
if (!tile.contentReady) {
return emptyArray;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as before, is this check needed? Plus the situation where this actually returns the empty array is somewhat low, since tile.hasTilesetContent will be false until the content has been loaded.

}

var childrenVisibility = updateChildren(tileset, tile, this.frameState);
var showAdditive = tile.refine === Cesium3DTileRefine.ADD && tile._screenSpaceError > maximumScreenSpaceError;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment about the SSE check being redundant.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's needed if tile.hasTilesetContent === true

@@ -263,6 +267,7 @@ define([
this.show = defaultValue(options.show, true);

this._maximumScreenSpaceError = defaultValue(options.maximumScreenSpaceError, 16);
this._baseScreenSpaceError = defaultValue(options.baseScreenSpaceError, 1024);
Copy link
Contributor

@lilleyse lilleyse May 1, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm getting a crash when changing the the maximum sse in the inspector with skipLODs as false.

var length = leaves.length;
for (var i = 0; i < length; ++i) {
tileset._desiredTiles.push(leaves.get(i));
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When skipLODs is false, should the bivariateVisibilityTest ever be on? I am seeing some cases where it is.

@lilleyse
Copy link
Contributor

lilleyse commented May 1, 2017

After testing the old performance/ behavior is still there which is good, minus the low-lod geometry blips that happened sometimes. I like the way the traversal code is organized now.

@lilleyse lilleyse mentioned this pull request May 1, 2017
6 tasks
@lilleyse
Copy link
Contributor

lilleyse commented May 2, 2017

The new code doesn't use the child union optimization. Is that intentional?

@austinEng austinEng force-pushed the improved-tile-loading branch from 777c3b9 to 2b858d6 Compare May 2, 2017 19:53
@austinEng
Copy link
Contributor Author

austinEng commented May 2, 2017

@lilleyse Updated based on comments.

When would stack.trim be undefined?

Whoops. Just an old thing

The new code doesn't use the child union optimization. Is that intentional?

Accidentally removed. Added back.

tile._screenSpaceError > baseScreenSpaceError will always be true by this point.

Fixed. The one with maximum screen space error isn't always true. We need the check if tile.hasTilesetContent === true

@austinEng austinEng force-pushed the improved-tile-loading branch from c2ca6ff to 50ec7c8 Compare May 3, 2017 14:16
@pjcozzi
Copy link
Contributor

pjcozzi commented May 3, 2017

@austinEng have you ran a few JavaScript profiles to make sure this didn't introduce any high CPU or memory usage?

@pjcozzi
Copy link
Contributor

pjcozzi commented May 3, 2017

There's a few CI test failures: https://travis-ci.org/AnalyticalGraphicsInc/cesium/builds/228357786

@austinEng
Copy link
Contributor Author

@lilleyse Updated.

Spent a lot of time trying to solve what seemed to be bugs in the traversal. This turned out to be problems with translucency. Translucent commands were being put in the CESIUM_3D_TILE render pass and taken out of the translucency pass. Tilesets with translucent geometry seemed to be randomly occluding content behind them. @bagnell I've added a check so that translucent commands do not get put in the CESIUM_3D_TILE pass.

@pjcozzi Performance and memory usage is about the same.

@austinEng austinEng force-pushed the improved-tile-loading branch 2 times, most recently from 8099a04 to b8ccf80 Compare May 4, 2017 16:14
@austinEng austinEng force-pushed the improved-tile-loading branch from b8ccf80 to 80806ea Compare May 4, 2017 17:05
};

function baseUpdateAndCheckChildren(tileset, tile, baseScreenSpaceError, frameState) {
if (tile.hasTilesetContent) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still don't think this block is needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you're right, but I think we should then add to the condition below

tile._screenSpaceError <= baseScreenSpaceError && !tile.hasTilesetContent

so that the base traversal doesn't stop at the root of external tilesets

// stop traversal when we've attained the desired level of error
if (tile._screenSpaceError <= baseScreenSpaceError) {
// When skipping LODs, require an existing base level of content first
updateChildren(tileset, tile, frameState);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't completely see why the children are updated here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

leafHandler checks if at least one child is visible for the children union bound optimization

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, leave a comment about that.

}

InternalBaseTraversal.prototype.execute = function(root) {
this.allLoaded = true;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be defined in the class.

BaseTraversal.prototype.visitStart = function(tile) {
if (tile._lastVisitedFrame !== this.frameState.frameNumber) {
visitTile(this.tileset, tile, this.frameState, this.outOfCore);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This last visited check is pretty much the same throughout, why not put back in visitTile?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also this goes to the second point, are visitStart and visitEnd needed? If InternalBaseTraversal.prototype.visitStart is changed based on other comments, visitStart would be the same throughout. The code in visitEnd can be incorporated in visit along with my comment right above.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. visitStart
  2. getChildren
  3. if no children, leafHandler
  4. visitEnd

sometimes the leaf handler checks tile._lastVisitedFrame !== this.frameState.frameNumber so we don't add tiles twice

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For that case it can stay, but do you think the others can be simplified?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Never mind, I see now. Maybe a short isVisited helper would be cleaner.

}
}

function touch(tileset, tile, outOfCore) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should there be a touchedFrame? Some things will be touched twice - first in getChildren and then visitTile.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it worth adding this? The other flags are important because they prevent tiles from getting put onto a list twice, but this just moves the tile in the doubly linked list.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well it's a bit like the sse check.

}
if (!tile.contentReady) {
this.allLoaded = false;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since shouldVisit() will return false for any tile with renderable content, this code would not got reached for those tiles. This would create a situation allLoaded is true even if one of its descendants with renderable content is not ready.

@austinEng
Copy link
Contributor Author

@lilleyse Updated

@lilleyse
Copy link
Contributor

lilleyse commented May 8, 2017

Thanks @austinEng, the recent updates look good.

@lilleyse lilleyse merged commit 85fd29c into CesiumGS:3d-tiles May 8, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants