From 12a394fdd054b88f68e5390734294fc7468f0543 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Sat, 5 Nov 2016 23:03:04 +1100 Subject: [PATCH 01/20] Group loads into three priority tiers. From highest to lowest priority: 1. Tiles blocking refinement. 2. Tiles that are being rendered. 3. All other tiles. --- Source/Scene/Camera.js | 2 +- Source/Scene/GlobeSurfaceTile.js | 21 +++ Source/Scene/ImageryLayer.js | 2 + Source/Scene/QuadtreePrimitive.js | 154 +++++++++++++----- Source/Scene/TileTerrain.js | 1 + .../CesiumInspectorViewModel.js | 7 +- 6 files changed, 147 insertions(+), 40 deletions(-) diff --git a/Source/Scene/Camera.js b/Source/Scene/Camera.js index ee5dba704e7f..a464d83fcaef 100644 --- a/Source/Scene/Camera.js +++ b/Source/Scene/Camera.js @@ -927,7 +927,7 @@ define([ } var globe = this._scene.globe; - var globeFinishedUpdating = !defined(globe) || (globe._surface.tileProvider.ready && !defined(globe._surface._tileLoadQueue.head) && globe._surface._debug.tilesWaitingForChildren === 0); + var globeFinishedUpdating = !defined(globe) || (globe._surface.tileProvider.ready && globe._surface._tileLoadQueueHigh.length == 0 && globe._surface._tileLoadQueueMedium.length == 0 && globe._surface._tileLoadQueueLow.length == 0 && globe._surface._debug.tilesWaitingForChildren === 0); if (this._suspendTerrainAdjustment) { this._suspendTerrainAdjustment = !globeFinishedUpdating; } diff --git a/Source/Scene/GlobeSurfaceTile.js b/Source/Scene/GlobeSurfaceTile.js index 688680b4a8ac..2ae2ce3b7cec 100644 --- a/Source/Scene/GlobeSurfaceTile.js +++ b/Source/Scene/GlobeSurfaceTile.js @@ -8,6 +8,7 @@ define([ '../Core/defined', '../Core/defineProperties', '../Core/IntersectionTests', + '../Core/OrientedBoundingBox', '../Core/PixelFormat', '../Core/Rectangle', '../Renderer/PixelDatatype', @@ -31,6 +32,7 @@ define([ defined, defineProperties, IntersectionTests, + OrientedBoundingBox, PixelFormat, Rectangle, PixelDatatype, @@ -75,6 +77,9 @@ define([ this.boundingSphere3D = new BoundingSphere(); this.boundingSphere2D = new BoundingSphere(); this.orientedBoundingBox = undefined; + this.boundingBoxMinimumHeight = 0.0; + this.boundingBoxMaximumHeight = 0.0; + this.minimumAndMaximumHeightAreFromParent = true; this.tileBoundingBox = undefined; this.occludeePointInScaledSpace = new Cartesian3(); @@ -200,6 +205,8 @@ define([ this.pickTerrain = undefined; } + this.minimumAndMaximumHeightAreFromParent = true; + var i, len; var imageryList = this.imagery; @@ -252,6 +259,20 @@ define([ tile.state = QuadtreeTileLoadState.LOADING; } + // Update our min/max height if they came from our parent + if (this.minimumAndMaximumHeightAreFromParent && defined(tile.parent) && defined(tile.parent.data)) { + var parentData = tile.parent.data; + this.minimumHeight = parentData.minimumHeight; + this.maximumHeight = parentData.maximumHeight; + } + + // Recompute our oriented bounding box if our min/max heights changed. + if (!defined(this.orientedBoundingBox) || this.boundingBoxMinimumHeight !== this.minimumHeight || this.boundingBoxMaximumHeight !== this.maximumHeight) { + this.orientedBoundingBox = OrientedBoundingBox.fromRectangle(tile.rectangle, this.minimumHeight, this.maximumHeight, this.orientedBoundingBox); + this.boundingBoxMinimumHeight = this.minimumHeight; + this.boundingBoxMaximumHeight = this.maximumHeight; + } + if (tile.state === QuadtreeTileLoadState.LOADING) { processTerrainStateMachine(tile, frameState, terrainProvider, vertexArraysToDestroy); } diff --git a/Source/Scene/ImageryLayer.js b/Source/Scene/ImageryLayer.js index 3d0e2640286c..beb174d95074 100644 --- a/Source/Scene/ImageryLayer.js +++ b/Source/Scene/ImageryLayer.js @@ -667,6 +667,8 @@ define([ imagery.image = image; imagery.state = ImageryState.RECEIVED; + console.log('Received L' + imagery.level + 'X' + imagery.x + 'Y' + imagery.y); + TileProviderError.handleSuccess(that._requestImageError); } diff --git a/Source/Scene/QuadtreePrimitive.js b/Source/Scene/QuadtreePrimitive.js index 3c013df87671..977537926829 100644 --- a/Source/Scene/QuadtreePrimitive.js +++ b/Source/Scene/QuadtreePrimitive.js @@ -96,7 +96,9 @@ define([ this._tilesToRender = []; this._tileTraversalQueue = new Queue(); - this._tileLoadQueue = []; + this._tileLoadQueueHigh = []; // high priority tiles are preventing refinement + this._tileLoadQueueMedium = []; // medium priority tiles are being rendered + this._tileLoadQueueLow = []; // low priority tiles were were refined past or are non-visible parts of quads. this._tileReplacementQueue = new TileReplacementQueue(); this._levelZeroTiles = undefined; this._levelZeroTilesReady = false; @@ -108,6 +110,7 @@ define([ this._tileToUpdateHeights = []; this._lastTileIndex = 0; this._updateHeightsTimeSlice = 2.0; + this._estimatedVisibility = Visibility.NONE; /** * Gets or sets the maximum screen-space error, in pixels, that is allowed. @@ -284,7 +287,9 @@ define([ debug.tilesRendered = 0; debug.tilesWaitingForChildren = 0; - this._tileLoadQueue.length = 0; + this._tileLoadQueueHigh.length = 0; + this._tileLoadQueueMedium.length = 0; + this._tileLoadQueueLow.length = 0; this._tileReplacementQueue.markStartOfRenderFrame(); }; @@ -439,16 +444,16 @@ define([ for (i = 0, len = levelZeroTiles.length; i < len; ++i) { tile = levelZeroTiles[i]; primitive._tileReplacementQueue.markTileRendered(tile); - if (tile.needsLoading) { - queueTileLoad(primitive, tile); - } - if (tile.renderable && tileProvider.computeTileVisibility(tile, frameState, occluders) !== Visibility.NONE) { + if (!tile.renderable) { + primitive._tileLoadQueueHigh.push(tile); + ++debug.tilesWaitingForChildren; + } else if (tileProvider.computeTileVisibility(tile, frameState, occluders) !== Visibility.NONE) { traversalQueue.enqueue(tile); } else { - ++debug.tilesCulled; - if (!tile.renderable) { - ++debug.tilesWaitingForChildren; + if (tile.needsLoading) { + primitive._tileLoadQueueLow.push(tile); } + ++debug.tilesCulled; } } @@ -472,21 +477,32 @@ define([ if (screenSpaceError(primitive, frameState, tile) < primitive.maximumScreenSpaceError) { // This tile meets SSE requirements, so render it. + if (tile.needsLoading) { + // Rendered tile meeting SSE loads with medium priority. + primitive._tileLoadQueueMedium.push(tile); + } addTileToRenderList(primitive, tile); - } else if (queueChildrenLoadAndDetermineIfChildrenAreAllRenderable(primitive, tile)) { - // SSE is not good enough and children are loaded, so refine. - var children = tile.children; - // PERFORMANCE_IDEA: traverse children front-to-back so we can avoid sorting by distance later. - for (i = 0, len = children.length; i < len; ++i) { - if (tileProvider.computeTileVisibility(children[i], frameState, occluders) !== Visibility.NONE) { - traversalQueue.enqueue(children[i]); - } else { - ++debug.tilesCulled; + } else { + if (tile.needsLoading) { + // We want to refine this tile if possible, so load with low priority. + primitive._tileLoadQueueLow.push(tile); + } + + if (queueChildrenLoadAndDetermineIfChildrenAreAllRenderable(frameState, primitive, tile)) { + // SSE is not good enough and children are loaded, so refine. + var children = tile.children; + // PERFORMANCE_IDEA: traverse children front-to-back so we can avoid sorting by distance later. + for (i = 0, len = children.length; i < len; ++i) { + if (tileProvider.computeTileVisibility(children[i], frameState, occluders) !== Visibility.NONE) { + traversalQueue.enqueue(children[i]); + } else { + ++debug.tilesCulled; + } } + } else { + // SSE is not good enough but not all children are loaded, so render this tile anyway. + addTileToRenderList(primitive, tile); } - } else { - // SSE is not good enough but not all children are loaded, so render this tile anyway. - addTileToRenderList(primitive, tile); } } @@ -498,7 +514,7 @@ define([ * a new one. */ function raiseTileLoadProgressEvent(primitive) { - var currentLoadQueueLength = primitive._tileLoadQueue.length; + var currentLoadQueueLength = primitive._tileLoadQueueHigh.length + primitive._tileLoadQueueMedium.length + primitive._tileLoadQueueLow.length; if (currentLoadQueueLength !== primitive._lastTileLoadQueueLength) { primitive._tileLoadProgressEvent.raiseEvent(currentLoadQueueLength); @@ -544,44 +560,86 @@ define([ ++primitive._debug.tilesRendered; } - function queueChildrenLoadAndDetermineIfChildrenAreAllRenderable(primitive, tile) { + function queueChildrenLoadAndDetermineIfChildrenAreAllRenderable(frameState, primitive, tile) { var allRenderable = true; var allUpsampledOnly = true; + var allDoneLoading = true; var children = tile.children; - for (var i = 0, len = children.length; i < len; ++i) { - var child = children[i]; + var i + var len; + var child; + for (i = 0, len = children.length; i < len; ++i) { + child = children[i]; primitive._tileReplacementQueue.markTileRendered(child); allUpsampledOnly = allUpsampledOnly && child.upsampledFromParent; allRenderable = allRenderable && child.renderable; - - if (child.needsLoading) { - queueTileLoad(primitive, child); - } + allDoneLoading = allDoneLoading && !child.needsLoading; } if (!allRenderable) { ++primitive._debug.tilesWaitingForChildren; } - // If all children are upsampled from this tile, we just render this tile instead of its children. - return allRenderable && !allUpsampledOnly; - } + // If all children are upsampled from this tile, we just render this tile instead of its children (don't refine). + var willRefine = allRenderable && !allUpsampledOnly; - function queueTileLoad(primitive, tile) { - primitive._tileLoadQueue.push(tile); + if (!willRefine && !allDoneLoading) { + for (i = 0, len = children.length; i < len; ++i) { + child = children[i]; + if (child.needsLoading) { + if (child.renderable) { + primitive._tileLoadQueueLow.push(child); + } else { + // A tile blocking refine loads with high priority + primitive._tileLoadQueueHigh.push(child); + } + } + } + } + + return willRefine; } + // function queueTileLoad(frameState, primitive, tile) { + // // if (defined(tile.data) && defined(tile.data.orientedBoundingBox)) { + // // tile._estimatedVisibility = frameState.cullingVolume.computeVisibility(tile.data.orientedBoundingBox) >= 0; + // // } + // primitive._tileLoadQueue.push(tile); + // } + function processTileLoadQueue(primitive, frameState) { - var tileLoadQueue = primitive._tileLoadQueue; + var tileLoadQueueHigh = primitive._tileLoadQueueHigh; + var tileLoadQueueMedium = primitive._tileLoadQueueMedium; + var tileLoadQueueLow = primitive._tileLoadQueueLow; var tileProvider = primitive._tileProvider; - if (tileLoadQueue.length === 0) { + if (tileLoadQueueHigh.length === 0 && tileLoadQueueMedium.length === 0 && tileLoadQueueLow.length === 0) { return; } + // Our goal with load ordering is to first load all of the tiles we need to + // render the current scene at full detail. Loading any other tiles is just + // a form of prefetching, and we need not do it at all. This simple and obvious + // statement gets more complicated when we realize that, because we don't have + // bounding volumes for the entire terrain tile pyramid, we don't precisely know + // which tiles we need to render the scene at full detail, until we do some + // loading. + // + // So our load priority is (from high to low): + // 1. Tiles that we _would_ render, except that they're not sufficiently loaded yet. + // Ideally this would only include tiles that we've already determined to be visible, + // but since we don't have reliable visibility information until a tile is loaded, + // and because we (currently) must have all children in a quad renderable before we + // can refine, this pretty much means tiles we'd like to refine to, regardless of + // visibility. (high) + // 2. Tiles that we're rendering. (medium) + // 3. All other tiles. (low) + // + // Within each group, tiles should be loaded in approximate near-to-far order. + // Remove any tiles that were not used this frame beyond the number // we're allowed to keep. primitive._tileReplacementQueue.trimTiles(primitive.tileCacheSize); @@ -589,9 +647,29 @@ define([ var startTime = getTimestamp(); var timeSlice = primitive._loadQueueTimeSlice; var endTime = startTime + timeSlice; + var i = 0; + var tile; + + for (i = tileLoadQueueHigh.length - 1; i >= 0; --i) { + tile = tileLoadQueueHigh[i]; + primitive._tileReplacementQueue.markTileRendered(tile); + tileProvider.loadTile(frameState, tile); + if (getTimestamp() >= endTime) { + break; + } + } + + for (i = tileLoadQueueMedium.length - 1; i >= 0; --i) { + tile = tileLoadQueueMedium[i]; + primitive._tileReplacementQueue.markTileRendered(tile); + tileProvider.loadTile(frameState, tile); + if (getTimestamp() >= endTime) { + break; + } + } - for (var i = tileLoadQueue.length - 1; i >= 0; --i) { - var tile = tileLoadQueue[i]; + for (i = tileLoadQueueLow.length - 1; i >= 0; --i) { + tile = tileLoadQueueLow[i]; primitive._tileReplacementQueue.markTileRendered(tile); tileProvider.loadTile(frameState, tile); if (getTimestamp() >= endTime) { diff --git a/Source/Scene/TileTerrain.js b/Source/Scene/TileTerrain.js index fa039c3a29d3..8975d6207d43 100644 --- a/Source/Scene/TileTerrain.js +++ b/Source/Scene/TileTerrain.js @@ -91,6 +91,7 @@ define([ maximumHeight : mesh.maximumHeight, ellipsoid : tile.tilingScheme.ellipsoid }); + surfaceTile.minimumAndMaximumHeightAreFromParent = false; tile.data.occludeePointInScaledSpace = Cartesian3.clone(mesh.occludeePointInScaledSpace, surfaceTile.occludeePointInScaledSpace); }; diff --git a/Source/Widgets/CesiumInspector/CesiumInspectorViewModel.js b/Source/Widgets/CesiumInspector/CesiumInspectorViewModel.js index 57b76e94c8a3..cfe9a63ed572 100644 --- a/Source/Widgets/CesiumInspector/CesiumInspectorViewModel.js +++ b/Source/Widgets/CesiumInspector/CesiumInspectorViewModel.js @@ -8,6 +8,7 @@ define([ '../../Core/Rectangle', '../../Core/ScreenSpaceEventHandler', '../../Core/ScreenSpaceEventType', + '../../Core/WebMercatorTilingScheme', '../../Scene/DebugModelMatrixPrimitive', '../../Scene/PerformanceDisplay', '../../Scene/TileCoordinatesImageryProvider', @@ -22,6 +23,7 @@ define([ Rectangle, ScreenSpaceEventHandler, ScreenSpaceEventType, + WebMercatorTilingScheme, DebugModelMatrixPrimitive, PerformanceDisplay, TileCoordinatesImageryProvider, @@ -418,7 +420,10 @@ define([ this._showTileCoordinates = createCommand(function() { if (that.tileCoordinates && !defined(tileBoundariesLayer)) { tileBoundariesLayer = scene.imageryLayers.addImageryProvider(new TileCoordinatesImageryProvider({ - tilingScheme : scene.terrainProvider.tilingScheme + tilingScheme : new WebMercatorTilingScheme({ + numberOfLevelZeroTilesX : 2, + numberOfLevelZeroTilesY : 2 + }) })); } else if (!that.tileCoordinates && defined(tileBoundariesLayer)) { scene.imageryLayers.remove(tileBoundariesLayer); From e9cd45344ca9cd2f00ea092a62a5631e5887fa4c Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Sun, 6 Nov 2016 16:31:38 +1100 Subject: [PATCH 02/20] Remove console.log. --- Source/Scene/ImageryLayer.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/Source/Scene/ImageryLayer.js b/Source/Scene/ImageryLayer.js index beb174d95074..3d0e2640286c 100644 --- a/Source/Scene/ImageryLayer.js +++ b/Source/Scene/ImageryLayer.js @@ -667,8 +667,6 @@ define([ imagery.image = image; imagery.state = ImageryState.RECEIVED; - console.log('Received L' + imagery.level + 'X' + imagery.x + 'Y' + imagery.y); - TileProviderError.handleSuccess(that._requestImageError); } From 3291d8ee0a501104519b6a1004ae4507fc55a068 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Sun, 6 Nov 2016 16:47:44 +1100 Subject: [PATCH 03/20] Cleanup. --- Source/Scene/QuadtreePrimitive.js | 53 ++++++++++++++----------------- 1 file changed, 24 insertions(+), 29 deletions(-) diff --git a/Source/Scene/QuadtreePrimitive.js b/Source/Scene/QuadtreePrimitive.js index 977537926829..f60195716017 100644 --- a/Source/Scene/QuadtreePrimitive.js +++ b/Source/Scene/QuadtreePrimitive.js @@ -440,6 +440,28 @@ define([ customDataRemoved.length = 0; } + // Our goal with load ordering is to first load all of the tiles we need to + // render the current scene at full detail. Loading any other tiles is just + // a form of prefetching, and we need not do it at all (other concerns aside). This + // simple and obvious statement gets more complicated when we realize that, because + // we don't have bounding volumes for the entire terrain tile pyramid, we don't + // precisely know which tiles we need to render the scene at full detail, until we do + // some loading. + // + // So our load priority is (from high to low): + // 1. Tiles that we _would_ render, except that they're not sufficiently loaded yet. + // Ideally this would only include tiles that we've already determined to be visible, + // but since we don't have reliable visibility information until a tile is loaded, + // and because we (currently) must have all children in a quad renderable before we + // can refine, this pretty much means tiles we'd like to refine to, regardless of + // visibility. (high) + // 2. Tiles that we're rendering. (medium) + // 3. All other tiles. (low) + // + // Within each priority group, tiles should be loaded in approximate near-to-far order, + // but currently they're just loaded in our traversal order which makes no guarantees + // about depth ordering. + // Enqueue the root tiles that are renderable and visible. for (i = 0, len = levelZeroTiles.length; i < len; ++i) { tile = levelZeroTiles[i]; @@ -488,7 +510,7 @@ define([ primitive._tileLoadQueueLow.push(tile); } - if (queueChildrenLoadAndDetermineIfChildrenAreAllRenderable(frameState, primitive, tile)) { + if (queueChildrenLoadAndDetermineIfChildrenAreAllRenderable(primitive, tile)) { // SSE is not good enough and children are loaded, so refine. var children = tile.children; // PERFORMANCE_IDEA: traverse children front-to-back so we can avoid sorting by distance later. @@ -560,7 +582,7 @@ define([ ++primitive._debug.tilesRendered; } - function queueChildrenLoadAndDetermineIfChildrenAreAllRenderable(frameState, primitive, tile) { + function queueChildrenLoadAndDetermineIfChildrenAreAllRenderable(primitive, tile) { var allRenderable = true; var allUpsampledOnly = true; var allDoneLoading = true; @@ -603,13 +625,6 @@ define([ return willRefine; } - // function queueTileLoad(frameState, primitive, tile) { - // // if (defined(tile.data) && defined(tile.data.orientedBoundingBox)) { - // // tile._estimatedVisibility = frameState.cullingVolume.computeVisibility(tile.data.orientedBoundingBox) >= 0; - // // } - // primitive._tileLoadQueue.push(tile); - // } - function processTileLoadQueue(primitive, frameState) { var tileLoadQueueHigh = primitive._tileLoadQueueHigh; var tileLoadQueueMedium = primitive._tileLoadQueueMedium; @@ -620,26 +635,6 @@ define([ return; } - // Our goal with load ordering is to first load all of the tiles we need to - // render the current scene at full detail. Loading any other tiles is just - // a form of prefetching, and we need not do it at all. This simple and obvious - // statement gets more complicated when we realize that, because we don't have - // bounding volumes for the entire terrain tile pyramid, we don't precisely know - // which tiles we need to render the scene at full detail, until we do some - // loading. - // - // So our load priority is (from high to low): - // 1. Tiles that we _would_ render, except that they're not sufficiently loaded yet. - // Ideally this would only include tiles that we've already determined to be visible, - // but since we don't have reliable visibility information until a tile is loaded, - // and because we (currently) must have all children in a quad renderable before we - // can refine, this pretty much means tiles we'd like to refine to, regardless of - // visibility. (high) - // 2. Tiles that we're rendering. (medium) - // 3. All other tiles. (low) - // - // Within each group, tiles should be loaded in approximate near-to-far order. - // Remove any tiles that were not used this frame beyond the number // we're allowed to keep. primitive._tileReplacementQueue.trimTiles(primitive.tileCacheSize); From be3e9909e1711fbcca1c9974ebaabe97fa0b3578 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Sun, 6 Nov 2016 16:51:45 +1100 Subject: [PATCH 04/20] More cleanup. --- Source/Scene/GlobeSurfaceTile.js | 21 ------------------- Source/Scene/QuadtreePrimitive.js | 1 - Source/Scene/TileTerrain.js | 1 - .../CesiumInspectorViewModel.js | 7 +------ 4 files changed, 1 insertion(+), 29 deletions(-) diff --git a/Source/Scene/GlobeSurfaceTile.js b/Source/Scene/GlobeSurfaceTile.js index 2ae2ce3b7cec..688680b4a8ac 100644 --- a/Source/Scene/GlobeSurfaceTile.js +++ b/Source/Scene/GlobeSurfaceTile.js @@ -8,7 +8,6 @@ define([ '../Core/defined', '../Core/defineProperties', '../Core/IntersectionTests', - '../Core/OrientedBoundingBox', '../Core/PixelFormat', '../Core/Rectangle', '../Renderer/PixelDatatype', @@ -32,7 +31,6 @@ define([ defined, defineProperties, IntersectionTests, - OrientedBoundingBox, PixelFormat, Rectangle, PixelDatatype, @@ -77,9 +75,6 @@ define([ this.boundingSphere3D = new BoundingSphere(); this.boundingSphere2D = new BoundingSphere(); this.orientedBoundingBox = undefined; - this.boundingBoxMinimumHeight = 0.0; - this.boundingBoxMaximumHeight = 0.0; - this.minimumAndMaximumHeightAreFromParent = true; this.tileBoundingBox = undefined; this.occludeePointInScaledSpace = new Cartesian3(); @@ -205,8 +200,6 @@ define([ this.pickTerrain = undefined; } - this.minimumAndMaximumHeightAreFromParent = true; - var i, len; var imageryList = this.imagery; @@ -259,20 +252,6 @@ define([ tile.state = QuadtreeTileLoadState.LOADING; } - // Update our min/max height if they came from our parent - if (this.minimumAndMaximumHeightAreFromParent && defined(tile.parent) && defined(tile.parent.data)) { - var parentData = tile.parent.data; - this.minimumHeight = parentData.minimumHeight; - this.maximumHeight = parentData.maximumHeight; - } - - // Recompute our oriented bounding box if our min/max heights changed. - if (!defined(this.orientedBoundingBox) || this.boundingBoxMinimumHeight !== this.minimumHeight || this.boundingBoxMaximumHeight !== this.maximumHeight) { - this.orientedBoundingBox = OrientedBoundingBox.fromRectangle(tile.rectangle, this.minimumHeight, this.maximumHeight, this.orientedBoundingBox); - this.boundingBoxMinimumHeight = this.minimumHeight; - this.boundingBoxMaximumHeight = this.maximumHeight; - } - if (tile.state === QuadtreeTileLoadState.LOADING) { processTerrainStateMachine(tile, frameState, terrainProvider, vertexArraysToDestroy); } diff --git a/Source/Scene/QuadtreePrimitive.js b/Source/Scene/QuadtreePrimitive.js index f60195716017..2d4f3d2c22c3 100644 --- a/Source/Scene/QuadtreePrimitive.js +++ b/Source/Scene/QuadtreePrimitive.js @@ -110,7 +110,6 @@ define([ this._tileToUpdateHeights = []; this._lastTileIndex = 0; this._updateHeightsTimeSlice = 2.0; - this._estimatedVisibility = Visibility.NONE; /** * Gets or sets the maximum screen-space error, in pixels, that is allowed. diff --git a/Source/Scene/TileTerrain.js b/Source/Scene/TileTerrain.js index 8975d6207d43..fa039c3a29d3 100644 --- a/Source/Scene/TileTerrain.js +++ b/Source/Scene/TileTerrain.js @@ -91,7 +91,6 @@ define([ maximumHeight : mesh.maximumHeight, ellipsoid : tile.tilingScheme.ellipsoid }); - surfaceTile.minimumAndMaximumHeightAreFromParent = false; tile.data.occludeePointInScaledSpace = Cartesian3.clone(mesh.occludeePointInScaledSpace, surfaceTile.occludeePointInScaledSpace); }; diff --git a/Source/Widgets/CesiumInspector/CesiumInspectorViewModel.js b/Source/Widgets/CesiumInspector/CesiumInspectorViewModel.js index cfe9a63ed572..57b76e94c8a3 100644 --- a/Source/Widgets/CesiumInspector/CesiumInspectorViewModel.js +++ b/Source/Widgets/CesiumInspector/CesiumInspectorViewModel.js @@ -8,7 +8,6 @@ define([ '../../Core/Rectangle', '../../Core/ScreenSpaceEventHandler', '../../Core/ScreenSpaceEventType', - '../../Core/WebMercatorTilingScheme', '../../Scene/DebugModelMatrixPrimitive', '../../Scene/PerformanceDisplay', '../../Scene/TileCoordinatesImageryProvider', @@ -23,7 +22,6 @@ define([ Rectangle, ScreenSpaceEventHandler, ScreenSpaceEventType, - WebMercatorTilingScheme, DebugModelMatrixPrimitive, PerformanceDisplay, TileCoordinatesImageryProvider, @@ -420,10 +418,7 @@ define([ this._showTileCoordinates = createCommand(function() { if (that.tileCoordinates && !defined(tileBoundariesLayer)) { tileBoundariesLayer = scene.imageryLayers.addImageryProvider(new TileCoordinatesImageryProvider({ - tilingScheme : new WebMercatorTilingScheme({ - numberOfLevelZeroTilesX : 2, - numberOfLevelZeroTilesY : 2 - }) + tilingScheme : scene.terrainProvider.tilingScheme })); } else if (!that.tileCoordinates && defined(tileBoundariesLayer)) { scene.imageryLayers.remove(tileBoundariesLayer); From 300e4aa88e7e39f8fca77687b4d604f19537b448 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Sun, 6 Nov 2016 18:25:14 +1100 Subject: [PATCH 05/20] Fix test failures. --- Source/Scene/Camera.js | 2 +- Source/Scene/QuadtreePrimitive.js | 4 +++- Specs/DataSources/EntityClusterSpec.js | 4 +++- Specs/Scene/GlobeSpec.js | 2 +- Specs/Scene/GlobeSurfaceTileProviderSpec.js | 2 +- Specs/Scene/ImageryLayerCollectionSpec.js | 2 +- Specs/Scene/ModelSpec.js | 4 +++- Specs/Scene/ScreenSpaceCameraControllerSpec.js | 4 +++- Specs/Scene/ShadowMapSpec.js | 3 ++- 9 files changed, 18 insertions(+), 9 deletions(-) diff --git a/Source/Scene/Camera.js b/Source/Scene/Camera.js index a464d83fcaef..49799f1218ee 100644 --- a/Source/Scene/Camera.js +++ b/Source/Scene/Camera.js @@ -927,7 +927,7 @@ define([ } var globe = this._scene.globe; - var globeFinishedUpdating = !defined(globe) || (globe._surface.tileProvider.ready && globe._surface._tileLoadQueueHigh.length == 0 && globe._surface._tileLoadQueueMedium.length == 0 && globe._surface._tileLoadQueueLow.length == 0 && globe._surface._debug.tilesWaitingForChildren === 0); + var globeFinishedUpdating = !defined(globe) || (globe._surface.tileProvider.ready && globe._surface._tileLoadQueueHigh.length === 0 && globe._surface._tileLoadQueueMedium.length === 0 && globe._surface._tileLoadQueueLow.length === 0 && globe._surface._debug.tilesWaitingForChildren === 0); if (this._suspendTerrainAdjustment) { this._suspendTerrainAdjustment = !globeFinishedUpdating; } diff --git a/Source/Scene/QuadtreePrimitive.js b/Source/Scene/QuadtreePrimitive.js index 2d4f3d2c22c3..c86b2333b0e5 100644 --- a/Source/Scene/QuadtreePrimitive.js +++ b/Source/Scene/QuadtreePrimitive.js @@ -466,7 +466,9 @@ define([ tile = levelZeroTiles[i]; primitive._tileReplacementQueue.markTileRendered(tile); if (!tile.renderable) { - primitive._tileLoadQueueHigh.push(tile); + if (tile.needsLoading) { + primitive._tileLoadQueueHigh.push(tile); + } ++debug.tilesWaitingForChildren; } else if (tileProvider.computeTileVisibility(tile, frameState, occluders) !== Visibility.NONE) { traversalQueue.enqueue(tile); diff --git a/Specs/DataSources/EntityClusterSpec.js b/Specs/DataSources/EntityClusterSpec.js index ea5d75ec066d..665343f1c236 100644 --- a/Specs/DataSources/EntityClusterSpec.js +++ b/Specs/DataSources/EntityClusterSpec.js @@ -44,7 +44,9 @@ defineSuite([ tileProvider : { ready : true }, - _tileLoadQueue : {}, + _tileLoadQueueHigh : [], + _tileLoadQueueMedium : [], + _tileLoadQueueLow : [], _debug : { tilesWaitingForChildren : 0 } diff --git a/Specs/Scene/GlobeSpec.js b/Specs/Scene/GlobeSpec.js index bae5e841afa7..466b18231e9a 100644 --- a/Specs/Scene/GlobeSpec.js +++ b/Specs/Scene/GlobeSpec.js @@ -68,7 +68,7 @@ defineSuite([ return pollToPromise(function() { globe._surface._debug.enableDebugOutput = true; scene.render(); - return globe._surface.tileProvider.ready && !defined(globe._surface._tileLoadQueue.head) && globe._surface._debug.tilesWaitingForChildren === 0; + return globe._surface.tileProvider.ready && globe._surface._tileLoadQueueHigh.length === 0 && globe._surface._tileLoadQueueMedium.length === 0 && globe._surface._tileLoadQueueLow.length === 0 && globe._surface._debug.tilesWaitingForChildren === 0; }); } diff --git a/Specs/Scene/GlobeSurfaceTileProviderSpec.js b/Specs/Scene/GlobeSurfaceTileProviderSpec.js index 4f3d44b0de31..b4685e66e783 100644 --- a/Specs/Scene/GlobeSurfaceTileProviderSpec.js +++ b/Specs/Scene/GlobeSurfaceTileProviderSpec.js @@ -83,7 +83,7 @@ defineSuite([ // update until the load queue is empty. return pollToPromise(function() { scene.renderForSpecs(); - return globe._surface.tileProvider.ready && !defined(globe._surface._tileLoadQueue.head) && globe._surface._debug.tilesWaitingForChildren === 0; + return globe._surface.tileProvider.ready && globe._surface._tileLoadQueueHigh.length === 0 && globe._surface._tileLoadQueueMedium.length === 0 && globe._surface._tileLoadQueueLow.length === 0 && globe._surface._debug.tilesWaitingForChildren === 0; }); } diff --git a/Specs/Scene/ImageryLayerCollectionSpec.js b/Specs/Scene/ImageryLayerCollectionSpec.js index 39f392a2f7a9..7749c1f490ff 100644 --- a/Specs/Scene/ImageryLayerCollectionSpec.js +++ b/Specs/Scene/ImageryLayerCollectionSpec.js @@ -304,7 +304,7 @@ defineSuite([ return pollToPromise(function() { globe._surface._debug.enableDebugOutput = true; scene.render(); - return globe._surface.tileProvider.ready && globe._surface._tileLoadQueue.length === 0 && globe._surface._debug.tilesWaitingForChildren === 0; + return globe._surface.tileProvider.ready && globe._surface._tileLoadQueueHigh.length === 0 && globe._surface._tileLoadQueueMedium.length === 0 && globe._surface._tileLoadQueueLow.length === 0 && globe._surface._debug.tilesWaitingForChildren === 0; }); } diff --git a/Specs/Scene/ModelSpec.js b/Specs/Scene/ModelSpec.js index 2699f497616c..444c9a9d1137 100644 --- a/Specs/Scene/ModelSpec.js +++ b/Specs/Scene/ModelSpec.js @@ -1883,7 +1883,9 @@ defineSuite([ tileProvider : { ready : true }, - _tileLoadQueue : {}, + _tileLoadQueueHigh : [], + _tileLoadQueueMedium : [], + _tileLoadQueueLow : [], _debug : { tilesWaitingForChildren : 0 } diff --git a/Specs/Scene/ScreenSpaceCameraControllerSpec.js b/Specs/Scene/ScreenSpaceCameraControllerSpec.js index f205c1ce6db6..e1c42abae3fd 100644 --- a/Specs/Scene/ScreenSpaceCameraControllerSpec.js +++ b/Specs/Scene/ScreenSpaceCameraControllerSpec.js @@ -72,7 +72,9 @@ defineSuite([ tileProvider : { ready : true }, - _tileLoadQueue : {}, + _tileLoadQueueHigh : [], + _tileLoadQueueMedium : [], + _tileLoadQueueLow : [], _debug : { tilesWaitingForChildren : 0 } diff --git a/Specs/Scene/ShadowMapSpec.js b/Specs/Scene/ShadowMapSpec.js index 692a68548a2f..5330c1c48417 100644 --- a/Specs/Scene/ShadowMapSpec.js +++ b/Specs/Scene/ShadowMapSpec.js @@ -247,7 +247,8 @@ defineSuite([ function loadGlobe() { return pollToPromise(function() { scene.render(); - return scene.globe._surface.tileProvider.ready && !defined(scene.globe._surface._tileLoadQueue.head) && scene.globe._surface._debug.tilesWaitingForChildren === 0; + var globe = scene.globe; + return globe._surface.tileProvider.ready && globe._surface._tileLoadQueueHigh.length === 0 && globe._surface._tileLoadQueueMedium.length === 0 && globe._surface._tileLoadQueueLow.length === 0 && globe._surface._debug.tilesWaitingForChildren === 0; }); } From 9c0616cacd023d55482639201054e1fcde45d867 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Sun, 6 Nov 2016 18:39:58 +1100 Subject: [PATCH 06/20] Fix jsHint warning. --- Source/Scene/QuadtreePrimitive.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Scene/QuadtreePrimitive.js b/Source/Scene/QuadtreePrimitive.js index c86b2333b0e5..93c082c778da 100644 --- a/Source/Scene/QuadtreePrimitive.js +++ b/Source/Scene/QuadtreePrimitive.js @@ -589,7 +589,7 @@ define([ var allDoneLoading = true; var children = tile.children; - var i + var i; var len; var child; for (i = 0, len = children.length; i < len; ++i) { From 77ba56bcbf532ca5c50b9d8212dde5bd495d440c Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Sun, 6 Nov 2016 19:31:49 +1100 Subject: [PATCH 07/20] Fix typo, update CHANGES.md. --- CHANGES.md | 1 + Source/Scene/QuadtreePrimitive.js | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 452161ec5881..b792c21afb18 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -7,6 +7,7 @@ Change Log * Breaking changes * * Added support for saving html and css in Github Gists [#4125](https://github.com/AnalyticalGraphicsInc/cesium/issues/4125) +* Improved terrain/imagery load ordering, especially when the terrain is already fully loaded and we add a new imagery layer. ### 1.27 - 2016-11-01 * Deprecated diff --git a/Source/Scene/QuadtreePrimitive.js b/Source/Scene/QuadtreePrimitive.js index 93c082c778da..bd2cd271064a 100644 --- a/Source/Scene/QuadtreePrimitive.js +++ b/Source/Scene/QuadtreePrimitive.js @@ -98,7 +98,7 @@ define([ this._tileTraversalQueue = new Queue(); this._tileLoadQueueHigh = []; // high priority tiles are preventing refinement this._tileLoadQueueMedium = []; // medium priority tiles are being rendered - this._tileLoadQueueLow = []; // low priority tiles were were refined past or are non-visible parts of quads. + this._tileLoadQueueLow = []; // low priority tiles were refined past or are non-visible parts of quads. this._tileReplacementQueue = new TileReplacementQueue(); this._levelZeroTiles = undefined; this._levelZeroTilesReady = false; From eb6c78b099b0946cb3f9193bb3967eec9cd397c8 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Sun, 6 Nov 2016 21:07:08 +1100 Subject: [PATCH 08/20] Use four properties instead of an array for child tiles. --- Source/Scene/QuadtreeTile.js | 107 +++++++++++++++++++++++++---------- 1 file changed, 78 insertions(+), 29 deletions(-) diff --git a/Source/Scene/QuadtreeTile.js b/Source/Scene/QuadtreeTile.js index bce4d3442a15..e65730bb939a 100644 --- a/Source/Scene/QuadtreeTile.js +++ b/Source/Scene/QuadtreeTile.js @@ -52,7 +52,11 @@ define([ this._level = options.level; this._parent = options.parent; this._rectangle = this._tilingScheme.tileXYToRectangle(this._x, this._y, this._level); - this._children = undefined; + + this._southwestChild = undefined; + this._southeastChild = undefined; + this._northwestChild = undefined; + this._northeastChild = undefined; // QuadtreeTileReplacementQueue gets/sets these private properties. this._replacementPrevious = undefined; @@ -247,6 +251,7 @@ define([ } }, + /** * An array of tiles that is at the next level of the tile tree. * @memberof QuadtreeTile.prototype @@ -254,39 +259,83 @@ define([ */ children : { get : function() { - if (!defined(this._children)) { - var tilingScheme = this.tilingScheme; - var level = this.level + 1; - var x = this.x * 2; - var y = this.y * 2; - this._children = [new QuadtreeTile({ - tilingScheme : tilingScheme, - x : x, - y : y, - level : level, - parent : this - }), new QuadtreeTile({ - tilingScheme : tilingScheme, - x : x + 1, - y : y, - level : level, + return [this.northwestChild, this.northeastChild, this.southwestChild, this.southeastChild]; + } + }, + + /** + * Gets the southwest child tile. + * @type {QuadtreeTile} + */ + southwestChild : { + get : function() { + if (!defined(this._southwestChild)) { + this._southwestChild = new QuadtreeTile({ + tilingScheme : this.tilingScheme, + x : this.x * 2, + y : this.y * 2 + 1, + level : this.level + 1, parent : this - }), new QuadtreeTile({ - tilingScheme : tilingScheme, - x : x, - y : y + 1, - level : level, + }); + } + return this._southwestChild; + } + }, + + /** + * Gets the southeast child tile. + * @type {QuadtreeTile} + */ + southeastChild : { + get : function() { + if (!defined(this._southeastChild)) { + this._southeastChild = new QuadtreeTile({ + tilingScheme : this.tilingScheme, + x : this.x * 2 + 1, + y : this.y * 2 + 1, + level : this.level + 1, parent : this - }), new QuadtreeTile({ - tilingScheme : tilingScheme, - x : x + 1, - y : y + 1, - level : level, + }); + } + return this._southeastChild; + } + }, + + /** + * Gets the northwest child tile. + * @type {QuadtreeTile} + */ + northwestChild : { + get : function() { + if (!defined(this._northwestChild)) { + this._northwestChild = new QuadtreeTile({ + tilingScheme : this.tilingScheme, + x : this.x * 2, + y : this.y * 2, + level : this.level + 1, parent : this - })]; + }); } + return this._northwestChild; + } + }, - return this._children; + /** + * Gets the northeast child tile. + * @type {QuadtreeTile} + */ + northeastChild : { + get : function() { + if (!defined(this._northeastChild)) { + this._northeastChild = new QuadtreeTile({ + tilingScheme : this.tilingScheme, + x : this.x * 2 + 1, + y : this.y * 2, + level : this.level + 1, + parent : this + }); + } + return this._northeastChild; } }, From 46242f33267cd2175d7ef80e357cc701497d66c6 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Mon, 7 Nov 2016 13:08:25 +1100 Subject: [PATCH 09/20] Render and load tiles in near-to-far order. --- Source/Scene/Globe.js | 17 +- Source/Scene/GlobeSurfaceTile.js | 60 +++--- Source/Scene/QuadtreePrimitive.js | 195 +++++++++++++----- .../CesiumInspectorViewModel.js | 8 +- 4 files changed, 182 insertions(+), 98 deletions(-) diff --git a/Source/Scene/Globe.js b/Source/Scene/Globe.js index e84b483997b0..a3ea4f6ddd3f 100644 --- a/Source/Scene/Globe.js +++ b/Source/Scene/Globe.js @@ -373,6 +373,10 @@ define([ var scratchGetHeightCartographic = new Cartographic(); var scratchGetHeightRay = new Ray(); + function tileIfContainsCartographic(tile, cartographic) { + return Rectangle.contains(tile.rectangle, cartographic) ? tile : undefined; + } + /** * Get the height of the surface at a given cartographic. * @@ -407,15 +411,10 @@ define([ } while (tile.renderable) { - var children = tile.children; - length = children.length; - - for (i = 0; i < length; ++i) { - tile = children[i]; - if (Rectangle.contains(tile.rectangle, cartographic)) { - break; - } - } + tile = tileIfContainsCartographic(tile.southwestChild, cartographic) || + tileIfContainsCartographic(tile.southeastChild, cartographic) || + tileIfContainsCartographic(tile.northwestChild, cartographic) || + tile.northeastChild; } while (defined(tile) && (!defined(tile.data) || !defined(tile.data.pickTerrain))) { diff --git a/Source/Scene/GlobeSurfaceTile.js b/Source/Scene/GlobeSurfaceTile.js index 688680b4a8ac..aacd731823f5 100644 --- a/Source/Scene/GlobeSurfaceTile.js +++ b/Source/Scene/GlobeSurfaceTile.js @@ -494,40 +494,42 @@ define([ // - child tiles that were previously upsampled need to be re-upsampled based on the new data. // - child tiles that were previously deemed unavailable may now be available. - if (defined(tile.children)) { - for (var childIndex = 0; childIndex < 4; ++childIndex) { - var childTile = tile.children[childIndex]; - if (childTile.state !== QuadtreeTileLoadState.START) { - var childSurfaceTile = childTile.data; - if (defined(childSurfaceTile.terrainData) && !childSurfaceTile.terrainData.wasCreatedByUpsampling()) { - // Data for the child tile has already been loaded. - continue; - } + propagateNewLoadedDataToChildTile(tile, surfaceTile, tile.southwestChild); + propagateNewLoadedDataToChildTile(tile, surfaceTile, tile.southeastChild); + propagateNewLoadedDataToChildTile(tile, surfaceTile, tile.northwestChild); + propagateNewLoadedDataToChildTile(tile, surfaceTile, tile.northeastChild); + } - // Restart the upsampling process, no matter its current state. - // We create a new instance rather than just restarting the existing one - // because there could be an asynchronous operation pending on the existing one. - if (defined(childSurfaceTile.upsampledTerrain)) { - childSurfaceTile.upsampledTerrain.freeResources(); - } - childSurfaceTile.upsampledTerrain = new TileTerrain({ - data : surfaceTile.terrainData, - x : tile.x, - y : tile.y, - level : tile.level - }); + function propagateNewLoadedDataToChildTile(tile, surfaceTile, childTile) { + if (childTile.state !== QuadtreeTileLoadState.START) { + var childSurfaceTile = childTile.data; + if (defined(childSurfaceTile.terrainData) && !childSurfaceTile.terrainData.wasCreatedByUpsampling()) { + // Data for the child tile has already been loaded. + return; + } - if (surfaceTile.terrainData.isChildAvailable(tile.x, tile.y, childTile.x, childTile.y)) { - // Data is available for the child now. It might have been before, too. - if (!defined(childSurfaceTile.loadedTerrain)) { - // No load process is in progress, so start one. - childSurfaceTile.loadedTerrain = new TileTerrain(); - } - } + // Restart the upsampling process, no matter its current state. + // We create a new instance rather than just restarting the existing one + // because there could be an asynchronous operation pending on the existing one. + if (defined(childSurfaceTile.upsampledTerrain)) { + childSurfaceTile.upsampledTerrain.freeResources(); + } + childSurfaceTile.upsampledTerrain = new TileTerrain({ + data : surfaceTile.terrainData, + x : tile.x, + y : tile.y, + level : tile.level + }); - childTile.state = QuadtreeTileLoadState.LOADING; + if (surfaceTile.terrainData.isChildAvailable(tile.x, tile.y, childTile.x, childTile.y)) { + // Data is available for the child now. It might have been before, too. + if (!defined(childSurfaceTile.loadedTerrain)) { + // No load process is in progress, so start one. + childSurfaceTile.loadedTerrain = new TileTerrain(); } } + + childTile.state = QuadtreeTileLoadState.LOADING; } } diff --git a/Source/Scene/QuadtreePrimitive.js b/Source/Scene/QuadtreePrimitive.js index bd2cd271064a..3b79eeb5a274 100644 --- a/Source/Scene/QuadtreePrimitive.js +++ b/Source/Scene/QuadtreePrimitive.js @@ -95,7 +95,7 @@ define([ var ellipsoid = tilingScheme.ellipsoid; this._tilesToRender = []; - this._tileTraversalQueue = new Queue(); + this._tileTraversalStack = []; this._tileLoadQueueHigh = []; // high priority tiles are preventing refinement this._tileLoadQueueMedium = []; // medium priority tiles are being rendered this._tileLoadQueueLow = []; // low priority tiles were refined past or are non-visible parts of quads. @@ -403,8 +403,8 @@ define([ var tilesToRender = primitive._tilesToRender; tilesToRender.length = 0; - var traversalQueue = primitive._tileTraversalQueue; - traversalQueue.clear(); + var traversalStack = primitive._tileTraversalStack; + traversalStack.length = 0; // We can't render anything before the level zero tiles exist. if (!defined(primitive._levelZeroTiles)) { @@ -418,6 +418,7 @@ define([ } primitive._occluders.ellipsoid.cameraPosition = frameState.camera.positionWC; + var cameraPositionCartographic = frameState.camera.positionCartographic; var tileProvider = primitive._tileProvider; var occluders = primitive._occluders; @@ -471,7 +472,7 @@ define([ } ++debug.tilesWaitingForChildren; } else if (tileProvider.computeTileVisibility(tile, frameState, occluders) !== Visibility.NONE) { - traversalQueue.enqueue(tile); + traversalStack.push(tile); } else { if (tile.needsLoading) { primitive._tileLoadQueueLow.push(tile); @@ -480,11 +481,8 @@ define([ } } - // Traverse the tiles in breadth-first order. - // This ordering allows us to load bigger, lower-detail tiles before smaller, higher-detail ones. - // This maximizes the average detail across the scene and results in fewer sharp transitions - // between very different LODs. - while (defined((tile = traversalQueue.dequeue()))) { + // Traverse in depth-first, near-to-far order. + while (defined((tile = traversalStack.pop()))) { ++debug.tilesVisited; primitive._tileReplacementQueue.markTileRendered(tile); @@ -494,10 +492,6 @@ define([ debug.maxDepth = tile.level; } - // There are a few different algorithms we could use here. - // This one doesn't load children unless we refine to them. - // We may want to revisit this in the future. - if (screenSpaceError(primitive, frameState, tile) < primitive.maximumScreenSpaceError) { // This tile meets SSE requirements, so render it. if (tile.needsLoading) { @@ -505,26 +499,46 @@ define([ primitive._tileLoadQueueMedium.push(tile); } addTileToRenderList(primitive, tile); - } else { + continue; + } + + var southwestChild = tile.southwestChild; + var southeastChild = tile.southeastChild; + var northwestChild = tile.northwestChild; + var northeastChild = tile.northeastChild; + var allAreRenderable = southwestChild.renderable && southeastChild.renderable && + northwestChild.renderable && northeastChild.renderable; + var allAreUpsampled = southwestChild.upsampledFromParent && southeastChild.upsampledFromParent && + northwestChild.upsampledFromParent && northeastChild.upsampledFromParent; + var allDoneLoading = !southwestChild.needsLoading && !southeastChild.needsLoading && + !northwestChild.needsLoading && !northeastChild.needsLoading; + + if (allAreRenderable && !allAreUpsampled) { + // SSE is not good enough and children are loaded, so refine. + // No need to add the children to the load queue because they'll be added (if necessary) when they're traversed. + traverseVisibleChildrenNearToFar(primitive, southwestChild, southeastChild, northwestChild, northeastChild, tileProvider, frameState, occluders, traversalStack); + if (tile.needsLoading) { - // We want to refine this tile if possible, so load with low priority. + // Tile is not rendered, so load it with low priority. primitive._tileLoadQueueLow.push(tile); } + } else if (allAreRenderable && !allAreUpsampled) { + // No point in rendering the children because they're all upsampled. Render this tile instead. + addTileToRenderList(primitive, tile); - if (queueChildrenLoadAndDetermineIfChildrenAreAllRenderable(primitive, tile)) { - // SSE is not good enough and children are loaded, so refine. - var children = tile.children; - // PERFORMANCE_IDEA: traverse children front-to-back so we can avoid sorting by distance later. - for (i = 0, len = children.length; i < len; ++i) { - if (tileProvider.computeTileVisibility(children[i], frameState, occluders) !== Visibility.NONE) { - traversalQueue.enqueue(children[i]); - } else { - ++debug.tilesCulled; - } - } - } else { - // SSE is not good enough but not all children are loaded, so render this tile anyway. - addTileToRenderList(primitive, tile); + if (tile.needsLoading) { + // Rendered tile that's not waiting on children loads with medium priority. + primitive._tileLoadQueueMedium.push(tile); + } + } else { + // We'd like to refine but can't. Load the refinement blockers with high priority and + // render this tile in the meantime. + queueChildLoadNearToFar(primitive, cameraPositionCartographic, southwestChild, southeastChild, northwestChild, northeastChild); + addTileToRenderList(primitive, tile); + + if (tile.needsLoading) { + // We will refine this tile when it's possible, so load this tile only with low priority. + primitive._tileLoadQueueLow.push(tile); } } } @@ -532,6 +546,94 @@ define([ raiseTileLoadProgressEvent(primitive); } + function queueChildLoadNearToFar(primitive, cameraPosition, southwest, southeast, northwest, northeast) { + if (cameraPosition.longitude < southwest.east) { + if (cameraPosition.latitude < southwest.north) { + // Camera in southwest quadrant + queueChildTileLoad(primitive, southwest); + queueChildTileLoad(primitive, southeast); + queueChildTileLoad(primitive, northwest); + queueChildTileLoad(primitive, northeast); + } else { + // Camera in northwest quadrant + queueChildTileLoad(primitive, northwest); + queueChildTileLoad(primitive, southwest); + queueChildTileLoad(primitive, northeast); + queueChildTileLoad(primitive, southeast); + } + } else { + if (cameraPosition.latitude < southwest.north) { + // Camera southeast quadrant + queueChildTileLoad(primitive, southeast); + queueChildTileLoad(primitive, southwest); + queueChildTileLoad(primitive, northeast); + queueChildTileLoad(primitive, northwest); + } else { + // Camera in northeast quadrant + queueChildTileLoad(primitive, northeast); + queueChildTileLoad(primitive, northwest); + queueChildTileLoad(primitive, southeast); + queueChildTileLoad(primitive, southwest); + } + } + } + + function queueChildTileLoad(primitive, childTile) { + primitive._tileReplacementQueue.markTileRendered(childTile); + if (childTile.needsLoading) { + if (childTile.renderable) { + primitive._tileLoadQueueLow.push(childTile); + } else { + // A tile blocking refine loads with high priority + primitive._tileLoadQueueHigh.push(childTile); + } + } + } + + function traverseVisibleChildrenNearToFar(primitive, southwest, southeast, northwest, northeast, tileProvider, frameState, occluders, traversalStack) { + var cameraPosition = frameState.camera.positionCartographic; + + // Traverse in the opposite of the desired order, because we're pushing onto a stack (first in, last out), + // and we want the nearest out first. + if (cameraPosition.longitude < southwest.east) { + if (cameraPosition.latitude < southwest.north) { + // Camera in southwest quadrant + traverseIfVisible(primitive, northeast, tileProvider, frameState, occluders, traversalStack); + traverseIfVisible(primitive, southeast, tileProvider, frameState, occluders, traversalStack); + traverseIfVisible(primitive, northwest, tileProvider, frameState, occluders, traversalStack); + traverseIfVisible(primitive, southwest, tileProvider, frameState, occluders, traversalStack); + } else { + // Camera in northwest quadrant + traverseIfVisible(primitive, southeast, tileProvider, frameState, occluders, traversalStack); + traverseIfVisible(primitive, southwest, tileProvider, frameState, occluders, traversalStack); + traverseIfVisible(primitive, northeast, tileProvider, frameState, occluders, traversalStack); + traverseIfVisible(primitive, northwest, tileProvider, frameState, occluders, traversalStack); + } + } else { + if (cameraPosition.latitude < southwest.north) { + // Camera southeast quadrant + traverseIfVisible(primitive, northwest, tileProvider, frameState, occluders, traversalStack); + traverseIfVisible(primitive, southwest, tileProvider, frameState, occluders, traversalStack); + traverseIfVisible(primitive, northeast, tileProvider, frameState, occluders, traversalStack); + traverseIfVisible(primitive, southeast, tileProvider, frameState, occluders, traversalStack); + } else { + // Camera in northeast quadrant + traverseIfVisible(primitive, southwest, tileProvider, frameState, occluders, traversalStack); + traverseIfVisible(primitive, northwest, tileProvider, frameState, occluders, traversalStack); + traverseIfVisible(primitive, southeast, tileProvider, frameState, occluders, traversalStack); + traverseIfVisible(primitive, northeast, tileProvider, frameState, occluders, traversalStack); + } + } + } + + function traverseIfVisible(primitive, tile, tileProvider, frameState, occluders, traversalStack) { + if (tileProvider.computeTileVisibility(tile, frameState, occluders) !== Visibility.NONE) { + traversalStack.push(tile); + } else { + ++primitive._debug.tilesCulled; + primitive._tileReplacementQueue.markTileRendered(tile); + } + } /** * Checks if the load queue length has changed since the last time we raised a queue change event - if so, raises * a new one. @@ -640,37 +742,18 @@ define([ // we're allowed to keep. primitive._tileReplacementQueue.trimTiles(primitive.tileCacheSize); - var startTime = getTimestamp(); - var timeSlice = primitive._loadQueueTimeSlice; - var endTime = startTime + timeSlice; - var i = 0; - var tile; - - for (i = tileLoadQueueHigh.length - 1; i >= 0; --i) { - tile = tileLoadQueueHigh[i]; - primitive._tileReplacementQueue.markTileRendered(tile); - tileProvider.loadTile(frameState, tile); - if (getTimestamp() >= endTime) { - break; - } - } + var endTime = getTimestamp() + primitive._loadQueueTimeSlice; - for (i = tileLoadQueueMedium.length - 1; i >= 0; --i) { - tile = tileLoadQueueMedium[i]; - primitive._tileReplacementQueue.markTileRendered(tile); - tileProvider.loadTile(frameState, tile); - if (getTimestamp() >= endTime) { - break; - } - } + processSinglePriorityLoadQueue(primitive, frameState, tileProvider, endTime, tileLoadQueueHigh); + processSinglePriorityLoadQueue(primitive, frameState, tileProvider, endTime, tileLoadQueueMedium); + processSinglePriorityLoadQueue(primitive, frameState, tileProvider, endTime, tileLoadQueueLow); + } - for (i = tileLoadQueueLow.length - 1; i >= 0; --i) { - tile = tileLoadQueueLow[i]; + function processSinglePriorityLoadQueue(primitive, frameState, tileProvider, endTime, loadQueue) { + for (var i = 0, len = loadQueue.length; i < len && getTimestamp() < endTime; ++i) { + var tile = loadQueue[i]; primitive._tileReplacementQueue.markTileRendered(tile); tileProvider.loadTile(frameState, tile); - if (getTimestamp() >= endTime) { - break; - } } } diff --git a/Source/Widgets/CesiumInspector/CesiumInspectorViewModel.js b/Source/Widgets/CesiumInspector/CesiumInspectorViewModel.js index 57b76e94c8a3..34cdfe968807 100644 --- a/Source/Widgets/CesiumInspector/CesiumInspectorViewModel.js +++ b/Source/Widgets/CesiumInspector/CesiumInspectorViewModel.js @@ -805,7 +805,7 @@ define([ get : function() { var that = this; return createCommand(function() { - that.tile = that.tile.children[0]; + that.tile = that.tile.northwestChild; }); } }, @@ -820,7 +820,7 @@ define([ get : function() { var that = this; return createCommand(function() { - that.tile = that.tile.children[1]; + that.tile = that.tile.northeastChild; }); } }, @@ -835,7 +835,7 @@ define([ get : function() { var that = this; return createCommand(function() { - that.tile = that.tile.children[2]; + that.tile = that.tile.southwestChild; }); } }, @@ -850,7 +850,7 @@ define([ get : function() { var that = this; return createCommand(function() { - that.tile = that.tile.children[3]; + that.tile = that.tile.southeastChild; }); } }, From 2ef5282e93fdca74fa8eac40dd2f2dfbcfe550e5 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Mon, 7 Nov 2016 15:43:01 +1100 Subject: [PATCH 10/20] Actually load near-to-far. --- Source/Scene/QuadtreePrimitive.js | 51 +++---------------------------- 1 file changed, 4 insertions(+), 47 deletions(-) diff --git a/Source/Scene/QuadtreePrimitive.js b/Source/Scene/QuadtreePrimitive.js index 3b79eeb5a274..3c167892ad89 100644 --- a/Source/Scene/QuadtreePrimitive.js +++ b/Source/Scene/QuadtreePrimitive.js @@ -522,7 +522,7 @@ define([ // Tile is not rendered, so load it with low priority. primitive._tileLoadQueueLow.push(tile); } - } else if (allAreRenderable && !allAreUpsampled) { + } else if (allAreRenderable && allAreUpsampled) { // No point in rendering the children because they're all upsampled. Render this tile instead. addTileToRenderList(primitive, tile); @@ -595,8 +595,8 @@ define([ // Traverse in the opposite of the desired order, because we're pushing onto a stack (first in, last out), // and we want the nearest out first. - if (cameraPosition.longitude < southwest.east) { - if (cameraPosition.latitude < southwest.north) { + if (cameraPosition.longitude < southwest.rectangle.east) { + if (cameraPosition.latitude < southwest.rectangle.north) { // Camera in southwest quadrant traverseIfVisible(primitive, northeast, tileProvider, frameState, occluders, traversalStack); traverseIfVisible(primitive, southeast, tileProvider, frameState, occluders, traversalStack); @@ -610,7 +610,7 @@ define([ traverseIfVisible(primitive, northwest, tileProvider, frameState, occluders, traversalStack); } } else { - if (cameraPosition.latitude < southwest.north) { + if (cameraPosition.latitude < southwest.rectangle.north) { // Camera southeast quadrant traverseIfVisible(primitive, northwest, tileProvider, frameState, occluders, traversalStack); traverseIfVisible(primitive, southwest, tileProvider, frameState, occluders, traversalStack); @@ -685,49 +685,6 @@ define([ ++primitive._debug.tilesRendered; } - function queueChildrenLoadAndDetermineIfChildrenAreAllRenderable(primitive, tile) { - var allRenderable = true; - var allUpsampledOnly = true; - var allDoneLoading = true; - - var children = tile.children; - var i; - var len; - var child; - for (i = 0, len = children.length; i < len; ++i) { - child = children[i]; - - primitive._tileReplacementQueue.markTileRendered(child); - - allUpsampledOnly = allUpsampledOnly && child.upsampledFromParent; - allRenderable = allRenderable && child.renderable; - allDoneLoading = allDoneLoading && !child.needsLoading; - } - - if (!allRenderable) { - ++primitive._debug.tilesWaitingForChildren; - } - - // If all children are upsampled from this tile, we just render this tile instead of its children (don't refine). - var willRefine = allRenderable && !allUpsampledOnly; - - if (!willRefine && !allDoneLoading) { - for (i = 0, len = children.length; i < len; ++i) { - child = children[i]; - if (child.needsLoading) { - if (child.renderable) { - primitive._tileLoadQueueLow.push(child); - } else { - // A tile blocking refine loads with high priority - primitive._tileLoadQueueHigh.push(child); - } - } - } - } - - return willRefine; - } - function processTileLoadQueue(primitive, frameState) { var tileLoadQueueHigh = primitive._tileLoadQueueHigh; var tileLoadQueueMedium = primitive._tileLoadQueueMedium; From 099a65acca0271e6adff9df86cf5fc89439b73d9 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Mon, 7 Nov 2016 17:20:18 +1100 Subject: [PATCH 11/20] Use recursion instead of an explicit stack. This makes it easier to do pre- and post- order actions. --- Source/Scene/QuadtreePrimitive.js | 142 +++++++++++++++--------------- 1 file changed, 69 insertions(+), 73 deletions(-) diff --git a/Source/Scene/QuadtreePrimitive.js b/Source/Scene/QuadtreePrimitive.js index 3c167892ad89..7f6634ceeef2 100644 --- a/Source/Scene/QuadtreePrimitive.js +++ b/Source/Scene/QuadtreePrimitive.js @@ -95,7 +95,6 @@ define([ var ellipsoid = tilingScheme.ellipsoid; this._tilesToRender = []; - this._tileTraversalStack = []; this._tileLoadQueueHigh = []; // high priority tiles are preventing refinement this._tileLoadQueueMedium = []; // medium priority tiles are being rendered this._tileLoadQueueLow = []; // low priority tiles were refined past or are non-visible parts of quads. @@ -403,9 +402,6 @@ define([ var tilesToRender = primitive._tilesToRender; tilesToRender.length = 0; - var traversalStack = primitive._tileTraversalStack; - traversalStack.length = 0; - // We can't render anything before the level zero tiles exist. if (!defined(primitive._levelZeroTiles)) { if (primitive._tileProvider.ready) { @@ -418,7 +414,6 @@ define([ } primitive._occluders.ellipsoid.cameraPosition = frameState.camera.positionWC; - var cameraPositionCartographic = frameState.camera.positionCartographic; var tileProvider = primitive._tileProvider; var occluders = primitive._occluders; @@ -462,7 +457,7 @@ define([ // but currently they're just loaded in our traversal order which makes no guarantees // about depth ordering. - // Enqueue the root tiles that are renderable and visible. + // Traverse in depth-first, near-to-far order. for (i = 0, len = levelZeroTiles.length; i < len; ++i) { tile = levelZeroTiles[i]; primitive._tileReplacementQueue.markTileRendered(tile); @@ -472,7 +467,7 @@ define([ } ++debug.tilesWaitingForChildren; } else if (tileProvider.computeTileVisibility(tile, frameState, occluders) !== Visibility.NONE) { - traversalStack.push(tile); + visitTile(primitive, frameState, tile); } else { if (tile.needsLoading) { primitive._tileLoadQueueLow.push(tile); @@ -481,48 +476,42 @@ define([ } } - // Traverse in depth-first, near-to-far order. - while (defined((tile = traversalStack.pop()))) { - ++debug.tilesVisited; + raiseTileLoadProgressEvent(primitive); + } - primitive._tileReplacementQueue.markTileRendered(tile); - tile._updateCustomData(frameNumber); + function visitTile(primitive, frameState, tile) { + var debug = primitive._debug; - if (tile.level > debug.maxDepth) { - debug.maxDepth = tile.level; - } + ++debug.tilesVisited; - if (screenSpaceError(primitive, frameState, tile) < primitive.maximumScreenSpaceError) { - // This tile meets SSE requirements, so render it. - if (tile.needsLoading) { - // Rendered tile meeting SSE loads with medium priority. - primitive._tileLoadQueueMedium.push(tile); - } - addTileToRenderList(primitive, tile); - continue; - } + primitive._tileReplacementQueue.markTileRendered(tile); + tile._updateCustomData(frameState.frameNumber); - var southwestChild = tile.southwestChild; - var southeastChild = tile.southeastChild; - var northwestChild = tile.northwestChild; - var northeastChild = tile.northeastChild; - var allAreRenderable = southwestChild.renderable && southeastChild.renderable && - northwestChild.renderable && northeastChild.renderable; - var allAreUpsampled = southwestChild.upsampledFromParent && southeastChild.upsampledFromParent && - northwestChild.upsampledFromParent && northeastChild.upsampledFromParent; - var allDoneLoading = !southwestChild.needsLoading && !southeastChild.needsLoading && - !northwestChild.needsLoading && !northeastChild.needsLoading; - - if (allAreRenderable && !allAreUpsampled) { - // SSE is not good enough and children are loaded, so refine. - // No need to add the children to the load queue because they'll be added (if necessary) when they're traversed. - traverseVisibleChildrenNearToFar(primitive, southwestChild, southeastChild, northwestChild, northeastChild, tileProvider, frameState, occluders, traversalStack); + if (tile.level > debug.maxDepth) { + debug.maxDepth = tile.level; + } - if (tile.needsLoading) { - // Tile is not rendered, so load it with low priority. - primitive._tileLoadQueueLow.push(tile); - } - } else if (allAreRenderable && allAreUpsampled) { + if (screenSpaceError(primitive, frameState, tile) < primitive.maximumScreenSpaceError) { + // This tile meets SSE requirements, so render it. + if (tile.needsLoading) { + // Rendered tile meeting SSE loads with medium priority. + primitive._tileLoadQueueMedium.push(tile); + } + addTileToRenderList(primitive, tile); + return; + } + + var southwestChild = tile.southwestChild; + var southeastChild = tile.southeastChild; + var northwestChild = tile.northwestChild; + var northeastChild = tile.northeastChild; + var allAreRenderable = southwestChild.renderable && southeastChild.renderable && + northwestChild.renderable && northeastChild.renderable; + var allAreUpsampled = southwestChild.upsampledFromParent && southeastChild.upsampledFromParent && + northwestChild.upsampledFromParent && northeastChild.upsampledFromParent; + + if (allAreRenderable) { + if (allAreUpsampled) { // No point in rendering the children because they're all upsampled. Render this tile instead. addTileToRenderList(primitive, tile); @@ -531,19 +520,26 @@ define([ primitive._tileLoadQueueMedium.push(tile); } } else { - // We'd like to refine but can't. Load the refinement blockers with high priority and - // render this tile in the meantime. - queueChildLoadNearToFar(primitive, cameraPositionCartographic, southwestChild, southeastChild, northwestChild, northeastChild); - addTileToRenderList(primitive, tile); + // SSE is not good enough and children are loaded, so refine. + // No need to add the children to the load queue because they'll be added (if necessary) when they're visited. + visitVisibleChildrenNearToFar(primitive, southwestChild, southeastChild, northwestChild, northeastChild, frameState); if (tile.needsLoading) { - // We will refine this tile when it's possible, so load this tile only with low priority. + // Tile is not rendered, so load it with low priority. primitive._tileLoadQueueLow.push(tile); } } + } else { + // We'd like to refine but can't because not all of our children are renderable. Load the refinement blockers with high priority and + // render this tile in the meantime. + queueChildLoadNearToFar(primitive, frameState.camera.positionCartographic, southwestChild, southeastChild, northwestChild, northeastChild); + addTileToRenderList(primitive, tile); + + if (tile.needsLoading) { + // We will refine this tile when it's possible, so load this tile only with low priority. + primitive._tileLoadQueueLow.push(tile); + } } - - raiseTileLoadProgressEvent(primitive); } function queueChildLoadNearToFar(primitive, cameraPosition, southwest, southeast, northwest, northeast) { @@ -590,45 +586,45 @@ define([ } } - function traverseVisibleChildrenNearToFar(primitive, southwest, southeast, northwest, northeast, tileProvider, frameState, occluders, traversalStack) { + function visitVisibleChildrenNearToFar(primitive, southwest, southeast, northwest, northeast, frameState) { var cameraPosition = frameState.camera.positionCartographic; + var tileProvider = primitive._tileProvider; + var occluders = primitive._occluders; - // Traverse in the opposite of the desired order, because we're pushing onto a stack (first in, last out), - // and we want the nearest out first. if (cameraPosition.longitude < southwest.rectangle.east) { if (cameraPosition.latitude < southwest.rectangle.north) { // Camera in southwest quadrant - traverseIfVisible(primitive, northeast, tileProvider, frameState, occluders, traversalStack); - traverseIfVisible(primitive, southeast, tileProvider, frameState, occluders, traversalStack); - traverseIfVisible(primitive, northwest, tileProvider, frameState, occluders, traversalStack); - traverseIfVisible(primitive, southwest, tileProvider, frameState, occluders, traversalStack); + visitIfVisible(primitive, southwest, tileProvider, frameState, occluders); + visitIfVisible(primitive, southeast, tileProvider, frameState, occluders); + visitIfVisible(primitive, northwest, tileProvider, frameState, occluders); + visitIfVisible(primitive, northeast, tileProvider, frameState, occluders); } else { // Camera in northwest quadrant - traverseIfVisible(primitive, southeast, tileProvider, frameState, occluders, traversalStack); - traverseIfVisible(primitive, southwest, tileProvider, frameState, occluders, traversalStack); - traverseIfVisible(primitive, northeast, tileProvider, frameState, occluders, traversalStack); - traverseIfVisible(primitive, northwest, tileProvider, frameState, occluders, traversalStack); + visitIfVisible(primitive, northwest, tileProvider, frameState, occluders); + visitIfVisible(primitive, southwest, tileProvider, frameState, occluders); + visitIfVisible(primitive, northeast, tileProvider, frameState, occluders); + visitIfVisible(primitive, southeast, tileProvider, frameState, occluders); } } else { if (cameraPosition.latitude < southwest.rectangle.north) { // Camera southeast quadrant - traverseIfVisible(primitive, northwest, tileProvider, frameState, occluders, traversalStack); - traverseIfVisible(primitive, southwest, tileProvider, frameState, occluders, traversalStack); - traverseIfVisible(primitive, northeast, tileProvider, frameState, occluders, traversalStack); - traverseIfVisible(primitive, southeast, tileProvider, frameState, occluders, traversalStack); + visitIfVisible(primitive, southeast, tileProvider, frameState, occluders); + visitIfVisible(primitive, southwest, tileProvider, frameState, occluders); + visitIfVisible(primitive, northeast, tileProvider, frameState, occluders); + visitIfVisible(primitive, northwest, tileProvider, frameState, occluders); } else { // Camera in northeast quadrant - traverseIfVisible(primitive, southwest, tileProvider, frameState, occluders, traversalStack); - traverseIfVisible(primitive, northwest, tileProvider, frameState, occluders, traversalStack); - traverseIfVisible(primitive, southeast, tileProvider, frameState, occluders, traversalStack); - traverseIfVisible(primitive, northeast, tileProvider, frameState, occluders, traversalStack); + visitIfVisible(primitive, northeast, tileProvider, frameState, occluders); + visitIfVisible(primitive, northwest, tileProvider, frameState, occluders); + visitIfVisible(primitive, southeast, tileProvider, frameState, occluders); + visitIfVisible(primitive, southwest, tileProvider, frameState, occluders); } } } - function traverseIfVisible(primitive, tile, tileProvider, frameState, occluders, traversalStack) { + function visitIfVisible(primitive, tile, tileProvider, frameState, occluders) { if (tileProvider.computeTileVisibility(tile, frameState, occluders) !== Visibility.NONE) { - traversalStack.push(tile); + visitTile(primitive, frameState, tile); } else { ++primitive._debug.tilesCulled; primitive._tileReplacementQueue.markTileRendered(tile); @@ -703,7 +699,7 @@ define([ processSinglePriorityLoadQueue(primitive, frameState, tileProvider, endTime, tileLoadQueueHigh); processSinglePriorityLoadQueue(primitive, frameState, tileProvider, endTime, tileLoadQueueMedium); - processSinglePriorityLoadQueue(primitive, frameState, tileProvider, endTime, tileLoadQueueLow); + //processSinglePriorityLoadQueue(primitive, frameState, tileProvider, endTime, tileLoadQueueLow); } function processSinglePriorityLoadQueue(primitive, frameState, tileProvider, endTime, loadQueue) { From 083e36e68fb950a1d0933b144954180389542297 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Mon, 7 Nov 2016 21:18:28 +1100 Subject: [PATCH 12/20] Fix test failures by removing uses of _children. --- Source/Scene/GlobeSurfaceTile.js | 50 +++++++++++++++------------- Source/Scene/QuadtreePrimitive.js | 2 +- Source/Scene/QuadtreeTile.js | 20 +++++++---- Specs/Scene/QuadtreePrimitiveSpec.js | 8 ++--- 4 files changed, 43 insertions(+), 37 deletions(-) diff --git a/Source/Scene/GlobeSurfaceTile.js b/Source/Scene/GlobeSurfaceTile.js index aacd731823f5..4e66c11fe858 100644 --- a/Source/Scene/GlobeSurfaceTile.js +++ b/Source/Scene/GlobeSurfaceTile.js @@ -458,32 +458,34 @@ define([ // of its ancestors receives new (better) data and we want to re-upsample from the // new data. - if (defined(tile._children)) { - for (var childIndex = 0; childIndex < 4; ++childIndex) { - var childTile = tile._children[childIndex]; - if (childTile.state !== QuadtreeTileLoadState.START) { - var childSurfaceTile = childTile.data; - if (defined(childSurfaceTile.terrainData) && !childSurfaceTile.terrainData.wasCreatedByUpsampling()) { - // Data for the child tile has already been loaded. - continue; - } + propagateNewUpsampledDataToChild(tile, tile._southwestChild); + propagateNewUpsampledDataToChild(tile, tile._southeastChild); + propagateNewUpsampledDataToChild(tile, tile._northwestChild); + propagateNewUpsampledDataToChild(tile, tile._northeastChild); + } - // Restart the upsampling process, no matter its current state. - // We create a new instance rather than just restarting the existing one - // because there could be an asynchronous operation pending on the existing one. - if (defined(childSurfaceTile.upsampledTerrain)) { - childSurfaceTile.upsampledTerrain.freeResources(); - } - childSurfaceTile.upsampledTerrain = new TileTerrain({ - data : surfaceTile.terrainData, - x : tile.x, - y : tile.y, - level : tile.level - }); - - childTile.state = QuadtreeTileLoadState.LOADING; - } + function propagateNewUpsampledDataToChild(tile, childTile) { + if (defined(childTile) && childTile.state !== QuadtreeTileLoadState.START) { + var childSurfaceTile = childTile.data; + if (defined(childSurfaceTile.terrainData) && !childSurfaceTile.terrainData.wasCreatedByUpsampling()) { + // Data for the child tile has already been loaded. + return; + } + + // Restart the upsampling process, no matter its current state. + // We create a new instance rather than just restarting the existing one + // because there could be an asynchronous operation pending on the existing one. + if (defined(childSurfaceTile.upsampledTerrain)) { + childSurfaceTile.upsampledTerrain.freeResources(); } + childSurfaceTile.upsampledTerrain = new TileTerrain({ + data : tile.data.terrainData, + x : tile.x, + y : tile.y, + level : tile.level + }); + + childTile.state = QuadtreeTileLoadState.LOADING; } } diff --git a/Source/Scene/QuadtreePrimitive.js b/Source/Scene/QuadtreePrimitive.js index 7f6634ceeef2..c1f6d902e991 100644 --- a/Source/Scene/QuadtreePrimitive.js +++ b/Source/Scene/QuadtreePrimitive.js @@ -699,7 +699,7 @@ define([ processSinglePriorityLoadQueue(primitive, frameState, tileProvider, endTime, tileLoadQueueHigh); processSinglePriorityLoadQueue(primitive, frameState, tileProvider, endTime, tileLoadQueueMedium); - //processSinglePriorityLoadQueue(primitive, frameState, tileProvider, endTime, tileLoadQueueLow); + processSinglePriorityLoadQueue(primitive, frameState, tileProvider, endTime, tileLoadQueueLow); } function processSinglePriorityLoadQueue(primitive, frameState, tileProvider, endTime, loadQueue) { diff --git a/Source/Scene/QuadtreeTile.js b/Source/Scene/QuadtreeTile.js index e65730bb939a..faa211f32f74 100644 --- a/Source/Scene/QuadtreeTile.js +++ b/Source/Scene/QuadtreeTile.js @@ -406,13 +406,21 @@ define([ this.data.freeResources(); } - if (defined(this._children)) { - for (var i = 0, len = this._children.length; i < len; ++i) { - this._children[i].freeResources(); - } - this._children = undefined; - } + freeTile(this._southwestChild); + this._southwestChild = undefined; + freeTile(this._southeastChild); + this._southeastChild = undefined; + freeTile(this._northwestChild); + this._northwestChild = undefined; + freeTile(this._northeastChild); + this._northeastChild = undefined; }; + function freeTile(tile) { + if (defined(tile)) { + tile.freeResources(); + } + } + return QuadtreeTile; }); diff --git a/Specs/Scene/QuadtreePrimitiveSpec.js b/Specs/Scene/QuadtreePrimitiveSpec.js index 57e606d5642c..203041434204 100644 --- a/Specs/Scene/QuadtreePrimitiveSpec.js +++ b/Specs/Scene/QuadtreePrimitiveSpec.js @@ -191,18 +191,14 @@ defineSuite([ expect(progressEventSpy.calls.mostRecent().args[0]).toEqual(1); // Simulate the second zero-level child having loaded with two children. - quadtree._levelZeroTiles[1]._children = [ - buildEmptyQuadtreeTile(tileProvider), - buildEmptyQuadtreeTile(tileProvider) - ]; quadtree._levelZeroTiles[1].state = QuadtreeTileLoadState.DONE; quadtree._levelZeroTiles[1].renderable = true; quadtree.beginFrame(scene.frameState); quadtree.update(scene.frameState); quadtree.endFrame(scene.frameState); - // Now this should be back to 2. - expect(progressEventSpy.calls.mostRecent().args[0]).toEqual(2); + // Now that tile's four children should be in the load queue. + expect(progressEventSpy.calls.mostRecent().args[0]).toEqual(4); }); it('forEachLoadedTile does not enumerate tiles in the START state', function() { From d4426ae75c39e2456e7e65e7635e0685d8908ca1 Mon Sep 17 00:00:00 2001 From: Patrick Cozzi Date: Mon, 7 Nov 2016 07:22:17 -0500 Subject: [PATCH 13/20] Whitespace --- Source/Scene/QuadtreeTile.js | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/Scene/QuadtreeTile.js b/Source/Scene/QuadtreeTile.js index faa211f32f74..1609bf12dfe1 100644 --- a/Source/Scene/QuadtreeTile.js +++ b/Source/Scene/QuadtreeTile.js @@ -251,7 +251,6 @@ define([ } }, - /** * An array of tiles that is at the next level of the tile tree. * @memberof QuadtreeTile.prototype From 439161df20166ecae9a6aa42ab472de5dccf1dab Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Wed, 9 Nov 2016 16:17:30 +1100 Subject: [PATCH 14/20] Fix jshint warnings. --- Source/Scene/GlobeSurfaceTile.js | 2 -- Specs/Scene/QuadtreePrimitiveSpec.js | 9 --------- 2 files changed, 11 deletions(-) diff --git a/Source/Scene/GlobeSurfaceTile.js b/Source/Scene/GlobeSurfaceTile.js index 4e66c11fe858..e88ae7e81be6 100644 --- a/Source/Scene/GlobeSurfaceTile.js +++ b/Source/Scene/GlobeSurfaceTile.js @@ -449,8 +449,6 @@ define([ } function propagateNewUpsampledDataToChildren(tile) { - var surfaceTile = tile.data; - // Now that there's new data for this tile: // - child tiles that were previously upsampled need to be re-upsampled based on the new data. diff --git a/Specs/Scene/QuadtreePrimitiveSpec.js b/Specs/Scene/QuadtreePrimitiveSpec.js index 203041434204..63c13b86627e 100644 --- a/Specs/Scene/QuadtreePrimitiveSpec.js +++ b/Specs/Scene/QuadtreePrimitiveSpec.js @@ -350,13 +350,4 @@ defineSuite([ expect(position).toEqual(updatedPosition); }); - - function buildEmptyQuadtreeTile(tileProvider) { - return new QuadtreeTile({ - x : 0, - y : 0, - level : 0, - tilingScheme : tileProvider.tilingScheme - }); - } }, 'WebGL'); From bbcf49429cb89c2b9ebc3f351165bff6fff77e79 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Thu, 10 Nov 2016 15:51:52 +1100 Subject: [PATCH 15/20] Add a test, tweak loading of upsampled-only children. --- Source/Scene/QuadtreePrimitive.js | 5 ++ Specs/Scene/QuadtreePrimitiveSpec.js | 85 ++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+) diff --git a/Source/Scene/QuadtreePrimitive.js b/Source/Scene/QuadtreePrimitive.js index c1f6d902e991..2103b76daed8 100644 --- a/Source/Scene/QuadtreePrimitive.js +++ b/Source/Scene/QuadtreePrimitive.js @@ -515,6 +515,11 @@ define([ // No point in rendering the children because they're all upsampled. Render this tile instead. addTileToRenderList(primitive, tile); + // Load the children even though we're (currently) not going to render them. + // A tile that is "upsampled only" right now might change its tune once it does more loading. + // A tile that is upsampled now and forever should also be done loading, so no harm done. + queueChildLoadNearToFar(primitive, frameState.camera.positionCartographic, southwestChild, southeastChild, northwestChild, northeastChild); + if (tile.needsLoading) { // Rendered tile that's not waiting on children loads with medium priority. primitive._tileLoadQueueMedium.push(tile); diff --git a/Specs/Scene/QuadtreePrimitiveSpec.js b/Specs/Scene/QuadtreePrimitiveSpec.js index 63c13b86627e..b857ca534501 100644 --- a/Specs/Scene/QuadtreePrimitiveSpec.js +++ b/Specs/Scene/QuadtreePrimitiveSpec.js @@ -350,4 +350,89 @@ defineSuite([ expect(position).toEqual(updatedPosition); }); + + it('gives correct priority to tile loads', function() { + var tileProvider = createSpyTileProvider(); + tileProvider.getReady.and.returnValue(true); + tileProvider.computeTileVisibility.and.returnValue(Visibility.FULL); + + var quadtree = new QuadtreePrimitive({ + tileProvider : tileProvider + }); + + quadtree.beginFrame(scene.frameState); + quadtree.update(scene.frameState); + quadtree.endFrame(scene.frameState); + + // The root tiles should be in the high priority load queue + expect(quadtree._tileLoadQueueHigh.length).toBe(2); + expect(quadtree._tileLoadQueueHigh).toContain(quadtree._levelZeroTiles[0]); + expect(quadtree._tileLoadQueueHigh).toContain(quadtree._levelZeroTiles[1]); + expect(quadtree._tileLoadQueueMedium.length).toBe(0); + expect(quadtree._tileLoadQueueLow.length).toBe(0); + + // Mark the first root tile renderable (but not done loading) + quadtree._levelZeroTiles[0].renderable = true; + + quadtree.beginFrame(scene.frameState); + quadtree.update(scene.frameState); + quadtree.endFrame(scene.frameState); + + // That root tile should now load with low priority while its children should load with high. + expect(quadtree._tileLoadQueueHigh.length).toBe(5); + expect(quadtree._tileLoadQueueHigh).toContain(quadtree._levelZeroTiles[1]); + expect(quadtree._tileLoadQueueHigh).toContain(quadtree._levelZeroTiles[0].children[0]); + expect(quadtree._tileLoadQueueHigh).toContain(quadtree._levelZeroTiles[0].children[1]); + expect(quadtree._tileLoadQueueHigh).toContain(quadtree._levelZeroTiles[0].children[2]); + expect(quadtree._tileLoadQueueHigh).toContain(quadtree._levelZeroTiles[0].children[3]); + expect(quadtree._tileLoadQueueMedium.length).toBe(0); + expect(quadtree._tileLoadQueueLow.length).toBe(1); + expect(quadtree._tileLoadQueueLow).toContain(quadtree._levelZeroTiles[0]); + + // Mark the children of that root tile renderable too, so we can refine it + quadtree._levelZeroTiles[0].children[0].renderable = true; + quadtree._levelZeroTiles[0].children[1].renderable = true; + quadtree._levelZeroTiles[0].children[2].renderable = true; + quadtree._levelZeroTiles[0].children[3].renderable = true; + + quadtree.beginFrame(scene.frameState); + quadtree.update(scene.frameState); + quadtree.endFrame(scene.frameState); + + expect(quadtree._tileLoadQueueHigh.length).toBe(17); // levelZeroTiles[1] plus levelZeroTiles[0]'s 16 grandchildren + expect(quadtree._tileLoadQueueHigh).toContain(quadtree._levelZeroTiles[1]); + expect(quadtree._tileLoadQueueHigh).toContain(quadtree._levelZeroTiles[0].children[0].children[0]); + expect(quadtree._tileLoadQueueHigh).toContain(quadtree._levelZeroTiles[0].children[0].children[1]); + expect(quadtree._tileLoadQueueHigh).toContain(quadtree._levelZeroTiles[0].children[0].children[2]); + expect(quadtree._tileLoadQueueHigh).toContain(quadtree._levelZeroTiles[0].children[0].children[3]); + expect(quadtree._tileLoadQueueMedium.length).toBe(0); + expect(quadtree._tileLoadQueueLow.length).toBe(5); + expect(quadtree._tileLoadQueueLow).toContain(quadtree._levelZeroTiles[0]); + expect(quadtree._tileLoadQueueLow).toContain(quadtree._levelZeroTiles[0].children[0]); + expect(quadtree._tileLoadQueueLow).toContain(quadtree._levelZeroTiles[0].children[1]); + expect(quadtree._tileLoadQueueLow).toContain(quadtree._levelZeroTiles[0].children[2]); + expect(quadtree._tileLoadQueueLow).toContain(quadtree._levelZeroTiles[0].children[3]); + + // Mark the children of levelZeroTiles[0] upsampled + quadtree._levelZeroTiles[0].children[0].upsampledFromParent = true; + quadtree._levelZeroTiles[0].children[1].upsampledFromParent = true; + quadtree._levelZeroTiles[0].children[2].upsampledFromParent = true; + quadtree._levelZeroTiles[0].children[3].upsampledFromParent = true; + + quadtree.beginFrame(scene.frameState); + quadtree.update(scene.frameState); + quadtree.endFrame(scene.frameState); + + // levelZeroTiles[0] should move to medium priority. + // Its descendents should continue loading, so they have a chance to decide they're not upsampled later. + expect(quadtree._tileLoadQueueHigh.length).toBe(1); + expect(quadtree._tileLoadQueueHigh).toContain(quadtree._levelZeroTiles[1]); + expect(quadtree._tileLoadQueueMedium.length).toBe(1); + expect(quadtree._tileLoadQueueMedium).toContain(quadtree._levelZeroTiles[0]); + expect(quadtree._tileLoadQueueLow.length).toBe(4); + expect(quadtree._tileLoadQueueLow).toContain(quadtree._levelZeroTiles[0].children[0]); + expect(quadtree._tileLoadQueueLow).toContain(quadtree._levelZeroTiles[0].children[1]); + expect(quadtree._tileLoadQueueLow).toContain(quadtree._levelZeroTiles[0].children[2]); + expect(quadtree._tileLoadQueueLow).toContain(quadtree._levelZeroTiles[0].children[3]); + }); }, 'WebGL'); From 33878c3d1e1f17fbaf6d621c9e263568a0edbefd Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Thu, 10 Nov 2016 17:49:19 +1100 Subject: [PATCH 16/20] Remove explicit sort for near-to-far rendering. --- Source/Scene/QuadtreePrimitive.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Source/Scene/QuadtreePrimitive.js b/Source/Scene/QuadtreePrimitive.js index 2103b76daed8..9b3f896a1a7c 100644 --- a/Source/Scene/QuadtreePrimitive.js +++ b/Source/Scene/QuadtreePrimitive.js @@ -802,17 +802,11 @@ define([ } } - function tileDistanceSortFunction(a, b) { - return a._distance - b._distance; - } - function createRenderCommandsForSelectedTiles(primitive, frameState) { var tileProvider = primitive._tileProvider; var tilesToRender = primitive._tilesToRender; var tilesToUpdateHeights = primitive._tileToUpdateHeights; - tilesToRender.sort(tileDistanceSortFunction); - for (var i = 0, len = tilesToRender.length; i < len; ++i) { var tile = tilesToRender[i]; tileProvider.showTileThisFrame(tile, frameState); From 2daf35397c2f24c51c7fa3da8596696f4bd182ea Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Thu, 10 Nov 2016 18:11:21 +1100 Subject: [PATCH 17/20] Update CHANGES.md. --- CHANGES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index f7d86848e02c..b6b5b82dd5a3 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -6,7 +6,7 @@ Change Log * * Breaking changes * -* Improved terrain/imagery load ordering, especially when the terrain is already fully loaded and we add a new imagery layer. +* Improved terrain/imagery load ordering, especially when the terrain is already fully loaded and we add a new imagery layer. This results in a 25% reduction in load times in many cases. * Added support for saving html and css in Github Gists. [#4125](https://github.com/AnalyticalGraphicsInc/cesium/issues/4125) * Fixed `Cartographic.fromCartesian` when the cartesian is not on the ellipsoid surface. [#4611](https://github.com/AnalyticalGraphicsInc/cesium/issues/4611) From d61437d23aaaff42377c5e7276a3bbb89cd06e45 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Fri, 11 Nov 2016 11:14:46 +1100 Subject: [PATCH 18/20] Add test of near-to-far render order. --- Source/Scene/QuadtreePrimitive.js | 20 ++++++++++ Specs/Scene/QuadtreePrimitiveSpec.js | 55 ++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+) diff --git a/Source/Scene/QuadtreePrimitive.js b/Source/Scene/QuadtreePrimitive.js index 9b3f896a1a7c..ef14e0b3697c 100644 --- a/Source/Scene/QuadtreePrimitive.js +++ b/Source/Scene/QuadtreePrimitive.js @@ -389,6 +389,20 @@ define([ this._tileProvider = this._tileProvider && this._tileProvider.destroy(); }; + var comparisonPoint; + var centerScratch = new Cartographic(); + function compareDistanceToPoint(a, b) { + var center = Rectangle.center(a.rectangle, centerScratch); + var alon = center.longitude - comparisonPoint.longitude; + var alat = center.latitude - comparisonPoint.latitude; + + center = Rectangle.center(b.rectangle, centerScratch); + var blon = center.longitude - comparisonPoint.longitude; + var blat = center.latitude - comparisonPoint.latitude; + + return (alon * alon + alat * alat) - (blon * blon + blat * blat); + } + function selectTilesForRendering(primitive, frameState) { var debug = primitive._debug; if (debug.suspendLodUpdate) { @@ -421,6 +435,12 @@ define([ var tile; var levelZeroTiles = primitive._levelZeroTiles; + // Sort the level zero tiles by the distance from the center to the camera. + // The level zero tiles aren't necessarily a nice neat quad, so we can use the + // quadtree ordering we use elsewhere in the tree + comparisonPoint = frameState.camera.positionCartographic; + levelZeroTiles.sort(compareDistanceToPoint) + var customDataAdded = primitive._addHeightCallbacks; var customDataRemoved = primitive._removeHeightCallbacks; var frameNumber = frameState.frameNumber; diff --git a/Specs/Scene/QuadtreePrimitiveSpec.js b/Specs/Scene/QuadtreePrimitiveSpec.js index b857ca534501..c23dc2ba5d0b 100644 --- a/Specs/Scene/QuadtreePrimitiveSpec.js +++ b/Specs/Scene/QuadtreePrimitiveSpec.js @@ -9,6 +9,7 @@ defineSuite([ 'Core/Visibility', 'Scene/QuadtreeTile', 'Scene/QuadtreeTileLoadState', + 'Specs/pollToPromise', 'Specs/createScene' ], function( QuadtreePrimitive, @@ -20,6 +21,7 @@ defineSuite([ Visibility, QuadtreeTile, QuadtreeTileLoadState, + pollToPromise, createScene) { 'use strict'; @@ -435,4 +437,57 @@ defineSuite([ expect(quadtree._tileLoadQueueLow).toContain(quadtree._levelZeroTiles[0].children[2]); expect(quadtree._tileLoadQueueLow).toContain(quadtree._levelZeroTiles[0].children[3]); }); + + it('renders tiles in approximate near-to-far order', function() { + var tileProvider = createSpyTileProvider(); + tileProvider.getReady.and.returnValue(true); + tileProvider.computeTileVisibility.and.returnValue(Visibility.FULL); + + var quadtree = new QuadtreePrimitive({ + tileProvider : tileProvider + }); + + tileProvider.loadTile.and.callFake(function(frameState, tile) { + if (tile.level <= 1) { + tile.state = QuadtreeTileLoadState.DONE; + tile.renderable = true; + } + }); + + scene.camera.setView({ + destination : Cartesian3.fromDegrees(1.0, 1.0, 15000.0) + }); + scene.camera.update(scene.mode); + + return pollToPromise(function() { + quadtree.beginFrame(scene.frameState); + quadtree.update(scene.frameState); + quadtree.endFrame(scene.frameState); + + return quadtree._tilesToRender.filter(function(tile) { return tile.level === 1; }).length === 8; + }).then(function() { + quadtree.beginFrame(scene.frameState); + quadtree.update(scene.frameState); + quadtree.endFrame(scene.frameState); + + // Rendered tiles: + // +----+----+----+----+ + // |w.nw|w.ne|e.nw|e.ne| + // +----+----+----+----+ + // |w.sw|w.se|e.sw|e.se| + // +----+----+----+----+ + // camera is located in e.nw (east.northwestChild) + + var west = quadtree._levelZeroTiles.filter(function(tile) { return tile.x === 0; })[0]; + var east = quadtree._levelZeroTiles.filter(function(tile) { return tile.x === 1; })[0]; + expect(quadtree._tilesToRender[0]).toBe(east.northwestChild); + expect(quadtree._tilesToRender[1] == east.southwestChild || quadtree._tilesToRender[1] === east.northeastChild).toBe(true); + expect(quadtree._tilesToRender[2] == east.southwestChild || quadtree._tilesToRender[2] === east.northeastChild).toBe(true); + expect(quadtree._tilesToRender[3]).toBe(east.southeastChild); + expect(quadtree._tilesToRender[4]).toBe(west.northeastChild); + expect(quadtree._tilesToRender[5] == west.northwestChild || quadtree._tilesToRender[5] === west.southeastChild).toBe(true); + expect(quadtree._tilesToRender[6] == west.northwestChild || quadtree._tilesToRender[6] === west.southeastChild).toBe(true); + expect(quadtree._tilesToRender[7]).toBe(west.southwestChild); + }); + }); }, 'WebGL'); From 71b56bfd3e32a9e84700b26805def2fa2d554f08 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Fri, 11 Nov 2016 11:45:44 +1100 Subject: [PATCH 19/20] Fix jshint warnings. --- Source/Scene/QuadtreePrimitive.js | 2 +- Specs/Scene/QuadtreePrimitiveSpec.js | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Source/Scene/QuadtreePrimitive.js b/Source/Scene/QuadtreePrimitive.js index ef14e0b3697c..782230e37ea2 100644 --- a/Source/Scene/QuadtreePrimitive.js +++ b/Source/Scene/QuadtreePrimitive.js @@ -439,7 +439,7 @@ define([ // The level zero tiles aren't necessarily a nice neat quad, so we can use the // quadtree ordering we use elsewhere in the tree comparisonPoint = frameState.camera.positionCartographic; - levelZeroTiles.sort(compareDistanceToPoint) + levelZeroTiles.sort(compareDistanceToPoint); var customDataAdded = primitive._addHeightCallbacks; var customDataRemoved = primitive._removeHeightCallbacks; diff --git a/Specs/Scene/QuadtreePrimitiveSpec.js b/Specs/Scene/QuadtreePrimitiveSpec.js index c23dc2ba5d0b..3a5641b3939b 100644 --- a/Specs/Scene/QuadtreePrimitiveSpec.js +++ b/Specs/Scene/QuadtreePrimitiveSpec.js @@ -481,12 +481,12 @@ defineSuite([ var west = quadtree._levelZeroTiles.filter(function(tile) { return tile.x === 0; })[0]; var east = quadtree._levelZeroTiles.filter(function(tile) { return tile.x === 1; })[0]; expect(quadtree._tilesToRender[0]).toBe(east.northwestChild); - expect(quadtree._tilesToRender[1] == east.southwestChild || quadtree._tilesToRender[1] === east.northeastChild).toBe(true); - expect(quadtree._tilesToRender[2] == east.southwestChild || quadtree._tilesToRender[2] === east.northeastChild).toBe(true); + expect(quadtree._tilesToRender[1] === east.southwestChild || quadtree._tilesToRender[1] === east.northeastChild).toBe(true); + expect(quadtree._tilesToRender[2] === east.southwestChild || quadtree._tilesToRender[2] === east.northeastChild).toBe(true); expect(quadtree._tilesToRender[3]).toBe(east.southeastChild); expect(quadtree._tilesToRender[4]).toBe(west.northeastChild); - expect(quadtree._tilesToRender[5] == west.northwestChild || quadtree._tilesToRender[5] === west.southeastChild).toBe(true); - expect(quadtree._tilesToRender[6] == west.northwestChild || quadtree._tilesToRender[6] === west.southeastChild).toBe(true); + expect(quadtree._tilesToRender[5] === west.northwestChild || quadtree._tilesToRender[5] === west.southeastChild).toBe(true); + expect(quadtree._tilesToRender[6] === west.northwestChild || quadtree._tilesToRender[6] === west.southeastChild).toBe(true); expect(quadtree._tilesToRender[7]).toBe(west.southwestChild); }); }); From 8a5ffc7249901549e6329950035a67dc4148f12c Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Fri, 11 Nov 2016 11:50:28 +1100 Subject: [PATCH 20/20] Remove QuadtreePrimitive development Sandcastle example. It doesn't work, and 3D Tiles are the true answer anway. --- .../development/QuadtreePrimitive.html | 173 ------------------ 1 file changed, 173 deletions(-) delete mode 100644 Apps/Sandcastle/gallery/development/QuadtreePrimitive.html diff --git a/Apps/Sandcastle/gallery/development/QuadtreePrimitive.html b/Apps/Sandcastle/gallery/development/QuadtreePrimitive.html deleted file mode 100644 index 0f9bc1427620..000000000000 --- a/Apps/Sandcastle/gallery/development/QuadtreePrimitive.html +++ /dev/null @@ -1,173 +0,0 @@ - - - - - - - - - Cesium Demo - - - - - - -
-

Loading...

-
- - -