From 02ba0eb14bc6398d282e4d3870c3e8819ee792bd Mon Sep 17 00:00:00 2001 From: Sean Lilley Date: Tue, 13 Jun 2017 15:10:17 -0400 Subject: [PATCH] Reintegrate request scheduler --- Source/Scene/Cesium3DTile.js | 47 +++++++++------- Source/Scene/Cesium3DTileset.js | 58 +++++--------------- Source/Scene/Cesium3DTilesetTraversal.js | 68 ++++++++++++++---------- 3 files changed, 81 insertions(+), 92 deletions(-) diff --git a/Source/Scene/Cesium3DTile.js b/Source/Scene/Cesium3DTile.js index 733ecdba021..151c782661c 100644 --- a/Source/Scene/Cesium3DTile.js +++ b/Source/Scene/Cesium3DTile.js @@ -17,6 +17,7 @@ define([ '../Core/Rectangle', '../Core/Request', '../Core/RequestScheduler', + '../Core/RequestState', '../Core/RequestType', '../Core/RuntimeError', '../ThirdParty/when', @@ -48,6 +49,7 @@ define([ Rectangle, Request, RequestScheduler, + RequestState, RequestType, RuntimeError, when, @@ -173,13 +175,13 @@ define([ var hasEmptyContent; var contentState; var contentUrl; - var requestServer; + var serverKey; if (defined(contentHeader)) { hasEmptyContent = false; contentState = Cesium3DTileContentState.UNLOADED; contentUrl = joinUrls(basePath, contentHeader.url); - requestServer = RequestScheduler.getRequestServer(contentUrl); + serverKey = RequestScheduler.getServerKey(contentUrl); } else { content = new Empty3DTileContent(tileset, this); hasEmptyContent = true; @@ -193,7 +195,7 @@ define([ this._contentReadyPromise = undefined; this._expiredContent = undefined; - this._requestServer = requestServer; + this._serverKey = serverKey; /** * When true, the tile has no content. @@ -300,7 +302,6 @@ define([ this._screenSpaceError = 0; this._screenSpaceErrorComputedFrame = -1; this._finalResolution = true; - this._requestHeap = undefined; this._depth = 0; this._centerZDepth = 0; this._stackLength = 0; @@ -415,9 +416,9 @@ define([ * @readonly * @private */ - requestServer : { + serverKey : { get : function() { - return this._requestServer; + return this._serverKey; } }, @@ -584,6 +585,12 @@ define([ }; } + function createPriorityFunction(tile) { + return function() { + return tile._distanceToCamera; + }; + } + /** * Requests the tile's content. *

@@ -599,10 +606,6 @@ define([ return false; } - if (!this._requestServer.hasAvailableRequests()) { - return false; - } - var url = this._contentUrl; var expired = this.contentExpired; if (expired) { @@ -611,19 +614,20 @@ define([ url = joinUrls(url, timestampQuery, false); } - var distance = this._distanceToCamera; - var promise = RequestScheduler.schedule(new Request({ - url : url, - server : this._requestServer, - requestFunction : loadArrayBuffer, + var request = new Request({ + throttle : true, + throttleByServer : true, type : RequestType.TILES3D, - distance : distance - })); + priorityFunction : createPriorityFunction(this) + }); + + var promise = loadArrayBuffer(url, undefined, request); if (!defined(promise)) { return false; } + var contentState = this._contentState; this._contentState = Cesium3DTileContentState.LOADING; this._contentReadyToProcessPromise = when.defer(); this._contentReadyPromise = when.defer(); @@ -672,7 +676,14 @@ define([ that._contentState = Cesium3DTileContentState.READY; that._contentReadyPromise.resolve(content); }); - }).otherwise(contentFailedFunction); + }).otherwise(function(error) { + if (request.state === RequestState.CANCELLED) { + // Cancelled due to low priority - try again later. + that._contentState = contentState; + return; + } + contentFailedFunction(error); + }); return true; }; diff --git a/Source/Scene/Cesium3DTileset.js b/Source/Scene/Cesium3DTileset.js index f746fbb81a7..aac8848fd80 100644 --- a/Source/Scene/Cesium3DTileset.js +++ b/Source/Scene/Cesium3DTileset.js @@ -21,8 +21,6 @@ define([ '../Core/ManagedArray', '../Core/Math', '../Core/Matrix4', - '../Core/RequestScheduler', - '../Core/RequestType', '../Core/RuntimeError', '../Renderer/ClearCommand', '../Renderer/Pass', @@ -63,8 +61,6 @@ define([ ManagedArray, CesiumMath, Matrix4, - RequestScheduler, - RequestType, RuntimeError, ClearCommand, Pass, @@ -185,6 +181,7 @@ define([ this._gltfUpAxis = undefined; this._processingQueue = []; this._selectedTiles = []; + this._requestedTiles = []; this._desiredTiles = new ManagedArray(); this._selectedTilesToStyle = []; this._loadTimestamp = undefined; @@ -200,7 +197,6 @@ define([ this._cullWithChildrenBounds = defaultValue(options.cullWithChildrenBounds, true); - this._requestHeaps = {}; this._hasMixedContent = false; this._baseTraversal = new Cesium3DTilesetTraversal.BaseTraversal(); @@ -724,7 +720,7 @@ define([ var that = this; // We don't know the distance of the tileset until tileset.json is loaded, so use the default distance for now - RequestScheduler.request(tilesetUrl, loadJson, undefined, RequestType.TILES3D).then(function(tilesetJson) { + loadJson(tilesetUrl).then(function(tilesetJson) { that._root = that.loadTileset(tilesetUrl, tilesetJson); var gltfUpAxis = defined(tilesetJson.asset.gltfUpAxis) ? Axis.fromName(tilesetJson.asset.gltfUpAxis) : Axis.Y; that._asset = tilesetJson.asset; @@ -1199,23 +1195,6 @@ define([ if (this._cullWithChildrenBounds) { Cesium3DTileOptimizations.checkChildrenWithinParent(tile3D); } - - // Create a load heap, one for each unique server. We can only make limited requests to a given - // server so it is unnecessary to keep a queue of all tiles needed to be loaded. - // Instead of creating a list of all tiles to load and then sorting it entirely to find the best ones, - // we keep just a heap so we have the best `maximumRequestsPerServer` to load. The order of these does - // not matter much as we will try to load them all. - // The heap approach is a O(n log k) to find the best tiles for loading. - var requestServer = tile3D.requestServer; - if (defined(requestServer)) { - if (!defined(this._requestHeaps[requestServer])) { - var heap = new Heap(sortForLoad); - this._requestHeaps[requestServer] = heap; - heap.maximumSize = RequestScheduler.maximumRequestsPerServer; - heap.reserve(heap.maximumSize); - } - tile3D._requestHeap = this._requestHeaps[requestServer]; - } } return rootTile; @@ -1309,20 +1288,10 @@ define([ (tile._depth > ancestor._depth + skipLevels); } - function sortForLoad(a, b) { - var distanceDifference = a._distanceToCamera - b._distanceToCamera; - if (a.refine === Cesium3DTileRefine.ADD || b.refine === Cesium3DTileRefine.ADD) { - return distanceDifference; - } - - var screenSpaceErrorDifference = b._screenSpaceError - a._screenSpaceError; - return screenSpaceErrorDifference === 0 ? distanceDifference : screenSpaceErrorDifference; - } - /////////////////////////////////////////////////////////////////////////// - function requestContent(tileset, tile, outOfCore) { - if (!outOfCore || tile.hasEmptyContent) { + function requestContent(tileset, tile) { + if (tile.hasEmptyContent) { return; } @@ -1351,15 +1320,14 @@ define([ tile.contentReadyPromise.then(removeFunction).otherwise(removeFunction); } - function requestTiles(tileset, requestHeaps, outOfCore) { - for (var name in requestHeaps) { - if (requestHeaps.hasOwnProperty(name)) { - var heap = requestHeaps[name]; - var tile; - while (defined(tile = heap.pop())) { - requestContent(tileset, tile, outOfCore); - } - } + function requestTiles(tileset, outOfCore) { + if (!outOfCore) { + return; + } + var requestedTiles = tileset._requestedTiles; + var length = requestedTiles.length; + for (var i = 0; i < length; ++i) { + requestContent(tileset, requestedTiles[i]); } } @@ -1754,7 +1722,7 @@ define([ } Cesium3DTilesetTraversal.selectTiles(this, frameState, outOfCore); - requestTiles(this, this._requestHeaps, outOfCore); + requestTiles(this, outOfCore); updateTiles(this, frameState); if (outOfCore) { diff --git a/Source/Scene/Cesium3DTilesetTraversal.js b/Source/Scene/Cesium3DTilesetTraversal.js index 711be022722..12c5e736463 100644 --- a/Source/Scene/Cesium3DTilesetTraversal.js +++ b/Source/Scene/Cesium3DTilesetTraversal.js @@ -37,6 +37,7 @@ define([ tileset._desiredTiles.length = 0; tileset._selectedTiles.length = 0; + tileset._requestedTiles.length = 0; tileset._selectedTilesToStyle.length = 0; tileset._hasMixedContent = false; @@ -65,7 +66,7 @@ define([ return; } - loadTile(root, frameState); + loadTile(tileset, root, frameState); if (!tileset.skipLevelOfDetail) { // just execute base traversal and add tiles to _desiredTiles @@ -302,30 +303,36 @@ define([ }; BaseTraversal.prototype.getChildren = function(tile) { - if (baseUpdateAndCheckChildren(this.tileset, tile, this.baseScreenSpaceError, this.frameState)) { - var children = tile.children; - var childrenLength = children.length; - var allReady = true; - var replacementWithContent = tile.refine === Cesium3DTileRefine.REPLACE && tile.hasRenderableContent; - for (var i = 0; i < childrenLength; ++i) { - var child = children[i]; - loadTile(child, this.frameState); - touch(this.tileset, child, this.outOfCore); - - // content cannot be replaced until all of the nearest descendants with content are all loaded - if (replacementWithContent) { - if (!child.hasEmptyContent) { - allReady = allReady && child.contentAvailable; - } else { - allReady = allReady && this.internalDFS.execute(child); - } + var tileset = this.tileset; + var outOfCore = this.outOfCore; + var frameState = this.frameState; + if (!baseUpdateAndCheckChildren(tileset, tile, this.baseScreenSpaceError, frameState)) { + return emptyArray; + } + + var children = tile.children; + var childrenLength = children.length; + var allReady = true; + var replacementWithContent = tile.refine === Cesium3DTileRefine.REPLACE && tile.hasRenderableContent; + for (var i = 0; i < childrenLength; ++i) { + var child = children[i]; + loadTile(tileset, child, frameState); + touch(tileset, child, outOfCore); + + // content cannot be replaced until all of the nearest descendants with content are all loaded + if (replacementWithContent) { + if (!child.hasEmptyContent) { + allReady = allReady && child.contentAvailable; + } else { + allReady = allReady && this.internalDFS.execute(child); } } + } - if (allReady) { - return children; - } + if (allReady) { + return children; } + return emptyArray; }; @@ -397,7 +404,11 @@ define([ }; InternalBaseTraversal.prototype.getChildren = function(tile) { - if (!baseUpdateAndCheckChildren(this.tileset, tile, this.baseScreenSpaceError, this.frameState)) { + var tileset = this.tileset; + var frameState = this.frameState; + var outOfCore = this.outOfCore; + + if (!baseUpdateAndCheckChildren(tileset, tile, this.baseScreenSpaceError, frameState)) { return emptyArray; } @@ -405,8 +416,8 @@ define([ var childrenLength = children.length; for (var i = 0; i < childrenLength; ++i) { var child = children[i]; - loadTile(child, this.frameState); - touch(this.tileset, child, this.outOfCore); + loadTile(tileset, child, frameState); + touch(tileset, child, outOfCore); if (!tile.contentAvailable) { this.allLoaded = false; } @@ -563,11 +574,11 @@ define([ var tiles = parent.children; var length = tiles.length; for (var i = 0; i < length; ++i) { - loadTile(tiles[i], this.frameState); + loadTile(this.tileset, tiles[i], this.frameState); touch(this.tileset, tiles[i], this.outOfCore); } } else { - loadTile(tile, this.frameState); + loadTile(this.tileset, tile, this.frameState); touch(this.tileset, tile, this.outOfCore); } } @@ -630,11 +641,10 @@ define([ } } - function loadTile(tile, frameState) { + function loadTile(tileset, tile, frameState) { if ((tile.contentUnloaded || tile.contentExpired) && tile._requestedFrame !== frameState.frameNumber) { tile._requestedFrame = frameState.frameNumber; - computeSSE(tile, frameState); - tile._requestHeap.insert(tile); + tileset._requestedTiles.push(tile); } }