Skip to content

Commit

Permalink
Merge pull request #1042 from CesiumGS/kick-to-renderable
Browse files Browse the repository at this point in the history
"Kick" only when the current tile is renderable
  • Loading branch information
j9liu authored Dec 18, 2024
2 parents 9a95ae6 + c32cdaf commit 8cdf943
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 25 deletions.
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
- Fixed a raster overlay bug that could cause unnecessary upsampling with failed or missing overlay tiles.
- Fixed a bug in `SubtreeFileReader::loadBinary` that prevented valid subtrees from loading if they did not contain binary data.
- Fixed a bug in the `Tileset` selection algorithm that could cause detail to disappear during load in some cases.
- Improved the "kicking" mechanism in the tileset selection algorithm. The new criteria allows holes in a `Tileset`, when they do occur, to be filled with loaded tiles more incrementally.

### v0.42.0 - 2024-12-02

Expand Down
11 changes: 10 additions & 1 deletion Cesium3DTilesSelection/src/Tileset.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1437,7 +1437,16 @@ Tileset::TraversalDetails Tileset::_visitTile(
lastFrameSelectionResult == TileSelectionState::Result::Rendered &&
pRenderContent && pRenderContent->getLodTransitionFadePercentage() < 1.0f;

if (kickDueToNonReadyDescendant || kickDueToTileFadingIn) {
// Only kick the descendants of this tile if it is renderable, or if we've
// exceeded the loadingDescendantLimit. It's pointless to kick the descendants
// of a tile that is not yet loaded, because it means we will still have a
// hole, and quite possibly a bigger one.
bool wantToKick = kickDueToNonReadyDescendant || kickDueToTileFadingIn;
bool willKick = wantToKick && (traversalDetails.notYetRenderableCount >
this->_options.loadingDescendantLimit ||
tile.isRenderable());

if (willKick) {
// Kick all descendants out of the render list and render this tile instead
// Continue to load them though!
queuedForLoad = _kickDescendantsAndRenderTile(
Expand Down
63 changes: 39 additions & 24 deletions Cesium3DTilesSelection/test/TestTilesetSelectionAlgorithm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -563,31 +563,32 @@ TEST_CASE("Test additive refinement") {
SECTION("Load external tilesets") {
ViewState viewState = zoomToTileset(tileset);

// 1st frame. Root will get rendered first and 5 of its children are loading
// since they meet sse
// 1st frame. Root, its child, and its four grandchildren will all be
// rendered because they meet SSE, even though they're not loaded yet.
{
ViewUpdateResult result = tileset.updateView({viewState});

// root is rendered first
const std::vector<Tile*>& ttr = result.tilesToRenderThisFrame;
REQUIRE(ttr.size() == 7);

REQUIRE(root->getState() == TileLoadState::Done);
REQUIRE(!doesTileMeetSSE(viewState, *root, tileset));
REQUIRE(root->getChildren().size() == 1);
REQUIRE(std::find(ttr.begin(), ttr.end(), pTilesetJson) != ttr.end());
REQUIRE(std::find(ttr.begin(), ttr.end(), root) != ttr.end());

// root's children don't have content loading right now, so only root get
// rendered
const Tile& parentB3DM = root->getChildren().front();
REQUIRE(parentB3DM.getState() == TileLoadState::ContentLoading);
REQUIRE(!doesTileMeetSSE(viewState, parentB3DM, tileset));
REQUIRE(parentB3DM.getChildren().size() == 4);
REQUIRE(std::find(ttr.begin(), ttr.end(), &parentB3DM) != ttr.end());

for (const Tile& child : parentB3DM.getChildren()) {
REQUIRE(child.getState() == TileLoadState::ContentLoading);
REQUIRE(doesTileMeetSSE(viewState, child, tileset));
REQUIRE(std::find(ttr.begin(), ttr.end(), &child) != ttr.end());
}

REQUIRE(result.tilesToRenderThisFrame.size() == 1);
REQUIRE(result.tilesToRenderThisFrame.front() == pTilesetJson);

REQUIRE(result.tilesFadingOut.size() == 0);

REQUIRE(result.tilesVisited == 7);
Expand All @@ -600,20 +601,26 @@ TEST_CASE("Test additive refinement") {
{
ViewUpdateResult result = tileset.updateView({viewState});

// root is rendered first
const std::vector<Tile*>& ttr = result.tilesToRenderThisFrame;
REQUIRE(ttr.size() == 8);

// root is done loading and rendered.
REQUIRE(root->getState() == TileLoadState::Done);
REQUIRE(!doesTileMeetSSE(viewState, *root, tileset));
REQUIRE(root->getChildren().size() == 1);
REQUIRE(std::find(ttr.begin(), ttr.end(), root) != ttr.end());

// root's children don't have content loading right now, so only root get
// rendered
// root's child is done loading and rendered, too.
const Tile& parentB3DM = root->getChildren().front();
REQUIRE(parentB3DM.getState() == TileLoadState::Done);
REQUIRE(!doesTileMeetSSE(viewState, parentB3DM, tileset));
REQUIRE(parentB3DM.getChildren().size() == 4);
REQUIRE(std::find(ttr.begin(), ttr.end(), &parentB3DM) != ttr.end());

for (const Tile& child : parentB3DM.getChildren()) {
// parentB3DM's children are all done loading and are rendered.
REQUIRE(child.getState() == TileLoadState::Done);
REQUIRE(std::find(ttr.begin(), ttr.end(), &child) != ttr.end());

if (*std::get_if<std::string>(&child.getTileID()) !=
"tileset3/tileset3.json") {
Expand All @@ -622,21 +629,20 @@ TEST_CASE("Test additive refinement") {
// external tilesets get unconditionally refined
REQUIRE(root->getUnconditionallyRefine());

// expect the children to meet sse and begin loading the content
// expect the children to meet sse and begin loading the content,
// while also getting rendered.
REQUIRE(child.getChildren().size() == 1);
REQUIRE(
doesTileMeetSSE(viewState, child.getChildren().front(), tileset));
REQUIRE(
child.getChildren().front().getState() ==
TileLoadState::ContentLoading);
REQUIRE(
std::find(ttr.begin(), ttr.end(), &child.getChildren().front()) !=
ttr.end());
}
}

REQUIRE(result.tilesToRenderThisFrame.size() == 3);
REQUIRE(result.tilesToRenderThisFrame[0] == pTilesetJson);
REQUIRE(result.tilesToRenderThisFrame[1] == root);
REQUIRE(result.tilesToRenderThisFrame[2] == &parentB3DM);

REQUIRE(result.tilesFadingOut.size() == 0);

REQUIRE(result.tilesVisited == 8);
Expand Down Expand Up @@ -1554,21 +1560,30 @@ void runUnconditionallyRefinedTestCase(const TilesetOptions& options) {
pRawLoader->createRootTile(),
options);

// On the first update, we should render the root tile, even though nothing is
// loaded yet.
// On the first update, we should refine down to the grandchild tile, even
// though no tiles are loaded yet.
initializeTileset(tileset);
const Tile& child = tileset.getRootTile()->getChildren()[0];
const Tile& grandchild = child.getChildren()[0];
CHECK(
tileset.getRootTile()->getLastSelectionState().getResult(
tileset.getRootTile()->getLastSelectionState().getFrameNumber()) ==
TileSelectionState::Result::Refined);
CHECK(
child.getLastSelectionState().getResult(
tileset.getRootTile()->getLastSelectionState().getFrameNumber()) ==
TileSelectionState::Result::Refined);
CHECK(
grandchild.getLastSelectionState().getResult(
tileset.getRootTile()->getLastSelectionState().getFrameNumber()) ==
TileSelectionState::Result::Rendered);

// On the third update, the grandchild load will still be pending.
// But the child is unconditionally refined, so we should render the root
// instead of the child.
// After the third update, the root and child tiles have been loaded, while
// the grandchild has not. But the child is unconditionally refined, so we
// can't render that one. Therefore the root tile should be rendered, after
// the child and grandchild are kicked.
initializeTileset(tileset);
initializeTileset(tileset);
const Tile& child = tileset.getRootTile()->getChildren()[0];
const Tile& grandchild = child.getChildren()[0];
CHECK(
tileset.getRootTile()->getLastSelectionState().getResult(
tileset.getRootTile()->getLastSelectionState().getFrameNumber()) ==
Expand Down

0 comments on commit 8cdf943

Please sign in to comment.