Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Request Scheduler Prioritization #3397

Merged
merged 13 commits into from
Jan 25, 2016
2 changes: 2 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ Change Log

* Breaking changes
* Removed `OpenStreetMapImageryProvider`. Use `createOpenStreetMapImageryProvider` instead.
* Deprecated
* Deprecated `throttleRequests` flag in `TerrainProvider`. It is replaced by an optional `Request` object that stores information used to prioritize requests.
Copy link
Contributor

Choose a reason for hiding this comment

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

What version will it be removed in? 1.19 is find IMO.

* Reduced the amount of CPU memory used by terrain by ~25% in Chrome.
* Fixed a picking problem ([#3386](https://github.com/AnalyticalGraphicsInc/cesium/issues/3386)) that sometimes prevented objects being selected.
* Added `Scene.useDepthPicking` to enable or disable picking using the depth buffer. [#3390](https://github.com/AnalyticalGraphicsInc/cesium/pull/3390)
Expand Down
22 changes: 20 additions & 2 deletions Source/Core/ArcGisImageServerTerrainProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ define([
'./HeightmapTerrainData',
'./loadImage',
'./Math',
'./Request',
'./RequestScheduler',
'./RequestType',
'./TerrainProvider'
], function(
when,
Expand All @@ -29,7 +31,9 @@ define([
HeightmapTerrainData,
loadImage,
CesiumMath,
Request,
RequestScheduler,
RequestType,
TerrainProvider) {
"use strict";

Expand Down Expand Up @@ -200,11 +204,13 @@ 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.
*
* @returns {Promise.<TerrainData>|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.
*/
ArcGisImageServerTerrainProvider.prototype.requestTileGeometry = function(x, y, level) {
ArcGisImageServerTerrainProvider.prototype.requestTileGeometry = function(x, y, level, request) {
var rectangle = this._tilingScheme.tileXYToRectangle(x, y, level);

// Each pixel in the heightmap represents the height at the center of that
Expand All @@ -231,7 +237,19 @@ define([
url = proxy.getURL(url);
}

var promise = RequestScheduler.throttleRequest(url, loadImage);
// TODO : request used to be a boolean called throttleRequest. Continue to handle that case until it is deprecated.
Copy link
Contributor

Choose a reason for hiding this comment

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

Remove this TODO and friends, and submit an issue for the deprecation.

if (!defined(request) || (request === false)) {
// If a request object isn't provided, perform an immediate request
request = new Request({
defer : true
});
}

request.url = url;
request.requestFunction = loadImage;
request.type = RequestType.TERRAIN;

var promise = RequestScheduler.schedule(request);
if (!defined(promise)) {
return undefined;
}
Expand Down
39 changes: 24 additions & 15 deletions Source/Core/CesiumTerrainProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ define([
'./Matrix3',
'./OrientedBoundingBox',
'./QuantizedMeshTerrainData',
'./Request',
'./RequestScheduler',
'./RequestType',
'./RuntimeError',
'./TerrainProvider',
'./TileProviderError'
Expand All @@ -45,7 +47,9 @@ define([
Matrix3,
OrientedBoundingBox,
QuantizedMeshTerrainData,
Request,
RequestScheduler,
RequestType,
RuntimeError,
TerrainProvider,
TileProviderError) {
Expand Down Expand Up @@ -246,7 +250,7 @@ define([
}

function requestMetadata() {
var metadata = loadJson(metadataUrl);
var metadata = RequestScheduler.request(metadataUrl, loadJson);
Copy link
Contributor

Choose a reason for hiding this comment

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

Looking at this now, I suspect this is a better approach than doing this implicitly/explicitly in the utility functions.

It's explicit to the user, should extend well to new request functions, and I imagine is what a typical game engine's "resource manager" looks like.

when(metadata, metadataSuccess, metadataFailure);
}

Expand Down Expand Up @@ -474,17 +478,16 @@ 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 {Boolean} [throttleRequests=true] True if the number of simultaneous requests should be limited,
* or false if the request should be initiated regardless of the number of requests
* already in progress.
* @param {Request} [request] The request object.
*
* @returns {Promise.<TerrainData>|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.
*
* @exception {DeveloperError} This function must not be called before {@link CesiumTerrainProvider#ready}
* returns true.
*/
CesiumTerrainProvider.prototype.requestTileGeometry = function(x, y, level, throttleRequests) {
CesiumTerrainProvider.prototype.requestTileGeometry = function(x, y, level, request) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This needs some backwards compatibility code for when "request" is still a boolean. We'll also deprecate this - over one release is fine.

//>>includeStart('debug', pragmas.debug)
if (!this._ready) {
throw new DeveloperError('requestTileGeometry must not be called before the terrain provider is ready.');
Expand All @@ -507,8 +510,6 @@ define([
url = proxy.getURL(url);
}

var promise;

var extensionList = [];
if (this._requestVertexNormals && this._hasVertexNormals) {
extensionList.push(this._littleEndianExtensionSize ? "octvertexnormals" : "vertexnormals");
Expand All @@ -520,14 +521,22 @@ define([
function tileLoader(tileUrl) {
return loadArrayBuffer(tileUrl, getRequestHeader(extensionList));
}
throttleRequests = defaultValue(throttleRequests, true);
if (throttleRequests) {
promise = RequestScheduler.throttleRequest(url, tileLoader);
if (!defined(promise)) {
return undefined;
}
} else {
promise = tileLoader(url);

// TODO : request used to be a boolean called throttleRequest. Continue to handle that case until it is deprecated.
if (!defined(request) || (request === false)) {
// If a request object isn't provided, perform an immediate request
request = new Request({
defer : true
});
}

request.url = url;
request.requestFunction = tileLoader;
request.type = RequestType.TERRAIN;

var promise = RequestScheduler.schedule(request);
if (!defined(promise)) {
return undefined;
}

var that = this;
Expand Down
4 changes: 3 additions & 1 deletion Source/Core/EarthOrientationParameters.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ define([
'./JulianDate',
'./LeapSecond',
'./loadJson',
'./RequestScheduler',
'./RuntimeError',
'./TimeConstants',
'./TimeStandard'
Expand All @@ -22,6 +23,7 @@ define([
JulianDate,
LeapSecond,
loadJson,
RequestScheduler,
RuntimeError,
TimeConstants,
TimeStandard) {
Expand Down Expand Up @@ -96,7 +98,7 @@ define([
} else if (defined(options.url)) {
// Download EOP data.
var that = this;
this._downloadPromise = when(loadJson(options.url), function(eopData) {
this._downloadPromise = when(RequestScheduler.request(options.url, loadJson), function(eopData) {
onDataReady(that, eopData);
}, function() {
that._dataError = 'An error occurred while retrieving the EOP data from the URL ' + options.url + '.';
Expand Down
7 changes: 3 additions & 4 deletions Source/Core/EllipsoidTerrainProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -160,14 +160,13 @@ 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 {Boolean} [throttleRequests=true] True if the number of simultaneous requests should be limited,
* or false if the request should be initiated regardless of the number of requests
* already in progress.
* @param {Request} [request] The request object.
*
* @returns {Promise.<TerrainData>|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.
*/
EllipsoidTerrainProvider.prototype.requestTileGeometry = function(x, y, level, throttleRequests) {
EllipsoidTerrainProvider.prototype.requestTileGeometry = function(x, y, level, request) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Same deprecation comment throughout.

return this._terrainData;
};

Expand Down
4 changes: 3 additions & 1 deletion Source/Core/Iau2006XysData.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ define([
'./Iau2006XysSample',
'./JulianDate',
'./loadJson',
'./RequestScheduler',
'./TimeStandard'
], function(
when,
Expand All @@ -16,6 +17,7 @@ define([
Iau2006XysSample,
JulianDate,
loadJson,
RequestScheduler,
TimeStandard) {
"use strict";

Expand Down Expand Up @@ -244,7 +246,7 @@ define([
chunkUrl = buildModuleUrl('Assets/IAU2006_XYS/IAU2006_XYS_' + chunkIndex + '.json');
}

when(loadJson(chunkUrl), function(chunk) {
when(RequestScheduler.request(chunkUrl, loadJson), function(chunk) {
xysData._chunkDownloadsInProgress[chunkIndex] = false;

var samples = xysData._samples;
Expand Down
80 changes: 80 additions & 0 deletions Source/Core/Request.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*global define*/
define([
'./defaultValue'
], function(
defaultValue) {
"use strict";

/**
* Stores information for making a request using {@link RequestScheduler}.
*
* @exports Request
*
* @private
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we plan to keep Request and RequestScheduler private when they go into master?

Although their most important use for most apps will be deep inside of Cesium for things that stream, I think they will be useful as a public API for any app that wants to play nice with requests.

Perhaps the best approach is to keep them private when this is first merged into master, and then make them public when 3d-tiles is merged in case we want to change the interface while working on 3D Tiles. If you agree, update the 3D Tiles roadmap to add a note to make these public.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I agree with keeping it private for now.

*/
function Request(options) {
options = defaultValue(options, defaultValue.EMPTY_OBJECT);

/**
* The URL to request.
*
* @private
Copy link
Contributor

Choose a reason for hiding this comment

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

Since the class is private, each member does not need to be private. I would just mark ones private if they are only intended to be used by RequestScheduler (looks like startPromise and server).

*/
this.url = options.url;

/**
* Extra parameters to send with the request. For example, HTTP headers or jsonp parameters.
*
* @private
*/
this.parameters = options.parameters;

/**
* The actual function that makes the request.
*
* @private
*/
this.requestFunction = options.requestFunction;

/**
* Type of request. Used for more fine-grained priority sorting.
*
* @private
*/
this.type = options.type;

/**
* Specifies that the request should be deferred until an open slot is available.
* A deferred request will always return a promise, which is suitable for data
* sources and utility functions.
*
* @private
*/
this.defer = defaultValue(options.defer, false);

/**
* The distance from the camera, used to prioritize requests.
*
* @private
*/
this.distance = defaultValue(options.distance, 0.0);

// Helper members for RequestScheduler
Copy link
Contributor

Choose a reason for hiding this comment

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

Mark these as @private since they are being used like C#'s internal.


/**
* A promise for when a deferred request can start.
*
* @private
*/
this.startPromise = undefined;

/**
* Reference to a {@link RequestScheduler~RequestServer}.
*
* @private
*/
this.server = options.server;
Copy link
Contributor

Choose a reason for hiding this comment

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

This would have more precise semantics if it was named requestServer as it is elsewhere.

Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe not. Up to you.

}

return Request;
});
Loading