diff --git a/CHANGES.md b/CHANGES.md index 36aad1f95daa..952a44d2595c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -5,8 +5,10 @@ Change Log * Deprecated * `GoogleEarthImageryProvider` has been deprecated and will be removed in Cesium 1.37, use `GoogleEarthEnterpriseMapsProvider` instead. - * The `throttleRequest` parameter for `TerrainProvider.requestTileGeometry`, `CesiumTerrainProvider.requestTileGeometry`, `VRTheWorldTerrainProvider.requestTileGeometry`, and `EllipsoidTerrainProvider.requestTileGeometry` is deprecated and will be replaced with an optional `Request` object. The `throttleRequests` parameter will be removed in 1.36, instead to throttle requests set the request's `throttle` property to `true`. - * The ability to provide a Promise for the `options.url` parameter of `loadWithXhr` is deprecated. The same applies for the `url` parameter for `loadArrayBuffer`, `loadBlob`, `loadImageViaBlob`, `loadText`, `loadJson`, `loadXML`, `loadImage`, `loadCRN`, `loadKTX`, and `loadCubeMap`. This will be removed in 1.36, instead `url` must be a string. + * The `throttleRequest` parameter for `TerrainProvider.requestTileGeometry`, `CesiumTerrainProvider.requestTileGeometry`, `VRTheWorldTerrainProvider.requestTileGeometry`, and `EllipsoidTerrainProvider.requestTileGeometry` is deprecated and will be replaced with an optional `Request` object. The `throttleRequests` parameter will be removed in 1.37. Instead to throttle requests set the request's `throttle` property to `true`. + * The ability to provide a Promise for the `options.url` parameter of `loadWithXhr` and for the `url` parameter of `loadArrayBuffer`, `loadBlob`, `loadImageViaBlob`, `loadText`, `loadJson`, `loadXML`, `loadImage`, `loadCRN`, `loadKTX`, and `loadCubeMap` is deprecated. This will be removed in 1.37, instead `url` must be a string. + +* Added an `options.request` parameter to `loadWithXhr` and a `request` parameter to `loadArrayBuffer`, `loadBlob`, `loadImageViaBlob`, `loadText`, `loadJson`, `loadJsonp`, `loadXML`, `loadImageFromTypedArray`, `loadImage`, `loadCRN`, and `loadKTX`. ### 1.34 - 2017-06-01 diff --git a/Source/Core/CesiumTerrainProvider.js b/Source/Core/CesiumTerrainProvider.js index 677b47897af2..11fb68504de8 100644 --- a/Source/Core/CesiumTerrainProvider.js +++ b/Source/Core/CesiumTerrainProvider.js @@ -493,7 +493,7 @@ define([ * @param {Number} x The X coordinate of the tile for which to request geometry. * @param {Number} y The Y coordinate of the tile for which to request geometry. * @param {Number} level The level of the tile for which to request geometry. - * @param {Request} [request] The request object. + * @param {Request} [request] The request object. Intended for internal use only. * * @returns {Promise.|undefined} A promise for the requested geometry. If this method * returns undefined instead of a promise, it is an indication that too many requests are already @@ -534,7 +534,7 @@ define([ } if (typeof request === 'boolean') { - deprecationWarning('throttleRequests', 'The throttleRequest parameter for requestTileGeometry was deprecated in Cesium 1.35. It will be removed in 1.36.'); + deprecationWarning('throttleRequests', 'The throttleRequest parameter for requestTileGeometry was deprecated in Cesium 1.35. It will be removed in 1.37.'); request = new Request({ throttle : request, throttleByServer : request, diff --git a/Source/Core/EllipsoidTerrainProvider.js b/Source/Core/EllipsoidTerrainProvider.js index 31189d7ec512..58adf683ca83 100644 --- a/Source/Core/EllipsoidTerrainProvider.js +++ b/Source/Core/EllipsoidTerrainProvider.js @@ -152,7 +152,7 @@ define([ * @param {Number} x The X coordinate of the tile for which to request geometry. * @param {Number} y The Y coordinate of the tile for which to request geometry. * @param {Number} level The level of the tile for which to request geometry. - * @param {Request} [request] The request object. + * @param {Request} [request] The request object. Intended for internal use only. * * @returns {Promise.|undefined} A promise for the requested geometry. If this method * returns undefined instead of a promise, it is an indication that too many requests are already diff --git a/Source/Core/GoogleEarthEnterpriseMetadata.js b/Source/Core/GoogleEarthEnterpriseMetadata.js index be0b51f72dd0..d5ee5dd6185b 100644 --- a/Source/Core/GoogleEarthEnterpriseMetadata.js +++ b/Source/Core/GoogleEarthEnterpriseMetadata.js @@ -294,7 +294,7 @@ define([ * * @param {String} [quadKey=''] The quadkey to retrieve the packet for. * @param {Number} [version=1] The cnode version to be used in the request. - * @param {Request} [request] The request object. + * @param {Request} [request] The request object. Intended for internal use only. * * @private */ @@ -373,7 +373,7 @@ define([ * @param {Number} x The tile X coordinate. * @param {Number} y The tile Y coordinate. * @param {Number} level The tile level. - * @param {Request} [request] The request object. + * @param {Request} [request] The request object. Intended for internal use only. * * @returns {Promise} A promise that resolves to the tile info for the requested quad key * @@ -409,7 +409,7 @@ define([ throttle : request.throttle, throttleByServer : request.throttleByServer, type : request.type, - distance : request.distance + priorityFunction : request.priorityFunction }); return populateSubtree(that, quadKey, subtreeRequest); }); @@ -439,7 +439,7 @@ define([ throttle : request.throttle, throttleByServer : request.throttleByServer, type : request.type, - distance : request.distance + priorityFunction : request.priorityFunction }); return populateSubtree(that, quadKey, subtreeRequest); }) diff --git a/Source/Core/GoogleEarthEnterpriseTerrainProvider.js b/Source/Core/GoogleEarthEnterpriseTerrainProvider.js index 5ed226e6c56a..d5e2928633b5 100644 --- a/Source/Core/GoogleEarthEnterpriseTerrainProvider.js +++ b/Source/Core/GoogleEarthEnterpriseTerrainProvider.js @@ -346,7 +346,7 @@ define([ * @param {Number} x The X coordinate of the tile for which to request geometry. * @param {Number} y The Y coordinate of the tile for which to request geometry. * @param {Number} level The level of the tile for which to request geometry. - * @param {Request} [request] The request object. + * @param {Request} [request] The request object. Intended for internal use only. * @returns {Promise.|undefined} A promise for the requested geometry. If this method * returns undefined instead of a promise, it is an indication that too many requests are already * pending and the request will be retried later. @@ -448,7 +448,7 @@ define([ sharedRequest = terrainRequests[q]; } else { // Create new request for terrain if (typeof request === 'boolean') { - deprecationWarning('throttleRequests', 'The throttleRequest parameter for requestTileGeometry was deprecated in Cesium 1.35. It will be removed in 1.36.'); + deprecationWarning('throttleRequests', 'The throttleRequest parameter for requestTileGeometry was deprecated in Cesium 1.35. It will be removed in 1.37.'); request = new Request({ throttle : request, throttleByServer : request, diff --git a/Source/Core/Heap.js b/Source/Core/Heap.js index a455ce6d3cd2..77da477e03dc 100644 --- a/Source/Core/Heap.js +++ b/Source/Core/Heap.js @@ -18,14 +18,16 @@ define([ * @constructor * @private * - * @param {Function} comparator The comparator to use for the heap. If comparator(a, b) is less than 0, sort a to a lower index than b, otherwise sort to a higher index. + * @param {Object} options Object with the following properties: + * @param {Function} options.comparator The comparator to use for the heap. If comparator(a, b) is less than 0, sort a to a lower index than b, otherwise sort to a higher index. */ - function Heap(comparator) { + function Heap(options) { //>>includeStart('debug', pragmas.debug); - Check.defined('comparator', comparator); + Check.typeOf.object('options', options); + Check.defined('options.comparator', options.comparator); //>>includeEnd('debug'); - this._comparator = comparator; + this._comparator = options.comparator; this._array = []; this._length = 0; this._maximumLength = undefined; @@ -78,6 +80,19 @@ define([ this._array.length = value; } } + }, + + /** + * The comparator to use for the heap. If comparator(a, b) is less than 0, sort a to a lower index than b, otherwise sort to a higher index. + * + * @memberof Heap.prototype + * + * @type (Function} + */ + comparator : { + get : function() { + return this._comparator; + } } }); @@ -156,6 +171,7 @@ define([ var array = this._array; var comparator = this._comparator; + var maximumLength = this._maximumLength; var index = this._length++; if (index < array.length) { @@ -176,9 +192,9 @@ define([ var removedElement; - if (defined(this._maximumLength) && (this._length > this._maximumLength)) { - removedElement = array[this.maximumLength]; - this._length = this._maximumLength; + if (defined(maximumLength) && (this._length > maximumLength)) { + removedElement = array[maximumLength]; + this._length = maximumLength; } return removedElement; diff --git a/Source/Core/Request.js b/Source/Core/Request.js index 7661249d0569..44a263bc7837 100644 --- a/Source/Core/Request.js +++ b/Source/Core/Request.js @@ -14,7 +14,7 @@ define([ 'use strict'; /** - * Stores information for making a request. This should not be constructed directly. Instead, call the appropriate load function, like `loadWithXhr`, `loadImage`, etc. + * Stores information for making a request. In general this does not need to be constructed directly. * * @alias Request * @constructor @@ -22,11 +22,12 @@ define([ * @param {Object} [options] An object with the following properties: * @param {Boolean} [options.url] The url to request. * @param {Function} [options.requestFunction] The actual function that makes the request. The function takes no arguments and returns a promise for the requested data. + * @param {Function} [options.cancelFunction] Function to call when a request is cancelled. The function takes no arguments. + * @param {Function} [options.priorityFunction] Function that is called when the request is updated. The function takes no arguments and returns the updated priority value. + * @param {Number} [options.priority=0.0] The initial priority of the request. * @param {Boolean} [options.throttle=false] Whether to throttle and prioritize the request. If false, the request will be sent immediately. If true, the request will be throttled and sent based on priority. * @param {Boolean} [options.throttleByServer=false] Whether to throttle the request by server. * @param {RequestType} [options.type=RequestType.OTHER] The type of request. - * @param {Number} [options.distance=0.0] The distance from the camera, used to prioritize requests. - * @param {Number} [options.screenSpaceError=0.0] The screen space error, used to prioritize requests. */ function Request(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); @@ -45,11 +46,35 @@ define([ * The actual function that makes the request. The function takes no arguments and returns a promise for the requested data. * * @type {Function} - * - * @private */ this.requestFunction = options.requestFunction; + /** + * Function to call when a request is cancelled. The function takes no arguments. + * + * @type {Function} + */ + this.cancelFunction = options.cancelFunction; + + /** + * Function that is called when the request is updated. The function takes no arguments and returns the updated priority value. + * + * @type {Function} + */ + this.priorityFunction = options.priorityFunction; + + /** + * Priority is a unit-less value where lower values represent higher priority. + * For world-based objects, this is usually the distance from the camera. + * A request that does not have a priority function defaults to a priority of 0. + * + * If priorityFunction is defined, this value is updated every frame with the result of that call. + * + * @type {Number} + * @default 0.0 + */ + this.priority = defaultValue(options.priority, 0.0); + /** * Whether to throttle and prioritize the request. If false, the request will be sent immediately. If true, the * request will be throttled and sent based on priority. @@ -84,52 +109,22 @@ define([ this.type = defaultValue(options.type, RequestType.OTHER); /** - * The distance from the camera, used to prioritize requests. This value may be edited continually to update - * the request's priority. - * - * @type {Number} - * - * @default 0.0 - */ - this.distance = defaultValue(options.distance, 0.0); - - /** - * The screen space error, used to prioritize requests. This value may be edited continually to update - * the request's priority. - * - * @type {Number} - * - * @default 0.0 - */ - this.screenSpaceError = defaultValue(options.screenSpaceError, 0.0); - - /** - * The request server, derived from the url. + * A key used to identify the server that a request is going to. It is derived from the url's authority and scheme. * * @type {String} * * @private */ - this.server = undefined; + this.serverKey = undefined; /** * The current state of the request. * * @type {RequestState} - * - * @private + * @readonly */ this.state = RequestState.UNISSUED; - /** - * Reference to the underlying XMLHttpRequest so that it may be aborted in RequestScheduler. - * - * @type {Object} - * - * @private - */ - this.xhr = undefined; - /** * The requests's deferred promise. * diff --git a/Source/Core/RequestScheduler.js b/Source/Core/RequestScheduler.js index 2c302924d78b..264838a5b826 100644 --- a/Source/Core/RequestScheduler.js +++ b/Source/Core/RequestScheduler.js @@ -24,7 +24,7 @@ define([ 'use strict'; function sortRequests(a, b) { - return a.distance - b.distance; + return a.priority - b.priority; } var statistics = { @@ -37,16 +37,17 @@ define([ }; var priorityHeapLength = 20; - var requestHeap = new Heap(sortRequests); + var requestHeap = new Heap({ + comparator : sortRequests + }); requestHeap.maximumLength = priorityHeapLength; requestHeap.reserve(priorityHeapLength); - var activeRequests = []; + var activeRequests = []; var numberOfActiveRequestsByServer = {}; var pageUri = typeof document !== 'undefined' ? new Uri(document.location.href) : new Uri(); - /** * Tracks the number of active requests and prioritizes incoming requests. * @@ -67,7 +68,7 @@ define([ /** * The maximum number of simultaneous active requests per server. Un-throttled requests do not observe this limit. * @type {Number} - * @default 50 + * @default 6 */ RequestScheduler.maximumRequestsPerServer = 6; @@ -76,7 +77,7 @@ define([ * @type {Boolean} * @default true */ - RequestScheduler.debugThrottle = true; + RequestScheduler.throttleRequests = true; /** * When true, log statistics to the console every frame @@ -128,69 +129,14 @@ define([ } }); - /** - * Get the server name from a given url. - * - * @param {String} url The url. - * @returns {String} The server name. - */ - RequestScheduler.getServer = function(url) { - //>>includeStart('debug', pragmas.debug); - Check.typeOf.string('url', url); - //>>includeEnd('debug'); - - var uri = new Uri(url).resolve(pageUri); - uri.normalize(); - var serverName = uri.authority; - if (!/:/.test(serverName)) { - serverName = serverName + ':' + (uri.scheme === 'https' ? '443' : '80'); - } - - var length = numberOfActiveRequestsByServer[serverName]; - if (!defined(length)) { - numberOfActiveRequestsByServer[serverName] = 0; - } - - return serverName; - }; - - /** - * For testing only. - * - * @private - */ - RequestScheduler.clearForSpecs = function() { - while (requestHeap.length > 0) { - var request = requestHeap.pop(); - cancelRequest(request); + function updatePriority(request) { + if (defined(request.priorityFunction)) { + request.priority = request.priorityFunction(); } - var length = activeRequests.length; - for (var i = 0; i < length; ++i) { - cancelRequest(activeRequests[i]); - } - activeRequests.length = 0; - numberOfActiveRequestsByServer = {}; - - // Clear stats - statistics.numberOfAttemptedRequests = 0; - statistics.numberOfActiveRequests = 0; - statistics.numberOfCancelledRequests = 0; - statistics.numberOfCancelledActiveRequests = 0; - statistics.numberOfFailedRequests = 0; - statistics.numberOfActiveRequestsEver = 0; - }; - - /** - * For testing only. - * - * @private - */ - RequestScheduler.numberOfActiveRequestsByServer = function(serverName) { - return numberOfActiveRequestsByServer[serverName]; - }; + } - function serverHasOpenSlots(server) { - return numberOfActiveRequestsByServer[server] < RequestScheduler.maximumRequestsPerServer; + function serverHasOpenSlots(serverKey) { + return numberOfActiveRequestsByServer[serverKey] < RequestScheduler.maximumRequestsPerServer; } function issueRequest(request) { @@ -204,14 +150,11 @@ define([ function getRequestReceivedFunction(request) { return function(results) { if (request.state === RequestState.CANCELLED) { + // If the data request comes back but the request is cancelled, ignore it. return; } - - if (request.state !== RequestState.IGNORED) { - --statistics.numberOfActiveRequests; - --numberOfActiveRequestsByServer[request.server]; - } - + --statistics.numberOfActiveRequests; + --numberOfActiveRequestsByServer[request.serverKey]; request.state = RequestState.RECEIVED; request.deferred.resolve(results); }; @@ -220,15 +163,12 @@ define([ function getRequestFailedFunction(request) { return function(error) { if (request.state === RequestState.CANCELLED) { + // If the data request comes back but the request is cancelled, ignore it. return; } - - if (request.state !== RequestState.IGNORED) { - ++statistics.numberOfFailedRequests; - --statistics.numberOfActiveRequests; - --numberOfActiveRequestsByServer[request.server]; - } - + ++statistics.numberOfFailedRequests; + --statistics.numberOfActiveRequests; + --numberOfActiveRequestsByServer[request.serverKey]; request.state = RequestState.FAILED; request.deferred.reject(error); }; @@ -240,8 +180,7 @@ define([ activeRequests.push(request); ++statistics.numberOfActiveRequests; ++statistics.numberOfActiveRequestsEver; - ++numberOfActiveRequestsByServer[request.server]; - + ++numberOfActiveRequestsByServer[request.serverKey]; request.requestFunction().then(getRequestReceivedFunction(request)).otherwise(getRequestFailedFunction(request)); return promise; } @@ -250,32 +189,30 @@ define([ var active = request.state === RequestState.ACTIVE; request.state = RequestState.CANCELLED; ++statistics.numberOfCancelledRequests; - request.deferred.reject('Cancelled'); + request.deferred.reject(); if (active) { - // Despite the Request being cancelled, the xhr request is still in flight. - // When it resolves getRequestReceivedFunction or getRequestFailedFunction will ignore it. --statistics.numberOfActiveRequests; - --numberOfActiveRequestsByServer[request.server]; + --numberOfActiveRequestsByServer[request.serverKey]; ++statistics.numberOfCancelledActiveRequests; - if (defined(request.xhr)) { - request.xhr.abort(); - } + } + + if (defined(request.cancelFunction)) { + request.cancelFunction(); } } /** - * Issuers of a request should update properties of requests. At the end of the frame, - * RequestScheduler.update is called to start, cancel, or defer requests. + * Sort requests by priority and start requests. */ RequestScheduler.update = function() { + var i; var request; // Loop over all active requests. Cancelled, failed, or received requests are removed from the array to make room for new requests. - // If an active request is cancelled, its XMLHttpRequest will be aborted. var removeCount = 0; var activeLength = activeRequests.length; - for (var i = 0; i < activeLength; ++i) { + for (i = 0; i < activeLength; ++i) { request = activeRequests[i]; if (request.cancelled) { // Request was explicitly cancelled @@ -293,7 +230,12 @@ define([ } activeRequests.length -= removeCount; - // Resort the heap since priority may have changed. Distance and sse are updated prior to getting here. + // Update priority of issued requests and resort the heap + var issuedRequests = requestHeap.internalArray; + var issuedLength = requestHeap.length; + for (i = 0; i < issuedLength; ++i) { + updatePriority(issuedRequests[i]); + } requestHeap.resort(); // Get the number of open slots and fill with the highest priority requests. @@ -309,7 +251,7 @@ define([ continue; } - if (request.throttleByServer && !serverHasOpenSlots(request.server)) { + if (request.throttleByServer && !serverHasOpenSlots(request.serverKey)) { // Open slots are available, but the request is throttled by its server. Cancel and try again later. cancelRequest(request); continue; @@ -322,6 +264,33 @@ define([ updateStatistics(); }; + /** + * Get the server key from a given url. + * + * @param {String} url The url. + * @returns {String} The server key. + */ + RequestScheduler.getServerKey = function(url) { + //>>includeStart('debug', pragmas.debug); + Check.typeOf.string('url', url); + //>>includeEnd('debug'); + + var uri = new Uri(url).resolve(pageUri); + uri.normalize(); + var serverKey = uri.authority; + if (!/:/.test(serverKey)) { + // If the authority does not contain a port number, add port 443 for https or port 80 for http + serverKey = serverKey + ':' + (uri.scheme === 'https' ? '443' : '80'); + } + + var length = numberOfActiveRequestsByServer[serverKey]; + if (!defined(length)) { + numberOfActiveRequestsByServer[serverKey] = 0; + } + + return serverKey; + }; + /** * Issue a request. If request.throttle is false, the request is sent immediately. Otherwise the request will be * queued and sorted by priority before being sent. @@ -344,11 +313,11 @@ define([ ++statistics.numberOfAttemptedRequests; - if (!defined(request.server)) { - request.server = RequestScheduler.getServer(request.url); + if (!defined(request.serverKey)) { + request.serverKey = RequestScheduler.getServerKey(request.url); } - if (!RequestScheduler.debugThrottle || !request.throttle) { + if (!RequestScheduler.throttleRequests || !request.throttle) { return startRequest(request); } @@ -357,13 +326,14 @@ define([ return undefined; } - if (request.throttleByServer && !serverHasOpenSlots(request.server)) { + if (request.throttleByServer && !serverHasOpenSlots(request.serverKey)) { // Server is saturated. Try again later. return undefined; } // Insert into the priority heap and see if a request was bumped off. If this request is the lowest // priority it will be returned. + updatePriority(request); var removedRequest = requestHeap.insert(request); if (defined(removedRequest)) { @@ -408,8 +378,47 @@ define([ clearStatistics(); } - // For testing - RequestScheduler._requestHeap = requestHeap; + /** + * For testing only. Clears any requests that may not have completed from previous tests. + * + * @private + */ + RequestScheduler.clearForSpecs = function() { + while (requestHeap.length > 0) { + var request = requestHeap.pop(); + cancelRequest(request); + } + var length = activeRequests.length; + for (var i = 0; i < length; ++i) { + cancelRequest(activeRequests[i]); + } + activeRequests.length = 0; + numberOfActiveRequestsByServer = {}; + + // Clear stats + statistics.numberOfAttemptedRequests = 0; + statistics.numberOfActiveRequests = 0; + statistics.numberOfCancelledRequests = 0; + statistics.numberOfCancelledActiveRequests = 0; + statistics.numberOfFailedRequests = 0; + statistics.numberOfActiveRequestsEver = 0; + }; + + /** + * For testing only. + * + * @private + */ + RequestScheduler.numberOfActiveRequestsByServer = function(serverKey) { + return numberOfActiveRequestsByServer[serverKey]; + }; + + /** + * For testing only. + * + * @private + */ + RequestScheduler.requestHeap = requestHeap; return RequestScheduler; }); diff --git a/Source/Core/RequestState.js b/Source/Core/RequestState.js index 7cd28c4e65db..23498ef57fc7 100644 --- a/Source/Core/RequestState.js +++ b/Source/Core/RequestState.js @@ -6,16 +6,58 @@ define([ 'use strict'; /** - * @private + * State of the request. + * + * @exports RequestState */ var RequestState = { - UNISSUED : 0, // Initial unissued state. - ISSUED : 1, // Issued but not yet active. Will become active when open slots are available. - ACTIVE : 2, // Actual http request has been sent. - RECEIVED : 3, // Request completed successfully. - CANCELLED : 4, // Request was cancelled, either explicitly or automatically because of low priority. - FAILED : 5, // Request failed. - IGNORED : 6 // For RequestScheduler.clearForSpecs - lets requests finish but doesn't contribute to statistics. + /** + * Initial unissued state. + * + * @type Number + * @constant + */ + UNISSUED : 0, + + /** + * Issued but not yet active. Will become active when open slots are available. + * + * @type Number + * @constant + */ + ISSUED : 1, + + /** + * Actual http request has been sent. + * + * @type Number + * @constant + */ + ACTIVE : 2, + + /** + * Request completed successfully. + * + * @type Number + * @constant + */ + RECEIVED : 3, + + /** + * Request was cancelled, either explicitly or automatically because of low priority. + * + * @type Number + * @constant + */ + CANCELLED : 4, + + /** + * Request failed. + * + * @type Number + * @constant + */ + FAILED : 5 }; return freezeObject(RequestState); diff --git a/Source/Core/TerrainProvider.js b/Source/Core/TerrainProvider.js index bfba9cf039d2..6bdc2e000016 100644 --- a/Source/Core/TerrainProvider.js +++ b/Source/Core/TerrainProvider.js @@ -198,7 +198,7 @@ define([ * @param {Number} x The X coordinate of the tile for which to request geometry. * @param {Number} y The Y coordinate of the tile for which to request geometry. * @param {Number} level The level of the tile for which to request geometry. - * @param {Request} [request] The request object. + * @param {Request} [request] The request object. Intended for internal use only. * * @returns {Promise.|undefined} A promise for the requested geometry. If this method * returns undefined instead of a promise, it is an indication that too many requests are already diff --git a/Source/Core/VRTheWorldTerrainProvider.js b/Source/Core/VRTheWorldTerrainProvider.js index 79523b4afe62..5339deb9c6c8 100644 --- a/Source/Core/VRTheWorldTerrainProvider.js +++ b/Source/Core/VRTheWorldTerrainProvider.js @@ -260,7 +260,7 @@ define([ * @param {Number} x The X coordinate of the tile for which to request geometry. * @param {Number} y The Y coordinate of the tile for which to request geometry. * @param {Number} level The level of the tile for which to request geometry. - * @param {Request} [request] The request object. + * @param {Request} [request] The request object. Intended for internal use only. * @returns {Promise.|undefined} A promise for the requested geometry. If this method * returns undefined instead of a promise, it is an indication that too many requests are already * pending and the request will be retried later. @@ -281,7 +281,7 @@ define([ } if (typeof request === 'boolean') { - deprecationWarning('throttleRequests', 'The throttleRequest parameter for requestTileGeometry was deprecated in Cesium 1.35. It will be removed in 1.36.'); + deprecationWarning('throttleRequests', 'The throttleRequest parameter for requestTileGeometry was deprecated in Cesium 1.35. It will be removed in 1.37.'); request = new Request({ throttle : request, throttleByServer : request, diff --git a/Source/Core/loadArrayBuffer.js b/Source/Core/loadArrayBuffer.js index d86038eff303..cc56d573ff6c 100644 --- a/Source/Core/loadArrayBuffer.js +++ b/Source/Core/loadArrayBuffer.js @@ -15,7 +15,7 @@ define([ * * @param {String} url The URL of the binary data. * @param {Object} [headers] HTTP headers to send with the requests. - * @param {Request} [request] The request object. + * @param {Request} [request] The request object. Intended for internal use only. * @returns {Promise.|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. * * @example diff --git a/Source/Core/loadBlob.js b/Source/Core/loadBlob.js index aad892afdbd2..0e136d22c37c 100644 --- a/Source/Core/loadBlob.js +++ b/Source/Core/loadBlob.js @@ -15,7 +15,7 @@ define([ * * @param {String} url The URL of the data. * @param {Object} [headers] HTTP headers to send with the requests. - * @param {Request} [request] The request object. + * @param {Request} [request] The request object. Intended for internal use only. * @returns {Promise.|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. * * @example diff --git a/Source/Core/loadCRN.js b/Source/Core/loadCRN.js index f01270bb6ffd..eac7121e1004 100644 --- a/Source/Core/loadCRN.js +++ b/Source/Core/loadCRN.js @@ -28,7 +28,7 @@ define([ * * @param {String|ArrayBuffer} urlOrBuffer The URL of the binary data or an ArrayBuffer. * @param {Object} [headers] HTTP headers to send with the requests. - * @param {Request} [request] The request object. + * @param {Request} [request] The request object. Intended for internal use only. * @returns {Promise.|undefined} A promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. * * @exception {RuntimeError} Unsupported compressed format. diff --git a/Source/Core/loadImage.js b/Source/Core/loadImage.js index b726ed800564..79c0c95673ae 100644 --- a/Source/Core/loadImage.js +++ b/Source/Core/loadImage.js @@ -35,7 +35,7 @@ define([ * @param {Boolean} [allowCrossOrigin=true] Whether to request the image using Cross-Origin * Resource Sharing (CORS). CORS is only actually used if the image URL is actually cross-origin. * Data URIs are never requested using CORS. - * @param {Request} [request] The request object. + * @param {Request} [request] The request object. Intended for internal use only. * @returns {Promise.|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. * * @@ -65,7 +65,7 @@ define([ if (typeof url !== 'string') { // Returning a promise here is okay because it is unlikely that anyone using the deprecated functionality is also // providing a Request object marked as throttled. - deprecationWarning('url promise', 'url as a Promise is deprecated and will be removed in Cesium 1.36'); + deprecationWarning('url promise', 'url as a Promise is deprecated and will be removed in 1.37'); return url.then(function(url) { return makeRequest(url, allowCrossOrigin, request); }); diff --git a/Source/Core/loadImageViaBlob.js b/Source/Core/loadImageViaBlob.js index 7f2e12ecc23f..9d2d32924534 100644 --- a/Source/Core/loadImageViaBlob.js +++ b/Source/Core/loadImageViaBlob.js @@ -28,7 +28,7 @@ define([ * @exports loadImageViaBlob * * @param {String} url The source URL of the image. - * @param {Request} [request] The request object. + * @param {Request} [request] The request object. Intended for internal use only. * @returns {Promise.|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. * * diff --git a/Source/Core/loadJson.js b/Source/Core/loadJson.js index f3215e59448b..1fd1de5133c0 100644 --- a/Source/Core/loadJson.js +++ b/Source/Core/loadJson.js @@ -30,7 +30,7 @@ define([ * @param {Object} [headers] HTTP headers to send with the request. * 'Accept: application/json,*/*;q=0.01' is added to the request headers automatically * if not specified. - * @param {Request} [request] The request object. + * @param {Request} [request] The request object. Intended for internal use only. * @returns {Promise.|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. * * diff --git a/Source/Core/loadJsonp.js b/Source/Core/loadJsonp.js index 5e721769340e..c63f3b58b0b0 100644 --- a/Source/Core/loadJsonp.js +++ b/Source/Core/loadJsonp.js @@ -33,7 +33,7 @@ define([ * @param {Object} [options.parameters] Any extra query parameters to append to the URL. * @param {String} [options.callbackParameterName='callback'] The callback parameter name that the server expects. * @param {Proxy} [options.proxy] A proxy to use for the request. This object is expected to have a getURL function which returns the proxied URL, if needed. - * @param {Request} [request] The request object. + * @param {Request} [request] The request object. Intended for internal use only. * @returns {Promise.|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. * * diff --git a/Source/Core/loadKTX.js b/Source/Core/loadKTX.js index 9be69bb2bb80..cd8600b10d14 100644 --- a/Source/Core/loadKTX.js +++ b/Source/Core/loadKTX.js @@ -39,7 +39,7 @@ define([ * * @param {String|ArrayBuffer} urlOrBuffer The URL of the binary data or an ArrayBuffer. * @param {Object} [headers] HTTP headers to send with the requests. - * @param {Request} [request] The request object. + * @param {Request} [request] The request object. Intended for internal use only. * @returns {Promise.|undefined} A promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. * * @exception {RuntimeError} Invalid KTX file. diff --git a/Source/Core/loadText.js b/Source/Core/loadText.js index a92607405edf..8ca386fe1bfa 100644 --- a/Source/Core/loadText.js +++ b/Source/Core/loadText.js @@ -15,7 +15,7 @@ define([ * * @param {String} url The URL to request. * @param {Object} [headers] HTTP headers to send with the request. - * @param {Request} [request] The request object. + * @param {Request} [request] The request object. Intended for internal use only. * @returns {Promise.|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. * * diff --git a/Source/Core/loadWithXhr.js b/Source/Core/loadWithXhr.js index d2102b50bac5..789b737607ac 100644 --- a/Source/Core/loadWithXhr.js +++ b/Source/Core/loadWithXhr.js @@ -74,7 +74,7 @@ define([ if (typeof url !== 'string') { // Returning a promise here is okay because it is unlikely that anyone using the deprecated functionality is also // providing a Request object marked as throttled. - deprecationWarning('url promise', 'options.url as a Promise is deprecated and will be removed in Cesium 1.36'); + deprecationWarning('url promise', 'options.url as a Promise is deprecated and will be removed in Cesium 1.37'); return url.then(function(url) { return makeRequest(options, url); }); @@ -95,7 +95,12 @@ define([ request.url = url; request.requestFunction = function() { var deferred = when.defer(); - request.xhr = loadWithXhr.load(url, responseType, method, data, headers, deferred, overrideMimeType); + var xhr = loadWithXhr.load(url, responseType, method, data, headers, deferred, overrideMimeType); + if (defined(xhr)) { + request.cancelFunction = function() { + xhr.abort(); + }; + } return deferred.promise; }; diff --git a/Source/Core/loadXML.js b/Source/Core/loadXML.js index e43ef0064367..780ff6ad5eea 100644 --- a/Source/Core/loadXML.js +++ b/Source/Core/loadXML.js @@ -15,7 +15,7 @@ define([ * * @param {String} url The URL to request. * @param {Object} [headers] HTTP headers to send with the request. - * @param {Request} [request] The request object. + * @param {Request} [request] The request object. Intended for internal use only. * @returns {Promise.|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if request.throttle is true and the request does not have high enough priority. * * diff --git a/Source/Scene/ArcGisMapServerImageryProvider.js b/Source/Scene/ArcGisMapServerImageryProvider.js index 0970ce28b2ed..fa53f26b766e 100644 --- a/Source/Scene/ArcGisMapServerImageryProvider.js +++ b/Source/Scene/ArcGisMapServerImageryProvider.js @@ -579,7 +579,7 @@ define([ * @param {Number} x The tile X coordinate. * @param {Number} y The tile Y coordinate. * @param {Number} level The tile level. - * @param {Request} [request] The request object. + * @param {Request} [request] The request object. Intended for internal use only. * @returns {Promise.|undefined} A promise for the image that will resolve when the image is available, or * undefined if there are too many active requests to the server, and the request * should be retried later. The resolved image may be either an diff --git a/Source/Scene/BingMapsImageryProvider.js b/Source/Scene/BingMapsImageryProvider.js index 3dea11dc2da9..fdcf1531b7c6 100644 --- a/Source/Scene/BingMapsImageryProvider.js +++ b/Source/Scene/BingMapsImageryProvider.js @@ -522,7 +522,7 @@ define([ * @param {Number} x The tile X coordinate. * @param {Number} y The tile Y coordinate. * @param {Number} level The tile level. - * @param {Request} [request] The request object. + * @param {Request} [request] The request object. Intended for internal use only. * @returns {Promise.|undefined} A promise for the image that will resolve when the image is available, or * undefined if there are too many active requests to the server, and the request * should be retried later. The resolved image may be either an diff --git a/Source/Scene/GlobeSurfaceTile.js b/Source/Scene/GlobeSurfaceTile.js index 90b0da7a0020..1e27a4df4f80 100644 --- a/Source/Scene/GlobeSurfaceTile.js +++ b/Source/Scene/GlobeSurfaceTile.js @@ -252,6 +252,12 @@ define([ }); } + function createPriorityFunction(surfaceTile, frameState) { + return function() { + return surfaceTile.tileBoundingRegion.distanceToCamera(frameState); + }; + } + GlobeSurfaceTile.processStateMachine = function(tile, frameState, terrainProvider, imageryLayerCollection, vertexArraysToDestroy) { var surfaceTile = tile.data; if (!defined(surfaceTile)) { @@ -261,8 +267,10 @@ define([ surfaceTile.tileBoundingRegion = createTileBoundingRegion(tile); } - // Update distance while the tile loads - tile._distance = surfaceTile.tileBoundingRegion.distanceToCamera(frameState); + if (!defined(tile._priorityFunction)) { + // The priority function is used to prioritize requests among all requested tiles + tile._priorityFunction = createPriorityFunction(surfaceTile, frameState); + } if (tile.state === QuadtreeTileLoadState.START) { prepareNewTile(tile, terrainProvider, imageryLayerCollection); @@ -328,6 +336,7 @@ define([ if (isDoneLoading) { tile.state = QuadtreeTileLoadState.DONE; + tile._priorityFunction = undefined; } } }; @@ -360,7 +369,7 @@ define([ var suspendUpsampling = false; if (defined(loaded)) { - loaded.processLoadStateMachine(frameState, terrainProvider, tile.x, tile.y, tile.level, tile._distance); + loaded.processLoadStateMachine(frameState, terrainProvider, tile.x, tile.y, tile.level, tile._priorityFunction); // Publish the terrain data on the tile as soon as it is available. // We'll potentially need it to upsample child tiles. diff --git a/Source/Scene/GoogleEarthEnterpriseImageryProvider.js b/Source/Scene/GoogleEarthEnterpriseImageryProvider.js index 79c0f4d56df8..3dfb87bbcf20 100644 --- a/Source/Scene/GoogleEarthEnterpriseImageryProvider.js +++ b/Source/Scene/GoogleEarthEnterpriseImageryProvider.js @@ -432,7 +432,7 @@ define([ * @param {Number} x The tile X coordinate. * @param {Number} y The tile Y coordinate. * @param {Number} level The tile level. - * @param {Request} [request] The request object. + * @param {Request} [request] The request object. Intended for internal use only. * @returns {Promise.|undefined} A promise for the image that will resolve when the image is available, or * undefined if there are too many active requests to the server, and the request * should be retried later. The resolved image may be either an @@ -457,7 +457,7 @@ define([ throttle : request.throttle, throttleByServer : request.throttleByServer, type : request.type, - distance : request.distance + priorityFunction : request.priorityFunction }); metadata.populateSubtree(x, y, level, metadataRequest); return undefined; // No metadata so return undefined so we can be loaded later diff --git a/Source/Scene/GoogleEarthEnterpriseMapsProvider.js b/Source/Scene/GoogleEarthEnterpriseMapsProvider.js index ab4adf47350e..4251d3d2d40a 100644 --- a/Source/Scene/GoogleEarthEnterpriseMapsProvider.js +++ b/Source/Scene/GoogleEarthEnterpriseMapsProvider.js @@ -538,7 +538,7 @@ define([ * @param {Number} x The tile X coordinate. * @param {Number} y The tile Y coordinate. * @param {Number} level The tile level. - * @param {Request} [request] The request object. + * @param {Request} [request] The request object. Intended for internal use only. * @returns {Promise.|undefined} A promise for the image that will resolve when the image is available, or * undefined if there are too many active requests to the server, and the request * should be retried later. The resolved image may be either an diff --git a/Source/Scene/GridImageryProvider.js b/Source/Scene/GridImageryProvider.js index b98e628a84ad..7bfa4f16485e 100644 --- a/Source/Scene/GridImageryProvider.js +++ b/Source/Scene/GridImageryProvider.js @@ -321,7 +321,7 @@ define([ * @param {Number} x The tile X coordinate. * @param {Number} y The tile Y coordinate. * @param {Number} level The tile level. - * @param {Request} [request] The request object. + * @param {Request} [request] The request object. Intended for internal use only. * @returns {Promise.|undefined} A promise for the image that will resolve when the image is available, or * undefined if there are too many active requests to the server, and the request * should be retried later. The resolved image may be either an diff --git a/Source/Scene/Imagery.js b/Source/Scene/Imagery.js index d832d9f210ac..07414e2ac3e7 100644 --- a/Source/Scene/Imagery.js +++ b/Source/Scene/Imagery.js @@ -87,15 +87,10 @@ define([ return this.referenceCount; }; - Imagery.prototype.processStateMachine = function(frameState, needGeographicProjection, distance) { + Imagery.prototype.processStateMachine = function(frameState, needGeographicProjection, priorityFunction) { if (this.state === ImageryState.UNLOADED) { this.state = ImageryState.TRANSITIONING; - this.imageryLayer._requestImagery(this, distance); - } - - if (defined(this.request) && this.request.state === RequestState.ISSUED) { - // Update distance while loading to prioritize request - this.request.distance = distance; + this.imageryLayer._requestImagery(this, priorityFunction); } if (this.state === ImageryState.RECEIVED) { diff --git a/Source/Scene/ImageryLayer.js b/Source/Scene/ImageryLayer.js index 038826aaea09..39d7014322fd 100644 --- a/Source/Scene/ImageryLayer.js +++ b/Source/Scene/ImageryLayer.js @@ -662,9 +662,9 @@ define([ * @private * * @param {Imagery} imagery The imagery to request. - * @param {Number} [distance] The distance of the tile from the camera. + * @param {Function} [priorityFunction] The priority function used for sorting the imagery request. */ - ImageryLayer.prototype._requestImagery = function(imagery, distance) { + ImageryLayer.prototype._requestImagery = function(imagery, priorityFunction) { var imageryProvider = this._imageryProvider; var that = this; @@ -710,7 +710,7 @@ define([ throttle : true, throttleByServer : true, type : RequestType.IMAGERY, - distance : distance + priorityFunction : priorityFunction }); imagery.request = request; imagery.state = ImageryState.TRANSITIONING; diff --git a/Source/Scene/ImageryProvider.js b/Source/Scene/ImageryProvider.js index b73b5adfddb0..36c5a2eb573e 100644 --- a/Source/Scene/ImageryProvider.js +++ b/Source/Scene/ImageryProvider.js @@ -266,7 +266,7 @@ define([ * @param {Number} x The tile X coordinate. * @param {Number} y The tile Y coordinate. * @param {Number} level The tile level. - * @param {Request} [request] The request object. + * @param {Request} [request] The request object. Intended for internal use only. * @returns {Promise.|undefined} A promise for the image that will resolve when the image is available, or * undefined if there are too many active requests to the server, and the request * should be retried later. The resolved image may be either an @@ -307,7 +307,7 @@ define([ * * @param {ImageryProvider} imageryProvider The imagery provider for the URL. * @param {String} url The URL of the image. - * @param {Request} [request] The request object. + * @param {Request} [request] The request object. Intended for internal use only. * @returns {Promise.|undefined} A promise for the image that will resolve when the image is available, or * undefined if there are too many active requests to the server, and the request * should be retried later. The resolved image may be either an diff --git a/Source/Scene/MapboxImageryProvider.js b/Source/Scene/MapboxImageryProvider.js index 5c32d2082b6a..ac2949df0c54 100644 --- a/Source/Scene/MapboxImageryProvider.js +++ b/Source/Scene/MapboxImageryProvider.js @@ -317,7 +317,7 @@ define([ * @param {Number} x The tile X coordinate. * @param {Number} y The tile Y coordinate. * @param {Number} level The tile level. - * @param {Request} [request] The request object. + * @param {Request} [request] The request object. Intended for internal use only. * @returns {Promise.|undefined} A promise for the image that will resolve when the image is available, or * undefined if there are too many active requests to the server, and the request * should be retried later. The resolved image may be either an diff --git a/Source/Scene/QuadtreePrimitive.js b/Source/Scene/QuadtreePrimitive.js index c5b736112777..faab9989d1fa 100644 --- a/Source/Scene/QuadtreePrimitive.js +++ b/Source/Scene/QuadtreePrimitive.js @@ -100,7 +100,6 @@ define([ 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; this._loadQueueTimeSlice = 5.0; this._addHeightCallbacks = []; diff --git a/Source/Scene/QuadtreeTile.js b/Source/Scene/QuadtreeTile.js index 5ebea3fe0051..65c45f7274a1 100644 --- a/Source/Scene/QuadtreeTile.js +++ b/Source/Scene/QuadtreeTile.js @@ -67,6 +67,7 @@ define([ // distance - for example, by using the natural ordering of a quadtree. // QuadtreePrimitive gets/sets this private property. this._distance = 0.0; + this._priorityFunction = undefined; this._customData = []; this._frameUpdated = undefined; diff --git a/Source/Scene/SingleTileImageryProvider.js b/Source/Scene/SingleTileImageryProvider.js index ca9486e0f1f4..9e869c6b5b62 100644 --- a/Source/Scene/SingleTileImageryProvider.js +++ b/Source/Scene/SingleTileImageryProvider.js @@ -370,7 +370,7 @@ define([ * @param {Number} x The tile X coordinate. * @param {Number} y The tile Y coordinate. * @param {Number} level The tile level. - * @param {Request} [request] The request object. + * @param {Request} [request] The request object. Intended for internal use only. * @returns {Promise.|undefined} A promise for the image that will resolve when the image is available, or * undefined if there are too many active requests to the server, and the request * should be retried later. The resolved image may be either an diff --git a/Source/Scene/TileCoordinatesImageryProvider.js b/Source/Scene/TileCoordinatesImageryProvider.js index 52e4ebc2b2be..0ecf383b481f 100644 --- a/Source/Scene/TileCoordinatesImageryProvider.js +++ b/Source/Scene/TileCoordinatesImageryProvider.js @@ -240,7 +240,7 @@ define([ * @param {Number} x The tile X coordinate. * @param {Number} y The tile Y coordinate. * @param {Number} level The tile level. - * @param {Request} [request] The request object. + * @param {Request} [request] The request object. Intended for internal use only. * @returns {Promise.|undefined} A promise for the image that will resolve when the image is available, or * undefined if there are too many active requests to the server, and the request * should be retried later. The resolved image may be either an diff --git a/Source/Scene/TileImagery.js b/Source/Scene/TileImagery.js index 282b44cda6a7..d3ffdd839df3 100644 --- a/Source/Scene/TileImagery.js +++ b/Source/Scene/TileImagery.js @@ -50,7 +50,7 @@ define([ var loadingImagery = this.loadingImagery; var imageryLayer = loadingImagery.imageryLayer; - loadingImagery.processStateMachine(frameState, !this.useWebMercatorT, tile._distance); + loadingImagery.processStateMachine(frameState, !this.useWebMercatorT, tile._priorityFunction); if (loadingImagery.state === ImageryState.READY) { if (defined(this.readyImagery)) { @@ -92,7 +92,7 @@ define([ // Push the ancestor's load process along a bit. This is necessary because some ancestor imagery // tiles may not be attached directly to a terrain tile. Such tiles will never load if // we don't do it here. - closestAncestorThatNeedsLoading.processStateMachine(frameState, !this.useWebMercatorT, tile._distance); + closestAncestorThatNeedsLoading.processStateMachine(frameState, !this.useWebMercatorT, tile._priorityFunction); return false; // not done loading } else { // This imagery tile is failed or invalid, and we have the "best available" substitute. diff --git a/Source/Scene/TileTerrain.js b/Source/Scene/TileTerrain.js index 8e1a4b12c0ff..98c315518c93 100644 --- a/Source/Scene/TileTerrain.js +++ b/Source/Scene/TileTerrain.js @@ -93,14 +93,9 @@ define([ tile.data.occludeePointInScaledSpace = Cartesian3.clone(mesh.occludeePointInScaledSpace, surfaceTile.occludeePointInScaledSpace); }; - TileTerrain.prototype.processLoadStateMachine = function(frameState, terrainProvider, x, y, level, distance) { + TileTerrain.prototype.processLoadStateMachine = function(frameState, terrainProvider, x, y, level, priorityFunction) { if (this.state === TerrainState.UNLOADED) { - requestTileGeometry(this, terrainProvider, x, y, level, distance); - } - - if (defined(this.request) && this.request.state === RequestState.ISSUED) { - // Update distance while loading to prioritize request - this.request.distance = distance; + requestTileGeometry(this, terrainProvider, x, y, level, priorityFunction); } if (this.state === TerrainState.RECEIVED) { @@ -112,7 +107,7 @@ define([ } }; - function requestTileGeometry(tileTerrain, terrainProvider, x, y, level, distance) { + function requestTileGeometry(tileTerrain, terrainProvider, x, y, level, priorityFunction) { function success(terrainData) { tileTerrain.data = terrainData; tileTerrain.state = TerrainState.RECEIVED; @@ -149,7 +144,7 @@ define([ throttle : true, throttleByServer : true, type : RequestType.TERRAIN, - distance : distance + priorityFunction : priorityFunction }); tileTerrain.request = request; tileTerrain.data = terrainProvider.requestTileGeometry(x, y, level, request); diff --git a/Source/Scene/UrlTemplateImageryProvider.js b/Source/Scene/UrlTemplateImageryProvider.js index 5ab353eab4ee..6d1edf87cca8 100644 --- a/Source/Scene/UrlTemplateImageryProvider.js +++ b/Source/Scene/UrlTemplateImageryProvider.js @@ -601,7 +601,7 @@ define([ * @param {Number} x The tile X coordinate. * @param {Number} y The tile Y coordinate. * @param {Number} level The tile level. - * @param {Request} [request] The request object. + * @param {Request} [request] The request object. Intended for internal use only. * @returns {Promise.|undefined} A promise for the image that will resolve when the image is available, or * undefined if there are too many active requests to the server, and the request * should be retried later. The resolved image may be either an diff --git a/Source/Scene/WebMapServiceImageryProvider.js b/Source/Scene/WebMapServiceImageryProvider.js index 93f3c83dc431..93e98bc4a010 100644 --- a/Source/Scene/WebMapServiceImageryProvider.js +++ b/Source/Scene/WebMapServiceImageryProvider.js @@ -433,7 +433,7 @@ define([ * @param {Number} x The tile X coordinate. * @param {Number} y The tile Y coordinate. * @param {Number} level The tile level. - * @param {Request} [request] The request object. + * @param {Request} [request] The request object. Intended for internal use only. * @returns {Promise.|undefined} A promise for the image that will resolve when the image is available, or * undefined if there are too many active requests to the server, and the request * should be retried later. The resolved image may be either an diff --git a/Source/Scene/WebMapTileServiceImageryProvider.js b/Source/Scene/WebMapTileServiceImageryProvider.js index 2da7cd3fb36d..21986a61866f 100644 --- a/Source/Scene/WebMapTileServiceImageryProvider.js +++ b/Source/Scene/WebMapTileServiceImageryProvider.js @@ -432,7 +432,7 @@ define([ * @param {Number} x The tile X coordinate. * @param {Number} y The tile Y coordinate. * @param {Number} level The tile level. - * @param {Request} [request] The request object. + * @param {Request} [request] The request object. Intended for internal use only. * @returns {Promise.|undefined} A promise for the image that will resolve when the image is available, or * undefined if there are too many active requests to the server, and the request * should be retried later. The resolved image may be either an diff --git a/Specs/Core/HeapSpec.js b/Specs/Core/HeapSpec.js index a3af65f71848..4d41e7e48fb0 100644 --- a/Specs/Core/HeapSpec.js +++ b/Specs/Core/HeapSpec.js @@ -30,7 +30,9 @@ defineSuite([ } it('maintains heap property on insert', function() { - var heap = new Heap(comparator); + var heap = new Heap({ + comparator : comparator + }); var pass = true; for (var i = 0; i < length; ++i) { heap.insert(Math.random()); @@ -41,7 +43,9 @@ defineSuite([ }); it('maintains heap property on pop', function() { - var heap = new Heap(comparator); + var heap = new Heap({ + comparator : comparator + }); var i; for (i = 0; i < length; ++i) { heap.insert(Math.random()); @@ -55,7 +59,9 @@ defineSuite([ }); it('limited by maximum length', function() { - var heap = new Heap(comparator); + var heap = new Heap({ + comparator : comparator + }); heap.maximumLength = length / 2; var pass = true; for (var i = 0; i < length; ++i) { @@ -69,7 +75,9 @@ defineSuite([ }); it('pops in sorted order', function() { - var heap = new Heap(comparator); + var heap = new Heap({ + comparator : comparator + }); var i; for (i = 0; i < length; ++i) { heap.insert(Math.random()); @@ -85,7 +93,9 @@ defineSuite([ }); it('insert returns the removed element when maximumLength is set', function() { - var heap = new Heap(comparator); + var heap = new Heap({ + comparator : comparator + }); heap.maximumLength = length; var i; @@ -123,7 +133,9 @@ defineSuite([ } var i; - var heap = new Heap(comparator); + var heap = new Heap({ + comparator : comparator + }); for (i = 0; i < length; ++i) { heap.insert({ distance : i / (length - 1), diff --git a/Specs/Core/RequestSchedulerSpec.js b/Specs/Core/RequestSchedulerSpec.js index 63a7e1a90aa7..4dfff78ecff2 100644 --- a/Specs/Core/RequestSchedulerSpec.js +++ b/Specs/Core/RequestSchedulerSpec.js @@ -59,17 +59,17 @@ defineSuite([ it('getServer throws if url is undefined', function() { expect(function() { - RequestScheduler.getServer(); + RequestScheduler.getServerKey(); }).toThrowDeveloperError(); }); it('getServer with https', function() { - var server = RequestScheduler.getServer('https://foo.com/1'); + var server = RequestScheduler.getServerKey('https://foo.com/1'); expect(server).toEqual('foo.com:443'); }); it('getServer with http', function() { - var server = RequestScheduler.getServer('http://foo.com/1'); + var server = RequestScheduler.getServerKey('http://foo.com/1'); expect(server).toEqual('foo.com:80'); }); @@ -151,7 +151,7 @@ defineSuite([ } var url = 'http://foo.com/1'; - var server = RequestScheduler.getServer(url); + var server = RequestScheduler.getServerKey(url); function createRequest() { return new Request({ @@ -217,12 +217,12 @@ defineSuite([ return deferred.promise; } - function createRequest(distance) { + function createRequest(priority) { var request = new Request({ url : 'http://foo.com/1', requestFunction : requestFunction, throttle : true, - distance : distance + priority : priority }); requests.push(request); return request; @@ -271,11 +271,11 @@ defineSuite([ expect(promise).toBeDefined(); if (dataOrBlobUri) { - expect(request.server).toBeUndefined(); + expect(request.serverKey).toBeUndefined(); expect(statistics.numberOfActiveRequests).toBe(0); } else { expect(statistics.numberOfActiveRequests).toBe(1); - expect(RequestScheduler.numberOfActiveRequestsByServer(request.server)).toBe(1); + expect(RequestScheduler.numberOfActiveRequestsByServer(request.serverKey)).toBe(1); } deferreds[0].resolve(); @@ -284,7 +284,7 @@ defineSuite([ expect(request.state).toBe(RequestState.RECEIVED); expect(statistics.numberOfActiveRequests).toBe(0); if (!dataOrBlobUri) { - expect(RequestScheduler.numberOfActiveRequestsByServer(request.server)).toBe(0); + expect(RequestScheduler.numberOfActiveRequestsByServer(request.serverKey)).toBe(0); } }); } @@ -362,28 +362,23 @@ defineSuite([ return promise.then(function() { fail('should not be called'); }).otherwise(function(error) { - expect(error).toBe('Cancelled'); + expect(request.state).toBe(RequestState.CANCELLED); }); }); it('cancels an active request', function() { var statistics = RequestScheduler.statistics; - var aborted = true; - var mockXhr = { - abort : function() { - aborted = true; - } - }; + var cancelFunction = jasmine.createSpy('cancelFunction'); function requestFunction() { - request.xhr = mockXhr; return when.defer().promise; } var request = new Request({ throttle : true, url : 'https://foo.com/1', - requestFunction : requestFunction + requestFunction : requestFunction, + cancelFunction : cancelFunction }); var promise = RequestScheduler.request(request); @@ -396,13 +391,13 @@ defineSuite([ expect(request.state).toBe(RequestState.CANCELLED); expect(statistics.numberOfCancelledRequests).toBe(1); expect(statistics.numberOfCancelledActiveRequests).toBe(1); - expect(RequestScheduler.numberOfActiveRequestsByServer(request.server)).toBe(0); - expect(aborted).toBe(true); + expect(RequestScheduler.numberOfActiveRequestsByServer(request.serverKey)).toBe(0); + expect(cancelFunction).toHaveBeenCalled(); return promise.then(function() { fail('should not be called'); }).otherwise(function(error) { - expect(error).toBe('Cancelled'); + expect(request.state).toBe(RequestState.CANCELLED); }); }); @@ -436,47 +431,58 @@ defineSuite([ }); }); - it('prioritizes requests by distance', function() { - var currentDistance = 0.0; + it('prioritizes requests', function() { + var currentPriority = 0.0; - function getRequestFunction(distance) { + function getRequestFunction(priority) { return function() { - expect(distance).toBeGreaterThan(currentDistance); - currentDistance = distance; + expect(priority).toBeGreaterThan(currentPriority); + currentPriority = priority; return when.resolve(); }; } - function createRequest(distance) { + function createRequest(priority) { return new Request({ throttle : true, url : 'https://foo.com/1', - requestFunction : getRequestFunction(distance), - distance : distance + requestFunction : getRequestFunction(priority), + priority : priority }); } var length = RequestScheduler.priorityHeapLength; for (var i = 0; i < length; ++i) { - var distance = Math.random(); - RequestScheduler.request(createRequest(distance)); + var priority = Math.random(); + RequestScheduler.request(createRequest(priority)); } RequestScheduler.update(); - expect(currentDistance).toBeGreaterThan(0.0); // Ensures that the expect in getRequestFunction is actually called + expect(currentPriority).toBeGreaterThan(0.0); // Ensures that the expect in getRequestFunction is actually called }); it('updates priority', function() { + var invertPriority = false; + + function getPriorityFunction(priority) { + return function() { + if (invertPriority) { + return 1.0 - priority; + } + return priority; + }; + } + function requestFunction() { return when.resolve(); } - function createRequest(distance) { + function createRequest(priority) { return new Request({ throttle : true, url : 'https://foo.com/1', requestFunction : requestFunction, - distance : distance + priorityFunction : getPriorityFunction(priority) }); } @@ -484,16 +490,17 @@ defineSuite([ var request; var length = RequestScheduler.priorityHeapLength; for (i = 0; i < length; ++i) { - var distance = i / (length - 1); - request = createRequest(distance); + var priority = i / (length - 1); + request = createRequest(priority); request.testId = i; RequestScheduler.request(request); } + // Update priorities while not letting any requests go through RequestScheduler.maximumRequests = 0; RequestScheduler.update(); - var requestHeap = RequestScheduler._requestHeap; + var requestHeap = RequestScheduler.requestHeap; var requests = []; var currentTestId = 0; while (requestHeap.length > 0) { @@ -507,11 +514,9 @@ defineSuite([ requestHeap.insert(requests[i]); } - for (i = 0; i < length; ++i) { - requests[i].distance = 1.0 - requests[i].distance; // Invert priority - } - + invertPriority = true; RequestScheduler.update(); + while (requestHeap.length > 0) { request = requestHeap.pop(); expect(request.testId).toBeLessThanOrEqualTo(currentTestId); @@ -524,12 +529,12 @@ defineSuite([ return when.resolve(); } - function createRequest(distance) { + function createRequest(priority) { return new Request({ throttle : true, url : 'https://foo.com/1', requestFunction : requestFunction, - distance : distance + priority : priority }); } @@ -625,14 +630,14 @@ defineSuite([ } }); - it('debugThrottle', function() { + it('throttleRequests', function() { RequestScheduler.maximumRequests = 0; function requestFunction() { return when.resolve(); } - RequestScheduler.debugThrottle = true; + RequestScheduler.throttleRequests = true; var request = new Request({ throttle : true, url : 'https://foo.com/1', @@ -641,7 +646,7 @@ defineSuite([ var promise = RequestScheduler.request(request); expect(promise).toBeUndefined(); - RequestScheduler.debugThrottle = false; + RequestScheduler.throttleRequests = false; request = new Request({ throttle : true, url : 'https://foo.com/1', @@ -650,7 +655,7 @@ defineSuite([ promise = RequestScheduler.request(request); expect(promise).toBeDefined(); - RequestScheduler.debugThrottle = true; + RequestScheduler.throttleRequests = true; }); it('debugShowStatistics', function() { @@ -693,5 +698,7 @@ defineSuite([ for (var i = 0; i < length; ++i) { deferreds[i].resolve(); } + + RequestScheduler.debugShowStatistics = false; }); });