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

Speed up Draco loading #6420

Merged
merged 12 commits into from Apr 24, 2018
3 changes: 3 additions & 0 deletions CHANGES.md
Expand Up @@ -10,6 +10,8 @@ Change Log
* Added `Math.log2` to compute the base 2 logarithm of a number.
* Added 'PeliasGeocoderService', which provides geocoding via a [Pelias](https://pelias.io) server.
* Added `GeocodeType` enum and use it as an optional parameter to all `GeocoderService` instances to differentiate between autocomplete and search requests.
* Added `initWebAssemblyModule` function to `TaskProcessor` to load a Web Assembly module in a web worker. [#6420](https://github.com/AnalyticalGraphicsInc/cesium/pull/6420)
* Added `supportsWebAssembly` function to `FeatureDetection` to check if a browser supports loading Web Assembly modules. [#6420](https://github.com/AnalyticalGraphicsInc/cesium/pull/6420)
* Improved `MapboxImageryProvider` performance by 300% via `tiles.mapbox.com` subdomain switching. [#6426](https://github.com/AnalyticalGraphicsInc/cesium/issues/6426)
* Added ability to invoke `sampleTerrain` from node.js to enable offline terrain sampling

Expand All @@ -18,6 +20,7 @@ Change Log
* Fixed glTF support to handle meshes with and without tangent vectors, and with/without morph targets, sharing one material. [#6421](https://github.com/AnalyticalGraphicsInc/cesium/pull/6421)
* Fixed glTF support to handle skinned meshes when no skin is supplied. [#6061](https://github.com/AnalyticalGraphicsInc/cesium/issues/6061)
* Allow loadWithXhr to work with string URLs in a web worker.
* Updated to Draco 1.3.0 and implemented faster loading of Draco compressed glTF assets in browsers that support Web Assembly. [#6420](https://github.com/AnalyticalGraphicsInc/cesium/pull/6420)
* `GroundPrimitive`s and `ClassificationPrimitive`s will become ready when `show` is `false`. [#6428](https://github.com/AnalyticalGraphicsInc/cesium/pull/6428)
* Fix Firefox WebGL console warnings. [#5912](https://github.com/AnalyticalGraphicsInc/cesium/issues/5912)
* Fix parsing Cesium.js in older browsers that do not support all TypedArray types. [#6396](https://github.com/AnalyticalGraphicsInc/cesium/pull/6396)
Expand Down
11 changes: 11 additions & 0 deletions Source/Core/FeatureDetection.js
Expand Up @@ -280,5 +280,16 @@ define([
return typeof Worker !== 'undefined';
};

/**
* Detects whether the current browser supports Web Assembly.
*
* @returns {Boolean} true if the browsers supports Web Assembly, false if not.
*
* @see {@link https://developer.mozilla.org/en-US/docs/WebAssembly}
*/
FeatureDetection.supportsWebAssembly = function() {
return typeof WebAssembly !== 'undefined';
};

return FeatureDetection;
});
74 changes: 74 additions & 0 deletions Source/Core/TaskProcessor.js
Expand Up @@ -6,8 +6,10 @@ define([
'./destroyObject',
Copy link
Contributor

Choose a reason for hiding this comment

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

Even though Draco doesn't work in IE yet I went to open Sandcastle in IE and it doesn't get past the loading screen. master seems fine. Is this a result of any of the changes here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Merged in master, that should incorporate the error message fix.

'./DeveloperError',
'./Event',
'./FeatureDetection',
'./getAbsoluteUri',
'./isCrossOriginUrl',
'./Resource',
'./RuntimeError',
'require'
], function(
Expand All @@ -18,8 +20,10 @@ define([
destroyObject,
DeveloperError,
Event,
FeatureDetection,
getAbsoluteUri,
isCrossOriginUrl,
Resource,
RuntimeError,
require) {
'use strict';
Expand Down Expand Up @@ -161,6 +165,34 @@ define([
return worker;
}

function getWebAssemblyLoaderConfig(processor, wasmOptions) {
var config = {
modulePath : undefined,
wasmBinaryFile : undefined,
wasmBinary : undefined
};

// Web assembly not supported, use fallback js module if provided
if (!FeatureDetection.supportsWebAssembly()) {
if (!defined(wasmOptions.fallbackModulePath)) {
throw new RuntimeError('This browser does not support Web Assembly, and no backup module was provided for ' + processor._workerName);
}

config.modulePath = buildModuleUrl(wasmOptions.fallbackModulePath);
return when.resolve(config);
}

config.modulePath = buildModuleUrl(wasmOptions.modulePath);
config.wasmBinaryFile = buildModuleUrl(wasmOptions.wasmBinaryFile);

return Resource.fetchArrayBuffer({
url: config.wasmBinaryFile
}).then(function (arrayBuffer) {
config.wasmBinary = arrayBuffer;
return config;
});
}

/**
* A wrapper around a web worker that allows scheduling tasks for a given worker,
* returning results asynchronously via a promise.
Expand Down Expand Up @@ -245,6 +277,48 @@ define([
});
};

/**
* Posts a message to a web worker with configuration to initialize loading
* and compiling a web assembly module asychronously, as well as an optional
* fallback JavaScript module to use if Web Assembly is not supported.
*
* @param {Object} [webAssemblyOptions] An object with the following properties:
* @param {String} [webAssemblyOptions.modulePath] The path of the web assembly JavaScript wrapper module.
* @param {String} [webAssemblyOptions.wasmBinaryFile] The path of the web assembly binary file.
* @param {String} [webAssemblyOptions.fallbackModulePath] The path of the fallback JavaScript module to use if web assembly is not supported.
* @returns {Promise.<Object>} A promise that resolves to the result when the web worker has loaded and compiled the web assembly module and is ready to process tasks.
*/
TaskProcessor.prototype.initWebAssemblyModule = function (webAssemblyOptions) {
if (!defined(this._worker)) {
this._worker = createWorker(this);
}

var deferred = when.defer();
var processor = this;
var worker = this._worker;
getWebAssemblyLoaderConfig(this, webAssemblyOptions).then(function(wasmConfig) {
return when(canTransferArrayBuffer(), function(canTransferArrayBuffer) {
var transferableObjects;
var binary = wasmConfig.wasmBinary;
if (defined(binary) && canTransferArrayBuffer) {
transferableObjects = [binary];
}

worker.onmessage = function(event) {
worker.onmessage = function(event) {
completeTask(processor, event.data);
};

deferred.resolve(event.data);
};

worker.postMessage({ webAssemblyConfig : wasmConfig }, transferableObjects);
});
});

return deferred;
};

/**
* Returns true if this object was destroyed; otherwise, false.
* <br /><br />
Expand Down
16 changes: 15 additions & 1 deletion Source/Scene/DracoLoader.js
Expand Up @@ -28,9 +28,18 @@ define([

// Exposed for testing purposes
DracoLoader._decoderTaskProcessor = undefined;
DracoLoader._taskProcessorReady = false;
DracoLoader._getDecoderTaskProcessor = function () {
if (!defined(DracoLoader._decoderTaskProcessor)) {
DracoLoader._decoderTaskProcessor = new TaskProcessor('decodeDraco', DracoLoader._maxDecodingConcurrency);
var processor = new TaskProcessor('decodeDraco', DracoLoader._maxDecodingConcurrency);
processor.initWebAssemblyModule({
modulePath : 'ThirdParty/Workers/draco_wasm_wrapper.js',
wasmBinaryFile : 'ThirdParty/draco_decoder.wasm',
fallbackModulePath : 'ThirdParty/Workers/draco_decoder.js'
}).then(function () {
DracoLoader._taskProcessorReady = true;
});
DracoLoader._decoderTaskProcessor = processor;
}

return DracoLoader._decoderTaskProcessor;
Expand Down Expand Up @@ -85,6 +94,11 @@ define([
}

function scheduleDecodingTask(decoderTaskProcessor, model, loadResources, context) {
if (!DracoLoader._taskProcessorReady) {
// The task processor is not ready to schedule tasks
return;
}

var taskData = loadResources.primitivesToDecode.peek();
if (!defined(taskData)) {
// All primitives are processing
Expand Down
32 changes: 32 additions & 0 deletions Source/ThirdParty/Workers/draco_decoder.js

Large diffs are not rendered by default.